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:
commit
f9f366e296
|
@ -27,7 +27,7 @@ jobs:
|
||||||
&& tar -zxf cmake-$CMAKE_VERSION-Linux-x86_64.tar.gz \
|
&& 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 rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list \
|
||||||
&& sudo apt-get update \
|
&& 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
|
- name: Create build environment
|
||||||
run: mkdir ${{runner.workspace}}/build
|
run: mkdir ${{runner.workspace}}/build
|
||||||
- name: Configure
|
- name: Configure
|
||||||
|
@ -40,7 +40,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
make -j$(nproc --all) \
|
make -j$(nproc --all) \
|
||||||
&& mkdir dist \
|
&& mkdir dist \
|
||||||
&& cp {melonDS,romlist.bin} dist
|
&& cp melonDS dist
|
||||||
- uses: actions/upload-artifact@v1
|
- uses: actions/upload-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: melonDS
|
name: melonDS
|
||||||
|
|
|
@ -25,7 +25,7 @@ jobs:
|
||||||
7z x -y msys64.7z "-oC:\tools\"
|
7z x -y msys64.7z "-oC:\tools\"
|
||||||
C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Syuq --noconfirm"
|
C:\tools\msys64\usr\bin\bash.exe -lc "pacman -Syuq --noconfirm"
|
||||||
- name: Install dependencies
|
- 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
|
- name: Create build environment
|
||||||
run: |
|
run: |
|
||||||
New-Item -ItemType directory -Path ${{runner.workspace}}\melonDS\build
|
New-Item -ItemType directory -Path ${{runner.workspace}}\melonDS\build
|
||||||
|
@ -33,7 +33,7 @@ jobs:
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \
|
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
|
- name: Make
|
||||||
run: |
|
run: |
|
||||||
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \
|
C:\tools\msys64\usr\bin\bash.exe -lc "export PATH=`"/mingw64/bin:`$PATH`" \
|
||||||
|
|
|
@ -10,23 +10,17 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
project(melonDS)
|
project(melonDS)
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Release)
|
set(CMAKE_BUILD_TYPE Release)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL Release)
|
if (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
option(ENABLE_LTO "Enable link-time optimization" ON)
|
add_compile_options(-Og)
|
||||||
else()
|
|
||||||
option(ENABLE_LTO "Enable link-time optimization" OFF)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_LTO)
|
if (CMAKE_BUILD_TYPE STREQUAL Release)
|
||||||
add_compile_options(-O3 -flto)
|
add_compile_options(-O3)
|
||||||
set(CMAKE_AR "gcc-ar")
|
add_link_options(-s)
|
||||||
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)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_compile_options(-fno-pic)
|
add_compile_options(-fno-pic)
|
||||||
|
@ -34,12 +28,12 @@ add_link_options(-no-pie)
|
||||||
|
|
||||||
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
|
option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
option(BUILD_STATIC "Statically link dependencies" OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
if (BUILD_QT_SDL)
|
if (BUILD_QT_SDL)
|
||||||
add_subdirectory(src/frontend/qt_sdl)
|
add_subdirectory(src/frontend/qt_sdl)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
configure_file(
|
|
||||||
${CMAKE_SOURCE_DIR}/romlist.bin
|
|
||||||
${CMAKE_BINARY_DIR}/romlist.bin COPYONLY)
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ As for the rest, the interface should be pretty straightforward. If you have a q
|
||||||
* Install dependencies:
|
* Install dependencies:
|
||||||
|
|
||||||
```sh
|
```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:
|
* Compile:
|
||||||
|
@ -52,10 +52,6 @@ make -j$(nproc --all)
|
||||||
|
|
||||||
### Windows:
|
### Windows:
|
||||||
|
|
||||||
* use CodeBlocks
|
|
||||||
|
|
||||||
#### MSYS2 and CMake
|
|
||||||
|
|
||||||
1. Install [MSYS2](https://www.msys2.org/)
|
1. Install [MSYS2](https://www.msys2.org/)
|
||||||
2. Open the **MSYS2 MinGW 64-bit** terminal
|
2. Open the **MSYS2 MinGW 64-bit** terminal
|
||||||
3. Update the packages using `pacman -Syu` and reopen the terminal if it asks you to
|
3. Update the packages using `pacman -Syu` and reopen the terminal if it asks you to
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<!DOCTYPE RCC>
|
||||||
|
<RCC version="1.0">
|
||||||
|
<qresource>
|
||||||
|
<file alias="melon-icon">icon/melon_32x32.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
|
@ -10,5 +10,3 @@ mkdir -p dist
|
||||||
for lib in $(ldd melonDS.exe | grep mingw | sed "s/.*=> //" | sed "s/(.*)//"); do
|
for lib in $(ldd melonDS.exe | grep mingw | sed "s/.*=> //" | sed "s/(.*)//"); do
|
||||||
cp "${lib}" dist
|
cp "${lib}" dist
|
||||||
done
|
done
|
||||||
|
|
||||||
cp melonDS.exe romlist.bin dist
|
|
||||||
|
|
BIN
romlist.bin
BIN
romlist.bin
Binary file not shown.
49
src/ARM.cpp
49
src/ARM.cpp
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
|
#include "DSi.h"
|
||||||
#include "ARM.h"
|
#include "ARM.h"
|
||||||
#include "ARMInterpreter.h"
|
#include "ARMInterpreter.h"
|
||||||
#include "AREngine.h"
|
#include "AREngine.h"
|
||||||
|
@ -101,10 +102,55 @@ void ARM::Reset()
|
||||||
|
|
||||||
void ARMv5::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();
|
CP15Reset();
|
||||||
ARM::Reset();
|
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)
|
void ARM::DoSavestate(Savestate* file)
|
||||||
{
|
{
|
||||||
|
@ -222,9 +268,6 @@ void ARMv5::JumpTo(u32 addr, bool restorecpsr)
|
||||||
CPSR &= ~0x20;
|
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))
|
/*if (!(PU_Map[addr>>12] & 0x04))
|
||||||
{
|
{
|
||||||
printf("jumped to %08X. very bad\n", addr);
|
printf("jumped to %08X. very bad\n", addr);
|
||||||
|
|
32
src/ARM.h
32
src/ARM.h
|
@ -132,6 +132,14 @@ public:
|
||||||
NDS::MemRegion CodeMem;
|
NDS::MemRegion CodeMem;
|
||||||
|
|
||||||
static u32 ConditionTable[16];
|
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
|
class ARMv5 : public ARM
|
||||||
|
@ -259,6 +267,8 @@ public:
|
||||||
|
|
||||||
s32 RegionCodeCycles;
|
s32 RegionCodeCycles;
|
||||||
u8* CurICacheLine;
|
u8* CurICacheLine;
|
||||||
|
|
||||||
|
bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ARMv4 : public ARM
|
class ARMv4 : public ARM
|
||||||
|
@ -266,23 +276,25 @@ class ARMv4 : public ARM
|
||||||
public:
|
public:
|
||||||
ARMv4();
|
ARMv4();
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
|
||||||
void JumpTo(u32 addr, bool restorecpsr = false);
|
void JumpTo(u32 addr, bool restorecpsr = false);
|
||||||
|
|
||||||
void Execute();
|
void Execute();
|
||||||
|
|
||||||
u16 CodeRead16(u32 addr)
|
u16 CodeRead16(u32 addr)
|
||||||
{
|
{
|
||||||
return NDS::ARM7Read16(addr);
|
return BusRead16(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 CodeRead32(u32 addr)
|
u32 CodeRead32(u32 addr)
|
||||||
{
|
{
|
||||||
return NDS::ARM7Read32(addr);
|
return BusRead32(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataRead8(u32 addr, u32* val)
|
void DataRead8(u32 addr, u32* val)
|
||||||
{
|
{
|
||||||
*val = NDS::ARM7Read8(addr);
|
*val = BusRead8(addr);
|
||||||
DataRegion = addr >> 24;
|
DataRegion = addr >> 24;
|
||||||
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
|
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
|
||||||
}
|
}
|
||||||
|
@ -291,7 +303,7 @@ public:
|
||||||
{
|
{
|
||||||
addr &= ~1;
|
addr &= ~1;
|
||||||
|
|
||||||
*val = NDS::ARM7Read16(addr);
|
*val = BusRead16(addr);
|
||||||
DataRegion = addr >> 24;
|
DataRegion = addr >> 24;
|
||||||
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
|
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
|
||||||
}
|
}
|
||||||
|
@ -300,7 +312,7 @@ public:
|
||||||
{
|
{
|
||||||
addr &= ~3;
|
addr &= ~3;
|
||||||
|
|
||||||
*val = NDS::ARM7Read32(addr);
|
*val = BusRead32(addr);
|
||||||
DataRegion = addr >> 24;
|
DataRegion = addr >> 24;
|
||||||
DataCycles = NDS::ARM7MemTimings[DataRegion][2];
|
DataCycles = NDS::ARM7MemTimings[DataRegion][2];
|
||||||
}
|
}
|
||||||
|
@ -309,13 +321,13 @@ public:
|
||||||
{
|
{
|
||||||
addr &= ~3;
|
addr &= ~3;
|
||||||
|
|
||||||
*val = NDS::ARM7Read32(addr);
|
*val = BusRead32(addr);
|
||||||
DataCycles += NDS::ARM7MemTimings[DataRegion][3];
|
DataCycles += NDS::ARM7MemTimings[DataRegion][3];
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataWrite8(u32 addr, u8 val)
|
void DataWrite8(u32 addr, u8 val)
|
||||||
{
|
{
|
||||||
NDS::ARM7Write8(addr, val);
|
BusWrite8(addr, val);
|
||||||
DataRegion = addr >> 24;
|
DataRegion = addr >> 24;
|
||||||
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
|
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
|
||||||
}
|
}
|
||||||
|
@ -324,7 +336,7 @@ public:
|
||||||
{
|
{
|
||||||
addr &= ~1;
|
addr &= ~1;
|
||||||
|
|
||||||
NDS::ARM7Write16(addr, val);
|
BusWrite16(addr, val);
|
||||||
DataRegion = addr >> 24;
|
DataRegion = addr >> 24;
|
||||||
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
|
DataCycles = NDS::ARM7MemTimings[DataRegion][0];
|
||||||
}
|
}
|
||||||
|
@ -333,7 +345,7 @@ public:
|
||||||
{
|
{
|
||||||
addr &= ~3;
|
addr &= ~3;
|
||||||
|
|
||||||
NDS::ARM7Write32(addr, val);
|
BusWrite32(addr, val);
|
||||||
DataRegion = addr >> 24;
|
DataRegion = addr >> 24;
|
||||||
DataCycles = NDS::ARM7MemTimings[DataRegion][2];
|
DataCycles = NDS::ARM7MemTimings[DataRegion][2];
|
||||||
}
|
}
|
||||||
|
@ -342,7 +354,7 @@ public:
|
||||||
{
|
{
|
||||||
addr &= ~3;
|
addr &= ~3;
|
||||||
|
|
||||||
NDS::ARM7Write32(addr, val);
|
BusWrite32(addr, val);
|
||||||
DataCycles += NDS::ARM7MemTimings[DataRegion][3];
|
DataCycles += NDS::ARM7MemTimings[DataRegion][3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,19 @@ add_library(core STATIC
|
||||||
CP15.cpp
|
CP15.cpp
|
||||||
CRC32.cpp
|
CRC32.cpp
|
||||||
DMA.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
|
FIFO.h
|
||||||
GBACart.cpp
|
GBACart.cpp
|
||||||
GPU.cpp
|
GPU.cpp
|
||||||
|
GPU_OpenGL.cpp
|
||||||
|
GPU_OpenGL_shaders.h
|
||||||
GPU2D.cpp
|
GPU2D.cpp
|
||||||
GPU3D.cpp
|
GPU3D.cpp
|
||||||
GPU3D_OpenGL.cpp
|
GPU3D_OpenGL.cpp
|
||||||
|
@ -25,6 +35,7 @@ add_library(core STATIC
|
||||||
NDSCart.cpp
|
NDSCart.cpp
|
||||||
OpenGLSupport.cpp
|
OpenGLSupport.cpp
|
||||||
Platform.h
|
Platform.h
|
||||||
|
ROMList.h
|
||||||
RTC.cpp
|
RTC.cpp
|
||||||
Savestate.cpp
|
Savestate.cpp
|
||||||
SPI.cpp
|
SPI.cpp
|
||||||
|
@ -33,6 +44,8 @@ add_library(core STATIC
|
||||||
version.h
|
version.h
|
||||||
Wifi.cpp
|
Wifi.cpp
|
||||||
WifiAP.cpp
|
WifiAP.cpp
|
||||||
|
|
||||||
|
tiny-AES-c/aes.c
|
||||||
)
|
)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
|
21
src/CP15.cpp
21
src/CP15.cpp
|
@ -19,6 +19,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
|
#include "DSi.h"
|
||||||
#include "ARM.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];
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
*val = NDS::ARM9Read8(addr);
|
*val = BusRead8(addr);
|
||||||
DataCycles = MemTimings[addr >> 12][1];
|
DataCycles = MemTimings[addr >> 12][1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -759,7 +760,7 @@ void ARMv5::DataRead16(u32 addr, u32* val)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
*val = NDS::ARM9Read16(addr);
|
*val = BusRead16(addr);
|
||||||
DataCycles = MemTimings[addr >> 12][1];
|
DataCycles = MemTimings[addr >> 12][1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -780,7 +781,7 @@ void ARMv5::DataRead32(u32 addr, u32* val)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
*val = NDS::ARM9Read32(addr);
|
*val = BusRead32(addr);
|
||||||
DataCycles = MemTimings[addr >> 12][2];
|
DataCycles = MemTimings[addr >> 12][2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,7 +802,7 @@ void ARMv5::DataRead32S(u32 addr, u32* val)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
*val = NDS::ARM9Read32(addr);
|
*val = BusRead32(addr);
|
||||||
DataCycles += MemTimings[addr >> 12][3];
|
DataCycles += MemTimings[addr >> 12][3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -820,7 +821,7 @@ void ARMv5::DataWrite8(u32 addr, u8 val)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NDS::ARM9Write8(addr, val);
|
BusWrite8(addr, val);
|
||||||
DataCycles = MemTimings[addr >> 12][1];
|
DataCycles = MemTimings[addr >> 12][1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -841,7 +842,7 @@ void ARMv5::DataWrite16(u32 addr, u16 val)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NDS::ARM9Write16(addr, val);
|
BusWrite16(addr, val);
|
||||||
DataCycles = MemTimings[addr >> 12][1];
|
DataCycles = MemTimings[addr >> 12][1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -862,7 +863,7 @@ void ARMv5::DataWrite32(u32 addr, u32 val)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NDS::ARM9Write32(addr, val);
|
BusWrite32(addr, val);
|
||||||
DataCycles = MemTimings[addr >> 12][2];
|
DataCycles = MemTimings[addr >> 12][2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -883,7 +884,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NDS::ARM9Write32(addr, val);
|
BusWrite32(addr, val);
|
||||||
DataCycles += MemTimings[addr >> 12][3];
|
DataCycles += MemTimings[addr >> 12][3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -896,6 +897,6 @@ void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region)
|
||||||
return;
|
return;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
NDS::ARM9GetMemRegion(addr, false, &CodeMem);
|
GetMemRegion(addr, false, &CodeMem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,19 +28,25 @@ namespace Config
|
||||||
|
|
||||||
const char* kConfigFile = "melonDS.ini";
|
const char* kConfigFile = "melonDS.ini";
|
||||||
|
|
||||||
int _3DRenderer;
|
char BIOS9Path[1024];
|
||||||
int Threaded3D;
|
char BIOS7Path[1024];
|
||||||
|
char FirmwarePath[1024];
|
||||||
|
|
||||||
int GL_ScaleFactor;
|
char DSiBIOS9Path[1024];
|
||||||
int GL_Antialias;
|
char DSiBIOS7Path[1024];
|
||||||
|
char DSiFirmwarePath[1024];
|
||||||
|
char DSiNANDPath[1024];
|
||||||
|
|
||||||
ConfigEntry ConfigFile[] =
|
ConfigEntry ConfigFile[] =
|
||||||
{
|
{
|
||||||
{"3DRenderer", 0, &_3DRenderer, 1, NULL, 0},
|
{"BIOS9Path", 1, BIOS9Path, 0, "", 1023},
|
||||||
{"Threaded3D", 0, &Threaded3D, 1, NULL, 0},
|
{"BIOS7Path", 1, BIOS7Path, 0, "", 1023},
|
||||||
|
{"FirmwarePath", 1, FirmwarePath, 0, "", 1023},
|
||||||
|
|
||||||
{"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, NULL, 0},
|
{"DSiBIOS9Path", 1, DSiBIOS9Path, 0, "", 1023},
|
||||||
{"GL_Antialias", 0, &GL_Antialias, 0, NULL, 0},
|
{"DSiBIOS7Path", 1, DSiBIOS7Path, 0, "", 1023},
|
||||||
|
{"DSiFirmwarePath", 1, DSiFirmwarePath, 0, "", 1023},
|
||||||
|
{"DSiNANDPath", 1, DSiNANDPath, 0, "", 1023},
|
||||||
|
|
||||||
{"", -1, NULL, 0, NULL, 0}
|
{"", -1, NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
|
@ -82,7 +88,8 @@ void Load()
|
||||||
while (!feof(f))
|
while (!feof(f))
|
||||||
{
|
{
|
||||||
fgets(linebuf, 1024, 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;
|
if (ret < 2) continue;
|
||||||
|
|
||||||
ConfigEntry* entry = &ConfigFile[0];
|
ConfigEntry* entry = &ConfigFile[0];
|
||||||
|
|
11
src/Config.h
11
src/Config.h
|
@ -42,11 +42,14 @@ bool HasConfigFile(const char* fileName);
|
||||||
void Load();
|
void Load();
|
||||||
void Save();
|
void Save();
|
||||||
|
|
||||||
extern int _3DRenderer;
|
extern char BIOS9Path[1024];
|
||||||
extern int Threaded3D;
|
extern char BIOS7Path[1024];
|
||||||
|
extern char FirmwarePath[1024];
|
||||||
|
|
||||||
extern int GL_ScaleFactor;
|
extern char DSiBIOS9Path[1024];
|
||||||
extern int GL_Antialias;
|
extern char DSiBIOS7Path[1024];
|
||||||
|
extern char DSiFirmwarePath[1024];
|
||||||
|
extern char DSiNANDPath[1024];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
32
src/DMA.cpp
32
src/DMA.cpp
|
@ -18,16 +18,11 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
|
#include "DSi.h"
|
||||||
#include "DMA.h"
|
#include "DMA.h"
|
||||||
#include "NDSCart.h"
|
|
||||||
#include "GPU.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
|
// DMA TIMINGS
|
||||||
//
|
//
|
||||||
|
@ -58,8 +53,6 @@ DMA::DMA(u32 cpu, u32 num)
|
||||||
CountMask = 0x001FFFFF;
|
CountMask = 0x001FFFFF;
|
||||||
else
|
else
|
||||||
CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF);
|
CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF);
|
||||||
|
|
||||||
Reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DMA::~DMA()
|
DMA::~DMA()
|
||||||
|
@ -82,6 +75,21 @@ void DMA::Reset()
|
||||||
|
|
||||||
Running = false;
|
Running = false;
|
||||||
InProgress = 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)
|
void DMA::DoSavestate(Savestate* file)
|
||||||
|
@ -232,7 +240,7 @@ void DMA::Run9()
|
||||||
{
|
{
|
||||||
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
|
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
|
||||||
|
|
||||||
NDS::ARM9Write16(CurDstAddr, NDS::ARM9Read16(CurSrcAddr));
|
BusWrite16(CurDstAddr, BusRead16(CurSrcAddr));
|
||||||
|
|
||||||
CurSrcAddr += SrcAddrInc<<1;
|
CurSrcAddr += SrcAddrInc<<1;
|
||||||
CurDstAddr += DstAddrInc<<1;
|
CurDstAddr += DstAddrInc<<1;
|
||||||
|
@ -268,7 +276,7 @@ void DMA::Run9()
|
||||||
{
|
{
|
||||||
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
|
NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift);
|
||||||
|
|
||||||
NDS::ARM9Write32(CurDstAddr, NDS::ARM9Read32(CurSrcAddr));
|
BusWrite32(CurDstAddr, BusRead32(CurSrcAddr));
|
||||||
|
|
||||||
CurSrcAddr += SrcAddrInc<<2;
|
CurSrcAddr += SrcAddrInc<<2;
|
||||||
CurDstAddr += DstAddrInc<<2;
|
CurDstAddr += DstAddrInc<<2;
|
||||||
|
@ -344,7 +352,7 @@ void DMA::Run7()
|
||||||
{
|
{
|
||||||
NDS::ARM7Timestamp += unitcycles;
|
NDS::ARM7Timestamp += unitcycles;
|
||||||
|
|
||||||
NDS::ARM7Write16(CurDstAddr, NDS::ARM7Read16(CurSrcAddr));
|
BusWrite16(CurDstAddr, BusRead16(CurSrcAddr));
|
||||||
|
|
||||||
CurSrcAddr += SrcAddrInc<<1;
|
CurSrcAddr += SrcAddrInc<<1;
|
||||||
CurDstAddr += DstAddrInc<<1;
|
CurDstAddr += DstAddrInc<<1;
|
||||||
|
@ -380,7 +388,7 @@ void DMA::Run7()
|
||||||
{
|
{
|
||||||
NDS::ARM7Timestamp += unitcycles;
|
NDS::ARM7Timestamp += unitcycles;
|
||||||
|
|
||||||
NDS::ARM7Write32(CurDstAddr, NDS::ARM7Read32(CurSrcAddr));
|
BusWrite32(CurDstAddr, BusRead32(CurSrcAddr));
|
||||||
|
|
||||||
CurSrcAddr += SrcAddrInc<<2;
|
CurSrcAddr += SrcAddrInc<<2;
|
||||||
CurDstAddr += DstAddrInc<<2;
|
CurDstAddr += DstAddrInc<<2;
|
||||||
|
|
|
@ -86,6 +86,11 @@ private:
|
||||||
bool Stall;
|
bool Stall;
|
||||||
|
|
||||||
bool IsGXFIFODMA;
|
bool IsGXFIFODMA;
|
||||||
|
|
||||||
|
u16 (*BusRead16)(u32 addr);
|
||||||
|
u32 (*BusRead32)(u32 addr);
|
||||||
|
void (*BusWrite16)(u32 addr, u16 val);
|
||||||
|
void (*BusWrite32)(u32 addr, u32 val);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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/.
|
||||||
|
*/
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -89,6 +89,15 @@ public:
|
||||||
return Entries[ReadPos];
|
return Entries[ReadPos];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T Peek(u32 offset)
|
||||||
|
{
|
||||||
|
u32 pos = ReadPos + offset;
|
||||||
|
if (pos >= NumEntries)
|
||||||
|
pos -= NumEntries;
|
||||||
|
|
||||||
|
return Entries[pos];
|
||||||
|
}
|
||||||
|
|
||||||
u32 Level() { return NumOccupied; }
|
u32 Level() { return NumOccupied; }
|
||||||
bool IsEmpty() { return NumOccupied == 0; }
|
bool IsEmpty() { return NumOccupied == 0; }
|
||||||
bool IsFull() { return NumOccupied >= NumEntries; }
|
bool IsFull() { return NumOccupied >= NumEntries; }
|
||||||
|
|
89
src/GPU.cpp
89
src/GPU.cpp
|
@ -21,6 +21,7 @@
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
#include "GPU.h"
|
#include "GPU.h"
|
||||||
|
|
||||||
|
|
||||||
namespace GPU
|
namespace GPU
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -78,6 +79,7 @@ u8* VRAMPtr_BOBJ[0x8];
|
||||||
|
|
||||||
int FrontBuffer;
|
int FrontBuffer;
|
||||||
u32* Framebuffer[2][2];
|
u32* Framebuffer[2][2];
|
||||||
|
int Renderer;
|
||||||
bool Accelerated;
|
bool Accelerated;
|
||||||
|
|
||||||
GPU2D* GPU2D_A;
|
GPU2D* GPU2D_A;
|
||||||
|
@ -93,8 +95,8 @@ bool Init()
|
||||||
FrontBuffer = 0;
|
FrontBuffer = 0;
|
||||||
Framebuffer[0][0] = NULL; Framebuffer[0][1] = NULL;
|
Framebuffer[0][0] = NULL; Framebuffer[0][1] = NULL;
|
||||||
Framebuffer[1][0] = NULL; Framebuffer[1][1] = NULL;
|
Framebuffer[1][0] = NULL; Framebuffer[1][1] = NULL;
|
||||||
|
Renderer = 0;
|
||||||
Accelerated = false;
|
Accelerated = false;
|
||||||
SetDisplaySettings(false);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -182,6 +184,8 @@ void Reset()
|
||||||
int backbuf = FrontBuffer ? 0 : 1;
|
int backbuf = FrontBuffer ? 0 : 1;
|
||||||
GPU2D_A->SetFramebuffer(Framebuffer[backbuf][1]);
|
GPU2D_A->SetFramebuffer(Framebuffer[backbuf][1]);
|
||||||
GPU2D_B->SetFramebuffer(Framebuffer[backbuf][0]);
|
GPU2D_B->SetFramebuffer(Framebuffer[backbuf][0]);
|
||||||
|
|
||||||
|
ResetRenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stop()
|
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;
|
int fbsize;
|
||||||
if (accel) fbsize = (256*3 + 1) * 192;
|
if (accel) fbsize = (256*3 + 1) * 192;
|
||||||
else fbsize = 256 * 192;
|
else fbsize = 256 * 192;
|
||||||
if (Framebuffer[0][0]) delete[] Framebuffer[0][0];
|
if (Framebuffer[0][0]) { delete[] Framebuffer[0][0]; Framebuffer[0][0] = nullptr; }
|
||||||
if (Framebuffer[1][0]) delete[] Framebuffer[1][0];
|
if (Framebuffer[1][0]) { delete[] Framebuffer[1][0]; Framebuffer[1][0] = nullptr; }
|
||||||
if (Framebuffer[0][1]) delete[] Framebuffer[0][1];
|
if (Framebuffer[0][1]) { delete[] Framebuffer[0][1]; Framebuffer[0][1] = nullptr; }
|
||||||
if (Framebuffer[1][1]) delete[] Framebuffer[1][1];
|
if (Framebuffer[1][1]) { delete[] Framebuffer[1][1]; Framebuffer[1][1] = nullptr; }
|
||||||
|
|
||||||
Framebuffer[0][0] = new u32[fbsize];
|
Framebuffer[0][0] = new u32[fbsize];
|
||||||
Framebuffer[1][0] = new u32[fbsize];
|
Framebuffer[1][0] = new u32[fbsize];
|
||||||
|
@ -296,10 +357,18 @@ void SetDisplaySettings(bool accel)
|
||||||
|
|
||||||
AssignFramebuffers();
|
AssignFramebuffers();
|
||||||
|
|
||||||
GPU2D_A->SetDisplaySettings(accel);
|
GPU2D_A->SetRenderSettings(accel);
|
||||||
GPU2D_B->SetDisplaySettings(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_A->VBlank();
|
||||||
GPU2D_B->VBlank();
|
GPU2D_B->VBlank();
|
||||||
GPU3D::VBlank();
|
GPU3D::VBlank();
|
||||||
|
|
||||||
|
if (Accelerated) GLCompositor::RenderFrame();
|
||||||
}
|
}
|
||||||
else if (VCount == 144)
|
else if (VCount == 144)
|
||||||
{
|
{
|
||||||
|
|
34
src/GPU.h
34
src/GPU.h
|
@ -20,7 +20,6 @@
|
||||||
#define GPU_H
|
#define GPU_H
|
||||||
|
|
||||||
#include "GPU2D.h"
|
#include "GPU2D.h"
|
||||||
#include "GPU3D.h"
|
|
||||||
|
|
||||||
namespace GPU
|
namespace GPU
|
||||||
{
|
{
|
||||||
|
@ -72,6 +71,17 @@ extern u32* Framebuffer[2][2];
|
||||||
extern GPU2D* GPU2D_A;
|
extern GPU2D* GPU2D_A;
|
||||||
extern GPU2D* GPU2D_B;
|
extern GPU2D* GPU2D_B;
|
||||||
|
|
||||||
|
extern int Renderer;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool Soft_Threaded;
|
||||||
|
|
||||||
|
int GL_ScaleFactor;
|
||||||
|
|
||||||
|
} RenderSettings;
|
||||||
|
|
||||||
|
|
||||||
bool Init();
|
bool Init();
|
||||||
void DeInit();
|
void DeInit();
|
||||||
|
@ -80,7 +90,11 @@ void Stop();
|
||||||
|
|
||||||
void DoSavestate(Savestate* file);
|
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);
|
u8* GetUniqueBankPtr(u32 mask, u32 offset);
|
||||||
|
@ -422,6 +436,22 @@ void SetDispStat(u32 cpu, u16 val);
|
||||||
|
|
||||||
void SetVCount(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
|
#endif
|
||||||
|
|
|
@ -202,16 +202,8 @@ void GPU2D::DoSavestate(Savestate* file)
|
||||||
file->Var32(&CaptureCnt);
|
file->Var32(&CaptureCnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file->IsAtleastVersion(2, 1))
|
file->Var32(&Win0Active);
|
||||||
{
|
file->Var32(&Win1Active);
|
||||||
file->Var32(&Win0Active);
|
|
||||||
file->Var32(&Win1Active);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Win0Active = 0;
|
|
||||||
Win1Active = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file->Saving)
|
if (!file->Saving)
|
||||||
{
|
{
|
||||||
|
@ -232,7 +224,7 @@ void GPU2D::SetFramebuffer(u32* buf)
|
||||||
Framebuffer = buf;
|
Framebuffer = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU2D::SetDisplaySettings(bool accel)
|
void GPU2D::SetRenderSettings(bool accel)
|
||||||
{
|
{
|
||||||
Accelerated = accel;
|
Accelerated = accel;
|
||||||
|
|
||||||
|
@ -728,6 +720,8 @@ u32 GPU2D::ColorComposite(int i, u32 val1, u32 val2)
|
||||||
case 3: return ColorBrightnessDown(val1, EVY);
|
case 3: return ColorBrightnessDown(val1, EVY);
|
||||||
case 4: return ColorBlend5(val1, val2);
|
case 4: return ColorBlend5(val1, val2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return val1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ public:
|
||||||
|
|
||||||
void SetEnabled(bool enable) { Enabled = enable; }
|
void SetEnabled(bool enable) { Enabled = enable; }
|
||||||
void SetFramebuffer(u32* buf);
|
void SetFramebuffer(u32* buf);
|
||||||
void SetDisplaySettings(bool accel);
|
void SetRenderSettings(bool accel);
|
||||||
|
|
||||||
u8 Read8(u32 addr);
|
u8 Read8(u32 addr);
|
||||||
u16 Read16(u32 addr);
|
u16 Read16(u32 addr);
|
||||||
|
|
|
@ -157,8 +157,6 @@ u32 NumCommands, CurCommand, ParamCount, TotalParams;
|
||||||
bool GeometryEnabled;
|
bool GeometryEnabled;
|
||||||
bool RenderingEnabled;
|
bool RenderingEnabled;
|
||||||
|
|
||||||
int Renderer;
|
|
||||||
|
|
||||||
u32 DispCnt;
|
u32 DispCnt;
|
||||||
u8 AlphaRefVal, AlphaRef;
|
u8 AlphaRefVal, AlphaRef;
|
||||||
|
|
||||||
|
@ -280,17 +278,11 @@ bool Init()
|
||||||
|
|
||||||
CmdStallQueue = new FIFO<CmdFIFOEntry>(64);
|
CmdStallQueue = new FIFO<CmdFIFOEntry>(64);
|
||||||
|
|
||||||
Renderer = -1;
|
|
||||||
// SetRenderer() will be called to set it up later
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeInit()
|
void DeInit()
|
||||||
{
|
{
|
||||||
if (Renderer == 0) SoftRenderer::DeInit();
|
|
||||||
else GLRenderer::DeInit();
|
|
||||||
|
|
||||||
delete CmdFIFO;
|
delete CmdFIFO;
|
||||||
delete CmdPIPE;
|
delete CmdPIPE;
|
||||||
|
|
||||||
|
@ -391,8 +383,6 @@ void Reset()
|
||||||
FlushAttributes = 0;
|
FlushAttributes = 0;
|
||||||
|
|
||||||
ResetRenderingState();
|
ResetRenderingState();
|
||||||
if (Renderer == 0) SoftRenderer::Reset();
|
|
||||||
else GLRenderer::Reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoSavestate(Savestate* file)
|
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)
|
void MatrixLoadIdentity(s32* m)
|
||||||
{
|
{
|
||||||
|
@ -2470,7 +2423,7 @@ void CheckFIFODMA()
|
||||||
|
|
||||||
void VCount144()
|
void VCount144()
|
||||||
{
|
{
|
||||||
if (Renderer == 0) SoftRenderer::VCount144();
|
if (GPU::Renderer == 0) SoftRenderer::VCount144();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2552,14 +2505,14 @@ void VBlank()
|
||||||
|
|
||||||
void VCount215()
|
void VCount215()
|
||||||
{
|
{
|
||||||
if (Renderer == 0) SoftRenderer::RenderFrame();
|
if (GPU::Renderer == 0) SoftRenderer::RenderFrame();
|
||||||
else GLRenderer::RenderFrame();
|
else GLRenderer::RenderFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32* GetLine(int line)
|
u32* GetLine(int line)
|
||||||
{
|
{
|
||||||
if (Renderer == 0) return SoftRenderer::GetLine(line);
|
if (GPU::Renderer == 0) return SoftRenderer::GetLine(line);
|
||||||
else return GLRenderer::GetLine(line);
|
else return GLRenderer::GetLine(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -102,10 +102,6 @@ void DoSavestate(Savestate* file);
|
||||||
|
|
||||||
void SetEnabled(bool geometry, bool rendering);
|
void SetEnabled(bool geometry, bool rendering);
|
||||||
|
|
||||||
int InitRenderer(bool hasGL);
|
|
||||||
void DeInitRenderer();
|
|
||||||
void UpdateRendererConfig();
|
|
||||||
|
|
||||||
void ExecuteCommand();
|
void ExecuteCommand();
|
||||||
|
|
||||||
s32 CyclesToRunFor();
|
s32 CyclesToRunFor();
|
||||||
|
@ -134,6 +130,7 @@ bool Init();
|
||||||
void DeInit();
|
void DeInit();
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
void SetRenderSettings(GPU::RenderSettings& settings);
|
||||||
void SetupRenderThread();
|
void SetupRenderThread();
|
||||||
|
|
||||||
void VCount144();
|
void VCount144();
|
||||||
|
@ -149,7 +146,7 @@ bool Init();
|
||||||
void DeInit();
|
void DeInit();
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
void UpdateDisplaySettings();
|
void SetRenderSettings(GPU::RenderSettings& settings);
|
||||||
|
|
||||||
void RenderFrame();
|
void RenderFrame();
|
||||||
void PrepareCaptureFrame();
|
void PrepareCaptureFrame();
|
||||||
|
|
|
@ -29,6 +29,8 @@ namespace GPU3D
|
||||||
namespace GLRenderer
|
namespace GLRenderer
|
||||||
{
|
{
|
||||||
|
|
||||||
|
using namespace OpenGL;
|
||||||
|
|
||||||
// GL version requirements
|
// GL version requirements
|
||||||
// * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS)
|
// * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS)
|
||||||
// * UBO: 3.1
|
// * UBO: 3.1
|
||||||
|
@ -142,7 +144,7 @@ bool BuildRenderShader(u32 flags, const char* vs, const char* fs)
|
||||||
strcpy(&fsbuf[headerlen], kRenderFSCommon);
|
strcpy(&fsbuf[headerlen], kRenderFSCommon);
|
||||||
strcpy(&fsbuf[headerlen + fsclen], fs);
|
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[] vsbuf;
|
||||||
delete[] fsbuf;
|
delete[] fsbuf;
|
||||||
|
@ -158,7 +160,7 @@ bool BuildRenderShader(u32 flags, const char* vs, const char* fs)
|
||||||
glBindFragDataLocation(prog, 0, "oColor");
|
glBindFragDataLocation(prog, 0, "oColor");
|
||||||
glBindFragDataLocation(prog, 1, "oAttr");
|
glBindFragDataLocation(prog, 1, "oAttr");
|
||||||
|
|
||||||
if (!OpenGL_LinkShaderProgram(RenderShader[flags]))
|
if (!OpenGL::LinkShaderProgram(RenderShader[flags]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
GLint uni_id = glGetUniformBlockIndex(prog, "uConfig");
|
GLint uni_id = glGetUniformBlockIndex(prog, "uConfig");
|
||||||
|
@ -197,19 +199,18 @@ bool Init()
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glEnable(GL_STENCIL_TEST);
|
glEnable(GL_STENCIL_TEST);
|
||||||
|
|
||||||
|
|
||||||
glDepthRange(0, 1);
|
glDepthRange(0, 1);
|
||||||
glClearDepth(1.0);
|
glClearDepth(1.0);
|
||||||
|
|
||||||
|
|
||||||
if (!OpenGL_BuildShaderProgram(kClearVS, kClearFS, ClearShaderPlain, "ClearShader"))
|
if (!OpenGL::BuildShaderProgram(kClearVS, kClearFS, ClearShaderPlain, "ClearShader"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
glBindAttribLocation(ClearShaderPlain[2], 0, "vPosition");
|
glBindAttribLocation(ClearShaderPlain[2], 0, "vPosition");
|
||||||
glBindFragDataLocation(ClearShaderPlain[2], 0, "oColor");
|
glBindFragDataLocation(ClearShaderPlain[2], 0, "oColor");
|
||||||
glBindFragDataLocation(ClearShaderPlain[2], 1, "oAttr");
|
glBindFragDataLocation(ClearShaderPlain[2], 1, "oAttr");
|
||||||
|
|
||||||
if (!OpenGL_LinkShaderProgram(ClearShaderPlain))
|
if (!OpenGL::LinkShaderProgram(ClearShaderPlain))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ClearUniformLoc[0] = glGetUniformLocation(ClearShaderPlain[2], "uColor");
|
ClearUniformLoc[0] = glGetUniformLocation(ClearShaderPlain[2], "uColor");
|
||||||
|
@ -237,15 +238,15 @@ bool Init()
|
||||||
kRenderVS_W, kRenderFS_WSM)) return false;
|
kRenderVS_W, kRenderFS_WSM)) return false;
|
||||||
|
|
||||||
|
|
||||||
if (!OpenGL_BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, FinalPassEdgeShader, "FinalPassEdgeShader"))
|
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, FinalPassEdgeShader, "FinalPassEdgeShader"))
|
||||||
return false;
|
return false;
|
||||||
if (!OpenGL_BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, FinalPassFogShader, "FinalPassFogShader"))
|
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, FinalPassFogShader, "FinalPassFogShader"))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
glBindAttribLocation(FinalPassEdgeShader[2], 0, "vPosition");
|
glBindAttribLocation(FinalPassEdgeShader[2], 0, "vPosition");
|
||||||
glBindFragDataLocation(FinalPassEdgeShader[2], 0, "oColor");
|
glBindFragDataLocation(FinalPassEdgeShader[2], 0, "oColor");
|
||||||
|
|
||||||
if (!OpenGL_LinkShaderProgram(FinalPassEdgeShader))
|
if (!OpenGL::LinkShaderProgram(FinalPassEdgeShader))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uni_id = glGetUniformBlockIndex(FinalPassEdgeShader[2], "uConfig");
|
uni_id = glGetUniformBlockIndex(FinalPassEdgeShader[2], "uConfig");
|
||||||
|
@ -261,7 +262,7 @@ bool Init()
|
||||||
glBindAttribLocation(FinalPassFogShader[2], 0, "vPosition");
|
glBindAttribLocation(FinalPassFogShader[2], 0, "vPosition");
|
||||||
glBindFragDataLocation(FinalPassFogShader[2], 0, "oColor");
|
glBindFragDataLocation(FinalPassFogShader[2], 0, "oColor");
|
||||||
|
|
||||||
if (!OpenGL_LinkShaderProgram(FinalPassFogShader))
|
if (!OpenGL::LinkShaderProgram(FinalPassFogShader))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uni_id = glGetUniformBlockIndex(FinalPassFogShader[2], "uConfig");
|
uni_id = glGetUniformBlockIndex(FinalPassFogShader[2], "uConfig");
|
||||||
|
@ -392,7 +393,7 @@ void DeInit()
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
{
|
{
|
||||||
if (!RenderShader[i][2]) continue;
|
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;
|
int scale = settings.GL_ScaleFactor;
|
||||||
bool antialias = false; //Config::GL_Antialias;
|
bool antialias = false; // REMOVE ME!
|
||||||
|
|
||||||
if (antialias) scale *= 2;
|
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);
|
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;
|
FrontBuffer = FrontBuffer ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@ bool Enabled;
|
||||||
|
|
||||||
// threading
|
// threading
|
||||||
|
|
||||||
|
bool Threaded;
|
||||||
void* RenderThread;
|
void* RenderThread;
|
||||||
bool RenderThreadRunning;
|
bool RenderThreadRunning;
|
||||||
bool RenderThreadRendering;
|
bool RenderThreadRendering;
|
||||||
|
@ -83,7 +84,7 @@ void StopRenderThread()
|
||||||
|
|
||||||
void SetupRenderThread()
|
void SetupRenderThread()
|
||||||
{
|
{
|
||||||
if (Config::Threaded3D)
|
if (Threaded)
|
||||||
{
|
{
|
||||||
if (!RenderThreadRunning)
|
if (!RenderThreadRunning)
|
||||||
{
|
{
|
||||||
|
@ -112,6 +113,7 @@ bool Init()
|
||||||
Sema_RenderDone = Platform::Semaphore_Create();
|
Sema_RenderDone = Platform::Semaphore_Create();
|
||||||
Sema_ScanlineCount = Platform::Semaphore_Create();
|
Sema_ScanlineCount = Platform::Semaphore_Create();
|
||||||
|
|
||||||
|
Threaded = false;
|
||||||
RenderThreadRunning = false;
|
RenderThreadRunning = false;
|
||||||
RenderThreadRendering = false;
|
RenderThreadRendering = false;
|
||||||
|
|
||||||
|
@ -138,6 +140,12 @@ void Reset()
|
||||||
SetupRenderThread();
|
SetupRenderThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetRenderSettings(GPU::RenderSettings& settings)
|
||||||
|
{
|
||||||
|
Threaded = settings.Soft_Threaded;
|
||||||
|
SetupRenderThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Notes on the interpolator:
|
// Notes on the interpolator:
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
232
src/NDS.cpp
232
src/NDS.cpp
|
@ -33,6 +33,9 @@
|
||||||
#include "AREngine.h"
|
#include "AREngine.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
|
|
||||||
|
#include "DSi.h"
|
||||||
|
#include "DSi_SPI_TSC.h"
|
||||||
|
|
||||||
|
|
||||||
namespace NDS
|
namespace NDS
|
||||||
{
|
{
|
||||||
|
@ -59,6 +62,8 @@ namespace NDS
|
||||||
//
|
//
|
||||||
// timings for GBA slot and wifi are set up at runtime
|
// timings for GBA slot and wifi are set up at runtime
|
||||||
|
|
||||||
|
int ConsoleType;
|
||||||
|
|
||||||
u8 ARM9MemTimings[0x40000][4];
|
u8 ARM9MemTimings[0x40000][4];
|
||||||
u8 ARM7MemTimings[0x20000][4];
|
u8 ARM7MemTimings[0x20000][4];
|
||||||
|
|
||||||
|
@ -88,7 +93,8 @@ u32 CPUStop;
|
||||||
u8 ARM9BIOS[0x1000];
|
u8 ARM9BIOS[0x1000];
|
||||||
u8 ARM7BIOS[0x4000];
|
u8 ARM7BIOS[0x4000];
|
||||||
|
|
||||||
u8 MainRAM[MAIN_RAM_SIZE];
|
u8 MainRAM[0x1000000];
|
||||||
|
u32 MainRAMMask;
|
||||||
|
|
||||||
u8 SharedWRAM[0x8000];
|
u8 SharedWRAM[0x8000];
|
||||||
u8 WRAMCnt;
|
u8 WRAMCnt;
|
||||||
|
@ -108,6 +114,7 @@ u8 ROMSeed1[2*8];
|
||||||
// IO shit
|
// IO shit
|
||||||
u32 IME[2];
|
u32 IME[2];
|
||||||
u32 IE[2], IF[2];
|
u32 IE[2], IF[2];
|
||||||
|
u32 IE2, IF2;
|
||||||
|
|
||||||
u8 PostFlag9;
|
u8 PostFlag9;
|
||||||
u8 PostFlag7;
|
u8 PostFlag7;
|
||||||
|
@ -181,6 +188,8 @@ bool Init()
|
||||||
if (!RTC::Init()) return false;
|
if (!RTC::Init()) return false;
|
||||||
if (!Wifi::Init()) return false;
|
if (!Wifi::Init()) return false;
|
||||||
|
|
||||||
|
if (!DSi::Init()) return false;
|
||||||
|
|
||||||
if (!AREngine::Init()) return false;
|
if (!AREngine::Init()) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -205,6 +214,8 @@ void DeInit()
|
||||||
RTC::DeInit();
|
RTC::DeInit();
|
||||||
Wifi::DeInit();
|
Wifi::DeInit();
|
||||||
|
|
||||||
|
DSi::DeInit();
|
||||||
|
|
||||||
AREngine::DeInit();
|
AREngine::DeInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +249,11 @@ void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq,
|
||||||
ARM9MemTimings[i][3] = S32;
|
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)
|
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!
|
// TODO: +3c nonseq waitstate doesn't apply to DMA!
|
||||||
// but of course mainRAM always gets 8c nonseq waitstate
|
// but of course mainRAM always gets 8c nonseq waitstate
|
||||||
|
|
||||||
|
// TODO: DSi-specific timings!!
|
||||||
|
|
||||||
SetARM9RegionTimings(0x00000000, 0xFFFFFFFF, 32, 1 + 3, 1); // void
|
SetARM9RegionTimings(0x00000000, 0xFFFFFFFF, 32, 1 + 3, 1); // void
|
||||||
|
|
||||||
SetARM9RegionTimings(0xFFFF0000, 0xFFFFFFFF, 32, 1 + 3, 1); // BIOS
|
SetARM9RegionTimings(0xFFFF0000, 0xFFFFFFFF, 32, 1 + 3, 1); // BIOS
|
||||||
|
@ -308,6 +325,12 @@ void InitTimings()
|
||||||
|
|
||||||
void SetupDirectBoot()
|
void SetupDirectBoot()
|
||||||
{
|
{
|
||||||
|
if (ConsoleType == 1)
|
||||||
|
{
|
||||||
|
printf("!! DIRECT BOOT NOT SUPPORTED IN DSI MODE\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
u32 bootparams[8];
|
u32 bootparams[8];
|
||||||
memcpy(bootparams, &NDSCart::CartROM[0x20], 8*4);
|
memcpy(bootparams, &NDSCart::CartROM[0x20], 8*4);
|
||||||
|
|
||||||
|
@ -416,7 +439,13 @@ void Reset()
|
||||||
RunningGame = false;
|
RunningGame = false;
|
||||||
LastSysClockCycles = 0;
|
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)
|
if (!f)
|
||||||
{
|
{
|
||||||
printf("ARM9 BIOS not found\n");
|
printf("ARM9 BIOS not found\n");
|
||||||
|
@ -433,7 +462,7 @@ void Reset()
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
f = Platform::OpenLocalFile("bios7.bin", "rb");
|
f = Platform::OpenLocalFile(Config::BIOS7Path, "rb");
|
||||||
if (!f)
|
if (!f)
|
||||||
{
|
{
|
||||||
printf("ARM7 BIOS not found\n");
|
printf("ARM7 BIOS not found\n");
|
||||||
|
@ -450,8 +479,19 @@ void Reset()
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO for later: configure this when emulating a DSi
|
if (ConsoleType == 1)
|
||||||
ARM9ClockShift = 1;
|
{
|
||||||
|
DSi::LoadBIOS();
|
||||||
|
DSi::LoadNAND();
|
||||||
|
|
||||||
|
ARM9ClockShift = 2;
|
||||||
|
MainRAMMask = 0xFFFFFF;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ARM9ClockShift = 1;
|
||||||
|
MainRAMMask = 0x3FFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
ARM9Timestamp = 0; ARM9Target = 0;
|
ARM9Timestamp = 0; ARM9Target = 0;
|
||||||
ARM7Timestamp = 0; ARM7Target = 0;
|
ARM7Timestamp = 0; ARM7Target = 0;
|
||||||
|
@ -459,14 +499,14 @@ void Reset()
|
||||||
|
|
||||||
InitTimings();
|
InitTimings();
|
||||||
|
|
||||||
memset(MainRAM, 0, MAIN_RAM_SIZE);
|
memset(MainRAM, 0, 0x1000000);
|
||||||
memset(SharedWRAM, 0, 0x8000);
|
memset(SharedWRAM, 0, 0x8000);
|
||||||
memset(ARM7WRAM, 0, 0x10000);
|
memset(ARM7WRAM, 0, 0x10000);
|
||||||
|
|
||||||
MapSharedWRAM(0);
|
MapSharedWRAM(0);
|
||||||
|
|
||||||
ExMemCnt[0] = 0;
|
ExMemCnt[0] = 0x4000;
|
||||||
ExMemCnt[1] = 0;
|
ExMemCnt[1] = 0x4000;
|
||||||
memset(ROMSeed0, 0, 2*8);
|
memset(ROMSeed0, 0, 2*8);
|
||||||
memset(ROMSeed1, 0, 2*8);
|
memset(ROMSeed1, 0, 2*8);
|
||||||
SetGBASlotTimings();
|
SetGBASlotTimings();
|
||||||
|
@ -477,6 +517,8 @@ void Reset()
|
||||||
IME[1] = 0;
|
IME[1] = 0;
|
||||||
IE[1] = 0;
|
IE[1] = 0;
|
||||||
IF[1] = 0;
|
IF[1] = 0;
|
||||||
|
IE2 = 0;
|
||||||
|
IF2 = 0;
|
||||||
|
|
||||||
PostFlag9 = 0x00;
|
PostFlag9 = 0x00;
|
||||||
PostFlag7 = 0x00;
|
PostFlag7 = 0x00;
|
||||||
|
@ -527,6 +569,12 @@ void Reset()
|
||||||
RTC::Reset();
|
RTC::Reset();
|
||||||
Wifi::Reset();
|
Wifi::Reset();
|
||||||
|
|
||||||
|
if (ConsoleType == 1)
|
||||||
|
{
|
||||||
|
DSi::Reset();
|
||||||
|
KeyInput &= ~(1 << (16+6));
|
||||||
|
}
|
||||||
|
|
||||||
AREngine::Reset();
|
AREngine::Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,7 +635,7 @@ bool DoSavestate_Scheduler(Savestate* file)
|
||||||
}
|
}
|
||||||
if (funcid == -1)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -635,6 +683,11 @@ bool DoSavestate(Savestate* file)
|
||||||
{
|
{
|
||||||
file->Section("NDSG");
|
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(MainRAM, 0x400000);
|
||||||
file->VarArray(SharedWRAM, 0x8000);
|
file->VarArray(SharedWRAM, 0x8000);
|
||||||
file->VarArray(ARM7WRAM, 0x10000);
|
file->VarArray(ARM7WRAM, 0x10000);
|
||||||
|
@ -737,6 +790,11 @@ bool DoSavestate(Savestate* file)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetConsoleType(int type)
|
||||||
|
{
|
||||||
|
ConsoleType = type;
|
||||||
|
}
|
||||||
|
|
||||||
bool LoadROM(const char* path, const char* sram, bool direct)
|
bool LoadROM(const char* path, const char* sram, bool direct)
|
||||||
{
|
{
|
||||||
if (NDSCart::LoadROM(path, sram, direct))
|
if (NDSCart::LoadROM(path, sram, direct))
|
||||||
|
@ -848,6 +906,7 @@ u32 RunFrame()
|
||||||
if (!(CPUStop & 0x80000000)) DMAs[1]->Run();
|
if (!(CPUStop & 0x80000000)) DMAs[1]->Run();
|
||||||
if (!(CPUStop & 0x80000000)) DMAs[2]->Run();
|
if (!(CPUStop & 0x80000000)) DMAs[2]->Run();
|
||||||
if (!(CPUStop & 0x80000000)) DMAs[3]->Run();
|
if (!(CPUStop & 0x80000000)) DMAs[3]->Run();
|
||||||
|
if (ConsoleType == 1) DSi::RunNDMAs(0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -870,6 +929,7 @@ u32 RunFrame()
|
||||||
DMAs[5]->Run();
|
DMAs[5]->Run();
|
||||||
DMAs[6]->Run();
|
DMAs[6]->Run();
|
||||||
DMAs[7]->Run();
|
DMAs[7]->Run();
|
||||||
|
if (ConsoleType == 1) DSi::RunNDMAs(1);
|
||||||
}
|
}
|
||||||
else
|
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)
|
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()
|
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);
|
KeyInput |= key_lo | (key_hi << 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsLidClosed()
|
||||||
|
{
|
||||||
|
if (KeyInput & (1<<23)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void SetLidClosed(bool closed)
|
void SetLidClosed(bool closed)
|
||||||
{
|
{
|
||||||
if (closed)
|
if (closed)
|
||||||
|
@ -1095,6 +1167,8 @@ void UpdateIRQ(u32 cpu)
|
||||||
if (IME[cpu] & 0x1)
|
if (IME[cpu] & 0x1)
|
||||||
{
|
{
|
||||||
arm->IRQ = IE[cpu] & IF[cpu];
|
arm->IRQ = IE[cpu] & IF[cpu];
|
||||||
|
if ((ConsoleType == 1) && cpu)
|
||||||
|
arm->IRQ |= (IE2 & IF2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1114,6 +1188,18 @@ void ClearIRQ(u32 cpu, u32 irq)
|
||||||
UpdateIRQ(cpu);
|
UpdateIRQ(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetIRQ2(u32 irq)
|
||||||
|
{
|
||||||
|
IF2 |= (1 << irq);
|
||||||
|
UpdateIRQ(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearIRQ2(u32 irq)
|
||||||
|
{
|
||||||
|
IF2 &= ~(1 << irq);
|
||||||
|
UpdateIRQ(1);
|
||||||
|
}
|
||||||
|
|
||||||
bool HaltInterrupted(u32 cpu)
|
bool HaltInterrupted(u32 cpu)
|
||||||
{
|
{
|
||||||
if (cpu == 0)
|
if (cpu == 0)
|
||||||
|
@ -1161,6 +1247,7 @@ void GXFIFOStall()
|
||||||
DMAs[1]->StallIfRunning();
|
DMAs[1]->StallIfRunning();
|
||||||
DMAs[2]->StallIfRunning();
|
DMAs[2]->StallIfRunning();
|
||||||
DMAs[3]->StallIfRunning();
|
DMAs[3]->StallIfRunning();
|
||||||
|
if (ConsoleType == 1) DSi::StallNDMAs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1298,6 +1385,7 @@ void NocashPrint(u32 ncpu, u32 addr)
|
||||||
void MonitorARM9Jump(u32 addr)
|
void MonitorARM9Jump(u32 addr)
|
||||||
{
|
{
|
||||||
// checkme: can the entrypoint addr be THUMB?
|
// checkme: can the entrypoint addr be THUMB?
|
||||||
|
// also TODO: make it work in DSi mode
|
||||||
|
|
||||||
if ((!RunningGame) && NDSCart::CartROM)
|
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)
|
bool DMAsInMode(u32 cpu, u32 mode)
|
||||||
{
|
{
|
||||||
cpu <<= 2;
|
cpu <<= 2;
|
||||||
|
@ -1381,6 +1492,13 @@ bool DMAsInMode(u32 cpu, u32 mode)
|
||||||
if (DMAs[cpu+1]->IsInMode(mode)) return true;
|
if (DMAs[cpu+1]->IsInMode(mode)) return true;
|
||||||
if (DMAs[cpu+2]->IsInMode(mode)) return true;
|
if (DMAs[cpu+2]->IsInMode(mode)) return true;
|
||||||
if (DMAs[cpu+3]->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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1391,6 +1509,10 @@ bool DMAsRunning(u32 cpu)
|
||||||
if (DMAs[cpu+1]->IsRunning()) return true;
|
if (DMAs[cpu+1]->IsRunning()) return true;
|
||||||
if (DMAs[cpu+2]->IsRunning()) return true;
|
if (DMAs[cpu+2]->IsRunning()) return true;
|
||||||
if (DMAs[cpu+3]->IsRunning()) return true;
|
if (DMAs[cpu+3]->IsRunning()) return true;
|
||||||
|
if (ConsoleType == 1)
|
||||||
|
{
|
||||||
|
if (DSi::NDMAsRunning(cpu>>2)) return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1401,6 +1523,12 @@ void CheckDMAs(u32 cpu, u32 mode)
|
||||||
DMAs[cpu+1]->StartIfNeeded(mode);
|
DMAs[cpu+1]->StartIfNeeded(mode);
|
||||||
DMAs[cpu+2]->StartIfNeeded(mode);
|
DMAs[cpu+2]->StartIfNeeded(mode);
|
||||||
DMAs[cpu+3]->StartIfNeeded(mode);
|
DMAs[cpu+3]->StartIfNeeded(mode);
|
||||||
|
|
||||||
|
if (ConsoleType == 1)
|
||||||
|
{
|
||||||
|
cpu >>= 2;
|
||||||
|
DSi::CheckNDMAs(cpu, NDMAModes[mode]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopDMAs(u32 cpu, u32 mode)
|
void StopDMAs(u32 cpu, u32 mode)
|
||||||
|
@ -1410,6 +1538,12 @@ void StopDMAs(u32 cpu, u32 mode)
|
||||||
DMAs[cpu+1]->StopIfNeeded(mode);
|
DMAs[cpu+1]->StopIfNeeded(mode);
|
||||||
DMAs[cpu+2]->StopIfNeeded(mode);
|
DMAs[cpu+2]->StopIfNeeded(mode);
|
||||||
DMAs[cpu+3]->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("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("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++)
|
//for (int i = 0; i < 9; i++)
|
||||||
// printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]);
|
// printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]);
|
||||||
|
|
||||||
FILE*
|
/*FILE*
|
||||||
shit = fopen("debug/party.bin", "wb");
|
shit = fopen("debug/party.bin", "wb");
|
||||||
fwrite(ARM9->ITCM, 0x8000, 1, shit);
|
fwrite(ARM9->ITCM, 0x8000, 1, shit);
|
||||||
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
|
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
|
||||||
|
@ -1620,6 +1754,21 @@ void debug(u32 param)
|
||||||
u32 val = ARM7Read32(i);
|
u32 val = ARM7Read32(i);
|
||||||
fwrite(&val, 4, 1, shit);
|
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);
|
fclose(shit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1635,7 +1784,7 @@ u8 ARM9Read8(u32 addr)
|
||||||
switch (addr & 0xFF000000)
|
switch (addr & 0xFF000000)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
return *(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
|
return *(u8*)&MainRAM[addr & MainRAMMask];
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
if (SWRAM_ARM9)
|
if (SWRAM_ARM9)
|
||||||
|
@ -1700,7 +1849,7 @@ u16 ARM9Read16(u32 addr)
|
||||||
switch (addr & 0xFF000000)
|
switch (addr & 0xFF000000)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
return *(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
|
return *(u16*)&MainRAM[addr & MainRAMMask];
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
if (SWRAM_ARM9)
|
if (SWRAM_ARM9)
|
||||||
|
@ -1765,7 +1914,7 @@ u32 ARM9Read32(u32 addr)
|
||||||
switch (addr & 0xFF000000)
|
switch (addr & 0xFF000000)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
return *(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
|
return *(u32*)&MainRAM[addr & MainRAMMask];
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
if (SWRAM_ARM9)
|
if (SWRAM_ARM9)
|
||||||
|
@ -1825,7 +1974,7 @@ void ARM9Write8(u32 addr, u8 val)
|
||||||
switch (addr & 0xFF000000)
|
switch (addr & 0xFF000000)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
*(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
|
*(u8*)&MainRAM[addr & MainRAMMask] = val;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
|
@ -1875,7 +2024,7 @@ void ARM9Write16(u32 addr, u16 val)
|
||||||
switch (addr & 0xFF000000)
|
switch (addr & 0xFF000000)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
*(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
|
*(u16*)&MainRAM[addr & MainRAMMask] = val;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
|
@ -1941,7 +2090,7 @@ void ARM9Write32(u32 addr, u32 val)
|
||||||
switch (addr & 0xFF000000)
|
switch (addr & 0xFF000000)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
*(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
|
*(u32*)&MainRAM[addr & MainRAMMask] = val;
|
||||||
return ;
|
return ;
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
|
@ -2009,7 +2158,7 @@ bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
region->Mem = MainRAM;
|
region->Mem = MainRAM;
|
||||||
region->Mask = MAIN_RAM_SIZE-1;
|
region->Mask = MainRAMMask;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
|
@ -2039,7 +2188,8 @@ u8 ARM7Read8(u32 addr)
|
||||||
{
|
{
|
||||||
if (addr < 0x00004000)
|
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;
|
return 0xFF;
|
||||||
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
|
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
|
@ -2051,7 +2201,7 @@ u8 ARM7Read8(u32 addr)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
case 0x02800000:
|
case 0x02800000:
|
||||||
return *(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
|
return *(u8*)&MainRAM[addr & MainRAMMask];
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
if (SWRAM_ARM7)
|
if (SWRAM_ARM7)
|
||||||
|
@ -2099,7 +2249,7 @@ u16 ARM7Read16(u32 addr)
|
||||||
{
|
{
|
||||||
if (addr < 0x00004000)
|
if (addr < 0x00004000)
|
||||||
{
|
{
|
||||||
if (ARM7->R[15] >= 0x4000)
|
if (ARM7->R[15] >= 0x00004000)
|
||||||
return 0xFFFF;
|
return 0xFFFF;
|
||||||
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
|
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
|
||||||
return 0xFFFF;
|
return 0xFFFF;
|
||||||
|
@ -2111,7 +2261,7 @@ u16 ARM7Read16(u32 addr)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
case 0x02800000:
|
case 0x02800000:
|
||||||
return *(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
|
return *(u16*)&MainRAM[addr & MainRAMMask];
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
if (SWRAM_ARM7)
|
if (SWRAM_ARM7)
|
||||||
|
@ -2166,7 +2316,7 @@ u32 ARM7Read32(u32 addr)
|
||||||
{
|
{
|
||||||
if (addr < 0x00004000)
|
if (addr < 0x00004000)
|
||||||
{
|
{
|
||||||
if (ARM7->R[15] >= 0x4000)
|
if (ARM7->R[15] >= 0x00004000)
|
||||||
return 0xFFFFFFFF;
|
return 0xFFFFFFFF;
|
||||||
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
|
if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt)
|
||||||
return 0xFFFFFFFF;
|
return 0xFFFFFFFF;
|
||||||
|
@ -2178,7 +2328,7 @@ u32 ARM7Read32(u32 addr)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
case 0x02800000:
|
case 0x02800000:
|
||||||
return *(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)];
|
return *(u32*)&MainRAM[addr & MainRAMMask];
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
if (SWRAM_ARM7)
|
if (SWRAM_ARM7)
|
||||||
|
@ -2235,7 +2385,7 @@ void ARM7Write8(u32 addr, u8 val)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
case 0x02800000:
|
case 0x02800000:
|
||||||
*(u8*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
|
*(u8*)&MainRAM[addr & MainRAMMask] = val;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
|
@ -2294,7 +2444,7 @@ void ARM7Write16(u32 addr, u16 val)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
case 0x02800000:
|
case 0x02800000:
|
||||||
*(u16*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
|
*(u16*)&MainRAM[addr & MainRAMMask] = val;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
|
@ -2363,7 +2513,7 @@ void ARM7Write32(u32 addr, u32 val)
|
||||||
{
|
{
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
case 0x02800000:
|
case 0x02800000:
|
||||||
*(u32*)&MainRAM[addr & (MAIN_RAM_SIZE - 1)] = val;
|
*(u32*)&MainRAM[addr & MainRAMMask] = val;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
|
@ -2435,7 +2585,7 @@ bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region)
|
||||||
case 0x02000000:
|
case 0x02000000:
|
||||||
case 0x02800000:
|
case 0x02800000:
|
||||||
region->Mem = MainRAM;
|
region->Mem = MainRAM;
|
||||||
region->Mask = MAIN_RAM_SIZE-1;
|
region->Mask = MainRAMMask;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case 0x03000000:
|
case 0x03000000:
|
||||||
|
|
63
src/NDS.h
63
src/NDS.h
|
@ -42,6 +42,11 @@ enum
|
||||||
Event_Div,
|
Event_Div,
|
||||||
Event_Sqrt,
|
Event_Sqrt,
|
||||||
|
|
||||||
|
// DSi
|
||||||
|
Event_DSi_SDMMCTransfer,
|
||||||
|
Event_DSi_SDIOTransfer,
|
||||||
|
Event_DSi_NWifi,
|
||||||
|
|
||||||
Event_MAX
|
Event_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,12 +79,42 @@ enum
|
||||||
IRQ_IPCSync,
|
IRQ_IPCSync,
|
||||||
IRQ_IPCSendDone,
|
IRQ_IPCSendDone,
|
||||||
IRQ_IPCRecv,
|
IRQ_IPCRecv,
|
||||||
IRQ_CartSendDone,
|
IRQ_CartSendDone, // TODO: less misleading name
|
||||||
IRQ_CartIREQMC,
|
IRQ_CartIREQMC, // IRQ triggered by game cart (example: Pokémon Typing Adventure, BT controller)
|
||||||
IRQ_GXFIFO,
|
IRQ_GXFIFO,
|
||||||
IRQ_LidOpen,
|
IRQ_LidOpen,
|
||||||
IRQ_SPI,
|
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
|
typedef struct
|
||||||
|
@ -98,6 +133,8 @@ typedef struct
|
||||||
|
|
||||||
} MemRegion;
|
} MemRegion;
|
||||||
|
|
||||||
|
extern int ConsoleType;
|
||||||
|
|
||||||
extern u8 ARM9MemTimings[0x40000][4];
|
extern u8 ARM9MemTimings[0x40000][4];
|
||||||
extern u8 ARM7MemTimings[0x20000][4];
|
extern u8 ARM7MemTimings[0x20000][4];
|
||||||
|
|
||||||
|
@ -105,12 +142,15 @@ extern u64 ARM9Timestamp, ARM9Target;
|
||||||
extern u64 ARM7Timestamp, ARM7Target;
|
extern u64 ARM7Timestamp, ARM7Target;
|
||||||
extern u32 ARM9ClockShift;
|
extern u32 ARM9ClockShift;
|
||||||
|
|
||||||
// hax
|
|
||||||
extern u32 IME[2];
|
extern u32 IME[2];
|
||||||
extern u32 IE[2];
|
extern u32 IE[2];
|
||||||
extern u32 IF[2];
|
extern u32 IF[2];
|
||||||
|
extern u32 IE2;
|
||||||
|
extern u32 IF2;
|
||||||
extern Timer Timers[8];
|
extern Timer Timers[8];
|
||||||
|
|
||||||
|
extern u32 CPUStop;
|
||||||
|
|
||||||
extern u16 PowerControl9;
|
extern u16 PowerControl9;
|
||||||
|
|
||||||
extern u16 ExMemCnt[2];
|
extern u16 ExMemCnt[2];
|
||||||
|
@ -119,10 +159,12 @@ extern u8 ROMSeed1[2*8];
|
||||||
|
|
||||||
extern u8 ARM9BIOS[0x1000];
|
extern u8 ARM9BIOS[0x1000];
|
||||||
extern u8 ARM7BIOS[0x4000];
|
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();
|
bool Init();
|
||||||
void DeInit();
|
void DeInit();
|
||||||
|
@ -134,6 +176,9 @@ bool DoSavestate(Savestate* file);
|
||||||
void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
|
void SetARM9RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, int seq);
|
||||||
void SetARM7RegionTimings(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 LoadROM(const char* path, const char* sram, bool direct);
|
||||||
bool LoadGBAROM(const char* path, const char* sram);
|
bool LoadGBAROM(const char* path, const char* sram);
|
||||||
void LoadBIOS();
|
void LoadBIOS();
|
||||||
|
@ -142,13 +187,12 @@ void RelocateSave(const char* path, bool write);
|
||||||
|
|
||||||
u32 RunFrame();
|
u32 RunFrame();
|
||||||
|
|
||||||
void PressKey(u32 key);
|
|
||||||
void ReleaseKey(u32 key);
|
|
||||||
void TouchScreen(u16 x, u16 y);
|
void TouchScreen(u16 x, u16 y);
|
||||||
void ReleaseScreen();
|
void ReleaseScreen();
|
||||||
|
|
||||||
void SetKeyMask(u32 mask);
|
void SetKeyMask(u32 mask);
|
||||||
|
|
||||||
|
bool IsLidClosed();
|
||||||
void SetLidClosed(bool closed);
|
void SetLidClosed(bool closed);
|
||||||
|
|
||||||
void MicInputFrame(s16* data, int samples);
|
void MicInputFrame(s16* data, int samples);
|
||||||
|
@ -162,8 +206,11 @@ void Halt();
|
||||||
|
|
||||||
void MapSharedWRAM(u8 val);
|
void MapSharedWRAM(u8 val);
|
||||||
|
|
||||||
|
void UpdateIRQ(u32 cpu);
|
||||||
void SetIRQ(u32 cpu, u32 irq);
|
void SetIRQ(u32 cpu, u32 irq);
|
||||||
void ClearIRQ(u32 cpu, u32 irq);
|
void ClearIRQ(u32 cpu, u32 irq);
|
||||||
|
void SetIRQ2(u32 irq);
|
||||||
|
void ClearIRQ2(u32 irq);
|
||||||
bool HaltInterrupted(u32 cpu);
|
bool HaltInterrupted(u32 cpu);
|
||||||
void StopCPU(u32 cpu, u32 mask);
|
void StopCPU(u32 cpu, u32 mask);
|
||||||
void ResumeCPU(u32 cpu, u32 mask);
|
void ResumeCPU(u32 cpu, u32 mask);
|
||||||
|
|
160
src/NDSCart.cpp
160
src/NDSCart.cpp
|
@ -19,10 +19,13 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
|
#include "DSi.h"
|
||||||
#include "NDSCart.h"
|
#include "NDSCart.h"
|
||||||
#include "ARM.h"
|
#include "ARM.h"
|
||||||
#include "CRC32.h"
|
#include "CRC32.h"
|
||||||
|
#include "DSi_AES.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
|
#include "ROMList.h"
|
||||||
|
|
||||||
|
|
||||||
namespace NDSCart_SRAM
|
namespace NDSCart_SRAM
|
||||||
|
@ -473,6 +476,7 @@ u32 CartROMSize;
|
||||||
u32 CartCRC;
|
u32 CartCRC;
|
||||||
u32 CartID;
|
u32 CartID;
|
||||||
bool CartIsHomebrew;
|
bool CartIsHomebrew;
|
||||||
|
bool CartIsDSi;
|
||||||
|
|
||||||
u32 CmdEncMode;
|
u32 CmdEncMode;
|
||||||
u32 DataEncMode;
|
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};
|
u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
|
||||||
if (level >= 1) Key1_ApplyKeycode(keycode, mod);
|
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()
|
bool Init()
|
||||||
{
|
{
|
||||||
if (!NDSCart_SRAM::Init()) return false;
|
if (!NDSCart_SRAM::Init()) return false;
|
||||||
|
@ -610,32 +627,19 @@ void DeInit()
|
||||||
|
|
||||||
void Reset()
|
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;
|
CartInserted = false;
|
||||||
if (CartROM) delete[] CartROM;
|
if (CartROM) delete[] CartROM;
|
||||||
CartROM = NULL;
|
CartROM = NULL;
|
||||||
CartROMSize = 0;
|
CartROMSize = 0;
|
||||||
CartID = 0;
|
CartID = 0;
|
||||||
CartIsHomebrew = false;
|
CartIsHomebrew = false;
|
||||||
|
CartIsDSi = false;
|
||||||
|
|
||||||
ROMCommandHandler = NULL;
|
ROMCommandHandler = NULL;
|
||||||
|
|
||||||
CmdEncMode = 0;
|
|
||||||
DataEncMode = 0;
|
|
||||||
|
|
||||||
NDSCart_SRAM::Reset();
|
NDSCart_SRAM::Reset();
|
||||||
|
|
||||||
|
ResetCart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoSavestate(Savestate* file)
|
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:
|
u32 len = sizeof(ROMList) / sizeof(ROMListEntry);
|
||||||
// [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 offset = 0;
|
u32 offset = 0;
|
||||||
u32 chk_size = len >> 1;
|
u32 chk_size = len >> 1;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
u32 key = 0;
|
u32 key = 0;
|
||||||
fseek(f, offset + (chk_size << 4), SEEK_SET);
|
ROMListEntry* curentry = &ROMList[offset + chk_size];
|
||||||
fread(&key, 4, 1, f);
|
key = curentry->GameCode;
|
||||||
|
|
||||||
printf("chk_size=%d, key=%08X, wanted=%08X, offset=%08X\n", chk_size, key, gamecode, offset);
|
|
||||||
|
|
||||||
if (key == gamecode)
|
if (key == gamecode)
|
||||||
{
|
{
|
||||||
fread(params, 4, 3, f);
|
memcpy(params, curentry, sizeof(ROMListEntry));
|
||||||
fclose(f);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -843,22 +834,20 @@ bool ReadROMParams(u32 gamecode, u32* params)
|
||||||
if (key < gamecode)
|
if (key < gamecode)
|
||||||
{
|
{
|
||||||
if (chk_size == 0)
|
if (chk_size == 0)
|
||||||
offset += 0x10;
|
offset++;
|
||||||
else
|
else
|
||||||
offset += (chk_size << 4);
|
offset += chk_size;
|
||||||
}
|
}
|
||||||
else if (chk_size == 0)
|
else if (chk_size == 0)
|
||||||
{
|
{
|
||||||
fclose(f);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
chk_size >>= 1;
|
chk_size >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset >= maxlen)
|
if (offset >= len)
|
||||||
{
|
{
|
||||||
fclose(f);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -867,15 +856,20 @@ bool ReadROMParams(u32 gamecode, u32* params)
|
||||||
|
|
||||||
void DecryptSecureArea(u8* out)
|
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 gamecode = *(u32*)&CartROM[0x0C];
|
||||||
u32 arm9base = *(u32*)&CartROM[0x20];
|
u32 arm9base = *(u32*)&CartROM[0x20];
|
||||||
|
|
||||||
memcpy(out, &CartROM[arm9base], 0x800);
|
memcpy(out, &CartROM[arm9base], 0x800);
|
||||||
|
|
||||||
Key1_InitKeycode(gamecode, 2, 2);
|
Key1_InitKeycode(false, gamecode, 2, 2);
|
||||||
Key1_Decrypt((u32*)&out[0]);
|
Key1_Decrypt((u32*)&out[0]);
|
||||||
|
|
||||||
Key1_InitKeycode(gamecode, 3, 2);
|
Key1_InitKeycode(false, gamecode, 3, 2);
|
||||||
for (u32 i = 0; i < 0x800; i += 8)
|
for (u32 i = 0; i < 0x800; i += 8)
|
||||||
Key1_Decrypt((u32*)&out[i]);
|
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
|
// TODO: streaming mode? for really big ROMs or systems with limited RAM
|
||||||
// for now we're lazy
|
// for now we're lazy
|
||||||
|
// also TODO: validate what we're loading!!
|
||||||
|
|
||||||
FILE* f = Platform::OpenFile(path, "rb");
|
FILE* f = Platform::OpenFile(path, "rb");
|
||||||
if (!f)
|
if (!f)
|
||||||
|
@ -919,6 +914,11 @@ bool LoadROM(const char* path, const char* sram, bool direct)
|
||||||
fread(&gamecode, 4, 1, f);
|
fread(&gamecode, 4, 1, f);
|
||||||
printf("Game code: %c%c%c%c\n", gamecode&0xFF, (gamecode>>8)&0xFF, (gamecode>>16)&0xFF, gamecode>>24);
|
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];
|
CartROM = new u8[CartROMSize];
|
||||||
memset(CartROM, 0, CartROMSize);
|
memset(CartROM, 0, CartROMSize);
|
||||||
fseek(f, 0, SEEK_SET);
|
fseek(f, 0, SEEK_SET);
|
||||||
|
@ -930,22 +930,23 @@ bool LoadROM(const char* path, const char* sram, bool direct)
|
||||||
CartCRC = CRC32(CartROM, CartROMSize);
|
CartCRC = CRC32(CartROM, CartROMSize);
|
||||||
printf("ROM CRC32: %08X\n", CartCRC);
|
printf("ROM CRC32: %08X\n", CartCRC);
|
||||||
|
|
||||||
u32 romparams[3];
|
ROMListEntry romparams;
|
||||||
if (!ReadROMParams(gamecode, romparams))
|
if (!ReadROMParams(gamecode, &romparams))
|
||||||
{
|
{
|
||||||
// set defaults
|
// set defaults
|
||||||
printf("ROM entry not found\n");
|
printf("ROM entry not found\n");
|
||||||
|
|
||||||
romparams[0] = CartROMSize;
|
romparams.GameCode = gamecode;
|
||||||
|
romparams.ROMSize = CartROMSize;
|
||||||
if (*(u32*)&CartROM[0x20] < 0x4000)
|
if (*(u32*)&CartROM[0x20] < 0x4000)
|
||||||
romparams[1] = 0; // no saveRAM for homebrew
|
romparams.SaveMemType = 0; // no saveRAM for homebrew
|
||||||
else
|
else
|
||||||
romparams[1] = 2; // assume EEPROM 64k (TODO FIXME)
|
romparams.SaveMemType = 2; // assume EEPROM 64k (TODO FIXME)
|
||||||
}
|
}
|
||||||
else
|
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
|
// generate a ROM ID
|
||||||
// note: most games don't check the actual value
|
// note: most games don't check the actual value
|
||||||
|
@ -957,9 +958,12 @@ bool LoadROM(const char* path, const char* sram, bool direct)
|
||||||
else
|
else
|
||||||
CartID |= (0x100 - (CartROMSize >> 28)) << 8;
|
CartID |= (0x100 - (CartROMSize >> 28)) << 8;
|
||||||
|
|
||||||
if (romparams[1] == 8)
|
if (romparams.SaveMemType == 8)
|
||||||
CartID |= 0x08000000; // NAND flag
|
CartID |= 0x08000000; // NAND flag
|
||||||
|
|
||||||
|
if (CartIsDSi)
|
||||||
|
CartID |= 0x40000000;
|
||||||
|
|
||||||
printf("Cart ID: %08X\n", CartID);
|
printf("Cart ID: %08X\n", CartID);
|
||||||
|
|
||||||
u32 arm9base = *(u32*)&CartROM[0x20];
|
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);
|
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)
|
for (u32 i = 0; i < 0x800; i += 8)
|
||||||
Key1_Encrypt((u32*)&CartROM[arm9base + i]);
|
Key1_Encrypt((u32*)&CartROM[arm9base + i]);
|
||||||
|
|
||||||
Key1_InitKeycode(gamecode, 2, 2);
|
Key1_InitKeycode(false, gamecode, 2, 2);
|
||||||
Key1_Encrypt((u32*)&CartROM[arm9base]);
|
Key1_Encrypt((u32*)&CartROM[arm9base]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1007,12 +1011,11 @@ bool LoadROM(const char* path, const char* sram, bool direct)
|
||||||
ROMCommandHandler = ROMCommand_Retail;
|
ROMCommandHandler = ROMCommand_Retail;
|
||||||
|
|
||||||
// encryption
|
// encryption
|
||||||
Key1_InitKeycode(gamecode, 2, 2);
|
Key1_InitKeycode(false, gamecode, 2, 2);
|
||||||
|
|
||||||
|
|
||||||
// save
|
// save
|
||||||
printf("Save file: %s\n", sram);
|
printf("Save file: %s\n", sram);
|
||||||
NDSCart_SRAM::LoadSave(sram, romparams[1]);
|
NDSCart_SRAM::LoadSave(sram, romparams.SaveMemType);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1023,6 +1026,27 @@ void RelocateSave(const char* path, bool write)
|
||||||
NDSCart_SRAM::RelocateSave(path, 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)
|
void ReadROM(u32 addr, u32 len, u32 offset)
|
||||||
{
|
{
|
||||||
if (!CartInserted) return;
|
if (!CartInserted) return;
|
||||||
|
@ -1197,7 +1221,7 @@ void WriteROMCnt(u32 val)
|
||||||
// handle KEY1 encryption as needed.
|
// handle KEY1 encryption as needed.
|
||||||
// KEY2 encryption is implemented in hardware and doesn't need to be handled.
|
// KEY2 encryption is implemented in hardware and doesn't need to be handled.
|
||||||
u8 cmd[8];
|
u8 cmd[8];
|
||||||
if (CmdEncMode == 1)
|
if (CmdEncMode == 1 || CmdEncMode == 11)
|
||||||
{
|
{
|
||||||
*(u32*)&cmd[0] = ByteSwap(*(u32*)&ROMCommand[4]);
|
*(u32*)&cmd[0] = ByteSwap(*(u32*)&ROMCommand[4]);
|
||||||
*(u32*)&cmd[4] = ByteSwap(*(u32*)&ROMCommand[0]);
|
*(u32*)&cmd[4] = ByteSwap(*(u32*)&ROMCommand[0]);
|
||||||
|
@ -1243,11 +1267,23 @@ void WriteROMCnt(u32 val)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x3C:
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (CmdEncMode == 1)
|
if (CmdEncMode == 1 || CmdEncMode == 11)
|
||||||
{
|
{
|
||||||
switch (cmd[0] & 0xF0)
|
switch (cmd[0] & 0xF0)
|
||||||
{
|
{
|
||||||
|
@ -1263,6 +1299,12 @@ void WriteROMCnt(u32 val)
|
||||||
case 0x20:
|
case 0x20:
|
||||||
{
|
{
|
||||||
u32 addr = (cmd[2] & 0xF0) << 8;
|
u32 addr = (cmd[2] & 0xF0) << 8;
|
||||||
|
if (CmdEncMode == 11)
|
||||||
|
{
|
||||||
|
u32 arm9i_base = *(u32*)&CartROM[0x1C0];
|
||||||
|
addr -= 0x4000;
|
||||||
|
addr += arm9i_base;
|
||||||
|
}
|
||||||
ReadROM(addr, 0x1000, 0);
|
ReadROM(addr, 0x1000, 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -48,6 +48,8 @@ void DecryptSecureArea(u8* out);
|
||||||
bool LoadROM(const char* path, const char* sram, bool direct);
|
bool LoadROM(const char* path, const char* sram, bool direct);
|
||||||
void RelocateSave(const char* path, bool write);
|
void RelocateSave(const char* path, bool write);
|
||||||
|
|
||||||
|
void ResetCart();
|
||||||
|
|
||||||
void WriteROMCnt(u32 val);
|
void WriteROMCnt(u32 val);
|
||||||
u32 ReadROMData();
|
u32 ReadROMData();
|
||||||
|
|
||||||
|
|
|
@ -19,18 +19,20 @@
|
||||||
#include "OpenGLSupport.h"
|
#include "OpenGLSupport.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace OpenGL
|
||||||
|
{
|
||||||
|
|
||||||
DO_PROCLIST(DECLPROC);
|
DO_PROCLIST(DECLPROC);
|
||||||
|
|
||||||
|
|
||||||
bool OpenGL_Init()
|
bool Init()
|
||||||
{
|
{
|
||||||
DO_PROCLIST(LOADPROC);
|
DO_PROCLIST(LOADPROC);
|
||||||
|
|
||||||
return true;
|
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 len;
|
||||||
int res;
|
int res;
|
||||||
|
@ -89,7 +91,7 @@ bool OpenGL_BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, cons
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenGL_LinkShaderProgram(GLuint* ids)
|
bool LinkShaderProgram(GLuint* ids)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
@ -115,14 +117,16 @@ bool OpenGL_LinkShaderProgram(GLuint* ids)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL_DeleteShaderProgram(GLuint* ids)
|
void DeleteShaderProgram(GLuint* ids)
|
||||||
{
|
{
|
||||||
glDeleteShader(ids[0]);
|
glDeleteShader(ids[0]);
|
||||||
glDeleteShader(ids[1]);
|
glDeleteShader(ids[1]);
|
||||||
glDeleteProgram(ids[2]);
|
glDeleteProgram(ids[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGL_UseShaderProgram(GLuint* ids)
|
void UseShaderProgram(GLuint* ids)
|
||||||
{
|
{
|
||||||
glUseProgram(ids[2]);
|
glUseProgram(ids[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
// TODO: different includes for each platform
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
#include <GL/glext.h>
|
#include <GL/glext.h>
|
||||||
|
|
||||||
|
@ -45,18 +47,18 @@
|
||||||
|
|
||||||
|
|
||||||
// if you need more OpenGL functions, add them to the macronator here
|
// 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)
|
#ifdef __WIN32__
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define DO_PROCLIST_1_3(func) \
|
#define DO_PROCLIST_1_3(func) \
|
||||||
func(GLACTIVETEXTURE, glActiveTexture); \
|
func(GLACTIVETEXTURE, glActiveTexture); \
|
||||||
func(GLBLENDCOLOR, glBlendColor); \
|
func(GLBLENDCOLOR, glBlendColor); \
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define DO_PROCLIST_1_3(func)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,6 +114,11 @@
|
||||||
func(GLGETUNIFORMLOCATION, glGetUniformLocation); \
|
func(GLGETUNIFORMLOCATION, glGetUniformLocation); \
|
||||||
func(GLGETUNIFORMBLOCKINDEX, glGetUniformBlockIndex); \
|
func(GLGETUNIFORMBLOCKINDEX, glGetUniformBlockIndex); \
|
||||||
\
|
\
|
||||||
|
func(GLFENCESYNC, glFenceSync); \
|
||||||
|
func(GLDELETESYNC, glDeleteSync); \
|
||||||
|
func(GLWAITSYNC, glWaitSync); \
|
||||||
|
func(GLCLIENTWAITSYNC, glClientWaitSync); \
|
||||||
|
\
|
||||||
func(GLDRAWBUFFERS, glDrawBuffers); \
|
func(GLDRAWBUFFERS, glDrawBuffers); \
|
||||||
\
|
\
|
||||||
func(GLBLENDFUNCSEPARATE, glBlendFuncSeparate); \
|
func(GLBLENDFUNCSEPARATE, glBlendFuncSeparate); \
|
||||||
|
@ -122,14 +129,18 @@
|
||||||
func(GLGETSTRINGI, glGetStringi); \
|
func(GLGETSTRINGI, glGetStringi); \
|
||||||
|
|
||||||
|
|
||||||
|
namespace OpenGL
|
||||||
|
{
|
||||||
|
|
||||||
DO_PROCLIST(DECLPROC_EXT);
|
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
|
#endif // OPENGLSUPPORT_H
|
||||||
|
|
|
@ -24,6 +24,9 @@
|
||||||
namespace Platform
|
namespace Platform
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void Init(int argc, char** argv);
|
||||||
|
void DeInit();
|
||||||
|
|
||||||
void StopEmu();
|
void StopEmu();
|
||||||
|
|
||||||
// fopen() wrappers
|
// fopen() wrappers
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
52
src/SPI.cpp
52
src/SPI.cpp
|
@ -22,12 +22,14 @@
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
#include "SPI.h"
|
#include "SPI.h"
|
||||||
|
#include "DSi_SPI_TSC.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
|
|
||||||
|
|
||||||
namespace SPI_Firmware
|
namespace SPI_Firmware
|
||||||
{
|
{
|
||||||
|
|
||||||
|
char FirmwarePath[1024];
|
||||||
u8* Firmware;
|
u8* Firmware;
|
||||||
u32 FirmwareLength;
|
u32 FirmwareLength;
|
||||||
u32 FirmwareMask;
|
u32 FirmwareMask;
|
||||||
|
@ -76,6 +78,7 @@ bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset)
|
||||||
|
|
||||||
bool Init()
|
bool Init()
|
||||||
{
|
{
|
||||||
|
memset(FirmwarePath, 0, sizeof(FirmwarePath));
|
||||||
Firmware = NULL;
|
Firmware = NULL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -90,10 +93,15 @@ void Reset()
|
||||||
if (Firmware) delete[] Firmware;
|
if (Firmware) delete[] Firmware;
|
||||||
Firmware = NULL;
|
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)
|
if (!f)
|
||||||
{
|
{
|
||||||
printf("firmware.bin not found\n");
|
printf("Firmware not found\n");
|
||||||
|
|
||||||
// TODO: generate default firmware
|
// TODO: generate default firmware
|
||||||
return;
|
return;
|
||||||
|
@ -129,7 +137,11 @@ void Reset()
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
// take a backup
|
// 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");
|
f = Platform::OpenLocalFile(firmbkp, "rb");
|
||||||
if (f) fclose(f);
|
if (f) fclose(f);
|
||||||
else
|
else
|
||||||
|
@ -151,6 +163,7 @@ void Reset()
|
||||||
UserSettings = userdata;
|
UserSettings = userdata;
|
||||||
|
|
||||||
// fix touchscreen coords
|
// fix touchscreen coords
|
||||||
|
#if 0
|
||||||
*(u16*)&Firmware[userdata+0x58] = 0;
|
*(u16*)&Firmware[userdata+0x58] = 0;
|
||||||
*(u16*)&Firmware[userdata+0x5A] = 0;
|
*(u16*)&Firmware[userdata+0x5A] = 0;
|
||||||
Firmware[userdata+0x5C] = 0;
|
Firmware[userdata+0x5C] = 0;
|
||||||
|
@ -173,12 +186,12 @@ void Reset()
|
||||||
Firmware[0x39] = rand()&0xFF;
|
Firmware[0x39] = rand()&0xFF;
|
||||||
Firmware[0x3A] = rand()&0xFF;
|
Firmware[0x3A] = rand()&0xFF;
|
||||||
Firmware[0x3B] = rand()&0xFF;
|
Firmware[0x3B] = rand()&0xFF;
|
||||||
|
#endif
|
||||||
printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
|
||||||
Firmware[0x36], Firmware[0x37], Firmware[0x38],
|
Firmware[0x36], Firmware[0x37], Firmware[0x38],
|
||||||
Firmware[0x39], Firmware[0x3A], Firmware[0x3B]);
|
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
|
// verify shit
|
||||||
printf("FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware[0x2C], 0x2A)?"GOOD":"BAD");
|
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 GetConsoleType() { return Firmware[0x1D]; }
|
||||||
u8 GetWifiVersion() { return Firmware[0x2F]; }
|
u8 GetWifiVersion() { return Firmware[0x2F]; }
|
||||||
u8 GetRFVersion() { return Firmware[0x40]; }
|
u8 GetRFVersion() { return Firmware[0x40]; }
|
||||||
|
u8* GetWifiMAC() { return &Firmware[0x36]; }
|
||||||
|
|
||||||
u8 Read()
|
u8 Read()
|
||||||
{
|
{
|
||||||
|
@ -325,7 +339,7 @@ void Write(u8 val, u32 hold)
|
||||||
|
|
||||||
if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A))
|
if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A))
|
||||||
{
|
{
|
||||||
FILE* f = Platform::OpenLocalFile("firmware.bin", "r+b");
|
FILE* f = Platform::OpenLocalFile(FirmwarePath, "r+b");
|
||||||
if (f)
|
if (f)
|
||||||
{
|
{
|
||||||
u32 cutoff = 0x7FA00 & FirmwareMask;
|
u32 cutoff = 0x7FA00 & FirmwareMask;
|
||||||
|
@ -581,7 +595,7 @@ namespace SPI
|
||||||
|
|
||||||
u16 Cnt;
|
u16 Cnt;
|
||||||
|
|
||||||
u32 CurDevice;
|
u32 CurDevice; // remove me
|
||||||
|
|
||||||
|
|
||||||
bool Init()
|
bool Init()
|
||||||
|
@ -589,6 +603,7 @@ bool Init()
|
||||||
if (!SPI_Firmware::Init()) return false;
|
if (!SPI_Firmware::Init()) return false;
|
||||||
if (!SPI_Powerman::Init()) return false;
|
if (!SPI_Powerman::Init()) return false;
|
||||||
if (!SPI_TSC::Init()) return false;
|
if (!SPI_TSC::Init()) return false;
|
||||||
|
if (!DSi_SPI_TSC::Init()) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -598,6 +613,7 @@ void DeInit()
|
||||||
SPI_Firmware::DeInit();
|
SPI_Firmware::DeInit();
|
||||||
SPI_Powerman::DeInit();
|
SPI_Powerman::DeInit();
|
||||||
SPI_TSC::DeInit();
|
SPI_TSC::DeInit();
|
||||||
|
DSi_SPI_TSC::DeInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
|
@ -607,6 +623,7 @@ void Reset()
|
||||||
SPI_Firmware::Reset();
|
SPI_Firmware::Reset();
|
||||||
SPI_Powerman::Reset();
|
SPI_Powerman::Reset();
|
||||||
SPI_TSC::Reset();
|
SPI_TSC::Reset();
|
||||||
|
if (NDS::ConsoleType == 1) DSi_SPI_TSC::Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoSavestate(Savestate* file)
|
void DoSavestate(Savestate* file)
|
||||||
|
@ -619,6 +636,7 @@ void DoSavestate(Savestate* file)
|
||||||
SPI_Firmware::DoSavestate(file);
|
SPI_Firmware::DoSavestate(file);
|
||||||
SPI_Powerman::DoSavestate(file);
|
SPI_Powerman::DoSavestate(file);
|
||||||
SPI_TSC::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 0x0000: SPI_Powerman::Hold = 0; break;
|
||||||
case 0x0100: SPI_Firmware::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 0x0000: return SPI_Powerman::Read();
|
||||||
case 0x0100: return SPI_Firmware::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;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -674,7 +701,12 @@ void WriteData(u8 val)
|
||||||
{
|
{
|
||||||
case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break;
|
case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break;
|
||||||
case 0x0100: SPI_Firmware::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;
|
default: printf("SPI to unknown device %04X %02X\n", Cnt, val); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ void SetupDirectBoot();
|
||||||
u8 GetConsoleType();
|
u8 GetConsoleType();
|
||||||
u8 GetWifiVersion();
|
u8 GetWifiVersion();
|
||||||
u8 GetRFVersion();
|
u8 GetRFVersion();
|
||||||
|
u8* GetWifiMAC();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +39,9 @@ namespace SPI_TSC
|
||||||
void SetTouchCoords(u16 x, u16 y);
|
void SetTouchCoords(u16 x, u16 y);
|
||||||
void MicInputFrame(s16* data, int samples);
|
void MicInputFrame(s16* data, int samples);
|
||||||
|
|
||||||
|
u8 Read();
|
||||||
|
void Write(u8 val, u32 hold);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace SPI
|
namespace SPI
|
||||||
|
@ -50,7 +54,6 @@ void DeInit();
|
||||||
void Reset();
|
void Reset();
|
||||||
void DoSavestate(Savestate* file);
|
void DoSavestate(Savestate* file);
|
||||||
|
|
||||||
u16 ReadCnt();
|
|
||||||
void WriteCnt(u16 val);
|
void WriteCnt(u16 val);
|
||||||
|
|
||||||
u8 ReadData();
|
u8 ReadData();
|
||||||
|
|
18
src/SPU.cpp
18
src/SPU.cpp
|
@ -19,6 +19,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
|
#include "DSi.h"
|
||||||
#include "SPU.h"
|
#include "SPU.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,6 +117,9 @@ void Reset()
|
||||||
void Stop()
|
void Stop()
|
||||||
{
|
{
|
||||||
memset(OutputBuffer, 0, 2*OutputBufferSize*2);
|
memset(OutputBuffer, 0, 2*OutputBufferSize*2);
|
||||||
|
|
||||||
|
OutputReadOffset = 0;
|
||||||
|
OutputWriteOffset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoSavestate(Savestate* file)
|
void DoSavestate(Savestate* file)
|
||||||
|
@ -151,6 +155,11 @@ Channel::~Channel()
|
||||||
|
|
||||||
void Channel::Reset()
|
void Channel::Reset()
|
||||||
{
|
{
|
||||||
|
if (NDS::ConsoleType == 1)
|
||||||
|
BusRead32 = DSi::ARM7Read32;
|
||||||
|
else
|
||||||
|
BusRead32 = NDS::ARM7Read32;
|
||||||
|
|
||||||
SetCnt(0);
|
SetCnt(0);
|
||||||
SrcAddr = 0;
|
SrcAddr = 0;
|
||||||
TimerReload = 0;
|
TimerReload = 0;
|
||||||
|
@ -213,7 +222,7 @@ void Channel::FIFO_BufferData()
|
||||||
|
|
||||||
for (u32 i = 0; i < burstlen; i += 4)
|
for (u32 i = 0; i < burstlen; i += 4)
|
||||||
{
|
{
|
||||||
FIFO[FIFOWritePos] = NDS::ARM7Read32(SrcAddr + FIFOReadOffset);
|
FIFO[FIFOWritePos] = BusRead32(SrcAddr + FIFOReadOffset);
|
||||||
FIFOReadOffset += 4;
|
FIFOReadOffset += 4;
|
||||||
FIFOWritePos++;
|
FIFOWritePos++;
|
||||||
FIFOWritePos &= 0x7;
|
FIFOWritePos &= 0x7;
|
||||||
|
@ -461,6 +470,11 @@ CaptureUnit::~CaptureUnit()
|
||||||
|
|
||||||
void CaptureUnit::Reset()
|
void CaptureUnit::Reset()
|
||||||
{
|
{
|
||||||
|
if (NDS::ConsoleType == 1)
|
||||||
|
BusWrite32 = DSi::ARM7Write32;
|
||||||
|
else
|
||||||
|
BusWrite32 = NDS::ARM7Write32;
|
||||||
|
|
||||||
SetCnt(0);
|
SetCnt(0);
|
||||||
DstAddr = 0;
|
DstAddr = 0;
|
||||||
TimerReload = 0;
|
TimerReload = 0;
|
||||||
|
@ -496,7 +510,7 @@ void CaptureUnit::FIFO_FlushData()
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < 4; i++)
|
for (u32 i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
NDS::ARM7Write32(DstAddr + FIFOWriteOffset, FIFO[FIFOReadPos]);
|
BusWrite32(DstAddr + FIFOWriteOffset, FIFO[FIFOReadPos]);
|
||||||
|
|
||||||
FIFOReadPos++;
|
FIFOReadPos++;
|
||||||
FIFOReadPos &= 0x3;
|
FIFOReadPos &= 0x3;
|
||||||
|
|
|
@ -142,6 +142,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanOutput(s32* inbuf, u32 samples, s32* leftbuf, s32* rightbuf);
|
void PanOutput(s32* inbuf, u32 samples, s32* leftbuf, s32* rightbuf);
|
||||||
|
|
||||||
|
private:
|
||||||
|
u32 (*BusRead32)(u32 addr);
|
||||||
};
|
};
|
||||||
|
|
||||||
class CaptureUnit
|
class CaptureUnit
|
||||||
|
@ -196,6 +199,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void Run(s32 sample);
|
void Run(s32 sample);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void (*BusWrite32)(u32 addr, u32 val);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,33 @@ enum
|
||||||
ROMSlot_MAX
|
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 ROMPath [ROMSlot_MAX][1024];
|
||||||
extern char SRAMPath[ROMSlot_MAX][1024];
|
extern char SRAMPath[ROMSlot_MAX][1024];
|
||||||
extern bool SavestateLoaded;
|
extern bool SavestateLoaded;
|
||||||
|
@ -41,11 +68,18 @@ extern bool SavestateLoaded;
|
||||||
void Init_ROM();
|
void Init_ROM();
|
||||||
|
|
||||||
// load the BIOS/firmware and boot from it
|
// load the BIOS/firmware and boot from it
|
||||||
bool LoadBIOS();
|
int LoadBIOS();
|
||||||
|
|
||||||
// load a ROM file to the specified cart slot
|
// load a ROM file to the specified cart slot
|
||||||
// note: loading a ROM to the NDS slot resets emulation
|
// 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)
|
// get the filename associated with the given savestate slot (1-8)
|
||||||
void GetSavestateName(int slot, char* filename, int len);
|
void GetSavestateName(int slot, char* filename, int len);
|
||||||
|
@ -64,6 +98,33 @@ bool SaveState(const char* filename);
|
||||||
void UndoStateLoad();
|
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
|
// initialize the audio utility
|
||||||
void Init_Audio(int outputfreq);
|
void Init_Audio(int outputfreq);
|
||||||
|
|
||||||
|
@ -72,9 +133,20 @@ void Init_Audio(int outputfreq);
|
||||||
int AudioOut_GetNumSamples(int outlen);
|
int AudioOut_GetNumSamples(int outlen);
|
||||||
|
|
||||||
// resample audio from the core audio output to match the frontend's
|
// 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
|
// 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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "FrontendUtil.h"
|
#include "FrontendUtil.h"
|
||||||
#include "Config.h"
|
|
||||||
#include "qt_sdl/PlatformConfig.h" // FIXME!!!
|
|
||||||
#include "Platform.h"
|
|
||||||
|
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
#include "GBACart.h"
|
|
||||||
|
#include "mic_blow.h"
|
||||||
|
|
||||||
|
|
||||||
namespace Frontend
|
namespace Frontend
|
||||||
|
@ -35,13 +34,22 @@ namespace Frontend
|
||||||
int AudioOut_Freq;
|
int AudioOut_Freq;
|
||||||
float AudioOut_SampleFrac;
|
float AudioOut_SampleFrac;
|
||||||
|
|
||||||
|
s16* MicBuffer;
|
||||||
|
u32 MicBufferLength;
|
||||||
|
u32 MicBufferReadPos;
|
||||||
|
|
||||||
|
|
||||||
void Init_Audio(int outputfreq)
|
void Init_Audio(int outputfreq)
|
||||||
{
|
{
|
||||||
AudioOut_Freq = outputfreq;
|
AudioOut_Freq = outputfreq;
|
||||||
AudioOut_SampleFrac = 0;
|
AudioOut_SampleFrac = 0;
|
||||||
|
|
||||||
|
MicBuffer = nullptr;
|
||||||
|
MicBufferLength = 0;
|
||||||
|
MicBufferReadPos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int AudioOut_GetNumSamples(int outlen)
|
int AudioOut_GetNumSamples(int outlen)
|
||||||
{
|
{
|
||||||
float f_len_in = (outlen * 32823.6328125) / (float)AudioOut_Freq;
|
float f_len_in = (outlen * 32823.6328125) / (float)AudioOut_Freq;
|
||||||
|
@ -52,14 +60,12 @@ int AudioOut_GetNumSamples(int outlen)
|
||||||
return len_in;
|
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_incr = inlen / (float)outlen;
|
||||||
float res_timer = 0;
|
float res_timer = 0;
|
||||||
int res_pos = 0;
|
int res_pos = 0;
|
||||||
|
|
||||||
int volume = Config::AudioVolume;
|
|
||||||
|
|
||||||
for (int i = 0; i < outlen; i++)
|
for (int i = 0; i < outlen; i++)
|
||||||
{
|
{
|
||||||
outbuf[i*2 ] = (inbuf[res_pos*2 ] * volume) >> 8;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,11 @@ void Init_ROM()
|
||||||
memset(PrevSRAMPath[ROMSlot_GBA], 0, 1024);
|
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)
|
void SetupSRAMPath(int slot)
|
||||||
{
|
{
|
||||||
strncpy(SRAMPath[slot], ROMPath[slot], 1023);
|
strncpy(SRAMPath[slot], ROMPath[slot], 1023);
|
||||||
|
@ -57,8 +62,166 @@ void SetupSRAMPath(int slot)
|
||||||
strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3);
|
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:
|
// TODO:
|
||||||
// original code in the libui frontend called NDS::LoadGBAROM() if needed
|
// original code in the libui frontend called NDS::LoadGBAROM() if needed
|
||||||
// should this be carried over here?
|
// should this be carried over here?
|
||||||
|
@ -67,16 +230,54 @@ bool LoadBIOS()
|
||||||
ROMPath[ROMSlot_NDS][0] = '\0';
|
ROMPath[ROMSlot_NDS][0] = '\0';
|
||||||
SRAMPath[ROMSlot_NDS][0] = '\0';
|
SRAMPath[ROMSlot_NDS][0] = '\0';
|
||||||
|
|
||||||
|
NDS::SetConsoleType(Config::ConsoleType);
|
||||||
NDS::LoadBIOS();
|
NDS::LoadBIOS();
|
||||||
|
|
||||||
SavestateLoaded = false;
|
SavestateLoaded = false;
|
||||||
|
|
||||||
// TODO: error reporting?
|
return Load_OK;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 oldpath[1024];
|
||||||
char oldsram[1024];
|
char oldsram[1024];
|
||||||
strncpy(oldpath, ROMPath[slot], 1024);
|
strncpy(oldpath, ROMPath[slot], 1024);
|
||||||
|
@ -88,31 +289,107 @@ bool LoadROM(const char* file, int slot)
|
||||||
SetupSRAMPath(0);
|
SetupSRAMPath(0);
|
||||||
SetupSRAMPath(1);
|
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;
|
SavestateLoaded = false;
|
||||||
|
|
||||||
// Reload the inserted GBA cartridge (if any)
|
// 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]);
|
if (ROMPath[ROMSlot_GBA][0] != '\0') NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]);
|
||||||
|
|
||||||
strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
|
strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
|
||||||
return true;
|
return Load_OK;
|
||||||
}
|
}
|
||||||
else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot]))
|
else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot]))
|
||||||
{
|
{
|
||||||
SavestateLoaded = false;
|
SavestateLoaded = false; // checkme??
|
||||||
|
|
||||||
strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
|
strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
|
||||||
return true;
|
return Load_OK;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
strncpy(ROMPath[slot], oldpath, 1024);
|
strncpy(ROMPath[slot], oldpath, 1024);
|
||||||
strncpy(SRAMPath[slot], oldsram, 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
|
// SAVESTATE TODO
|
||||||
// * configurable paths. not everyone wants their ROM directory to be polluted, I guess.
|
// * 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,8 +537,6 @@ void UndoStateLoad()
|
||||||
strncpy(SRAMPath[ROMSlot_NDS], PrevSRAMPath[ROMSlot_NDS], 1024);
|
strncpy(SRAMPath[ROMSlot_NDS], PrevSRAMPath[ROMSlot_NDS], 1024);
|
||||||
NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false);
|
NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false);
|
||||||
}
|
}
|
||||||
|
|
||||||
//OSD::AddMessage(0, "State load undone");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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><html><head/><body><p>Controls the volume of the audio output.</p></body></html></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><html><head/><body><p>Forward a WAV file to the emulated microphone.</p><p>This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QRadioButton" name="rbMicWav">
|
||||||
|
<property name="whatsThis">
|
||||||
|
<string><html><head/><body><p>Forward a WAV file to the emulated microphone.</p><p>This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).</p></body></html></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><html><head/><body><p>Input from an external microphone, if available, will be forwarded to the emulated microphone.</p></body></html></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><html><head/><body><p>Noise will be forwarded to the emulated microphone, simulating blowing into the microphone.</p><p>This input mode is activated by holding the microphone hotkey (see Input and Hotkeys).</p></body></html></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><html><head/><body><p>No microphone input.</p></body></html></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>
|
|
@ -2,56 +2,93 @@ project(qt_sdl)
|
||||||
|
|
||||||
SET(SOURCES_QT_SDL
|
SET(SOURCES_QT_SDL
|
||||||
main.cpp
|
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
|
Platform.cpp
|
||||||
PlatformConfig.cpp
|
PlatformConfig.cpp
|
||||||
|
|
||||||
../Util_ROM.cpp
|
../Util_ROM.cpp
|
||||||
|
../Util_Video.cpp
|
||||||
../Util_Audio.cpp
|
../Util_Audio.cpp
|
||||||
../FrontendUtil.h
|
../FrontendUtil.h
|
||||||
|
../mic_blow.h
|
||||||
|
|
||||||
|
../../../melon.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -i <SOURCE> -o <OBJECT>")
|
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -i <SOURCE> -o <OBJECT>")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Qt5 COMPONENTS Core REQUIRED)
|
if (BUILD_STATIC AND QT5_STATIC_DIR)
|
||||||
find_package(Qt5 COMPONENTS Gui REQUIRED)
|
set(QT5_STATIC_BASE ${QT5_STATIC_DIR}/lib/cmake/Qt5)
|
||||||
find_package(Qt5 COMPONENTS Widgets REQUIRED)
|
set(Qt5_DIR ${QT5_STATIC_BASE})
|
||||||
find_package(Threads REQUIRED)
|
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_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(SDL2 REQUIRED sdl2)
|
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 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_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)
|
if (UNIX)
|
||||||
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
|
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
|
||||||
|
target_link_libraries(melonDS dl Qt5::Core Qt5::Gui Qt5::Widgets)
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|
||||||
target_link_libraries(melonDS dl Qt5::Core Qt5::Gui Qt5::Widgets)
|
|
||||||
endif ()
|
|
||||||
elseif (WIN32)
|
elseif (WIN32)
|
||||||
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" ON)
|
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_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)
|
if (PORTABLE)
|
||||||
add_definitions(-DPORTABLE)
|
add_definitions(-DPORTABLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(FILES ../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
|
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_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_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_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_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_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 ../../../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(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
install(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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><html><head/><body><p>DS-mode ARM9 BIOS</p><p>Size should be 4 KB</p></body></html></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><html><head/><body><p>DS-mode ARM7 BIOS</p><p>Size should be 16 KB</p></body></html></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><html><head/><body><p>DS-mode firmware</p><p><br/></p><p>Possible firmwares:</p><p>* 128 KB: DS-mode firmware from a DSi or 3DS. Not bootable.</p><p>* 256 KB: regular DS firmware.</p><p>* 512 KB: iQue DS firmware.</p></body></html></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><html><head/><body><p>DSi-mode ARM7 BIOS</p><p><br/></p><p>Size should be 64 KB</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="txtDSiFirmwarePath">
|
||||||
|
<property name="whatsThis">
|
||||||
|
<string><html><head/><body><p>DSi-mode firmware (used for DS-mode backwards compatibility)</p><p><br/></p><p>Size should be 128 KB</p></body></html></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><html><head/><body><p>DSi-mode ARM9 BIOS</p><p><br/></p><p>Size should be 64 KB</p></body></html></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><html><head/><body><p>DSi NAND dump</p><p><br/></p><p>Should have 'nocash footer' at the end</p></body></html></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><html><head/><body><p>The type of console to emulate</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="chkDirectBoot">
|
||||||
|
<property name="whatsThis">
|
||||||
|
<string><html><head/><body><p>When loading a ROM, completely skip the regular boot process (&quot;Nintendo DS&quot; screen) to boot the ROM directly.</p><p><br/></p><p>Note: if your firmware dump isn't bootable, the ROM will be booted directly regardless of this setting.</p></body></html></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>
|
|
@ -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
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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><html><head/><body><p>Selects which joystick will be used for joystick input, if any is present.</p></body></html></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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -27,8 +27,9 @@
|
||||||
|
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "PlatformConfig.h"
|
#include "PlatformConfig.h"
|
||||||
//#include "LAN_Socket.h"
|
#include "LAN_Socket.h"
|
||||||
//#include "LAN_PCap.h"
|
#include "LAN_PCap.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#ifdef __WIN32__
|
#ifdef __WIN32__
|
||||||
#define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK
|
#define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK
|
||||||
|
@ -56,9 +57,10 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
extern char* EmuDirectory;
|
char* EmuDirectory;
|
||||||
|
|
||||||
void Stop(bool internal);
|
void emuStop();
|
||||||
|
void* oglGetProcAddress(const char* proc);
|
||||||
|
|
||||||
|
|
||||||
namespace Platform
|
namespace Platform
|
||||||
|
@ -71,11 +73,57 @@ u8 PacketBuffer[2048];
|
||||||
#define NIFI_VER 1
|
#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()
|
void StopEmu()
|
||||||
{
|
{
|
||||||
//Stop(true);
|
emuStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FILE* OpenFile(const char* path, const char* mode, bool mustexist)
|
FILE* OpenFile(const char* path, const char* mode, bool mustexist)
|
||||||
{
|
{
|
||||||
QFile f(path);
|
QFile f(path);
|
||||||
|
@ -200,30 +248,30 @@ void Semaphore_Post(void* sema)
|
||||||
|
|
||||||
void* GL_GetProcAddress(const char* proc)
|
void* GL_GetProcAddress(const char* proc)
|
||||||
{
|
{
|
||||||
return (void*) QOpenGLContext::globalShareContext()->getProcAddress(proc);
|
return oglGetProcAddress(proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool MP_Init()
|
bool MP_Init()
|
||||||
{
|
{
|
||||||
int opt_true = 1;
|
int opt_true = 1;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
#ifdef __WIN32__
|
#ifdef __WIN32__
|
||||||
WSADATA wsadata;
|
WSADATA wsadata;
|
||||||
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
|
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif // __WIN32__
|
#endif // __WIN32__
|
||||||
|
|
||||||
MPSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
MPSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
if (MPSocket < 0)
|
if (MPSocket < 0)
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
closesocket(MPSocket);
|
closesocket(MPSocket);
|
||||||
|
@ -233,8 +281,8 @@ bool MP_Init()
|
||||||
|
|
||||||
sockaddr_t saddr;
|
sockaddr_t saddr;
|
||||||
saddr.sa_family = AF_INET;
|
saddr.sa_family = AF_INET;
|
||||||
*(u32*) &saddr.sa_data[2] = htonl(Config::SocketBindAnyAddr ? INADDR_ANY : INADDR_LOOPBACK);
|
*(u32*)&saddr.sa_data[2] = htonl(Config::SocketBindAnyAddr ? INADDR_ANY : INADDR_LOOPBACK);
|
||||||
*(u16*) &saddr.sa_data[0] = htons(7064);
|
*(u16*)&saddr.sa_data[0] = htons(7064);
|
||||||
res = bind(MPSocket, &saddr, sizeof(sockaddr_t));
|
res = bind(MPSocket, &saddr, sizeof(sockaddr_t));
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
|
@ -243,7 +291,7 @@ bool MP_Init()
|
||||||
return false;
|
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)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
closesocket(MPSocket);
|
closesocket(MPSocket);
|
||||||
|
@ -252,50 +300,50 @@ bool MP_Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
MPSendAddr.sa_family = AF_INET;
|
MPSendAddr.sa_family = AF_INET;
|
||||||
*(u32*) &MPSendAddr.sa_data[2] = htonl(INADDR_BROADCAST);
|
*(u32*)&MPSendAddr.sa_data[2] = htonl(INADDR_BROADCAST);
|
||||||
*(u16*) &MPSendAddr.sa_data[0] = htons(7064);
|
*(u16*)&MPSendAddr.sa_data[0] = htons(7064);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MP_DeInit()
|
void MP_DeInit()
|
||||||
{
|
{
|
||||||
if (MPSocket >= 0)
|
if (MPSocket >= 0)
|
||||||
closesocket(MPSocket);
|
closesocket(MPSocket);
|
||||||
|
|
||||||
#ifdef __WIN32__
|
#ifdef __WIN32__
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
#endif // __WIN32__
|
#endif // __WIN32__
|
||||||
}
|
}
|
||||||
|
|
||||||
int MP_SendPacket(u8* data, int len)
|
int MP_SendPacket(u8* data, int len)
|
||||||
{
|
{
|
||||||
if (MPSocket < 0)
|
if (MPSocket < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (len > 2048 - 8)
|
if (len > 2048-8)
|
||||||
{
|
{
|
||||||
printf("MP_SendPacket: error: packet too long (%d)\n", len);
|
printf("MP_SendPacket: error: packet too long (%d)\n", len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
*(u32*) &PacketBuffer[0] = htonl(0x4946494E); // NIFI
|
*(u32*)&PacketBuffer[0] = htonl(0x4946494E); // NIFI
|
||||||
PacketBuffer[4] = NIFI_VER;
|
PacketBuffer[4] = NIFI_VER;
|
||||||
PacketBuffer[5] = 0;
|
PacketBuffer[5] = 0;
|
||||||
*(u16*) &PacketBuffer[6] = htons(len);
|
*(u16*)&PacketBuffer[6] = htons(len);
|
||||||
memcpy(&PacketBuffer[8], data, len);
|
memcpy(&PacketBuffer[8], data, len);
|
||||||
|
|
||||||
int slen = sendto(MPSocket, (const char*) PacketBuffer, len + 8, 0, &MPSendAddr, sizeof(sockaddr_t));
|
int slen = sendto(MPSocket, (const char*)PacketBuffer, len+8, 0, &MPSendAddr, sizeof(sockaddr_t));
|
||||||
if (slen < 8) return 0;
|
if (slen < 8) return 0;
|
||||||
return slen - 8;
|
return slen - 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MP_RecvPacket(u8* data, bool block)
|
int MP_RecvPacket(u8* data, bool block)
|
||||||
{
|
{
|
||||||
if (MPSocket < 0)
|
if (MPSocket < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fd_set fd;
|
fd_set fd;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
FD_ZERO(&fd);
|
FD_ZERO(&fd);
|
||||||
|
@ -303,83 +351,82 @@ int MP_RecvPacket(u8* data, bool block)
|
||||||
tv.tv_sec = 0;
|
tv.tv_sec = 0;
|
||||||
tv.tv_usec = block ? 5000 : 0;
|
tv.tv_usec = block ? 5000 : 0;
|
||||||
|
|
||||||
if (!select(MPSocket + 1, &fd, 0, 0, &tv))
|
if (!select(MPSocket+1, &fd, 0, 0, &tv))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sockaddr_t fromAddr;
|
sockaddr_t fromAddr;
|
||||||
socklen_t fromLen = sizeof(sockaddr_t);
|
socklen_t fromLen = sizeof(sockaddr_t);
|
||||||
int rlen = recvfrom(MPSocket, (char*) PacketBuffer, 2048, 0, &fromAddr, &fromLen);
|
int rlen = recvfrom(MPSocket, (char*)PacketBuffer, 2048, 0, &fromAddr, &fromLen);
|
||||||
if (rlen < 8 + 24)
|
if (rlen < 8+24)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
rlen -= 8;
|
rlen -= 8;
|
||||||
|
|
||||||
if (ntohl(*(u32*) &PacketBuffer[0]) != 0x4946494E)
|
if (ntohl(*(u32*)&PacketBuffer[0]) != 0x4946494E)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PacketBuffer[4] != NIFI_VER)
|
if (PacketBuffer[4] != NIFI_VER)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ntohs(*(u16*) &PacketBuffer[6]) != rlen)
|
if (ntohs(*(u16*)&PacketBuffer[6]) != rlen)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(data, &PacketBuffer[8], rlen);
|
memcpy(data, &PacketBuffer[8], rlen);
|
||||||
return rlen;
|
return rlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool LAN_Init()
|
bool LAN_Init()
|
||||||
{
|
{
|
||||||
/*if (Config::DirectLAN)
|
if (Config::DirectLAN)
|
||||||
{
|
{
|
||||||
if (!LAN_PCap::Init(true))
|
if (!LAN_PCap::Init(true))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!LAN_Socket::Init())
|
if (!LAN_Socket::Init())
|
||||||
return false;
|
return false;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LAN_DeInit()
|
void LAN_DeInit()
|
||||||
{
|
{
|
||||||
// checkme. blarg
|
// checkme. blarg
|
||||||
//if (Config::DirectLAN)
|
//if (Config::DirectLAN)
|
||||||
// LAN_PCap::DeInit();
|
// LAN_PCap::DeInit();
|
||||||
//else
|
//else
|
||||||
// LAN_Socket::DeInit();
|
// LAN_Socket::DeInit();
|
||||||
/*LAN_PCap::DeInit();
|
LAN_PCap::DeInit();
|
||||||
LAN_Socket::DeInit();*/
|
LAN_Socket::DeInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
int LAN_SendPacket(u8* data, int len)
|
int LAN_SendPacket(u8* data, int len)
|
||||||
{
|
{
|
||||||
/*if (Config::DirectLAN)
|
if (Config::DirectLAN)
|
||||||
return LAN_PCap::SendPacket(data, len);
|
return LAN_PCap::SendPacket(data, len);
|
||||||
else
|
else
|
||||||
return LAN_Socket::SendPacket(data, len);*/
|
return LAN_Socket::SendPacket(data, len);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int LAN_RecvPacket(u8* data)
|
int LAN_RecvPacket(u8* data)
|
||||||
{
|
{
|
||||||
/*if (Config::DirectLAN)
|
if (Config::DirectLAN)
|
||||||
return LAN_PCap::RecvPacket(data);
|
return LAN_PCap::RecvPacket(data);
|
||||||
else
|
else
|
||||||
return LAN_Socket::RecvPacket(data);*/
|
return LAN_Socket::RecvPacket(data);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -40,16 +40,24 @@ int ScreenRotation;
|
||||||
int ScreenGap;
|
int ScreenGap;
|
||||||
int ScreenLayout;
|
int ScreenLayout;
|
||||||
int ScreenSizing;
|
int ScreenSizing;
|
||||||
|
int IntegerScaling;
|
||||||
int ScreenFilter;
|
int ScreenFilter;
|
||||||
|
|
||||||
int ScreenUseGL;
|
int ScreenUseGL;
|
||||||
int ScreenVSync;
|
int ScreenVSync;
|
||||||
int ScreenRatio;
|
int ScreenVSyncInterval;
|
||||||
|
|
||||||
|
int _3DRenderer;
|
||||||
|
int Threaded3D;
|
||||||
|
|
||||||
|
int GL_ScaleFactor;
|
||||||
|
int GL_Antialias;
|
||||||
|
|
||||||
int LimitFPS;
|
int LimitFPS;
|
||||||
int AudioSync;
|
int AudioSync;
|
||||||
int ShowOSD;
|
int ShowOSD;
|
||||||
|
|
||||||
|
int ConsoleType;
|
||||||
int DirectBoot;
|
int DirectBoot;
|
||||||
|
|
||||||
int SocketBindAnyAddr;
|
int SocketBindAnyAddr;
|
||||||
|
@ -60,25 +68,25 @@ int SavestateRelocSRAM;
|
||||||
|
|
||||||
int AudioVolume;
|
int AudioVolume;
|
||||||
int MicInputType;
|
int MicInputType;
|
||||||
char MicWavPath[512];
|
char MicWavPath[1024];
|
||||||
|
|
||||||
char LastROMFolder[512];
|
char LastROMFolder[1024];
|
||||||
|
|
||||||
|
|
||||||
ConfigEntry PlatformConfigFile[] =
|
ConfigEntry PlatformConfigFile[] =
|
||||||
{
|
{
|
||||||
{"Key_A", 0, &KeyMapping[0], 32, NULL, 0},
|
{"Key_A", 0, &KeyMapping[0], -1, NULL, 0},
|
||||||
{"Key_B", 0, &KeyMapping[1], 31, NULL, 0},
|
{"Key_B", 0, &KeyMapping[1], -1, NULL, 0},
|
||||||
{"Key_Select", 0, &KeyMapping[2], 57, NULL, 0},
|
{"Key_Select", 0, &KeyMapping[2], -1, NULL, 0},
|
||||||
{"Key_Start", 0, &KeyMapping[3], 28, NULL, 0},
|
{"Key_Start", 0, &KeyMapping[3], -1, NULL, 0},
|
||||||
{"Key_Right", 0, &KeyMapping[4], 333, NULL, 0},
|
{"Key_Right", 0, &KeyMapping[4], -1, NULL, 0},
|
||||||
{"Key_Left", 0, &KeyMapping[5], 331, NULL, 0},
|
{"Key_Left", 0, &KeyMapping[5], -1, NULL, 0},
|
||||||
{"Key_Up", 0, &KeyMapping[6], 328, NULL, 0},
|
{"Key_Up", 0, &KeyMapping[6], -1, NULL, 0},
|
||||||
{"Key_Down", 0, &KeyMapping[7], 336, NULL, 0},
|
{"Key_Down", 0, &KeyMapping[7], -1, NULL, 0},
|
||||||
{"Key_R", 0, &KeyMapping[8], 54, NULL, 0},
|
{"Key_R", 0, &KeyMapping[8], -1, NULL, 0},
|
||||||
{"Key_L", 0, &KeyMapping[9], 86, NULL, 0},
|
{"Key_L", 0, &KeyMapping[9], -1, NULL, 0},
|
||||||
{"Key_X", 0, &KeyMapping[10], 17, NULL, 0},
|
{"Key_X", 0, &KeyMapping[10], -1, NULL, 0},
|
||||||
{"Key_Y", 0, &KeyMapping[11], 30, NULL, 0},
|
{"Key_Y", 0, &KeyMapping[11], -1, NULL, 0},
|
||||||
|
|
||||||
{"Joy_A", 0, &JoyMapping[0], -1, NULL, 0},
|
{"Joy_A", 0, &JoyMapping[0], -1, NULL, 0},
|
||||||
{"Joy_B", 0, &JoyMapping[1], -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_X", 0, &JoyMapping[10], -1, NULL, 0},
|
||||||
{"Joy_Y", 0, &JoyMapping[11], -1, NULL, 0},
|
{"Joy_Y", 0, &JoyMapping[11], -1, NULL, 0},
|
||||||
|
|
||||||
{"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], 0x0D, NULL, 0},
|
{"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1, NULL, 0},
|
||||||
{"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], 0x35, NULL, 0},
|
{"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1, NULL, 0},
|
||||||
{"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0},
|
{"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0},
|
||||||
{"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0},
|
{"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0},
|
||||||
{"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], 0x0F, NULL, 0},
|
{"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], -1, NULL, 0},
|
||||||
{"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0},
|
{"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0},
|
||||||
{"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], 0x4B, NULL, 0},
|
{"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, NULL, 0},
|
||||||
{"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], 0x4D, NULL, 0},
|
{"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, NULL, 0},
|
||||||
|
|
||||||
{"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0},
|
{"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0},
|
||||||
{"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0},
|
{"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0},
|
||||||
|
@ -121,16 +129,24 @@ ConfigEntry PlatformConfigFile[] =
|
||||||
{"ScreenGap", 0, &ScreenGap, 0, NULL, 0},
|
{"ScreenGap", 0, &ScreenGap, 0, NULL, 0},
|
||||||
{"ScreenLayout", 0, &ScreenLayout, 0, NULL, 0},
|
{"ScreenLayout", 0, &ScreenLayout, 0, NULL, 0},
|
||||||
{"ScreenSizing", 0, &ScreenSizing, 0, NULL, 0},
|
{"ScreenSizing", 0, &ScreenSizing, 0, NULL, 0},
|
||||||
|
{"IntegerScaling", 0, &IntegerScaling, 0, NULL, 0},
|
||||||
{"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0},
|
{"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0},
|
||||||
|
|
||||||
{"ScreenUseGL", 0, &ScreenUseGL, 1, NULL, 0},
|
{"ScreenUseGL", 0, &ScreenUseGL, 1, NULL, 0},
|
||||||
{"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0},
|
{"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0},
|
||||||
{"ScreenRatio", 0, &ScreenRatio, 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},
|
{"LimitFPS", 0, &LimitFPS, 0, NULL, 0},
|
||||||
{"AudioSync", 0, &AudioSync, 1, NULL, 0},
|
{"AudioSync", 0, &AudioSync, 1, NULL, 0},
|
||||||
{"ShowOSD", 0, &ShowOSD, 1, NULL, 0},
|
{"ShowOSD", 0, &ShowOSD, 1, NULL, 0},
|
||||||
|
|
||||||
|
{"ConsoleType", 0, &ConsoleType, 0, NULL, 0},
|
||||||
{"DirectBoot", 0, &DirectBoot, 1, NULL, 0},
|
{"DirectBoot", 0, &DirectBoot, 1, NULL, 0},
|
||||||
|
|
||||||
{"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0},
|
{"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0},
|
||||||
|
@ -141,9 +157,9 @@ ConfigEntry PlatformConfigFile[] =
|
||||||
|
|
||||||
{"AudioVolume", 0, &AudioVolume, 256, NULL, 0},
|
{"AudioVolume", 0, &AudioVolume, 256, NULL, 0},
|
||||||
{"MicInputType", 0, &MicInputType, 1, 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}
|
{"", -1, NULL, 0, NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,16 +53,24 @@ extern int ScreenRotation;
|
||||||
extern int ScreenGap;
|
extern int ScreenGap;
|
||||||
extern int ScreenLayout;
|
extern int ScreenLayout;
|
||||||
extern int ScreenSizing;
|
extern int ScreenSizing;
|
||||||
|
extern int IntegerScaling;
|
||||||
extern int ScreenFilter;
|
extern int ScreenFilter;
|
||||||
|
|
||||||
extern int ScreenUseGL;
|
extern int ScreenUseGL;
|
||||||
extern int ScreenVSync;
|
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 LimitFPS;
|
||||||
extern int AudioSync;
|
extern int AudioSync;
|
||||||
extern int ShowOSD;
|
extern int ShowOSD;
|
||||||
|
|
||||||
|
extern int ConsoleType;
|
||||||
extern int DirectBoot;
|
extern int DirectBoot;
|
||||||
|
|
||||||
extern int SocketBindAnyAddr;
|
extern int SocketBindAnyAddr;
|
||||||
|
@ -73,9 +81,9 @@ extern int SavestateRelocSRAM;
|
||||||
|
|
||||||
extern int AudioVolume;
|
extern int AudioVolume;
|
||||||
extern int MicInputType;
|
extern int MicInputType;
|
||||||
extern char MicWavPath[512];
|
extern char MicWavPath[1024];
|
||||||
|
|
||||||
extern char LastROMFolder[512];
|
extern char LastROMFolder[1024];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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><html><head/><body><p>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.</p></body></html></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><html><head/><body><p>Run the software renderer on a separate thread. Yields better performance on multi-core CPUs.</p></body></html></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><html><head/><body><p>The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...</p></body></html></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><html><head/><body><p>The interval at which to synchronize to the monitor's refresh rate. Set to 1 for a 60Hz monitor, 2 for 120Hz, ...</p></body></html></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><html><head/><body><p>Use OpenGL to draw the DS screens to the main window. May result in better frame pacing. Mandatory when using the OpenGL 3D renderer.</p></body></html></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><html><head/><body><p>When using OpenGL, synchronize the video output to your monitor's refresh rate.</p></body></html></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><html><head/><body><p>The OpenGL renderer may be faster than software and supports graphical enhancements, but is more prone to glitches.</p></body></html></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><html><head/><body><p>The software renderer is more accurate and less prone to rendering glitches, but requires more CPU power.</p></body></html></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>
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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><html><head/><body><p>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.</p></body></html></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><html><head/><body><p>Direct mode directly routes network traffic to the host network. It is the most reliable, but requires an ethernet connection.</p><p><br/></p><p>Non-direct mode uses a layer of emulation to get around this, but is more prone to problems.</p></body></html></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><html><head/><body><p>Selects the network adapter through which to route network traffic under direct mode.</p></body></html></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>
|
|
@ -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
|
@ -21,8 +21,17 @@
|
||||||
|
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <QWindow>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
#include <QActionGroup>
|
||||||
|
|
||||||
|
#include <QOffscreenSurface>
|
||||||
|
#include <QOpenGLWidget>
|
||||||
|
#include <QOpenGLContext>
|
||||||
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QOpenGLFunctions_3_2_Core>
|
||||||
|
#include <QOpenGLShaderProgram>
|
||||||
|
|
||||||
|
|
||||||
class EmuThread : public QThread
|
class EmuThread : public QThread
|
||||||
|
@ -33,43 +42,124 @@ class EmuThread : public QThread
|
||||||
public:
|
public:
|
||||||
explicit EmuThread(QObject* parent = nullptr);
|
explicit EmuThread(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
void initOpenGL();
|
||||||
|
void deinitOpenGL();
|
||||||
|
|
||||||
|
void* oglGetProcAddress(const char* proc);
|
||||||
|
|
||||||
void changeWindowTitle(char* title);
|
void changeWindowTitle(char* title);
|
||||||
|
|
||||||
// to be called from the UI thread
|
// to be called from the UI thread
|
||||||
void emuRun();
|
void emuRun();
|
||||||
void emuPause(bool refresh);
|
void emuPause();
|
||||||
void emuUnpause();
|
void emuUnpause();
|
||||||
void emuStop();
|
void emuStop();
|
||||||
|
|
||||||
bool emuIsRunning();
|
bool emuIsRunning();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void windowUpdate();
|
||||||
void windowTitleChange(QString title);
|
void windowTitleChange(QString title);
|
||||||
|
|
||||||
void windowEmuStart();
|
void windowEmuStart();
|
||||||
void windowEmuStop();
|
void windowEmuStop();
|
||||||
void windowPauseToggle();
|
void windowEmuPause();
|
||||||
|
void windowEmuReset();
|
||||||
|
|
||||||
|
void windowLimitFPSChange();
|
||||||
|
|
||||||
|
void screenLayoutChange();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
volatile int EmuStatus;
|
volatile int EmuStatus;
|
||||||
int PrevEmuStatus;
|
int PrevEmuStatus;
|
||||||
int EmuRunning;
|
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
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindowPanel(QWidget* parent);
|
explicit ScreenPanelNative(QWidget* parent);
|
||||||
~MainWindowPanel();
|
~ScreenPanelNative();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent* event) override;
|
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:
|
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);
|
explicit MainWindow(QWidget* parent = nullptr);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
|
bool hasOGL;
|
||||||
|
QOpenGLContext* getOGLContext();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
|
|
||||||
void keyPressEvent(QKeyEvent* 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:
|
private slots:
|
||||||
void onOpenFile();
|
void onOpenFile();
|
||||||
|
@ -96,15 +198,40 @@ private slots:
|
||||||
void onReset();
|
void onReset();
|
||||||
void onStop();
|
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 onTitleUpdate(QString title);
|
||||||
|
|
||||||
void onEmuStart();
|
void onEmuStart();
|
||||||
void onEmuStop();
|
void onEmuStop();
|
||||||
void onEmuPause();
|
|
||||||
void onEmuUnpause();
|
void onUpdateVideoSettings(bool glchange);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MainWindowPanel* panel;
|
void createScreenPanel();
|
||||||
|
|
||||||
|
QString loadErrorStr(int error);
|
||||||
|
|
||||||
|
public:
|
||||||
|
QWidget* panel;
|
||||||
|
|
||||||
QAction* actOpenROM;
|
QAction* actOpenROM;
|
||||||
QAction* actBootFirmware;
|
QAction* actBootFirmware;
|
||||||
|
@ -116,6 +243,27 @@ private:
|
||||||
QAction* actPause;
|
QAction* actPause;
|
||||||
QAction* actReset;
|
QAction* actReset;
|
||||||
QAction* actStop;
|
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
|
#endif // MAIN_H
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue