Merge remote-tracking branch 'remotes/upstream/master' into feature/qt-platform

# Conflicts:
#	src/frontend/qt_sdl/CMakeLists.txt
#	src/frontend/qt_sdl/Platform.cpp
#	src/frontend/qt_sdl/main.cpp
This commit is contained in:
Nadia Holmquist Pedersen 2020-06-03 13:54:28 +02:00
commit f9f366e296
114 changed files with 28867 additions and 804 deletions

View File

@ -27,7 +27,7 @@ jobs:
&& tar -zxf cmake-$CMAKE_VERSION-Linux-x86_64.tar.gz \
&& sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list \
&& sudo apt-get update \
&& sudo apt-get install gtk+-3.0 libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev
&& sudo apt-get install gtk+-3.0 libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default
- name: Create build environment
run: mkdir ${{runner.workspace}}/build
- name: Configure
@ -40,7 +40,7 @@ jobs:
run: |
make -j$(nproc --all) \
&& mkdir dist \
&& cp {melonDS,romlist.bin} dist
&& cp melonDS dist
- uses: actions/upload-artifact@v1
with:
name: melonDS

View File

@ -25,7 +25,7 @@ jobs:
7z x -y msys64.7z "-oC:\tools\"
C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Syuq --noconfirm"
- name: Install dependencies
run: C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Sq --noconfirm git make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain}"
run: C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Sq --noconfirm git make mingw-w64-x86_64-{cmake,mesa,SDL2,qt5-static,toolchain}"
- name: Create build environment
run: |
New-Item -ItemType directory -Path ${{runner.workspace}}\melonDS\build
@ -33,7 +33,7 @@ jobs:
- name: Configure
run: |
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \
&& cd melonDS/build && cmake .. -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}"
&& cd melonDS/build && cmake .. -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_STATIC=ON -DQT5_STATIC_DIR=C:/tools/msys64/mingw64/qt5-static"
- name: Make
run: |
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \

View File

@ -10,23 +10,17 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
project(melonDS)
if(NOT CMAKE_BUILD_TYPE)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
if (CMAKE_BUILD_TYPE STREQUAL Release)
option(ENABLE_LTO "Enable link-time optimization" ON)
else()
option(ENABLE_LTO "Enable link-time optimization" OFF)
if (CMAKE_BUILD_TYPE STREQUAL Debug)
add_compile_options(-Og)
endif()
if(ENABLE_LTO)
add_compile_options(-O3 -flto)
set(CMAKE_AR "gcc-ar")
set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_C_ARCHIVE_FINISH true)
set(CMAKE_CXX_ARCHIVE_CREATE "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_CXX_ARCHIVE_FINISH true)
if (CMAKE_BUILD_TYPE STREQUAL Release)
add_compile_options(-O3)
add_link_options(-s)
endif()
add_compile_options(-fno-pic)
@ -34,12 +28,12 @@ add_link_options(-no-pie)
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
if (WIN32)
option(BUILD_STATIC "Statically link dependencies" OFF)
endif()
add_subdirectory(src)
if (BUILD_QT_SDL)
add_subdirectory(src/frontend/qt_sdl)
endif()
configure_file(
${CMAKE_SOURCE_DIR}/romlist.bin
${CMAKE_BINARY_DIR}/romlist.bin COPYONLY)

View File

@ -38,7 +38,7 @@ As for the rest, the interface should be pretty straightforward. If you have a q
* Install dependencies:
```sh
sudo apt-get install gtk+-3.0 libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev
sudo apt-get install libgtk-3-dev libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev
```
* Compile:
@ -52,10 +52,6 @@ make -j$(nproc --all)
### Windows:
* use CodeBlocks
#### MSYS2 and CMake
1. Install [MSYS2](https://www.msys2.org/)
2. Open the **MSYS2 MinGW 64-bit** terminal
3. Update the packages using `pacman -Syu` and reopen the terminal if it asks you to

6
melon.qrc Normal file
View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource>
<file alias="melon-icon">icon/melon_32x32.png</file>
</qresource>
</RCC>

View File

@ -10,5 +10,3 @@ mkdir -p dist
for lib in $(ldd melonDS.exe | grep mingw | sed "s/.*=> //" | sed "s/(.*)//"); do
cp "${lib}" dist
done
cp melonDS.exe romlist.bin dist

Binary file not shown.

View File

@ -18,6 +18,7 @@
#include <stdio.h>
#include "NDS.h"
#include "DSi.h"
#include "ARM.h"
#include "ARMInterpreter.h"
#include "AREngine.h"
@ -101,10 +102,55 @@ void ARM::Reset()
void ARMv5::Reset()
{
if (NDS::ConsoleType == 1)
{
BusRead8 = DSi::ARM9Read8;
BusRead16 = DSi::ARM9Read16;
BusRead32 = DSi::ARM9Read32;
BusWrite8 = DSi::ARM9Write8;
BusWrite16 = DSi::ARM9Write16;
BusWrite32 = DSi::ARM9Write32;
GetMemRegion = DSi::ARM9GetMemRegion;
}
else
{
BusRead8 = NDS::ARM9Read8;
BusRead16 = NDS::ARM9Read16;
BusRead32 = NDS::ARM9Read32;
BusWrite8 = NDS::ARM9Write8;
BusWrite16 = NDS::ARM9Write16;
BusWrite32 = NDS::ARM9Write32;
GetMemRegion = NDS::ARM9GetMemRegion;
}
CP15Reset();
ARM::Reset();
}
void ARMv4::Reset()
{
if (NDS::ConsoleType)
{
BusRead8 = DSi::ARM7Read8;
BusRead16 = DSi::ARM7Read16;
BusRead32 = DSi::ARM7Read32;
BusWrite8 = DSi::ARM7Write8;
BusWrite16 = DSi::ARM7Write16;
BusWrite32 = DSi::ARM7Write32;
}
else
{
BusRead8 = NDS::ARM7Read8;
BusRead16 = NDS::ARM7Read16;
BusRead32 = NDS::ARM7Read32;
BusWrite8 = NDS::ARM7Write8;
BusWrite16 = NDS::ARM7Write16;
BusWrite32 = NDS::ARM7Write32;
}
ARM::Reset();
}
void ARM::DoSavestate(Savestate* file)
{
@ -222,9 +268,6 @@ void ARMv5::JumpTo(u32 addr, bool restorecpsr)
CPSR &= ~0x20;
}
// TODO: investigate this
// firmware jumps to region 01FFxxxx, but region 5 (01000000-02000000) is set to non-executable
// is melonDS fucked up somewhere, or is the DS PU just incomplete/crapoed?
/*if (!(PU_Map[addr>>12] & 0x04))
{
printf("jumped to %08X. very bad\n", addr);

View File

@ -132,6 +132,14 @@ public:
NDS::MemRegion CodeMem;
static u32 ConditionTable[16];
protected:
u8 (*BusRead8)(u32 addr);
u16 (*BusRead16)(u32 addr);
u32 (*BusRead32)(u32 addr);
void (*BusWrite8)(u32 addr, u8 val);
void (*BusWrite16)(u32 addr, u16 val);
void (*BusWrite32)(u32 addr, u32 val);
};
class ARMv5 : public ARM
@ -259,6 +267,8 @@ public:
s32 RegionCodeCycles;
u8* CurICacheLine;
bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region);
};
class ARMv4 : public ARM
@ -266,23 +276,25 @@ class ARMv4 : public ARM
public:
ARMv4();
void Reset();
void JumpTo(u32 addr, bool restorecpsr = false);
void Execute();
u16 CodeRead16(u32 addr)
{
return NDS::ARM7Read16(addr);
return BusRead16(addr);
}
u32 CodeRead32(u32 addr)
{
return NDS::ARM7Read32(addr);
return BusRead32(addr);
}
void DataRead8(u32 addr, u32* val)
{
*val = NDS::ARM7Read8(addr);
*val = BusRead8(addr);
DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
@ -291,7 +303,7 @@ public:
{
addr &= ~1;
*val = NDS::ARM7Read16(addr);
*val = BusRead16(addr);
DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
@ -300,7 +312,7 @@ public:
{
addr &= ~3;
*val = NDS::ARM7Read32(addr);
*val = BusRead32(addr);
DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][2];
}
@ -309,13 +321,13 @@ public:
{
addr &= ~3;
*val = NDS::ARM7Read32(addr);
*val = BusRead32(addr);
DataCycles += NDS::ARM7MemTimings[DataRegion][3];
}
void DataWrite8(u32 addr, u8 val)
{
NDS::ARM7Write8(addr, val);
BusWrite8(addr, val);
DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
@ -324,7 +336,7 @@ public:
{
addr &= ~1;
NDS::ARM7Write16(addr, val);
BusWrite16(addr, val);
DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
}
@ -333,7 +345,7 @@ public:
{
addr &= ~3;
NDS::ARM7Write32(addr, val);
BusWrite32(addr, val);
DataRegion = addr >> 24;
DataCycles = NDS::ARM7MemTimings[DataRegion][2];
}
@ -342,7 +354,7 @@ public:
{
addr &= ~3;
NDS::ARM7Write32(addr, val);
BusWrite32(addr, val);
DataCycles += NDS::ARM7MemTimings[DataRegion][3];
}

View File

@ -13,9 +13,19 @@ add_library(core STATIC
CP15.cpp
CRC32.cpp
DMA.cpp
DSi.cpp
DSi_AES.cpp
DSi_Camera.cpp
DSi_I2C.cpp
DSi_NDMA.cpp
DSi_NWifi.cpp
DSi_SD.cpp
DSi_SPI_TSC.cpp
FIFO.h
GBACart.cpp
GPU.cpp
GPU_OpenGL.cpp
GPU_OpenGL_shaders.h
GPU2D.cpp
GPU3D.cpp
GPU3D_OpenGL.cpp
@ -25,6 +35,7 @@ add_library(core STATIC
NDSCart.cpp
OpenGLSupport.cpp
Platform.h
ROMList.h
RTC.cpp
Savestate.cpp
SPI.cpp
@ -33,6 +44,8 @@ add_library(core STATIC
version.h
Wifi.cpp
WifiAP.cpp
tiny-AES-c/aes.c
)
if (WIN32)

View File

@ -19,6 +19,7 @@
#include <stdio.h>
#include <string.h>
#include "NDS.h"
#include "DSi.h"
#include "ARM.h"
@ -719,7 +720,7 @@ u32 ARMv5::CodeRead32(u32 addr, bool branch)
if (CodeMem.Mem) return *(u32*)&CodeMem.Mem[addr & CodeMem.Mask];
return NDS::ARM9Read32(addr);
return BusRead32(addr);
}
@ -738,7 +739,7 @@ void ARMv5::DataRead8(u32 addr, u32* val)
return;
}
*val = NDS::ARM9Read8(addr);
*val = BusRead8(addr);
DataCycles = MemTimings[addr >> 12][1];
}
@ -759,7 +760,7 @@ void ARMv5::DataRead16(u32 addr, u32* val)
return;
}
*val = NDS::ARM9Read16(addr);
*val = BusRead16(addr);
DataCycles = MemTimings[addr >> 12][1];
}
@ -780,7 +781,7 @@ void ARMv5::DataRead32(u32 addr, u32* val)
return;
}
*val = NDS::ARM9Read32(addr);
*val = BusRead32(addr);
DataCycles = MemTimings[addr >> 12][2];
}
@ -801,7 +802,7 @@ void ARMv5::DataRead32S(u32 addr, u32* val)
return;
}
*val = NDS::ARM9Read32(addr);
*val = BusRead32(addr);
DataCycles += MemTimings[addr >> 12][3];
}
@ -820,7 +821,7 @@ void ARMv5::DataWrite8(u32 addr, u8 val)
return;
}
NDS::ARM9Write8(addr, val);
BusWrite8(addr, val);
DataCycles = MemTimings[addr >> 12][1];
}
@ -841,7 +842,7 @@ void ARMv5::DataWrite16(u32 addr, u16 val)
return;
}
NDS::ARM9Write16(addr, val);
BusWrite16(addr, val);
DataCycles = MemTimings[addr >> 12][1];
}
@ -862,7 +863,7 @@ void ARMv5::DataWrite32(u32 addr, u32 val)
return;
}
NDS::ARM9Write32(addr, val);
BusWrite32(addr, val);
DataCycles = MemTimings[addr >> 12][2];
}
@ -883,7 +884,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val)
return;
}
NDS::ARM9Write32(addr, val);
BusWrite32(addr, val);
DataCycles += MemTimings[addr >> 12][3];
}
@ -896,6 +897,6 @@ void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region)
return;
}*/
NDS::ARM9GetMemRegion(addr, false, &CodeMem);
GetMemRegion(addr, false, &CodeMem);
}

View File

@ -28,19 +28,25 @@ namespace Config
const char* kConfigFile = "melonDS.ini";
int _3DRenderer;
int Threaded3D;
char BIOS9Path[1024];
char BIOS7Path[1024];
char FirmwarePath[1024];
int GL_ScaleFactor;
int GL_Antialias;
char DSiBIOS9Path[1024];
char DSiBIOS7Path[1024];
char DSiFirmwarePath[1024];
char DSiNANDPath[1024];
ConfigEntry ConfigFile[] =
{
{"3DRenderer", 0, &_3DRenderer, 1, NULL, 0},
{"Threaded3D", 0, &Threaded3D, 1, NULL, 0},
{"BIOS9Path", 1, BIOS9Path, 0, "", 1023},
{"BIOS7Path", 1, BIOS7Path, 0, "", 1023},
{"FirmwarePath", 1, FirmwarePath, 0, "", 1023},
{"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, NULL, 0},
{"GL_Antialias", 0, &GL_Antialias, 0, NULL, 0},
{"DSiBIOS9Path", 1, DSiBIOS9Path, 0, "", 1023},
{"DSiBIOS7Path", 1, DSiBIOS7Path, 0, "", 1023},
{"DSiFirmwarePath", 1, DSiFirmwarePath, 0, "", 1023},
{"DSiNANDPath", 1, DSiNANDPath, 0, "", 1023},
{"", -1, NULL, 0, NULL, 0}
};
@ -82,7 +88,8 @@ void Load()
while (!feof(f))
{
fgets(linebuf, 1024, f);
int ret = sscanf(linebuf, "%32[A-Za-z_0-9]=%[^\t\n]", entryname, entryval);
int ret = sscanf(linebuf, "%31[A-Za-z_0-9]=%[^\t\n]", entryname, entryval);
entryname[31] = '\0';
if (ret < 2) continue;
ConfigEntry* entry = &ConfigFile[0];

View File

@ -42,11 +42,14 @@ bool HasConfigFile(const char* fileName);
void Load();
void Save();
extern int _3DRenderer;
extern int Threaded3D;
extern char BIOS9Path[1024];
extern char BIOS7Path[1024];
extern char FirmwarePath[1024];
extern int GL_ScaleFactor;
extern int GL_Antialias;
extern char DSiBIOS9Path[1024];
extern char DSiBIOS7Path[1024];
extern char DSiFirmwarePath[1024];
extern char DSiNANDPath[1024];
}

View File

@ -18,16 +18,11 @@
#include <stdio.h>
#include "NDS.h"
#include "DSi.h"
#include "DMA.h"
#include "NDSCart.h"
#include "GPU.h"
// NOTES ON DMA SHIT
//
// * could use optimized code paths for common types of DMA transfers. for example, VRAM
// have to profile it to see if it's actually worth doing
// DMA TIMINGS
//
@ -58,8 +53,6 @@ DMA::DMA(u32 cpu, u32 num)
CountMask = 0x001FFFFF;
else
CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF);
Reset();
}
DMA::~DMA()
@ -82,6 +75,21 @@ void DMA::Reset()
Running = false;
InProgress = false;
if (NDS::ConsoleType == 1)
{
BusRead16 = (CPU==0) ? DSi::ARM9Read16 : DSi::ARM7Read16;
BusRead32 = (CPU==0) ? DSi::ARM9Read32 : DSi::ARM7Read32;
BusWrite16 = (CPU==0) ? DSi::ARM9Write16 : DSi::ARM7Write16;
BusWrite32 = (CPU==0) ? DSi::ARM9Write32 : DSi::ARM7Write32;
}
else
{
BusRead16 = (CPU==0) ? NDS::ARM9Read16 : NDS::ARM7Read16;
BusRead32 = (CPU==0) ? NDS::ARM9Read32 : NDS::ARM7Read32;
BusWrite16 = (CPU==0) ? NDS::ARM9Write16 : NDS::ARM7Write16;
BusWrite32 = (CPU==0) ? NDS::ARM9Write32 : NDS::ARM7Write32;
}
}
void DMA::DoSavestate(Savestate* file)
@ -232,7 +240,7 @@ void DMA::Run9()
{
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
NDS::ARM9Write16(CurDstAddr, NDS::ARM9Read16(CurSrcAddr));
BusWrite16(CurDstAddr, BusRead16(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<1;
CurDstAddr += DstAddrInc<<1;
@ -268,7 +276,7 @@ void DMA::Run9()
{
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
NDS::ARM9Write32(CurDstAddr, NDS::ARM9Read32(CurSrcAddr));
BusWrite32(CurDstAddr, BusRead32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
@ -344,7 +352,7 @@ void DMA::Run7()
{
NDS::ARM7Timestamp += unitcycles;
NDS::ARM7Write16(CurDstAddr, NDS::ARM7Read16(CurSrcAddr));
BusWrite16(CurDstAddr, BusRead16(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<1;
CurDstAddr += DstAddrInc<<1;
@ -380,7 +388,7 @@ void DMA::Run7()
{
NDS::ARM7Timestamp += unitcycles;
NDS::ARM7Write32(CurDstAddr, NDS::ARM7Read32(CurSrcAddr));
BusWrite32(CurDstAddr, BusRead32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;

View File

@ -86,6 +86,11 @@ private:
bool Stall;
bool IsGXFIFODMA;
u16 (*BusRead16)(u32 addr);
u32 (*BusRead32)(u32 addr);
void (*BusWrite16)(u32 addr, u16 val);
void (*BusWrite32)(u32 addr, u32 val);
};
#endif

1806
src/DSi.cpp Normal file

File diff suppressed because it is too large Load Diff

93
src/DSi.h Normal file
View File

@ -0,0 +1,93 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_H
#define DSI_H
#include "NDS.h"
#include "DSi_SD.h"
namespace DSi
{
extern u8 ARM9iBIOS[0x10000];
extern u8 ARM7iBIOS[0x10000];
extern u8 eMMC_CID[16];
extern u64 ConsoleID;
extern DSi_SDHost* SDMMC;
extern DSi_SDHost* SDIO;
bool Init();
void DeInit();
void Reset();
void SoftReset();
bool LoadBIOS();
bool LoadNAND();
void RunNDMAs(u32 cpu);
void StallNDMAs();
bool NDMAsInMode(u32 cpu, u32 mode);
bool NDMAsRunning(u32 cpu);
void CheckNDMAs(u32 cpu, u32 mode);
void StopNDMAs(u32 cpu, u32 mode);
void MapNWRAM_A(u32 num, u8 val);
void MapNWRAM_B(u32 num, u8 val);
void MapNWRAM_C(u32 num, u8 val);
void MapNWRAMRange(u32 cpu, u32 num, u32 val);
u8 ARM9Read8(u32 addr);
u16 ARM9Read16(u32 addr);
u32 ARM9Read32(u32 addr);
void ARM9Write8(u32 addr, u8 val);
void ARM9Write16(u32 addr, u16 val);
void ARM9Write32(u32 addr, u32 val);
bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region);
u8 ARM7Read8(u32 addr);
u16 ARM7Read16(u32 addr);
u32 ARM7Read32(u32 addr);
void ARM7Write8(u32 addr, u8 val);
void ARM7Write16(u32 addr, u16 val);
void ARM7Write32(u32 addr, u32 val);
bool ARM7GetMemRegion(u32 addr, bool write, NDS::MemRegion* region);
u8 ARM9IORead8(u32 addr);
u16 ARM9IORead16(u32 addr);
u32 ARM9IORead32(u32 addr);
void ARM9IOWrite8(u32 addr, u8 val);
void ARM9IOWrite16(u32 addr, u16 val);
void ARM9IOWrite32(u32 addr, u32 val);
u8 ARM7IORead8(u32 addr);
u16 ARM7IORead16(u32 addr);
u32 ARM7IORead32(u32 addr);
void ARM7IOWrite8(u32 addr, u8 val);
void ARM7IOWrite16(u32 addr, u16 val);
void ARM7IOWrite32(u32 addr, u32 val);
}
#endif // DSI_H

17
src/DSiCrypto.cpp Normal file
View File

@ -0,0 +1,17 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/

24
src/DSiCrypto.h Normal file
View File

@ -0,0 +1,24 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSICRYPTO_H
#define DSICRYPTO_H
//
#endif // DSICRYPTO_H

555
src/DSi_AES.cpp Normal file
View File

@ -0,0 +1,555 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "DSi_AES.h"
#include "FIFO.h"
#include "tiny-AES-c/aes.hpp"
#include "Platform.h"
namespace DSi_AES
{
u32 Cnt;
u32 BlkCnt;
u32 RemBlocks;
bool OutputFlush;
u32 InputDMASize, OutputDMASize;
u32 AESMode;
FIFO<u32>* InputFIFO;
FIFO<u32>* OutputFIFO;
u8 IV[16];
u8 MAC[16];
u8 KeyNormal[4][16];
u8 KeyX[4][16];
u8 KeyY[4][16];
u8 CurKey[16];
u8 CurMAC[16];
AES_ctx Ctx;
void Swap16(u8* dst, u8* src)
{
for (int i = 0; i < 16; i++)
dst[i] = src[15-i];
}
void ROL16(u8* val, u32 n)
{
u32 n_coarse = n >> 3;
u32 n_fine = n & 7;
u8 tmp[16];
for (u32 i = 0; i < 16; i++)
{
tmp[i] = val[(i - n_coarse) & 0xF];
}
for (u32 i = 0; i < 16; i++)
{
val[i] = (tmp[i] << n_fine) | (tmp[(i - 1) & 0xF] >> (8-n_fine));
}
}
#define _printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); }
#define _printhex2(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); }
#define _printhexR(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); printf("\n"); }
#define _printhex2R(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); }
bool Init()
{
InputFIFO = new FIFO<u32>(16);
OutputFIFO = new FIFO<u32>(16);
const u8 zero[16] = {0};
AES_init_ctx_iv(&Ctx, zero, zero);
return true;
}
void DeInit()
{
delete InputFIFO;
delete OutputFIFO;
}
void Reset()
{
Cnt = 0;
BlkCnt = 0;
RemBlocks = 0;
OutputFlush = false;
InputDMASize = 0;
OutputDMASize = 0;
AESMode = 0;
InputFIFO->Clear();
OutputFIFO->Clear();
memset(IV, 0, sizeof(IV));
memset(MAC, 0, sizeof(MAC));
memset(KeyNormal, 0, sizeof(KeyNormal));
memset(KeyX, 0, sizeof(KeyX));
memset(KeyY, 0, sizeof(KeyY));
memset(CurKey, 0, sizeof(CurKey));
memset(CurMAC, 0, sizeof(CurMAC));
// initialize keys
// slot 0: modcrypt
*(u32*)&KeyX[0][0] = 0x746E694E;
*(u32*)&KeyX[0][4] = 0x6F646E65;
// slot 1: 'Tad'/dev.kp
*(u32*)&KeyX[1][0] = 0x4E00004A;
*(u32*)&KeyX[1][4] = 0x4A00004E;
*(u32*)&KeyX[1][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xC80C4B72;
*(u32*)&KeyX[1][12] = (u32)DSi::ConsoleID;
// slot 3: console-unique eMMC crypto
*(u32*)&KeyX[3][0] = (u32)DSi::ConsoleID;
*(u32*)&KeyX[3][4] = (u32)DSi::ConsoleID ^ 0x24EE6906;
*(u32*)&KeyX[3][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xE65B601D;
*(u32*)&KeyX[3][12] = (u32)(DSi::ConsoleID >> 32);
*(u32*)&KeyY[3][0] = 0x0AB9DC76;
*(u32*)&KeyY[3][4] = 0xBD4DC4D3;
*(u32*)&KeyY[3][8] = 0x202DDD1D;
}
void ProcessBlock_CCM_Decrypt()
{
u8 data[16];
u8 data_rev[16];
*(u32*)&data[0] = InputFIFO->Read();
*(u32*)&data[4] = InputFIFO->Read();
*(u32*)&data[8] = InputFIFO->Read();
*(u32*)&data[12] = InputFIFO->Read();
//printf("AES-CCM: "); _printhex2(data, 16);
Swap16(data_rev, data);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
for (int i = 0; i < 16; i++) CurMAC[i] ^= data_rev[i];
AES_ECB_encrypt(&Ctx, CurMAC);
Swap16(data, data_rev);
//printf(" -> "); _printhex2(data, 16);
OutputFIFO->Write(*(u32*)&data[0]);
OutputFIFO->Write(*(u32*)&data[4]);
OutputFIFO->Write(*(u32*)&data[8]);
OutputFIFO->Write(*(u32*)&data[12]);
}
void ProcessBlock_CTR()
{
u8 data[16];
u8 data_rev[16];
*(u32*)&data[0] = InputFIFO->Read();
*(u32*)&data[4] = InputFIFO->Read();
*(u32*)&data[8] = InputFIFO->Read();
*(u32*)&data[12] = InputFIFO->Read();
//printf("AES-CTR: "); _printhex2(data, 16);
Swap16(data_rev, data);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
Swap16(data, data_rev);
//printf(" -> "); _printhex(data, 16);
OutputFIFO->Write(*(u32*)&data[0]);
OutputFIFO->Write(*(u32*)&data[4]);
OutputFIFO->Write(*(u32*)&data[8]);
OutputFIFO->Write(*(u32*)&data[12]);
}
u32 ReadCnt()
{
u32 ret = Cnt;
ret |= InputFIFO->Level();
ret |= (OutputFIFO->Level() << 5);
return ret;
}
void WriteCnt(u32 val)
{
u32 oldcnt = Cnt;
Cnt = val & 0xFC1FF000;
/*if (val & (3<<10))
{
if (val & (1<<11)) OutputFlush = true;
Update();
}*/
u32 dmasize_in[4] = {0, 4, 8, 12};
u32 dmasize_out[4] = {4, 8, 12, 16};
InputDMASize = dmasize_in[(val >> 12) & 0x3];
OutputDMASize = dmasize_out[(val >> 14) & 0x3];
AESMode = (val >> 28) & 0x3;
if (AESMode == 1) printf("AES-CCM TODO\n");
if (val & (1<<24))
{
u32 slot = (val >> 26) & 0x3;
memcpy(CurKey, KeyNormal[slot], 16);
}
if (!(oldcnt & (1<<31)) && (val & (1<<31)))
{
// transfer start (checkme)
RemBlocks = BlkCnt >> 16;
if (RemBlocks > 0)
{
u8 key[16];
u8 iv[16];
Swap16(key, CurKey);
Swap16(iv, IV);
if (AESMode < 2)
{
if (BlkCnt & 0xFFFF) printf("AES: CCM EXTRA LEN TODO\n");
u32 maclen = (val >> 16) & 0x7;
if (maclen < 1) maclen = 1;
iv[0] = 0x02;
for (int i = 0; i < 12; i++) iv[1+i] = iv[4+i];
iv[13] = 0x00;
iv[14] = 0x00;
iv[15] = 0x01;
AES_init_ctx_iv(&Ctx, key, iv);
iv[0] |= (maclen << 3) | ((BlkCnt & 0xFFFF) ? (1<<6) : 0);
iv[13] = RemBlocks >> 12;
iv[14] = RemBlocks >> 4;
iv[15] = RemBlocks << 4;
memcpy(CurMAC, iv, 16);
AES_ECB_encrypt(&Ctx, CurMAC);
}
else
{
AES_init_ctx_iv(&Ctx, key, iv);
}
DSi::CheckNDMAs(1, 0x2A);
}
else
{
// no blocks to process? oh well. mark it finished
// CHECKME: does this trigger any IRQ or shit?
Cnt &= ~(1<<31);
}
}
//printf("AES CNT: %08X / mode=%d key=%d inDMA=%d outDMA=%d blocks=%d\n",
// val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks);
}
void WriteBlkCnt(u32 val)
{
BlkCnt = val;
}
u32 ReadOutputFIFO()
{
if (OutputFIFO->IsEmpty()) printf("!!! AES OUTPUT FIFO EMPTY\n");
u32 ret = OutputFIFO->Read();
if (Cnt & (1<<31))
{
CheckInputDMA();
CheckOutputDMA();
}
else
{
if (OutputFIFO->Level() > 0)
DSi::CheckNDMAs(1, 0x2B);
else
DSi::StopNDMAs(1, 0x2B);
}
return ret;
}
void WriteInputFIFO(u32 val)
{
// TODO: add some delay to processing
if (InputFIFO->IsFull()) printf("!!! AES INPUT FIFO FULL\n");
InputFIFO->Write(val);
if (!(Cnt & (1<<31))) return;
Update();
}
void CheckInputDMA()
{
if (RemBlocks == 0) return;
if (InputFIFO->Level() <= InputDMASize)
{
// trigger input DMA
DSi::CheckNDMAs(1, 0x2A);
}
Update();
}
void CheckOutputDMA()
{
if (OutputFIFO->Level() >= OutputDMASize)
{
// trigger output DMA
DSi::CheckNDMAs(1, 0x2B);
}
}
void Update()
{
while (InputFIFO->Level() >= 4 && OutputFIFO->Level() <= 12 && RemBlocks > 0)
{
switch (AESMode)
{
case 0: ProcessBlock_CCM_Decrypt(); break;
case 2:
case 3: ProcessBlock_CTR(); break;
default:
// dorp
OutputFIFO->Write(InputFIFO->Read());
OutputFIFO->Write(InputFIFO->Read());
OutputFIFO->Write(InputFIFO->Read());
OutputFIFO->Write(InputFIFO->Read());
}
RemBlocks--;
}
CheckOutputDMA();
if (RemBlocks == 0)
{
if (AESMode == 0)
{
Ctx.Iv[13] = 0x00;
Ctx.Iv[14] = 0x00;
Ctx.Iv[15] = 0x00;
AES_CTR_xcrypt_buffer(&Ctx, CurMAC, 16);
//printf("FINAL MAC: "); _printhexR(CurMAC, 16);
//printf("INPUT MAC: "); _printhex(MAC, 16);
Cnt |= (1<<21);
for (int i = 0; i < 16; i++)
{
if (CurMAC[15-i] != MAC[i]) Cnt &= ~(1<<21);
}
}
else
{
// CHECKME
Cnt &= ~(1<<21);
}
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) NDS::SetIRQ2(NDS::IRQ2_DSi_AES);
DSi::StopNDMAs(1, 0x2A);
if (OutputFIFO->Level() > 0)
DSi::CheckNDMAs(1, 0x2B);
else
DSi::StopNDMAs(1, 0x2B);
OutputFlush = false;
}
}
void WriteIV(u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&IV[offset];
*(u32*)&IV[offset] = (old & ~mask) | (val & mask);
//printf("AES: IV: "); _printhex(IV, 16);
}
void WriteMAC(u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&MAC[offset];
*(u32*)&MAC[offset] = (old & ~mask) | (val & mask);
//printf("AES: MAC: "); _printhex(MAC, 16);
}
void DeriveNormalKey(u32 slot)
{
const u8 key_const[16] = {0xFF, 0xFE, 0xFB, 0x4E, 0x29, 0x59, 0x02, 0x58, 0x2A, 0x68, 0x0F, 0x5F, 0x1A, 0x4F, 0x3E, 0x79};
u8 tmp[16];
//printf("slot%d keyX: ", slot); _printhex(KeyX[slot], 16);
//printf("slot%d keyY: ", slot); _printhex(KeyY[slot], 16);
for (int i = 0; i < 16; i++)
tmp[i] = KeyX[slot][i] ^ KeyY[slot][i];
u32 carry = 0;
for (int i = 0; i < 16; i++)
{
u32 res = tmp[i] + key_const[15-i] + carry;
tmp[i] = res & 0xFF;
carry = res >> 8;
}
ROL16(tmp, 42);
//printf("derive normalkey %d\n", slot); _printhex(tmp, 16);
memcpy(KeyNormal[slot], tmp, 16);
}
void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyNormal[slot][offset];
*(u32*)&KeyNormal[slot][offset] = (old & ~mask) | (val & mask);
//printf("KeyNormal(%d): ", slot); _printhex(KeyNormal[slot], 16);
}
void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyX[slot][offset];
*(u32*)&KeyX[slot][offset] = (old & ~mask) | (val & mask);
//printf("KeyX(%d): ", slot); _printhex(KeyX[slot], 16);
}
void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask)
{
u32 old = *(u32*)&KeyY[slot][offset];
*(u32*)&KeyY[slot][offset] = (old & ~mask) | (val & mask);
//printf("[%08X] KeyY(%d): ", NDS::GetPC(1), slot); _printhex(KeyY[slot], 16);
if (offset >= 0xC)
{
DeriveNormalKey(slot);
}
}
// utility
void GetModcryptKey(u8* romheader, u8* key)
{
if ((romheader[0x01C] & 0x04) || (romheader[0x1BF] & 0x80))
{
// dev key
memcpy(key, &romheader[0x000], 16);
return;
}
u8 oldkeys[16*3];
memcpy(&oldkeys[16*0], KeyX[0], 16);
memcpy(&oldkeys[16*1], KeyY[0], 16);
memcpy(&oldkeys[16*2], KeyNormal[0], 16);
KeyX[0][8] = romheader[0x00C];
KeyX[0][9] = romheader[0x00D];
KeyX[0][10] = romheader[0x00E];
KeyX[0][11] = romheader[0x00F];
KeyX[0][12] = romheader[0x00F];
KeyX[0][13] = romheader[0x00E];
KeyX[0][14] = romheader[0x00D];
KeyX[0][15] = romheader[0x00C];
memcpy(KeyY[0], &romheader[0x350], 16);
DeriveNormalKey(0);
memcpy(key, KeyNormal[0], 16);
memcpy(KeyX[0], &oldkeys[16*0], 16);
memcpy(KeyY[0], &oldkeys[16*1], 16);
memcpy(KeyNormal[0], &oldkeys[16*2], 16);
}
void ApplyModcrypt(u8* data, u32 len, u8* key, u8* iv)
{
u8 key_rev[16], iv_rev[16];
u8 data_rev[16];
u8 oldkeys[16*2];
memcpy(&oldkeys[16*0], Ctx.RoundKey, 16);
memcpy(&oldkeys[16*1], Ctx.Iv, 16);
Swap16(key_rev, key);
Swap16(iv_rev, iv);
AES_init_ctx_iv(&Ctx, key_rev, iv_rev);
for (u32 i = 0; i < len; i += 16)
{
Swap16(data_rev, &data[i]);
AES_CTR_xcrypt_buffer(&Ctx, data_rev, 16);
Swap16(&data[i], data_rev);
}
memcpy(Ctx.RoundKey, &oldkeys[16*0], 16);
memcpy(Ctx.Iv, &oldkeys[16*1], 16);
}
}

54
src/DSi_AES.h Normal file
View File

@ -0,0 +1,54 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_AES_H
#define DSI_AES_H
#include "types.h"
namespace DSi_AES
{
extern u32 Cnt;
bool Init();
void DeInit();
void Reset();
u32 ReadCnt();
void WriteCnt(u32 val);
void WriteBlkCnt(u32 val);
u32 ReadOutputFIFO();
void WriteInputFIFO(u32 val);
void CheckInputDMA();
void CheckOutputDMA();
void Update();
void WriteIV(u32 offset, u32 val, u32 mask);
void WriteMAC(u32 offset, u32 val, u32 mask);
void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask);
void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask);
void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask);
void GetModcryptKey(u8* romheader, u8* key);
void ApplyModcrypt(u8* data, u32 len, u8* key, u8* iv);
}
#endif // DSI_AES_H

166
src/DSi_Camera.cpp Normal file
View File

@ -0,0 +1,166 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi_Camera.h"
DSi_Camera* DSi_Camera0; // 78 / facing outside
DSi_Camera* DSi_Camera1; // 7A / selfie cam
bool DSi_Camera::Init()
{
DSi_Camera0 = new DSi_Camera(0);
DSi_Camera1 = new DSi_Camera(1);
return true;
}
void DSi_Camera::DeInit()
{
delete DSi_Camera0;
delete DSi_Camera1;
}
void DSi_Camera::Reset()
{
DSi_Camera0->ResetCam();
DSi_Camera1->ResetCam();
}
DSi_Camera::DSi_Camera(u32 num)
{
Num = num;
}
DSi_Camera::~DSi_Camera()
{
//
}
void DSi_Camera::ResetCam()
{
DataPos = 0;
RegAddr = 0;
RegData = 0;
PLLCnt = 0;
StandbyCnt = 0x4029; // checkme
}
void DSi_Camera::Start()
{
}
u8 DSi_Camera::Read(bool last)
{
u8 ret;
if (DataPos < 2)
{
printf("DSi_Camera: WHAT??\n");
ret = 0;
}
else
{
if (DataPos & 0x1)
{
ret = RegData & 0xFF;
RegAddr += 2; // checkme
}
else
{
RegData = ReadReg(RegAddr);
ret = RegData >> 8;
}
}
if (last) DataPos = 0;
else DataPos++;
return ret;
}
void DSi_Camera::Write(u8 val, bool last)
{
if (DataPos < 2)
{
if (DataPos == 0)
RegAddr = val << 8;
else
RegAddr |= val;
if (RegAddr & 0x1) printf("DSi_Camera: !! UNALIGNED REG ADDRESS %04X\n", RegAddr);
}
else
{
if (DataPos & 0x1)
{
RegData |= val;
WriteReg(RegAddr, RegData);
RegAddr += 2; // checkme
}
else
{
RegData = val << 8;
}
}
if (last) DataPos = 0;
else DataPos++;
}
u16 DSi_Camera::ReadReg(u16 addr)
{
switch (addr)
{
case 0x0000: return 0x2280; // chip ID
case 0x0014: return PLLCnt;
case 0x0018: return StandbyCnt;
case 0x301A: return ((~StandbyCnt) & 0x4000) >> 12;
}
printf("DSi_Camera%d: unknown read %04X\n", Num, addr);
return 0;
}
void DSi_Camera::WriteReg(u16 addr, u16 val)
{
switch (addr)
{
case 0x0014:
// shouldn't be instant either?
val &= 0x7FFF;
val |= ((val & 0x0002) << 14);
PLLCnt = val;
return;
case 0x0018:
// TODO: this shouldn't be instant, but uh
val &= 0x003F;
val |= ((val & 0x0001) << 14);
StandbyCnt = val;
return;
}
printf("DSi_Camera%d: unknown write %04X %04X\n", Num, addr, val);
}

58
src/DSi_Camera.h Normal file
View File

@ -0,0 +1,58 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_CAMERA_H
#define DSI_CAMERA_H
#include "types.h"
class DSi_Camera
{
public:
static bool Init();
static void DeInit();
static void Reset();
DSi_Camera(u32 num);
~DSi_Camera();
void ResetCam();
void Start();
u8 Read(bool last);
void Write(u8 val, bool last);
private:
u32 Num;
u32 DataPos;
u32 RegAddr;
u16 RegData;
u16 ReadReg(u16 addr);
void WriteReg(u16 addr, u16 val);
u16 PLLCnt;
u16 StandbyCnt;
};
extern DSi_Camera* DSi_Camera0;
extern DSi_Camera* DSi_Camera1;
#endif // DSI_CAMERA_H

255
src/DSi_I2C.cpp Normal file
View File

@ -0,0 +1,255 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "DSi_I2C.h"
#include "DSi_Camera.h"
namespace DSi_BPTWL
{
u8 Registers[0x100];
u32 CurPos;
bool Init()
{
return true;
}
void DeInit()
{
}
void Reset()
{
CurPos = -1;
memset(Registers, 0x5A, 0x100);
Registers[0x00] = 0x33; // TODO: support others??
Registers[0x01] = 0x00;
Registers[0x02] = 0x50;
Registers[0x10] = 0x00; // power btn
Registers[0x11] = 0x00; // reset
Registers[0x12] = 0x00; // power btn tap
Registers[0x20] = 0x83; // battery
Registers[0x21] = 0x07;
Registers[0x30] = 0x13;
Registers[0x31] = 0x00; // camera power
Registers[0x40] = 0x1F; // volume
Registers[0x41] = 0x04; // backlight
Registers[0x60] = 0x00;
Registers[0x61] = 0x01;
Registers[0x62] = 0x50;
Registers[0x63] = 0x00;
Registers[0x70] = 0x00; // boot flag
Registers[0x71] = 0x00;
Registers[0x72] = 0x00;
Registers[0x73] = 0x00;
Registers[0x74] = 0x00;
Registers[0x75] = 0x00;
Registers[0x76] = 0x00;
Registers[0x77] = 0x00;
Registers[0x80] = 0x10;
Registers[0x81] = 0x64;
}
void Start()
{
//printf("BPTWL: start\n");
}
u8 Read(bool last)
{
if (last)
{
CurPos = -1;
return 0;
}
//printf("BPTWL: read %02X -> %02X\n", CurPos, Registers[CurPos]);
return Registers[CurPos++];
}
void Write(u8 val, bool last)
{
if (last)
{
CurPos = -1;
return;
}
if (CurPos == -1)
{
CurPos = val;
//printf("BPTWL: reg=%02X\n", val);
return;
}
if (CurPos == 0x11 && val == 0x01)
{
printf("BPTWL: soft-reset\n");
val = 0; // checkme
// TODO: soft-reset might need to be scheduled later!
DSi::SoftReset();
CurPos = -1;
return;
}
if (CurPos == 0x11 || CurPos == 0x12 ||
CurPos == 0x21 ||
CurPos == 0x30 || CurPos == 0x31 ||
CurPos == 0x40 || CurPos == 0x31 ||
CurPos == 0x60 || CurPos == 0x63 ||
(CurPos >= 0x70 && CurPos <= 0x77) ||
CurPos == 0x80 || CurPos == 0x81)
{
Registers[CurPos] = val;
}
//printf("BPTWL: write %02X -> %02X\n", CurPos, val);
CurPos++; // CHECKME
}
}
namespace DSi_I2C
{
u8 Cnt;
u8 Data;
u32 Device;
bool Init()
{
if (!DSi_BPTWL::Init()) return false;
if (!DSi_Camera::Init()) return false;
return true;
}
void DeInit()
{
DSi_BPTWL::DeInit();
DSi_Camera::DeInit();
}
void Reset()
{
Cnt = 0;
Data = 0;
Device = -1;
DSi_BPTWL::Reset();
DSi_Camera::Reset();
}
void WriteCnt(u8 val)
{
//printf("I2C: write CNT %02X, %08X\n", val, NDS::GetPC(1));
// TODO: check ACK flag
// TODO: transfer delay
// TODO: IRQ
// TODO: check read/write direction
if (val & (1<<7))
{
bool islast = val & (1<<0);
if (val & (1<<5))
{
// read
val &= 0xF7;
switch (Device)
{
case 0x4A: Data = DSi_BPTWL::Read(islast); break;
case 0x78: Data = DSi_Camera0->Read(islast); break;
case 0x7A: Data = DSi_Camera1->Read(islast); break;
default:
printf("I2C: read on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, 0, islast);
Data = 0xFF;
break;
}
//printf("I2C read, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
}
else
{
// write
val &= 0xE7;
bool ack = true;
if (val & (1<<1))
{
Device = Data & 0xFE;
//printf("I2C: %s start, device=%02X\n", (Data&0x01)?"read":"write", Device);
switch (Device)
{
case 0x4A: DSi_BPTWL::Start(); break;
case 0x78: DSi_Camera0->Start(); break;
case 0x7A: DSi_Camera1->Start(); break;
default:
printf("I2C: %s start on unknown device %02X\n", (Data&0x01)?"read":"write", Device);
ack = false;
break;
}
}
else
{
//printf("I2C write, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
switch (Device)
{
case 0x4A: DSi_BPTWL::Write(Data, islast); break;
case 0x78: DSi_Camera0->Write(Data, islast); break;
case 0x7A: DSi_Camera1->Write(Data, islast); break;
default:
printf("I2C: write on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast);
ack = false;
break;
}
}
if (ack) val |= (1<<4);
}
val &= 0x7F;
}
Cnt = val;
}
u8 ReadData()
{
return Data;
}
void WriteData(u8 val)
{
Data = val;
}
}

41
src/DSi_I2C.h Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_I2C_H
#define DSI_I2C_H
namespace DSi_I2C
{
extern u8 Cnt;
bool Init();
void DeInit();
void Reset();
//void DoSavestate(Savestate* file);
void WriteCnt(u8 val);
u8 ReadData();
void WriteData(u8 val);
//void TransferDone(u32 param);
}
#endif // DSI_I2C_H

341
src/DSi_NDMA.cpp Normal file
View File

@ -0,0 +1,341 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include "NDS.h"
#include "DSi.h"
#include "DSi_NDMA.h"
#include "GPU.h"
#include "DSi_AES.h"
DSi_NDMA::DSi_NDMA(u32 cpu, u32 num)
{
CPU = cpu;
Num = num;
Reset();
}
DSi_NDMA::~DSi_NDMA()
{
}
void DSi_NDMA::Reset()
{
SrcAddr = 0;
DstAddr = 0;
TotalLength = 0;
BlockLength = 0;
SubblockTimer = 0;
FillData = 0;
Cnt = 0;
StartMode = 0;
CurSrcAddr = 0;
CurDstAddr = 0;
SubblockLength = 0;
RemCount = 0;
IterCount = 0;
TotalRemCount = 0;
SrcAddrInc = 0;
DstAddrInc = 0;
Running = false;
InProgress = false;
}
void DSi_NDMA::DoSavestate(Savestate* file)
{
// TODO!
}
void DSi_NDMA::WriteCnt(u32 val)
{
u32 oldcnt = Cnt;
Cnt = val;
if ((!(oldcnt & 0x80000000)) && (val & 0x80000000)) // checkme
{
CurSrcAddr = SrcAddr;
CurDstAddr = DstAddr;
TotalRemCount = TotalLength;
switch ((Cnt >> 10) & 0x3)
{
case 0: DstAddrInc = 1; break;
case 1: DstAddrInc = -1; break;
case 2: DstAddrInc = 0; break;
case 3: DstAddrInc = 1; printf("BAD NDMA DST INC MODE 3\n"); break;
}
switch ((Cnt >> 13) & 0x3)
{
case 0: SrcAddrInc = 1; break;
case 1: SrcAddrInc = -1; break;
case 2: SrcAddrInc = 0; break;
case 3: SrcAddrInc = 0; break; // fill mode
}
StartMode = (Cnt >> 24) & 0x1F;
if (StartMode > 0x10) StartMode = 0x10;
if (CPU == 1) StartMode |= 0x20;
if ((StartMode & 0x1F) == 0x10)
Start();
if (StartMode != 0x10 && StartMode != 0x30 &&
StartMode != 0x04 && StartMode != 0x06 && StartMode != 0x07 && StartMode != 0x08 && StartMode != 0x09 &&
StartMode != 0x24 && StartMode != 0x26 && StartMode != 0x28 && StartMode != 0x29 && StartMode != 0x2A && StartMode != 0x2B)
printf("UNIMPLEMENTED ARM%d NDMA%d START MODE %02X, %08X->%08X LEN=%d BLK=%d CNT=%08X\n",
CPU?7:9, Num, StartMode, SrcAddr, DstAddr, TotalLength, BlockLength, Cnt);
}
}
void DSi_NDMA::Start()
{
if (Running) return;
if (!InProgress)
{
RemCount = BlockLength;
if (!RemCount)
RemCount = 0x1000000;
}
// TODO: how does GXFIFO DMA work with all the block shito?
IterCount = RemCount;
if (((StartMode & 0x1F) != 0x10) && !(Cnt & (1<<29)))
{
if (IterCount > TotalRemCount)
{
IterCount = TotalRemCount;
RemCount = IterCount;
}
}
if (Cnt & (1<<12)) CurDstAddr = DstAddr;
if (Cnt & (1<<15)) CurSrcAddr = SrcAddr;
//printf("ARM%d NDMA%d %08X %02X %08X->%08X %d bytes, total=%d\n", CPU?7:9, Num, Cnt, StartMode, CurSrcAddr, CurDstAddr, RemCount*4, TotalRemCount*4);
//IsGXFIFODMA = (CPU == 0 && (CurSrcAddr>>24) == 0x02 && CurDstAddr == 0x04000400 && DstAddrInc == 0);
// TODO eventually: not stop if we're running code in ITCM
//if (SubblockTimer & 0xFFFF)
// printf("TODO! NDMA SUBBLOCK TIMER: %08X\n", SubblockTimer);
if (NDS::DMAsRunning(CPU))
Running = 1;
else
Running = 2;
InProgress = true;
NDS::StopCPU(CPU, 1<<(Num+4));
}
void DSi_NDMA::Run()
{
if (!Running) return;
if (CPU == 0) return Run9();
else return Run7();
}
void DSi_NDMA::Run9()
{
if (NDS::ARM9Timestamp >= NDS::ARM9Target) return;
Executing = true;
// add NS penalty for first accesses in burst
bool burststart = (Running == 2);
Running = 1;
s32 unitcycles;
//s32 lastcycles = cycles;
bool dofill = ((Cnt >> 13) & 0x3) == 3;
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2];
}
else
{
unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][3] + NDS::ARM9MemTimings[CurDstAddr >> 14][3];
if ((CurSrcAddr >> 24) == (CurDstAddr >> 24))
unitcycles++;
else if ((CurSrcAddr >> 24) == 0x02)
unitcycles--;
/*if (burststart)
{
cycles -= 2;
cycles -= (NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2]);
cycles += unitcycles;
}*/
}
while (IterCount > 0 && !Stall)
{
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
if (dofill)
DSi::ARM9Write32(CurDstAddr, FillData);
else
DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
IterCount--;
RemCount--;
TotalRemCount--;
if (NDS::ARM9Timestamp >= NDS::ARM9Target) break;
}
Executing = false;
Stall = false;
if (RemCount)
{
if (IterCount == 0)
{
Running = 0;
NDS::ResumeCPU(0, 1<<(Num+4));
//if (StartMode == 0x07)
// GPU3D::CheckFIFODMA();
}
return;
}
if ((StartMode & 0x1F) == 0x10) // CHECKME
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num);
}
else if (!(Cnt & (1<<29)))
{
if (TotalRemCount == 0)
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num);
}
}
Running = 0;
InProgress = false;
NDS::ResumeCPU(0, 1<<(Num+4));
}
void DSi_NDMA::Run7()
{
if (NDS::ARM7Timestamp >= NDS::ARM7Target) return;
Executing = true;
// add NS penalty for first accesses in burst
bool burststart = (Running == 2);
Running = 1;
s32 unitcycles;
//s32 lastcycles = cycles;
bool dofill = ((Cnt >> 13) & 0x3) == 3;
if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02)
{
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2];
}
else
{
unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][3] + NDS::ARM7MemTimings[CurDstAddr >> 15][3];
if ((CurSrcAddr >> 23) == (CurDstAddr >> 23))
unitcycles++;
else if ((CurSrcAddr >> 24) == 0x02)
unitcycles--;
/*if (burststart)
{
cycles -= 2;
cycles -= (NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2]);
cycles += unitcycles;
}*/
}
while (IterCount > 0 && !Stall)
{
NDS::ARM7Timestamp += unitcycles;
if (dofill)
DSi::ARM7Write32(CurDstAddr, FillData);
else
DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr));
CurSrcAddr += SrcAddrInc<<2;
CurDstAddr += DstAddrInc<<2;
IterCount--;
RemCount--;
TotalRemCount--;
if (NDS::ARM7Timestamp >= NDS::ARM7Target) break;
}
Executing = false;
Stall = false;
if (RemCount)
{
if (IterCount == 0)
{
Running = 0;
NDS::ResumeCPU(1, 1<<(Num+4));
DSi_AES::CheckInputDMA();
DSi_AES::CheckOutputDMA();
}
return;
}
if ((StartMode & 0x1F) == 0x10) // CHECKME
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num);
}
else if (!(Cnt & (1<<29)))
{
if (TotalRemCount == 0)
{
Cnt &= ~(1<<31);
if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num);
}
}
Running = 0;
InProgress = false;
NDS::ResumeCPU(1, 1<<(Num+4));
DSi_AES::CheckInputDMA();
DSi_AES::CheckOutputDMA();
}

96
src/DSi_NDMA.h Normal file
View File

@ -0,0 +1,96 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_NDMA_H
#define DSI_NDMA_H
#include "types.h"
class DSi_NDMA
{
public:
DSi_NDMA(u32 cpu, u32 num);
~DSi_NDMA();
void Reset();
void DoSavestate(Savestate* file);
void WriteCnt(u32 val);
void Start();
void Run();
void Run9();
void Run7();
bool IsInMode(u32 mode)
{
return ((mode == StartMode) && (Cnt & 0x80000000));
}
bool IsRunning() { return Running!=0; }
void StartIfNeeded(u32 mode)
{
if ((mode == StartMode) && (Cnt & 0x80000000))
Start();
}
void StopIfNeeded(u32 mode)
{
if (mode == StartMode)
Cnt &= ~0x80000000;
}
void StallIfRunning()
{
if (Executing) Stall = true;
}
u32 SrcAddr;
u32 DstAddr;
u32 TotalLength; // total length, when transferring multiple blocks
u32 BlockLength; // length of one transfer
u32 SubblockTimer; // optional delay between subblocks (only in round-robin mode)
u32 FillData;
u32 Cnt;
private:
u32 CPU, Num;
u32 StartMode;
u32 CurSrcAddr;
u32 CurDstAddr;
u32 SubblockLength; // length transferred per run when delay is used
u32 RemCount;
u32 IterCount;
u32 TotalRemCount;
u32 SrcAddrInc;
u32 DstAddrInc;
u32 Running;
bool InProgress;
bool Executing;
bool Stall;
bool IsGXFIFODMA;
};
#endif // DSI_NDMA_H

881
src/DSi_NWifi.cpp Normal file
View File

@ -0,0 +1,881 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <string.h>
#include <stdio.h>
#include "DSi.h"
#include "DSi_NWifi.h"
#include "SPI.h"
const u8 CIS0[256] =
{
0x01, 0x03, 0xD9, 0x01, 0xFF,
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
0x21, 0x02, 0x0C, 0x00,
0x22, 0x04, 0x00, 0x00, 0x08, 0x32,
0x1A, 0x05, 0x01, 0x01, 0x00, 0x02, 0x07,
0x1B, 0x08, 0xC1, 0x41, 0x30, 0x30, 0xFF, 0xFF, 0x32, 0x00,
0x14, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
};
const u8 CIS1[256] =
{
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
0x21, 0x02, 0x0C, 0x00,
0x22, 0x2A, 0x01,
0x01, 0x11,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x08,
0x00, 0x00, 0xFF, 0x80,
0x00, 0x00, 0x00,
0x00, 0x01, 0x0A,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x01,
0x00, 0x01, 0x00, 0x01,
0x80, 0x01, 0x06,
0x81, 0x01, 0x07,
0x82, 0x01, 0xDF,
0xFF,
0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// hax
DSi_NWifi* hax_wifi;
void triggerirq(u32 param)
{
hax_wifi->SetIRQ_F1_Counter(0);
}
DSi_NWifi::DSi_NWifi(DSi_SDHost* host) : DSi_SDDevice(host)
{
TransferCmd = 0xFFFFFFFF;
RemSize = 0;
F0_IRQEnable = 0;
F0_IRQStatus = 0;
F1_IRQEnable = 0; F1_IRQEnable_CPU = 0; F1_IRQEnable_Error = 0; F1_IRQEnable_Counter = 0;
F1_IRQStatus = 0; F1_IRQStatus_CPU = 0; F1_IRQStatus_Error = 0; F1_IRQStatus_Counter = 0;
WindowData = 0;
WindowReadAddr = 0;
WindowWriteAddr = 0;
// TODO: check the actual mailbox size (presumably 0x200)
for (int i = 0; i < 8; i++)
Mailbox[i] = new FIFO<u8>(0x200);
u8* mac = SPI_Firmware::GetWifiMAC();
printf("NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
memset(EEPROM, 0, 0x400);
*(u32*)&EEPROM[0x000] = 0x300;
*(u16*)&EEPROM[0x008] = 0x8348; // TODO: determine properly (country code)
memcpy(&EEPROM[0x00A], mac, 6);
*(u32*)&EEPROM[0x010] = 0x60000000;
memset(&EEPROM[0x03C], 0xFF, 0x70);
memset(&EEPROM[0x140], 0xFF, 0x8);
u16 chk = 0xFFFF;
for (int i = 0; i < 0x300; i+=2)
chk ^= *(u16*)&EEPROM[i];
*(u16*)&EEPROM[0x004] = chk;
EEPROMReady = 0;
BootPhase = 0;
}
DSi_NWifi::~DSi_NWifi()
{
for (int i = 0; i < 8; i++)
delete Mailbox[i];
}
// CHECKME
// can IRQ status bits be set when the corresponding IRQs are disabled in the enable register?
// otherwise, does disabling them clear the status register?
void DSi_NWifi::UpdateIRQ()
{
F0_IRQStatus = 0;
IRQ = false;
if (F1_IRQStatus & F1_IRQEnable)
F0_IRQStatus |= (1<<1);
if (F0_IRQEnable & (1<<0))
{
if (F0_IRQStatus & F0_IRQEnable)
IRQ = true;
}
Host->SetCardIRQ();
}
void DSi_NWifi::UpdateIRQ_F1()
{
F1_IRQStatus = 0;
if (!Mailbox[4]->IsEmpty()) F1_IRQStatus |= (1<<0);
if (!Mailbox[5]->IsEmpty()) F1_IRQStatus |= (1<<1);
if (!Mailbox[6]->IsEmpty()) F1_IRQStatus |= (1<<2);
if (!Mailbox[7]->IsEmpty()) F1_IRQStatus |= (1<<3);
if (F1_IRQStatus_Counter & F1_IRQEnable_Counter) F1_IRQStatus |= (1<<4);
if (F1_IRQStatus_CPU & F1_IRQEnable_CPU) F1_IRQStatus |= (1<<6);
if (F1_IRQStatus_Error & F1_IRQEnable_Error) F1_IRQStatus |= (1<<7);
UpdateIRQ();
}
void DSi_NWifi::SetIRQ_F1_Counter(u32 n)
{
F1_IRQStatus_Counter |= (1<<n);
UpdateIRQ_F1();
}
void DSi_NWifi::ClearIRQ_F1_Counter(u32 n)
{
F1_IRQStatus_Counter &= ~(1<<n);
UpdateIRQ_F1();
}
void DSi_NWifi::SetIRQ_F1_CPU(u32 n)
{
F1_IRQStatus_CPU |= (1<<n);
UpdateIRQ_F1();
}
u8 DSi_NWifi::F0_Read(u32 addr)
{
switch (addr)
{
case 0x00000: return 0x11;
case 0x00001: return 0x00;
case 0x00002: return 0x02; // writable??
case 0x00003: return 0x02;
case 0x00004: return F0_IRQEnable;
case 0x00005: return F0_IRQStatus;
case 0x00008: return 0x17;
case 0x00009: return 0x00;
case 0x0000A: return 0x10;
case 0x0000B: return 0x00;
case 0x00012: return 0x03;
case 0x00109: return 0x00;
case 0x0010A: return 0x11;
case 0x0010B: return 0x00;
}
if (addr >= 0x01000 && addr < 0x01100)
{
return CIS0[addr & 0xFF];
}
if (addr >= 0x01100 && addr < 0x01200)
{
return CIS1[addr & 0xFF];
}
printf("NWIFI: unknown func0 read %05X\n", addr);
return 0;
}
void DSi_NWifi::F0_Write(u32 addr, u8 val)
{
switch (addr)
{
case 0x00004:
F0_IRQEnable = val;
UpdateIRQ();
return;
}
printf("NWIFI: unknown func0 write %05X %02X\n", addr, val);
}
u8 DSi_NWifi::F1_Read(u32 addr)
{
if (addr < 0x100)
{
u8 ret = Mailbox[4]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x200)
{
u8 ret = Mailbox[5]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x300)
{
u8 ret = Mailbox[6]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x400)
{
u8 ret = Mailbox[7]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x800)
{
switch (addr)
{
case 0x00400: return F1_IRQStatus;
case 0x00401: return F1_IRQStatus_CPU;
case 0x00402: return F1_IRQStatus_Error;
case 0x00403: return F1_IRQStatus_Counter;
case 0x00405:
{
u8 ret = 0;
if (Mailbox[4]->Level() >= 4) ret |= (1<<0);
if (Mailbox[5]->Level() >= 4) ret |= (1<<1);
if (Mailbox[6]->Level() >= 4) ret |= (1<<2);
if (Mailbox[7]->Level() >= 4) ret |= (1<<3);
return ret;
}
case 0x00408: return Mailbox[4]->Peek(0);
case 0x00409: return Mailbox[4]->Peek(1);
case 0x0040A: return Mailbox[4]->Peek(2);
case 0x0040B: return Mailbox[4]->Peek(3);
case 0x00418: return F1_IRQEnable;
case 0x00419: return F1_IRQEnable_CPU;
case 0x0041A: return F1_IRQEnable_Error;
case 0x0041B: return F1_IRQEnable_Counter;
// GROSS FUCKING HACK
case 0x00440: ClearIRQ_F1_Counter(0); return 0;
case 0x00450: return 1; // HAX!!
case 0x00474: return WindowData & 0xFF;
case 0x00475: return (WindowData >> 8) & 0xFF;
case 0x00476: return (WindowData >> 16) & 0xFF;
case 0x00477: return WindowData >> 24;
}
}
else if (addr < 0x1000)
{
u8 ret = Mailbox[4]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x1800)
{
u8 ret = Mailbox[5]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x2000)
{
u8 ret = Mailbox[6]->Read();
UpdateIRQ_F1();
return ret;
}
else if (addr < 0x2800)
{
u8 ret = Mailbox[7]->Read();
UpdateIRQ_F1();
return ret;
}
else
{
u8 ret = Mailbox[4]->Read();
UpdateIRQ_F1();
return ret;
}
printf("NWIFI: unknown func1 read %05X\n", addr);
return 0;
}
void DSi_NWifi::F1_Write(u32 addr, u8 val)
{
if (addr < 0x100)
{
if (Mailbox[0]->IsFull()) printf("!!! NWIFI: MBOX0 FULL\n");
Mailbox[0]->Write(val);
if (addr == 0xFF) HandleCommand();
UpdateIRQ_F1();
return;
}
else if (addr < 0x200)
{
if (Mailbox[1]->IsFull()) printf("!!! NWIFI: MBOX1 FULL\n");
Mailbox[1]->Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x300)
{
if (Mailbox[2]->IsFull()) printf("!!! NWIFI: MBOX2 FULL\n");
Mailbox[2]->Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x400)
{
if (Mailbox[3]->IsFull()) printf("!!! NWIFI: MBOX3 FULL\n");
Mailbox[3]->Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x800)
{
switch (addr)
{
case 0x00418: F1_IRQEnable = val; UpdateIRQ_F1(); return;
case 0x00419: F1_IRQEnable_CPU = val; UpdateIRQ_F1(); return;
case 0x0041A: F1_IRQEnable_Error = val; UpdateIRQ_F1(); return;
case 0x0041B: F1_IRQEnable_Counter = val; UpdateIRQ_F1(); return;
// GROSS FUCKING HACK
case 0x00440: ClearIRQ_F1_Counter(0); return;
case 0x00474: WindowData = (WindowData & 0xFFFFFF00) | val; return;
case 0x00475: WindowData = (WindowData & 0xFFFF00FF) | (val << 8); return;
case 0x00476: WindowData = (WindowData & 0xFF00FFFF) | (val << 16); return;
case 0x00477: WindowData = (WindowData & 0x00FFFFFF) | (val << 24); return;
case 0x00478:
WindowWriteAddr = (WindowWriteAddr & 0xFFFFFF00) | val;
WindowWrite(WindowWriteAddr, WindowData);
return;
case 0x00479: WindowWriteAddr = (WindowWriteAddr & 0xFFFF00FF) | (val << 8); return;
case 0x0047A: WindowWriteAddr = (WindowWriteAddr & 0xFF00FFFF) | (val << 16); return;
case 0x0047B: WindowWriteAddr = (WindowWriteAddr & 0x00FFFFFF) | (val << 24); return;
case 0x0047C:
WindowReadAddr = (WindowReadAddr & 0xFFFFFF00) | val;
WindowData = WindowRead(WindowReadAddr);
return;
case 0x0047D: WindowReadAddr = (WindowReadAddr & 0xFFFF00FF) | (val << 8); return;
case 0x0047E: WindowReadAddr = (WindowReadAddr & 0xFF00FFFF) | (val << 16); return;
case 0x0047F: WindowReadAddr = (WindowReadAddr & 0x00FFFFFF) | (val << 24); return;
}
}
else if (addr < 0x1000)
{
if (Mailbox[0]->IsFull()) printf("!!! NWIFI: MBOX0 FULL\n");
Mailbox[0]->Write(val);
if (addr == 0xFFF) HandleCommand();
UpdateIRQ_F1();
return;
}
else if (addr < 0x1800)
{
if (Mailbox[1]->IsFull()) printf("!!! NWIFI: MBOX1 FULL\n");
Mailbox[1]->Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x2000)
{
if (Mailbox[2]->IsFull()) printf("!!! NWIFI: MBOX2 FULL\n");
Mailbox[2]->Write(val);
UpdateIRQ_F1();
return;
}
else if (addr < 0x2800)
{
if (Mailbox[3]->IsFull()) printf("!!! NWIFI: MBOX3 FULL\n");
Mailbox[3]->Write(val);
UpdateIRQ_F1();
return;
}
else
{
if (Mailbox[0]->IsFull()) printf("!!! NWIFI: MBOX0 FULL\n");
Mailbox[0]->Write(val);
if (addr == 0x3FFF) HandleCommand(); // CHECKME
UpdateIRQ_F1();
return;
}
printf("NWIFI: unknown func1 write %05X %02X\n", addr, val);
}
u8 DSi_NWifi::SDIO_Read(u32 func, u32 addr)
{
switch (func)
{
case 0: return F0_Read(addr);
case 1: return F1_Read(addr);
}
printf("NWIFI: unknown SDIO read %d %05X\n", func, addr);
return 0;
}
void DSi_NWifi::SDIO_Write(u32 func, u32 addr, u8 val)
{
switch (func)
{
case 0: return F0_Write(addr, val);
case 1: return F1_Write(addr, val);
}
printf("NWIFI: unknown SDIO write %d %05X %02X\n", func, addr, val);
}
void DSi_NWifi::SendCMD(u8 cmd, u32 param)
{
switch (cmd)
{
case 12:
// stop command
// CHECKME: does the SDIO controller actually send those??
// DSi firmware sets it to send them
return;
case 52: // IO_RW_DIRECT
{
u32 func = (param >> 28) & 0x7;
u32 addr = (param >> 9) & 0x1FFFF;
if (param & (1<<31))
{
// write
u8 val = param & 0xFF;
SDIO_Write(func, addr, val);
if (param & (1<<27))
val = SDIO_Read(func, addr); // checkme
Host->SendResponse(val | 0x1000, true);
}
else
{
// read
u8 val = SDIO_Read(func, addr);
Host->SendResponse(val | 0x1000, true);
}
}
return;
case 53: // IO_RW_EXTENDED
{
u32 addr = (param >> 9) & 0x1FFFF;
TransferCmd = param;
TransferAddr = addr;
if (param & (1<<27))
{
RemSize = (param & 0x1FF) << 9; // checkme
}
else
{
RemSize = (param & 0x1FF);
if (!RemSize) RemSize = 0x200;
}
if (param & (1<<31))
{
// write
WriteBlock();
Host->SendResponse(0x1000, true);
}
else
{
// read
ReadBlock();
Host->SendResponse(0x1000, true);
}
}
return;
}
printf("NWIFI: unknown CMD %d %08X\n", cmd, param);
}
void DSi_NWifi::SendACMD(u8 cmd, u32 param)
{
printf("NWIFI: unknown ACMD %d %08X\n", cmd, param);
}
void DSi_NWifi::ContinueTransfer()
{
if (TransferCmd & (1<<31))
WriteBlock();
else
ReadBlock();
}
void DSi_NWifi::ReadBlock()
{
u32 func = (TransferCmd >> 28) & 0x7;
u32 len = (TransferCmd & (1<<27)) ? 0x200 : RemSize;
len = Host->GetTransferrableLen(len);
u8 data[0x200];
for (u32 i = 0; i < len; i++)
{
data[i] = SDIO_Read(func, TransferAddr);
if (TransferCmd & (1<<26))
{
TransferAddr++;
TransferAddr &= 0x1FFFF; // checkme
}
}
len = Host->DataRX(data, len);
if (RemSize > 0)
{
RemSize -= len;
if (RemSize == 0)
{
// TODO?
}
}
}
void DSi_NWifi::WriteBlock()
{
u32 func = (TransferCmd >> 28) & 0x7;
u32 len = (TransferCmd & (1<<27)) ? 0x200 : RemSize;
len = Host->GetTransferrableLen(len);
u8 data[0x200];
if (len = Host->DataTX(data, len))
{
for (u32 i = 0; i < len; i++)
{
SDIO_Write(func, TransferAddr, data[i]);
if (TransferCmd & (1<<26))
{
TransferAddr++;
TransferAddr &= 0x1FFFF; // checkme
}
}
if (RemSize > 0)
{
RemSize -= len;
if (RemSize == 0)
{
// TODO?
}
}
}
}
void DSi_NWifi::HandleCommand()
{
switch (BootPhase)
{
case 0: return BMI_Command();
case 1: return WMI_Command();
}
}
void DSi_NWifi::BMI_Command()
{
// HLE command handling stub
u32 cmd = MB_Read32(0);
switch (cmd)
{
case 0x01: // BMI_DONE
{
printf("BMI_DONE\n");
EEPROMReady = 1; // GROSS FUCKING HACK
u8 ready_msg[8] = {0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00};
SendWMIFrame(ready_msg, 8, 0, 0x00, 0x0000);
BootPhase = 1;
}
return;
case 0x03: // BMI_WRITE_MEMORY
{
u32 addr = MB_Read32(0);
u32 len = MB_Read32(0);
printf("BMI mem write %08X %08X\n", addr, len);
for (int i = 0; i < len; i++)
{
u8 val = Mailbox[0]->Read();
// TODO: do something with it!!
}
}
return;
case 0x04: // BMI_EXECUTE
{
u32 entry = MB_Read32(0);
u32 arg = MB_Read32(0);
printf("BMI_EXECUTE %08X %08X\n", entry, arg);
}
return;
case 0x06: // BMI_READ_SOC_REGISTER
{
u32 addr = MB_Read32(0);
u32 val = WindowRead(addr);
MB_Write32(4, val);
}
return;
case 0x07: // BMI_WRITE_SOC_REGISTER
{
u32 addr = MB_Read32(0);
u32 val = MB_Read32(0);
WindowWrite(addr, val);
}
return;
case 0x08: // BMI_GET_TARGET_ID
MB_Write32(4, 0xFFFFFFFF);
MB_Write32(4, 0x0000000C);
//MB_Write32(4, 0x20000118);
MB_Write32(4, 0x23000024); // ROM version (TODO: how to determine correct one?)
MB_Write32(4, 0x00000002);
return;
case 0x0D: // BMI_LZ_STREAM_START
{
u32 addr = MB_Read32(0);
printf("BMI_LZ_STREAM_START %08X\n", addr);
}
return;
case 0x0E: // BMI_LZ_DATA
{
u32 len = MB_Read32(0);
printf("BMI LZ write %08X\n", len);
//FILE* f = fopen("wififirm.bin", "ab");
for (int i = 0; i < len; i++)
{
u8 val = Mailbox[0]->Read();
// TODO: do something with it!!
//fwrite(&val, 1, 1, f);
}
//fclose(f);
}
return;
default:
printf("unknown BMI command %08X\n", cmd);
return;
}
}
void DSi_NWifi::WMI_Command()
{
// HLE command handling stub
u16 h0 = MB_Read16(0);
u16 len = MB_Read16(0);
u16 h2 = MB_Read16(0);
u16 cmd = MB_Read16(0);
printf("WMI: cmd %04X\n", cmd);
switch (cmd)
{
case 0x0002: // service connect
{
u16 svc_id = MB_Read16(0);
u16 conn_flags = MB_Read16(0);
u8 svc_resp[10];
*(u16*)&svc_resp[0] = 0x0003;
*(u16*)&svc_resp[2] = svc_id;
svc_resp[4] = 0;
svc_resp[5] = (svc_id & 0xFF) + 1;
*(u16*)&svc_resp[6] = 0x0001;
*(u16*)&svc_resp[8] = 0x0001;
SendWMIFrame(svc_resp, 10, 0, 0x00, 0x0000);
}
break;
case 0x0004: // setup complete
{
u8 ready_evt[14];
memset(ready_evt, 0, 14);
*(u16*)&ready_evt[0] = 0x1001;
memcpy(&ready_evt[2], SPI_Firmware::GetWifiMAC(), 6);
ready_evt[8] = 0x02;
*(u32*)&ready_evt[10] = 0x23000024;
// ctrl[0] = trailer size
// trailer[1] = trailer extra size
// trailer[0] = trailer type???
SendWMIFrame(ready_evt, 14, 1, 0x00, 0x0000);
}
break;
default:
printf("unknown WMI command %04X\n", cmd);
break;
}
MB_Drain(0);
}
void DSi_NWifi::SendWMIFrame(u8* data, u32 len, u8 ep, u8 flags, u16 ctrl)
{
u32 wlen = 0;
Mailbox[4]->Write(ep); // eid
Mailbox[4]->Write(flags); // flags
MB_Write16(4, len); // payload length
MB_Write16(4, ctrl); // ctrl
wlen += 6;
for (int i = 0; i < len; i++)
{
Mailbox[4]->Write(data[i]);
wlen++;
}
for (; wlen & 0x7F; wlen++)
Mailbox[4]->Write(0);
}
u32 DSi_NWifi::WindowRead(u32 addr)
{
printf("NWifi: window read %08X\n", addr);
if ((addr & 0xFFFF00) == 0x520000)
{
// RAM host interest area
// TODO: different base based on hardware version
switch (addr & 0xFF)
{
case 0x54:
// base address of EEPROM data
// TODO find what the actual address is!
return 0x1FFC00;
case 0x58: return EEPROMReady; // hax
}
return 0;
}
// hax
if ((addr & 0x1FFC00) == 0x1FFC00)
{
return *(u32*)&EEPROM[addr & 0x3FF];
}
switch (addr)
{
case 0x40EC: // chip ID
// 0D000000 / 0D000001 == AR6013
// TODO: check firmware.bin to determine the correct value
return 0x0D000001;
// SOC_RESET_CAUSE
case 0x40C0: return 2;
}
return 0;
}
void DSi_NWifi::WindowWrite(u32 addr, u32 val)
{
printf("NWifi: window write %08X %08X\n", addr, val);
}

121
src/DSi_NWifi.h Normal file
View File

@ -0,0 +1,121 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_NWIFI_H
#define DSI_NWIFI_H
#include "DSi_SD.h"
#include "FIFO.h"
class DSi_NWifi : public DSi_SDDevice
{
public:
DSi_NWifi(DSi_SDHost* host);
~DSi_NWifi();
void SendCMD(u8 cmd, u32 param);
void SendACMD(u8 cmd, u32 param);
void ContinueTransfer();
void SetIRQ_F1_Counter(u32 n);
private:
u32 TransferCmd;
u32 TransferAddr;
u32 RemSize;
void UpdateIRQ();
void UpdateIRQ_F1();
//void SetIRQ_F1_Counter(u32 n);
void ClearIRQ_F1_Counter(u32 n);
void SetIRQ_F1_CPU(u32 n);
u8 F0_Read(u32 addr);
void F0_Write(u32 addr, u8 val);
u8 F1_Read(u32 addr);
void F1_Write(u32 addr, u8 val);
u8 SDIO_Read(u32 func, u32 addr);
void SDIO_Write(u32 func, u32 addr, u8 val);
void ReadBlock();
void WriteBlock();
void HandleCommand();
void BMI_Command();
void WMI_Command();
void SendWMIFrame(u8* data, u32 len, u8 ep, u8 flags, u16 ctrl);
u32 WindowRead(u32 addr);
void WindowWrite(u32 addr, u32 val);
u16 MB_Read16(int n)
{
u16 ret = Mailbox[n]->Read();
ret |= (Mailbox[n]->Read() << 8);
return ret;
}
void MB_Write16(int n, u16 val)
{
Mailbox[n]->Write(val & 0xFF); val >>= 8;
Mailbox[n]->Write(val & 0xFF);
}
u32 MB_Read32(int n)
{
u32 ret = Mailbox[n]->Read();
ret |= (Mailbox[n]->Read() << 8);
ret |= (Mailbox[n]->Read() << 16);
ret |= (Mailbox[n]->Read() << 24);
return ret;
}
void MB_Write32(int n, u32 val)
{
Mailbox[n]->Write(val & 0xFF); val >>= 8;
Mailbox[n]->Write(val & 0xFF); val >>= 8;
Mailbox[n]->Write(val & 0xFF); val >>= 8;
Mailbox[n]->Write(val & 0xFF);
}
void MB_Drain(int n)
{
while (!Mailbox[n]->IsEmpty()) Mailbox[n]->Read();
}
FIFO<u8>* Mailbox[8];
u8 F0_IRQEnable;
u8 F0_IRQStatus;
u8 F1_IRQEnable, F1_IRQEnable_CPU, F1_IRQEnable_Error, F1_IRQEnable_Counter;
u8 F1_IRQStatus, F1_IRQStatus_CPU, F1_IRQStatus_Error, F1_IRQStatus_Counter;
u32 WindowData, WindowReadAddr, WindowWriteAddr;
u8 EEPROM[0x400];
u32 EEPROMReady;
u32 BootPhase;
};
#endif // DSI_NWIFI_H

934
src/DSi_SD.cpp Normal file
View File

@ -0,0 +1,934 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "DSi_SD.h"
#include "DSi_NWifi.h"
#include "Platform.h"
// observed IRQ behavior during transfers
//
// during reads:
// * bit23 is cleared during the first block, always set otherwise. weird
// * bit24 (RXRDY) gets set when the FIFO is full
//
// during reads with FIFO32:
// * FIFO16 drains directly into FIFO32
// * when bit24 is set, FIFO32 is already full (with contents from the other FIFO)
// * reading from an empty FIFO just wraps around (and sets bit21)
// * FIFO32 starts filling when bit24 would be set?
//
//
// TX:
// * when sending command, if current FIFO full
// * upon ContinueTransfer(), if current FIFO full
// * -> upon DataTX() if current FIFO full
// * when filling FIFO
#define SD_DESC Num?"SDIO":"SD/MMC"
DSi_SDHost::DSi_SDHost(u32 num)
{
Num = num;
DataFIFO[0] = new FIFO<u16>(0x100);
DataFIFO[1] = new FIFO<u16>(0x100);
DataFIFO32 = new FIFO<u32>(0x80);
Ports[0] = NULL;
Ports[1] = NULL;
}
DSi_SDHost::~DSi_SDHost()
{
delete DataFIFO[0];
delete DataFIFO[1];
delete DataFIFO32;
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
}
void DSi_SDHost::Reset()
{
if (Num == 0)
{
PortSelect = 0x0200; // CHECKME
}
else
{
PortSelect = 0x0100; // CHECKME
}
SoftReset = 0x0007; // CHECKME
SDClock = 0;
SDOption = 0;
Command = 0;
Param = 0;
memset(ResponseBuffer, 0, sizeof(ResponseBuffer));
DataFIFO[0]->Clear();
DataFIFO[1]->Clear();
CurFIFO = 0;
DataFIFO32->Clear();
IRQStatus = 0;
IRQMask = 0x8B7F031D;
CardIRQStatus = 0;
CardIRQMask = 0xC007;
CardIRQCtl = 0;
DataCtl = 0;
Data32IRQ = 0;
DataMode = 0;
BlockCount16 = 0; BlockCount32 = 0; BlockCountInternal = 0;
BlockLen16 = 0; BlockLen32 = 0;
StopAction = 0;
TXReq = false;
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
Ports[0] = NULL;
Ports[1] = NULL;
if (Num == 0)
{
// TODO: eventually pull from host filesystem
/*DSi_MMCStorage* sd = new DSi_MMCStorage(this, false, "sd.bin");
u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
sd->SetCID(sd_cid);*/
DSi_MMCStorage* sd = NULL;
DSi_MMCStorage* mmc = new DSi_MMCStorage(this, true, "nand.bin");
mmc->SetCID(DSi::eMMC_CID);
Ports[0] = sd;
Ports[1] = mmc;
}
else
{
DSi_NWifi* nwifi = new DSi_NWifi(this);
Ports[0] = nwifi;
}
}
void DSi_SDHost::DoSavestate(Savestate* file)
{
// TODO!
}
void DSi_SDHost::UpdateData32IRQ()
{
if (DataMode == 0) return;
u32 oldflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2);
oldflags &= (Data32IRQ >> 11);
Data32IRQ &= ~0x0300;
if (DataFIFO32->Level() >= (BlockLen32>>2)) Data32IRQ |= (1<<8);
if (!DataFIFO32->IsEmpty()) Data32IRQ |= (1<<9);
u32 newflags = ((Data32IRQ >> 8) & 0x1) | (((~Data32IRQ) >> 8) & 0x2);
newflags &= (Data32IRQ >> 11);
if ((oldflags == 0) && (newflags != 0))
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
}
void DSi_SDHost::ClearIRQ(u32 irq)
{
IRQStatus &= ~(1<<irq);
}
void DSi_SDHost::SetIRQ(u32 irq)
{
u32 oldflags = IRQStatus & ~IRQMask;
IRQStatus |= (1<<irq);
u32 newflags = IRQStatus & ~IRQMask;
if ((oldflags == 0) && (newflags != 0))
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
}
void DSi_SDHost::UpdateIRQ(u32 oldmask)
{
u32 oldflags = IRQStatus & ~oldmask;
u32 newflags = IRQStatus & ~IRQMask;
if ((oldflags == 0) && (newflags != 0))
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
}
void DSi_SDHost::SetCardIRQ()
{
if (!(CardIRQCtl & (1<<0))) return;
u16 oldflags = CardIRQStatus & ~CardIRQMask;
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
if (dev->IRQ) CardIRQStatus |= (1<<0);
else CardIRQStatus &= ~(1<<0);
u16 newflags = CardIRQStatus & ~CardIRQMask;
if ((oldflags == 0) && (newflags != 0)) // checkme
{
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC);
NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1);
}
}
void DSi_SDHost::SendResponse(u32 val, bool last)
{
*(u32*)&ResponseBuffer[6] = *(u32*)&ResponseBuffer[4];
*(u32*)&ResponseBuffer[4] = *(u32*)&ResponseBuffer[2];
*(u32*)&ResponseBuffer[2] = *(u32*)&ResponseBuffer[0];
*(u32*)&ResponseBuffer[0] = val;
if (last) SetIRQ(0);
}
void DSi_SDHost::FinishRX(u32 param)
{
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
host->CheckSwapFIFO();
if (host->DataMode == 1)
host->UpdateFIFO32();
else
host->SetIRQ(24);
}
u32 DSi_SDHost::DataRX(u8* data, u32 len)
{
if (len != BlockLen16) { printf("!! BAD BLOCKLEN\n"); len = BlockLen16; }
bool last = (BlockCountInternal == 0);
u32 f = CurFIFO ^ 1;
for (u32 i = 0; i < len; i += 2)
DataFIFO[f]->Write(*(u16*)&data[i]);
//CurFIFO = f;
//SetIRQ(24);
// TODO: determine what the delay should be!
// for now, this is a placeholder
// we need a delay because DSi boot2 will send a command and then wait for IRQ0
// but if IRQ24 is thrown instantly, the handler clears IRQ0 before the
// send-command function starts polling IRQ status
u32 param = Num | (last << 1);
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
false, 512, FinishRX, param);
return len;
}
void DSi_SDHost::FinishTX(u32 param)
{
DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC;
DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1];
if (host->BlockCountInternal == 0)
{
if (host->StopAction & (1<<8))
{
if (dev) dev->SendCMD(12, 0);
}
// CHECKME: presumably IRQ2 should not trigger here, but rather
// when the data transfer is done
//SetIRQ(0);
host->SetIRQ(2);
host->TXReq = false;
}
else
{
if (dev) dev->ContinueTransfer();
}
}
u32 DSi_SDHost::DataTX(u8* data, u32 len)
{
TXReq = true;
u32 f = CurFIFO;
if (DataMode == 1)
{
if ((DataFIFO32->Level() << 2) < len)
{
if (DataFIFO32->IsEmpty())
{
SetIRQ(25);
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
}
return 0;
}
// drain FIFO32 into FIFO16
if (!DataFIFO[f]->IsEmpty()) printf("VERY BAD!! TRYING TO DRAIN FIFO32 INTO FIFO16 BUT IT CONTAINS SHIT ALREADY\n");
for (;;)
{
u32 f = CurFIFO;
if ((DataFIFO[f]->Level() << 1) >= BlockLen16) break;
if (DataFIFO32->IsEmpty()) break;
u32 val = DataFIFO32->Read();
DataFIFO[f]->Write(val & 0xFFFF);
DataFIFO[f]->Write(val >> 16);
}
UpdateData32IRQ();
if (BlockCount32 > 1)
BlockCount32--;
}
else
{
if ((DataFIFO[f]->Level() << 1) < len)
{
if (DataFIFO[f]->IsEmpty()) SetIRQ(25);
return 0;
}
}
for (u32 i = 0; i < len; i += 2)
*(u16*)&data[i] = DataFIFO[f]->Read();
CurFIFO ^= 1;
BlockCountInternal--;
NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer,
false, 512, FinishTX, Num);
return len;
}
u32 DSi_SDHost::GetTransferrableLen(u32 len)
{
if (len > BlockLen16) len = BlockLen16; // checkme
return len;
}
void DSi_SDHost::CheckRX()
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
CheckSwapFIFO();
if (BlockCountInternal <= 1)
{
if (StopAction & (1<<8))
{
if (dev) dev->SendCMD(12, 0);
}
// CHECKME: presumably IRQ2 should not trigger here, but rather
// when the data transfer is done
//SetIRQ(0);
SetIRQ(2);
}
else
{
BlockCountInternal--;
if (dev) dev->ContinueTransfer();
}
}
void DSi_SDHost::CheckTX()
{
if (!TXReq) return;
if (DataMode == 1)
{
if ((DataFIFO32->Level() << 2) < BlockLen32)
return;
}
else
{
u32 f = CurFIFO;
if ((DataFIFO[f]->Level() << 1) < BlockLen16)
return;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
if (dev) dev->ContinueTransfer();
}
u16 DSi_SDHost::Read(u32 addr)
{
switch (addr & 0x1FF)
{
case 0x000: return Command;
case 0x002: return PortSelect & 0x030F;
case 0x004: return Param & 0xFFFF;
case 0x006: return Param >> 16;
case 0x008: return StopAction;
case 0x00A: return BlockCount16;
case 0x00C: return ResponseBuffer[0];
case 0x00E: return ResponseBuffer[1];
case 0x010: return ResponseBuffer[2];
case 0x012: return ResponseBuffer[3];
case 0x014: return ResponseBuffer[4];
case 0x016: return ResponseBuffer[5];
case 0x018: return ResponseBuffer[6];
case 0x01A: return ResponseBuffer[7];
case 0x01C:
{
u16 ret = (IRQStatus & 0x031D);
if (!Num)
{
if (Ports[0]) // basic check of whether the SD card is inserted
ret |= 0x0030;
else
ret |= 0x0008;
}
else
{
// SDIO wifi is always inserted, I guess
ret |= 0x0030;
}
return ret;
}
case 0x01E: return ((IRQStatus >> 16) & 0x8B7F);
case 0x020: return IRQMask & 0x031D;
case 0x022: return (IRQMask >> 16) & 0x8B7F;
case 0x024: return SDClock;
case 0x026: return BlockLen16;
case 0x028: return SDOption;
case 0x02C: return 0; // TODO
case 0x034: return CardIRQCtl;
case 0x036: return CardIRQStatus;
case 0x038: return CardIRQMask;
case 0x030: return ReadFIFO16();
case 0x0D8: return DataCtl;
case 0x0E0: return SoftReset;
case 0x0F6: return 0; // MMC write protect (always 0)
case 0x100: return Data32IRQ;
case 0x104: return BlockLen32;
case 0x108: return BlockCount32;
}
printf("unknown %s read %08X @ %08X\n", SD_DESC, addr, NDS::GetPC(1));
return 0;
}
u16 DSi_SDHost::ReadFIFO16()
{
u32 f = CurFIFO;
if (DataFIFO[f]->IsEmpty())
{
// TODO
// on hardware it seems to wrap around. underflow bit is set upon the first 'empty' read.
return 0;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u16 ret = DataFIFO[f]->Read();
if (DataFIFO[f]->IsEmpty())
{
CheckRX();
}
return ret;
}
u32 DSi_SDHost::ReadFIFO32()
{
if (DataMode != 1) return 0;
if (DataFIFO32->IsEmpty())
{
// TODO
return 0;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 ret = DataFIFO32->Read();
if (DataFIFO32->IsEmpty())
{
CheckRX();
}
UpdateData32IRQ();
return ret;
}
void DSi_SDHost::Write(u32 addr, u16 val)
{
switch (addr & 0x1FF)
{
case 0x000:
{
Command = val;
u8 cmd = Command & 0x3F;
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
if (dev)
{
// CHECKME
// "Setting Command Type to "ACMD" is automatically sending an APP_CMD prefix prior to the command number"
// except DSi boot2 manually sends an APP_CMD prefix AND sets the next command to be ACMD
switch ((Command >> 6) & 0x3)
{
case 0: dev->SendCMD(cmd, Param); break;
case 1: /*dev->SendCMD(55, 0);*/ dev->SendCMD(cmd, Param); break;
default:
printf("%s: unknown command type %d, %02X %08X\n", SD_DESC, (Command>>6)&0x3, cmd, Param);
break;
}
}
else printf("%s: SENDING CMD %04X TO NULL DEVICE\n", SD_DESC, val);
}
return;
case 0x002: PortSelect = (val & 0x040F) | (PortSelect & 0x0300); return;
case 0x004: Param = (Param & 0xFFFF0000) | val; return;
case 0x006: Param = (Param & 0x0000FFFF) | (val << 16); return;
case 0x008: StopAction = val & 0x0101; return;
case 0x00A: BlockCount16 = val; BlockCountInternal = val; return;
case 0x01C: IRQStatus &= (val | 0xFFFF0000); return;
case 0x01E: IRQStatus &= ((val << 16) | 0xFFFF); return;
case 0x020:
{
u32 oldmask = IRQMask;
IRQMask = (IRQMask & 0x8B7F0000) | (val & 0x031D);
UpdateIRQ(oldmask);
}
return;
case 0x022:
{
u32 oldmask = IRQMask;
IRQMask = (IRQMask & 0x0000031D) | ((val & 0x8B7F) << 16);
UpdateIRQ(oldmask);
if (!DataFIFO[CurFIFO]->IsEmpty()) SetIRQ(24); // checkme
if (DataFIFO[CurFIFO]->IsEmpty()) SetIRQ(25); // checkme
}
return;
case 0x024: SDClock = val & 0x03FF; return;
case 0x026:
BlockLen16 = val & 0x03FF;
if (BlockLen16 > 0x200) BlockLen16 = 0x200;
return;
case 0x028: SDOption = val & 0xC1FF; return;
case 0x030: WriteFIFO16(val); return;
case 0x034:
CardIRQCtl = val & 0x0305;
SetCardIRQ();
return;
case 0x036:
CardIRQStatus &= val;
return;
case 0x038:
CardIRQMask = val & 0xC007;
SetCardIRQ();
return;
case 0x0D8:
DataCtl = (val & 0x0022);
DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1);
return;
case 0x0E0:
if ((SoftReset & 0x0001) && !(val & 0x0001))
{
printf("%s: RESET\n", SD_DESC);
StopAction = 0;
memset(ResponseBuffer, 0, sizeof(ResponseBuffer));
IRQStatus = 0;
// TODO: ERROR_DETAIL_STATUS
SDClock &= ~0x0500;
SDOption = 0x40EE;
// TODO: CARD_IRQ_STAT
// TODO: FIFO16 shit
}
SoftReset = 0x0006 | (val & 0x0001);
return;
case 0x100:
Data32IRQ = (val & 0x1802) | (Data32IRQ & 0x0300);
if (val & (1<<10)) DataFIFO32->Clear();
DataMode = ((DataCtl >> 1) & 0x1) & ((Data32IRQ >> 1) & 0x1);
return;
case 0x104: BlockLen32 = val & 0x03FF; return;
case 0x108: BlockCount32 = val; return;
}
printf("unknown %s write %08X %04X\n", SD_DESC, addr, val);
}
void DSi_SDHost::WriteFIFO16(u16 val)
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 f = CurFIFO;
if (DataFIFO[f]->IsFull())
{
// TODO
printf("!!!! %s FIFO (16) FULL\n", SD_DESC);
return;
}
DataFIFO[f]->Write(val);
CheckTX();
}
void DSi_SDHost::WriteFIFO32(u32 val)
{
if (DataMode != 1) return;
if (DataFIFO32->IsFull())
{
// TODO
printf("!!!! %s FIFO (32) FULL\n", SD_DESC);
return;
}
DataFIFO32->Write(val);
CheckTX();
UpdateData32IRQ();
}
void DSi_SDHost::UpdateFIFO32()
{
// check whether we can drain FIFO32 into FIFO16, or vice versa
if (DataMode != 1) return;
if (!DataFIFO32->IsEmpty()) printf("VERY BAD!! TRYING TO DRAIN FIFO16 INTO FIFO32 BUT IT CONTAINS SHIT ALREADY\n");
for (;;)
{
u32 f = CurFIFO;
if ((DataFIFO32->Level() << 2) >= BlockLen32) break;
if (DataFIFO[f]->IsEmpty()) break;
u32 val = DataFIFO[f]->Read();
val |= (DataFIFO[f]->Read() << 16);
DataFIFO32->Write(val);
}
UpdateData32IRQ();
if ((DataFIFO32->Level() << 2) >= BlockLen32)
{
DSi::CheckNDMAs(1, Num ? 0x29 : 0x28);
}
}
void DSi_SDHost::CheckSwapFIFO()
{
// check whether we can swap the FIFOs
u32 f = CurFIFO;
bool cur_empty = (DataMode == 1) ? DataFIFO32->IsEmpty() : DataFIFO[f]->IsEmpty();
if (cur_empty && ((DataFIFO[f^1]->Level() << 1) >= BlockLen16))
{
CurFIFO ^= 1;
}
}
#define MMC_DESC (Internal?"NAND":"SDcard")
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const char* path) : DSi_SDDevice(host)
{
Internal = internal;
strncpy(FilePath, path, 1023); FilePath[1023] = '\0';
File = Platform::OpenLocalFile(path, "r+b");
if (!File)
{
if (internal)
{
// TODO: proper failure
printf("!! MMC file %s does not exist\n", path);
}
else
{
File = Platform::OpenLocalFile(path, "w+b");
}
}
CSR = 0x00000100; // checkme
// TODO: busy bit
// TODO: SDHC/SDXC bit
OCR = 0x80FF8000;
// TODO: customize based on card size etc
u8 csd_template[16] = {0x40, 0x40, 0x96, 0xE9, 0x7F, 0xDB, 0xF6, 0xDF, 0x01, 0x59, 0x0F, 0x2A, 0x01, 0x26, 0x90, 0x00};
memcpy(CSD, csd_template, 16);
// checkme
memset(SCR, 0, 8);
*(u32*)&SCR[0] = 0x012A0000;
memset(SSR, 0, 64);
BlockSize = 0;
RWAddress = 0;
RWCommand = 0;
}
DSi_MMCStorage::~DSi_MMCStorage()
{
if (File) fclose(File);
}
void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
{
if (CSR & (1<<5))
{
CSR &= ~(1<<5);
return SendACMD(cmd, param);
}
switch (cmd)
{
case 0: // reset/etc
Host->SendResponse(CSR, true);
return;
case 2:
case 10: // get CID
Host->SendResponse(*(u32*)&CID[12], false);
Host->SendResponse(*(u32*)&CID[8], false);
Host->SendResponse(*(u32*)&CID[4], false);
Host->SendResponse(*(u32*)&CID[0], true);
if (cmd == 2) SetState(0x02);
return;
case 3: // get/set RCA
if (Internal)
{
RCA = param >> 16;
Host->SendResponse(CSR|0x10000, true); // huh??
}
else
{
// TODO
printf("CMD3 on SD card: TODO\n");
Host->SendResponse((CSR & 0x1FFF) | ((CSR >> 6) & 0x2000) | ((CSR >> 8) & 0xC000) | (1 << 16), true);
}
return;
case 7: // select card (by RCA)
Host->SendResponse(CSR, true);
return;
case 8: // set voltage
Host->SendResponse(param, true);
return;
case 9: // get CSD
Host->SendResponse(*(u32*)&CSD[12], false);
Host->SendResponse(*(u32*)&CSD[8], false);
Host->SendResponse(*(u32*)&CSD[4], false);
Host->SendResponse(*(u32*)&CSD[0], true);
return;
case 12: // stop operation
SetState(0x04);
if (File) fflush(File);
RWCommand = 0;
Host->SendResponse(CSR, true);
return;
case 13: // get status
Host->SendResponse(CSR, true);
return;
case 16: // set block size
BlockSize = param;
if (BlockSize > 0x200)
{
// TODO! raise error
printf("!! SD/MMC: BAD BLOCK LEN %d\n", BlockSize);
BlockSize = 0x200;
}
SetState(0x04); // CHECKME
Host->SendResponse(CSR, true);
return;
case 18: // read multiple blocks
//printf("READ_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize);
RWAddress = param;
if (OCR & (1<<30))
{
RWAddress <<= 9;
BlockSize = 512;
}
RWCommand = 18;
Host->SendResponse(CSR, true);
ReadBlock(RWAddress);
RWAddress += BlockSize;
SetState(0x05);
return;
case 25: // write multiple blocks
//printf("WRITE_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize);
RWAddress = param;
if (OCR & (1<<30))
{
RWAddress <<= 9;
BlockSize = 512;
}
RWCommand = 25;
Host->SendResponse(CSR, true);
WriteBlock(RWAddress);
RWAddress += BlockSize;
SetState(0x04);
return;
case 55: // appcmd prefix
CSR |= (1<<5);
Host->SendResponse(CSR, true);
return;
}
printf("MMC: unknown CMD %d %08X\n", cmd, param);
}
void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
{
switch (cmd)
{
case 6: // set bus width (TODO?)
//printf("SET BUS WIDTH %08X\n", param);
Host->SendResponse(CSR, true);
return;
case 13: // get SSR
Host->SendResponse(CSR, true);
Host->DataRX(SSR, 64);
return;
case 41: // set operating conditions
// CHECKME:
// DSi boot2 sets this to 0x40100000 (hardcoded)
// then has two codepaths depending on whether bit30 did get set
// is it settable at all on the MMC? probably not.
if (Internal) param &= ~(1<<30);
OCR &= 0xBF000000;
OCR |= (param & 0x40FFFFFF);
Host->SendResponse(OCR, true);
SetState(0x01);
return;
case 42: // ???
Host->SendResponse(CSR, true);
return;
case 51: // get SCR
Host->SendResponse(CSR, true);
Host->DataRX(SCR, 8);
return;
}
printf("MMC: unknown ACMD %d %08X\n", cmd, param);
}
void DSi_MMCStorage::ContinueTransfer()
{
if (RWCommand == 0) return;
u32 len = 0;
switch (RWCommand)
{
case 18:
len = ReadBlock(RWAddress);
break;
case 25:
len = WriteBlock(RWAddress);
break;
}
RWAddress += len;
}
u32 DSi_MMCStorage::ReadBlock(u64 addr)
{
u32 len = BlockSize;
len = Host->GetTransferrableLen(len);
u8 data[0x200];
if (File)
{
fseek(File, addr, SEEK_SET);
fread(data, 1, len, File);
}
return Host->DataRX(data, len);
}
u32 DSi_MMCStorage::WriteBlock(u64 addr)
{
u32 len = BlockSize;
len = Host->GetTransferrableLen(len);
u8 data[0x200];
if (len = Host->DataTX(data, len))
{
if (File)
{
fseek(File, addr, SEEK_SET);
fwrite(data, 1, len, File);
}
}
return len;
}

154
src/DSi_SD.h Normal file
View File

@ -0,0 +1,154 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_SD_H
#define DSI_SD_H
#include <string.h>
#include "FIFO.h"
class DSi_SDDevice;
class DSi_SDHost
{
public:
DSi_SDHost(u32 num);
~DSi_SDHost();
void Reset();
void DoSavestate(Savestate* file);
static void FinishRX(u32 param);
static void FinishTX(u32 param);
void SendResponse(u32 val, bool last);
u32 DataRX(u8* data, u32 len);
u32 DataTX(u8* data, u32 len);
u32 GetTransferrableLen(u32 len);
void CheckRX();
void CheckTX();
bool TXReq;
void SetCardIRQ();
u16 Read(u32 addr);
void Write(u32 addr, u16 val);
u16 ReadFIFO16();
void WriteFIFO16(u16 val);
u32 ReadFIFO32();
void WriteFIFO32(u32 val);
void UpdateFIFO32();
void CheckSwapFIFO();
private:
u32 Num;
u16 PortSelect;
u16 SoftReset;
u16 SDClock;
u16 SDOption;
u32 IRQStatus; // IF
u32 IRQMask; // ~IE
u16 CardIRQStatus;
u16 CardIRQMask;
u16 CardIRQCtl;
u16 DataCtl;
u16 Data32IRQ;
u32 DataMode; // 0=16bit 1=32bit
u16 BlockCount16, BlockCount32, BlockCountInternal;
u16 BlockLen16, BlockLen32;
u16 StopAction;
u16 Command;
u32 Param;
u16 ResponseBuffer[8];
FIFO<u16>* DataFIFO[2];
u32 CurFIFO; // FIFO accessible for read/write
FIFO<u32>* DataFIFO32;
DSi_SDDevice* Ports[2];
void UpdateData32IRQ();
void ClearIRQ(u32 irq);
void SetIRQ(u32 irq);
void UpdateIRQ(u32 oldmask);
};
class DSi_SDDevice
{
public:
DSi_SDDevice(DSi_SDHost* host) { Host = host; IRQ = false; }
~DSi_SDDevice() {}
virtual void SendCMD(u8 cmd, u32 param) = 0;
virtual void ContinueTransfer() = 0;
bool IRQ;
protected:
DSi_SDHost* Host;
};
class DSi_MMCStorage : public DSi_SDDevice
{
public:
DSi_MMCStorage(DSi_SDHost* host, bool internal, const char* path);
~DSi_MMCStorage();
void SetCID(u8* cid) { memcpy(CID, cid, 16); }
void SendCMD(u8 cmd, u32 param);
void SendACMD(u8 cmd, u32 param);
void ContinueTransfer();
private:
bool Internal;
char FilePath[1024];
FILE* File;
u8 CID[16];
u8 CSD[16];
u32 CSR;
u32 OCR;
u32 RCA;
u8 SCR[8];
u8 SSR[64];
u32 BlockSize;
u64 RWAddress;
u32 RWCommand;
void SetState(u32 state) { CSR &= ~(0xF << 9); CSR |= (state << 9); }
u32 ReadBlock(u64 addr);
u32 WriteBlock(u64 addr);
};
#endif // DSI_SD_H

231
src/DSi_SPI_TSC.cpp Normal file
View File

@ -0,0 +1,231 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "DSi.h"
#include "SPI.h"
#include "DSi_SPI_TSC.h"
namespace DSi_SPI_TSC
{
u32 DataPos;
u8 Index;
u8 Bank;
u8 Data;
u8 Bank3Regs[0x80];
u8 TSCMode;
u16 TouchX, TouchY;
bool Init()
{
return true;
}
void DeInit()
{
}
void Reset()
{
DataPos = 0;
Bank = 0;
Index = 0;
Data = 0;
memset(Bank3Regs, 0, 0x80);
Bank3Regs[0x02] = 0x18;
Bank3Regs[0x03] = 0x87;
Bank3Regs[0x04] = 0x22;
Bank3Regs[0x05] = 0x04;
Bank3Regs[0x06] = 0x20;
Bank3Regs[0x09] = 0x40;
Bank3Regs[0x0E] = 0xAD;
Bank3Regs[0x0F] = 0xA0;
Bank3Regs[0x10] = 0x88;
Bank3Regs[0x11] = 0x81;
TSCMode = 0x01; // DSi mode
}
void DoSavestate(Savestate* file)
{
/*file->Section("SPTi");
file->Var32(&DataPos);
file->Var8(&ControlByte);
file->Var8(&Data);
file->Var16(&ConvResult);*/
// TODO!!
}
void SetTouchCoords(u16 x, u16 y)
{
if (TSCMode == 0x00)
{
if (y == 0xFFF) NDS::KeyInput |= (1 << (16+6));
else NDS::KeyInput &= ~(1 << (16+6));
return SPI_TSC::SetTouchCoords(x, y);
}
TouchX = x;
TouchY = y;
u8 oldpress = Bank3Regs[0x0E] & 0x01;
if (y == 0xFFF)
{
// released
// TODO: GBAtek says it can also be 1000 or 3000??
TouchX = 0x7000;
TouchY = 0x7000;
Bank3Regs[0x09] = 0x40;
//Bank3Regs[0x09] &= ~0x80;
Bank3Regs[0x0E] |= 0x01;
}
else
{
// pressed
TouchX <<= 4;
TouchY <<= 4;
Bank3Regs[0x09] = 0x80;
//Bank3Regs[0x09] |= 0x80;
Bank3Regs[0x0E] &= ~0x01;
}
if (oldpress ^ (Bank3Regs[0x0E] & 0x01))
{
TouchX |= 0x8000;
TouchY |= 0x8000;
}
}
void MicInputFrame(s16* data, int samples)
{
if (TSCMode == 0x00) return SPI_TSC::MicInputFrame(data, samples);
// otherwise we don't handle mic input
// TODO: handle it where it needs to be
}
u8 Read()
{
if (TSCMode == 0x00) return SPI_TSC::Read();
return Data;
}
void Write(u8 val, u32 hold)
{
if (TSCMode == 0x00) return SPI_TSC::Write(val, hold);
#define READWRITE(var) { if (Index & 0x01) Data = var; else var = val; }
if (DataPos == 0)
{
Index = val;
}
else
{
u8 id = Index >> 1;
if (id == 0)
{
READWRITE(Bank);
}
else if (Bank == 0x03)
{
if (Index & 0x01) Data = Bank3Regs[id];
else
{
if (id == 0x0D || id == 0x0E)
Bank3Regs[id] = (Bank3Regs[id] & 0x03) | (val & 0xFC);
}
}
else if ((Bank == 0xFC) && (Index & 0x01))
{
if (id < 0x0B)
{
// X coordinates
if (id & 0x01) Data = TouchX >> 8;
else Data = TouchX & 0xFF;
TouchX &= 0x7FFF;
}
else if (id < 0x15)
{
// Y coordinates
if (id & 0x01) Data = TouchY >> 8;
else Data = TouchY & 0xFF;
TouchY &= 0x7FFF; // checkme
}
else
{
// whatever (TODO)
Data = 0;
}
}
else if (Bank == 0xFF)
{
if (id == 0x05)
{
// TSC mode register
// 01: normal (DSi) mode
// 00: compatibility (DS) mode
if (Index & 0x01) Data = TSCMode;
else
{
TSCMode = val;
if (TSCMode == 0x00)
{
printf("DSi_SPI_TSC: DS-compatibility mode\n");
DataPos = 0;
NDS::KeyInput |= (1 << (16+6));
return;
}
}
}
}
else
{
printf("DSi_SPI_TSC: unknown IO, bank=%02X, index=%02X (%02X %s)\n", Bank, Index, Index>>1, (Index&1)?"read":"write");
}
Index += (1<<1); // increment index
}
if (hold) DataPos++;
else DataPos = 0;
}
}

40
src/DSi_SPI_TSC.h Normal file
View File

@ -0,0 +1,40 @@
/*
Copyright 2016-2019 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DSI_SPI_TSC
#define DSI_SPI_TSC
namespace DSi_SPI_TSC
{
extern u32 DataPos;
bool Init();
void DeInit();
void Reset();
void DoSavestate(Savestate* file);
void SetTouchCoords(u16 x, u16 y);
void MicInputFrame(s16* data, int samples);
u8 Read();
void Write(u8 val, u32 hold);
}
#endif // DSI_SPI_TSC

View File

@ -89,6 +89,15 @@ public:
return Entries[ReadPos];
}
T Peek(u32 offset)
{
u32 pos = ReadPos + offset;
if (pos >= NumEntries)
pos -= NumEntries;
return Entries[pos];
}
u32 Level() { return NumOccupied; }
bool IsEmpty() { return NumOccupied == 0; }
bool IsFull() { return NumOccupied >= NumEntries; }

View File

@ -21,6 +21,7 @@
#include "NDS.h"
#include "GPU.h"
namespace GPU
{
@ -78,6 +79,7 @@ u8* VRAMPtr_BOBJ[0x8];
int FrontBuffer;
u32* Framebuffer[2][2];
int Renderer;
bool Accelerated;
GPU2D* GPU2D_A;
@ -93,8 +95,8 @@ bool Init()
FrontBuffer = 0;
Framebuffer[0][0] = NULL; Framebuffer[0][1] = NULL;
Framebuffer[1][0] = NULL; Framebuffer[1][1] = NULL;
Renderer = 0;
Accelerated = false;
SetDisplaySettings(false);
return true;
}
@ -182,6 +184,8 @@ void Reset()
int backbuf = FrontBuffer ? 0 : 1;
GPU2D_A->SetFramebuffer(Framebuffer[backbuf][1]);
GPU2D_B->SetFramebuffer(Framebuffer[backbuf][0]);
ResetRenderer();
}
void Stop()
@ -274,15 +278,72 @@ void AssignFramebuffers()
}
}
void SetDisplaySettings(bool accel)
void InitRenderer(int renderer)
{
if (renderer == 1)
{
if (!GLCompositor::Init())
{
renderer = 0;
}
else if (!GPU3D::GLRenderer::Init())
{
GLCompositor::DeInit();
renderer = 0;
}
}
if (renderer == 0)
{
GPU3D::SoftRenderer::Init();
}
Renderer = renderer;
Accelerated = renderer != 0;
}
void DeInitRenderer()
{
if (Renderer == 0)
{
GPU3D::SoftRenderer::DeInit();
}
else
{
GPU3D::GLRenderer::DeInit();
GLCompositor::DeInit();
}
}
void ResetRenderer()
{
if (Renderer == 0)
{
GPU3D::SoftRenderer::Reset();
}
else
{
GLCompositor::Reset();
GPU3D::GLRenderer::Reset();
}
}
void SetRenderSettings(int renderer, RenderSettings& settings)
{
if (renderer != Renderer)
{
DeInitRenderer();
InitRenderer(renderer);
}
bool accel = Accelerated;
int fbsize;
if (accel) fbsize = (256*3 + 1) * 192;
else fbsize = 256 * 192;
if (Framebuffer[0][0]) delete[] Framebuffer[0][0];
if (Framebuffer[1][0]) delete[] Framebuffer[1][0];
if (Framebuffer[0][1]) delete[] Framebuffer[0][1];
if (Framebuffer[1][1]) delete[] Framebuffer[1][1];
if (Framebuffer[0][0]) { delete[] Framebuffer[0][0]; Framebuffer[0][0] = nullptr; }
if (Framebuffer[1][0]) { delete[] Framebuffer[1][0]; Framebuffer[1][0] = nullptr; }
if (Framebuffer[0][1]) { delete[] Framebuffer[0][1]; Framebuffer[0][1] = nullptr; }
if (Framebuffer[1][1]) { delete[] Framebuffer[1][1]; Framebuffer[1][1] = nullptr; }
Framebuffer[0][0] = new u32[fbsize];
Framebuffer[1][0] = new u32[fbsize];
@ -296,10 +357,18 @@ void SetDisplaySettings(bool accel)
AssignFramebuffers();
GPU2D_A->SetDisplaySettings(accel);
GPU2D_B->SetDisplaySettings(accel);
GPU2D_A->SetRenderSettings(accel);
GPU2D_B->SetRenderSettings(accel);
Accelerated = accel;
if (Renderer == 0)
{
GPU3D::SoftRenderer::SetRenderSettings(settings);
}
else
{
GLCompositor::SetRenderSettings(settings);
GPU3D::GLRenderer::SetRenderSettings(settings);
}
}
@ -985,6 +1054,8 @@ void StartScanline(u32 line)
GPU2D_A->VBlank();
GPU2D_B->VBlank();
GPU3D::VBlank();
if (Accelerated) GLCompositor::RenderFrame();
}
else if (VCount == 144)
{

View File

@ -20,7 +20,6 @@
#define GPU_H
#include "GPU2D.h"
#include "GPU3D.h"
namespace GPU
{
@ -72,6 +71,17 @@ extern u32* Framebuffer[2][2];
extern GPU2D* GPU2D_A;
extern GPU2D* GPU2D_B;
extern int Renderer;
typedef struct
{
bool Soft_Threaded;
int GL_ScaleFactor;
} RenderSettings;
bool Init();
void DeInit();
@ -80,7 +90,11 @@ void Stop();
void DoSavestate(Savestate* file);
void SetDisplaySettings(bool accel);
void InitRenderer(int renderer);
void DeInitRenderer();
void ResetRenderer();
void SetRenderSettings(int renderer, RenderSettings& settings);
u8* GetUniqueBankPtr(u32 mask, u32 offset);
@ -422,6 +436,22 @@ void SetDispStat(u32 cpu, u16 val);
void SetVCount(u16 val);
namespace GLCompositor
{
bool Init();
void DeInit();
void Reset();
void SetRenderSettings(RenderSettings& settings);
void RenderFrame();
void BindOutputTexture();
}
}
#include "GPU3D.h"
#endif

View File

@ -202,16 +202,8 @@ void GPU2D::DoSavestate(Savestate* file)
file->Var32(&CaptureCnt);
}
if (file->IsAtleastVersion(2, 1))
{
file->Var32(&Win0Active);
file->Var32(&Win1Active);
}
else
{
Win0Active = 0;
Win1Active = 0;
}
file->Var32(&Win0Active);
file->Var32(&Win1Active);
if (!file->Saving)
{
@ -232,7 +224,7 @@ void GPU2D::SetFramebuffer(u32* buf)
Framebuffer = buf;
}
void GPU2D::SetDisplaySettings(bool accel)
void GPU2D::SetRenderSettings(bool accel)
{
Accelerated = accel;
@ -728,6 +720,8 @@ u32 GPU2D::ColorComposite(int i, u32 val1, u32 val2)
case 3: return ColorBrightnessDown(val1, EVY);
case 4: return ColorBlend5(val1, val2);
}
return val1;
}

View File

@ -31,7 +31,7 @@ public:
void SetEnabled(bool enable) { Enabled = enable; }
void SetFramebuffer(u32* buf);
void SetDisplaySettings(bool accel);
void SetRenderSettings(bool accel);
u8 Read8(u32 addr);
u16 Read16(u32 addr);

View File

@ -157,8 +157,6 @@ u32 NumCommands, CurCommand, ParamCount, TotalParams;
bool GeometryEnabled;
bool RenderingEnabled;
int Renderer;
u32 DispCnt;
u8 AlphaRefVal, AlphaRef;
@ -280,17 +278,11 @@ bool Init()
CmdStallQueue = new FIFO<CmdFIFOEntry>(64);
Renderer = -1;
// SetRenderer() will be called to set it up later
return true;
}
void DeInit()
{
if (Renderer == 0) SoftRenderer::DeInit();
else GLRenderer::DeInit();
delete CmdFIFO;
delete CmdPIPE;
@ -391,8 +383,6 @@ void Reset()
FlushAttributes = 0;
ResetRenderingState();
if (Renderer == 0) SoftRenderer::Reset();
else GLRenderer::Reset();
}
void DoSavestate(Savestate* file)
@ -607,43 +597,6 @@ void SetEnabled(bool geometry, bool rendering)
}
int InitRenderer(bool hasGL)
{
int renderer = hasGL ? Config::_3DRenderer : 0;
if (renderer == 1)
{
if (!GLRenderer::Init())
renderer = 0;
}
if (renderer == 0) SoftRenderer::Init();
Renderer = renderer;
UpdateRendererConfig();
GPU::SetDisplaySettings(Renderer != 0);
return renderer;
}
void DeInitRenderer()
{
if (Renderer == 0) SoftRenderer::DeInit();
else GLRenderer::DeInit();
}
void UpdateRendererConfig()
{
if (Renderer == 0)
{
SoftRenderer::SetupRenderThread();
}
else
{
GLRenderer::UpdateDisplaySettings();
}
}
void MatrixLoadIdentity(s32* m)
{
@ -2470,7 +2423,7 @@ void CheckFIFODMA()
void VCount144()
{
if (Renderer == 0) SoftRenderer::VCount144();
if (GPU::Renderer == 0) SoftRenderer::VCount144();
}
@ -2552,14 +2505,14 @@ void VBlank()
void VCount215()
{
if (Renderer == 0) SoftRenderer::RenderFrame();
else GLRenderer::RenderFrame();
if (GPU::Renderer == 0) SoftRenderer::RenderFrame();
else GLRenderer::RenderFrame();
}
u32* GetLine(int line)
{
if (Renderer == 0) return SoftRenderer::GetLine(line);
else return GLRenderer::GetLine(line);
if (GPU::Renderer == 0) return SoftRenderer::GetLine(line);
else return GLRenderer::GetLine(line);
}

View File

@ -102,10 +102,6 @@ void DoSavestate(Savestate* file);
void SetEnabled(bool geometry, bool rendering);
int InitRenderer(bool hasGL);
void DeInitRenderer();
void UpdateRendererConfig();
void ExecuteCommand();
s32 CyclesToRunFor();
@ -134,6 +130,7 @@ bool Init();
void DeInit();
void Reset();
void SetRenderSettings(GPU::RenderSettings& settings);
void SetupRenderThread();
void VCount144();
@ -149,7 +146,7 @@ bool Init();
void DeInit();
void Reset();
void UpdateDisplaySettings();
void SetRenderSettings(GPU::RenderSettings& settings);
void RenderFrame();
void PrepareCaptureFrame();

View File

@ -29,6 +29,8 @@ namespace GPU3D
namespace GLRenderer
{
using namespace OpenGL;
// GL version requirements
// * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS)
// * UBO: 3.1
@ -142,7 +144,7 @@ bool BuildRenderShader(u32 flags, const char* vs, const char* fs)
strcpy(&fsbuf[headerlen], kRenderFSCommon);
strcpy(&fsbuf[headerlen + fsclen], fs);
bool ret = OpenGL_BuildShaderProgram(vsbuf, fsbuf, RenderShader[flags], shadername);
bool ret = OpenGL::BuildShaderProgram(vsbuf, fsbuf, RenderShader[flags], shadername);
delete[] vsbuf;
delete[] fsbuf;
@ -158,7 +160,7 @@ bool BuildRenderShader(u32 flags, const char* vs, const char* fs)
glBindFragDataLocation(prog, 0, "oColor");
glBindFragDataLocation(prog, 1, "oAttr");
if (!OpenGL_LinkShaderProgram(RenderShader[flags]))
if (!OpenGL::LinkShaderProgram(RenderShader[flags]))
return false;
GLint uni_id = glGetUniformBlockIndex(prog, "uConfig");
@ -197,19 +199,18 @@ bool Init()
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glDepthRange(0, 1);
glClearDepth(1.0);
if (!OpenGL_BuildShaderProgram(kClearVS, kClearFS, ClearShaderPlain, "ClearShader"))
if (!OpenGL::BuildShaderProgram(kClearVS, kClearFS, ClearShaderPlain, "ClearShader"))
return false;
glBindAttribLocation(ClearShaderPlain[2], 0, "vPosition");
glBindFragDataLocation(ClearShaderPlain[2], 0, "oColor");
glBindFragDataLocation(ClearShaderPlain[2], 1, "oAttr");
if (!OpenGL_LinkShaderProgram(ClearShaderPlain))
if (!OpenGL::LinkShaderProgram(ClearShaderPlain))
return false;
ClearUniformLoc[0] = glGetUniformLocation(ClearShaderPlain[2], "uColor");
@ -237,15 +238,15 @@ bool Init()
kRenderVS_W, kRenderFS_WSM)) return false;
if (!OpenGL_BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, FinalPassEdgeShader, "FinalPassEdgeShader"))
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, FinalPassEdgeShader, "FinalPassEdgeShader"))
return false;
if (!OpenGL_BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, FinalPassFogShader, "FinalPassFogShader"))
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, FinalPassFogShader, "FinalPassFogShader"))
return false;
glBindAttribLocation(FinalPassEdgeShader[2], 0, "vPosition");
glBindFragDataLocation(FinalPassEdgeShader[2], 0, "oColor");
if (!OpenGL_LinkShaderProgram(FinalPassEdgeShader))
if (!OpenGL::LinkShaderProgram(FinalPassEdgeShader))
return false;
uni_id = glGetUniformBlockIndex(FinalPassEdgeShader[2], "uConfig");
@ -261,7 +262,7 @@ bool Init()
glBindAttribLocation(FinalPassFogShader[2], 0, "vPosition");
glBindFragDataLocation(FinalPassFogShader[2], 0, "oColor");
if (!OpenGL_LinkShaderProgram(FinalPassFogShader))
if (!OpenGL::LinkShaderProgram(FinalPassFogShader))
return false;
uni_id = glGetUniformBlockIndex(FinalPassFogShader[2], "uConfig");
@ -392,7 +393,7 @@ void DeInit()
for (int i = 0; i < 16; i++)
{
if (!RenderShader[i][2]) continue;
OpenGL_DeleteShaderProgram(RenderShader[i]);
OpenGL::DeleteShaderProgram(RenderShader[i]);
}
}
@ -400,10 +401,10 @@ void Reset()
{
}
void UpdateDisplaySettings()
void SetRenderSettings(GPU::RenderSettings& settings)
{
int scale = Config::GL_ScaleFactor;
bool antialias = false; //Config::GL_Antialias;
int scale = settings.GL_ScaleFactor;
bool antialias = false; // REMOVE ME!
if (antialias) scale *= 2;
@ -1224,7 +1225,7 @@ void RenderFrame()
glBlitFramebuffer(0, 0, ScreenW, ScreenH, 0, 0, ScreenW/2, ScreenH/2, GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[FrontBuffer]);
//glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[FrontBuffer]);
FrontBuffer = FrontBuffer ? 0 : 1;
}

View File

@ -60,6 +60,7 @@ bool Enabled;
// threading
bool Threaded;
void* RenderThread;
bool RenderThreadRunning;
bool RenderThreadRendering;
@ -83,7 +84,7 @@ void StopRenderThread()
void SetupRenderThread()
{
if (Config::Threaded3D)
if (Threaded)
{
if (!RenderThreadRunning)
{
@ -112,6 +113,7 @@ bool Init()
Sema_RenderDone = Platform::Semaphore_Create();
Sema_ScanlineCount = Platform::Semaphore_Create();
Threaded = false;
RenderThreadRunning = false;
RenderThreadRendering = false;
@ -138,6 +140,12 @@ void Reset()
SetupRenderThread();
}
void SetRenderSettings(GPU::RenderSettings& settings)
{
Threaded = settings.Soft_Threaded;
SetupRenderThread();
}
// Notes on the interpolator:

206
src/GPU_OpenGL.cpp Normal file
View File

@ -0,0 +1,206 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "NDS.h"
#include "GPU.h"
#include "Config.h"
#include "OpenGLSupport.h"
#include "GPU_OpenGL_shaders.h"
namespace GPU
{
namespace GLCompositor
{
using namespace OpenGL;
int Scale;
int ScreenH, ScreenW;
GLuint CompShader[1][3];
GLuint CompScaleLoc[1];
GLuint CompVertexBufferID;
GLuint CompVertexArrayID;
float CompVertices[2 * 3*2 * 2]; // position
GLuint CompScreenInputTex;
GLuint CompScreenOutputTex;
GLuint CompScreenOutputFB;
bool Init()
{
if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Nearest, CompShader[0], "CompositorShader"))
//if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Linear, CompShader[0], "CompositorShader"))
//if (!OpenGL::BuildShaderProgram(kCompositorVS_xBRZ, kCompositorFS_xBRZ, CompShader[0], "CompositorShader"))
return false;
for (int i = 0; i < 1; i++)
{
GLint uni_id;
glBindAttribLocation(CompShader[i][2], 0, "vPosition");
glBindFragDataLocation(CompShader[i][2], 0, "oColor");
if (!OpenGL::LinkShaderProgram(CompShader[i]))
return false;
CompScaleLoc[i] = glGetUniformLocation(CompShader[i][2], "u3DScale");
glUseProgram(CompShader[i][2]);
uni_id = glGetUniformLocation(CompShader[i][2], "ScreenTex");
glUniform1i(uni_id, 0);
uni_id = glGetUniformLocation(CompShader[i][2], "_3DTex");
glUniform1i(uni_id, 1);
}
#define SETVERTEX(i, x, y) \
CompVertices[2*(i) + 0] = x; \
CompVertices[2*(i) + 1] = y;
// top screen
SETVERTEX(0, -1, 1);
SETVERTEX(1, 1, 0);
SETVERTEX(2, 1, 1);
SETVERTEX(3, -1, 1);
SETVERTEX(4, -1, 0);
SETVERTEX(5, 1, 0);
// bottom screen
SETVERTEX(6, -1, 0);
SETVERTEX(7, 1, -1);
SETVERTEX(8, 1, 0);
SETVERTEX(9, -1, 0);
SETVERTEX(10, -1, -1);
SETVERTEX(11, 1, -1);
#undef SETVERTEX
glGenBuffers(1, &CompVertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(CompVertices), CompVertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &CompVertexArrayID);
glBindVertexArray(CompVertexArrayID);
glEnableVertexAttribArray(0); // position
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*4, (void*)(0));
glGenFramebuffers(1, &CompScreenOutputFB);
glGenTextures(1, &CompScreenInputTex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, CompScreenInputTex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 256*3 + 1, 192*2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL);
glGenTextures(1, &CompScreenOutputTex);
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
return true;
}
void DeInit()
{
glDeleteFramebuffers(1, &CompScreenOutputFB);
glDeleteTextures(1, &CompScreenInputTex);
glDeleteTextures(1, &CompScreenOutputTex);
glDeleteVertexArrays(1, &CompVertexArrayID);
glDeleteBuffers(1, &CompVertexBufferID);
for (int i = 0; i < 1; i++)
OpenGL::DeleteShaderProgram(CompShader[i]);
}
void Reset()
{
}
void SetRenderSettings(RenderSettings& settings)
{
int scale = settings.GL_ScaleFactor;
Scale = scale;
ScreenW = 256 * scale;
ScreenH = 384 * scale;
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ScreenW, ScreenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
GLenum fbassign[] = {GL_COLOR_ATTACHMENT0};
glBindFramebuffer(GL_FRAMEBUFFER, CompScreenOutputFB);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, CompScreenOutputTex, 0);
glDrawBuffers(1, fbassign);
}
void RenderFrame()
{
glBindFramebuffer(GL_FRAMEBUFFER, CompScreenOutputFB);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_BLEND);
glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glViewport(0, 0, ScreenW, ScreenH);
// TODO: select more shaders (filtering, etc)
OpenGL::UseShaderProgram(CompShader[0]);
glUniform1ui(CompScaleLoc[0], Scale);
int frontbuf = GPU::FrontBuffer;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, CompScreenInputTex);
if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1])
{
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER,
GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER,
GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]);
}
glActiveTexture(GL_TEXTURE1);
GPU3D::GLRenderer::SetupAccelFrame();
glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID);
glBindVertexArray(CompVertexArrayID);
glDrawArrays(GL_TRIANGLES, 0, 4*3);
glFlush();
}
void BindOutputTexture()
{
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex);
}
}
}

867
src/GPU_OpenGL_shaders.h Normal file
View File

@ -0,0 +1,867 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef GPU_OPENGL_SHADERS_H
#define GPU_OPENGL_SHADERS_H
const char* kCompositorVS = R"(#version 140
in vec2 vPosition;
smooth out vec2 fTexcoord;
void main()
{
vec4 fpos;
fpos.xy = vPosition;
fpos.z = 0.0;
fpos.w = 1.0;
gl_Position = fpos;
fTexcoord = (vPosition + vec2(1.0, 1.0)) * (vec2(256.0, 384.0) / 2.0);
}
)";
const char* kCompositorFS_Nearest = R"(#version 140
uniform uint u3DScale;
uniform usampler2D ScreenTex;
uniform sampler2D _3DTex;
smooth in vec2 fTexcoord;
out vec4 oColor;
void main()
{
ivec4 pixel = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord), 0));
ivec4 mbright = ivec4(texelFetch(ScreenTex, ivec2(256*3, int(fTexcoord.y)), 0));
int dispmode = mbright.b & 0x3;
if (dispmode == 1)
{
ivec4 val1 = pixel;
ivec4 val2 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(256,0), 0));
ivec4 val3 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(512,0), 0));
int compmode = val3.a & 0xF;
int eva, evb, evy;
if (compmode == 4)
{
// 3D on top, blending
float xpos = val3.r + fract(fTexcoord.x);
float ypos = mod(fTexcoord.y, 192);
ivec4 _3dpix = ivec4(texelFetch(_3DTex, ivec2(vec2(xpos, ypos)*u3DScale), 0).bgra
* vec4(63,63,63,31));
if (_3dpix.a > 0)
{
eva = (_3dpix.a & 0x1F) + 1;
evb = 32 - eva;
val1 = ((_3dpix * eva) + (val1 * evb)) >> 5;
if (eva <= 16) val1 += ivec4(1,1,1,0);
val1 = min(val1, 0x3F);
}
else
val1 = val2;
}
else if (compmode == 1)
{
// 3D on bottom, blending
float xpos = val3.r + fract(fTexcoord.x);
float ypos = mod(fTexcoord.y, 192);
ivec4 _3dpix = ivec4(texelFetch(_3DTex, ivec2(vec2(xpos, ypos)*u3DScale), 0).bgra
* vec4(63,63,63,31));
if (_3dpix.a > 0)
{
eva = val3.g;
evb = val3.b;
val1 = ((val1 * eva) + (_3dpix * evb)) >> 4;
val1 = min(val1, 0x3F);
}
else
val1 = val2;
}
else if (compmode <= 3)
{
// 3D on top, normal/fade
float xpos = val3.r + fract(fTexcoord.x);
float ypos = mod(fTexcoord.y, 192);
ivec4 _3dpix = ivec4(texelFetch(_3DTex, ivec2(vec2(xpos, ypos)*u3DScale), 0).bgra
* vec4(63,63,63,31));
if (_3dpix.a > 0)
{
evy = val3.g;
val1 = _3dpix;
if (compmode == 2) val1 += ((ivec4(0x3F,0x3F,0x3F,0) - val1) * evy) >> 4;
else if (compmode == 3) val1 -= (val1 * evy) >> 4;
}
else
val1 = val2;
}
pixel = val1;
}
if (dispmode != 0)
{
int brightmode = mbright.g >> 6;
if (brightmode == 1)
{
// up
int evy = mbright.r & 0x1F;
if (evy > 16) evy = 16;
pixel += ((ivec4(0x3F,0x3F,0x3F,0) - pixel) * evy) >> 4;
}
else if (brightmode == 2)
{
// down
int evy = mbright.r & 0x1F;
if (evy > 16) evy = 16;
pixel -= (pixel * evy) >> 4;
}
}
pixel.rgb <<= 2;
pixel.rgb |= (pixel.rgb >> 6);
// TODO: filters
oColor = vec4(vec3(pixel.bgr) / 255.0, 1.0);
}
)";
const char* kCompositorFS_Linear = R"(#version 140
uniform uint u3DScale;
uniform usampler2D ScreenTex;
uniform sampler2D _3DTex;
smooth in vec2 fTexcoord;
out vec4 oColor;
ivec4 Get3DPixel(vec2 pos)
{
return ivec4(texelFetch(_3DTex, ivec2(pos*u3DScale), 0).bgra
* vec4(63,63,63,31));
}
ivec4 GetFullPixel(ivec4 val1, ivec4 val2, ivec4 val3, ivec4 _3dpix)
{
int compmode = val3.a & 0xF;
int eva, evb, evy;
if (compmode == 4)
{
// 3D on top, blending
if (_3dpix.a > 0)
{
eva = (_3dpix.a & 0x1F) + 1;
evb = 32 - eva;
val1 = ((_3dpix * eva) + (val1 * evb)) >> 5;
if (eva <= 16) val1 += ivec4(1,1,1,0);
val1 = min(val1, 0x3F);
}
else
val1 = val2;
}
else if (compmode == 1)
{
// 3D on bottom, blending
if (_3dpix.a > 0)
{
eva = val3.g;
evb = val3.b;
val1 = ((val1 * eva) + (_3dpix * evb)) >> 4;
val1 = min(val1, 0x3F);
}
else
val1 = val2;
}
else if (compmode <= 3)
{
// 3D on top, normal/fade
if (_3dpix.a > 0)
{
evy = val3.g;
val1 = _3dpix;
if (compmode == 2) val1 += ((ivec4(0x3F,0x3F,0x3F,0) - val1) * evy) >> 4;
else if (compmode == 3) val1 -= (val1 * evy) >> 4;
}
else
val1 = val2;
}
return val1;
}
ivec4 imix(ivec4 a, ivec4 b, float x)
{
return ivec4(vec4(a)*(1-x) + vec4(b)*x);
}
void main()
{
ivec4 pixel = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord), 0));
ivec4 mbright = ivec4(texelFetch(ScreenTex, ivec2(256*3, int(fTexcoord.y)), 0));
int dispmode = mbright.b & 0x3;
if (dispmode == 1)
{
ivec4 val1 = pixel;
ivec4 val2 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(256,0), 0));
ivec4 val3 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(512,0), 0));
float xfract = fract(fTexcoord.x);
float yfract = fract(fTexcoord.y);
float xpos = val3.r + xfract;
float ypos = mod(fTexcoord.y, 192);
ivec4 _3dpix = Get3DPixel(vec2(xpos,ypos));
ivec4 p00 = GetFullPixel(val1, val2, val3, _3dpix);
int xdisp = 1 - int(step(255, fTexcoord.x));
int ydisp = 1 - int(step(191, ypos));
ivec4 p01 = GetFullPixel(ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+0 ,0), 0)),
ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+256,0), 0)),
ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+512,0), 0)),
_3dpix);
ivec4 p10 = GetFullPixel(ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(0+0 ,ydisp), 0)),
ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(0+256,ydisp), 0)),
ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(0+512,ydisp), 0)),
_3dpix);
ivec4 p11 = GetFullPixel(ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+0 ,ydisp), 0)),
ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+256,ydisp), 0)),
ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(xdisp+512,ydisp), 0)),
_3dpix);
ivec4 pa = imix(p00, p01, xfract);
ivec4 pb = imix(p10, p11, xfract);
pixel = imix(pa, pb, yfract);
}
if (dispmode != 0)
{
int brightmode = mbright.g >> 6;
if (brightmode == 1)
{
// up
int evy = mbright.r & 0x1F;
if (evy > 16) evy = 16;
pixel += ((ivec4(0x3F,0x3F,0x3F,0) - pixel) * evy) >> 4;
}
else if (brightmode == 2)
{
// down
int evy = mbright.r & 0x1F;
if (evy > 16) evy = 16;
pixel -= (pixel * evy) >> 4;
}
}
pixel.rgb <<= 2;
pixel.rgb |= (pixel.rgb >> 6);
// TODO: filters
oColor = vec4(vec3(pixel.bgr) / 255.0, 1.0);
}
)";
// HUGE TEST ZONE ARRLGD
const char* kCompositorVS_xBRZ = R"(#version 140
#define BLEND_NONE 0
#define BLEND_NORMAL 1
#define BLEND_DOMINANT 2
#define LUMINANCE_WEIGHT 1.0
#define EQUAL_COLOR_TOLERANCE 30.0/255.0
#define STEEP_DIRECTION_THRESHOLD 2.2
#define DOMINANT_DIRECTION_THRESHOLD 3.6
#if __VERSION__ >= 130
#define COMPAT_VARYING out
#define COMPAT_ATTRIBUTE in
#define COMPAT_TEXTURE texture
#else
#define COMPAT_VARYING varying
#define COMPAT_ATTRIBUTE attribute
#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
COMPAT_ATTRIBUTE vec2 vPosition;
COMPAT_VARYING vec4 TEX0;
COMPAT_VARYING vec4 t1;
COMPAT_VARYING vec4 t2;
COMPAT_VARYING vec4 t3;
COMPAT_VARYING vec4 t4;
COMPAT_VARYING vec4 t5;
COMPAT_VARYING vec4 t6;
COMPAT_VARYING vec4 t7;
uniform COMPAT_PRECISION int FrameDirection;
uniform COMPAT_PRECISION int FrameCount;
uniform COMPAT_PRECISION vec2 OutputSize;
uniform COMPAT_PRECISION vec2 TextureSize;
uniform COMPAT_PRECISION vec2 InputSize;
// vertex compatibility #defines
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define outsize vec4(OutputSize, 1.0 / OutputSize)
void main()
{
vec4 fpos;
fpos.xy = vPosition;
fpos.z = 0.0;
fpos.w = 1.0;
gl_Position = fpos;
vec2 TexCoord = (vPosition + vec2(1.0, 1.0)) * (vec2(256.0, 384.0) / 2.0);
//gl_Position = MVPMatrix * VertexCoord;
//COL0 = COLOR;
TEX0.xy = TexCoord.xy;
vec2 ps = vec2(1,1);//vec2(SourceSize.z, SourceSize.w);
float dx = ps.x;
float dy = ps.y;
// A1 B1 C1
// A0 A B C C4
// D0 D E F F4
// G0 G H I I4
// G5 H5 I5
t1 = vTexCoord.xxxy + vec4( -dx, 0.0, dx,-2.0*dy); // A1 B1 C1
t2 = vTexCoord.xxxy + vec4( -dx, 0.0, dx, -dy); // A B C
t3 = vTexCoord.xxxy + vec4( -dx, 0.0, dx, 0.0); // D E F
t4 = vTexCoord.xxxy + vec4( -dx, 0.0, dx, dy); // G H I
t5 = vTexCoord.xxxy + vec4( -dx, 0.0, dx, 2.0*dy); // G5 H5 I5
t6 = vTexCoord.xyyy + vec4(-2.0*dx,-dy, 0.0, dy); // A0 D0 G0
t7 = vTexCoord.xyyy + vec4( 2.0*dx,-dy, 0.0, dy); // C4 F4 I4
}
)";
const char* kCompositorFS_xBRZ = R"(#version 140
#define BLEND_NONE 0
#define BLEND_NORMAL 1
#define BLEND_DOMINANT 2
#define LUMINANCE_WEIGHT 1.0
#define EQUAL_COLOR_TOLERANCE 30.0/255.0
#define STEEP_DIRECTION_THRESHOLD 2.2
#define DOMINANT_DIRECTION_THRESHOLD 3.6
#if __VERSION__ >= 130
#define COMPAT_VARYING in
//#define COMPAT_TEXTURE texture
#define FragColor oColor
#else
#define COMPAT_VARYING varying
#define FragColor gl_FragColor
//#define COMPAT_TEXTURE texture2D
#endif
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#define COMPAT_PRECISION mediump
#else
#define COMPAT_PRECISION
#endif
uniform uint u3DScale;
uniform usampler2D ScreenTex;
uniform sampler2D _3DTex;
smooth in vec2 fTexcoord;
out vec4 oColor;
//uniform COMPAT_PRECISION vec2 OutputSize;
//uniform COMPAT_PRECISION vec2 TextureSize;
#define TextureSize vec2(256,384)
//uniform COMPAT_PRECISION vec2 InputSize;
//uniform sampler2D Texture;
#define Texture 1312
COMPAT_VARYING vec4 TEX0;
COMPAT_VARYING vec4 t1;
COMPAT_VARYING vec4 t2;
COMPAT_VARYING vec4 t3;
COMPAT_VARYING vec4 t4;
COMPAT_VARYING vec4 t5;
COMPAT_VARYING vec4 t6;
COMPAT_VARYING vec4 t7;
// fragment compatibility #defines
#define Source Texture
#define vTexCoord TEX0.xy
#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
#define outsize vec4(OutputSize, 1.0 / OutputSize)
const float one_sixth = 1.0 / 6.0;
const float two_sixth = 2.0 / 6.0;
const float four_sixth = 4.0 / 6.0;
const float five_sixth = 5.0 / 6.0;
vec4 Get2DPixel(vec2 texcoord, int level)
{
ivec4 pixel = ivec4(texelFetch(ScreenTex, ivec2(texcoord) + ivec2(level*256,0), 0));
return vec4(pixel) / vec4(63.0, 63.0, 63.0, 31.0);
}
ivec4 Get3DPixel(vec2 pos)
{
return ivec4(texelFetch(_3DTex, ivec2(pos*u3DScale), 0).bgra
* vec4(63,63,63,31));
}
float reduce(const vec3 color)
{
return dot(color, vec3(65536.0, 256.0, 1.0));
}
float DistYCbCr(const vec3 pixA, const vec3 pixB)
{
const vec3 w = vec3(0.2627, 0.6780, 0.0593);
const float scaleB = 0.5 / (1.0 - w.b);
const float scaleR = 0.5 / (1.0 - w.r);
vec3 diff = pixA - pixB;
float Y = dot(diff, w);
float Cb = scaleB * (diff.b - Y);
float Cr = scaleR * (diff.r - Y);
return sqrt( ((LUMINANCE_WEIGHT * Y) * (LUMINANCE_WEIGHT * Y)) + (Cb * Cb) + (Cr * Cr) );
}
bool IsPixEqual(const vec3 pixA, const vec3 pixB)
{
return (DistYCbCr(pixA, pixB) < EQUAL_COLOR_TOLERANCE);
}
bool IsBlendingNeeded(const ivec4 blend)
{
return any(notEqual(blend, ivec4(BLEND_NONE)));
}
//---------------------------------------
// Input Pixel Mapping: --|21|22|23|--
// 19|06|07|08|09
// 18|05|00|01|10
// 17|04|03|02|11
// --|15|14|13|--
//
// Output Pixel Mapping: 20|21|22|23|24|25
// 19|06|07|08|09|26
// 18|05|00|01|10|27
// 17|04|03|02|11|28
// 16|15|14|13|12|29
// 35|34|33|32|31|30
ivec4 GetFiltered2DPixel(int level)
{
vec2 f = fract(vTexCoord.xy);// * SourceSize.xy);
//---------------------------------------
// Input Pixel Mapping: 20|21|22|23|24
// 19|06|07|08|09
// 18|05|00|01|10
// 17|04|03|02|11
// 16|15|14|13|12
vec3 src[25];
src[21] = Get2DPixel(t1.xw, level).rgb;
src[22] = Get2DPixel(t1.yw, level).rgb;
src[23] = Get2DPixel(t1.zw, level).rgb;
src[ 6] = Get2DPixel(t2.xw, level).rgb;
src[ 7] = Get2DPixel(t2.yw, level).rgb;
src[ 8] = Get2DPixel(t2.zw, level).rgb;
src[ 5] = Get2DPixel(t3.xw, level).rgb;
src[ 0] = Get2DPixel(t3.yw, level).rgb;
src[ 1] = Get2DPixel(t3.zw, level).rgb;
src[ 4] = Get2DPixel(t4.xw, level).rgb;
src[ 3] = Get2DPixel(t4.yw, level).rgb;
src[ 2] = Get2DPixel(t4.zw, level).rgb;
src[15] = Get2DPixel(t5.xw, level).rgb;
src[14] = Get2DPixel(t5.yw, level).rgb;
src[13] = Get2DPixel(t5.zw, level).rgb;
src[19] = Get2DPixel(t6.xy, level).rgb;
src[18] = Get2DPixel(t6.xz, level).rgb;
src[17] = Get2DPixel(t6.xw, level).rgb;
src[ 9] = Get2DPixel(t7.xy, level).rgb;
src[10] = Get2DPixel(t7.xz, level).rgb;
src[11] = Get2DPixel(t7.xw, level).rgb;
float v[9];
v[0] = reduce(src[0]);
v[1] = reduce(src[1]);
v[2] = reduce(src[2]);
v[3] = reduce(src[3]);
v[4] = reduce(src[4]);
v[5] = reduce(src[5]);
v[6] = reduce(src[6]);
v[7] = reduce(src[7]);
v[8] = reduce(src[8]);
ivec4 blendResult = ivec4(BLEND_NONE);
// Preprocess corners
// Pixel Tap Mapping: --|--|--|--|--
// --|--|07|08|--
// --|05|00|01|10
// --|04|03|02|11
// --|--|14|13|--
// Corner (1, 1)
if ( ((v[0] == v[1] && v[3] == v[2]) || (v[0] == v[3] && v[1] == v[2])) == false)
{
float dist_03_01 = DistYCbCr(src[ 4], src[ 0]) + DistYCbCr(src[ 0], src[ 8]) + DistYCbCr(src[14], src[ 2]) + DistYCbCr(src[ 2], src[10]) + (4.0 * DistYCbCr(src[ 3], src[ 1]));
float dist_00_02 = DistYCbCr(src[ 5], src[ 3]) + DistYCbCr(src[ 3], src[13]) + DistYCbCr(src[ 7], src[ 1]) + DistYCbCr(src[ 1], src[11]) + (4.0 * DistYCbCr(src[ 0], src[ 2]));
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_03_01) < dist_00_02;
blendResult[2] = ((dist_03_01 < dist_00_02) && (v[0] != v[1]) && (v[0] != v[3])) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
}
// Pixel Tap Mapping: --|--|--|--|--
// --|06|07|--|--
// 18|05|00|01|--
// 17|04|03|02|--
// --|15|14|--|--
// Corner (0, 1)
if ( ((v[5] == v[0] && v[4] == v[3]) || (v[5] == v[4] && v[0] == v[3])) == false)
{
float dist_04_00 = DistYCbCr(src[17], src[ 5]) + DistYCbCr(src[ 5], src[ 7]) + DistYCbCr(src[15], src[ 3]) + DistYCbCr(src[ 3], src[ 1]) + (4.0 * DistYCbCr(src[ 4], src[ 0]));
float dist_05_03 = DistYCbCr(src[18], src[ 4]) + DistYCbCr(src[ 4], src[14]) + DistYCbCr(src[ 6], src[ 0]) + DistYCbCr(src[ 0], src[ 2]) + (4.0 * DistYCbCr(src[ 5], src[ 3]));
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_05_03) < dist_04_00;
blendResult[3] = ((dist_04_00 > dist_05_03) && (v[0] != v[5]) && (v[0] != v[3])) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
}
// Pixel Tap Mapping: --|--|22|23|--
// --|06|07|08|09
// --|05|00|01|10
// --|--|03|02|--
// --|--|--|--|--
// Corner (1, 0)
if ( ((v[7] == v[8] && v[0] == v[1]) || (v[7] == v[0] && v[8] == v[1])) == false)
{
float dist_00_08 = DistYCbCr(src[ 5], src[ 7]) + DistYCbCr(src[ 7], src[23]) + DistYCbCr(src[ 3], src[ 1]) + DistYCbCr(src[ 1], src[ 9]) + (4.0 * DistYCbCr(src[ 0], src[ 8]));
float dist_07_01 = DistYCbCr(src[ 6], src[ 0]) + DistYCbCr(src[ 0], src[ 2]) + DistYCbCr(src[22], src[ 8]) + DistYCbCr(src[ 8], src[10]) + (4.0 * DistYCbCr(src[ 7], src[ 1]));
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_07_01) < dist_00_08;
blendResult[1] = ((dist_00_08 > dist_07_01) && (v[0] != v[7]) && (v[0] != v[1])) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
}
// Pixel Tap Mapping: --|21|22|--|--
// 19|06|07|08|--
// 18|05|00|01|--
// --|04|03|--|--
// --|--|--|--|--
// Corner (0, 0)
if ( ((v[6] == v[7] && v[5] == v[0]) || (v[6] == v[5] && v[7] == v[0])) == false)
{
float dist_05_07 = DistYCbCr(src[18], src[ 6]) + DistYCbCr(src[ 6], src[22]) + DistYCbCr(src[ 4], src[ 0]) + DistYCbCr(src[ 0], src[ 8]) + (4.0 * DistYCbCr(src[ 5], src[ 7]));
float dist_06_00 = DistYCbCr(src[19], src[ 5]) + DistYCbCr(src[ 5], src[ 3]) + DistYCbCr(src[21], src[ 7]) + DistYCbCr(src[ 7], src[ 1]) + (4.0 * DistYCbCr(src[ 6], src[ 0]));
bool dominantGradient = (DOMINANT_DIRECTION_THRESHOLD * dist_05_07) < dist_06_00;
blendResult[0] = ((dist_05_07 < dist_06_00) && (v[0] != v[5]) && (v[0] != v[7])) ? ((dominantGradient) ? BLEND_DOMINANT : BLEND_NORMAL) : BLEND_NONE;
}
vec3 dst[16];
dst[ 0] = src[0];
dst[ 1] = src[0];
dst[ 2] = src[0];
dst[ 3] = src[0];
dst[ 4] = src[0];
dst[ 5] = src[0];
dst[ 6] = src[0];
dst[ 7] = src[0];
dst[ 8] = src[0];
dst[ 9] = src[0];
dst[10] = src[0];
dst[11] = src[0];
dst[12] = src[0];
dst[13] = src[0];
dst[14] = src[0];
dst[15] = src[0];
// Scale pixel
if (IsBlendingNeeded(blendResult) == true)
{
float dist_01_04 = DistYCbCr(src[1], src[4]);
float dist_03_08 = DistYCbCr(src[3], src[8]);
bool haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_01_04 <= dist_03_08) && (v[0] != v[4]) && (v[5] != v[4]);
bool haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_03_08 <= dist_01_04) && (v[0] != v[8]) && (v[7] != v[8]);
bool needBlend = (blendResult[2] != BLEND_NONE);
bool doLineBlend = ( blendResult[2] >= BLEND_DOMINANT ||
((blendResult[1] != BLEND_NONE && !IsPixEqual(src[0], src[4])) ||
(blendResult[3] != BLEND_NONE && !IsPixEqual(src[0], src[8])) ||
(IsPixEqual(src[4], src[3]) && IsPixEqual(src[3], src[2]) && IsPixEqual(src[2], src[1]) && IsPixEqual(src[1], src[8]) && IsPixEqual(src[0], src[2]) == false) ) == false );
vec3 blendPix = ( DistYCbCr(src[0], src[1]) <= DistYCbCr(src[0], src[3]) ) ? src[1] : src[3];
dst[ 2] = mix(dst[ 2], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? ((haveSteepLine) ? 1.0/3.0 : 0.25) : ((haveSteepLine) ? 0.25 : 0.00)) : 0.00);
dst[ 9] = mix(dst[ 9], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.25 : 0.00);
dst[10] = mix(dst[10], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.75 : 0.00);
dst[11] = mix(dst[11], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.00 : ((haveShallowLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00);
dst[12] = mix(dst[12], blendPix, (needBlend) ? ((doLineBlend) ? 1.00 : 0.6848532563) : 0.00);
dst[13] = mix(dst[13], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.00 : ((haveSteepLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00);
dst[14] = mix(dst[14], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.75 : 0.00);
dst[15] = mix(dst[15], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.25 : 0.00);
dist_01_04 = DistYCbCr(src[7], src[2]);
dist_03_08 = DistYCbCr(src[1], src[6]);
haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_01_04 <= dist_03_08) && (v[0] != v[2]) && (v[3] != v[2]);
haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_03_08 <= dist_01_04) && (v[0] != v[6]) && (v[5] != v[6]);
needBlend = (blendResult[1] != BLEND_NONE);
doLineBlend = ( blendResult[1] >= BLEND_DOMINANT ||
!((blendResult[0] != BLEND_NONE && !IsPixEqual(src[0], src[2])) ||
(blendResult[2] != BLEND_NONE && !IsPixEqual(src[0], src[6])) ||
(IsPixEqual(src[2], src[1]) && IsPixEqual(src[1], src[8]) && IsPixEqual(src[8], src[7]) && IsPixEqual(src[7], src[6]) && !IsPixEqual(src[0], src[8])) ) );
blendPix = ( DistYCbCr(src[0], src[7]) <= DistYCbCr(src[0], src[1]) ) ? src[7] : src[1];
dst[ 1] = mix(dst[ 1], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? ((haveSteepLine) ? 1.0/3.0 : 0.25) : ((haveSteepLine) ? 0.25 : 0.00)) : 0.00);
dst[ 6] = mix(dst[ 6], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.25 : 0.00);
dst[ 7] = mix(dst[ 7], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.75 : 0.00);
dst[ 8] = mix(dst[ 8], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.00 : ((haveShallowLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00);
dst[ 9] = mix(dst[ 9], blendPix, (needBlend) ? ((doLineBlend) ? 1.00 : 0.6848532563) : 0.00);
dst[10] = mix(dst[10], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.00 : ((haveSteepLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00);
dst[11] = mix(dst[11], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.75 : 0.00);
dst[12] = mix(dst[12], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.25 : 0.00);
dist_01_04 = DistYCbCr(src[5], src[8]);
dist_03_08 = DistYCbCr(src[7], src[4]);
haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_01_04 <= dist_03_08) && (v[0] != v[8]) && (v[1] != v[8]);
haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_03_08 <= dist_01_04) && (v[0] != v[4]) && (v[3] != v[4]);
needBlend = (blendResult[0] != BLEND_NONE);
doLineBlend = ( blendResult[0] >= BLEND_DOMINANT ||
!((blendResult[3] != BLEND_NONE && !IsPixEqual(src[0], src[8])) ||
(blendResult[1] != BLEND_NONE && !IsPixEqual(src[0], src[4])) ||
(IsPixEqual(src[8], src[7]) && IsPixEqual(src[7], src[6]) && IsPixEqual(src[6], src[5]) && IsPixEqual(src[5], src[4]) && !IsPixEqual(src[0], src[6])) ) );
blendPix = ( DistYCbCr(src[0], src[5]) <= DistYCbCr(src[0], src[7]) ) ? src[5] : src[7];
dst[ 0] = mix(dst[ 0], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? ((haveSteepLine) ? 1.0/3.0 : 0.25) : ((haveSteepLine) ? 0.25 : 0.00)) : 0.00);
dst[15] = mix(dst[15], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.25 : 0.00);
dst[ 4] = mix(dst[ 4], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.75 : 0.00);
dst[ 5] = mix(dst[ 5], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.00 : ((haveShallowLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00);
dst[ 6] = mix(dst[ 6], blendPix, (needBlend) ? ((doLineBlend) ? 1.00 : 0.6848532563) : 0.00);
dst[ 7] = mix(dst[ 7], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.00 : ((haveSteepLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00);
dst[ 8] = mix(dst[ 8], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.75 : 0.00);
dst[ 9] = mix(dst[ 9], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.25 : 0.00);
dist_01_04 = DistYCbCr(src[3], src[6]);
dist_03_08 = DistYCbCr(src[5], src[2]);
haveShallowLine = (STEEP_DIRECTION_THRESHOLD * dist_01_04 <= dist_03_08) && (v[0] != v[6]) && (v[7] != v[6]);
haveSteepLine = (STEEP_DIRECTION_THRESHOLD * dist_03_08 <= dist_01_04) && (v[0] != v[2]) && (v[1] != v[2]);
needBlend = (blendResult[3] != BLEND_NONE);
doLineBlend = ( blendResult[3] >= BLEND_DOMINANT ||
!((blendResult[2] != BLEND_NONE && !IsPixEqual(src[0], src[6])) ||
(blendResult[0] != BLEND_NONE && !IsPixEqual(src[0], src[2])) ||
(IsPixEqual(src[6], src[5]) && IsPixEqual(src[5], src[4]) && IsPixEqual(src[4], src[3]) && IsPixEqual(src[3], src[2]) && !IsPixEqual(src[0], src[4])) ) );
blendPix = ( DistYCbCr(src[0], src[3]) <= DistYCbCr(src[0], src[5]) ) ? src[3] : src[5];
dst[ 3] = mix(dst[ 3], blendPix, (needBlend && doLineBlend) ? ((haveShallowLine) ? ((haveSteepLine) ? 1.0/3.0 : 0.25) : ((haveSteepLine) ? 0.25 : 0.00)) : 0.00);
dst[12] = mix(dst[12], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.25 : 0.00);
dst[13] = mix(dst[13], blendPix, (needBlend && doLineBlend && haveSteepLine) ? 0.75 : 0.00);
dst[14] = mix(dst[14], blendPix, (needBlend) ? ((doLineBlend) ? ((haveSteepLine) ? 1.00 : ((haveShallowLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00);
dst[15] = mix(dst[15], blendPix, (needBlend) ? ((doLineBlend) ? 1.00 : 0.6848532563) : 0.00);
dst[ 4] = mix(dst[ 4], blendPix, (needBlend) ? ((doLineBlend) ? ((haveShallowLine) ? 1.00 : ((haveSteepLine) ? 0.75 : 0.50)) : 0.08677704501) : 0.00);
dst[ 5] = mix(dst[ 5], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.75 : 0.00);
dst[ 6] = mix(dst[ 6], blendPix, (needBlend && doLineBlend && haveShallowLine) ? 0.25 : 0.00);
}
vec3 res = mix( mix( mix( mix(dst[ 6], dst[ 7], step(0.25, f.x)), mix(dst[ 8], dst[ 9], step(0.75, f.x)), step(0.50, f.x)),
mix( mix(dst[ 5], dst[ 0], step(0.25, f.x)), mix(dst[ 1], dst[10], step(0.75, f.x)), step(0.50, f.x)), step(0.25, f.y)),
mix( mix( mix(dst[ 4], dst[ 3], step(0.25, f.x)), mix(dst[ 2], dst[11], step(0.75, f.x)), step(0.50, f.x)),
mix( mix(dst[15], dst[14], step(0.25, f.x)), mix(dst[13], dst[12], step(0.75, f.x)), step(0.50, f.x)), step(0.75, f.y)),
step(0.50, f.y));
return ivec4(res * vec3(63,63,63), 0);
}
void main()
{
vec2 fTexcoord = vTexCoord.xy;
ivec4 pixel;// = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord), 0));
ivec4 mbright = ivec4(texelFetch(ScreenTex, ivec2(256*3, int(fTexcoord.y)), 0));
int dispmode = mbright.b & 0x3;
if (dispmode == 1)
{
ivec4 val1;// = pixel;
//ivec4 val2 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(256,0), 0));
ivec4 val3 = ivec4(texelFetch(ScreenTex, ivec2(fTexcoord) + ivec2(512,0), 0));
int compmode = val3.a & 0xF;
int eva, evb, evy;
float xpos = val3.r + fract(fTexcoord.x);
float ypos = mod(fTexcoord.y, 192);
ivec4 _3dpix = Get3DPixel(vec2(xpos, ypos));
if (compmode == 4)
{
// 3D on top, blending
if (_3dpix.a > 0)
{
eva = (_3dpix.a & 0x1F) + 1;
if (eva == 32)
{
val1 = _3dpix;
}
else
{
evb = 32 - eva;
val1 = GetFiltered2DPixel(0);
val1 = ((_3dpix * eva) + (val1 * evb)) >> 5;
if (eva <= 16) val1 += ivec4(1,1,1,0);
val1 = min(val1, 0x3F);
}
}
else
val1 = GetFiltered2DPixel(1);
}
else if (compmode == 1)
{
// 3D on bottom, blending
if (_3dpix.a > 0)
{
eva = val3.g;
evb = val3.b;
val1 = GetFiltered2DPixel(0);
val1 = ((val1 * eva) + (_3dpix * evb)) >> 4;
val1 = min(val1, 0x3F);
}
else
val1 = GetFiltered2DPixel(1);
}
else if (compmode <= 3)
{
// 3D on top, normal/fade
if (_3dpix.a > 0)
{
evy = val3.g;
val1 = _3dpix;
if (compmode == 2) val1 += ((ivec4(0x3F,0x3F,0x3F,0) - val1) * evy) >> 4;
else if (compmode == 3) val1 -= (val1 * evy) >> 4;
}
else
val1 = GetFiltered2DPixel(1);
}
else
val1 = GetFiltered2DPixel(0);
pixel = val1;
}
else
{
pixel = GetFiltered2DPixel(0);
}
if (dispmode != 0)
{
int brightmode = mbright.g >> 6;
if (brightmode == 1)
{
// up
int evy = mbright.r & 0x1F;
if (evy > 16) evy = 16;
pixel += ((ivec4(0x3F,0x3F,0x3F,0) - pixel) * evy) >> 4;
}
else if (brightmode == 2)
{
// down
int evy = mbright.r & 0x1F;
if (evy > 16) evy = 16;
pixel -= (pixel * evy) >> 4;
}
}
pixel.rgb <<= 2;
pixel.rgb |= (pixel.rgb >> 6);
FragColor = vec4(vec3(pixel.bgr) / 255.0, 1.0);
}
)";
#endif // GPU_OPENGL_SHADERS_H

View File

@ -33,6 +33,9 @@
#include "AREngine.h"
#include "Platform.h"
#include "DSi.h"
#include "DSi_SPI_TSC.h"
namespace NDS
{
@ -59,6 +62,8 @@ namespace NDS
//
// timings for GBA slot and wifi are set up at runtime
int ConsoleType;
u8 ARM9MemTimings[0x40000][4];
u8 ARM7MemTimings[0x20000][4];
@ -88,7 +93,8 @@ u32 CPUStop;
u8 ARM9BIOS[0x1000];
u8 ARM7BIOS[0x4000];
u8 MainRAM[MAIN_RAM_SIZE];
u8 MainRAM[0x1000000];
u32 MainRAMMask;
u8 SharedWRAM[0x8000];
u8 WRAMCnt;
@ -108,6 +114,7 @@ u8 ROMSeed1[2*8];
// IO shit
u32 IME[2];
u32 IE[2], IF[2];
u32 IE2, IF2;
u8 PostFlag9;
u8 PostFlag7;
@ -181,6 +188,8 @@ bool Init()
if (!RTC::Init()) return false;
if (!Wifi::Init()) return false;
if (!DSi::Init()) return false;
if (!AREngine::Init()) return false;
return true;
@ -205,6 +214,8 @@ void DeInit()
RTC::DeInit();
Wifi::DeInit();
DSi::DeInit();
AREngine::DeInit();
}
@ -238,7 +249,11 @@ void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq,
ARM9MemTimings[i][3] = S32;
}
ARM9->UpdateRegionTimings(addrstart<<14, addrend<<14);
addrstart <<= 14;
addrend <<= 14;
if (!addrend) addrend = 0xFFFFFFFF;
ARM9->UpdateRegionTimings(addrstart, addrend);
}
void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq)
@ -283,6 +298,8 @@ void InitTimings()
// TODO: +3c nonseq waitstate doesn't apply to DMA!
// but of course mainRAM always gets 8c nonseq waitstate
// TODO: DSi-specific timings!!
SetARM9RegionTimings(0x00000000, 0xFFFFFFFF, 32, 1 + 3, 1); // void
SetARM9RegionTimings(0xFFFF0000, 0xFFFFFFFF, 32, 1 + 3, 1); // BIOS
@ -308,6 +325,12 @@ void InitTimings()
void SetupDirectBoot()
{
if (ConsoleType == 1)
{
printf("!! DIRECT BOOT NOT SUPPORTED IN DSI MODE\n");
return;
}
u32 bootparams[8];
memcpy(bootparams, &NDSCart::CartROM[0x20], 8*4);
@ -416,7 +439,13 @@ void Reset()
RunningGame = false;
LastSysClockCycles = 0;
f = Platform::OpenLocalFile("bios9.bin", "rb");
memset(ARM9BIOS, 0, 0x1000);
memset(ARM7BIOS, 0, 0x4000);
// DS BIOSes are always loaded, even in DSi mode
// we need them for DS-compatible mode
f = Platform::OpenLocalFile(Config::BIOS9Path, "rb");
if (!f)
{
printf("ARM9 BIOS not found\n");
@ -433,7 +462,7 @@ void Reset()
fclose(f);
}
f = Platform::OpenLocalFile("bios7.bin", "rb");
f = Platform::OpenLocalFile(Config::BIOS7Path, "rb");
if (!f)
{
printf("ARM7 BIOS not found\n");
@ -450,8 +479,19 @@ void Reset()
fclose(f);
}
// TODO for later: configure this when emulating a DSi
ARM9ClockShift = 1;
if (ConsoleType == 1)
{
DSi::LoadBIOS();
DSi::LoadNAND();
ARM9ClockShift = 2;
MainRAMMask = 0xFFFFFF;
}
else
{
ARM9ClockShift = 1;
MainRAMMask = 0x3FFFFF;
}
ARM9Timestamp = 0; ARM9Target = 0;
ARM7Timestamp = 0; ARM7Target = 0;
@ -459,14 +499,14 @@ void Reset()
InitTimings();
memset(MainRAM, 0, MAIN_RAM_SIZE);
memset(MainRAM, 0, 0x1000000);
memset(SharedWRAM, 0, 0x8000);
memset(ARM7WRAM, 0, 0x10000);
MapSharedWRAM(0);
ExMemCnt[0] = 0;
ExMemCnt[1] = 0;
ExMemCnt[0] = 0x4000;
ExMemCnt[1] = 0x4000;
memset(ROMSeed0, 0, 2*8);
memset(ROMSeed1, 0, 2*8);
SetGBASlotTimings();
@ -477,6 +517,8 @@ void Reset()
IME[1] = 0;
IE[1] = 0;
IF[1] = 0;
IE2 = 0;
IF2 = 0;
PostFlag9 = 0x00;
PostFlag7 = 0x00;
@ -527,6 +569,12 @@ void Reset()
RTC::Reset();
Wifi::Reset();
if (ConsoleType == 1)
{
DSi::Reset();
KeyInput &= ~(1 << (16+6));
}
AREngine::Reset();
}
@ -587,7 +635,7 @@ bool DoSavestate_Scheduler(Savestate* file)
}
if (funcid == -1)
{
printf("savestate: VERY BAD!!!!! FUNCTION POINTER FOR EVENT %d NOT IN HACKY LIST. CANNOT SAVE. SMACK STAPLEBUTTER.\n", i);
printf("savestate: VERY BAD!!!!! FUNCTION POINTER FOR EVENT %d NOT IN HACKY LIST. CANNOT SAVE. SMACK ARISOTURA.\n", i);
return false;
}
}
@ -635,6 +683,11 @@ bool DoSavestate(Savestate* file)
{
file->Section("NDSG");
// TODO:
// * do something for bool's (sizeof=1)
// * do something for 'loading DSi-mode savestate in DS mode' and vice-versa
// * add IE2/IF2 there
file->VarArray(MainRAM, 0x400000);
file->VarArray(SharedWRAM, 0x8000);
file->VarArray(ARM7WRAM, 0x10000);
@ -737,6 +790,11 @@ bool DoSavestate(Savestate* file)
return true;
}
void SetConsoleType(int type)
{
ConsoleType = type;
}
bool LoadROM(const char* path, const char* sram, bool direct)
{
if (NDSCart::LoadROM(path, sram, direct))
@ -848,6 +906,7 @@ u32 RunFrame()
if (!(CPUStop & 0x80000000)) DMAs[1]->Run();
if (!(CPUStop & 0x80000000)) DMAs[2]->Run();
if (!(CPUStop & 0x80000000)) DMAs[3]->Run();
if (ConsoleType == 1) DSi::RunNDMAs(0);
}
else
{
@ -870,6 +929,7 @@ u32 RunFrame()
DMAs[5]->Run();
DMAs[6]->Run();
DMAs[7]->Run();
if (ConsoleType == 1) DSi::RunNDMAs(1);
}
else
{
@ -951,24 +1011,30 @@ void CancelEvent(u32 id)
}
void PressKey(u32 key)
{
KeyInput &= ~(1 << key);
}
void ReleaseKey(u32 key)
{
KeyInput |= (1 << key);
}
void TouchScreen(u16 x, u16 y)
{
SPI_TSC::SetTouchCoords(x, y);
if (ConsoleType == 1)
{
DSi_SPI_TSC::SetTouchCoords(x, y);
}
else
{
SPI_TSC::SetTouchCoords(x, y);
KeyInput &= ~(1 << (16+6));
}
}
void ReleaseScreen()
{
SPI_TSC::SetTouchCoords(0x000, 0xFFF);
if (ConsoleType == 1)
{
DSi_SPI_TSC::SetTouchCoords(0x000, 0xFFF);
}
else
{
SPI_TSC::SetTouchCoords(0x000, 0xFFF);
KeyInput |= (1 << (16+6));
}
}
@ -981,6 +1047,12 @@ void SetKeyMask(u32 mask)
KeyInput |= key_lo | (key_hi << 16);
}
bool IsLidClosed()
{
if (KeyInput & (1<<23)) return true;
return false;
}
void SetLidClosed(bool closed)
{
if (closed)
@ -1095,6 +1167,8 @@ void UpdateIRQ(u32 cpu)
if (IME[cpu] & 0x1)
{
arm->IRQ = IE[cpu] & IF[cpu];
if ((ConsoleType == 1) && cpu)
arm->IRQ |= (IE2 & IF2);
}
else
{
@ -1114,6 +1188,18 @@ void ClearIRQ(u32 cpu, u32 irq)
UpdateIRQ(cpu);
}
void SetIRQ2(u32 irq)
{
IF2 |= (1 << irq);
UpdateIRQ(1);
}
void ClearIRQ2(u32 irq)
{
IF2 &= ~(1 << irq);
UpdateIRQ(1);
}
bool HaltInterrupted(u32 cpu)
{
if (cpu == 0)
@ -1161,6 +1247,7 @@ void GXFIFOStall()
DMAs[1]->StallIfRunning();
DMAs[2]->StallIfRunning();
DMAs[3]->StallIfRunning();
if (ConsoleType == 1) DSi::StallNDMAs();
}
}
@ -1298,6 +1385,7 @@ void NocashPrint(u32 ncpu, u32 addr)
void MonitorARM9Jump(u32 addr)
{
// checkme: can the entrypoint addr be THUMB?
// also TODO: make it work in DSi mode
if ((!RunningGame) && NDSCart::CartROM)
{
@ -1374,6 +1462,29 @@ void RunTimers(u32 cpu)
// matching NDMA modes for DSi
const u32 NDMAModes[] =
{
// ARM9
0x10, // immediate
0x06, // VBlank
0x07, // HBlank
0x08, // scanline start
0x09, // mainmem FIFO
0x04, // DS cart slot
0xFF, // GBA cart slot
0x0A, // GX FIFO
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// ARM7
0x30, // immediate
0x26, // VBlank
0x24, // DS cart slot
0xFF, // wifi / GBA cart slot (TODO)
};
bool DMAsInMode(u32 cpu, u32 mode)
{
cpu <<= 2;
@ -1381,6 +1492,13 @@ bool DMAsInMode(u32 cpu, u32 mode)
if (DMAs[cpu+1]->IsInMode(mode)) return true;
if (DMAs[cpu+2]->IsInMode(mode)) return true;
if (DMAs[cpu+3]->IsInMode(mode)) return true;
if (ConsoleType == 1)
{
cpu >>= 2;
return DSi::NDMAsInMode(cpu, NDMAModes[mode]);
}
return false;
}
@ -1391,6 +1509,10 @@ bool DMAsRunning(u32 cpu)
if (DMAs[cpu+1]->IsRunning()) return true;
if (DMAs[cpu+2]->IsRunning()) return true;
if (DMAs[cpu+3]->IsRunning()) return true;
if (ConsoleType == 1)
{
if (DSi::NDMAsRunning(cpu>>2)) return true;
}
return false;
}
@ -1401,6 +1523,12 @@ void CheckDMAs(u32 cpu, u32 mode)
DMAs[cpu+1]->StartIfNeeded(mode);
DMAs[cpu+2]->StartIfNeeded(mode);
DMAs[cpu+3]->StartIfNeeded(mode);
if (ConsoleType == 1)
{
cpu >>= 2;
DSi::CheckNDMAs(cpu, NDMAModes[mode]);
}
}
void StopDMAs(u32 cpu, u32 mode)
@ -1410,6 +1538,12 @@ void StopDMAs(u32 cpu, u32 mode)
DMAs[cpu+1]->StopIfNeeded(mode);
DMAs[cpu+2]->StopIfNeeded(mode);
DMAs[cpu+3]->StopIfNeeded(mode);
if (ConsoleType == 1)
{
cpu >>= 2;
DSi::StopNDMAs(cpu, NDMAModes[mode]);
}
}
@ -1602,12 +1736,12 @@ void debug(u32 param)
printf("ARM7 PC=%08X LR=%08X %08X\n", ARM7->R[15], ARM7->R[14], ARM7->R_IRQ[1]);
printf("ARM9 IME=%08X IE=%08X IF=%08X\n", IME[0], IE[0], IF[0]);
printf("ARM7 IME=%08X IE=%08X IF=%08X\n", IME[1], IE[1], IF[1]);
printf("ARM7 IME=%08X IE=%08X IF=%08X IE2=%04X IF2=%04X\n", IME[1], IE[1], IF[1], IE2, IF2);
//for (int i = 0; i < 9; i++)
// printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]);
FILE*
/*FILE*
shit = fopen("debug/party.bin", "wb");
fwrite(ARM9->ITCM, 0x8000, 1, shit);
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
@ -1620,6 +1754,21 @@ void debug(u32 param)
u32 val = ARM7Read32(i);
fwrite(&val, 4, 1, shit);
}
fclose(shit);*/
FILE*
/*shit = fopen("debug/dump9.bin", "wb");
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
{
u32 val = DSi::ARM9Read32(i);
fwrite(&val, 4, 1, shit);
}
fclose(shit);*/
shit = fopen("debug/dump7_2.bin", "wb");
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
{
u32 val = DSi::ARM7Read32(i);
fwrite(&val, 4, 1, shit);
}
fclose(shit);
}
@ -1635,7 +1784,7 @@ u8 ARM9Read8(u32 addr)
switch (addr & 0xFF000000)
{
case 0x02000000:
return *(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
return *(u8*)&MainRAM[addr & MainRAMMask];
case 0x03000000:
if (SWRAM_ARM9)
@ -1700,7 +1849,7 @@ u16 ARM9Read16(u32 addr)
switch (addr & 0xFF000000)
{
case 0x02000000:
return *(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
return *(u16*)&MainRAM[addr & MainRAMMask];
case 0x03000000:
if (SWRAM_ARM9)
@ -1765,7 +1914,7 @@ u32 ARM9Read32(u32 addr)
switch (addr & 0xFF000000)
{
case 0x02000000:
return *(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
return *(u32*)&MainRAM[addr & MainRAMMask];
case 0x03000000:
if (SWRAM_ARM9)
@ -1825,7 +1974,7 @@ void ARM9Write8(u32 addr, u8 val)
switch (addr & 0xFF000000)
{
case 0x02000000:
*(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
*(u8*)&MainRAM[addr & MainRAMMask] = val;
return;
case 0x03000000:
@ -1875,7 +2024,7 @@ void ARM9Write16(u32 addr, u16 val)
switch (addr & 0xFF000000)
{
case 0x02000000:
*(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
*(u16*)&MainRAM[addr & MainRAMMask] = val;
return;
case 0x03000000:
@ -1941,7 +2090,7 @@ void ARM9Write32(u32 addr, u32 val)
switch (addr & 0xFF000000)
{
case 0x02000000:
*(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
*(u32*)&MainRAM[addr & MainRAMMask] = val;
return ;
case 0x03000000:
@ -2009,7 +2158,7 @@ bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
{
case 0x02000000:
region->Mem = MainRAM;
region->Mask = MAIN_RAM_SIZE-1;
region->Mask = MainRAMMask;
return true;
case 0x03000000:
@ -2039,7 +2188,8 @@ u8 ARM7Read8(u32 addr)
{
if (addr < 0x00004000)
{
if (ARM7->R[15] >= 0x4000)
// TODO: check the boundary? is it 4000 or higher on regular DS?
if (ARM7->R[15] >= 0x00004000)
return 0xFF;
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
return 0xFF;
@ -2051,7 +2201,7 @@ u8 ARM7Read8(u32 addr)
{
case 0x02000000:
case 0x02800000:
return *(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
return *(u8*)&MainRAM[addr & MainRAMMask];
case 0x03000000:
if (SWRAM_ARM7)
@ -2099,7 +2249,7 @@ u16 ARM7Read16(u32 addr)
{
if (addr < 0x00004000)
{
if (ARM7->R[15] >= 0x4000)
if (ARM7->R[15] >= 0x00004000)
return 0xFFFF;
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
return 0xFFFF;
@ -2111,7 +2261,7 @@ u16 ARM7Read16(u32 addr)
{
case 0x02000000:
case 0x02800000:
return *(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
return *(u16*)&MainRAM[addr & MainRAMMask];
case 0x03000000:
if (SWRAM_ARM7)
@ -2166,7 +2316,7 @@ u32 ARM7Read32(u32 addr)
{
if (addr < 0x00004000)
{
if (ARM7->R[15] >= 0x4000)
if (ARM7->R[15] >= 0x00004000)
return 0xFFFFFFFF;
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
return 0xFFFFFFFF;
@ -2178,7 +2328,7 @@ u32 ARM7Read32(u32 addr)
{
case 0x02000000:
case 0x02800000:
return *(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
return *(u32*)&MainRAM[addr & MainRAMMask];
case 0x03000000:
if (SWRAM_ARM7)
@ -2235,7 +2385,7 @@ void ARM7Write8(u32 addr, u8 val)
{
case 0x02000000:
case 0x02800000:
*(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
*(u8*)&MainRAM[addr & MainRAMMask] = val;
return;
case 0x03000000:
@ -2294,7 +2444,7 @@ void ARM7Write16(u32 addr, u16 val)
{
case 0x02000000:
case 0x02800000:
*(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
*(u16*)&MainRAM[addr & MainRAMMask] = val;
return;
case 0x03000000:
@ -2363,7 +2513,7 @@ void ARM7Write32(u32 addr, u32 val)
{
case 0x02000000:
case 0x02800000:
*(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
*(u32*)&MainRAM[addr & MainRAMMask] = val;
return;
case 0x03000000:
@ -2435,7 +2585,7 @@ bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region)
case 0x02000000:
case 0x02800000:
region->Mem = MainRAM;
region->Mask = MAIN_RAM_SIZE-1;
region->Mask = MainRAMMask;
return true;
case 0x03000000:

View File

@ -42,6 +42,11 @@ enum
Event_Div,
Event_Sqrt,
// DSi
Event_DSi_SDMMCTransfer,
Event_DSi_SDIOTransfer,
Event_DSi_NWifi,
Event_MAX
};
@ -74,12 +79,42 @@ enum
IRQ_IPCSync,
IRQ_IPCSendDone,
IRQ_IPCRecv,
IRQ_CartSendDone,
IRQ_CartIREQMC,
IRQ_CartSendDone, // TODO: less misleading name
IRQ_CartIREQMC, // IRQ triggered by game cart (example: Pokémon Typing Adventure, BT controller)
IRQ_GXFIFO,
IRQ_LidOpen,
IRQ_SPI,
IRQ_Wifi
IRQ_Wifi,
// DSi IRQs
IRQ_DSi_DSP = 24,
IRQ_DSi_Camera,
IRQ_DSi_Unk26,
IRQ_DSi_Unk27,
IRQ_DSi_NDMA0,
IRQ_DSi_NDMA1,
IRQ_DSi_NDMA2,
IRQ_DSi_NDMA3,
};
enum
{
// DSi ARM7-side IE2/IF2
IRQ2_DSi_GPIO18_0 = 0,
IRQ2_DSi_GPIO18_1,
IRQ2_DSi_GPIO18_2,
IRQ2_DSi_Unused3,
IRQ2_DSi_GPIO33_0,
IRQ2_DSi_Headphone,
IRQ2_DSi_PowerButton,
IRQ2_DSi_GPIO33_3, // "sound enable input"
IRQ2_DSi_SDMMC,
IRQ2_DSi_SD_Data1,
IRQ2_DSi_SDIO,
IRQ2_DSi_SDIO_Data1,
IRQ2_DSi_AES,
IRQ2_DSi_I2C,
IRQ2_DSi_MicExt
};
typedef struct
@ -98,6 +133,8 @@ typedef struct
} MemRegion;
extern int ConsoleType;
extern u8 ARM9MemTimings[0x40000][4];
extern u8 ARM7MemTimings[0x20000][4];
@ -105,12 +142,15 @@ extern u64 ARM9Timestamp, ARM9Target;
extern u64 ARM7Timestamp, ARM7Target;
extern u32 ARM9ClockShift;
// hax
extern u32 IME[2];
extern u32 IE[2];
extern u32 IF[2];
extern u32 IE2;
extern u32 IF2;
extern Timer Timers[8];
extern u32 CPUStop;
extern u16 PowerControl9;
extern u16 ExMemCnt[2];
@ -119,10 +159,12 @@ extern u8 ROMSeed1[2*8];
extern u8 ARM9BIOS[0x1000];
extern u8 ARM7BIOS[0x4000];
extern u16 ARM7BIOSProt;
#define MAIN_RAM_SIZE 0x400000
extern u8 MainRAM[0x1000000];
extern u32 MainRAMMask;
extern u8 MainRAM[MAIN_RAM_SIZE];
extern u32 KeyInput;
bool Init();
void DeInit();
@ -134,6 +176,9 @@ bool DoSavestate(Savestate* file);
void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
// 0=DS 1=DSi
void SetConsoleType(int type);
bool LoadROM(const char* path, const char* sram, bool direct);
bool LoadGBAROM(const char* path, const char* sram);
void LoadBIOS();
@ -142,13 +187,12 @@ void RelocateSave(const char* path, bool write);
u32 RunFrame();
void PressKey(u32 key);
void ReleaseKey(u32 key);
void TouchScreen(u16 x, u16 y);
void ReleaseScreen();
void SetKeyMask(u32 mask);
bool IsLidClosed();
void SetLidClosed(bool closed);
void MicInputFrame(s16* data, int samples);
@ -162,8 +206,11 @@ void Halt();
void MapSharedWRAM(u8 val);
void UpdateIRQ(u32 cpu);
void SetIRQ(u32 cpu, u32 irq);
void ClearIRQ(u32 cpu, u32 irq);
void SetIRQ2(u32 irq);
void ClearIRQ2(u32 irq);
bool HaltInterrupted(u32 cpu);
void StopCPU(u32 cpu, u32 mask);
void ResumeCPU(u32 cpu, u32 mask);

View File

@ -19,10 +19,13 @@
#include <stdio.h>
#include <string.h>
#include "NDS.h"
#include "DSi.h"
#include "NDSCart.h"
#include "ARM.h"
#include "CRC32.h"
#include "DSi_AES.h"
#include "Platform.h"
#include "ROMList.h"
namespace NDSCart_SRAM
@ -473,6 +476,7 @@ u32 CartROMSize;
u32 CartCRC;
u32 CartID;
bool CartIsHomebrew;
bool CartIsDSi;
u32 CmdEncMode;
u32 DataEncMode;
@ -555,9 +559,13 @@ void Key1_ApplyKeycode(u32* keycode, u32 mod)
}
}
void Key1_InitKeycode(u32 idcode, u32 level, u32 mod)
void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod)
{
memcpy(Key1_KeyBuf, &NDS::ARM7BIOS[0x30], 0x1048); // hax
// TODO: source the key data from different possible places
if (dsi && NDS::ConsoleType==1)
memcpy(Key1_KeyBuf, &DSi::ARM7iBIOS[0xC6D0], 0x1048); // hax
else
memcpy(Key1_KeyBuf, &NDS::ARM7BIOS[0x30], 0x1048); // hax
u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
if (level >= 1) Key1_ApplyKeycode(keycode, mod);
@ -592,6 +600,15 @@ void Key2_Encrypt(u8* data, u32 len)
}
void ApplyModcrypt(u32 addr, u32 len, u8* iv)
{return;
u8 key[16];
DSi_AES::GetModcryptKey(&CartROM[0], key);
DSi_AES::ApplyModcrypt(&CartROM[addr], len, key, iv);
}
bool Init()
{
if (!NDSCart_SRAM::Init()) return false;
@ -610,32 +627,19 @@ void DeInit()
void Reset()
{
SPICnt = 0;
ROMCnt = 0;
memset(ROMCommand, 0, 8);
ROMDataOut = 0;
Key2_X = 0;
Key2_Y = 0;
memset(DataOut, 0, 0x4000);
DataOutPos = 0;
DataOutLen = 0;
CartInserted = false;
if (CartROM) delete[] CartROM;
CartROM = NULL;
CartROMSize = 0;
CartID = 0;
CartIsHomebrew = false;
CartIsDSi = false;
ROMCommandHandler = NULL;
CmdEncMode = 0;
DataEncMode = 0;
NDSCart_SRAM::Reset();
ResetCart();
}
void DoSavestate(Savestate* file)
@ -808,34 +812,21 @@ void ApplyDLDIPatch()
}
bool ReadROMParams(u32 gamecode, u32* params)
bool ReadROMParams(u32 gamecode, ROMListEntry* params)
{
// format for romlist.bin:
// [gamecode] [ROM size] [save type] [reserved]
// list must be sorted by gamecode
FILE* f = Platform::OpenDataFile("romlist.bin");
if (!f) return false;
fseek(f, 0, SEEK_END);
u32 len = (u32)ftell(f);
u32 maxlen = len;
len >>= 4; // 16 bytes per entry
u32 len = sizeof(ROMList) / sizeof(ROMListEntry);
u32 offset = 0;
u32 chk_size = len >> 1;
for (;;)
{
u32 key = 0;
fseek(f, offset + (chk_size << 4), SEEK_SET);
fread(&key, 4, 1, f);
printf("chk_size=%d, key=%08X, wanted=%08X, offset=%08X\n", chk_size, key, gamecode, offset);
ROMListEntry* curentry = &ROMList[offset + chk_size];
key = curentry->GameCode;
if (key == gamecode)
{
fread(params, 4, 3, f);
fclose(f);
memcpy(params, curentry, sizeof(ROMListEntry));
return true;
}
else
@ -843,22 +834,20 @@ bool ReadROMParams(u32 gamecode, u32* params)
if (key < gamecode)
{
if (chk_size == 0)
offset += 0x10;
offset++;
else
offset += (chk_size << 4);
offset += chk_size;
}
else if (chk_size == 0)
{
fclose(f);
return false;
}
chk_size >>= 1;
}
if (offset >= maxlen)
if (offset >= len)
{
fclose(f);
return false;
}
}
@ -867,15 +856,20 @@ bool ReadROMParams(u32 gamecode, u32* params)
void DecryptSecureArea(u8* out)
{
// TODO: source decryption data from different possible sources
// * original DS-mode ARM7 BIOS has the key data at 0x30
// * .srl ROMs (VC dumps) have encrypted secure areas but have precomputed
// decryption data at 0x1000 (and at the beginning of the DSi region if any)
u32 gamecode = *(u32*)&CartROM[0x0C];
u32 arm9base = *(u32*)&CartROM[0x20];
memcpy(out, &CartROM[arm9base], 0x800);
Key1_InitKeycode(gamecode, 2, 2);
Key1_InitKeycode(false, gamecode, 2, 2);
Key1_Decrypt((u32*)&out[0]);
Key1_InitKeycode(gamecode, 3, 2);
Key1_InitKeycode(false, gamecode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8)
Key1_Decrypt((u32*)&out[i]);
@ -898,6 +892,7 @@ bool LoadROM(const char* path, const char* sram, bool direct)
{
// TODO: streaming mode? for really big ROMs or systems with limited RAM
// for now we're lazy
// also TODO: validate what we're loading!!
FILE* f = Platform::OpenFile(path, "rb");
if (!f)
@ -919,6 +914,11 @@ bool LoadROM(const char* path, const char* sram, bool direct)
fread(&gamecode, 4, 1, f);
printf("Game code: %c%c%c%c\n", gamecode&0xFF, (gamecode>>8)&0xFF, (gamecode>>16)&0xFF, gamecode>>24);
u8 unitcode;
fseek(f, 0x12, SEEK_SET);
fread(&unitcode, 1, 1, f);
CartIsDSi = (unitcode & 0x02) != 0;
CartROM = new u8[CartROMSize];
memset(CartROM, 0, CartROMSize);
fseek(f, 0, SEEK_SET);
@ -930,22 +930,23 @@ bool LoadROM(const char* path, const char* sram, bool direct)
CartCRC = CRC32(CartROM, CartROMSize);
printf("ROM CRC32: %08X\n", CartCRC);
u32 romparams[3];
if (!ReadROMParams(gamecode, romparams))
ROMListEntry romparams;
if (!ReadROMParams(gamecode, &romparams))
{
// set defaults
printf("ROM entry not found\n");
romparams[0] = CartROMSize;
romparams.GameCode = gamecode;
romparams.ROMSize = CartROMSize;
if (*(u32*)&CartROM[0x20] < 0x4000)
romparams[1] = 0; // no saveRAM for homebrew
romparams.SaveMemType = 0; // no saveRAM for homebrew
else
romparams[1] = 2; // assume EEPROM 64k (TODO FIXME)
romparams.SaveMemType = 2; // assume EEPROM 64k (TODO FIXME)
}
else
printf("ROM entry: %08X %08X %08X\n", romparams[0], romparams[1], romparams[2]);
printf("ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType);
if (romparams[0] != len) printf("!! bad ROM size %d (expected %d) rounded to %d\n", len, romparams[0], CartROMSize);
if (romparams.ROMSize != len) printf("!! bad ROM size %d (expected %d) rounded to %d\n", len, romparams.ROMSize, CartROMSize);
// generate a ROM ID
// note: most games don't check the actual value
@ -957,9 +958,12 @@ bool LoadROM(const char* path, const char* sram, bool direct)
else
CartID |= (0x100 - (CartROMSize >> 28)) << 8;
if (romparams[1] == 8)
if (romparams.SaveMemType == 8)
CartID |= 0x08000000; // NAND flag
if (CartIsDSi)
CartID |= 0x40000000;
printf("Cart ID: %08X\n", CartID);
u32 arm9base = *(u32*)&CartROM[0x20];
@ -975,11 +979,11 @@ bool LoadROM(const char* path, const char* sram, bool direct)
strncpy((char*)&CartROM[arm9base], "encryObj", 8);
Key1_InitKeycode(gamecode, 3, 2);
Key1_InitKeycode(false, gamecode, 3, 2);
for (u32 i = 0; i < 0x800; i += 8)
Key1_Encrypt((u32*)&CartROM[arm9base + i]);
Key1_InitKeycode(gamecode, 2, 2);
Key1_InitKeycode(false, gamecode, 2, 2);
Key1_Encrypt((u32*)&CartROM[arm9base]);
}
}
@ -1007,12 +1011,11 @@ bool LoadROM(const char* path, const char* sram, bool direct)
ROMCommandHandler = ROMCommand_Retail;
// encryption
Key1_InitKeycode(gamecode, 2, 2);
Key1_InitKeycode(false, gamecode, 2, 2);
// save
printf("Save file: %s\n", sram);
NDSCart_SRAM::LoadSave(sram, romparams[1]);
NDSCart_SRAM::LoadSave(sram, romparams.SaveMemType);
return true;
}
@ -1023,6 +1026,27 @@ void RelocateSave(const char* path, bool write)
NDSCart_SRAM::RelocateSave(path, write);
}
void ResetCart()
{
// CHECKME: what if there is a transfer in progress?
SPICnt = 0;
ROMCnt = 0;
memset(ROMCommand, 0, 8);
ROMDataOut = 0;
Key2_X = 0;
Key2_Y = 0;
memset(DataOut, 0, 0x4000);
DataOutPos = 0;
DataOutLen = 0;
CmdEncMode = 0;
DataEncMode = 0;
}
void ReadROM(u32 addr, u32 len, u32 offset)
{
if (!CartInserted) return;
@ -1197,7 +1221,7 @@ void WriteROMCnt(u32 val)
// handle KEY1 encryption as needed.
// KEY2 encryption is implemented in hardware and doesn't need to be handled.
u8 cmd[8];
if (CmdEncMode == 1)
if (CmdEncMode == 1 || CmdEncMode == 11)
{
*(u32*)&cmd[0] = ByteSwap(*(u32*)&ROMCommand[4]);
*(u32*)&cmd[4] = ByteSwap(*(u32*)&ROMCommand[0]);
@ -1243,11 +1267,23 @@ void WriteROMCnt(u32 val)
break;
case 0x3C:
if (CartInserted) CmdEncMode = 1;
if (CartInserted)
{
CmdEncMode = 1;
Key1_InitKeycode(false, *(u32*)&CartROM[0xC], 2, 2);
}
break;
case 0x3D:
if (CartInserted && CartIsDSi)
{
CmdEncMode = 11;
Key1_InitKeycode(true, *(u32*)&CartROM[0xC], 1, 2);
}
break;
default:
if (CmdEncMode == 1)
if (CmdEncMode == 1 || CmdEncMode == 11)
{
switch (cmd[0] & 0xF0)
{
@ -1263,6 +1299,12 @@ void WriteROMCnt(u32 val)
case 0x20:
{
u32 addr = (cmd[2] & 0xF0) << 8;
if (CmdEncMode == 11)
{
u32 arm9i_base = *(u32*)&CartROM[0x1C0];
addr -= 0x4000;
addr += arm9i_base;
}
ReadROM(addr, 0x1000, 0);
}
break;

View File

@ -48,6 +48,8 @@ void DecryptSecureArea(u8* out);
bool LoadROM(const char* path, const char* sram, bool direct);
void RelocateSave(const char* path, bool write);
void ResetCart();
void WriteROMCnt(u32 val);
u32 ReadROMData();

View File

@ -19,18 +19,20 @@
#include "OpenGLSupport.h"
namespace OpenGL
{
DO_PROCLIST(DECLPROC);
bool OpenGL_Init()
bool Init()
{
DO_PROCLIST(LOADPROC);
return true;
}
bool OpenGL_BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name)
bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name)
{
int len;
int res;
@ -89,7 +91,7 @@ bool OpenGL_BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, cons
return true;
}
bool OpenGL_LinkShaderProgram(GLuint* ids)
bool LinkShaderProgram(GLuint* ids)
{
int res;
@ -115,14 +117,16 @@ bool OpenGL_LinkShaderProgram(GLuint* ids)
return true;
}
void OpenGL_DeleteShaderProgram(GLuint* ids)
void DeleteShaderProgram(GLuint* ids)
{
glDeleteShader(ids[0]);
glDeleteShader(ids[1]);
glDeleteProgram(ids[2]);
}
void OpenGL_UseShaderProgram(GLuint* ids)
void UseShaderProgram(GLuint* ids)
{
glUseProgram(ids[2]);
}
}

View File

@ -21,6 +21,8 @@
#include <stdio.h>
#include <string.h>
// TODO: different includes for each platform
#include <GL/gl.h>
#include <GL/glext.h>
@ -45,18 +47,18 @@
// if you need more OpenGL functions, add them to the macronator here
// TODO: handle conditionally loading certain functions for different GL versions
#ifndef __WIN32__
#define DO_PROCLIST_1_3(func)
#else
#ifdef __WIN32__
#define DO_PROCLIST_1_3(func) \
func(GLACTIVETEXTURE, glActiveTexture); \
func(GLBLENDCOLOR, glBlendColor); \
#else
#define DO_PROCLIST_1_3(func)
#endif
@ -112,6 +114,11 @@
func(GLGETUNIFORMLOCATION, glGetUniformLocation); \
func(GLGETUNIFORMBLOCKINDEX, glGetUniformBlockIndex); \
\
func(GLFENCESYNC, glFenceSync); \
func(GLDELETESYNC, glDeleteSync); \
func(GLWAITSYNC, glWaitSync); \
func(GLCLIENTWAITSYNC, glClientWaitSync); \
\
func(GLDRAWBUFFERS, glDrawBuffers); \
\
func(GLBLENDFUNCSEPARATE, glBlendFuncSeparate); \
@ -122,14 +129,18 @@
func(GLGETSTRINGI, glGetStringi); \
namespace OpenGL
{
DO_PROCLIST(DECLPROC_EXT);
bool Init();
bool OpenGL_Init();
bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name);
bool LinkShaderProgram(GLuint* ids);
void DeleteShaderProgram(GLuint* ids);
void UseShaderProgram(GLuint* ids);
bool OpenGL_BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name);
bool OpenGL_LinkShaderProgram(GLuint* ids);
void OpenGL_DeleteShaderProgram(GLuint* ids);
void OpenGL_UseShaderProgram(GLuint* ids);
}
#endif // OPENGLSUPPORT_H

View File

@ -24,6 +24,9 @@
namespace Platform
{
void Init(int argc, char** argv);
void DeInit();
void StopEmu();
// fopen() wrappers

6809
src/ROMList.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -22,12 +22,14 @@
#include "Config.h"
#include "NDS.h"
#include "SPI.h"
#include "DSi_SPI_TSC.h"
#include "Platform.h"
namespace SPI_Firmware
{
char FirmwarePath[1024];
u8* Firmware;
u32 FirmwareLength;
u32 FirmwareMask;
@ -76,6 +78,7 @@ bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset)
bool Init()
{
memset(FirmwarePath, 0, sizeof(FirmwarePath));
Firmware = NULL;
return true;
}
@ -90,10 +93,15 @@ void Reset()
if (Firmware) delete[] Firmware;
Firmware = NULL;
FILE* f = Platform::OpenLocalFile("firmware.bin", "rb");
if (NDS::ConsoleType == 1)
strncpy(FirmwarePath, Config::DSiFirmwarePath, 1023);
else
strncpy(FirmwarePath, Config::FirmwarePath, 1023);
FILE* f = Platform::OpenLocalFile(FirmwarePath, "rb");
if (!f)
{
printf("firmware.bin not found\n");
printf("Firmware not found\n");
// TODO: generate default firmware
return;
@ -129,7 +137,11 @@ void Reset()
fclose(f);
// take a backup
const char* firmbkp = "firmware.bin.bak";
char firmbkp[1028];
int fplen = strlen(FirmwarePath);
strncpy(&firmbkp[0], FirmwarePath, fplen);
strncpy(&firmbkp[fplen], ".bak", 1028-fplen);
firmbkp[fplen+4] = '\0';
f = Platform::OpenLocalFile(firmbkp, "rb");
if (f) fclose(f);
else
@ -151,6 +163,7 @@ void Reset()
UserSettings = userdata;
// fix touchscreen coords
#if 0
*(u16*)&Firmware[userdata+0x58] = 0;
*(u16*)&Firmware[userdata+0x5A] = 0;
Firmware[userdata+0x5C] = 0;
@ -173,12 +186,12 @@ void Reset()
Firmware[0x39] = rand()&0xFF;
Firmware[0x3A] = rand()&0xFF;
Firmware[0x3B] = rand()&0xFF;
#endif
printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
Firmware[0x36], Firmware[0x37], Firmware[0x38],
Firmware[0x39], Firmware[0x3A], Firmware[0x3B]);
*(u16*)&Firmware[0x2A] = CRC16(&Firmware[0x2C], *(u16*)&Firmware[0x2C], 0x0000);
//*(u16*)&Firmware[0x2A] = CRC16(&Firmware[0x2C], *(u16*)&Firmware[0x2C], 0x0000);
// verify shit
printf("FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware[0x2C], 0x2A)?"GOOD":"BAD");
@ -225,6 +238,7 @@ void SetupDirectBoot()
u8 GetConsoleType() { return Firmware[0x1D]; }
u8 GetWifiVersion() { return Firmware[0x2F]; }
u8 GetRFVersion() { return Firmware[0x40]; }
u8* GetWifiMAC() { return &Firmware[0x36]; }
u8 Read()
{
@ -325,7 +339,7 @@ void Write(u8 val, u32 hold)
if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A))
{
FILE* f = Platform::OpenLocalFile("firmware.bin", "r+b");
FILE* f = Platform::OpenLocalFile(FirmwarePath, "r+b");
if (f)
{
u32 cutoff = 0x7FA00 & FirmwareMask;
@ -581,7 +595,7 @@ namespace SPI
u16 Cnt;
u32 CurDevice;
u32 CurDevice; // remove me
bool Init()
@ -589,6 +603,7 @@ bool Init()
if (!SPI_Firmware::Init()) return false;
if (!SPI_Powerman::Init()) return false;
if (!SPI_TSC::Init()) return false;
if (!DSi_SPI_TSC::Init()) return false;
return true;
}
@ -598,6 +613,7 @@ void DeInit()
SPI_Firmware::DeInit();
SPI_Powerman::DeInit();
SPI_TSC::DeInit();
DSi_SPI_TSC::DeInit();
}
void Reset()
@ -607,6 +623,7 @@ void Reset()
SPI_Firmware::Reset();
SPI_Powerman::Reset();
SPI_TSC::Reset();
if (NDS::ConsoleType == 1) DSi_SPI_TSC::Reset();
}
void DoSavestate(Savestate* file)
@ -619,6 +636,7 @@ void DoSavestate(Savestate* file)
SPI_Firmware::DoSavestate(file);
SPI_Powerman::DoSavestate(file);
SPI_TSC::DoSavestate(file);
if (NDS::ConsoleType == 1) DSi_SPI_TSC::DoSavestate(file);
}
@ -632,7 +650,12 @@ void WriteCnt(u16 val)
{
case 0x0000: SPI_Powerman::Hold = 0; break;
case 0x0100: SPI_Firmware::Hold = 0; break;
case 0x0200: SPI_TSC::DataPos = 0; break;
case 0x0200:
if (NDS::ConsoleType == 1)
DSi_SPI_TSC::DataPos = 0;
else
SPI_TSC::DataPos = 0;
break;
}
}
@ -658,7 +681,11 @@ u8 ReadData()
{
case 0x0000: return SPI_Powerman::Read();
case 0x0100: return SPI_Firmware::Read();
case 0x0200: return SPI_TSC::Read();
case 0x0200:
if (NDS::ConsoleType == 1)
return DSi_SPI_TSC::Read();
else
return SPI_TSC::Read();
default: return 0;
}
}
@ -674,7 +701,12 @@ void WriteData(u8 val)
{
case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break;
case 0x0100: SPI_Firmware::Write(val, Cnt&(1<<11)); break;
case 0x0200: SPI_TSC::Write(val, Cnt&(1<<11)); break;
case 0x0200:
if (NDS::ConsoleType == 1)
DSi_SPI_TSC::Write(val, Cnt&(1<<11));
else
SPI_TSC::Write(val, Cnt&(1<<11));
break;
default: printf("SPI to unknown device %04X %02X\n", Cnt, val); break;
}

View File

@ -29,6 +29,7 @@ void SetupDirectBoot();
u8 GetConsoleType();
u8 GetWifiVersion();
u8 GetRFVersion();
u8* GetWifiMAC();
}
@ -38,6 +39,9 @@ namespace SPI_TSC
void SetTouchCoords(u16 x, u16 y);
void MicInputFrame(s16* data, int samples);
u8 Read();
void Write(u8 val, u32 hold);
}
namespace SPI
@ -50,7 +54,6 @@ void DeInit();
void Reset();
void DoSavestate(Savestate* file);
u16 ReadCnt();
void WriteCnt(u16 val);
u8 ReadData();

View File

@ -19,6 +19,7 @@
#include <stdio.h>
#include <string.h>
#include "NDS.h"
#include "DSi.h"
#include "SPU.h"
@ -116,6 +117,9 @@ void Reset()
void Stop()
{
memset(OutputBuffer, 0, 2*OutputBufferSize*2);
OutputReadOffset = 0;
OutputWriteOffset = 0;
}
void DoSavestate(Savestate* file)
@ -151,6 +155,11 @@ Channel::~Channel()
void Channel::Reset()
{
if (NDS::ConsoleType == 1)
BusRead32 = DSi::ARM7Read32;
else
BusRead32 = NDS::ARM7Read32;
SetCnt(0);
SrcAddr = 0;
TimerReload = 0;
@ -213,7 +222,7 @@ void Channel::FIFO_BufferData()
for (u32 i = 0; i < burstlen; i += 4)
{
FIFO[FIFOWritePos] = NDS::ARM7Read32(SrcAddr + FIFOReadOffset);
FIFO[FIFOWritePos] = BusRead32(SrcAddr + FIFOReadOffset);
FIFOReadOffset += 4;
FIFOWritePos++;
FIFOWritePos &= 0x7;
@ -461,6 +470,11 @@ CaptureUnit::~CaptureUnit()
void CaptureUnit::Reset()
{
if (NDS::ConsoleType == 1)
BusWrite32 = DSi::ARM7Write32;
else
BusWrite32 = NDS::ARM7Write32;
SetCnt(0);
DstAddr = 0;
TimerReload = 0;
@ -496,7 +510,7 @@ void CaptureUnit::FIFO_FlushData()
{
for (u32 i = 0; i < 4; i++)
{
NDS::ARM7Write32(DstAddr + FIFOWriteOffset, FIFO[FIFOReadPos]);
BusWrite32(DstAddr + FIFOWriteOffset, FIFO[FIFOReadPos]);
FIFOReadPos++;
FIFOReadPos &= 0x3;

View File

@ -142,6 +142,9 @@ public:
}
void PanOutput(s32* inbuf, u32 samples, s32* leftbuf, s32* rightbuf);
private:
u32 (*BusRead32)(u32 addr);
};
class CaptureUnit
@ -196,6 +199,9 @@ public:
}
void Run(s32 sample);
private:
void (*BusWrite32)(u32 addr, u32 val);
};
}

View File

@ -32,6 +32,33 @@ enum
ROMSlot_MAX
};
enum
{
Load_OK = 0,
Load_BIOS9Missing,
Load_BIOS9Bad,
Load_BIOS7Missing,
Load_BIOS7Bad,
Load_FirmwareMissing,
Load_FirmwareBad,
Load_FirmwareNotBootable,
Load_DSiBIOS9Missing,
Load_DSiBIOS9Bad,
Load_DSiBIOS7Missing,
Load_DSiBIOS7Bad,
Load_DSiNANDMissing,
Load_DSiNANDBad,
// TODO: more precise errors for ROM loading
Load_ROMLoadError,
};
extern char ROMPath [ROMSlot_MAX][1024];
extern char SRAMPath[ROMSlot_MAX][1024];
extern bool SavestateLoaded;
@ -41,11 +68,18 @@ extern bool SavestateLoaded;
void Init_ROM();
// load the BIOS/firmware and boot from it
bool LoadBIOS();
int LoadBIOS();
// load a ROM file to the specified cart slot
// note: loading a ROM to the NDS slot resets emulation
bool LoadROM(const char* file, int slot);
int LoadROM(const char* file, int slot);
// unload the ROM loaded in the specified cart slot
// simulating ejection of the cartridge
void UnloadROM(int slot);
// reset execution of the current ROM
int Reset();
// get the filename associated with the given savestate slot (1-8)
void GetSavestateName(int slot, char* filename, int len);
@ -64,6 +98,33 @@ bool SaveState(const char* filename);
void UndoStateLoad();
// setup the display layout based on the provided display size and parameters
// * screenWidth/screenHeight: size of the host display
// * screenLayout: how the DS screens are laid out
// 0 = natural (top screen above bottom screen always)
// 1 = vertical
// 2 = horizontal
// * rotation: angle at which the DS screens are presented: 0/1/2/3 = 0/90/180/270
// * sizing: how the display size is shared between the two screens
// 0 = even (both screens get same size)
// 1 = emphasize top screen (make top screen as big as possible, fit bottom screen in remaining space)
// 2 = emphasize bottom screen
// * screenGap: size of the gap between the two screens
// * integerScale: force screens to be scaled up at integer scaling factors
void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int rotation, int sizing, int screenGap, bool integerScale);
// get a 2x3 transform matrix for each screen
// note: the transform assumes an origin point at the top left of the display,
// X going left and Y going down
// for each screen the source coordinates should be (0,0) and (256,192)
// 'top' and 'bot' should point each to an array of 6 floats
void GetScreenTransforms(float* top, float* bot);
// de-transform the provided host display coordinates to get coordinates
// on the bottom screen
void GetTouchCoords(int& x, int& y);
// initialize the audio utility
void Init_Audio(int outputfreq);
@ -72,9 +133,20 @@ void Init_Audio(int outputfreq);
int AudioOut_GetNumSamples(int outlen);
// resample audio from the core audio output to match the frontend's
// output frequency, and apply user-specified volume
// output frequency, and apply specified volume
// note: this assumes the output buffer is interleaved stereo
void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen);
void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume);
// feed silence to the microphone input
void Mic_FeedSilence();
// feed random noise to the microphone input
void Mic_FeedNoise();
// feed an external buffer to the microphone input
// buffer should be mono
void Mic_FeedExternalBuffer();
void Mic_SetExternalBuffer(s16* buffer, u32 len);
}

View File

@ -17,16 +17,15 @@
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "FrontendUtil.h"
#include "Config.h"
#include "qt_sdl/PlatformConfig.h" // FIXME!!!
#include "Platform.h"
#include "NDS.h"
#include "GBACart.h"
#include "mic_blow.h"
namespace Frontend
@ -35,13 +34,22 @@ namespace Frontend
int AudioOut_Freq;
float AudioOut_SampleFrac;
s16* MicBuffer;
u32 MicBufferLength;
u32 MicBufferReadPos;
void Init_Audio(int outputfreq)
{
AudioOut_Freq = outputfreq;
AudioOut_SampleFrac = 0;
MicBuffer = nullptr;
MicBufferLength = 0;
MicBufferReadPos = 0;
}
int AudioOut_GetNumSamples(int outlen)
{
float f_len_in = (outlen * 32823.6328125) / (float)AudioOut_Freq;
@ -52,14 +60,12 @@ int AudioOut_GetNumSamples(int outlen)
return len_in;
}
void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen)
void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume)
{
float res_incr = inlen / (float)outlen;
float res_timer = 0;
int res_pos = 0;
int volume = Config::AudioVolume;
for (int i = 0; i < outlen; i++)
{
outbuf[i*2 ] = (inbuf[res_pos*2 ] * volume) >> 8;
@ -74,4 +80,56 @@ void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen)
}
}
void Mic_FeedSilence()
{
MicBufferReadPos = 0;
NDS::MicInputFrame(NULL, 0);
}
void Mic_FeedNoise()
{
int sample_len = sizeof(mic_blow) / sizeof(u16);
static int sample_pos = 0;
s16 tmp[735];
for (int i = 0; i < 735; i++)
{
tmp[i] = mic_blow[sample_pos];
sample_pos++;
if (sample_pos >= sample_len) sample_pos = 0;
}
NDS::MicInputFrame(tmp, 735);
}
void Mic_FeedExternalBuffer()
{
if (!MicBuffer) return Mic_FeedSilence();
if ((MicBufferReadPos + 735) > MicBufferLength)
{
s16 tmp[735];
u32 len1 = MicBufferLength - MicBufferReadPos;
memcpy(&tmp[0], &MicBuffer[MicBufferReadPos], len1*sizeof(s16));
memcpy(&tmp[len1], &MicBuffer[0], (735 - len1)*sizeof(s16));
NDS::MicInputFrame(tmp, 735);
MicBufferReadPos = 735 - len1;
}
else
{
NDS::MicInputFrame(&MicBuffer[MicBufferReadPos], 735);
MicBufferReadPos += 735;
}
}
void Mic_SetExternalBuffer(s16* buffer, u32 len)
{
MicBuffer = buffer;
MicBufferLength = len;
MicBufferReadPos = 0;
}
}

View File

@ -50,6 +50,11 @@ void Init_ROM()
memset(PrevSRAMPath[ROMSlot_GBA], 0, 1024);
}
// TODO: currently, when failing to load a ROM for whatever reason, we attempt
// to revert to the previous state and resume execution; this may not be a very
// good thing, depending on what state the core was left in.
// should we do a better state revert (via the savestate system)? completely stop?
void SetupSRAMPath(int slot)
{
strncpy(SRAMPath[slot], ROMPath[slot], 1023);
@ -57,8 +62,166 @@ void SetupSRAMPath(int slot)
strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3);
}
bool LoadBIOS()
int VerifyDSBIOS()
{
FILE* f;
long len;
f = Platform::OpenLocalFile(Config::BIOS9Path, "rb");
if (!f) return Load_BIOS9Missing;
fseek(f, 0, SEEK_END);
len = ftell(f);
if (len != 0x1000)
{
fclose(f);
return Load_BIOS9Bad;
}
fclose(f);
f = Platform::OpenLocalFile(Config::BIOS7Path, "rb");
if (!f) return Load_BIOS7Missing;
fseek(f, 0, SEEK_END);
len = ftell(f);
if (len != 0x4000)
{
fclose(f);
return Load_BIOS7Bad;
}
fclose(f);
return Load_OK;
}
int VerifyDSiBIOS()
{
FILE* f;
long len;
// TODO: check the first 32 bytes
f = Platform::OpenLocalFile(Config::DSiBIOS9Path, "rb");
if (!f) return Load_DSiBIOS9Missing;
fseek(f, 0, SEEK_END);
len = ftell(f);
if (len != 0x10000)
{
fclose(f);
return Load_DSiBIOS9Bad;
}
fclose(f);
f = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
if (!f) return Load_DSiBIOS7Missing;
fseek(f, 0, SEEK_END);
len = ftell(f);
if (len != 0x10000)
{
fclose(f);
return Load_DSiBIOS7Bad;
}
fclose(f);
return Load_OK;
}
int VerifyDSFirmware()
{
FILE* f;
long len;
f = Platform::OpenLocalFile(Config::FirmwarePath, "rb");
if (!f) return Load_FirmwareMissing;
fseek(f, 0, SEEK_END);
len = ftell(f);
if (len == 0x20000)
{
// 128KB firmware, not bootable
fclose(f);
return Load_FirmwareNotBootable;
}
else if (len != 0x40000 && len != 0x80000)
{
fclose(f);
return Load_FirmwareBad;
}
fclose(f);
return Load_OK;
}
int VerifyDSiFirmware()
{
FILE* f;
long len;
f = Platform::OpenLocalFile(Config::DSiFirmwarePath, "rb");
if (!f) return Load_FirmwareMissing;
fseek(f, 0, SEEK_END);
len = ftell(f);
if (len != 0x20000)
{
// not 128KB
// TODO: check whether those work
fclose(f);
return Load_FirmwareBad;
}
fclose(f);
return Load_OK;
}
int VerifyDSiNAND()
{
FILE* f;
long len;
f = Platform::OpenLocalFile(Config::DSiNANDPath, "rb");
if (!f) return Load_DSiNANDMissing;
// TODO: some basic checks
// check that it has the nocash footer, and all
fclose(f);
return Load_OK;
}
int LoadBIOS()
{
int res;
res = VerifyDSBIOS();
if (res != Load_OK) return res;
if (Config::ConsoleType == 1)
{
res = VerifyDSiBIOS();
if (res != Load_OK) return res;
res = VerifyDSiFirmware();
if (res != Load_OK) return res;
res = VerifyDSiNAND();
if (res != Load_OK) return res;
}
else
{
res = VerifyDSFirmware();
if (res != Load_OK) return res;
}
// TODO:
// original code in the libui frontend called NDS::LoadGBAROM() if needed
// should this be carried over here?
@ -67,16 +230,54 @@ bool LoadBIOS()
ROMPath[ROMSlot_NDS][0] = '\0';
SRAMPath[ROMSlot_NDS][0] = '\0';
NDS::SetConsoleType(Config::ConsoleType);
NDS::LoadBIOS();
SavestateLoaded = false;
// TODO: error reporting?
return true;
return Load_OK;
}
bool LoadROM(const char* file, int slot)
int LoadROM(const char* file, int slot)
{
int res;
bool directboot = Config::DirectBoot != 0;
if (Config::ConsoleType == 1 && slot == 1)
{
// cannot load a GBA ROM into a DSi
return Load_ROMLoadError;
}
res = VerifyDSBIOS();
if (res != Load_OK) return res;
if (Config::ConsoleType == 1)
{
res = VerifyDSiBIOS();
if (res != Load_OK) return res;
res = VerifyDSiFirmware();
if (res != Load_OK) return res;
res = VerifyDSiNAND();
if (res != Load_OK) return res;
GBACart::Eject();
ROMPath[ROMSlot_GBA][0] = '\0';
}
else
{
res = VerifyDSFirmware();
if (res != Load_OK)
{
if (res == Load_FirmwareNotBootable)
directboot = true;
else
return res;
}
}
char oldpath[1024];
char oldsram[1024];
strncpy(oldpath, ROMPath[slot], 1024);
@ -88,31 +289,107 @@ bool LoadROM(const char* file, int slot)
SetupSRAMPath(0);
SetupSRAMPath(1);
if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], Config::DirectBoot))
NDS::SetConsoleType(Config::ConsoleType);
if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], directboot))
{
SavestateLoaded = false;
// Reload the inserted GBA cartridge (if any)
// TODO: report failure there??
if (ROMPath[ROMSlot_GBA][0] != '\0') NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]);
strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
return true;
return Load_OK;
}
else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot]))
{
SavestateLoaded = false;
SavestateLoaded = false; // checkme??
strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
return true;
return Load_OK;
}
else
{
strncpy(ROMPath[slot], oldpath, 1024);
strncpy(SRAMPath[slot], oldsram, 1024);
return false;
return Load_ROMLoadError;
}
}
void UnloadROM(int slot)
{
if (slot == ROMSlot_NDS)
{
// TODO!
}
else if (slot == ROMSlot_GBA)
{
GBACart::Eject();
}
ROMPath[slot][0] = '\0';
}
int Reset()
{
int res;
bool directboot = Config::DirectBoot != 0;
res = VerifyDSBIOS();
if (res != Load_OK) return res;
if (Config::ConsoleType == 1)
{
res = VerifyDSiBIOS();
if (res != Load_OK) return res;
res = VerifyDSiFirmware();
if (res != Load_OK) return res;
res = VerifyDSiNAND();
if (res != Load_OK) return res;
GBACart::Eject();
ROMPath[ROMSlot_GBA][0] = '\0';
}
else
{
res = VerifyDSFirmware();
if (res != Load_OK)
{
if (res == Load_FirmwareNotBootable)
directboot = true;
else
return res;
}
}
SavestateLoaded = false;
NDS::SetConsoleType(Config::ConsoleType);
if (ROMPath[ROMSlot_NDS][0] == '\0')
{
NDS::LoadBIOS();
}
else
{
SetupSRAMPath(0);
if (!NDS::LoadROM(ROMPath[ROMSlot_NDS], SRAMPath[ROMSlot_NDS], directboot))
return Load_ROMLoadError;
}
if (ROMPath[ROMSlot_GBA][0] != '\0')
{
SetupSRAMPath(1);
if (!NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]))
return Load_ROMLoadError;
}
return Load_OK;
}
// SAVESTATE TODO
// * configurable paths. not everyone wants their ROM directory to be polluted, I guess.
@ -241,10 +518,6 @@ bool SaveState(const char* filename)
}
}
/*char msg[64];
if (slot > 0) sprintf(msg, "State saved to slot %d", slot);
else sprintf(msg, "State saved to file");
OSD::AddMessage(0, msg);*/
return true;
}
@ -264,8 +537,6 @@ void UndoStateLoad()
strncpy(SRAMPath[ROMSlot_NDS], PrevSRAMPath[ROMSlot_NDS], 1024);
NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false);
}
//OSD::AddMessage(0, "State load undone");
}
}

334
src/frontend/Util_Video.cpp Normal file
View File

@ -0,0 +1,334 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmath>
#include <algorithm>
#include "FrontendUtil.h"
namespace Frontend
{
float TopScreenMtx[6];
float BotScreenMtx[6];
float TouchMtx[6];
void M23_Identity(float* m)
{
m[0] = 1; m[1] = 0;
m[2] = 0; m[3] = 1;
m[4] = 0; m[5] = 0;
}
void M23_Scale(float* m, float s)
{
m[0] *= s; m[1] *= s;
m[2] *= s; m[3] *= s;
m[4] *= s; m[5] *= s;
}
void M23_RotateFast(float* m, int angle)
{
if (angle == 0) return;
float temp[4]; memcpy(temp, m, sizeof(float)*4);
switch (angle)
{
case 1: // 90
m[0] = temp[2];
m[1] = temp[3];
m[2] = -temp[0];
m[3] = -temp[1];
break;
case 2: // 180
m[0] = -temp[0];
m[1] = -temp[1];
m[2] = -temp[2];
m[3] = -temp[3];
break;
case 3: // 270
m[0] = -temp[2];
m[1] = -temp[3];
m[2] = temp[0];
m[3] = temp[1];
break;
}
}
void M23_Translate(float* m, float tx, float ty)
{
m[4] += tx;
m[5] += ty;
}
void M23_Multiply(float* m, float* _a, float* _b)
{
float a[6]; memcpy(a, _a, 6*sizeof(float));
float b[6]; memcpy(b, _b, 6*sizeof(float));
m[0] = (a[0] * b[0]) + (a[2] * b[1]);
m[1] = (a[1] * b[0]) + (a[3] * b[1]);
m[2] = (a[0] * b[2]) + (a[2] * b[3]);
m[3] = (a[1] * b[2]) + (a[3] * b[3]);
m[4] = (a[0] * b[4]) + (a[2] * b[5]) + a[4];
m[5] = (a[1] * b[4]) + (a[3] * b[5]) + a[5];
}
void M23_Transform(float* m, float& x, float& y)
{
float vx = x;
float vy = y;
x = (vx * m[0]) + (vy * m[2]) + m[4];
y = (vx * m[1]) + (vy * m[3]) + m[5];
}
void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int rotation, int sizing, int screenGap, bool integerScale)
{
float refpoints[4][2] =
{
{0, 0}, {256, 192},
{0, 0}, {256, 192}
};
int layout = screenLayout == 0
? ((rotation % 2 == 0) ? 0 : 1)
: screenLayout - 1;
float botScale = 1;
float botTrans[4] = {0};
M23_Identity(TopScreenMtx);
M23_Identity(BotScreenMtx);
M23_Translate(TopScreenMtx, -256/2, -192/2);
M23_Translate(BotScreenMtx, -256/2, -192/2);
// rotation
{
float rotmtx[6];
M23_Identity(rotmtx);
M23_RotateFast(rotmtx, rotation);
M23_Multiply(TopScreenMtx, rotmtx, TopScreenMtx);
M23_Multiply(BotScreenMtx, rotmtx, BotScreenMtx);
M23_Transform(TopScreenMtx, refpoints[0][0], refpoints[0][1]);
M23_Transform(TopScreenMtx, refpoints[1][0], refpoints[1][1]);
M23_Transform(BotScreenMtx, refpoints[2][0], refpoints[2][1]);
M23_Transform(BotScreenMtx, refpoints[3][0], refpoints[3][1]);
}
// move screens apart
{
int idx = layout == 0 ? 1 : 0;
float offset =
(((layout == 0 && (rotation % 2 == 0)) || (layout == 1 && (rotation % 2 == 1))
? 192.f : 256.f)
+ screenGap) / 2.f;
if (rotation == 1 || rotation == 2)
offset *= -1.f;
M23_Translate(TopScreenMtx, (idx==0)?-offset:0, (idx==1)?-offset:0);
M23_Translate(BotScreenMtx, (idx==0)?offset:0, (idx==1)?offset:0);
refpoints[0][idx] -= offset;
refpoints[1][idx] -= offset;
refpoints[2][idx] += offset;
refpoints[3][idx] += offset;
botTrans[idx] = offset;
}
// scale
{
if (sizing == 0)
{
float minX = refpoints[0][0], maxX = minX;
float minY = refpoints[0][1], maxY = minY;
for (int i = 1; i < 4; i++)
{
minX = std::min(minX, refpoints[i][0]);
minY = std::min(minY, refpoints[i][1]);
maxX = std::max(maxX, refpoints[i][0]);
maxY = std::max(maxY, refpoints[i][1]);
}
float hSize = maxX - minX;
float vSize = maxY - minY;
// scale evenly
float scale = std::min(screenWidth / hSize, screenHeight / vSize);
if (integerScale)
scale = floor(scale);
M23_Scale(TopScreenMtx, scale);
M23_Scale(BotScreenMtx, scale);
for (int i = 0; i < 4; i++)
{
refpoints[i][0] *= scale;
refpoints[i][1] *= scale;
}
botScale = scale;
}
else
{
int primOffset = (sizing == 1) ? 0 : 2;
int secOffset = (sizing == 1) ? 2 : 0;
float* primMtx = (sizing == 1) ? TopScreenMtx : BotScreenMtx;
float* secMtx = (sizing == 1) ? BotScreenMtx : TopScreenMtx;
float primMinX = refpoints[primOffset][0], primMaxX = primMinX;
float primMinY = refpoints[primOffset][1], primMaxY = primMinY;
float secMinX = refpoints[secOffset][0], secMaxX = secMinX;
float secMinY = refpoints[secOffset][1], secMaxY = secMinY;
primMinX = std::min(primMinX, refpoints[primOffset+1][0]);
primMinY = std::min(primMinY, refpoints[primOffset+1][1]);
primMaxX = std::max(primMaxX, refpoints[primOffset+1][0]);
primMaxY = std::max(primMaxY, refpoints[primOffset+1][1]);
secMinX = std::min(secMinX, refpoints[secOffset+1][0]);
secMinY = std::min(secMinY, refpoints[secOffset+1][1]);
secMaxX = std::max(secMaxX, refpoints[secOffset+1][0]);
secMaxY = std::max(secMaxY, refpoints[secOffset+1][1]);
float primHSize = layout == 1 ? std::max(primMaxX, -primMinX) : primMaxX - primMinX;
float primVSize = layout == 0 ? std::max(primMaxY, -primMinY) : primMaxY - primMinY;
float secHSize = layout == 1 ? std::max(secMaxX, -secMinX) : secMaxX - secMinX;
float secVSize = layout == 0 ? std::max(secMaxY, -secMinY) : secMaxY - secMinY;
float primScale = std::min(screenWidth / primHSize, screenHeight / primVSize);
float secScale = 1.f;
if (layout == 0)
{
if (screenHeight - primVSize * primScale < secVSize)
primScale = std::min(screenWidth / primHSize, (screenHeight - secVSize) / primVSize);
else
secScale = std::min((screenHeight - primVSize * primScale) / secVSize, screenWidth / secHSize);
}
else
{
if (screenWidth - primHSize * primScale < secHSize)
primScale = std::min((screenWidth - secHSize) / primHSize, screenHeight / primVSize);
else
secScale = std::min((screenWidth - primHSize * primScale) / secHSize, screenHeight / secVSize);
}
if (integerScale)
{
primScale = floor(primScale);
secScale = floor(secScale);
}
M23_Scale(primMtx, primScale);
M23_Scale(secMtx, secScale);
refpoints[primOffset+0][0] *= primScale;
refpoints[primOffset+0][1] *= primScale;
refpoints[primOffset+1][0] *= primScale;
refpoints[primOffset+1][1] *= primScale;
refpoints[secOffset+0][0] *= secScale;
refpoints[secOffset+0][1] *= secScale;
refpoints[secOffset+1][0] *= secScale;
refpoints[secOffset+1][1] *= secScale;
botScale = (sizing == 1) ? secScale : primScale;
}
}
// position
{
float minX = refpoints[0][0], maxX = minX;
float minY = refpoints[0][1], maxY = minY;
for (int i = 1; i < 4; i++)
{
minX = std::min(minX, refpoints[i][0]);
minY = std::min(minY, refpoints[i][1]);
maxX = std::max(maxX, refpoints[i][0]);
maxY = std::max(maxY, refpoints[i][1]);
}
float width = maxX - minX;
float height = maxY - minY;
float tx = (screenWidth/2) - (width/2) - minX;
float ty = (screenHeight/2) - (height/2) - minY;
M23_Translate(TopScreenMtx, tx, ty);
M23_Translate(BotScreenMtx, tx, ty);
botTrans[2] = tx; botTrans[3] = ty;
}
// prepare a 'reverse' matrix for the touchscreen
// this matrix undoes the transforms applied to the bottom screen
// and can be used to calculate touchscreen coords from host screen coords
{
M23_Identity(TouchMtx);
M23_Translate(TouchMtx, -botTrans[2], -botTrans[3]);
M23_Scale(TouchMtx, 1.f / botScale);
M23_Translate(TouchMtx, -botTrans[0], -botTrans[1]);
float rotmtx[6];
M23_Identity(rotmtx);
M23_RotateFast(rotmtx, (4-rotation) & 3);
M23_Multiply(TouchMtx, rotmtx, TouchMtx);
M23_Translate(TouchMtx, 256/2, 192/2);
}
}
void GetScreenTransforms(float* top, float* bot)
{
memcpy(top, TopScreenMtx, 6*sizeof(float));
memcpy(bot, BotScreenMtx, 6*sizeof(float));
}
void GetTouchCoords(int& x, int& y)
{
float vx = x;
float vy = y;
M23_Transform(TouchMtx, vx, vy);
x = (int)vx;
y = (int)vy;
}
}

5539
src/frontend/mic_blow.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,103 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <QFileDialog>
#include "types.h"
#include "Platform.h"
#include "Config.h"
#include "PlatformConfig.h"
#include "AudioSettingsDialog.h"
#include "ui_AudioSettingsDialog.h"
AudioSettingsDialog* AudioSettingsDialog::currentDlg = nullptr;
extern char* EmuDirectory;
AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AudioSettingsDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
oldVolume = Config::AudioVolume;
ui->slVolume->setValue(Config::AudioVolume);
grpMicMode = new QButtonGroup(this);
grpMicMode->addButton(ui->rbMicNone, 0);
grpMicMode->addButton(ui->rbMicExternal, 1);
grpMicMode->addButton(ui->rbMicNoise, 2);
grpMicMode->addButton(ui->rbMicWav, 3);
connect(grpMicMode, SIGNAL(buttonClicked(int)), this, SLOT(onChangeMicMode(int)));
grpMicMode->button(Config::MicInputType)->setChecked(true);
ui->txtMicWavPath->setText(Config::MicWavPath);
bool iswav = (Config::MicInputType == 3);
ui->txtMicWavPath->setEnabled(iswav);
ui->btnMicWavBrowse->setEnabled(iswav);
}
AudioSettingsDialog::~AudioSettingsDialog()
{
delete ui;
}
void AudioSettingsDialog::on_AudioSettingsDialog_accepted()
{
Config::MicInputType = grpMicMode->checkedId();
strncpy(Config::MicWavPath, ui->txtMicWavPath->text().toStdString().c_str(), 1023); Config::MicWavPath[1023] = '\0';
Config::Save();
closeDlg();
}
void AudioSettingsDialog::on_AudioSettingsDialog_rejected()
{
Config::AudioVolume = oldVolume;
closeDlg();
}
void AudioSettingsDialog::on_slVolume_valueChanged(int val)
{
Config::AudioVolume = val;
}
void AudioSettingsDialog::onChangeMicMode(int mode)
{
bool iswav = (mode == 3);
ui->txtMicWavPath->setEnabled(iswav);
ui->btnMicWavBrowse->setEnabled(iswav);
}
void AudioSettingsDialog::on_btnMicWavBrowse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select WAV file...",
EmuDirectory,
"WAV files (*.wav);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtMicWavPath->setText(file);
}

View File

@ -0,0 +1,69 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef AUDIOSETTINGSDIALOG_H
#define AUDIOSETTINGSDIALOG_H
#include <QDialog>
#include <QButtonGroup>
namespace Ui { class AudioSettingsDialog; }
class AudioSettingsDialog;
class AudioSettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit AudioSettingsDialog(QWidget* parent);
~AudioSettingsDialog();
static AudioSettingsDialog* currentDlg;
static AudioSettingsDialog* openDlg(QWidget* parent)
{
if (currentDlg)
{
currentDlg->activateWindow();
return currentDlg;
}
currentDlg = new AudioSettingsDialog(parent);
currentDlg->show();
return currentDlg;
}
static void closeDlg()
{
currentDlg = nullptr;
}
private slots:
void on_AudioSettingsDialog_accepted();
void on_AudioSettingsDialog_rejected();
void on_slVolume_valueChanged(int val);
void onChangeMicMode(int mode);
void on_btnMicWavBrowse_clicked();
private:
Ui::AudioSettingsDialog* ui;
int oldVolume;
QButtonGroup* grpMicMode;
};
#endif // AUDIOSETTINGSDIALOG_H

View File

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AudioSettingsDialog</class>
<widget class="QDialog" name="AudioSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>482</width>
<height>230</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Audio settings - melonDS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Audio output</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Volume:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="slVolume">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Controls the volume of the audio output.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>256</number>
</property>
<property name="pageStep">
<number>16</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Microphone input</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="1">
<widget class="QLineEdit" name="txtMicWavPath">
<property name="minimumSize">
<size>
<width>290</width>
<height>0</height>
</size>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Forward a WAV file to the emulated microphone.&lt;/p&gt;&lt;p&gt;This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QRadioButton" name="rbMicWav">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Forward a WAV file to the emulated microphone.&lt;/p&gt;&lt;p&gt;This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>WAV file:</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="btnMicWavBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QRadioButton" name="rbMicExternal">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Input from an external microphone, if available, will be forwarded to the emulated microphone.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>External microphone</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QRadioButton" name="rbMicNoise">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Noise will be forwarded to the emulated microphone, simulating blowing into the microphone.&lt;/p&gt;&lt;p&gt;This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Blow noise</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QRadioButton" name="rbMicNone">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;No microphone input.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>None</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AudioSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AudioSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -2,56 +2,93 @@ project(qt_sdl)
SET(SOURCES_QT_SDL
main.cpp
main_shaders.h
EmuSettingsDialog.cpp
InputConfigDialog.cpp
VideoSettingsDialog.cpp
AudioSettingsDialog.cpp
WifiSettingsDialog.cpp
Input.cpp
LAN_PCap.cpp
LAN_Socket.cpp
OSD.cpp
OSD_shaders.h
font.h
Platform.cpp
PlatformConfig.cpp
../Util_ROM.cpp
../Util_Video.cpp
../Util_Audio.cpp
../FrontendUtil.h
../mic_blow.h
../../../melon.qrc
)
if (WIN32)
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -i <SOURCE> -o <OBJECT>")
endif()
find_package(Qt5 COMPONENTS Core REQUIRED)
find_package(Qt5 COMPONENTS Gui REQUIRED)
find_package(Qt5 COMPONENTS Widgets REQUIRED)
find_package(Threads REQUIRED)
if (BUILD_STATIC AND QT5_STATIC_DIR)
set(QT5_STATIC_BASE ${QT5_STATIC_DIR}/lib/cmake/Qt5)
set(Qt5_DIR ${QT5_STATIC_BASE})
set(Qt5Core_DIR ${QT5_STATIC_BASE}Core)
set(Qt5Gui_DIR ${QT5_STATIC_BASE}Gui)
set(Qt5Widgets_DIR ${QT5_STATIC_BASE}Widgets)
endif()
find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
add_executable(melonDS ${SOURCES_QT_SDL})
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL Release))
add_executable(melonDS WIN32 ${SOURCES_QT_SDL})
else()
add_executable(melonDS ${SOURCES_QT_SDL})
endif()
target_include_directories(melonDS PRIVATE ${SDL2_INCLUDE_DIRS})
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..")
target_link_libraries(melonDS core ${SDL2_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(melonDS core)
if (BUILD_STATIC)
target_link_libraries(melonDS -static ${SDL2_LIBRARIES})
else()
target_link_libraries(melonDS ${SDL2_LIBRARIES})
endif()
if (UNIX)
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(melonDS dl Qt5::Core Qt5::Gui Qt5::Widgets)
endif ()
target_link_libraries(melonDS dl Qt5::Core Qt5::Gui Qt5::Widgets)
elseif (WIN32)
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" ON)
target_sources(melonDS PUBLIC "${CMAKE_SOURCE_DIR}/melon.rc")
target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32 Qt5::Core Qt5::Gui Qt5::Widgets)
endif ()
target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32)
if (BUILD_STATIC)
target_link_libraries(melonDS imm32 winmm version setupapi -static Qt5::Core Qt5::Gui Qt5::Widgets z zstd)
else()
target_link_libraries(melonDS Qt5::Core Qt5::Gui Qt5::Widgets)
endif()
endif()
if (PORTABLE)
add_definitions(-DPORTABLE)
endif()
install(FILES ../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ../../icon/melon_16x16.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/16x16/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_32x32.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/32x32/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_48x48.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_64x64.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_128x128.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../romlist.bin DESTINATION ${CMAKE_INSTALL_PREFIX}/share/melonDS)
install(FILES ../../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ../../../icon/melon_16x16.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/16x16/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_32x32.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/32x32/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_48x48.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_64x64.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_128x128.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps RENAME net.kuribo64.melonDS.png)
install(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)

View File

@ -0,0 +1,213 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <QFileDialog>
#include <QMessageBox>
#include "types.h"
#include "Platform.h"
#include "Config.h"
#include "PlatformConfig.h"
#include "EmuSettingsDialog.h"
#include "ui_EmuSettingsDialog.h"
EmuSettingsDialog* EmuSettingsDialog::currentDlg = nullptr;
extern char* EmuDirectory;
EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::EmuSettingsDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
ui->txtBIOS9Path->setText(Config::BIOS9Path);
ui->txtBIOS7Path->setText(Config::BIOS7Path);
ui->txtFirmwarePath->setText(Config::FirmwarePath);
ui->txtDSiBIOS9Path->setText(Config::DSiBIOS9Path);
ui->txtDSiBIOS7Path->setText(Config::DSiBIOS7Path);
ui->txtDSiFirmwarePath->setText(Config::DSiFirmwarePath);
ui->txtDSiNANDPath->setText(Config::DSiNANDPath);
ui->cbxConsoleType->addItem("DS");
ui->cbxConsoleType->addItem("DSi (experimental)");
ui->cbxConsoleType->setCurrentIndex(Config::ConsoleType);
ui->chkDirectBoot->setChecked(Config::DirectBoot != 0);
}
EmuSettingsDialog::~EmuSettingsDialog()
{
delete ui;
}
void EmuSettingsDialog::verifyFirmware()
{
// verify the firmware
//
// there are dumps of an old hacked firmware floating around on the internet
// and those are problematic
// the hack predates WFC, and, due to this, any game that alters the WFC
// access point data will brick that firmware due to it having critical
// data in the same area. it has the same problem on hardware.
//
// but this should help stop users from reporting that issue over and over
// again, when the issue is not from melonDS but from their firmware dump.
//
// I don't know about all the firmware hacks in existence, but the one I
// looked at has 0x180 bytes from the header repeated at 0x3FC80, but
// bytes 0x0C-0x14 are different.
char filename[1024];
strncpy(filename, ui->txtFirmwarePath->text().toStdString().c_str(), 1023); filename[1023] = '\0';
FILE* f = Platform::OpenLocalFile(filename, "rb");
u8 chk1[0x180], chk2[0x180];
fseek(f, 0, SEEK_SET);
fread(chk1, 1, 0x180, f);
fseek(f, -0x380, SEEK_END);
fread(chk2, 1, 0x180, f);
memset(&chk1[0x0C], 0, 8);
memset(&chk2[0x0C], 0, 8);
fclose(f);
if (!memcmp(chk1, chk2, 0x180))
{
QMessageBox::warning((QWidget*)this->parent(),
"Problematic firmware dump",
"You are using an old hacked firmware dump.\n"
"Firmware boot will stop working if you run any game that alters WFC settings.\n\n"
"Note that the issue is not from melonDS, it would also happen on an actual DS.");
}
}
void EmuSettingsDialog::on_EmuSettingsDialog_accepted()
{
verifyFirmware();
strncpy(Config::BIOS9Path, ui->txtBIOS9Path->text().toStdString().c_str(), 1023); Config::BIOS9Path[1023] = '\0';
strncpy(Config::BIOS7Path, ui->txtBIOS7Path->text().toStdString().c_str(), 1023); Config::BIOS7Path[1023] = '\0';
strncpy(Config::FirmwarePath, ui->txtFirmwarePath->text().toStdString().c_str(), 1023); Config::FirmwarePath[1023] = '\0';
strncpy(Config::DSiBIOS9Path, ui->txtDSiBIOS9Path->text().toStdString().c_str(), 1023); Config::DSiBIOS9Path[1023] = '\0';
strncpy(Config::DSiBIOS7Path, ui->txtDSiBIOS7Path->text().toStdString().c_str(), 1023); Config::DSiBIOS7Path[1023] = '\0';
strncpy(Config::DSiFirmwarePath, ui->txtDSiFirmwarePath->text().toStdString().c_str(), 1023); Config::DSiFirmwarePath[1023] = '\0';
strncpy(Config::DSiNANDPath, ui->txtDSiNANDPath->text().toStdString().c_str(), 1023); Config::DSiNANDPath[1023] = '\0';
Config::ConsoleType = ui->cbxConsoleType->currentIndex();
Config::DirectBoot = ui->chkDirectBoot->isChecked() ? 1:0;
Config::Save();
closeDlg();
}
void EmuSettingsDialog::on_EmuSettingsDialog_rejected()
{
closeDlg();
}
void EmuSettingsDialog::on_btnBIOS9Browse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DS-mode ARM9 BIOS...",
EmuDirectory,
"BIOS files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtBIOS9Path->setText(file);
}
void EmuSettingsDialog::on_btnBIOS7Browse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DS-mode ARM7 BIOS...",
EmuDirectory,
"BIOS files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtBIOS7Path->setText(file);
}
void EmuSettingsDialog::on_btnFirmwareBrowse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DS-mode firmware...",
EmuDirectory,
"Firmware files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtFirmwarePath->setText(file);
}
void EmuSettingsDialog::on_btnDSiBIOS9Browse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DSi-mode ARM9 BIOS...",
EmuDirectory,
"BIOS files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtDSiBIOS9Path->setText(file);
}
void EmuSettingsDialog::on_btnDSiBIOS7Browse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DSi-mode ARM7 BIOS...",
EmuDirectory,
"BIOS files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtDSiBIOS7Path->setText(file);
}
void EmuSettingsDialog::on_btnDSiFirmwareBrowse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DSi DS-mode firmware...",
EmuDirectory,
"Firmware files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtDSiFirmwarePath->setText(file);
}
void EmuSettingsDialog::on_btnDSiNANDBrowse_clicked()
{
QString file = QFileDialog::getOpenFileName(this,
"Select DSi NAND...",
EmuDirectory,
"NAND files (*.bin *.rom);;Any file (*.*)");
if (file.isEmpty()) return;
ui->txtDSiNANDPath->setText(file);
}

View File

@ -0,0 +1,72 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef EMUSETTINGSDIALOG_H
#define EMUSETTINGSDIALOG_H
#include <QDialog>
namespace Ui { class EmuSettingsDialog; }
class EmuSettingsDialog;
class EmuSettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit EmuSettingsDialog(QWidget* parent);
~EmuSettingsDialog();
static EmuSettingsDialog* currentDlg;
static EmuSettingsDialog* openDlg(QWidget* parent)
{
if (currentDlg)
{
currentDlg->activateWindow();
return currentDlg;
}
currentDlg = new EmuSettingsDialog(parent);
currentDlg->show();
return currentDlg;
}
static void closeDlg()
{
currentDlg = nullptr;
}
private slots:
void on_EmuSettingsDialog_accepted();
void on_EmuSettingsDialog_rejected();
void on_btnBIOS9Browse_clicked();
void on_btnBIOS7Browse_clicked();
void on_btnFirmwareBrowse_clicked();
void on_btnDSiBIOS9Browse_clicked();
void on_btnDSiBIOS7Browse_clicked();
void on_btnDSiFirmwareBrowse_clicked();
void on_btnDSiNANDBrowse_clicked();
private:
void verifyFirmware();
Ui::EmuSettingsDialog* ui;
};
#endif // EMUSETTINGSDIALOG_H

View File

@ -0,0 +1,313 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EmuSettingsDialog</class>
<widget class="QDialog" name="EmuSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>490</width>
<height>392</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Emu settings - melonDS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>DS mode</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1">
<widget class="QLineEdit" name="txtBIOS9Path">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>290</width>
<height>0</height>
</size>
</property>
<property name="statusTip">
<string/>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DS-mode ARM9 BIOS&lt;/p&gt;&lt;p&gt;Size should be 4 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>DS firmware:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>DS ARM7 BIOS:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>DS ARM9 BIOS:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnBIOS9Browse">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Browse...</string>
</property>
<property name="autoDefault">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="txtBIOS7Path">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DS-mode ARM7 BIOS&lt;/p&gt;&lt;p&gt;Size should be 16 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btnBIOS7Browse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="txtFirmwarePath">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DS-mode firmware&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Possible firmwares:&lt;/p&gt;&lt;p&gt;* 128 KB: DS-mode firmware from a DSi or 3DS. Not bootable.&lt;/p&gt;&lt;p&gt;* 256 KB: regular DS firmware.&lt;/p&gt;&lt;p&gt;* 512 KB: iQue DS firmware.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="btnFirmwareBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>DSi mode</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="2">
<widget class="QPushButton" name="btnDSiBIOS9Browse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>DSi ARM9 BIOS:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="btnDSiFirmwareBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="txtDSiBIOS7Path">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DSi-mode ARM7 BIOS&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Size should be 64 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="txtDSiFirmwarePath">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DSi-mode firmware (used for DS-mode backwards compatibility)&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Size should be 128 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>DSi ARM7 BIOS:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>DSi firmware:</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btnDSiBIOS7Browse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="txtDSiBIOS9Path">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DSi-mode ARM9 BIOS&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Size should be 64 KB&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>DSi NAND:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="txtDSiNANDPath">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;DSi NAND dump&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Should have 'nocash footer' at the end&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="btnDSiNANDBrowse">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>General</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Console type:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cbxConsoleType">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The type of console to emulate&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="chkDirectBoot">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When loading a ROM, completely skip the regular boot process (&amp;quot;Nintendo DS&amp;quot; screen) to boot the ROM directly.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Note: if your firmware dump isn't bootable, the ROM will be booted directly regardless of this setting.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Boot game directly</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>EmuSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>EmuSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,244 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <QKeyEvent>
#include <SDL2/SDL.h>
#include "Input.h"
#include "PlatformConfig.h"
namespace Input
{
int JoystickID;
SDL_Joystick* Joystick = nullptr;
u32 KeyInputMask, JoyInputMask;
u32 KeyHotkeyMask, JoyHotkeyMask;
u32 HotkeyMask, LastHotkeyMask;
u32 HotkeyPress, HotkeyRelease;
u32 InputMask;
void Init()
{
KeyInputMask = 0xFFF;
JoyInputMask = 0xFFF;
InputMask = 0xFFF;
KeyHotkeyMask = 0;
JoyHotkeyMask = 0;
HotkeyMask = 0;
LastHotkeyMask = 0;
}
void OpenJoystick()
{
if (Joystick) SDL_JoystickClose(Joystick);
int num = SDL_NumJoysticks();
if (num < 1)
{
Joystick = nullptr;
return;
}
if (JoystickID >= num)
JoystickID = 0;
Joystick = SDL_JoystickOpen(JoystickID);
}
void CloseJoystick()
{
if (Joystick)
{
SDL_JoystickClose(Joystick);
Joystick = nullptr;
}
}
int GetEventKeyVal(QKeyEvent* event)
{
int key = event->key();
int mod = event->modifiers();
bool ismod = (key == Qt::Key_Control ||
key == Qt::Key_Alt ||
key == Qt::Key_AltGr ||
key == Qt::Key_Shift ||
key == Qt::Key_Meta);
if (!ismod)
key |= mod;
else if (Input::IsRightModKey(event))
key |= (1<<31);
return key;
}
void KeyPress(QKeyEvent* event)
{
int keyHK = GetEventKeyVal(event);
int keyKP = keyHK & ~event->modifiers();
for (int i = 0; i < 12; i++)
if (keyKP == Config::KeyMapping[i])
KeyInputMask &= ~(1<<i);
for (int i = 0; i < HK_MAX; i++)
if (keyHK == Config::HKKeyMapping[i])
KeyHotkeyMask |= (1<<i);
}
void KeyRelease(QKeyEvent* event)
{
int keyHK = GetEventKeyVal(event);
int keyKP = keyHK & ~event->modifiers();
for (int i = 0; i < 12; i++)
if (keyKP == Config::KeyMapping[i])
KeyInputMask |= (1<<i);
for (int i = 0; i < HK_MAX; i++)
if (keyHK == Config::HKKeyMapping[i])
KeyHotkeyMask &= ~(1<<i);
}
bool JoystickButtonDown(int val)
{
if (val == -1) return false;
bool hasbtn = ((val & 0xFFFF) != 0xFFFF);
if (hasbtn)
{
if (val & 0x100)
{
int hatnum = (val >> 4) & 0xF;
int hatdir = val & 0xF;
Uint8 hatval = SDL_JoystickGetHat(Joystick, hatnum);
bool pressed = false;
if (hatdir == 0x1) pressed = (hatval & SDL_HAT_UP);
else if (hatdir == 0x4) pressed = (hatval & SDL_HAT_DOWN);
else if (hatdir == 0x2) pressed = (hatval & SDL_HAT_RIGHT);
else if (hatdir == 0x8) pressed = (hatval & SDL_HAT_LEFT);
if (pressed) return true;
}
else
{
int btnnum = val & 0xFFFF;
Uint8 btnval = SDL_JoystickGetButton(Joystick, btnnum);
if (btnval) return true;
}
}
if (val & 0x10000)
{
int axisnum = (val >> 24) & 0xF;
int axisdir = (val >> 20) & 0xF;
Sint16 axisval = SDL_JoystickGetAxis(Joystick, axisnum);
switch (axisdir)
{
case 0: // positive
if (axisval > 16384) return true;
break;
case 1: // negative
if (axisval < -16384) return true;
break;
case 2: // trigger
if (axisval > 0) return true;
break;
}
}
return false;
}
void Process()
{
SDL_JoystickUpdate();
if (Joystick)
{
if (!SDL_JoystickGetAttached(Joystick))
{
SDL_JoystickClose(Joystick);
Joystick = NULL;
}
}
if (!Joystick && (SDL_NumJoysticks() > 0))
{
JoystickID = Config::JoystickID;
OpenJoystick();
}
JoyInputMask = 0xFFF;
for (int i = 0; i < 12; i++)
if (JoystickButtonDown(Config::JoyMapping[i]))
JoyInputMask &= ~(1<<i);
InputMask = KeyInputMask & JoyInputMask;
JoyHotkeyMask = 0;
for (int i = 0; i < HK_MAX; i++)
if (JoystickButtonDown(Config::HKJoyMapping[i]))
JoyHotkeyMask |= (1<<i);
HotkeyMask = KeyHotkeyMask | JoyHotkeyMask;
HotkeyPress = HotkeyMask & ~LastHotkeyMask;
HotkeyRelease = LastHotkeyMask & ~HotkeyMask;
LastHotkeyMask = HotkeyMask;
}
bool HotkeyDown(int id) { return HotkeyMask & (1<<id); }
bool HotkeyPressed(int id) { return HotkeyPress & (1<<id); }
bool HotkeyReleased(int id) { return HotkeyRelease & (1<<id); }
// TODO: MacOS version of this!
// distinguish between left and right modifier keys (Ctrl, Alt, Shift)
// Qt provides no real cross-platform way to do this, so here we go
// for Windows and Linux we can distinguish via scancodes (but both
// provide different scancodes)
#ifdef __WIN32__
bool IsRightModKey(QKeyEvent* event)
{
quint32 scan = event->nativeScanCode();
return (scan == 0x11D || scan == 0x138 || scan == 0x36);
}
#else
bool IsRightModKey(QKeyEvent* event)
{
quint32 scan = event->nativeScanCode();
return (scan == 0x69 || scan == 0x6C || scan == 0x3E);
}
#endif
}

View File

@ -0,0 +1,51 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef INPUT_H
#define INPUT_H
#include "types.h"
namespace Input
{
extern int JoystickID;
extern SDL_Joystick* Joystick;
extern u32 InputMask;
void Init();
// set joystickID before calling openJoystick()
void OpenJoystick();
void CloseJoystick();
void KeyPress(QKeyEvent* event);
void KeyRelease(QKeyEvent* event);
void Process();
bool HotkeyDown(int id);
bool HotkeyPressed(int id);
bool HotkeyReleased(int id);
bool IsRightModKey(QKeyEvent* event);
}
#endif // INPUT_H

View File

@ -0,0 +1,494 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <QGroupBox>
#include <QLabel>
#include <QKeyEvent>
#include <SDL2/SDL.h>
#include "types.h"
#include "Config.h"
#include "PlatformConfig.h"
#include "Input.h"
#include "InputConfigDialog.h"
#include "ui_InputConfigDialog.h"
InputConfigDialog* InputConfigDialog::currentDlg = nullptr;
const int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 2, 3};
const char* dskeylabels[12] = {"A", "B", "X", "Y", "Left", "Right", "Up", "Down", "L", "R", "Select", "Start"};
const int hk_addons[] =
{
HK_SolarSensorIncrease,
HK_SolarSensorDecrease,
};
const char* hk_addons_labels[] =
{
"[Boktai] Sunlight + ",
"[Boktai] Sunlight - ",
};
const int hk_general[] =
{
HK_Pause,
HK_Reset,
HK_FastForward,
HK_FastForwardToggle,
HK_Lid,
HK_Mic,
};
const char* hk_general_labels[] =
{
"Pause/resume",
"Reset",
"Fast forward",
"Toggle FPS limit",
"Close/open lid",
"Microphone",
};
InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new Ui::InputConfigDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
for (int i = 0; i < 12; i++)
{
keypadKeyMap[i] = Config::KeyMapping[dskeyorder[i]];
keypadJoyMap[i] = Config::JoyMapping[dskeyorder[i]];
}
for (int i = 0; i < 2; i++)
{
addonsKeyMap[i] = Config::HKKeyMapping[hk_addons[i]];
addonsJoyMap[i] = Config::HKJoyMapping[hk_addons[i]];
}
for (int i = 0; i < 6; i++)
{
hkGeneralKeyMap[i] = Config::HKKeyMapping[hk_general[i]];
hkGeneralJoyMap[i] = Config::HKJoyMapping[hk_general[i]];
}
populatePage(ui->tabInput, 12, dskeylabels, keypadKeyMap, keypadJoyMap);
populatePage(ui->tabAddons, 2, hk_addons_labels, addonsKeyMap, addonsJoyMap);
populatePage(ui->tabHotkeysGeneral, 6, hk_general_labels, hkGeneralKeyMap, hkGeneralJoyMap);
int njoy = SDL_NumJoysticks();
if (njoy > 0)
{
for (int i = 0; i < njoy; i++)
{
const char* name = SDL_JoystickNameForIndex(i);
ui->cbxJoystick->addItem(QString(name));
}
ui->cbxJoystick->setCurrentIndex(Input::JoystickID);
}
else
{
ui->cbxJoystick->addItem("(no joysticks available)");
ui->cbxJoystick->setEnabled(false);
}
}
InputConfigDialog::~InputConfigDialog()
{
delete ui;
}
void InputConfigDialog::populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap)
{
// kind of a hack
bool ishotkey = (page != ui->tabInput);
QHBoxLayout* main_layout = new QHBoxLayout();
QGroupBox* group;
QGridLayout* group_layout;
group = new QGroupBox("Keyboard mappings:");
main_layout->addWidget(group);
group_layout = new QGridLayout();
group_layout->setSpacing(1);
for (int i = 0; i < num; i++)
{
QLabel* label = new QLabel(QString(labels[i])+":");
KeyMapButton* btn = new KeyMapButton(&keymap[i], ishotkey);
group_layout->addWidget(label, i, 0);
group_layout->addWidget(btn, i, 1);
}
group_layout->setRowStretch(num, 1);
group->setLayout(group_layout);
group->setMinimumWidth(275);
group = new QGroupBox("Joystick mappings:");
main_layout->addWidget(group);
group_layout = new QGridLayout();
group_layout->setSpacing(1);
for (int i = 0; i < num; i++)
{
QLabel* label = new QLabel(QString(labels[i])+":");
JoyMapButton* btn = new JoyMapButton(&joymap[i], ishotkey);
group_layout->addWidget(label, i, 0);
group_layout->addWidget(btn, i, 1);
}
group_layout->setRowStretch(num, 1);
group->setLayout(group_layout);
group->setMinimumWidth(275);
page->setLayout(main_layout);
}
void InputConfigDialog::on_InputConfigDialog_accepted()
{
for (int i = 0; i < 12; i++)
{
Config::KeyMapping[dskeyorder[i]] = keypadKeyMap[i];
Config::JoyMapping[dskeyorder[i]] = keypadJoyMap[i];
}
for (int i = 0; i < 2; i++)
{
Config::HKKeyMapping[hk_addons[i]] = addonsKeyMap[i];
Config::HKJoyMapping[hk_addons[i]] = addonsJoyMap[i];
}
for (int i = 0; i < 6; i++)
{
Config::HKKeyMapping[hk_general[i]] = hkGeneralKeyMap[i];
Config::HKJoyMapping[hk_general[i]] = hkGeneralJoyMap[i];
}
Config::JoystickID = Input::JoystickID;
Config::Save();
closeDlg();
}
void InputConfigDialog::on_InputConfigDialog_rejected()
{
Input::JoystickID = Config::JoystickID;
Input::OpenJoystick();
closeDlg();
}
void InputConfigDialog::on_cbxJoystick_currentIndexChanged(int id)
{
// prevent a spurious change
if (ui->cbxJoystick->count() < 2) return;
Input::JoystickID = id;
Input::OpenJoystick();
}
KeyMapButton::KeyMapButton(int* mapping, bool hotkey) : QPushButton()
{
this->mapping = mapping;
this->isHotkey = hotkey;
setCheckable(true);
setText(mappingText());
connect(this, &KeyMapButton::clicked, this, &KeyMapButton::onClick);
}
KeyMapButton::~KeyMapButton()
{
}
void KeyMapButton::keyPressEvent(QKeyEvent* event)
{
if (!isChecked()) return QPushButton::keyPressEvent(event);
printf("KEY PRESSED = %08X %08X | %08X %08X %08X\n", event->key(), event->modifiers(), event->nativeVirtualKey(), event->nativeModifiers(), event->nativeScanCode());
int key = event->key();
int mod = event->modifiers();
bool ismod = (key == Qt::Key_Control ||
key == Qt::Key_Alt ||
key == Qt::Key_AltGr ||
key == Qt::Key_Shift ||
key == Qt::Key_Meta);
if (!mod)
{
if (key == Qt::Key_Escape) { click(); return; }
if (key == Qt::Key_Backspace) { *mapping = -1; click(); return; }
}
if (isHotkey)
{
if (ismod)
return;
}
if (!ismod)
key |= mod;
else if (Input::IsRightModKey(event))
key |= (1<<31);
*mapping = key;
click();
}
void KeyMapButton::focusOutEvent(QFocusEvent* event)
{
if (isChecked())
{
// if we lost the focus while mapping, consider it 'done'
click();
}
QPushButton::focusOutEvent(event);
}
void KeyMapButton::onClick()
{
if (isChecked())
{
setText("[press key]");
}
else
{
setText(mappingText());
}
}
QString KeyMapButton::mappingText()
{
int key = *mapping;
if (key == -1) return "None";
QString isright = (key & (1<<31)) ? "Right " : "Left ";
key &= ~(1<<31);
switch (key)
{
case Qt::Key_Control: return isright + "Ctrl";
case Qt::Key_Alt: return "Alt";
case Qt::Key_AltGr: return "AltGr";
case Qt::Key_Shift: return isright + "Shift";
case Qt::Key_Meta: return "Meta";
}
QKeySequence seq(key);
QString ret = seq.toString();
// weak attempt at detecting garbage key names
if (ret.length() == 2 && ret[0].unicode() > 0xFF)
return QString("[%1]").arg(key, 8, 16);
return ret.replace("&", "&&");
}
JoyMapButton::JoyMapButton(int* mapping, bool hotkey) : QPushButton()
{
this->mapping = mapping;
this->isHotkey = hotkey;
setCheckable(true);
setText(mappingText());
connect(this, &JoyMapButton::clicked, this, &JoyMapButton::onClick);
timerID = 0;
}
JoyMapButton::~JoyMapButton()
{
}
void JoyMapButton::keyPressEvent(QKeyEvent* event)
{
if (!isChecked()) return QPushButton::keyPressEvent(event);
int key = event->key();
int mod = event->modifiers();
if (!mod)
{
if (key == Qt::Key_Escape) { click(); return; }
if (key == Qt::Key_Backspace) { *mapping = -1; click(); return; }
}
}
void JoyMapButton::focusOutEvent(QFocusEvent* event)
{
if (isChecked())
{
// if we lost the focus while mapping, consider it 'done'
click();
}
QPushButton::focusOutEvent(event);
}
void JoyMapButton::timerEvent(QTimerEvent* event)
{
SDL_Joystick* joy = Input::Joystick;
if (!joy) { click(); return; }
if (!SDL_JoystickGetAttached(joy)) { click(); return; }
int oldmap;
if (*mapping == -1) oldmap = 0xFFFF;
else oldmap = *mapping;
int nbuttons = SDL_JoystickNumButtons(joy);
for (int i = 0; i < nbuttons; i++)
{
if (SDL_JoystickGetButton(joy, i))
{
*mapping = (oldmap & 0xFFFF0000) | i;
click();
return;
}
}
int nhats = SDL_JoystickNumHats(joy);
if (nhats > 16) nhats = 16;
for (int i = 0; i < nhats; i++)
{
Uint8 blackhat = SDL_JoystickGetHat(joy, i);
if (blackhat)
{
if (blackhat & 0x1) blackhat = 0x1;
else if (blackhat & 0x2) blackhat = 0x2;
else if (blackhat & 0x4) blackhat = 0x4;
else blackhat = 0x8;
*mapping = (oldmap & 0xFFFF0000) | 0x100 | blackhat | (i << 4);
click();
return;
}
}
int naxes = SDL_JoystickNumAxes(joy);
if (naxes > 16) naxes = 16;
for (int i = 0; i < naxes; i++)
{
Sint16 axisval = SDL_JoystickGetAxis(joy, i);
int diff = abs(axisval - axesRest[i]);
if (axesRest[i] < -16384 && axisval >= 0)
{
*mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
click();
return;
}
else if (diff > 16384)
{
int axistype;
if (axisval > 0) axistype = 0;
else axistype = 1;
*mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
click();
return;
}
}
}
void JoyMapButton::onClick()
{
if (isChecked())
{
setText("[press button/axis]");
timerID = startTimer(50);
memset(axesRest, 0, sizeof(axesRest));
if (Input::Joystick && SDL_JoystickGetAttached(Input::Joystick))
{
int naxes = SDL_JoystickNumAxes(Input::Joystick);
if (naxes > 16) naxes = 16;
for (int a = 0; a < naxes; a++)
{
axesRest[a] = SDL_JoystickGetAxis(Input::Joystick, a);
}
}
}
else
{
setText(mappingText());
if (timerID) { killTimer(timerID); timerID = 0; }
}
}
QString JoyMapButton::mappingText()
{
int id = *mapping;
if (id == -1) return "None";
bool hasbtn = ((id & 0xFFFF) != 0xFFFF);
QString str;
if (hasbtn)
{
if (id & 0x100)
{
int hatnum = ((id >> 4) & 0xF) + 1;
switch (id & 0xF)
{
case 0x1: str = "Hat %1 up"; break;
case 0x2: str = "Hat %1 right"; break;
case 0x4: str = "Hat %1 down"; break;
case 0x8: str = "Hat %1 left"; break;
}
str = str.arg(hatnum);
}
else
{
str = QString("Button %1").arg((id & 0xFFFF) + 1);
}
}
else
{
str = "";
}
if (id & 0x10000)
{
int axisnum = ((id >> 24) & 0xF) + 1;
if (hasbtn) str += " / ";
switch ((id >> 20) & 0xF)
{
case 0: str += QString("Axis %1 +").arg(axisnum); break;
case 1: str += QString("Axis %1 -").arg(axisnum); break;
case 2: str += QString("Trigger %1").arg(axisnum); break;
}
}
return str;
}

View File

@ -0,0 +1,123 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef INPUTCONFIGDIALOG_H
#define INPUTCONFIGDIALOG_H
#include <QDialog>
#include <QPushButton>
namespace Ui { class InputConfigDialog; }
class InputConfigDialog;
class InputConfigDialog : public QDialog
{
Q_OBJECT
public:
explicit InputConfigDialog(QWidget* parent);
~InputConfigDialog();
static InputConfigDialog* currentDlg;
static InputConfigDialog* openDlg(QWidget* parent)
{
if (currentDlg)
{
currentDlg->activateWindow();
return currentDlg;
}
currentDlg = new InputConfigDialog(parent);
currentDlg->open();
return currentDlg;
}
static void closeDlg()
{
currentDlg = nullptr;
}
private slots:
void on_InputConfigDialog_accepted();
void on_InputConfigDialog_rejected();
void on_cbxJoystick_currentIndexChanged(int id);
private:
void populatePage(QWidget* page, int num, const char** labels, int* keymap, int* joymap);
Ui::InputConfigDialog* ui;
int keypadKeyMap[12], keypadJoyMap[12];
int addonsKeyMap[2], addonsJoyMap[2];
int hkGeneralKeyMap[6], hkGeneralJoyMap[6];
};
class KeyMapButton : public QPushButton
{
Q_OBJECT
public:
explicit KeyMapButton(int* mapping, bool hotkey);
~KeyMapButton();
protected:
void keyPressEvent(QKeyEvent* event) override;
void focusOutEvent(QFocusEvent* event) override;
bool focusNextPrevChild(bool next) override { return false; }
private slots:
void onClick();
private:
QString mappingText();
int* mapping;
bool isHotkey;
};
class JoyMapButton : public QPushButton
{
Q_OBJECT
public:
explicit JoyMapButton(int* mapping, bool hotkey);
~JoyMapButton();
protected:
void keyPressEvent(QKeyEvent* event) override;
void focusOutEvent(QFocusEvent* event) override;
void timerEvent(QTimerEvent* event) override;
bool focusNextPrevChild(bool next) override { return false; }
private slots:
void onClick();
private:
QString mappingText();
int* mapping;
bool isHotkey;
int timerID;
int axesRest[16];
};
#endif // INPUTCONFIGDIALOG_H

View File

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InputConfigDialog</class>
<widget class="QDialog" name="InputConfigDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>488</width>
<height>365</height>
</rect>
</property>
<property name="windowTitle">
<string>Input and hotkeys - melonDS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabInput">
<attribute name="title">
<string>DS keypad</string>
</attribute>
</widget>
<widget class="QWidget" name="tabAddons">
<attribute name="title">
<string>Add-ons</string>
</attribute>
</widget>
<widget class="QWidget" name="tabHotkeysGeneral">
<attribute name="title">
<string>General hotkeys</string>
</attribute>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Joystick:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbxJoystick">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects which joystick will be used for joystick input, if any is present.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>InputConfigDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>InputConfigDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,387 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
// direct LAN interface. Currently powered by libpcap, may change.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL2/SDL.h>
#include <pcap/pcap.h>
#include "../Wifi.h"
#include "LAN_PCap.h"
#include "PlatformConfig.h"
#ifdef __WIN32__
#include <iphlpapi.h>
#else
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <linux/if_packet.h>
#endif
// welp
#ifndef PCAP_OPENFLAG_PROMISCUOUS
#define PCAP_OPENFLAG_PROMISCUOUS 1
#endif
#define DECL_PCAP_FUNC(ret, name, args, args2) \
typedef ret (*type_##name) args; \
type_##name ptr_##name = NULL; \
ret name args { return ptr_##name args2; }
DECL_PCAP_FUNC(int, pcap_findalldevs, (pcap_if_t** alldevs, char* errbuf), (alldevs,errbuf))
DECL_PCAP_FUNC(void, pcap_freealldevs, (pcap_if_t* alldevs), (alldevs))
DECL_PCAP_FUNC(pcap_t*, pcap_open_live, (const char* src, int snaplen, int flags, int readtimeout, char* errbuf), (src,snaplen,flags,readtimeout,errbuf))
DECL_PCAP_FUNC(void, pcap_close, (pcap_t* dev), (dev))
DECL_PCAP_FUNC(int, pcap_setnonblock, (pcap_t* dev, int nonblock, char* errbuf), (dev,nonblock,errbuf))
DECL_PCAP_FUNC(int, pcap_sendpacket, (pcap_t* dev, const u_char* data, int len), (dev,data,len))
DECL_PCAP_FUNC(int, pcap_dispatch, (pcap_t* dev, int num, pcap_handler callback, u_char* data), (dev,num,callback,data))
DECL_PCAP_FUNC(const u_char*, pcap_next, (pcap_t* dev, struct pcap_pkthdr* hdr), (dev,hdr))
namespace LAN_PCap
{
const char* PCapLibNames[] =
{
#ifdef __WIN32__
// TODO: name for npcap in non-WinPCap mode
"wpcap.dll",
#else
// Linux lib names
"libpcap.so.1",
"libpcap.so",
#endif
NULL
};
AdapterData* Adapters = NULL;
int NumAdapters = 0;
void* PCapLib = NULL;
pcap_t* PCapAdapter = NULL;
AdapterData* PCapAdapterData;
u8 PacketBuffer[2048];
int PacketLen;
volatile int RXNum;
#define LOAD_PCAP_FUNC(sym) \
ptr_##sym = (type_##sym)SDL_LoadFunction(lib, #sym); \
if (!ptr_##sym) return false;
bool TryLoadPCap(void* lib)
{
LOAD_PCAP_FUNC(pcap_findalldevs)
LOAD_PCAP_FUNC(pcap_freealldevs)
LOAD_PCAP_FUNC(pcap_open_live)
LOAD_PCAP_FUNC(pcap_close)
LOAD_PCAP_FUNC(pcap_setnonblock)
LOAD_PCAP_FUNC(pcap_sendpacket)
LOAD_PCAP_FUNC(pcap_dispatch)
LOAD_PCAP_FUNC(pcap_next)
return true;
}
bool Init(bool open_adapter)
{
// TODO: how to deal with cases where an adapter is unplugged or changes config??
if (!PCapLib)
{
PCapLib = NULL;
for (int i = 0; PCapLibNames[i]; i++)
{
void* lib = SDL_LoadObject(PCapLibNames[i]);
if (!lib) continue;
if (!TryLoadPCap(lib))
{
SDL_UnloadObject(lib);
continue;
}
printf("PCap: lib %s, init successful\n", PCapLibNames[i]);
PCapLib = lib;
break;
}
if (PCapLib == NULL)
{
printf("PCap: init failed\n");
return false;
}
}
PCapAdapter = NULL;
PacketLen = 0;
RXNum = 0;
NumAdapters = 0;
char errbuf[PCAP_ERRBUF_SIZE];
int ret;
pcap_if_t* alldevs;
ret = pcap_findalldevs(&alldevs, errbuf);
if (ret < 0 || alldevs == NULL)
{
printf("PCap: no devices available\n");
return false;
}
pcap_if_t* dev = alldevs;
while (dev) { NumAdapters++; dev = dev->next; }
Adapters = new AdapterData[NumAdapters];
memset(Adapters, 0, sizeof(AdapterData)*NumAdapters);
AdapterData* adata = &Adapters[0];
dev = alldevs;
while (dev)
{
adata->Internal = dev;
#ifdef __WIN32__
// hax
int len = strlen(dev->name);
len -= 12; if (len > 127) len = 127;
strncpy(adata->DeviceName, &dev->name[12], len);
adata->DeviceName[len] = '\0';
#else
strncpy(adata->DeviceName, dev->name, 127);
adata->DeviceName[127] = '\0';
strncpy(adata->FriendlyName, adata->DeviceName, 127);
adata->FriendlyName[127] = '\0';
#endif // __WIN32__
dev = dev->next;
adata++;
}
#ifdef __WIN32__
ULONG bufsize = 16384;
IP_ADAPTER_ADDRESSES* buf = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufsize);
ULONG uret = GetAdaptersAddresses(AF_INET, 0, NULL, buf, &bufsize);
if (uret == ERROR_BUFFER_OVERFLOW)
{
HeapFree(GetProcessHeap(), 0, buf);
buf = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufsize);
uret = GetAdaptersAddresses(AF_INET, 0, NULL, buf, &bufsize);
}
if (uret != ERROR_SUCCESS)
{
printf("GetAdaptersAddresses() shat itself: %08X\n", uret);
return false;
}
for (int i = 0; i < NumAdapters; i++)
{
adata = &Adapters[i];
IP_ADAPTER_ADDRESSES* addr = buf;
while (addr)
{
if (strcmp(addr->AdapterName, adata->DeviceName))
{
addr = addr->Next;
continue;
}
WideCharToMultiByte(CP_UTF8, 0, addr->FriendlyName, 127, adata->FriendlyName, 127, NULL, NULL);
adata->FriendlyName[127] = '\0';
WideCharToMultiByte(CP_UTF8, 0, addr->Description, 127, adata->Description, 127, NULL, NULL);
adata->Description[127] = '\0';
if (addr->PhysicalAddressLength != 6)
{
printf("weird MAC addr length %d for %s\n", addr->PhysicalAddressLength, addr->AdapterName);
}
else
memcpy(adata->MAC, addr->PhysicalAddress, 6);
IP_ADAPTER_UNICAST_ADDRESS* ipaddr = addr->FirstUnicastAddress;
while (ipaddr)
{
SOCKADDR* sa = ipaddr->Address.lpSockaddr;
if (sa->sa_family == AF_INET)
{
struct in_addr sa4 = ((sockaddr_in*)sa)->sin_addr;
memcpy(adata->IP_v4, &sa4, 4);
}
ipaddr = ipaddr->Next;
}
break;
}
}
HeapFree(GetProcessHeap(), 0, buf);
#else
struct ifaddrs* addrs;
if (getifaddrs(&addrs) != 0)
{
printf("getifaddrs() shat itself :(\n");
return false;
}
for (int i = 0; i < NumAdapters; i++)
{
adata = &Adapters[i];
struct ifaddrs* curaddr = addrs;
while (curaddr)
{
if (strcmp(curaddr->ifa_name, adata->DeviceName))
{
curaddr = curaddr->ifa_next;
continue;
}
if (!curaddr->ifa_addr)
{
printf("Device (%s) does not have an address :/\n", curaddr->ifa_name);
curaddr = curaddr->ifa_next;
continue;
}
u16 af = curaddr->ifa_addr->sa_family;
if (af == AF_INET)
{
struct sockaddr_in* sa = (sockaddr_in*)curaddr->ifa_addr;
memcpy(adata->IP_v4, &sa->sin_addr, 4);
}
else if (af == AF_PACKET)
{
struct sockaddr_ll* sa = (sockaddr_ll*)curaddr->ifa_addr;
if (sa->sll_halen != 6)
printf("weird MAC length %d for %s\n", sa->sll_halen, curaddr->ifa_name);
else
memcpy(adata->MAC, sa->sll_addr, 6);
}
curaddr = curaddr->ifa_next;
}
}
freeifaddrs(addrs);
#endif // __WIN32__
if (!open_adapter) return true;
if (PCapAdapter) pcap_close(PCapAdapter);
// open pcap device
PCapAdapterData = &Adapters[0];
for (int i = 0; i < NumAdapters; i++)
{
if (!strncmp(Adapters[i].DeviceName, Config::LANDevice, 128))
PCapAdapterData = &Adapters[i];
}
dev = (pcap_if_t*)PCapAdapterData->Internal;
PCapAdapter = pcap_open_live(dev->name, 2048, PCAP_OPENFLAG_PROMISCUOUS, 1, errbuf);
if (!PCapAdapter)
{
printf("PCap: failed to open adapter %s\n", errbuf);
return false;
}
pcap_freealldevs(alldevs);
if (pcap_setnonblock(PCapAdapter, 1, errbuf) < 0)
{
printf("PCap: failed to set nonblocking mode\n");
pcap_close(PCapAdapter); PCapAdapter = NULL;
return false;
}
return true;
}
void DeInit()
{
if (PCapLib)
{
if (PCapAdapter)
{
pcap_close(PCapAdapter);
PCapAdapter = NULL;
}
SDL_UnloadObject(PCapLib);
PCapLib = NULL;
}
}
void RXCallback(u_char* blarg, const struct pcap_pkthdr* header, const u_char* data)
{
while (RXNum > 0);
if (header->len > 2048-64) return;
PacketLen = header->len;
memcpy(PacketBuffer, data, PacketLen);
RXNum = 1;
}
int SendPacket(u8* data, int len)
{
if (PCapAdapter == NULL)
return 0;
if (len > 2048)
{
printf("LAN_SendPacket: error: packet too long (%d)\n", len);
return 0;
}
pcap_sendpacket(PCapAdapter, data, len);
// TODO: check success
return len;
}
int RecvPacket(u8* data)
{
if (PCapAdapter == NULL)
return 0;
int ret = 0;
if (RXNum > 0)
{
memcpy(data, PacketBuffer, PacketLen);
ret = PacketLen;
RXNum = 0;
}
pcap_dispatch(PCapAdapter, 1, RXCallback, NULL);
return ret;
}
}

View File

@ -0,0 +1,53 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef LAN_PCAP_H
#define LAN_PCAP_H
#include "../types.h"
namespace LAN_PCap
{
typedef struct
{
char DeviceName[128];
char FriendlyName[128];
char Description[128];
u8 MAC[6];
u8 IP_v4[4];
void* Internal;
} AdapterData;
extern AdapterData* Adapters;
extern int NumAdapters;
bool Init(bool open_adapter);
void DeInit();
int SendPacket(u8* data, int len);
int RecvPacket(u8* data);
}
#endif // LAN_PCAP_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef LAN_SOCKET_H
#define LAN_SOCKET_H
#include "../types.h"
namespace LAN_Socket
{
//
bool Init();
void DeInit();
int SendPacket(u8* data, int len);
int RecvPacket(u8* data);
}
#endif // LAN_SOCKET_H

474
src/frontend/qt_sdl/OSD.cpp Normal file
View File

@ -0,0 +1,474 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include <deque>
#include <SDL2/SDL.h>
#include "../types.h"
#include "main.h"
#include <QPainter>
#include "OSD.h"
#include "OSD_shaders.h"
#include "font.h"
#include "PlatformConfig.h"
extern MainWindow* mainWindow;
namespace OSD
{
const u32 kOSDMargin = 6;
struct Item
{
Uint32 Timestamp;
char Text[256];
u32 Color;
u32 Width, Height;
u32* Bitmap;
bool NativeBitmapLoaded;
QImage NativeBitmap;
bool GLTextureLoaded;
GLuint GLTexture;
};
std::deque<Item> ItemQueue;
QOpenGLShaderProgram* Shader;
GLint uScreenSize, uOSDPos, uOSDSize;
GLuint OSDVertexArray;
GLuint OSDVertexBuffer;
volatile bool Rendering;
bool Init(QOpenGLFunctions_3_2_Core* f)
{
if (f)
{
Shader = new QOpenGLShaderProgram();
Shader->addShaderFromSourceCode(QOpenGLShader::Vertex, kScreenVS_OSD);
Shader->addShaderFromSourceCode(QOpenGLShader::Fragment, kScreenFS_OSD);
GLuint pid = Shader->programId();
f->glBindAttribLocation(pid, 0, "vPosition");
f->glBindFragDataLocation(pid, 0, "oColor");
Shader->link();
Shader->bind();
Shader->setUniformValue("OSDTex", (GLint)0);
Shader->release();
uScreenSize = Shader->uniformLocation("uScreenSize");
uOSDPos = Shader->uniformLocation("uOSDPos");
uOSDSize = Shader->uniformLocation("uOSDSize");
float vertices[6*2] =
{
0, 0,
1, 1,
1, 0,
0, 0,
0, 1,
1, 1
};
f->glGenBuffers(1, &OSDVertexBuffer);
f->glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer);
f->glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
f->glGenVertexArrays(1, &OSDVertexArray);
f->glBindVertexArray(OSDVertexArray);
f->glEnableVertexAttribArray(0); // position
f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0));
}
return true;
}
void DeInit(QOpenGLFunctions_3_2_Core* f)
{
for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
Item& item = *it;
if (item.GLTextureLoaded && f) f->glDeleteTextures(1, &item.GLTexture);
if (item.Bitmap) delete[] item.Bitmap;
it = ItemQueue.erase(it);
}
if (f) delete Shader;
}
int FindBreakPoint(const char* text, int i)
{
// i = character that went out of bounds
for (int j = i; j >= 0; j--)
{
if (text[j] == ' ')
return j;
}
return i;
}
void LayoutText(const char* text, u32* width, u32* height, int* breaks)
{
u32 w = 0;
u32 h = 14;
u32 totalw = 0;
u32 maxw = mainWindow->panel->width() - (kOSDMargin*2);
int lastbreak = -1;
int numbrk = 0;
u16* ptr;
memset(breaks, 0, sizeof(int)*64);
for (int i = 0; text[i] != '\0'; )
{
int glyphsize;
if (text[i] == ' ')
{
glyphsize = 6;
}
else
{
u32 ch = text[i];
if (ch < 0x10 || ch > 0x7E) ch = 0x7F;
ptr = &font[(ch-0x10) << 4];
glyphsize = ptr[0];
if (!glyphsize) glyphsize = 6;
else glyphsize += 2; // space around the character
}
w += glyphsize;
if (w > maxw)
{
// wrap shit as needed
if (text[i] == ' ')
{
if (numbrk >= 64) break;
breaks[numbrk++] = i;
i++;
}
else
{
int brk = FindBreakPoint(text, i);
if (brk != lastbreak) i = brk;
if (numbrk >= 64) break;
breaks[numbrk++] = i;
lastbreak = brk;
}
w = 0;
h += 14;
}
else
i++;
if (w > totalw) totalw = w;
}
*width = totalw;
*height = h;
}
u32 RainbowColor(u32 inc)
{
// inspired from Acmlmboard
if (inc < 100) return 0xFFFF9B9B + (inc << 8);
else if (inc < 200) return 0xFFFFFF9B - ((inc-100) << 16);
else if (inc < 300) return 0xFF9BFF9B + (inc-200);
else if (inc < 400) return 0xFF9BFFFF - ((inc-300) << 8);
else if (inc < 500) return 0xFF9B9BFF + ((inc-400) << 16);
else return 0xFFFF9BFF - (inc-500);
}
void RenderText(u32 color, const char* text, Item* item)
{
u32 w, h;
int breaks[64];
bool rainbow = (color == 0);
u32 rainbowinc = ((text[0] * 17) + (SDL_GetTicks() * 13)) % 600;
color |= 0xFF000000;
const u32 shadow = 0xE0000000;
LayoutText(text, &w, &h, breaks);
item->Width = w;
item->Height = h;
item->Bitmap = new u32[w*h];
memset(item->Bitmap, 0, w*h*sizeof(u32));
u32 x = 0, y = 1;
u32 maxw = mainWindow->panel->width() - (kOSDMargin*2);
int curline = 0;
u16* ptr;
for (int i = 0; text[i] != '\0'; )
{
int glyphsize;
if (text[i] == ' ')
{
x += 6;
}
else
{
u32 ch = text[i];
if (ch < 0x10 || ch > 0x7E) ch = 0x7F;
ptr = &font[(ch-0x10) << 4];
int glyphsize = ptr[0];
if (!glyphsize) x += 6;
else
{
x++;
if (rainbow)
{
color = RainbowColor(rainbowinc);
rainbowinc = (rainbowinc + 30) % 600;
}
// draw character
for (int cy = 0; cy < 12; cy++)
{
u16 val = ptr[4+cy];
for (int cx = 0; cx < glyphsize; cx++)
{
if (val & (1<<cx))
item->Bitmap[((y+cy) * w) + x+cx] = color;
}
}
x += glyphsize;
x++;
}
}
i++;
if (breaks[curline] && i >= breaks[curline])
{
i = breaks[curline++];
if (text[i] == ' ') i++;
x = 0;
y += 14;
}
}
// shadow
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
u32 val;
val = item->Bitmap[(y * w) + x];
if ((val >> 24) == 0xFF) continue;
if (x > 0) val = item->Bitmap[(y * w) + x-1];
if (x < w-1) val |= item->Bitmap[(y * w) + x+1];
if (y > 0)
{
if (x > 0) val |= item->Bitmap[((y-1) * w) + x-1];
val |= item->Bitmap[((y-1) * w) + x];
if (x < w-1) val |= item->Bitmap[((y-1) * w) + x+1];
}
if (y < h-1)
{
if (x > 0) val |= item->Bitmap[((y+1) * w) + x-1];
val |= item->Bitmap[((y+1) * w) + x];
if (x < w-1) val |= item->Bitmap[((y+1) * w) + x+1];
}
if ((val >> 24) == 0xFF)
item->Bitmap[(y * w) + x] = shadow;
}
}
}
void AddMessage(u32 color, const char* text)
{
if (!Config::ShowOSD) return;
while (Rendering);
Item item;
item.Timestamp = SDL_GetTicks();
strncpy(item.Text, text, 255); item.Text[255] = '\0';
item.Color = color;
item.Bitmap = nullptr;
item.NativeBitmapLoaded = false;
item.GLTextureLoaded = false;
ItemQueue.push_back(item);
}
void Update(QOpenGLFunctions_3_2_Core* f)
{
if (!Config::ShowOSD)
{
Rendering = true;
for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
Item& item = *it;
if (item.GLTextureLoaded && f) f->glDeleteTextures(1, &item.GLTexture);
if (item.Bitmap) delete[] item.Bitmap;
it = ItemQueue.erase(it);
}
Rendering = false;
return;
}
Rendering = true;
Uint32 tick_now = SDL_GetTicks();
Uint32 tick_min = tick_now - 2500;
for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
Item& item = *it;
if (item.Timestamp < tick_min)
{
if (item.GLTextureLoaded) f->glDeleteTextures(1, &item.GLTexture);
if (item.Bitmap) delete[] item.Bitmap;
it = ItemQueue.erase(it);
continue;
}
if (!item.Bitmap)
{
RenderText(item.Color, item.Text, &item);
}
it++;
}
Rendering = false;
}
void DrawNative(QPainter& painter)
{
if (!Config::ShowOSD) return;
Rendering = true;
u32 y = kOSDMargin;
painter.resetTransform();
for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
Item& item = *it;
if (!item.NativeBitmapLoaded)
{
item.NativeBitmap = QImage((const uchar*)item.Bitmap, item.Width, item.Height, QImage::Format_ARGB32_Premultiplied);
item.NativeBitmapLoaded = true;
}
painter.drawImage(kOSDMargin, y, item.NativeBitmap);
y += item.Height;
it++;
}
Rendering = false;
}
void DrawGL(QOpenGLFunctions_3_2_Core* f, float w, float h)
{
if (!Config::ShowOSD) return;
if (!mainWindow || !mainWindow->panel) return;
Rendering = true;
u32 y = kOSDMargin;
Shader->bind();
f->glUniform2f(uScreenSize, w, h);
f->glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer);
f->glBindVertexArray(OSDVertexArray);
f->glActiveTexture(GL_TEXTURE0);
f->glEnable(GL_BLEND);
f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
Item& item = *it;
if (!item.GLTextureLoaded)
{
f->glGenTextures(1, &item.GLTexture);
f->glBindTexture(GL_TEXTURE_2D, item.GLTexture);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, item.Width, item.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, item.Bitmap);
item.GLTextureLoaded = true;
}
f->glBindTexture(GL_TEXTURE_2D, item.GLTexture);
f->glUniform2i(uOSDPos, kOSDMargin, y);
f->glUniform2i(uOSDSize, item.Width, item.Height);
f->glDrawArrays(GL_TRIANGLES, 0, 2*3);
y += item.Height;
it++;
}
f->glDisable(GL_BLEND);
Shader->release();
Rendering = false;
}
}

36
src/frontend/qt_sdl/OSD.h Normal file
View File

@ -0,0 +1,36 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef OSD_H
#define OSD_H
namespace OSD
{
bool Init(QOpenGLFunctions_3_2_Core* f);
void DeInit(QOpenGLFunctions_3_2_Core* f);
void AddMessage(u32 color, const char* text);
void Update(QOpenGLFunctions_3_2_Core* f);
void DrawNative(QPainter& painter);
void DrawGL(QOpenGLFunctions_3_2_Core* f, float w, float h);
}
#endif // OSD_H

View File

@ -0,0 +1,65 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef OSD_SHADERS_H
#define OSD_SHADERS_H
const char* kScreenVS_OSD = R"(#version 140
uniform vec2 uScreenSize;
uniform ivec2 uOSDPos;
uniform ivec2 uOSDSize;
in vec2 vPosition;
smooth out vec2 fTexcoord;
void main()
{
vec4 fpos;
vec2 osdpos = (vPosition * vec2(uOSDSize));
fTexcoord = osdpos;
osdpos += uOSDPos;
fpos.xy = ((osdpos * 2.0) / uScreenSize) - 1.0;
fpos.y *= -1;
fpos.z = 0.0;
fpos.w = 1.0;
gl_Position = fpos;
}
)";
const char* kScreenFS_OSD = R"(#version 140
uniform sampler2D OSDTex;
smooth in vec2 fTexcoord;
out vec4 oColor;
void main()
{
vec4 pixel = texelFetch(OSDTex, ivec2(fTexcoord), 0);
oColor = pixel.bgra;
}
)";
#endif // OSD_SHADERS_H

View File

@ -27,8 +27,9 @@
#include "Platform.h"
#include "PlatformConfig.h"
//#include "LAN_Socket.h"
//#include "LAN_PCap.h"
#include "LAN_Socket.h"
#include "LAN_PCap.h"
#include <string>
#ifdef __WIN32__
#define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK
@ -56,9 +57,10 @@
#endif
extern char* EmuDirectory;
char* EmuDirectory;
void Stop(bool internal);
void emuStop();
void* oglGetProcAddress(const char* proc);
namespace Platform
@ -71,11 +73,57 @@ u8 PacketBuffer[2048];
#define NIFI_VER 1
void Init(int argc, char** argv)
{
#if defined(__WIN32__) || defined(UNIX_PORTABLE)
if (argc > 0 && strlen(argv[0]) > 0)
{
int len = strlen(argv[0]);
while (len > 0)
{
if (argv[0][len] == '/') break;
if (argv[0][len] == '\\') break;
len--;
}
if (len > 0)
{
EmuDirectory = new char[len+1];
strncpy(EmuDirectory, argv[0], len);
EmuDirectory[len] = '\0';
}
else
{
EmuDirectory = new char[2];
strcpy(EmuDirectory, ".");
}
}
else
{
EmuDirectory = new char[2];
strcpy(EmuDirectory, ".");
}
#else
QString confdir;
QDir config(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation));
config.mkdir("melonDS");
confdir = config.absolutePath() + "/melonDS/";
EmuDirectory = new char[confdir.length() + 1];
memcpy(EmuDirectory, confdir.toUtf8().data(), confdir.length());
#endif
}
void DeInit()
{
delete[] EmuDirectory;
}
void StopEmu()
{
//Stop(true);
emuStop();
}
FILE* OpenFile(const char* path, const char* mode, bool mustexist)
{
QFile f(path);
@ -200,30 +248,30 @@ void Semaphore_Post(void* sema)
void* GL_GetProcAddress(const char* proc)
{
return (void*) QOpenGLContext::globalShareContext()->getProcAddress(proc);
return oglGetProcAddress(proc);
}
bool MP_Init()
{
int opt_true = 1;
int res;
int opt_true = 1;
int res;
#ifdef __WIN32__
WSADATA wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
{
return false;
}
WSADATA wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
{
return false;
}
#endif // __WIN32__
MPSocket = socket(AF_INET, SOCK_DGRAM, 0);
MPSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (MPSocket < 0)
{
return false;
}
res = setsockopt(MPSocket, SOL_SOCKET, SO_REUSEADDR, (const char*) &opt_true, sizeof(int));
res = setsockopt(MPSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt_true, sizeof(int));
if (res < 0)
{
closesocket(MPSocket);
@ -233,8 +281,8 @@ bool MP_Init()
sockaddr_t saddr;
saddr.sa_family = AF_INET;
*(u32*) &saddr.sa_data[2] = htonl(Config::SocketBindAnyAddr ? INADDR_ANY : INADDR_LOOPBACK);
*(u16*) &saddr.sa_data[0] = htons(7064);
*(u32*)&saddr.sa_data[2] = htonl(Config::SocketBindAnyAddr ? INADDR_ANY : INADDR_LOOPBACK);
*(u16*)&saddr.sa_data[0] = htons(7064);
res = bind(MPSocket, &saddr, sizeof(sockaddr_t));
if (res < 0)
{
@ -243,7 +291,7 @@ bool MP_Init()
return false;
}
res = setsockopt(MPSocket, SOL_SOCKET, SO_BROADCAST, (const char*) &opt_true, sizeof(int));
res = setsockopt(MPSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&opt_true, sizeof(int));
if (res < 0)
{
closesocket(MPSocket);
@ -252,50 +300,50 @@ bool MP_Init()
}
MPSendAddr.sa_family = AF_INET;
*(u32*) &MPSendAddr.sa_data[2] = htonl(INADDR_BROADCAST);
*(u16*) &MPSendAddr.sa_data[0] = htons(7064);
*(u32*)&MPSendAddr.sa_data[2] = htonl(INADDR_BROADCAST);
*(u16*)&MPSendAddr.sa_data[0] = htons(7064);
return true;
}
void MP_DeInit()
{
if (MPSocket >= 0)
closesocket(MPSocket);
if (MPSocket >= 0)
closesocket(MPSocket);
#ifdef __WIN32__
WSACleanup();
WSACleanup();
#endif // __WIN32__
}
int MP_SendPacket(u8* data, int len)
{
if (MPSocket < 0)
return 0;
if (MPSocket < 0)
return 0;
if (len > 2048 - 8)
{
printf("MP_SendPacket: error: packet too long (%d)\n", len);
return 0;
}
if (len > 2048-8)
{
printf("MP_SendPacket: error: packet too long (%d)\n", len);
return 0;
}
*(u32*) &PacketBuffer[0] = htonl(0x4946494E); // NIFI
PacketBuffer[4] = NIFI_VER;
PacketBuffer[5] = 0;
*(u16*) &PacketBuffer[6] = htons(len);
memcpy(&PacketBuffer[8], data, len);
*(u32*)&PacketBuffer[0] = htonl(0x4946494E); // NIFI
PacketBuffer[4] = NIFI_VER;
PacketBuffer[5] = 0;
*(u16*)&PacketBuffer[6] = htons(len);
memcpy(&PacketBuffer[8], data, len);
int slen = sendto(MPSocket, (const char*) PacketBuffer, len + 8, 0, &MPSendAddr, sizeof(sockaddr_t));
if (slen < 8) return 0;
return slen - 8;
int slen = sendto(MPSocket, (const char*)PacketBuffer, len+8, 0, &MPSendAddr, sizeof(sockaddr_t));
if (slen < 8) return 0;
return slen - 8;
}
int MP_RecvPacket(u8* data, bool block)
{
if (MPSocket < 0)
return 0;
if (MPSocket < 0)
return 0;
fd_set fd;
fd_set fd;
struct timeval tv;
FD_ZERO(&fd);
@ -303,83 +351,82 @@ int MP_RecvPacket(u8* data, bool block)
tv.tv_sec = 0;
tv.tv_usec = block ? 5000 : 0;
if (!select(MPSocket + 1, &fd, 0, 0, &tv))
{
return 0;
}
if (!select(MPSocket+1, &fd, 0, 0, &tv))
{
return 0;
}
sockaddr_t fromAddr;
socklen_t fromLen = sizeof(sockaddr_t);
int rlen = recvfrom(MPSocket, (char*) PacketBuffer, 2048, 0, &fromAddr, &fromLen);
if (rlen < 8 + 24)
{
return 0;
}
rlen -= 8;
sockaddr_t fromAddr;
socklen_t fromLen = sizeof(sockaddr_t);
int rlen = recvfrom(MPSocket, (char*)PacketBuffer, 2048, 0, &fromAddr, &fromLen);
if (rlen < 8+24)
{
return 0;
}
rlen -= 8;
if (ntohl(*(u32*) &PacketBuffer[0]) != 0x4946494E)
{
return 0;
}
if (ntohl(*(u32*)&PacketBuffer[0]) != 0x4946494E)
{
return 0;
}
if (PacketBuffer[4] != NIFI_VER)
{
return 0;
}
if (PacketBuffer[4] != NIFI_VER)
{
return 0;
}
if (ntohs(*(u16*) &PacketBuffer[6]) != rlen)
{
return 0;
}
if (ntohs(*(u16*)&PacketBuffer[6]) != rlen)
{
return 0;
}
memcpy(data, &PacketBuffer[8], rlen);
return rlen;
memcpy(data, &PacketBuffer[8], rlen);
return rlen;
}
bool LAN_Init()
{
/*if (Config::DirectLAN)
{
if (!LAN_PCap::Init(true))
return false;
}
else
{
if (!LAN_Socket::Init())
return false;
}*/
if (Config::DirectLAN)
{
if (!LAN_PCap::Init(true))
return false;
}
else
{
if (!LAN_Socket::Init())
return false;
}
return true;
return true;
}
void LAN_DeInit()
{
// checkme. blarg
//if (Config::DirectLAN)
// LAN_PCap::DeInit();
//else
// LAN_Socket::DeInit();
/*LAN_PCap::DeInit();
LAN_Socket::DeInit();*/
// checkme. blarg
//if (Config::DirectLAN)
// LAN_PCap::DeInit();
//else
// LAN_Socket::DeInit();
LAN_PCap::DeInit();
LAN_Socket::DeInit();
}
int LAN_SendPacket(u8* data, int len)
{
/*if (Config::DirectLAN)
return LAN_PCap::SendPacket(data, len);
else
return LAN_Socket::SendPacket(data, len);*/
return 0;
if (Config::DirectLAN)
return LAN_PCap::SendPacket(data, len);
else
return LAN_Socket::SendPacket(data, len);
}
int LAN_RecvPacket(u8* data)
{
/*if (Config::DirectLAN)
return LAN_PCap::RecvPacket(data);
else
return LAN_Socket::RecvPacket(data);*/
return 0;
if (Config::DirectLAN)
return LAN_PCap::RecvPacket(data);
else
return LAN_Socket::RecvPacket(data);
}

View File

@ -40,16 +40,24 @@ int ScreenRotation;
int ScreenGap;
int ScreenLayout;
int ScreenSizing;
int IntegerScaling;
int ScreenFilter;
int ScreenUseGL;
int ScreenVSync;
int ScreenRatio;
int ScreenVSyncInterval;
int _3DRenderer;
int Threaded3D;
int GL_ScaleFactor;
int GL_Antialias;
int LimitFPS;
int AudioSync;
int ShowOSD;
int ConsoleType;
int DirectBoot;
int SocketBindAnyAddr;
@ -60,25 +68,25 @@ int SavestateRelocSRAM;
int AudioVolume;
int MicInputType;
char MicWavPath[512];
char MicWavPath[1024];
char LastROMFolder[512];
char LastROMFolder[1024];
ConfigEntry PlatformConfigFile[] =
{
{"Key_A", 0, &KeyMapping[0], 32, NULL, 0},
{"Key_B", 0, &KeyMapping[1], 31, NULL, 0},
{"Key_Select", 0, &KeyMapping[2], 57, NULL, 0},
{"Key_Start", 0, &KeyMapping[3], 28, NULL, 0},
{"Key_Right", 0, &KeyMapping[4], 333, NULL, 0},
{"Key_Left", 0, &KeyMapping[5], 331, NULL, 0},
{"Key_Up", 0, &KeyMapping[6], 328, NULL, 0},
{"Key_Down", 0, &KeyMapping[7], 336, NULL, 0},
{"Key_R", 0, &KeyMapping[8], 54, NULL, 0},
{"Key_L", 0, &KeyMapping[9], 86, NULL, 0},
{"Key_X", 0, &KeyMapping[10], 17, NULL, 0},
{"Key_Y", 0, &KeyMapping[11], 30, NULL, 0},
{"Key_A", 0, &KeyMapping[0], -1, NULL, 0},
{"Key_B", 0, &KeyMapping[1], -1, NULL, 0},
{"Key_Select", 0, &KeyMapping[2], -1, NULL, 0},
{"Key_Start", 0, &KeyMapping[3], -1, NULL, 0},
{"Key_Right", 0, &KeyMapping[4], -1, NULL, 0},
{"Key_Left", 0, &KeyMapping[5], -1, NULL, 0},
{"Key_Up", 0, &KeyMapping[6], -1, NULL, 0},
{"Key_Down", 0, &KeyMapping[7], -1, NULL, 0},
{"Key_R", 0, &KeyMapping[8], -1, NULL, 0},
{"Key_L", 0, &KeyMapping[9], -1, NULL, 0},
{"Key_X", 0, &KeyMapping[10], -1, NULL, 0},
{"Key_Y", 0, &KeyMapping[11], -1, NULL, 0},
{"Joy_A", 0, &JoyMapping[0], -1, NULL, 0},
{"Joy_B", 0, &JoyMapping[1], -1, NULL, 0},
@ -93,14 +101,14 @@ ConfigEntry PlatformConfigFile[] =
{"Joy_X", 0, &JoyMapping[10], -1, NULL, 0},
{"Joy_Y", 0, &JoyMapping[11], -1, NULL, 0},
{"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], 0x0D, NULL, 0},
{"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], 0x35, NULL, 0},
{"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0},
{"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0},
{"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], 0x0F, NULL, 0},
{"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0},
{"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], 0x4B, NULL, 0},
{"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], 0x4D, NULL, 0},
{"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1, NULL, 0},
{"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1, NULL, 0},
{"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0},
{"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0},
{"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], -1, NULL, 0},
{"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0},
{"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, NULL, 0},
{"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, NULL, 0},
{"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0},
{"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0},
@ -121,16 +129,24 @@ ConfigEntry PlatformConfigFile[] =
{"ScreenGap", 0, &ScreenGap, 0, NULL, 0},
{"ScreenLayout", 0, &ScreenLayout, 0, NULL, 0},
{"ScreenSizing", 0, &ScreenSizing, 0, NULL, 0},
{"IntegerScaling", 0, &IntegerScaling, 0, NULL, 0},
{"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0},
{"ScreenUseGL", 0, &ScreenUseGL, 1, NULL, 0},
{"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0},
{"ScreenRatio", 0, &ScreenRatio, 0, NULL, 0},
{"ScreenUseGL", 0, &ScreenUseGL, 1, NULL, 0},
{"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0},
{"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, NULL, 0},
{"3DRenderer", 0, &_3DRenderer, 1, NULL, 0},
{"Threaded3D", 0, &Threaded3D, 1, NULL, 0},
{"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, NULL, 0},
{"GL_Antialias", 0, &GL_Antialias, 0, NULL, 0},
{"LimitFPS", 0, &LimitFPS, 0, NULL, 0},
{"AudioSync", 0, &AudioSync, 1, NULL, 0},
{"ShowOSD", 0, &ShowOSD, 1, NULL, 0},
{"ConsoleType", 0, &ConsoleType, 0, NULL, 0},
{"DirectBoot", 0, &DirectBoot, 1, NULL, 0},
{"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0},
@ -141,9 +157,9 @@ ConfigEntry PlatformConfigFile[] =
{"AudioVolume", 0, &AudioVolume, 256, NULL, 0},
{"MicInputType", 0, &MicInputType, 1, NULL, 0},
{"MicWavPath", 1, MicWavPath, 0, "", 511},
{"MicWavPath", 1, MicWavPath, 0, "", 1023},
{"LastROMFolder", 1, LastROMFolder, 0, "", 511},
{"LastROMFolder", 1, LastROMFolder, 0, "", 1023},
{"", -1, NULL, 0, NULL, 0}
};

View File

@ -53,16 +53,24 @@ extern int ScreenRotation;
extern int ScreenGap;
extern int ScreenLayout;
extern int ScreenSizing;
extern int IntegerScaling;
extern int ScreenFilter;
extern int ScreenUseGL;
extern int ScreenVSync;
extern int ScreenRatio;
extern int ScreenVSyncInterval;
extern int _3DRenderer;
extern int Threaded3D;
extern int GL_ScaleFactor;
extern int GL_Antialias;
extern int LimitFPS;
extern int AudioSync;
extern int ShowOSD;
extern int ConsoleType;
extern int DirectBoot;
extern int SocketBindAnyAddr;
@ -73,9 +81,9 @@ extern int SavestateRelocSRAM;
extern int AudioVolume;
extern int MicInputType;
extern char MicWavPath[512];
extern char MicWavPath[1024];
extern char LastROMFolder[512];
extern char LastROMFolder[1024];
}

View File

@ -0,0 +1,169 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <QFileDialog>
#include "types.h"
#include "Platform.h"
#include "Config.h"
#include "PlatformConfig.h"
#include "VideoSettingsDialog.h"
#include "ui_VideoSettingsDialog.h"
VideoSettingsDialog* VideoSettingsDialog::currentDlg = nullptr;
VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::VideoSettingsDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
oldRenderer = Config::_3DRenderer;
oldGLDisplay = Config::ScreenUseGL;
oldVSync = Config::ScreenVSync;
oldVSyncInterval = Config::ScreenVSyncInterval;
oldSoftThreaded = Config::Threaded3D;
oldGLScale = Config::GL_ScaleFactor;
grp3DRenderer = new QButtonGroup(this);
grp3DRenderer->addButton(ui->rb3DSoftware, 0);
grp3DRenderer->addButton(ui->rb3DOpenGL, 1);
connect(grp3DRenderer, SIGNAL(buttonClicked(int)), this, SLOT(onChange3DRenderer(int)));
grp3DRenderer->button(Config::_3DRenderer)->setChecked(true);
ui->cbGLDisplay->setChecked(Config::ScreenUseGL != 0);
ui->cbVSync->setChecked(Config::ScreenVSync != 0);
ui->sbVSyncInterval->setValue(Config::ScreenVSyncInterval);
ui->cbSoftwareThreaded->setChecked(Config::Threaded3D != 0);
for (int i = 1; i <= 16; i++)
ui->cbxGLResolution->addItem(QString("%1x native (%2x%3)").arg(i).arg(256*i).arg(192*i));
ui->cbxGLResolution->setCurrentIndex(Config::GL_ScaleFactor-1);
if (!Config::ScreenVSync)
ui->sbVSyncInterval->setEnabled(false);
if (Config::_3DRenderer == 0)
{
ui->cbGLDisplay->setEnabled(true);
ui->cbSoftwareThreaded->setEnabled(true);
ui->cbxGLResolution->setEnabled(false);
}
else
{
ui->cbGLDisplay->setEnabled(false);
ui->cbSoftwareThreaded->setEnabled(false);
ui->cbxGLResolution->setEnabled(true);
}
}
VideoSettingsDialog::~VideoSettingsDialog()
{
delete ui;
}
void VideoSettingsDialog::on_VideoSettingsDialog_accepted()
{
Config::Save();
closeDlg();
}
void VideoSettingsDialog::on_VideoSettingsDialog_rejected()
{
bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
Config::_3DRenderer = oldRenderer;
Config::ScreenUseGL = oldGLDisplay;
Config::ScreenVSync = oldVSync;
Config::ScreenVSyncInterval = oldVSyncInterval;
Config::Threaded3D = oldSoftThreaded;
Config::GL_ScaleFactor = oldGLScale;
bool new_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
emit updateVideoSettings(old_gl != new_gl);
closeDlg();
}
void VideoSettingsDialog::onChange3DRenderer(int renderer)
{
bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
Config::_3DRenderer = renderer;
if (renderer == 0)
{
ui->cbGLDisplay->setEnabled(true);
ui->cbSoftwareThreaded->setEnabled(true);
ui->cbxGLResolution->setEnabled(false);
}
else
{
ui->cbGLDisplay->setEnabled(false);
ui->cbSoftwareThreaded->setEnabled(false);
ui->cbxGLResolution->setEnabled(true);
}
bool new_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
emit updateVideoSettings(old_gl != new_gl);
}
void VideoSettingsDialog::on_cbGLDisplay_stateChanged(int state)
{
bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
Config::ScreenUseGL = (state != 0);
bool new_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
emit updateVideoSettings(old_gl != new_gl);
}
void VideoSettingsDialog::on_cbVSync_stateChanged(int state)
{
bool vsync = (state != 0);
ui->sbVSyncInterval->setEnabled(vsync);
Config::ScreenVSync = vsync;
}
void VideoSettingsDialog::on_sbVSyncInterval_valueChanged(int val)
{
Config::ScreenVSyncInterval = val;
}
void VideoSettingsDialog::on_cbSoftwareThreaded_stateChanged(int state)
{
Config::Threaded3D = (state != 0);
emit updateVideoSettings(false);
}
void VideoSettingsDialog::on_cbxGLResolution_currentIndexChanged(int idx)
{
// prevent a spurious change
if (ui->cbxGLResolution->count() < 16) return;
Config::GL_ScaleFactor = idx+1;
emit updateVideoSettings(false);
}

View File

@ -0,0 +1,84 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef VIDEOSETTINGSDIALOG_H
#define VIDEOSETTINGSDIALOG_H
#include <QDialog>
#include <QButtonGroup>
namespace Ui { class VideoSettingsDialog; }
class VideoSettingsDialog;
class VideoSettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit VideoSettingsDialog(QWidget* parent);
~VideoSettingsDialog();
static VideoSettingsDialog* currentDlg;
static VideoSettingsDialog* openDlg(QWidget* parent)
{
if (currentDlg)
{
currentDlg->activateWindow();
return currentDlg;
}
currentDlg = new VideoSettingsDialog(parent);
currentDlg->show();
return currentDlg;
}
static void closeDlg()
{
currentDlg = nullptr;
}
signals:
void updateVideoSettings(bool glchange);
private slots:
void on_VideoSettingsDialog_accepted();
void on_VideoSettingsDialog_rejected();
void onChange3DRenderer(int renderer);
void on_cbGLDisplay_stateChanged(int state);
void on_cbVSync_stateChanged(int state);
void on_sbVSyncInterval_valueChanged(int val);
void on_cbxGLResolution_currentIndexChanged(int idx);
void on_cbSoftwareThreaded_stateChanged(int state);
private:
Ui::VideoSettingsDialog* ui;
QButtonGroup* grp3DRenderer;
int oldRenderer;
int oldGLDisplay;
int oldVSync;
int oldVSyncInterval;
int oldSoftThreaded;
int oldGLScale;
};
#endif // VIDEOSETTINGSDIALOG_H

View File

@ -0,0 +1,229 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VideoSettingsDialog</class>
<widget class="QDialog" name="VideoSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>482</width>
<height>237</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Video settings - melonDS</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item row="1" column="1">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>OpenGL renderer</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Internal resolution:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="cbxGLResolution">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The resolution at which the 3D graphics will be rendered. Higher resolutions improve graphics quality when the main window is enlarged, but may also cause glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Software renderer</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QCheckBox" name="cbSoftwareThreaded">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Run the software renderer on a separate thread. Yields better performance on multi-core CPUs.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use separate thread</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" rowspan="3">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Display settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="6" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>VSync interval:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="sbVSyncInterval">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="cbGLDisplay">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use OpenGL to draw the DS screens to the main window. May result in better frame pacing. Mandatory when using the OpenGL 3D renderer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>OpenGL display</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="cbVSync">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When using OpenGL, synchronize the video output to your monitor's refresh rate.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>VSync</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0" colspan="2">
<widget class="QRadioButton" name="rb3DOpenGL">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The OpenGL renderer may be faster than software and supports graphical enhancements, but is more prone to glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>OpenGL</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QRadioButton" name="rb3DSoftware">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The software renderer is more accurate and less prone to rendering glitches, but requires more CPU power.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Software</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>3D renderer:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>VideoSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>VideoSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,140 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <QFileDialog>
#include "types.h"
#include "Platform.h"
#include "Config.h"
#include "PlatformConfig.h"
#include "LAN_Socket.h"
#include "LAN_PCap.h"
#include "Wifi.h"
#include "WifiSettingsDialog.h"
#include "ui_WifiSettingsDialog.h"
#ifdef __WIN32__
#define PCAP_NAME "winpcap/npcap"
#else
#define PCAP_NAME "libpcap"
#endif
WifiSettingsDialog* WifiSettingsDialog::currentDlg = nullptr;
WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::WifiSettingsDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
LAN_Socket::Init();
haspcap = LAN_PCap::Init(false);
ui->cbDirectMode->setText("Direct mode (requires " PCAP_NAME " and ethernet connection)");
ui->cbBindAnyAddr->setChecked(Config::SocketBindAnyAddr != 0);
int sel = 0;
for (int i = 0; i < LAN_PCap::NumAdapters; i++)
{
LAN_PCap::AdapterData* adapter = &LAN_PCap::Adapters[i];
ui->cbxDirectAdapter->addItem(QString(adapter->FriendlyName));
if (!strncmp(adapter->DeviceName, Config::LANDevice, 128))
sel = i;
}
ui->cbxDirectAdapter->setCurrentIndex(sel);
ui->cbDirectMode->setChecked(Config::DirectLAN != 0);
if (!haspcap) ui->cbDirectMode->setEnabled(false);
updateAdapterControls();
}
WifiSettingsDialog::~WifiSettingsDialog()
{
delete ui;
}
void WifiSettingsDialog::on_WifiSettingsDialog_accepted()
{
Config::SocketBindAnyAddr = ui->cbBindAnyAddr->isChecked() ? 1:0;
Config::DirectLAN = ui->cbDirectMode->isChecked() ? 1:0;
int sel = ui->cbxDirectAdapter->currentIndex();
if (sel < 0 || sel >= LAN_PCap::NumAdapters) sel = 0;
if (LAN_PCap::NumAdapters < 1)
{
Config::LANDevice[0] = '\0';
}
else
{
strncpy(Config::LANDevice, LAN_PCap::Adapters[sel].DeviceName, 127);
Config::LANDevice[127] = '\0';
}
Config::Save();
closeDlg();
}
void WifiSettingsDialog::on_WifiSettingsDialog_rejected()
{
closeDlg();
}
void WifiSettingsDialog::on_cbDirectMode_stateChanged(int state)
{
updateAdapterControls();
}
void WifiSettingsDialog::on_cbxDirectAdapter_currentIndexChanged(int sel)
{
if (!haspcap) return;
if (sel < 0 || sel >= LAN_PCap::NumAdapters) return;
if (LAN_PCap::NumAdapters < 1) return;
LAN_PCap::AdapterData* adapter = &LAN_PCap::Adapters[sel];
char tmp[64];
sprintf(tmp, "MAC: %02X:%02X:%02X:%02X:%02X:%02X",
adapter->MAC[0], adapter->MAC[1], adapter->MAC[2],
adapter->MAC[3], adapter->MAC[4], adapter->MAC[5]);
ui->lblAdapterMAC->setText(QString(tmp));
sprintf(tmp, "IP: %d.%d.%d.%d",
adapter->IP_v4[0], adapter->IP_v4[1],
adapter->IP_v4[2], adapter->IP_v4[3]);
ui->lblAdapterIP->setText(QString(tmp));
}
void WifiSettingsDialog::updateAdapterControls()
{
bool enable = haspcap && ui->cbDirectMode->isChecked();
ui->cbxDirectAdapter->setEnabled(enable);
ui->lblAdapterMAC->setEnabled(enable);
ui->lblAdapterIP->setEnabled(enable);
}

View File

@ -0,0 +1,68 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef WIFISETTINGSDIALOG_H
#define WIFISETTINGSDIALOG_H
#include <QDialog>
namespace Ui { class WifiSettingsDialog; }
class WifiSettingsDialog;
class WifiSettingsDialog : public QDialog
{
Q_OBJECT
public:
explicit WifiSettingsDialog(QWidget* parent);
~WifiSettingsDialog();
static WifiSettingsDialog* currentDlg;
static WifiSettingsDialog* openDlg(QWidget* parent)
{
if (currentDlg)
{
currentDlg->activateWindow();
return currentDlg;
}
currentDlg = new WifiSettingsDialog(parent);
currentDlg->show();
return currentDlg;
}
static void closeDlg()
{
currentDlg = nullptr;
}
private slots:
void on_WifiSettingsDialog_accepted();
void on_WifiSettingsDialog_rejected();
void on_cbDirectMode_stateChanged(int state);
void on_cbxDirectAdapter_currentIndexChanged(int sel);
private:
Ui::WifiSettingsDialog* ui;
bool haspcap;
void updateAdapterControls();
};
#endif // WIFISETTINGSDIALOG_H

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>WifiSettingsDialog</class>
<widget class="QDialog" name="WifiSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>479</width>
<height>217</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Wifi settings - melonDS</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Local</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="cbBindAnyAddr">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enabling this allows (theoretically) playing local multiplayer games over a local network. It may or may not help make for a better connection in general.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Bind socket to any address</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Online</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>MAC address:</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="cbDirectMode">
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Direct mode directly routes network traffic to the host network. It is the most reliable, but requires an ethernet connection.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Non-direct mode uses a layer of emulation to get around this, but is more prone to problems.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Direct mode [TEXT PLACEHOLDER]</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cbxDirectAdapter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>0</height>
</size>
</property>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Selects the network adapter through which to route network traffic under direct mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Network adapter:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>IP address:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblAdapterMAC">
<property name="text">
<string>[PLACEHOLDER]</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="lblAdapterIP">
<property name="text">
<string>[PLACEHOLDER]</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>WifiSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>WifiSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

135
src/frontend/qt_sdl/font.h Normal file
View File

@ -0,0 +1,135 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef FONT_H
#define FONT_H
unsigned short font[] = {
12, 0, 0, 0,0x0C03, 0x0E07, 0x070E, 0x039C, 0x01F8, 0x00F0, 0x00F0, 0x01F8, 0x039C, 0x070E, 0x0E07, 0x0C03,
12, 0, 0, 0,0x01C0, 0x00E0, 0x0060, 0x0860, 0x0C60, 0x0FE0, 0x07F0, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, 0x0003, 0x0003, 0x0000, 0x0000,
9, 0, 0, 0,0x01EF, 0x01EF, 0x018C, 0x01CE, 0x00E7, 0x0063, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
10, 0, 0, 0,0x00CC, 0x00CC, 0x03FF, 0x03FF, 0x00CC, 0x00CC, 0x03FF, 0x03FF, 0x00CC, 0x00CC, 0x0000, 0x0000,
8, 0, 0, 0,0x0018, 0x00FE, 0x00FF, 0x001B, 0x007F, 0x00FE, 0x00D8, 0x00FF, 0x007F, 0x0018, 0x0000, 0x0000,
10, 0, 0, 0,0x0306, 0x038F, 0x01CF, 0x00E6, 0x0070, 0x0038, 0x019C, 0x03CE, 0x03C7, 0x0183, 0x0000, 0x0000,
10, 0, 0, 0,0x007C, 0x00FE, 0x00C6, 0x00EE, 0x007C, 0x037E, 0x03E7, 0x01F3, 0x03BF, 0x031E, 0x0000, 0x0000,
4, 0, 0, 0,0x000F, 0x000F, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
4, 0, 0, 0,0x000C, 0x000E, 0x0007, 0x0003, 0x0003, 0x0003, 0x0003, 0x0007, 0x000E, 0x000C, 0x0000, 0x0000,
4, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x000C, 0x000C, 0x000C, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000,
10, 0, 0, 0,0x0030, 0x0333, 0x03B7, 0x01FE, 0x00FC, 0x00FC, 0x01FE, 0x03B7, 0x0333, 0x0030, 0x0000, 0x0000,
10, 0, 0, 0,0x0030, 0x0030, 0x0030, 0x0030, 0x03FF, 0x03FF, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000,
4, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000F, 0x000F, 0x000C, 0x000E, 0x0007, 0x0003,
10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x03FF, 0x03FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
3, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0x0007, 0x0007, 0x0000, 0x0000,
10, 0, 0, 0,0x0300, 0x0380, 0x01C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000,
4, 0, 0, 0,0x0006, 0x0007, 0x0007, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x000F, 0x000F, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C0, 0x00FE, 0x007F, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0000, 0x0000,
8, 0, 0, 0,0x007F, 0x00FF, 0x00C0, 0x00C0, 0x007C, 0x00FC, 0x00C0, 0x00C0, 0x00FF, 0x007F, 0x0000, 0x0000,
8, 0, 0, 0,0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x00FF, 0x00FE, 0x0060, 0x0060, 0x0000, 0x0000,
8, 0, 0, 0,0x00FF, 0x00FF, 0x0003, 0x0003, 0x007F, 0x00FF, 0x00C0, 0x00C0, 0x00FF, 0x007F, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x007F, 0x0003, 0x0003, 0x007F, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000,
8, 0, 0, 0,0x00FF, 0x00FF, 0x00C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000C, 0x000C, 0x000C, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x00FE, 0x00C0, 0x00C0, 0x00FE, 0x007E, 0x0000, 0x0000,
3, 0, 0, 0,0x0000, 0x0000, 0x0007, 0x0007, 0x0000, 0x0000, 0x0000, 0x0007, 0x0007, 0x0000, 0x0000, 0x0000,
4, 0, 0, 0,0x0000, 0x0000, 0x000E, 0x000E, 0x0000, 0x0000, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000,
6, 0, 0, 0,0x0030, 0x0038, 0x001C, 0x000E, 0x0007, 0x0007, 0x000E, 0x001C, 0x0038, 0x0030, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x007F, 0x007F, 0x0000, 0x0000, 0x007F, 0x007F, 0x0000, 0x0000, 0x0000,
6, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x001C, 0x0038, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00F0, 0x0078, 0x0018, 0x0000, 0x0018, 0x0018, 0x0000, 0x0000,
10, 0, 0, 0,0x00FC, 0x01FE, 0x0387, 0x0333, 0x037B, 0x03FB, 0x01F3, 0x0007, 0x03FE, 0x03FC, 0x0000, 0x0000,
9, 0, 0, 0,0x00FE, 0x01FF, 0x0183, 0x0183, 0x0183, 0x01FF, 0x01FF, 0x0183, 0x0183, 0x0183, 0x0000, 0x0000,
9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0000, 0x0000,
8, 0, 0, 0,0x00FE, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x00FF, 0x00FE, 0x0000, 0x0000,
9, 0, 0, 0,0x007F, 0x00FF, 0x01C3, 0x0183, 0x0183, 0x0183, 0x0183, 0x01C3, 0x00FF, 0x007F, 0x0000, 0x0000,
9, 0, 0, 0,0x01FF, 0x01FF, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0003, 0x0003, 0x01FF, 0x01FF, 0x0000, 0x0000,
9, 0, 0, 0,0x01FF, 0x01FF, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000,
9, 0, 0, 0,0x01FE, 0x01FF, 0x0003, 0x0003, 0x01F3, 0x01F3, 0x0183, 0x0183, 0x01FF, 0x00FE, 0x0000, 0x0000,
9, 0, 0, 0,0x0183, 0x0183, 0x0183, 0x0183, 0x01FF, 0x01FF, 0x0183, 0x0183, 0x0183, 0x0183, 0x0000, 0x0000,
6, 0, 0, 0,0x003F, 0x003F, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x003F, 0x003F, 0x0000, 0x0000,
9, 0, 0, 0,0x01F0, 0x01F0, 0x00C0, 0x00C0, 0x00C0, 0x00C0, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000,
9, 0, 0, 0,0x0183, 0x01C3, 0x00E3, 0x0073, 0x003F, 0x003F, 0x0073, 0x00E3, 0x01C3, 0x0183, 0x0000, 0x0000,
7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x007F, 0x007F, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0387, 0x03CF, 0x03FF, 0x037B, 0x0333, 0x0303, 0x0303, 0x0303, 0x0303, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0307, 0x030F, 0x031F, 0x033B, 0x0373, 0x03E3, 0x03C3, 0x0383, 0x0303, 0x0000, 0x0000,
10, 0, 0, 0,0x01FE, 0x03FF, 0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x03FF, 0x01FE, 0x0000, 0x0000,
9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000,
10, 0, 0, 0,0x01FE, 0x03FF, 0x0303, 0x0303, 0x0333, 0x0373, 0x03E3, 0x01C3, 0x03FF, 0x037E, 0x0000, 0x0000,
9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0073, 0x00E3, 0x01C3, 0x0183, 0x0000, 0x0000,
10, 0, 0, 0,0x01FE, 0x01FF, 0x0003, 0x0003, 0x01FF, 0x03FE, 0x0300, 0x0300, 0x03FE, 0x01FE, 0x0000, 0x0000,
10, 0, 0, 0,0x03FF, 0x03FF, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000,
9, 0, 0, 0,0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x01FF, 0x00FE, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0303, 0x0303, 0x0303, 0x0333, 0x037B, 0x03FF, 0x03CF, 0x0387, 0x0303, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0078, 0x00FC, 0x01CE, 0x0387, 0x0303, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000,
10, 0, 0, 0,0x03FF, 0x03FF, 0x01C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000E, 0x03FF, 0x03FF, 0x0000, 0x0000,
4, 0, 0, 0,0x000F, 0x000F, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x000F, 0x000F, 0x0000, 0x0000,
10, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x001C, 0x0038, 0x0070, 0x00E0, 0x01C0, 0x0380, 0x0300, 0x0000, 0x0000,
4, 0, 0, 0,0x000F, 0x000F, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000F, 0x000F, 0x0000, 0x0000,
8, 0, 0, 0,0x0018, 0x003C, 0x007E, 0x00E7, 0x00C3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03FF, 0x03FF,
4, 0, 0, 0,0x000F, 0x000F, 0x0003, 0x0007, 0x000E, 0x000C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x003E, 0x007E, 0x0060, 0x007E, 0x007F, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000,
7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x007F, 0x003F, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0003, 0x0003, 0x0003, 0x0003, 0x007F, 0x007E, 0x0000, 0x0000,
7, 0, 0, 0,0x0060, 0x0060, 0x0060, 0x007E, 0x007F, 0x0063, 0x0063, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x003E, 0x007F, 0x0063, 0x007F, 0x003F, 0x0003, 0x003F, 0x003E, 0x0000, 0x0000,
6, 0, 0, 0,0x003C, 0x003E, 0x0006, 0x0006, 0x001F, 0x001F, 0x0006, 0x0006, 0x0006, 0x0006, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0063, 0x0063, 0x007F, 0x007E, 0x0060, 0x0060, 0x007E, 0x003E,
7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0000, 0x0000,
2, 0, 0, 0,0x0003, 0x0003, 0x0000, 0x0000, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000,
7, 0, 0, 0,0x0060, 0x0060, 0x0000, 0x0000, 0x0060, 0x0060, 0x0060, 0x0060, 0x0060, 0x0063, 0x007F, 0x003E,
8, 0, 0, 0,0x0003, 0x0003, 0x00E3, 0x0073, 0x003B, 0x001F, 0x001F, 0x003B, 0x0073, 0x00E3, 0x0000, 0x0000,
4, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x000F, 0x000E, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x01FF, 0x03FF, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0000, 0x0000,
8, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x003F, 0x007F, 0x0063, 0x0063, 0x007F, 0x003F, 0x0003, 0x0003, 0x0003, 0x0003,
7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0063, 0x0063, 0x007F, 0x007E, 0x0060, 0x0060, 0x0060, 0x0060,
7, 0, 0, 0,0x0000, 0x0000, 0x003B, 0x007F, 0x0067, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000,
8, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0003, 0x007F, 0x00FE, 0x00C0, 0x00FE, 0x007E, 0x0000, 0x0000,
6, 0, 0, 0,0x0006, 0x0006, 0x003F, 0x003F, 0x0006, 0x0006, 0x0006, 0x0006, 0x003E, 0x003C, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0303, 0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0303, 0x0333, 0x037B, 0x03FF, 0x03CF, 0x0387, 0x0303, 0x0000, 0x0000,
8, 0, 0, 0,0x0000, 0x0000, 0x00C3, 0x00E7, 0x007E, 0x003C, 0x003C, 0x007E, 0x00E7, 0x00C3, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0307, 0x038E, 0x01DC, 0x00F8, 0x0070, 0x0038, 0x001C, 0x000E, 0x0006,
8, 0, 0, 0,0x0000, 0x0000, 0x00FF, 0x00FF, 0x0070, 0x0038, 0x001C, 0x000E, 0x00FF, 0x00FF, 0x0000, 0x0000,
6, 0, 0, 0,0x0038, 0x003C, 0x000C, 0x000C, 0x000F, 0x000F, 0x000C, 0x000C, 0x003C, 0x0038, 0x0000, 0x0000,
2, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000,
6, 0, 0, 0,0x0007, 0x000F, 0x000C, 0x000C, 0x003C, 0x003C, 0x000C, 0x000C, 0x000F, 0x0007, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x031C, 0x03BE, 0x01F7, 0x00E3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
11, 0, 0, 0,0x0555, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0555, 0x0000,
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -21,8 +21,17 @@
#include <QThread>
#include <QWidget>
#include <QWindow>
#include <QMainWindow>
#include <QImage>
#include <QActionGroup>
#include <QOffscreenSurface>
#include <QOpenGLWidget>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_3_2_Core>
#include <QOpenGLShaderProgram>
class EmuThread : public QThread
@ -33,43 +42,124 @@ class EmuThread : public QThread
public:
explicit EmuThread(QObject* parent = nullptr);
void initOpenGL();
void deinitOpenGL();
void* oglGetProcAddress(const char* proc);
void changeWindowTitle(char* title);
// to be called from the UI thread
void emuRun();
void emuPause(bool refresh);
void emuPause();
void emuUnpause();
void emuStop();
bool emuIsRunning();
signals:
void windowUpdate();
void windowTitleChange(QString title);
void windowEmuStart();
void windowEmuStop();
void windowPauseToggle();
void windowEmuPause();
void windowEmuReset();
void windowLimitFPSChange();
void screenLayoutChange();
private:
volatile int EmuStatus;
int PrevEmuStatus;
int EmuRunning;
QOffscreenSurface* oglSurface;
QOpenGLContext* oglContext;
};
class MainWindowPanel : public QWidget
class ScreenHandler
{
Q_GADGET
public:
virtual ~ScreenHandler() {}
protected:
void screenSetupLayout(int w, int h);
QSize screenGetMinSize();
void screenOnMousePress(QMouseEvent* event);
void screenOnMouseRelease(QMouseEvent* event);
void screenOnMouseMove(QMouseEvent* event);
float screenMatrix[2][6];
bool touching;
};
class ScreenPanelNative : public QWidget, public ScreenHandler
{
Q_OBJECT
public:
explicit MainWindowPanel(QWidget* parent);
~MainWindowPanel();
explicit ScreenPanelNative(QWidget* parent);
~ScreenPanelNative();
protected:
void paintEvent(QPaintEvent* event) override;
void resizeEvent(QResizeEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
private slots:
void onScreenLayoutChanged();
private:
QImage* screen[2];
void setupScreenLayout();
QImage screen[2];
QTransform screenTrans[2];
};
class ScreenPanelGL : public QOpenGLWidget, public ScreenHandler, protected QOpenGLFunctions_3_2_Core
{
Q_OBJECT
public:
explicit ScreenPanelGL(QWidget* parent);
~ScreenPanelGL();
protected:
void initializeGL() override;
void paintGL() override;
void resizeEvent(QResizeEvent* event) override;
void resizeGL(int w, int h) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
private slots:
void onScreenLayoutChanged();
private:
void setupScreenLayout();
QOpenGLShaderProgram* screenShader;
GLuint screenVertexBuffer;
GLuint screenVertexArray;
GLuint screenTexture;
};
@ -81,8 +171,20 @@ public:
explicit MainWindow(QWidget* parent = nullptr);
~MainWindow();
bool hasOGL;
QOpenGLContext* getOGLContext();
protected:
void resizeEvent(QResizeEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
signals:
void screenLayoutChange();
private slots:
void onOpenFile();
@ -96,15 +198,40 @@ private slots:
void onReset();
void onStop();
void onOpenEmuSettings();
void onOpenInputConfig();
void onInputConfigFinished(int res);
void onOpenVideoSettings();
void onOpenAudioSettings();
void onAudioSettingsFinished(int res);
void onOpenWifiSettings();
void onWifiSettingsFinished(int res);
void onChangeSavestateSRAMReloc(bool checked);
void onChangeScreenSize();
void onChangeScreenRotation(QAction* act);
void onChangeScreenGap(QAction* act);
void onChangeScreenLayout(QAction* act);
void onChangeScreenSizing(QAction* act);
void onChangeIntegerScaling(bool checked);
void onChangeScreenFiltering(bool checked);
void onChangeShowOSD(bool checked);
void onChangeLimitFramerate(bool checked);
void onChangeAudioSync(bool checked);
void onTitleUpdate(QString title);
void onEmuStart();
void onEmuStop();
void onEmuPause();
void onEmuUnpause();
void onUpdateVideoSettings(bool glchange);
private:
MainWindowPanel* panel;
void createScreenPanel();
QString loadErrorStr(int error);
public:
QWidget* panel;
QAction* actOpenROM;
QAction* actBootFirmware;
@ -116,6 +243,27 @@ private:
QAction* actPause;
QAction* actReset;
QAction* actStop;
QAction* actEmuSettings;
QAction* actInputConfig;
QAction* actVideoSettings;
QAction* actAudioSettings;
QAction* actWifiSettings;
QAction* actSavestateSRAMReloc;
QAction* actScreenSize[4];
QActionGroup* grpScreenRotation;
QAction* actScreenRotation[4];
QActionGroup* grpScreenGap;
QAction* actScreenGap[6];
QActionGroup* grpScreenLayout;
QAction* actScreenLayout[3];
QActionGroup* grpScreenSizing;
QAction* actScreenSizing[4];
QAction* actIntegerScaling;
QAction* actScreenFiltering;
QAction* actShowOSD;
QAction* actLimitFramerate;
QAction* actAudioSync;
};
#endif // MAIN_H

View File

@ -0,0 +1,64 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef MAIN_SHADERS_H
#define MAIN_SHADERS_H
const char* kScreenVS = R"(#version 140
uniform vec2 uScreenSize;
uniform mat2x3 uTransform;
in vec2 vPosition;
in vec2 vTexcoord;
smooth out vec2 fTexcoord;
void main()
{
vec4 fpos;
fpos.xy = vec3(vPosition, 1.0) * uTransform;
fpos.xy = ((fpos.xy * 2.0) / uScreenSize) - 1.0;
fpos.y *= -1;
fpos.z = 0.0;
fpos.w = 1.0;
gl_Position = fpos;
fTexcoord = vTexcoord;
}
)";
const char* kScreenFS = R"(#version 140
uniform sampler2D ScreenTex;
smooth in vec2 fTexcoord;
out vec4 oColor;
void main()
{
vec4 pixel = texture(ScreenTex, fTexcoord);
oColor = vec4(pixel.bgr, 1.0);
}
)";
#endif // MAIN_SHADERS_H

Some files were not shown because too many files have changed in this diff Show More