parent
391ad8c95e
commit
70d98deff4
|
@ -47,6 +47,12 @@ add_library(core STATIC
|
|||
version.h
|
||||
Wifi.cpp
|
||||
WifiAP.cpp
|
||||
|
||||
HLE.cpp
|
||||
HLE_Retail/IPC.cpp
|
||||
HLE_Retail/Sound_Nitro.cpp
|
||||
HLE_Retail/Sound_Peach.cpp
|
||||
HLE_Retail/Wifi.cpp
|
||||
|
||||
fatfs/diskio.c
|
||||
fatfs/ff.c
|
||||
|
|
|
@ -57,6 +57,9 @@ void Reset()
|
|||
Index = 0;
|
||||
Data = 0;
|
||||
|
||||
TouchX = 0;
|
||||
TouchY = 0xFFF;
|
||||
|
||||
memset(Bank3Regs, 0, 0x80);
|
||||
Bank3Regs[0x02] = 0x18;
|
||||
Bank3Regs[0x03] = 0x87;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <string.h>
|
||||
#include "NDS.h"
|
||||
#include "GPU.h"
|
||||
#include "HLE.h"
|
||||
|
||||
#ifdef JIT_ENABLED
|
||||
#include "ARMJIT.h"
|
||||
|
@ -1186,6 +1187,8 @@ void StartScanline(u32 line)
|
|||
}
|
||||
}
|
||||
|
||||
HLE::StartScanline(line);
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_LCD, true, HBLANK_CYCLES, StartHBlank, line);
|
||||
}
|
||||
|
||||
|
|
|
@ -562,7 +562,7 @@ void DoSavestate(Savestate* file)
|
|||
file->Bool32(&poly->IsShadowMask);
|
||||
file->Bool32(&poly->IsShadow);
|
||||
|
||||
if (file->IsAtLeastVersion(4, 1))
|
||||
if (file->IsAtleastVersion(4, 1))
|
||||
file->Var32((u32*)&poly->Type);
|
||||
else
|
||||
poly->Type = 0;
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
|
||||
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 "NDS.h"
|
||||
#include "HLE.h"
|
||||
|
||||
#include "HLE_Retail/IPC.h"
|
||||
|
||||
|
||||
namespace HLE
|
||||
{
|
||||
|
||||
void Reset()
|
||||
{
|
||||
Retail::Reset();
|
||||
}
|
||||
|
||||
|
||||
void StartScanline(u32 line)
|
||||
{
|
||||
return Retail::StartScanline(line);
|
||||
}
|
||||
|
||||
|
||||
void OnIPCSync()
|
||||
{
|
||||
// TODO: select retail or homebrew HLE
|
||||
|
||||
return Retail::OnIPCSync();
|
||||
}
|
||||
|
||||
void OnIPCRequest()
|
||||
{
|
||||
// TODO: select retail or homebrew HLE
|
||||
|
||||
return Retail::OnIPCRequest();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
|
||||
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 HLE_H
|
||||
#define HLE_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace HLE
|
||||
{
|
||||
|
||||
void Reset();
|
||||
|
||||
void StartScanline(u32 line);
|
||||
|
||||
void OnIPCSync();
|
||||
void OnIPCRequest();
|
||||
|
||||
}
|
||||
|
||||
#endif // HLE_H
|
|
@ -0,0 +1,750 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
|
||||
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 "../NDS.h"
|
||||
#include "../NDSCart.h"
|
||||
#include "../HLE.h"
|
||||
#include "../FIFO.h"
|
||||
#include "../SPU.h"
|
||||
|
||||
#include "IPC.h"
|
||||
#include "Sound_Nitro.h"
|
||||
#include "Sound_Peach.h"
|
||||
#include "Wifi.h"
|
||||
|
||||
|
||||
namespace NDS
|
||||
{
|
||||
extern u16 IPCSync9, IPCSync7;
|
||||
extern u16 IPCFIFOCnt9, IPCFIFOCnt7;
|
||||
extern FIFO<u32, 16> IPCFIFO9; // FIFO in which the ARM9 writes
|
||||
extern FIFO<u32, 16> IPCFIFO7;
|
||||
}
|
||||
|
||||
namespace SPI_Firmware
|
||||
{
|
||||
extern u8* Firmware;
|
||||
extern u32 FirmwareMask;
|
||||
u8 Read();
|
||||
void Write(u8 val, u32 hold);
|
||||
}
|
||||
|
||||
namespace SPI_Powerman
|
||||
{
|
||||
extern u8 Registers[8];
|
||||
extern u8 RegMasks[8];
|
||||
u8 Read();
|
||||
void Write(u8 val, u32 hold);
|
||||
}
|
||||
|
||||
namespace SPI_TSC
|
||||
{
|
||||
extern u16 TouchX, TouchY;
|
||||
}
|
||||
|
||||
namespace RTC
|
||||
{
|
||||
extern u32 InputPos;
|
||||
extern u8 Output[8];
|
||||
void ByteIn(u8 val);
|
||||
}
|
||||
|
||||
|
||||
namespace HLE
|
||||
{
|
||||
namespace Retail
|
||||
{
|
||||
|
||||
u16 FW_Data[16];
|
||||
|
||||
bool TS_Inited;
|
||||
u16 TS_Data[16];
|
||||
u16 TS_NumSamples;
|
||||
u16 TS_SamplePos[4];
|
||||
|
||||
int Sound_Engine;
|
||||
|
||||
u16 PM_Data[16];
|
||||
|
||||
u16 Mic_Data[16];
|
||||
|
||||
u32 SM_Command;
|
||||
u32 SM_DataPos;
|
||||
u32 SM_Buffer;
|
||||
|
||||
|
||||
void SendIPCSync(u8 val)
|
||||
{
|
||||
NDS::IPCSync9 = (NDS::IPCSync9 & 0xFFF0) | (val & 0xF);
|
||||
}
|
||||
|
||||
void SendIPCReply(u32 service, u32 data, u32 flag)
|
||||
{
|
||||
u32 val = (service & 0x1F) | (data << 6) | ((flag & 0x1) << 5);
|
||||
|
||||
if (NDS::IPCFIFO7.IsFull())
|
||||
printf("!!!! IPC FIFO FULL\n");
|
||||
else
|
||||
{
|
||||
bool wasempty = NDS::IPCFIFO7.IsEmpty();
|
||||
NDS::IPCFIFO7.Write(val);
|
||||
if ((NDS::IPCFIFOCnt9 & 0x0400) && wasempty)
|
||||
NDS::SetIRQ(0, NDS::IRQ_IPCRecv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Reset()
|
||||
{
|
||||
memset(FW_Data, 0, sizeof(FW_Data));
|
||||
|
||||
TS_Inited = false;
|
||||
memset(TS_Data, 0, sizeof(TS_Data));
|
||||
TS_NumSamples = 0;
|
||||
memset(TS_SamplePos, 0, sizeof(TS_SamplePos));
|
||||
|
||||
Sound_Engine = -1;
|
||||
|
||||
memset(PM_Data, 0, sizeof(PM_Data));
|
||||
|
||||
memset(Mic_Data, 0, sizeof(Mic_Data));
|
||||
|
||||
SM_Command = 0;
|
||||
SM_DataPos = 0;
|
||||
SM_Buffer = 0;
|
||||
|
||||
Wifi::Reset();
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_HLE_PollInput, true, 134016, PollInput, 0);
|
||||
}
|
||||
|
||||
|
||||
void Touchscreen_Sample()
|
||||
{
|
||||
u32 ts = NDS::ARM7Read16(0x027FFFAA) | (NDS::ARM7Read16(0x027FFFAC) << 16);
|
||||
|
||||
if (SPI_TSC::TouchY == 0xFFF)
|
||||
{
|
||||
ts &= 0xFE000000;
|
||||
ts |= 0x06000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
ts &= 0xF9000000;
|
||||
ts |= (SPI_TSC::TouchX & 0xFFF);
|
||||
ts |= ((SPI_TSC::TouchY & 0xFFF) << 12);
|
||||
ts |= 0x01000000;
|
||||
}
|
||||
|
||||
NDS::ARM7Write16(0x027FFFAA, ts & 0xFFFF);
|
||||
NDS::ARM7Write16(0x027FFFAC, ts >> 16);
|
||||
}
|
||||
|
||||
void StartScanline(u32 line)
|
||||
{
|
||||
for (int i = 0; i < TS_NumSamples; i++)
|
||||
{
|
||||
if (line == TS_SamplePos[i])
|
||||
{
|
||||
Touchscreen_Sample();
|
||||
SendIPCReply(0x6, 0x03009000 | i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PollInput(u32 param)
|
||||
{
|
||||
u16 input = (NDS::KeyInput >> 6) & 0x2C00;
|
||||
if (NDS::IsLidClosed()) input |= (1<<15);
|
||||
|
||||
NDS::ARM7Write16(0x027FFFA8, input);
|
||||
|
||||
NDS::ScheduleEvent(NDS::Event_HLE_PollInput, true, 134016, PollInput, 0);
|
||||
}
|
||||
|
||||
|
||||
void OnIPCSync()
|
||||
{
|
||||
u8 val = NDS::IPCSync7 & 0xF;
|
||||
|
||||
if (val < 5)
|
||||
{
|
||||
SendIPCSync(val+1);
|
||||
}
|
||||
else if (val == 5)
|
||||
{
|
||||
SendIPCSync(0);
|
||||
|
||||
// presumably ARM7-side ready flags for each IPC service
|
||||
NDS::ARM7Write32(0x027FFF8C, 0x0000FFF0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnIPCRequest_Firmware(u32 data)
|
||||
{
|
||||
if (data & (1<<25))
|
||||
{
|
||||
memset(FW_Data, 0, sizeof(FW_Data));
|
||||
}
|
||||
|
||||
FW_Data[(data >> 16) & 0xF] = data & 0xFFFF;
|
||||
|
||||
if (!(data & (1<<24))) return;
|
||||
|
||||
u32 cmd = (FW_Data[0] >> 8) - 0x20;
|
||||
switch (cmd)
|
||||
{
|
||||
case 0: // write enable
|
||||
SPI_Firmware::Write(0x06, false);
|
||||
SendIPCReply(0x4, 0x0300A000);
|
||||
break;
|
||||
|
||||
case 1: // write disable
|
||||
SPI_Firmware::Write(0x04, false);
|
||||
SendIPCReply(0x4, 0x0300A100);
|
||||
break;
|
||||
|
||||
case 2: // read status register
|
||||
{
|
||||
u32 addr = ((FW_Data[0] & 0xFF) << 24) | (FW_Data[1] << 8) | ((FW_Data[2] >> 8) & 0xFF);
|
||||
if (addr < 0x02000000 || addr >= 0x02800000)
|
||||
{
|
||||
SendIPCReply(0x4, 0x0300A202);
|
||||
break;
|
||||
}
|
||||
|
||||
SPI_Firmware::Write(0x05, true);
|
||||
SPI_Firmware::Write(0, false);
|
||||
u8 ret = SPI_Firmware::Read();
|
||||
NDS::ARM7Write8(addr, ret);
|
||||
|
||||
SendIPCReply(0x4, 0x0300A200);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // firmware read
|
||||
{
|
||||
u32 addr = (FW_Data[4] << 16) | FW_Data[5];
|
||||
if (addr < 0x02000000 || addr >= 0x02800000)
|
||||
{
|
||||
SendIPCReply(0x4, 0x0300A302);
|
||||
break;
|
||||
}
|
||||
|
||||
u32 src = ((FW_Data[0] & 0xFF) << 16) | FW_Data[1];
|
||||
u32 len = (FW_Data[2] << 16) | FW_Data[3];
|
||||
|
||||
for (u32 i = 0; i < len; i++)
|
||||
{
|
||||
u8 val = SPI_Firmware::Firmware[src & SPI_Firmware::FirmwareMask];
|
||||
NDS::ARM7Write8(addr, val);
|
||||
src++;
|
||||
addr++;
|
||||
}
|
||||
|
||||
SendIPCReply(0x4, 0x0300A300);
|
||||
}
|
||||
break;
|
||||
|
||||
case 5: // firmware write
|
||||
{
|
||||
u32 addr = (FW_Data[3] << 16) | FW_Data[4];
|
||||
if (addr < 0x02000000 || addr >= 0x02800000)
|
||||
{
|
||||
SendIPCReply(0x4, 0x0300A502);
|
||||
break;
|
||||
}
|
||||
|
||||
u32 dst = ((FW_Data[0] & 0xFF) << 16) | FW_Data[1];
|
||||
u32 len = FW_Data[2];
|
||||
|
||||
for (u32 i = 0; i < len; i++)
|
||||
{
|
||||
u8 val = NDS::ARM7Read8(addr);
|
||||
SPI_Firmware::Firmware[dst & SPI_Firmware::FirmwareMask] = val;
|
||||
dst++;
|
||||
addr++;
|
||||
}
|
||||
|
||||
// hack: trigger firmware save
|
||||
SPI_Firmware::Write(0x0A, true);
|
||||
SPI_Firmware::Write(0, false);
|
||||
|
||||
SendIPCReply(0x4, 0x0300A500);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unknown FW request %08X (%04X)\n", data, FW_Data[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RTC_Read(u8 reg, u32 addr, u32 len)
|
||||
{
|
||||
RTC::InputPos = 0;
|
||||
RTC::ByteIn(reg | 0x80);
|
||||
|
||||
for (u32 i = 0; i < len; i++)
|
||||
{
|
||||
NDS::ARM7Write8(addr+i, RTC::Output[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void OnIPCRequest_RTC(u32 data)
|
||||
{
|
||||
u32 cmd = (data >> 8) & 0x7F;
|
||||
|
||||
if ((cmd >= 2 && cmd <= 15) ||
|
||||
(cmd >= 26 && cmd <= 34) ||
|
||||
(cmd >= 42))
|
||||
{
|
||||
SendIPCReply(0x5, 0x8001 | (cmd << 8));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case 0x11: // read date
|
||||
RTC_Read(0x20, 0x027FFDE8, 4);
|
||||
SendIPCReply(0x5, 0x9100);
|
||||
break;
|
||||
|
||||
case 0x12: // read time
|
||||
RTC_Read(0x60, 0x027FFDE8+4, 3);
|
||||
SendIPCReply(0x5, 0x9200);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("HLE: unknown RTC command %02X (%08X)\n", cmd, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnIPCRequest_Touchscreen(u32 data)
|
||||
{
|
||||
if (data & (1<<25))
|
||||
{
|
||||
memset(TS_Data, 0, sizeof(TS_Data));
|
||||
}
|
||||
|
||||
TS_Data[(data >> 16) & 0xF] = data & 0xFFFF;
|
||||
|
||||
if (!(data & (1<<24))) return;
|
||||
|
||||
switch (TS_Data[0] >> 8)
|
||||
{
|
||||
case 0: // manual sampling
|
||||
{
|
||||
Touchscreen_Sample();
|
||||
SendIPCReply(0x6, 0x03008000);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // setup auto sampling
|
||||
{
|
||||
if (TS_Inited)
|
||||
{
|
||||
SendIPCReply(0x6, 0x03008103);
|
||||
break;
|
||||
}
|
||||
|
||||
// samples per frame
|
||||
u8 num = TS_Data[0] & 0xFF;
|
||||
if (num == 0 || num > 4)
|
||||
{
|
||||
SendIPCReply(0x6, 0x03008102);
|
||||
break;
|
||||
}
|
||||
|
||||
// offset in scanlines for first sample
|
||||
u16 offset = TS_Data[1];
|
||||
if (offset >= 263)
|
||||
{
|
||||
SendIPCReply(0x6, 0x03008102);
|
||||
break;
|
||||
}
|
||||
|
||||
TS_NumSamples = num;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
u32 ypos = (offset + ((i * 263) / num)) % 263;
|
||||
TS_SamplePos[i] = ypos;
|
||||
}
|
||||
|
||||
TS_Inited = true;
|
||||
SendIPCReply(0x6, 0x03008100);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // stop autosampling
|
||||
{
|
||||
// TODO CHECKME
|
||||
// Mario Kart uses this
|
||||
// but this here is wrong
|
||||
TS_NumSamples = 0;
|
||||
SendIPCReply(0x6, 0x03008200);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // manual sampling but with condition (TODO)
|
||||
{
|
||||
Touchscreen_Sample();
|
||||
SendIPCReply(0x6, 0x03008300);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unknown TS request %08X\n", data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnIPCRequest_Sound(u32 data)
|
||||
{
|
||||
if (Sound_Engine == -1)
|
||||
{
|
||||
if (data >= 0x02000000)
|
||||
{
|
||||
Sound_Engine = 0;
|
||||
Sound_Nitro::Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
Sound_Engine = 1;
|
||||
Sound_Peach::Reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (Sound_Engine == 0) return Sound_Nitro::OnIPCRequest(data);
|
||||
if (Sound_Engine == 1) return Sound_Peach::OnIPCRequest(data);
|
||||
}
|
||||
|
||||
void OnIPCRequest_Powerman(u32 data)
|
||||
{
|
||||
if (data & (1<<25))
|
||||
{
|
||||
memset(PM_Data, 0, sizeof(PM_Data));
|
||||
}
|
||||
|
||||
PM_Data[(data >> 16) & 0xF] = data & 0xFFFF;
|
||||
|
||||
if (!(data & (1<<24))) return;
|
||||
|
||||
u32 cmd = (PM_Data[0] >> 8) - 0x60;
|
||||
printf("PM CMD %04X %04X\n", PM_Data[0], PM_Data[1]);
|
||||
switch (cmd)
|
||||
{
|
||||
case 3: // utility
|
||||
{
|
||||
switch (PM_Data[1] & 0xFF)
|
||||
{
|
||||
case 1: // power LED: steady
|
||||
SPI_Powerman::Registers[0] &= ~0x10;
|
||||
break;
|
||||
case 2: // power LED: fast blink
|
||||
SPI_Powerman::Registers[0] |= 0x30;
|
||||
break;
|
||||
case 3: // power LED: slow blink
|
||||
SPI_Powerman::Registers[0] &= ~0x20;
|
||||
SPI_Powerman::Registers[0] |= 0x10;
|
||||
break;
|
||||
case 4: // lower backlights on
|
||||
SPI_Powerman::Registers[0] |= 0x04;
|
||||
break;
|
||||
case 5: // lower backlights off
|
||||
SPI_Powerman::Registers[0] &= ~0x04;
|
||||
break;
|
||||
case 6: // upper backlights on
|
||||
SPI_Powerman::Registers[0] |= 0x08;
|
||||
break;
|
||||
case 7: // upper backlights off
|
||||
SPI_Powerman::Registers[0] &= ~0x08;
|
||||
break;
|
||||
case 8: // backlights on
|
||||
SPI_Powerman::Registers[0] |= 0x0C;
|
||||
break;
|
||||
case 9: // backlights off
|
||||
SPI_Powerman::Registers[0] &= ~0x0C;
|
||||
break;
|
||||
case 10: // sound amp on
|
||||
SPI_Powerman::Registers[0] |= 0x01;
|
||||
break;
|
||||
case 11: // sound amp off
|
||||
SPI_Powerman::Registers[0] &= ~0x01;
|
||||
break;
|
||||
case 12: // sound mute on
|
||||
SPI_Powerman::Registers[0] |= 0x02;
|
||||
break;
|
||||
case 13: // sound mute off
|
||||
SPI_Powerman::Registers[0] &= ~0x02;
|
||||
break;
|
||||
case 14: // shutdown
|
||||
SPI_Powerman::Registers[0] &= ~0x01;
|
||||
SPI_Powerman::Registers[0] |= 0x40;
|
||||
NDS::Stop();
|
||||
break;
|
||||
case 15: // ????
|
||||
SPI_Powerman::Registers[0] &= ~0x40;
|
||||
break;
|
||||
}
|
||||
|
||||
SendIPCReply(0x8, 0x0300E300);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // write register
|
||||
{
|
||||
u8 addr = PM_Data[0] & 0xFF;
|
||||
u8 val = PM_Data[1] & 0xFF;
|
||||
SPI_Powerman::Write(addr & 0x7F, true);
|
||||
SPI_Powerman::Write(val, false);
|
||||
SendIPCReply(0x8, 0x03008000 | (((PM_Data[1] + 0x70) & 0xFF) << 8));
|
||||
}
|
||||
break;
|
||||
|
||||
case 5: // read register
|
||||
{
|
||||
u8 addr = PM_Data[0] & 0xFF;
|
||||
SPI_Powerman::Write((addr & 0x7F) | 0x80, true);
|
||||
SPI_Powerman::Write(0, false);
|
||||
u8 ret = SPI_Powerman::Read();
|
||||
SendIPCReply(0x8, 0x03008000 | ret | (((PM_Data[1] + 0x70) & 0xFF) << 8));
|
||||
}
|
||||
break;
|
||||
|
||||
case 6:
|
||||
{
|
||||
// TODO
|
||||
|
||||
SendIPCReply(0x8, 0x03008000 | (((PM_Data[1] + 0x70) & 0xFF) << 8));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnIPCRequest_Mic(u32 data)
|
||||
{
|
||||
if (data & (1<<25))
|
||||
{
|
||||
memset(Mic_Data, 0, sizeof(Mic_Data));
|
||||
}
|
||||
|
||||
Mic_Data[(data >> 16) & 0xF] = data & 0xFFFF;
|
||||
|
||||
if (!(data & (1<<24))) return;
|
||||
|
||||
u32 cmd = (Mic_Data[0] >> 8) - 0x40;
|
||||
switch (cmd)
|
||||
{
|
||||
case 0: // sampling?
|
||||
{
|
||||
// TODO
|
||||
|
||||
SendIPCReply(0x9, 0x0300C000);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("unknown mic request %08X\n", data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnIPCRequest_CartSave(u32 data)
|
||||
{
|
||||
if (SM_DataPos == 0)
|
||||
SM_Command = data;
|
||||
|
||||
switch (SM_Command)
|
||||
{
|
||||
case 0:
|
||||
if (SM_DataPos == 0) break;
|
||||
if (SM_DataPos == 1)
|
||||
{
|
||||
SM_Buffer = data;
|
||||
SendIPCReply(0xB, 0x1, 1);
|
||||
SM_DataPos = 0;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // identify savemem
|
||||
// TODO
|
||||
SendIPCReply(0xB, 0x1, 1);
|
||||
SM_DataPos = 0;
|
||||
return;
|
||||
|
||||
case 6: // read
|
||||
{
|
||||
u32 offset = NDS::ARM7Read32(SM_Buffer+0x0C);
|
||||
u32 dst = NDS::ARM7Read32(SM_Buffer+0x10);
|
||||
u32 len = NDS::ARM7Read32(SM_Buffer+0x14);
|
||||
|
||||
u8* mem = NDSCart::GetSaveMemory();
|
||||
u32 memlen = NDSCart::GetSaveMemoryLength();
|
||||
if (mem && memlen)
|
||||
{
|
||||
memlen--;
|
||||
|
||||
for (u32 i = 0; i < len; i++)
|
||||
{
|
||||
NDS::ARM7Write8(dst, mem[offset & memlen]);
|
||||
dst++;
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
SendIPCReply(0xB, 0x1, 1);
|
||||
SM_DataPos = 0;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 8: // write
|
||||
{
|
||||
u32 src = NDS::ARM7Read32(SM_Buffer+0x0C);
|
||||
u32 offset = NDS::ARM7Read32(SM_Buffer+0x10);
|
||||
u32 len = NDS::ARM7Read32(SM_Buffer+0x14);
|
||||
|
||||
printf("IPC CMD8: %08X %08X %08X\n", src, offset, len);
|
||||
|
||||
SendIPCReply(0xB, 0x1, 1);
|
||||
SM_DataPos = 0;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 9: // verify
|
||||
{
|
||||
u32 src = NDS::ARM7Read32(SM_Buffer+0x0C);
|
||||
u32 offset = NDS::ARM7Read32(SM_Buffer+0x10);
|
||||
u32 len = NDS::ARM7Read32(SM_Buffer+0x14);
|
||||
|
||||
printf("IPC CMD9: %08X %08X %08X\n", src, offset, len);
|
||||
|
||||
// writes result to first word of IPC buffer
|
||||
|
||||
SendIPCReply(0xB, 0x1, 1);
|
||||
SM_DataPos = 0;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("SAVEMEM: unknown cmd %08X\n", SM_Command);
|
||||
break;
|
||||
}
|
||||
|
||||
SM_DataPos++;
|
||||
}
|
||||
|
||||
void OnIPCRequest_Cart(u32 data)
|
||||
{
|
||||
if ((data & 0x3F) == 1)
|
||||
{
|
||||
// TODO other shito?
|
||||
|
||||
SendIPCReply(0xD, 0x1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// do something else
|
||||
}
|
||||
/*if (data & 0x1)
|
||||
{
|
||||
// init
|
||||
|
||||
SendIPCReply(0xD, 0x1);
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
void OnIPCRequest()
|
||||
{
|
||||
u32 val = NDS::IPCFIFO9.Read();
|
||||
|
||||
if (NDS::IPCFIFO9.IsEmpty() && (NDS::IPCFIFOCnt9 & 0x0004))
|
||||
NDS::SetIRQ(0, NDS::IRQ_IPCSendDone);
|
||||
|
||||
u32 service = val & 0x1F;
|
||||
u32 data = val >> 6;
|
||||
u32 flag = (val >> 5) & 0x1;
|
||||
//printf("IPC %08X\n", val);
|
||||
switch (service)
|
||||
{
|
||||
case 0x4: // firmware
|
||||
if (flag) break;
|
||||
OnIPCRequest_Firmware(data);
|
||||
break;
|
||||
|
||||
case 0x5: // RTC
|
||||
if (flag) break;
|
||||
OnIPCRequest_RTC(data);
|
||||
break;
|
||||
|
||||
case 0x6: // touchscreen
|
||||
if (flag) break;
|
||||
OnIPCRequest_Touchscreen(data);
|
||||
break;
|
||||
|
||||
case 0x7: // sound
|
||||
OnIPCRequest_Sound(data);
|
||||
break;
|
||||
|
||||
case 0x8: // powerman
|
||||
if (flag) break;
|
||||
OnIPCRequest_Powerman(data);
|
||||
break;
|
||||
|
||||
case 0x9: // mic
|
||||
if (flag) break;
|
||||
OnIPCRequest_Mic(data);
|
||||
break;
|
||||
|
||||
case 0xA: // wifi
|
||||
if (flag) break;
|
||||
Wifi::OnIPCRequest(data);
|
||||
break;
|
||||
|
||||
case 0xB: // cart savemem
|
||||
if (!flag) break;
|
||||
OnIPCRequest_CartSave(data);
|
||||
break;
|
||||
|
||||
case 0xD: // cart
|
||||
OnIPCRequest_Cart(data);
|
||||
break;
|
||||
|
||||
case 0xF:
|
||||
if (data == 0x10000)
|
||||
{
|
||||
SendIPCReply(0xF, 0x10000);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("HLE: unknown IPC request %08X service=%02X data=%08X flag=%d\n", val, service, data, flag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
|
||||
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 HLE_RETAIL_IPC_H
|
||||
#define HLE_RETAIL_IPC_H
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
namespace HLE
|
||||
{
|
||||
namespace Retail
|
||||
{
|
||||
|
||||
void Reset();
|
||||
|
||||
void SendIPCReply(u32 service, u32 data, u32 flag = 0);
|
||||
|
||||
void StartScanline(u32 line);
|
||||
void PollInput(u32 param);
|
||||
void SoundProcess(u32 param);
|
||||
void SoundPeach(u32 param);
|
||||
void SoundAlarm(u32 num);
|
||||
|
||||
void OnIPCSync();
|
||||
void OnIPCRequest();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HLE_RETAIL_IPC_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
|
||||
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 SOUND_NITRO_H
|
||||
#define SOUND_NITRO_H
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
namespace HLE
|
||||
{
|
||||
namespace Retail
|
||||
{
|
||||
namespace Sound_Nitro
|
||||
{
|
||||
|
||||
void Reset();
|
||||
|
||||
void OnAlarm(u32 param);
|
||||
void Process(u32 param);
|
||||
|
||||
void OnIPCRequest(u32 data);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SOUND_NITRO_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
|
||||
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 SOUND_PEACH_H
|
||||
#define SOUND_PEACH_H
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
namespace HLE
|
||||
{
|
||||
namespace Retail
|
||||
{
|
||||
namespace Sound_Peach
|
||||
{
|
||||
|
||||
void Reset();
|
||||
|
||||
void Process(u32 param);
|
||||
|
||||
void OnIPCRequest(u32 data);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SOUND_PEACH_H
|
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
|
||||
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 "../NDS.h"
|
||||
#include "../NDSCart.h"
|
||||
#include "../HLE.h"
|
||||
#include "../FIFO.h"
|
||||
|
||||
#include "IPC.h"
|
||||
#include "Wifi.h"
|
||||
|
||||
|
||||
namespace HLE
|
||||
{
|
||||
namespace Retail
|
||||
{
|
||||
namespace Wifi
|
||||
{
|
||||
|
||||
struct IPCReply
|
||||
{
|
||||
u16 Command;
|
||||
u16 Status;
|
||||
int NumExtra;
|
||||
u16 Extra[16];
|
||||
};
|
||||
|
||||
FIFO<IPCReply, 16> IPCReplyQueue;
|
||||
|
||||
u32 SharedMem[2];
|
||||
|
||||
u8 MAC[6];
|
||||
|
||||
u16 BeaconInterval;
|
||||
u8 BeaconFrame[1024];
|
||||
|
||||
void WifiIPCReply(u16 cmd, u16 status, int numextra=0, u16* extra=nullptr);
|
||||
|
||||
|
||||
void Reset()
|
||||
{
|
||||
IPCReplyQueue.Clear();
|
||||
|
||||
SharedMem[0] = 0;
|
||||
SharedMem[1] = 0;
|
||||
|
||||
u8* mac = SPI_Firmware::GetWifiMAC();
|
||||
memcpy(MAC, mac, 6);
|
||||
|
||||
BeaconInterval = 0;
|
||||
memset(BeaconFrame, 0, sizeof(BeaconFrame));
|
||||
|
||||
u16 chanmask = 0x2082;
|
||||
NDS::ARM7Write16(0x027FFCFA, chanmask);
|
||||
}
|
||||
|
||||
|
||||
void StartHostComm()
|
||||
{
|
||||
memset(BeaconFrame, 0xFF, sizeof(BeaconFrame));
|
||||
|
||||
u32 paramblock = SharedMem[1] + 0xE8;
|
||||
u8* ptr = &BeaconFrame[0];
|
||||
|
||||
u32 dd_addr = NDS::ARM7Read32(paramblock + 0x00);
|
||||
u16 dd_len = NDS::ARM7Read16(paramblock + 0x04);
|
||||
|
||||
u32 gameid = NDS::ARM7Read32(paramblock + 0x08);
|
||||
u16 streamcode = NDS::ARM7Read16(paramblock + 0x0C);
|
||||
|
||||
BeaconInterval = NDS::ARM7Read16(paramblock + 0x18);
|
||||
u16 channel = NDS::ARM7Read16(paramblock + 0x32);
|
||||
|
||||
u8 beacontype = 0;
|
||||
if (NDS::ARM7Read16(paramblock + 0x0E) & 0x1) beacontype |= (1<<0);
|
||||
if (NDS::ARM7Read16(paramblock + 0x12) & 0x1) beacontype |= (1<<1);
|
||||
if (NDS::ARM7Read16(paramblock + 0x14) & 0x1) beacontype |= (1<<2);
|
||||
if (NDS::ARM7Read16(paramblock + 0x16) & 0x1) beacontype |= (1<<3);
|
||||
|
||||
// TX header (required by comm layer)
|
||||
*(u16*)ptr = 0; ptr += 2;
|
||||
*(u16*)ptr = 0; ptr += 2;
|
||||
*(u16*)ptr = 0; ptr += 2;
|
||||
*(u16*)ptr = 0; ptr += 2;
|
||||
*(u16*)ptr = 0x14; ptr += 2; // TX rate
|
||||
*(u16*)ptr = 0; ptr += 2; // length, set later
|
||||
|
||||
// 802.11 header
|
||||
*(u16*)ptr = 0x0080; ptr += 2; // frame control
|
||||
*(u16*)ptr = 0; ptr += 2; // duration
|
||||
memset(ptr, 0xFF, 6); ptr += 6; // destination MAC
|
||||
memcpy(ptr, MAC, 6); ptr += 6; // source MAC
|
||||
memcpy(ptr, MAC, 6); ptr += 6; // BSSID
|
||||
*(u16*)ptr = 0; ptr += 2; // sequence number, set later
|
||||
|
||||
// beacon body
|
||||
*(u64*)ptr = 0; ptr += 8; // timestamp, set later
|
||||
*(u16*)ptr = BeaconInterval; ptr += 2; // beacon interval
|
||||
*(u16*)ptr = 0x0021; ptr += 2; // capability
|
||||
|
||||
// SSID
|
||||
/* *ptr++ = 0x00; *ptr++ = 0x20;
|
||||
*(u16*)ptr = NDS::ARM7Read16(paramblock + 0x8); ptr += 2;
|
||||
*(u16*)ptr = NDS::ARM7Read16(paramblock + 0xA); ptr += 2;
|
||||
*(u16*)ptr = NDS::ARM7Read16(paramblock + 0xC); ptr += 2;
|
||||
memset(ptr, 0, 0x1A); ptr += 0x1A;*/
|
||||
|
||||
// supported rates
|
||||
*ptr++ = 0x01; *ptr++ = 0x02;
|
||||
*ptr++ = 0x82;
|
||||
*ptr++ = 0x84;
|
||||
|
||||
// channel
|
||||
*ptr++ = 0x03; *ptr++ = 0x01;
|
||||
*ptr++ = (channel & 0xFF);
|
||||
|
||||
// TIM
|
||||
*ptr++ = 0x05; *ptr++ = 0x05;
|
||||
*ptr++ = 0x00;
|
||||
*ptr++ = 0x02;
|
||||
*ptr++ = 0x00;
|
||||
*ptr++ = 0x00;
|
||||
*ptr++ = 0x00;
|
||||
|
||||
// tag DD
|
||||
*ptr++ = 0xDD; *ptr++ = (0x18 + dd_len);
|
||||
*(u32*)ptr = 0x00BF0900; ptr += 4;
|
||||
*(u16*)ptr = 0x000A; ptr += 2; // stepping (checkme)
|
||||
*(u16*)ptr = 0; ptr += 2; // sync value (fixed later)
|
||||
//*(u32*)ptr = 0x00400001; ptr += 4;
|
||||
*(u32*)ptr = 0x00000100; ptr += 4;
|
||||
*(u32*)ptr = gameid; ptr += 4; // game ID
|
||||
*(u16*)ptr = streamcode; ptr += 2; // random stream code
|
||||
*ptr++ = dd_len;
|
||||
*ptr++ = beacontype;
|
||||
*(u16*)ptr = cmd_len; ptr += 2; // CMD frame length
|
||||
*(u16*)ptr = reply_len; ptr += 2; // REPLY frame length
|
||||
for (int i = 0; i < dd_len; i++)
|
||||
{
|
||||
*ptr++ = NDS::ARM7Read8(dd_addr + i);
|
||||
}
|
||||
|
||||
int len = (int)(ptr - (&BeaconFrame[0xC]));
|
||||
len = (len + 3) & ~3;
|
||||
|
||||
// FCS placeholder
|
||||
*(u32*)&BeaconFrame[0xC+len] = 0x1D46B6B8;
|
||||
len += 4;
|
||||
|
||||
// frame length
|
||||
*(u16*&)BeaconFrame[0xA] = len;
|
||||
}
|
||||
|
||||
|
||||
void WifiIPCRetry(u32 param)
|
||||
{
|
||||
u16 flag = NDS::ARM7Read16(0x027FFF96);
|
||||
if (flag & 0x1)
|
||||
{
|
||||
NDS::ScheduleEvent(NDS::Event_HLE_WifiIPCRetry, true, 1024, WifiIPCRetry, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
IPCReply reply = IPCReplyQueue.Read();
|
||||
WifiIPCReply(reply.Command, reply.Status, reply.NumExtra, reply.Extra);
|
||||
}
|
||||
|
||||
void WifiIPCReply(u16 cmd, u16 status, int numextra, u16* extra)
|
||||
{
|
||||
u16 flag = NDS::ARM7Read16(0x027FFF96);
|
||||
if (flag & 0x1)
|
||||
{
|
||||
IPCReply reply;
|
||||
reply.Command = cmd;
|
||||
reply.Status = status;
|
||||
reply.NumExtra = numextra;
|
||||
if (numextra) memcpy(reply.Extra, extra, numextra*sizeof(u16));
|
||||
IPCReplyQueue.Write(reply);
|
||||
NDS::ScheduleEvent(NDS::Event_HLE_WifiIPCRetry, false, 1024, WifiIPCRetry, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
flag |= 0x1;
|
||||
NDS::ARM7Write16(0x027FFF96, flag);
|
||||
|
||||
u32 replybuf = NDS::ARM7Read32(SharedMem[0]+0x8);
|
||||
NDS::ARM7Write16(replybuf+0x00, cmd);
|
||||
NDS::ARM7Write16(replybuf+0x02, status);
|
||||
|
||||
if (cmd == 0x8)
|
||||
{
|
||||
NDS::ARM7Write16(replybuf+0x8, extra[0]);
|
||||
if (numextra == 3)
|
||||
{
|
||||
NDS::ARM7Write16(replybuf+0x2C, extra[1]);
|
||||
NDS::ARM7Write16(replybuf+0x2E, extra[2]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numextra; i++)
|
||||
{
|
||||
NDS::ARM7Write16(replybuf+0x8+(i*2), extra[i]);
|
||||
}
|
||||
}
|
||||
|
||||
SendIPCReply(0xA, replybuf, 0);
|
||||
|
||||
if (!IPCReplyQueue.IsEmpty())
|
||||
NDS::ScheduleEvent(NDS::Event_HLE_WifiIPCRetry, false, 1024, WifiIPCRetry, 0);
|
||||
}
|
||||
|
||||
void OnIPCRequest(u32 addr)
|
||||
{
|
||||
u16 cmd = NDS::ARM7Read16(addr);
|
||||
|
||||
/*printf("WIFI HLE: cmd %04X\n", cmd);
|
||||
for (u32 i = 0; i < 16; i++)
|
||||
{
|
||||
for (u32 j = 0; j < 16; j+=4)
|
||||
{
|
||||
u32 varp = NDS::ARM7Read32(addr+(i*16)+j);
|
||||
printf("%08X ", varp);
|
||||
}
|
||||
printf("\n");
|
||||
}*/
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case 0x0: // init
|
||||
{
|
||||
SharedMem[0] = NDS::ARM7Read32(addr+0x4);
|
||||
SharedMem[1] = NDS::ARM7Read32(addr+0x8);
|
||||
u32 respbuf = NDS::ARM7Read32(addr+0xC);
|
||||
|
||||
NDS::ARM7Write32(SharedMem[0], SharedMem[1]);
|
||||
NDS::ARM7Write32(SharedMem[0]+0x8, respbuf);
|
||||
|
||||
// TODO init the sharedmem buffers
|
||||
// TODO other shito too!!
|
||||
|
||||
NDS::ARM7Write16(SharedMem[1], 2);
|
||||
|
||||
WifiIPCReply(0x0, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x2: // deinit
|
||||
{
|
||||
u16 status = NDS::ARM7Read16(SharedMem[1]);
|
||||
if (status == 2)
|
||||
{
|
||||
NDS::ARM7Write16(SharedMem[1], 0);
|
||||
WifiIPCReply(0x2, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
WifiIPCReply(0x2, 3);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x3: // enable
|
||||
{
|
||||
SharedMem[0] = NDS::ARM7Read32(addr+0x4);
|
||||
SharedMem[1] = NDS::ARM7Read32(addr+0x8);
|
||||
u32 respbuf = NDS::ARM7Read32(addr+0xC);
|
||||
|
||||
NDS::ARM7Write32(SharedMem[0], SharedMem[1]);
|
||||
NDS::ARM7Write32(SharedMem[0]+0x8, respbuf);
|
||||
|
||||
// TODO init the sharedmem buffers
|
||||
|
||||
NDS::ARM7Write16(SharedMem[1], 1);
|
||||
|
||||
WifiIPCReply(0x3, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x4: // disable
|
||||
{
|
||||
u16 status = NDS::ARM7Read16(SharedMem[1]);
|
||||
if (status == 1)
|
||||
{
|
||||
NDS::ARM7Write16(SharedMem[1], 0);
|
||||
WifiIPCReply(0x4, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
WifiIPCReply(0x4, 3);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x7: // set host param
|
||||
{
|
||||
// PARAM BLOCK FORMAT
|
||||
// offset size desc.
|
||||
// 00 4 tag DD: pointer to extra data
|
||||
// 04 2 tag DD: length of extra data (tag DD length minus 0x18)
|
||||
// 06 2
|
||||
// 08 6 SSID bytes 0..5 (SSID is 32 bytes, rest is all zeroes)
|
||||
// 08 4 tag DD: game ID
|
||||
// 0C 2 tag DD: stream code
|
||||
// 0E 2 tag DD: beacon type bit0
|
||||
// 10 2 ???
|
||||
// 12 2 tag DD: beacon type bit1
|
||||
// 14 2 tag DD: beacon type bit2
|
||||
// 16 2 tag DD: beacon type bit3
|
||||
// 18 2 beacon interval
|
||||
// 32 2 channel #
|
||||
// 34 2 tag DD: CMD data length
|
||||
// 36 2 tag DD: REPLY data length
|
||||
|
||||
// for now, the param block is just copied to sharedmem
|
||||
|
||||
u32 paramblock = NDS::ARM7Read32(addr+0x4);
|
||||
u32 dst = SharedMem[1] + 0xE8;
|
||||
for (u32 i = 0; i < 0x40; i+=4)
|
||||
{
|
||||
NDS::ARM7Write32(dst+i, NDS::ARM7Read32(paramblock+i));
|
||||
}
|
||||
|
||||
WifiIPCReply(0x7, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8: // start host comm
|
||||
{
|
||||
u16 status = NDS::ARM7Read16(SharedMem[1]);
|
||||
if (status != 2)
|
||||
{
|
||||
u16 ext = 0;
|
||||
WifiIPCReply(0x8, 3, 1, &ext);
|
||||
break;
|
||||
}
|
||||
|
||||
StartHostComm();
|
||||
|
||||
NDS::ARM7Write16(SharedMem[1], 7);
|
||||
|
||||
u16 extra[3];
|
||||
extra[0] = 0;
|
||||
extra[1] = NDS::ARM7Read16(SharedMem[1]+0xE8+0x34);
|
||||
extra[2] = NDS::ARM7Read16(SharedMem[1]+0xE8+0x36);
|
||||
WifiIPCReply(0x8, 0, 3, &extra);
|
||||
|
||||
NDS::ARM7Write16(SharedMem[1]+0xC2, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xA: // start host scan
|
||||
{
|
||||
// scan for viable host beacons
|
||||
// timeout: ~20480*64 cycles (with value 0x22)
|
||||
//
|
||||
// COMMAND BUFFER
|
||||
// offset size desc.
|
||||
// 04 4 pointer to beacon data buffer
|
||||
// 08 2 timeout in milliseconds
|
||||
// 0A 6 desired source MAC? seen set to FFFFFFFFFFFF
|
||||
//
|
||||
// REPLY BUFFER
|
||||
// offset size desc.
|
||||
// 08 2 ??? seen set to 4
|
||||
// 0A 6 source MAC from beacon (zero if none)
|
||||
// 10 ? channel
|
||||
// 36 2 length of tag DD data (minus first 8 bytes) (zero if none)
|
||||
// 38 X tag DD data (minus first 8 bytes)
|
||||
//
|
||||
// BEACON DATA BUFFER
|
||||
// offset size desc.
|
||||
// 00 2 buffer length in halfwords ((tag DD length - 8 + 0x41) >> 1)
|
||||
// 02 2 frame control? AND 0xFF -- 0080
|
||||
// 04 6 source MAC
|
||||
// 0A 2 SSID length (0 if none)
|
||||
// 0C 32 SSID (if present)
|
||||
// 2C 2 beacon capability field
|
||||
// 2E 2 supported rate bitmask (when bit7=1)
|
||||
// bit0 = 82 / 1Mbps
|
||||
// bit1 = 84 / 2Mbps
|
||||
// bit2 = 8B
|
||||
// bit3 = 8C
|
||||
// bit4 = 92
|
||||
// bit5 = 96
|
||||
// bit6 = B0
|
||||
// bit7 = C8
|
||||
// bit8 = E0
|
||||
// bit9 = EC
|
||||
// bit15 = any other rate (with bit7=1)
|
||||
// 30 2 supported rate bitmask
|
||||
// bit0 = 02/82 / 1Mbps
|
||||
// bit1 = 04/84 / 2Mbps
|
||||
// bit2 = 0B/8B
|
||||
// bit3 = 0C/8C
|
||||
// bit4 = 12/92
|
||||
// bit5 = 16/96
|
||||
// bit6 = 30/B0
|
||||
// bit7 = 48/C8
|
||||
// bit8 = 60/E0
|
||||
// bit9 = 6C/EC
|
||||
// bit15 = any other rate
|
||||
// 32 2 beacon interval
|
||||
// 34 2 beacon TIM period field (0 if none)
|
||||
// 36 2 beacon channel field
|
||||
// 38 2 beacon CF period field (0 if none)
|
||||
// 3A 2 ??
|
||||
// 3C 2 length of tag DD data (minus first 8 bytes)
|
||||
// 3E 2 number of bad tag DD's (when first 4 bytes are =/= 00:09:BF:00)
|
||||
// 40 X tag DD data (minus first 8 bytes)
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x1E: // measure channel
|
||||
{
|
||||
u16 status = NDS::ARM7Read16(SharedMem[1]);
|
||||
if (status != 2)
|
||||
{
|
||||
WifiIPCReply(0x1E, 3);
|
||||
break;
|
||||
}
|
||||
|
||||
u16 chan = NDS::ARM7Read16(addr+0x6);
|
||||
u16 extra[2] = {chan, 1};
|
||||
|
||||
WifiIPCReply(0x1E, 0, 2, extra);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("WIFI HLE: unknown command %04X\n", cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Copyright 2016-2022 melonDS team
|
||||
|
||||
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 HLE_RETAIL_WIFI_H
|
||||
#define HLE_RETAIL_WIFI_H
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
namespace HLE
|
||||
{
|
||||
namespace Retail
|
||||
{
|
||||
namespace Wifi
|
||||
{
|
||||
|
||||
void Reset();
|
||||
|
||||
//
|
||||
|
||||
void OnIPCRequest(u32 data);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HLE_RETAIL_WIFI_H
|
37
src/NDS.cpp
37
src/NDS.cpp
|
@ -45,6 +45,8 @@
|
|||
#include "DSi_Camera.h"
|
||||
#include "DSi_DSP.h"
|
||||
|
||||
#include "HLE.h"
|
||||
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
|
@ -95,7 +97,7 @@ u64 FrameStartTimestamp;
|
|||
|
||||
int CurCPU;
|
||||
|
||||
const s32 kMaxIterationCycles = 64;
|
||||
const s32 kMaxIterationCycles = 4096;//64;
|
||||
const s32 kIterationCycleMargin = 8;
|
||||
|
||||
u32 ARM9ClockShift;
|
||||
|
@ -683,6 +685,9 @@ void Reset()
|
|||
SPU::SetDegrade10Bit(degradeAudio);
|
||||
|
||||
AREngine::Reset();
|
||||
|
||||
|
||||
HLE::Reset();
|
||||
}
|
||||
|
||||
void Start()
|
||||
|
@ -815,10 +820,7 @@ bool DoSavestate(Savestate* file)
|
|||
u32 console;
|
||||
file->Var32(&console);
|
||||
if (console != ConsoleType)
|
||||
{
|
||||
Log(LogLevel::Error, "savestate: Expected console type %d, got console type %d. cannot load.\n", ConsoleType, console);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
file->VarArray(MainRAM, MainRAMMaxSize);
|
||||
|
@ -873,11 +875,7 @@ bool DoSavestate(Savestate* file)
|
|||
|
||||
file->VarArray(DMA9Fill, 4*sizeof(u32));
|
||||
|
||||
if (!DoSavestate_Scheduler(file))
|
||||
{
|
||||
Platform::Log(Platform::LogLevel::Error, "savestate: failed to %s scheduler state\n", file->Saving ? "save" : "load");
|
||||
return false;
|
||||
}
|
||||
if (!DoSavestate_Scheduler(file)) return false;
|
||||
file->Var32(&SchedListMask);
|
||||
file->Var64(&ARM9Timestamp);
|
||||
file->Var64(&ARM9Target);
|
||||
|
@ -944,8 +942,6 @@ bool DoSavestate(Savestate* file)
|
|||
}
|
||||
#endif
|
||||
|
||||
file->Finish();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1102,7 +1098,7 @@ u32 RunFrame()
|
|||
target = ARM9Timestamp >> ARM9ClockShift;
|
||||
CurCPU = 1;
|
||||
|
||||
while (ARM7Timestamp < target)
|
||||
/*while (ARM7Timestamp < target)
|
||||
{
|
||||
ARM7Target = target; // might be changed by a reschedule
|
||||
|
||||
|
@ -1125,7 +1121,8 @@ u32 RunFrame()
|
|||
}
|
||||
|
||||
RunTimers(1);
|
||||
}
|
||||
}*/
|
||||
ARM7Timestamp = target;
|
||||
|
||||
RunSystem(target);
|
||||
|
||||
|
@ -2006,9 +2003,9 @@ void debug(u32 param)
|
|||
|
||||
//for (int i = 0; i < 9; i++)
|
||||
// printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]);
|
||||
|
||||
return;
|
||||
FILE*
|
||||
shit = fopen("debug/crayon.bin", "wb");
|
||||
shit = fopen("debug/mkds.bin", "wb");
|
||||
fwrite(ARM9->ITCM, 0x8000, 1, shit);
|
||||
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
|
||||
{
|
||||
|
@ -2047,7 +2044,7 @@ void debug(u32 param)
|
|||
|
||||
|
||||
u8 ARM9Read8(u32 addr)
|
||||
{
|
||||
{if(addr>=0x0208DAE0 && addr<0x0208E7E0) printf("PRALON8 %08X %08X\n", addr, ARM9->R[15]);
|
||||
if ((addr & 0xFFFFF000) == 0xFFFF0000)
|
||||
{
|
||||
return *(u8*)&ARM9BIOS[addr & 0xFFF];
|
||||
|
@ -2105,7 +2102,7 @@ u8 ARM9Read8(u32 addr)
|
|||
}
|
||||
|
||||
u16 ARM9Read16(u32 addr)
|
||||
{
|
||||
{if(addr>=0x0208DAE0 && addr<0x0208E7E0) printf("PRALON16 %08X %08X\n", addr, ARM9->R[15]);
|
||||
addr &= ~0x1;
|
||||
|
||||
if ((addr & 0xFFFFF000) == 0xFFFF0000)
|
||||
|
@ -2165,7 +2162,7 @@ u16 ARM9Read16(u32 addr)
|
|||
}
|
||||
|
||||
u32 ARM9Read32(u32 addr)
|
||||
{
|
||||
{if(addr>=0x0208DAE0 && addr<0x0208E7E0) printf("PRALON32 %08X %08X\n", addr, ARM9->R[15]);
|
||||
addr &= ~0x3;
|
||||
|
||||
if ((addr & 0xFFFFF000) == 0xFFFF0000)
|
||||
|
@ -2271,7 +2268,7 @@ void ARM9Write8(u32 addr, u8 val)
|
|||
}
|
||||
|
||||
void ARM9Write16(u32 addr, u16 val)
|
||||
{
|
||||
{if(addr==0x027FFF96) printf("KPROAON %04X %08X\n", val, ARM9->R[15]);
|
||||
addr &= ~0x1;
|
||||
|
||||
switch (addr & 0xFF000000)
|
||||
|
@ -3457,6 +3454,7 @@ void ARM9IOWrite16(u32 addr, u16 val)
|
|||
{
|
||||
SetIRQ(1, IRQ_IPCSync);
|
||||
}
|
||||
HLE::OnIPCSync();
|
||||
return;
|
||||
|
||||
case 0x04000184:
|
||||
|
@ -3654,6 +3652,7 @@ void ARM9IOWrite32(u32 addr, u32 val)
|
|||
{
|
||||
bool wasempty = IPCFIFO9.IsEmpty();
|
||||
IPCFIFO9.Write(val);
|
||||
HLE::OnIPCRequest();
|
||||
if ((IPCFIFOCnt7 & 0x0400) && wasempty)
|
||||
SetIRQ(1, IRQ_IPCRecv);
|
||||
}
|
||||
|
|
13
src/NDS.h
13
src/NDS.h
|
@ -52,6 +52,19 @@ enum
|
|||
Event_DSi_CamTransfer,
|
||||
Event_DSi_DSP,
|
||||
|
||||
// HLE
|
||||
Event_HLE_PollInput,
|
||||
Event_HLE_SoundCmd,
|
||||
Event_HLE_SoundAlarm0,
|
||||
Event_HLE_SoundAlarm1,
|
||||
Event_HLE_SoundAlarm2,
|
||||
Event_HLE_SoundAlarm3,
|
||||
Event_HLE_SoundAlarm4,
|
||||
Event_HLE_SoundAlarm5,
|
||||
Event_HLE_SoundAlarm6,
|
||||
Event_HLE_SoundAlarm7,
|
||||
Event_HLE_WifiIPCRetry,
|
||||
|
||||
Event_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -194,14 +194,14 @@ void Key2_Encrypt(u8* data, u32 len)
|
|||
}
|
||||
|
||||
|
||||
CartCommon::CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump)
|
||||
CartCommon::CartCommon(u8* rom, u32 len, u32 chipid)
|
||||
{
|
||||
ROM = rom;
|
||||
ROMLength = len;
|
||||
ChipID = chipid;
|
||||
|
||||
u8 unitcode = ROM[0x12];
|
||||
IsDSi = (unitcode & 0x02) != 0 && !badDSiDump;
|
||||
IsDSi = (unitcode & 0x02) != 0;
|
||||
DSiBase = *(u16*)&ROM[0x92] << 19;
|
||||
}
|
||||
|
||||
|
@ -403,7 +403,7 @@ void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset)
|
|||
}
|
||||
|
||||
|
||||
CartRetail::CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump) : CartCommon(rom, len, chipid, badDSiDump)
|
||||
CartRetail::CartRetail(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid)
|
||||
{
|
||||
SRAM = nullptr;
|
||||
}
|
||||
|
@ -865,7 +865,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
|
|||
}
|
||||
|
||||
|
||||
CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid, false)
|
||||
CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1092,7 +1092,7 @@ void CartRetailNAND::BuildSRAMID()
|
|||
}
|
||||
|
||||
|
||||
CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump) : CartRetail(rom, len, chipid, badDSiDump)
|
||||
CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion) : CartRetail(rom, len, chipid)
|
||||
{
|
||||
IRVersion = irversion;
|
||||
}
|
||||
|
@ -1138,7 +1138,7 @@ u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last)
|
|||
}
|
||||
|
||||
|
||||
CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid, false)
|
||||
CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid) : CartRetail(rom, len, chipid)
|
||||
{
|
||||
Log(LogLevel::Info,"POKETYPE CART\n");
|
||||
}
|
||||
|
@ -1172,7 +1172,7 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
|
|||
}
|
||||
|
||||
|
||||
CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid, false)
|
||||
CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid)
|
||||
{
|
||||
SD = nullptr;
|
||||
}
|
||||
|
@ -1630,15 +1630,6 @@ bool LoadROM(const u8* romdata, u32 romlen)
|
|||
|
||||
u8 unitcode = Header.UnitCode;
|
||||
bool dsi = (unitcode & 0x02) != 0;
|
||||
bool badDSiDump = false;
|
||||
|
||||
u32 dsiRegion = *(u32*)&CartROM[0x1B0];
|
||||
if (dsi && dsiRegion == 0)
|
||||
{
|
||||
Log(LogLevel::Info, "DS header indicates DSi, but region is zero. Going in bad dump mode.\n");
|
||||
badDSiDump = true;
|
||||
dsi = false;
|
||||
}
|
||||
|
||||
size_t bannersize = dsi ? 0x23C0 : 0xA40;
|
||||
if (Header.BannerOffset >= 0x200 && Header.BannerOffset < (CartROMSize - bannersize))
|
||||
|
@ -1734,11 +1725,11 @@ bool LoadROM(const u8* romdata, u32 romlen)
|
|||
else if (CartID & 0x08000000)
|
||||
Cart = new CartRetailNAND(CartROM, CartROMSize, CartID);
|
||||
else if (irversion != 0)
|
||||
Cart = new CartRetailIR(CartROM, CartROMSize, CartID, irversion, badDSiDump);
|
||||
Cart = new CartRetailIR(CartROM, CartROMSize, CartID, irversion);
|
||||
else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx
|
||||
Cart = new CartRetailBT(CartROM, CartROMSize, CartID);
|
||||
else
|
||||
Cart = new CartRetail(CartROM, CartROMSize, CartID, badDSiDump);
|
||||
Cart = new CartRetail(CartROM, CartROMSize, CartID);
|
||||
|
||||
if (Cart)
|
||||
Cart->Reset();
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace NDSCart
|
|||
class CartCommon
|
||||
{
|
||||
public:
|
||||
CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump);
|
||||
CartCommon(u8* rom, u32 len, u32 chipid);
|
||||
virtual ~CartCommon();
|
||||
|
||||
virtual u32 Type() { return 0x001; }
|
||||
|
@ -75,7 +75,7 @@ protected:
|
|||
class CartRetail : public CartCommon
|
||||
{
|
||||
public:
|
||||
CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump);
|
||||
CartRetail(u8* rom, u32 len, u32 chipid);
|
||||
virtual ~CartRetail() override;
|
||||
|
||||
virtual u32 Type() override { return 0x101; }
|
||||
|
@ -145,7 +145,7 @@ private:
|
|||
class CartRetailIR : public CartRetail
|
||||
{
|
||||
public:
|
||||
CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump);
|
||||
CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion);
|
||||
~CartRetailIR() override;
|
||||
|
||||
virtual u32 Type() override { return 0x103; }
|
||||
|
|
|
@ -764,6 +764,9 @@ void Reset()
|
|||
|
||||
ConvResult = 0;
|
||||
|
||||
TouchX = 0;
|
||||
TouchY = 0xFFF;
|
||||
|
||||
MicBufferLen = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,16 +17,12 @@
|
|||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include "Savestate.h"
|
||||
#include "Platform.h"
|
||||
|
||||
using Platform::Log;
|
||||
using Platform::LogLevel;
|
||||
|
||||
static const char* SAVESTATE_MAGIC = "MELN";
|
||||
|
||||
/*
|
||||
Savestate format
|
||||
|
||||
|
@ -50,148 +46,230 @@ static const char* SAVESTATE_MAGIC = "MELN";
|
|||
* different minor means adjustments may have to be made
|
||||
*/
|
||||
|
||||
Savestate::Savestate(void *buffer, u32 size, bool save) :
|
||||
Error(false),
|
||||
Saving(save),
|
||||
CurSection(NO_SECTION),
|
||||
buffer(static_cast<u8 *>(buffer)),
|
||||
buffer_offset(0),
|
||||
buffer_length(size),
|
||||
buffer_owned(false),
|
||||
finished(false)
|
||||
// TODO: buffering system! or something of that sort
|
||||
// repeated fread/fwrite is slow on Switch
|
||||
|
||||
Savestate::Savestate(const std::string& filename, bool save)
|
||||
{
|
||||
if (Saving)
|
||||
const char* magic = "MELN";
|
||||
|
||||
Error = false;
|
||||
|
||||
if (save)
|
||||
{
|
||||
WriteSavestateHeader();
|
||||
Saving = true;
|
||||
file = Platform::OpenLocalFile(filename, "wb");
|
||||
if (!file)
|
||||
{
|
||||
Log(LogLevel::Error, "savestate: file %s doesn't exist\n", filename.c_str());
|
||||
Error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
VersionMajor = SAVESTATE_MAJOR;
|
||||
VersionMinor = SAVESTATE_MINOR;
|
||||
|
||||
fwrite(magic, 4, 1, file);
|
||||
fwrite(&VersionMajor, 2, 1, file);
|
||||
fwrite(&VersionMinor, 2, 1, file);
|
||||
fseek(file, 8, SEEK_CUR); // length to be fixed later
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ensure that the file starts with "MELN"
|
||||
u32 read_magic = 0;
|
||||
Var32(&read_magic);
|
||||
|
||||
if (read_magic != *((u32*)SAVESTATE_MAGIC))
|
||||
Saving = false;
|
||||
file = Platform::OpenFile(filename, "rb");
|
||||
if (!file)
|
||||
{
|
||||
Log(LogLevel::Error, "savestate: expected magic number %#08x (%s), got %#08x\n",
|
||||
*((u32*)SAVESTATE_MAGIC),
|
||||
SAVESTATE_MAGIC,
|
||||
read_magic
|
||||
);
|
||||
Log(LogLevel::Error, "savestate: file %s doesn't exist\n", filename.c_str());
|
||||
Error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
u16 major = 0;
|
||||
Var16(&major);
|
||||
if (major != SAVESTATE_MAJOR)
|
||||
u32 len;
|
||||
fseek(file, 0, SEEK_END);
|
||||
len = (u32)ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
u32 buf = 0;
|
||||
|
||||
fread(&buf, 4, 1, file);
|
||||
if (buf != ((u32*)magic)[0])
|
||||
{
|
||||
Log(LogLevel::Error, "savestate: bad version major %d, expecting %d\n", major, SAVESTATE_MAJOR);
|
||||
Log(LogLevel::Error, "savestate: invalid magic %08X\n", buf);
|
||||
Error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
u16 minor = 0;
|
||||
Var16(&minor);
|
||||
if (minor > SAVESTATE_MINOR)
|
||||
VersionMajor = 0;
|
||||
VersionMinor = 0;
|
||||
|
||||
fread(&VersionMajor, 2, 1, file);
|
||||
if (VersionMajor != SAVESTATE_MAJOR)
|
||||
{
|
||||
Log(LogLevel::Error, "savestate: state from the future, %d > %d\n", minor, SAVESTATE_MINOR);
|
||||
Log(LogLevel::Error, "savestate: bad version major %d, expecting %d\n", VersionMajor, SAVESTATE_MAJOR);
|
||||
Error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
u32 read_length = 0;
|
||||
Var32(&read_length);
|
||||
if (read_length != buffer_length)
|
||||
fread(&VersionMinor, 2, 1, file);
|
||||
if (VersionMinor > SAVESTATE_MINOR)
|
||||
{
|
||||
Log(LogLevel::Error, "savestate: expected a length of %d, got %d\n", buffer_length, read_length);
|
||||
Log(LogLevel::Error, "savestate: state from the future, %d > %d\n", VersionMinor, SAVESTATE_MINOR);
|
||||
Error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// The next 4 bytes are reserved
|
||||
buffer_offset += 4;
|
||||
}
|
||||
}
|
||||
buf = 0;
|
||||
fread(&buf, 4, 1, file);
|
||||
if (buf != len)
|
||||
{
|
||||
Log(LogLevel::Error, "savestate: bad length %d\n", buf);
|
||||
Error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Savestate::Savestate(u32 initial_size) :
|
||||
Error(false),
|
||||
Saving(true), // Can't load from an empty buffer
|
||||
CurSection(NO_SECTION),
|
||||
buffer(nullptr),
|
||||
buffer_offset(0),
|
||||
buffer_length(initial_size),
|
||||
buffer_owned(true),
|
||||
finished(false)
|
||||
{
|
||||
buffer = static_cast<u8 *>(malloc(buffer_length));
|
||||
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
Log(LogLevel::Error, "savestate: failed to allocate %d bytes\n", buffer_length);
|
||||
Error = true;
|
||||
return;
|
||||
fseek(file, 4, SEEK_CUR);
|
||||
}
|
||||
|
||||
WriteSavestateHeader();
|
||||
CurSection = -1;
|
||||
}
|
||||
|
||||
Savestate::~Savestate()
|
||||
{
|
||||
if (Saving && !finished && !buffer_owned && !Error)
|
||||
{ // If we haven't finished saving, and there hasn't been an error...
|
||||
Finish();
|
||||
// No need to close the active section for an owned buffer,
|
||||
// it's about to be thrown out.
|
||||
if (Error) return;
|
||||
|
||||
if (Saving)
|
||||
{
|
||||
if (CurSection != 0xFFFFFFFF)
|
||||
{
|
||||
u32 pos = (u32)ftell(file);
|
||||
fseek(file, CurSection+4, SEEK_SET);
|
||||
|
||||
u32 len = pos - CurSection;
|
||||
fwrite(&len, 4, 1, file);
|
||||
|
||||
fseek(file, pos, SEEK_SET);
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
u32 len = (u32)ftell(file);
|
||||
fseek(file, 8, SEEK_SET);
|
||||
fwrite(&len, 4, 1, file);
|
||||
}
|
||||
|
||||
if (buffer_owned)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
if (file) fclose(file);
|
||||
}
|
||||
|
||||
void Savestate::Section(const char* magic)
|
||||
{
|
||||
if (Error || finished) return;
|
||||
if (Error) return;
|
||||
|
||||
if (Saving)
|
||||
{
|
||||
// Go back to the current section's header and write the length
|
||||
CloseCurrentSection();
|
||||
if (CurSection != 0xFFFFFFFF)
|
||||
{
|
||||
u32 pos = (u32)ftell(file);
|
||||
fseek(file, CurSection+4, SEEK_SET);
|
||||
|
||||
CurSection = buffer_offset;
|
||||
u32 len = pos - CurSection;
|
||||
fwrite(&len, 4, 1, file);
|
||||
|
||||
// Write the new section's magic number
|
||||
VarArray((void*)magic, 4);
|
||||
fseek(file, pos, SEEK_SET);
|
||||
}
|
||||
|
||||
// The next 4 bytes are the length, which we'll come back to later.
|
||||
u32 zero = 0;
|
||||
Var32(&zero);
|
||||
CurSection = (u32)ftell(file);
|
||||
|
||||
// The 8 bytes afterward are reserved, so we skip them.
|
||||
Var32(&zero);
|
||||
Var32(&zero);
|
||||
fwrite(magic, 4, 1, file);
|
||||
fseek(file, 12, SEEK_CUR);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 section_offset = FindSection(magic);
|
||||
fseek(file, 0x10, SEEK_SET);
|
||||
|
||||
if (section_offset != NO_SECTION)
|
||||
for (;;)
|
||||
{
|
||||
buffer_offset = section_offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(LogLevel::Error, "savestate: section %s not found. blarg\n", magic);
|
||||
Error = true;
|
||||
u32 buf = 0;
|
||||
|
||||
fread(&buf, 4, 1, file);
|
||||
if (buf != ((u32*)magic)[0])
|
||||
{
|
||||
if (buf == 0)
|
||||
{
|
||||
Log(LogLevel::Error, "savestate: section %s not found. blarg\n", magic);
|
||||
return;
|
||||
}
|
||||
|
||||
buf = 0;
|
||||
fread(&buf, 4, 1, file);
|
||||
fseek(file, buf-8, SEEK_CUR);
|
||||
continue;
|
||||
}
|
||||
|
||||
fseek(file, 12, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Savestate::Var8(u8* var)
|
||||
{
|
||||
if (Error) return;
|
||||
|
||||
if (Saving)
|
||||
{
|
||||
fwrite(var, 1, 1, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
fread(var, 1, 1, file);
|
||||
}
|
||||
}
|
||||
|
||||
void Savestate::Var16(u16* var)
|
||||
{
|
||||
if (Error) return;
|
||||
|
||||
if (Saving)
|
||||
{
|
||||
fwrite(var, 2, 1, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
fread(var, 2, 1, file);
|
||||
}
|
||||
}
|
||||
|
||||
void Savestate::Var32(u32* var)
|
||||
{
|
||||
if (Error) return;
|
||||
|
||||
if (Saving)
|
||||
{
|
||||
fwrite(var, 4, 1, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
fread(var, 4, 1, file);
|
||||
}
|
||||
}
|
||||
|
||||
void Savestate::Var64(u64* var)
|
||||
{
|
||||
if (Error) return;
|
||||
|
||||
if (Saving)
|
||||
{
|
||||
fwrite(var, 8, 1, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
fread(var, 8, 1, file);
|
||||
}
|
||||
}
|
||||
|
||||
void Savestate::Bool32(bool* var)
|
||||
{
|
||||
// for compatibility
|
||||
// for compability
|
||||
if (Saving)
|
||||
{
|
||||
u32 val = *var;
|
||||
|
@ -207,175 +285,14 @@ void Savestate::Bool32(bool* var)
|
|||
|
||||
void Savestate::VarArray(void* data, u32 len)
|
||||
{
|
||||
if (Error || finished) return;
|
||||
|
||||
assert(buffer_offset <= buffer_length);
|
||||
if (Error) return;
|
||||
|
||||
if (Saving)
|
||||
{
|
||||
if (buffer_offset + len > buffer_length)
|
||||
{ // If writing the given data would take us past the buffer's end...
|
||||
Log(LogLevel::Warn, "savestate: %u-byte write would exceed %u-byte savestate buffer\n", len, buffer_length);
|
||||
|
||||
if (!(buffer_owned && Resize(buffer_length * 2 + len)))
|
||||
{ // If we're not allowed to resize this buffer, or if we are but failed...
|
||||
Log(LogLevel::Error, "savestate: Failed to write %d bytes to savestate\n", len);
|
||||
Error = true;
|
||||
return;
|
||||
}
|
||||
// The buffer's length is doubled, plus however much memory is needed for this write.
|
||||
// This way we can write the data and reduce the chance of needing to resize again.
|
||||
}
|
||||
|
||||
memcpy(buffer + buffer_offset, data, len);
|
||||
fwrite(data, len, 1, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (buffer_offset + len > buffer_length)
|
||||
{ // If reading the requested amount of data would take us past the buffer's edge...
|
||||
Log(LogLevel::Error, "savestate: %u-byte read would exceed %u-byte savestate buffer\n", len, buffer_length);
|
||||
Error = true;
|
||||
return;
|
||||
|
||||
// Can't realloc here.
|
||||
// Not only do we not own the buffer pointer (when loading a state),
|
||||
// but we can't magically make the desired data appear.
|
||||
}
|
||||
|
||||
memcpy(data, buffer + buffer_offset, len);
|
||||
}
|
||||
|
||||
buffer_offset += len;
|
||||
}
|
||||
|
||||
void Savestate::Finish()
|
||||
{
|
||||
if (Error || finished) return;
|
||||
CloseCurrentSection();
|
||||
WriteStateLength();
|
||||
finished = true;
|
||||
}
|
||||
|
||||
void Savestate::Rewind(bool save)
|
||||
{
|
||||
Error = false;
|
||||
Saving = save;
|
||||
CurSection = NO_SECTION;
|
||||
|
||||
buffer_offset = 0;
|
||||
finished = false;
|
||||
}
|
||||
|
||||
void Savestate::CloseCurrentSection()
|
||||
{
|
||||
if (CurSection != NO_SECTION && !finished)
|
||||
{ // If we're in the middle of writing a section...
|
||||
|
||||
// Go back to the section's header
|
||||
// Get the length of the section we've written thus far
|
||||
u32 section_length = buffer_offset - CurSection;
|
||||
|
||||
// Write the length in the section's header
|
||||
// (specifically the first 4 bytes after the magic number)
|
||||
memcpy(buffer + CurSection + 4, §ion_length, sizeof(section_length));
|
||||
|
||||
CurSection = NO_SECTION;
|
||||
fread(data, len, 1, file);
|
||||
}
|
||||
}
|
||||
|
||||
bool Savestate::Resize(u32 new_length)
|
||||
{
|
||||
if (!buffer_owned)
|
||||
{ // If we're not allowed to resize this buffer...
|
||||
Log(LogLevel::Error, "savestate: Buffer is externally-owned, cannot resize it\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 old_length = buffer_length;
|
||||
void* resized = realloc(buffer, new_length);
|
||||
if (!resized)
|
||||
{ // If the buffer couldn't be expanded...
|
||||
Log(LogLevel::Error, "savestate: Failed to resize owned savestate buffer from %dB to %dB\n", old_length, new_length);
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 length_diff = new_length - old_length;
|
||||
buffer = static_cast<u8 *>(resized);
|
||||
buffer_length = new_length;
|
||||
|
||||
Log(LogLevel::Debug, "savestate: Expanded %uB savestate buffer to %uB\n", old_length, new_length);
|
||||
// Zero out the newly-allocated memory (to ensure we don't introduce a security hole)
|
||||
memset(buffer + old_length, 0, length_diff);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Savestate::WriteSavestateHeader()
|
||||
{
|
||||
// The magic number
|
||||
VarArray((void *) SAVESTATE_MAGIC, 4);
|
||||
|
||||
// The major and minor versions
|
||||
u16 major = SAVESTATE_MAJOR;
|
||||
Var16(&major);
|
||||
|
||||
u16 minor = SAVESTATE_MINOR;
|
||||
Var16(&minor);
|
||||
|
||||
// The next 4 bytes are the file's length, which will be filled in at the end
|
||||
u32 zero = 0;
|
||||
Var32(&zero);
|
||||
|
||||
// The following 4 bytes are reserved
|
||||
Var32(&zero);
|
||||
}
|
||||
|
||||
void Savestate::WriteStateLength()
|
||||
{
|
||||
// Not to be confused with the buffer length.
|
||||
// The buffer might not be full,
|
||||
// so we don't want to write out the extra stuff.
|
||||
u32 state_length = buffer_offset;
|
||||
|
||||
// Write the length in the header
|
||||
memcpy(buffer + 0x08, &state_length, sizeof(state_length));
|
||||
}
|
||||
|
||||
u32 Savestate::FindSection(const char* magic) const
|
||||
{
|
||||
if (!magic) return NO_SECTION;
|
||||
|
||||
// Start looking at the savestate's beginning, right after its global header
|
||||
// (we can't start from the current offset because then we'd lose the ability to rearrange sections)
|
||||
|
||||
for (u32 offset = 0x10; offset < buffer_length;)
|
||||
{ // Until we've found the desired section...
|
||||
|
||||
// Get this section's magic number
|
||||
char read_magic[4] = {0};
|
||||
memcpy(read_magic, buffer + offset, sizeof(read_magic));
|
||||
|
||||
if (memcmp(read_magic, magic, sizeof(read_magic)) == 0)
|
||||
{ // If this is the right section...
|
||||
return offset + 16; // ...return the offset of the first byte of the section after the header
|
||||
}
|
||||
|
||||
// Haven't found our section yet. Let's move on to the next one.
|
||||
|
||||
u32 section_length_offset = offset + sizeof(read_magic);
|
||||
if (section_length_offset >= buffer_length)
|
||||
{ // If trying to read the section length would take us past the file's end...
|
||||
break;
|
||||
}
|
||||
|
||||
// First we need to find out how big this section is...
|
||||
u32 section_length = 0;
|
||||
memcpy(§ion_length, buffer + section_length_offset, sizeof(section_length));
|
||||
|
||||
// ...then skip it. (The section length includes the 16-byte header.)
|
||||
offset += section_length;
|
||||
}
|
||||
|
||||
// We've reached the end of the file without finding the requested section...
|
||||
Log(LogLevel::Error, "savestate: section %s not found. blarg\n", magic);
|
||||
return NO_SECTION;
|
||||
}
|
|
@ -19,7 +19,6 @@
|
|||
#ifndef SAVESTATE_H
|
||||
#define SAVESTATE_H
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include "types.h"
|
||||
|
@ -30,92 +29,37 @@
|
|||
class Savestate
|
||||
{
|
||||
public:
|
||||
static constexpr u32 DEFAULT_SIZE = 32 * 1024 * 1024; // 32 MB
|
||||
Savestate(void* buffer, u32 size, bool save);
|
||||
explicit Savestate(u32 initial_size = DEFAULT_SIZE);
|
||||
|
||||
Savestate(const std::string& filename, bool save);
|
||||
~Savestate();
|
||||
|
||||
bool Error;
|
||||
|
||||
bool Saving;
|
||||
u32 VersionMajor;
|
||||
u32 VersionMinor;
|
||||
|
||||
u32 CurSection;
|
||||
|
||||
void Section(const char* magic);
|
||||
|
||||
void Var8(u8* var)
|
||||
{
|
||||
VarArray(var, sizeof(*var));
|
||||
}
|
||||
|
||||
void Var16(u16* var)
|
||||
{
|
||||
VarArray(var, sizeof(*var));
|
||||
}
|
||||
|
||||
void Var32(u32* var)
|
||||
{
|
||||
VarArray(var, sizeof(*var));
|
||||
}
|
||||
|
||||
void Var64(u64* var)
|
||||
{
|
||||
VarArray(var, sizeof(*var));
|
||||
}
|
||||
void Var8(u8* var);
|
||||
void Var16(u16* var);
|
||||
void Var32(u32* var);
|
||||
void Var64(u64* var);
|
||||
|
||||
void Bool32(bool* var);
|
||||
|
||||
void VarArray(void* data, u32 len);
|
||||
|
||||
void Finish();
|
||||
|
||||
// TODO rewinds the stream
|
||||
void Rewind(bool save);
|
||||
|
||||
bool IsAtLeastVersion(u32 major, u32 minor)
|
||||
bool IsAtleastVersion(u32 major, u32 minor)
|
||||
{
|
||||
u16 major_version = MajorVersion();
|
||||
if (MajorVersion() > major) return true;
|
||||
if (major_version == major && MinorVersion() >= minor) return true;
|
||||
if (VersionMajor > major) return true;
|
||||
if (VersionMajor == major && VersionMinor >= minor) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void* Buffer() { return buffer; }
|
||||
[[nodiscard]] const void* Buffer() const { return buffer; }
|
||||
|
||||
[[nodiscard]] u32 BufferLength() const { return buffer_length; }
|
||||
|
||||
[[nodiscard]] u32 Length() const { return buffer_offset; }
|
||||
|
||||
[[nodiscard]] u16 MajorVersion() const
|
||||
{
|
||||
// major version is stored at offset 0x04
|
||||
u16 major = 0;
|
||||
memcpy(&major, buffer + 0x04, sizeof(major));
|
||||
return major;
|
||||
}
|
||||
|
||||
[[nodiscard]] u16 MinorVersion() const
|
||||
{
|
||||
// minor version is stored at offset 0x06
|
||||
u16 minor = 0;
|
||||
memcpy(&minor, buffer + 0x06, sizeof(minor));
|
||||
return minor;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr u32 NO_SECTION = 0xffffffff;
|
||||
void CloseCurrentSection();
|
||||
bool Resize(u32 new_length);
|
||||
void WriteSavestateHeader();
|
||||
void WriteStateLength();
|
||||
u32 FindSection(const char* magic) const;
|
||||
u8* buffer;
|
||||
u32 buffer_offset;
|
||||
u32 buffer_length;
|
||||
bool buffer_owned;
|
||||
bool finished;
|
||||
FILE* file;
|
||||
};
|
||||
|
||||
#endif // SAVESTATE_H
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <fstream>
|
||||
|
||||
#include <zstd.h>
|
||||
#ifdef ARCHIVE_SUPPORT_ENABLED
|
||||
|
@ -53,7 +52,6 @@ std::string BaseGBAAssetName = "";
|
|||
SaveManager* NDSSave = nullptr;
|
||||
SaveManager* GBASave = nullptr;
|
||||
|
||||
std::unique_ptr<Savestate> BackupState = nullptr;
|
||||
bool SavestateLoaded = false;
|
||||
std::string PreviousSaveFile = "";
|
||||
|
||||
|
@ -306,62 +304,35 @@ bool SavestateExists(int slot)
|
|||
|
||||
bool LoadState(const std::string& filename)
|
||||
{
|
||||
FILE* file = fopen(filename.c_str(), "rb");
|
||||
if (file == nullptr)
|
||||
{ // If we couldn't open the state file...
|
||||
Platform::Log(Platform::LogLevel::Error, "Failed to open state file \"%s\"\n", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
// backup
|
||||
Savestate* backup = new Savestate("timewarp.mln", true);
|
||||
NDS::DoSavestate(backup);
|
||||
delete backup;
|
||||
|
||||
std::unique_ptr<Savestate> backup = std::make_unique<Savestate>(Savestate::DEFAULT_SIZE);
|
||||
if (backup->Error)
|
||||
{ // If we couldn't allocate memory for the backup...
|
||||
Platform::Log(Platform::LogLevel::Error, "Failed to allocate memory for state backup\n");
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
bool failed = false;
|
||||
|
||||
if (!NDS::DoSavestate(backup.get()) || backup->Error)
|
||||
{ // Back up the emulator's state. If that failed...
|
||||
Platform::Log(Platform::LogLevel::Error, "Failed to back up state, aborting load (from \"%s\")\n", filename.c_str());
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
// We'll store the backup once we're sure that the state was loaded.
|
||||
// Now that we know the file and backup are both good, let's load the new state.
|
||||
|
||||
// Get the size of the file that we opened
|
||||
if (fseek(file, 0, SEEK_END) != 0)
|
||||
Savestate* state = new Savestate(filename, false);
|
||||
if (state->Error)
|
||||
{
|
||||
Platform::Log(Platform::LogLevel::Error, "Failed to seek to end of state file \"%s\"\n", filename.c_str());
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
size_t size = ftell(file);
|
||||
rewind(file); // reset the filebuf's position
|
||||
delete state;
|
||||
|
||||
// Allocate exactly as much memory as we need for the savestate
|
||||
std::vector<u8> buffer(size);
|
||||
if (fread(buffer.data(), size, 1, file) == 0)
|
||||
{ // Read the state file into the buffer. If that failed...
|
||||
Platform::Log(Platform::LogLevel::Error, "Failed to read %u-byte state file \"%s\"\n", size, filename.c_str());
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
fclose(file); // done with the file now
|
||||
|
||||
// Get ready to load the state from the buffer into the emulator
|
||||
std::unique_ptr<Savestate> state = std::make_unique<Savestate>(buffer.data(), size, false);
|
||||
|
||||
if (!NDS::DoSavestate(state.get()) || state->Error)
|
||||
{ // If we couldn't load the savestate from the buffer...
|
||||
Platform::Log(Platform::LogLevel::Error, "Failed to load state file \"%s\" into emulator\n", filename.c_str());
|
||||
return false;
|
||||
// current state might be crapoed, so restore from sane backup
|
||||
state = new Savestate("timewarp.mln", false);
|
||||
failed = true;
|
||||
}
|
||||
|
||||
// The backup was made and the state was loaded, so we can store the backup now.
|
||||
BackupState = std::move(backup); // This will clean up any existing backup
|
||||
assert(backup == nullptr);
|
||||
bool res = NDS::DoSavestate(state);
|
||||
delete state;
|
||||
|
||||
if (!res)
|
||||
{
|
||||
failed = true;
|
||||
state = new Savestate("timewarp.mln", false);
|
||||
NDS::DoSavestate(state);
|
||||
delete state;
|
||||
}
|
||||
|
||||
if (failed) return false;
|
||||
|
||||
if (Config::SavestateRelocSRAM && NDSSave)
|
||||
{
|
||||
|
@ -380,41 +351,15 @@ bool LoadState(const std::string& filename)
|
|||
|
||||
bool SaveState(const std::string& filename)
|
||||
{
|
||||
FILE* file = fopen(filename.c_str(), "wb");
|
||||
|
||||
if (file == nullptr)
|
||||
{ // If the file couldn't be opened...
|
||||
return false;
|
||||
}
|
||||
|
||||
Savestate state;
|
||||
if (state.Error)
|
||||
{ // If there was an error creating the state (and allocating its memory)...
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the savestate to the in-memory buffer
|
||||
NDS::DoSavestate(&state);
|
||||
|
||||
if (state.Error)
|
||||
Savestate* state = new Savestate(filename, true);
|
||||
if (state->Error)
|
||||
{
|
||||
fclose(file);
|
||||
delete state;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fwrite(state.Buffer(), state.Length(), 1, file) == 0)
|
||||
{ // Write the Savestate buffer to the file. If that fails...
|
||||
Platform::Log(Platform::Error,
|
||||
"Failed to write %d-byte savestate to %s\n",
|
||||
state.Length(),
|
||||
filename.c_str()
|
||||
);
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
NDS::DoSavestate(state);
|
||||
delete state;
|
||||
|
||||
if (Config::SavestateRelocSRAM && NDSSave)
|
||||
{
|
||||
|
@ -429,14 +374,14 @@ bool SaveState(const std::string& filename)
|
|||
|
||||
void UndoStateLoad()
|
||||
{
|
||||
if (!SavestateLoaded || !BackupState) return;
|
||||
if (!SavestateLoaded) return;
|
||||
|
||||
// Rewind the backup state and put it in load mode
|
||||
BackupState->Rewind(false);
|
||||
// pray that this works
|
||||
// what do we do if it doesn't???
|
||||
// but it should work.
|
||||
NDS::DoSavestate(BackupState.get());
|
||||
Savestate* backup = new Savestate("timewarp.mln", false);
|
||||
NDS::DoSavestate(backup);
|
||||
delete backup;
|
||||
|
||||
if (NDSSave && (!PreviousSaveFile.empty()))
|
||||
{
|
||||
|
@ -569,14 +514,6 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent)
|
|||
return realSize;
|
||||
}
|
||||
|
||||
void ClearBackupState()
|
||||
{
|
||||
if (BackupState != nullptr)
|
||||
{
|
||||
BackupState = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadROM(QStringList filepath, bool reset)
|
||||
{
|
||||
if (filepath.empty()) return false;
|
||||
|
|
|
@ -35,7 +35,6 @@ extern SaveManager* GBASave;
|
|||
QString VerifySetup();
|
||||
void Reset();
|
||||
bool LoadBIOS();
|
||||
void ClearBackupState();
|
||||
|
||||
bool LoadROM(QStringList filepath, bool reset);
|
||||
void EjectCart();
|
||||
|
|
|
@ -1057,6 +1057,8 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event)
|
|||
memcpy(screen[1].scanLine(0), GPU::Framebuffer[frontbuf][1], 256 * 192 * 4);
|
||||
emuThread->FrontBufferLock.unlock();
|
||||
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform, Config::ScreenFilter != 0);
|
||||
|
||||
QRect screenrc(0, 0, 256, 192);
|
||||
|
||||
for (int i = 0; i < numScreens; i++)
|
||||
|
@ -1873,8 +1875,6 @@ void MainWindow::createScreenPanel()
|
|||
}
|
||||
setCentralWidget(panelWidget);
|
||||
|
||||
actScreenFiltering->setEnabled(hasOGL);
|
||||
|
||||
connect(this, SIGNAL(screenLayoutChange()), panelWidget, SLOT(onScreenLayoutChanged()));
|
||||
emit screenLayoutChange();
|
||||
}
|
||||
|
@ -1923,7 +1923,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event)
|
|||
if (event->isAutoRepeat()) return;
|
||||
|
||||
// TODO!! REMOVE ME IN RELEASE BUILDS!!
|
||||
//if (event->key() == Qt::Key_F11) NDS::debug(0);
|
||||
if (event->key() == Qt::Key_F11) NDS::debug(0);
|
||||
|
||||
Input::KeyPress(event);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue