592 lines
15 KiB
C++
592 lines
15 KiB
C++
#include "NDS.h"
|
|
#include "GPU.h"
|
|
#include "SPU.h"
|
|
#include "RTC.h"
|
|
#include "ARM.h"
|
|
#include "NDSCart.h"
|
|
#include "GBACart.h"
|
|
#include "DSi.h"
|
|
#include "DSi_NAND.h"
|
|
#include "Platform.h"
|
|
#include "BizConfig.h"
|
|
#include "types.h"
|
|
#include "frontend/mic_blow.h"
|
|
|
|
#include "emulibc.h"
|
|
#include "waterboxcore.h"
|
|
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <time.h>
|
|
|
|
#include <sstream>
|
|
|
|
#define EXPORT extern "C" ECL_EXPORT
|
|
|
|
static GPU::RenderSettings biz_render_settings { false, 1, false };
|
|
static bool biz_skip_fw;
|
|
static time_t biz_time;
|
|
|
|
static time_t BizRtcCallback()
|
|
{
|
|
return biz_time;
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
NONE = 0x00,
|
|
USE_REAL_BIOS = 0x01,
|
|
SKIP_FIRMWARE = 0x02,
|
|
GBA_CART_PRESENT = 0x04,
|
|
RESERVED_FLAG = 0x08,
|
|
FIRMWARE_OVERRIDE = 0x10,
|
|
IS_DSI = 0x20,
|
|
LOAD_DSIWARE = 0x40,
|
|
THREADED_RENDERING = 0x80,
|
|
} LoadFlags;
|
|
|
|
typedef struct
|
|
{
|
|
u8* DsRomData;
|
|
u32 DsRomLen;
|
|
u8* GbaRomData;
|
|
u32 GbaRomLen;
|
|
u8* GbaRamData;
|
|
u32 GbaRamLen;
|
|
char* NandData;
|
|
u32 NandLen;
|
|
u8* TmdData;
|
|
s32 AudioBitrate;
|
|
} LoadData;
|
|
|
|
typedef struct
|
|
{
|
|
char* FirmwareUsername; // max 10 length (then terminator)
|
|
s32 FirmwareUsernameLength;
|
|
s32 FirmwareLanguage;
|
|
s32 FirmwareBirthdayMonth;
|
|
s32 FirmwareBirthdayDay;
|
|
s32 FirmwareFavouriteColour;
|
|
char* FirmwareMessage; // max 26 length (then terminator)
|
|
s32 FirmwareMessageLength;
|
|
} FirmwareSettings;
|
|
|
|
extern std::stringstream* NANDFilePtr;
|
|
|
|
static bool LoadDSiWare(u8* TmdData)
|
|
{
|
|
FILE* bios7i = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
|
|
if (!reinterpret_cast<void*>(bios7i))
|
|
return false;
|
|
|
|
u8 es_keyY[16];
|
|
fseek(bios7i, 0x8308, SEEK_SET);
|
|
fread(es_keyY, 16, 1, bios7i);
|
|
fclose(bios7i);
|
|
|
|
if (!DSi_NAND::Init(es_keyY))
|
|
return false;
|
|
|
|
bool ret = DSi_NAND::ImportTitle("dsiware.rom", TmdData, false);
|
|
|
|
DSi_NAND::DeInit();
|
|
return ret;
|
|
}
|
|
|
|
EXPORT bool Init(LoadFlags loadFlags, LoadData* loadData, FirmwareSettings* fwSettings)
|
|
{
|
|
Config::ExternalBIOSEnable = !!(loadFlags & USE_REAL_BIOS);
|
|
Config::AudioBitrate = loadData->AudioBitrate;
|
|
Config::FirmwareOverrideSettings = !!(loadFlags & FIRMWARE_OVERRIDE);
|
|
biz_skip_fw = !!(loadFlags & SKIP_FIRMWARE);
|
|
bool isDsi = !!(loadFlags & IS_DSI);
|
|
|
|
NDS::SetConsoleType(isDsi);
|
|
biz_time = 0;
|
|
RTC::RtcCallback = BizRtcCallback;
|
|
|
|
if (Config::FirmwareOverrideSettings)
|
|
{
|
|
std::string fwUsername(fwSettings->FirmwareUsername, fwSettings->FirmwareUsernameLength);
|
|
fwUsername += '\0';
|
|
Config::FirmwareUsername = fwUsername;
|
|
Config::FirmwareLanguage = fwSettings->FirmwareLanguage;
|
|
Config::FirmwareBirthdayMonth = fwSettings->FirmwareBirthdayMonth;
|
|
Config::FirmwareBirthdayDay = fwSettings->FirmwareBirthdayDay;
|
|
Config::FirmwareFavouriteColour = fwSettings->FirmwareFavouriteColour;
|
|
std::string fwMessage(fwSettings->FirmwareMessage, fwSettings->FirmwareMessageLength);
|
|
fwMessage += '\0';
|
|
Config::FirmwareMessage = fwMessage;
|
|
Config::FirmwareMAC = "00:09:BF:0E:49:16"; // TODO: Make configurable
|
|
}
|
|
|
|
NANDFilePtr = isDsi ? new std::stringstream(std::string(loadData->NandData, loadData->NandLen), std::ios_base::in | std::ios_base::out | std::ios_base::binary) : nullptr;
|
|
|
|
if (isDsi && (loadFlags & LOAD_DSIWARE))
|
|
{
|
|
if (!LoadDSiWare(loadData->TmdData))
|
|
return false;
|
|
}
|
|
|
|
if (!NDS::Init()) return false;
|
|
GPU::InitRenderer(false);
|
|
biz_render_settings.Soft_Threaded = !!(loadFlags & THREADED_RENDERING);
|
|
GPU::SetRenderSettings(false, biz_render_settings);
|
|
NDS::LoadBIOS();
|
|
if (!isDsi || !(loadFlags & LOAD_DSIWARE))
|
|
{
|
|
if (!NDS::LoadCart(loadData->DsRomData, loadData->DsRomLen, nullptr, 0))
|
|
return false;
|
|
}
|
|
if (!isDsi && (loadFlags & GBA_CART_PRESENT))
|
|
{
|
|
if (!NDS::LoadGBACart(loadData->GbaRomData, loadData->GbaRomLen, loadData->GbaRamData, loadData->GbaRamLen))
|
|
return false;
|
|
}
|
|
if (biz_skip_fw) NDS::SetupDirectBoot("");
|
|
NDS::Start();
|
|
Config::FirmwareOverrideSettings = false;
|
|
return true;
|
|
}
|
|
|
|
namespace NDSCart { extern CartCommon* Cart; }
|
|
extern bool NdsSaveRamIsDirty;
|
|
|
|
EXPORT void PutSaveRam(u8* data, u32 len)
|
|
{
|
|
NDS::LoadSave(data, len);
|
|
NdsSaveRamIsDirty = false;
|
|
}
|
|
|
|
EXPORT void GetSaveRam(u8* data)
|
|
{
|
|
if (NDSCart::Cart)
|
|
{
|
|
NDSCart::Cart->GetSaveData(data);
|
|
NdsSaveRamIsDirty = false;
|
|
}
|
|
}
|
|
|
|
EXPORT s32 GetSaveRamLength()
|
|
{
|
|
return NDSCart::Cart ? NDSCart::Cart->GetSaveLen() : 0;
|
|
}
|
|
|
|
EXPORT bool SaveRamIsDirty()
|
|
{
|
|
return NdsSaveRamIsDirty;
|
|
}
|
|
|
|
/* excerpted from gbatek
|
|
|
|
NDS9 Memory Map
|
|
|
|
00000000h Instruction TCM (32KB) (not moveable) (mirror-able to 1000000h)
|
|
0xxxx000h Data TCM (16KB) (moveable)
|
|
02000000h Main Memory (4MB)
|
|
03000000h Shared WRAM (0KB, 16KB, or 32KB can be allocated to ARM9)
|
|
04000000h ARM9-I/O Ports
|
|
05000000h Standard Palettes (2KB) (Engine A BG/OBJ, Engine B BG/OBJ)
|
|
06000000h VRAM - Engine A, BG VRAM (max 512KB)
|
|
06200000h VRAM - Engine B, BG VRAM (max 128KB)
|
|
06400000h VRAM - Engine A, OBJ VRAM (max 256KB)
|
|
06600000h VRAM - Engine B, OBJ VRAM (max 128KB)
|
|
06800000h VRAM - "LCDC"-allocated (max 656KB)
|
|
07000000h OAM (2KB) (Engine A, Engine B)
|
|
08000000h GBA Slot ROM (max 32MB)
|
|
0A000000h GBA Slot RAM (max 64KB)
|
|
FFFF0000h ARM9-BIOS (32KB) (only 3K used)
|
|
|
|
NDS7 Memory Map
|
|
|
|
00000000h ARM7-BIOS (16KB)
|
|
02000000h Main Memory (4MB)
|
|
03000000h Shared WRAM (0KB, 16KB, or 32KB can be allocated to ARM7)
|
|
03800000h ARM7-WRAM (64KB)
|
|
04000000h ARM7-I/O Ports
|
|
04800000h Wireless Communications Wait State 0 (8KB RAM at 4804000h)
|
|
04808000h Wireless Communications Wait State 1 (I/O Ports at 4808000h)
|
|
06000000h VRAM allocated as Work RAM to ARM7 (max 256K)
|
|
08000000h GBA Slot ROM (max 32MB)
|
|
0A000000h GBA Slot RAM (max 64KB)
|
|
|
|
Further Memory (not mapped to ARM9/ARM7 bus)
|
|
|
|
3D Engine Polygon RAM (52KBx2)
|
|
3D Engine Vertex RAM (72KBx2)
|
|
Firmware (256KB) (built-in serial flash memory)
|
|
GBA-BIOS (16KB) (not used in NDS mode)
|
|
NDS Slot ROM (serial 8bit-bus, max 4GB with default protocol)
|
|
NDS Slot FLASH/EEPROM/FRAM (serial 1bit-bus)
|
|
|
|
*/
|
|
|
|
template<bool arm9>
|
|
static bool SafeToPeek(u32 addr)
|
|
{
|
|
if (arm9)
|
|
{
|
|
switch (addr)
|
|
{
|
|
case 0x04000130:
|
|
case 0x04000131:
|
|
case 0x04000600:
|
|
case 0x04000601:
|
|
case 0x04000602:
|
|
case 0x04000603:
|
|
return false;
|
|
}
|
|
}
|
|
else // arm7
|
|
{
|
|
if (addr >= 0x04800000 && addr <= 0x04810000)
|
|
{
|
|
if (addr & 1) addr--;
|
|
addr &= 0x7FFE;
|
|
if (addr == 0x044 || addr == 0x060)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void ARM9Access(u8* buffer, s64 address, s64 count, bool write)
|
|
{
|
|
if (write)
|
|
{
|
|
void (*Write)(u32, u8) = NDS::ConsoleType == 1 ? DSi::ARM9Write8 : NDS::ARM9Write8;
|
|
while (count--)
|
|
{
|
|
if (address < NDS::ARM9->ITCMSize)
|
|
{
|
|
NDS::ARM9->ITCM[address++ & (ITCMPhysicalSize - 1)] = *buffer++;
|
|
}
|
|
else if ((address & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase)
|
|
{
|
|
NDS::ARM9->DTCM[address++ & (DTCMPhysicalSize - 1)] = *buffer++;
|
|
}
|
|
else
|
|
{
|
|
Write(address++, *buffer++);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u8 (*Read)(u32) = NDS::ConsoleType == 1 ? DSi::ARM9Read8 : NDS::ARM9Read8;
|
|
while (count--)
|
|
{
|
|
if (address < NDS::ARM9->ITCMSize)
|
|
{
|
|
*buffer++ = NDS::ARM9->ITCM[address & (ITCMPhysicalSize - 1)];
|
|
}
|
|
else if ((address & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase)
|
|
{
|
|
*buffer++ = NDS::ARM9->DTCM[address & (DTCMPhysicalSize - 1)];
|
|
}
|
|
else
|
|
{
|
|
*buffer++ = SafeToPeek<true>(address) ? Read(address) : 0;
|
|
}
|
|
|
|
address++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ARM7Access(u8* buffer, s64 address, s64 count, bool write)
|
|
{
|
|
if (write)
|
|
{
|
|
void (*Write)(u32, u8) = NDS::ConsoleType == 1 ? DSi::ARM7Write8 : NDS::ARM7Write8;
|
|
while (count--)
|
|
Write(address++, *buffer++);
|
|
}
|
|
else
|
|
{
|
|
u8 (*Read)(u32) = NDS::ConsoleType == 1 ? DSi::ARM7Read8 : NDS::ARM7Read8;
|
|
while (count--)
|
|
*buffer++ = SafeToPeek<true>(address) ? Read(address) : 0, address++;
|
|
}
|
|
}
|
|
|
|
EXPORT void GetMemoryAreas(MemoryArea *m)
|
|
{
|
|
m[0].Data = NDS::MainRAM;
|
|
m[0].Name = "Main RAM";
|
|
m[0].Size = NDS::MainRAMMaxSize;
|
|
m[0].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY;
|
|
|
|
m[1].Data = NDS::SharedWRAM;
|
|
m[1].Name = "Shared WRAM";
|
|
m[1].Size = NDS::SharedWRAMSize;
|
|
m[1].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE;
|
|
|
|
m[2].Data = NDS::ARM7WRAM;
|
|
m[2].Name = "ARM7 WRAM";
|
|
m[2].Size = NDS::ARM7WRAMSize;
|
|
m[2].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE;
|
|
|
|
m[3].Data = NDS::ARM9->ITCM;
|
|
m[3].Name = "Instruction TCM";
|
|
m[3].Size = ITCMPhysicalSize;
|
|
m[3].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE;
|
|
|
|
m[4].Data = NDS::ARM9->DTCM;
|
|
m[4].Name = "Data TCM";
|
|
m[4].Size = DTCMPhysicalSize;
|
|
m[4].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE;
|
|
|
|
m[5].Data = NDS::ARM9BIOS;
|
|
m[5].Name = "ARM9 BIOS";
|
|
m[5].Size = sizeof NDS::ARM9BIOS;
|
|
m[5].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE;
|
|
|
|
m[6].Data = NDS::ARM7BIOS;
|
|
m[6].Name = "ARM7 BIOS";
|
|
m[6].Size = sizeof NDS::ARM7BIOS;
|
|
m[6].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE;
|
|
|
|
m[7].Data = (void*)ARM9Access;
|
|
m[7].Name = "ARM9 System Bus";
|
|
m[7].Size = 1ull << 32;
|
|
m[7].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_FUNCTIONHOOK;
|
|
|
|
m[8].Data = (void*)ARM7Access;
|
|
m[8].Name = "ARM7 System Bus";
|
|
m[8].Size = 1ull << 32;
|
|
m[8].Flags = MEMORYAREA_FLAGS_WORDSIZE4 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_FUNCTIONHOOK;
|
|
|
|
// fixme: include more shit
|
|
}
|
|
|
|
struct MyFrameInfo : public FrameInfo
|
|
{
|
|
s64 Time;
|
|
u32 Keys;
|
|
u8 TouchX;
|
|
u8 TouchY;
|
|
s8 MicVolume;
|
|
s8 GBALightSensor;
|
|
bool ConsiderAltLag;
|
|
};
|
|
|
|
static s16 biz_mic_input[735];
|
|
|
|
static bool ValidRange(s8 sensor)
|
|
{
|
|
return (sensor >= 0) && (sensor <= 10);
|
|
}
|
|
|
|
static int sampPos = 0;
|
|
|
|
static void MicFeedNoise(s8 vol)
|
|
{
|
|
int sampLen = sizeof mic_blow / sizeof mic_blow[0];
|
|
|
|
for (int i = 0; i < 735; i++)
|
|
{
|
|
biz_mic_input[i] = std::round(mic_blow[sampPos++] * (vol / 100.0));
|
|
if (sampPos >= sampLen) sampPos = 0;
|
|
}
|
|
}
|
|
|
|
static bool RunningFrame = false;
|
|
|
|
EXPORT void FrameAdvance(MyFrameInfo* f)
|
|
{
|
|
RunningFrame = true;
|
|
|
|
if (f->Keys & 0x8000)
|
|
{
|
|
NDS::LoadBIOS();
|
|
if (biz_skip_fw) NDS::SetupDirectBoot("");
|
|
NDS::Start();
|
|
}
|
|
|
|
NDS::SetKeyMask(~f->Keys & 0xFFF);
|
|
|
|
if (f->Keys & 0x1000)
|
|
{
|
|
NDS::TouchScreen(f->TouchX, f->TouchY);
|
|
}
|
|
else
|
|
{
|
|
NDS::ReleaseScreen();
|
|
}
|
|
|
|
if (f->Keys & 0x2000)
|
|
NDS::SetLidClosed(false);
|
|
else if (f->Keys & 0x4000)
|
|
NDS::SetLidClosed(true);
|
|
|
|
MicFeedNoise(f->MicVolume);
|
|
|
|
NDS::MicInputFrame(biz_mic_input, 735);
|
|
|
|
int sensor = GBACart::SetInput(0, 1);
|
|
if (sensor != -1 && ValidRange(f->GBALightSensor))
|
|
{
|
|
if (sensor > f->GBALightSensor)
|
|
{
|
|
while (GBACart::SetInput(0, 1) != f->GBALightSensor) {}
|
|
}
|
|
else if (sensor < f->GBALightSensor)
|
|
{
|
|
while (GBACart::SetInput(1, 1) != f->GBALightSensor) {}
|
|
}
|
|
}
|
|
|
|
biz_time = f->Time;
|
|
NDS::RunFrame();
|
|
dynamic_cast<GPU3D::SoftRenderer*>(GPU3D::CurrentRenderer.get())->StopRenderThread();
|
|
const u32 SingleScreenSize = 256 * 192;
|
|
memcpy(f->VideoBuffer, GPU::Framebuffer[GPU::FrontBuffer][0], SingleScreenSize * sizeof (u32));
|
|
memcpy(f->VideoBuffer + SingleScreenSize, GPU::Framebuffer[GPU::FrontBuffer][1], SingleScreenSize * sizeof (u32));
|
|
f->Width = 256;
|
|
f->Height = 384;
|
|
f->Samples = SPU::GetOutputSize() / 2;
|
|
SPU::ReadOutput(f->SoundBuffer, f->Samples);
|
|
if (f->Samples < 547) // hack
|
|
{
|
|
memset(f->SoundBuffer + (f->Samples * 2), 0, ((547 * 2) - (f->Samples * 2)) * sizeof (u16));
|
|
f->Samples = 547;
|
|
}
|
|
f->Cycles = NDS::GetSysClockCycles(2);
|
|
f->Lagged = NDS::LagFrameFlag;
|
|
// if we want to consider other lag sources, use that lag flag if we haven't unlagged already
|
|
if (f->ConsiderAltLag && NDS::LagFrameFlag)
|
|
{
|
|
f->Lagged = NDS::AltLagFrameFlag;
|
|
}
|
|
|
|
RunningFrame = false;
|
|
}
|
|
|
|
void (*InputCallback)() = nullptr;
|
|
|
|
EXPORT void SetInputCallback(void (*callback)())
|
|
{
|
|
InputCallback = callback;
|
|
}
|
|
|
|
EXPORT void GetRegs(u32* regs)
|
|
{
|
|
NDS::GetRegs(regs);
|
|
}
|
|
|
|
EXPORT void SetReg(s32 ncpu, s32 index, s32 val)
|
|
{
|
|
NDS::SetReg(ncpu, index, val);
|
|
}
|
|
|
|
EXPORT u32 GetCallbackCycleOffset()
|
|
{
|
|
return RunningFrame ? NDS::GetSysClockCycles(2) : 0;
|
|
}
|
|
|
|
void (*ReadCallback)(u32) = nullptr;
|
|
void (*WriteCallback)(u32) = nullptr;
|
|
void (*ExecuteCallback)(u32) = nullptr;
|
|
|
|
EXPORT void SetMemoryCallback(u32 which, void (*callback)(u32 addr))
|
|
{
|
|
switch (which)
|
|
{
|
|
case 0: ReadCallback = callback; break;
|
|
case 1: WriteCallback = callback; break;
|
|
case 2: ExecuteCallback = callback; break;
|
|
}
|
|
}
|
|
|
|
TraceMask_t TraceMask = TRACE_NONE;
|
|
static void (*TraceCallback)(TraceMask_t, u32, u32*, char*, u32) = nullptr;
|
|
#define TRACE_STRING_LENGTH 80
|
|
typedef enum {
|
|
ARMv4T, //ARM v4, THUMB v1
|
|
ARMv5TE, //ARM v5, THUMB v2
|
|
ARMv6, //ARM v6, THUMB v3
|
|
} ARMARCH; //only 32-bit legacy architectures with THUMB support
|
|
extern "C" u32 Disassemble_thumb(u32 code, char str[TRACE_STRING_LENGTH], ARMARCH tv);
|
|
extern "C" void Disassemble_arm(u32 code, char str[TRACE_STRING_LENGTH], ARMARCH av);
|
|
|
|
void TraceTrampoline(TraceMask_t type, u32* regs, u32 opcode)
|
|
{
|
|
static char disasm[TRACE_STRING_LENGTH];
|
|
memset(disasm, 0, sizeof disasm);
|
|
switch (type) {
|
|
case TRACE_ARM7_THUMB: Disassemble_thumb(opcode, disasm, ARMv4T); break;
|
|
case TRACE_ARM7_ARM: Disassemble_arm(opcode, disasm, ARMv4T); break;
|
|
case TRACE_ARM9_THUMB: Disassemble_thumb(opcode, disasm, ARMv5TE); break;
|
|
case TRACE_ARM9_ARM: Disassemble_arm(opcode, disasm, ARMv5TE); break;
|
|
default: __builtin_unreachable();
|
|
}
|
|
TraceCallback(type, opcode, regs, disasm, NDS::GetSysClockCycles(2));
|
|
}
|
|
|
|
EXPORT void SetTraceCallback(void (*callback)(TraceMask_t mask, u32 opcode, u32* regs, char* disasm, u32 cyclesOff), TraceMask_t mask)
|
|
{
|
|
TraceCallback = callback;
|
|
TraceMask = callback ? mask : TRACE_NONE;
|
|
}
|
|
|
|
EXPORT void GetDisassembly(TraceMask_t type, u32 opcode, char* ret)
|
|
{
|
|
static char disasm[TRACE_STRING_LENGTH];
|
|
memset(disasm, 0, sizeof disasm);
|
|
switch (type) {
|
|
case TRACE_ARM7_THUMB: Disassemble_thumb(opcode, disasm, ARMv4T); break;
|
|
case TRACE_ARM7_ARM: Disassemble_arm(opcode, disasm, ARMv4T); break;
|
|
case TRACE_ARM9_THUMB: Disassemble_thumb(opcode, disasm, ARMv5TE); break;
|
|
case TRACE_ARM9_ARM: Disassemble_arm(opcode, disasm, ARMv5TE); break;
|
|
default: __builtin_unreachable();
|
|
}
|
|
memcpy(ret, disasm, TRACE_STRING_LENGTH);
|
|
}
|
|
|
|
|
|
namespace Platform
|
|
{
|
|
extern uintptr_t FrameThreadProc;
|
|
extern void (*ThreadStartCallback)();
|
|
}
|
|
|
|
EXPORT uintptr_t GetFrameThreadProc()
|
|
{
|
|
return Platform::FrameThreadProc;
|
|
}
|
|
|
|
EXPORT void SetThreadStartCallback(void (*callback)())
|
|
{
|
|
Platform::ThreadStartCallback = callback;
|
|
}
|
|
|
|
EXPORT u32 GetNANDSize()
|
|
{
|
|
if (NANDFilePtr)
|
|
{
|
|
NANDFilePtr->seekg(0, std::ios::end);
|
|
return NANDFilePtr->tellg();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
EXPORT void GetNANDData(char* buf)
|
|
{
|
|
if (NANDFilePtr)
|
|
{
|
|
u32 sz = GetNANDSize();
|
|
NANDFilePtr->seekg(0);
|
|
NANDFilePtr->read(buf, sz);
|
|
}
|
|
}
|
|
|
|
namespace GPU { void ResetVRAMCache(); }
|
|
|
|
EXPORT void ResetCaches()
|
|
{
|
|
GPU::ResetVRAMCache();
|
|
}
|