/* gbaloader.cpp Copyright (C) 2007-2009 somebody Copyright (C) 2009 yellow wood goblin SPDX-License-Identifier: GPL-3.0-or-later */ #include "gbaloader.h" #include #include #include #include #include #include #include #include #include "../../share/fifotool.h" #include "exptools.h" #include "gbapatcher.h" #include "language.h" #include "progresswnd.h" #include "sram.h" #define LEN 0x100000 #define LEN_NOR 0x8000 #define MAX_PSRAM 0x1000000 #define MAX_NOR 0x2000000 // writers > class CGbaWriterPSRAM : public CGbaWriter { public: void Open(void); void Write(u32 address, const u8* buffer, u32 size); void Close(void); u32 MaxSize(void); }; void CGbaWriterPSRAM::Open(void) {} void CGbaWriterPSRAM::Write(u32 address, const u8* buffer, u32 size) { cExpansion::WritePSRAM(address, buffer, size); } void CGbaWriterPSRAM::Close(void) {} u32 CGbaWriterPSRAM::MaxSize(void) { return 16 * 1024 * 1024; } class CGbaWriterNor : public CGbaWriter { private: u8* iBuffer; u32 iCurPage; private: enum { EPageSize = 0x40000, EGuardSize = 256 }; private: void Commit(void); void Fill(void); public: void Open(void); void Write(u32 address, const u8* buffer, u32 size); void Close(void); u32 MaxSize(void); }; void CGbaWriterNor::Commit(void) { u8* backupBuffer = NULL; if (iCurPage != 0) { backupBuffer = (u8*)malloc(0x4000); if (backupBuffer) memcpy(backupBuffer, (void*)0x08004000, 0x4000); } expansion().Block_Erase(iCurPage); for (u32 ii = 0; ii < 8; ii++) expansion().WriteNorFlash(iCurPage + ii * 0x8000, iBuffer + ii * 0x8000, 0x8000); if (backupBuffer && memcmp(backupBuffer, (void*)0x08004000, 0x4000) != 0) { expansion().WriteNorFlash(0x4000, backupBuffer, 0x4000); free(backupBuffer); } } void CGbaWriterNor::Fill(void) { memcpy(iBuffer, (void*)(0x08000000 + iCurPage), EPageSize + EGuardSize); } void CGbaWriterNor::Open(void) { iCurPage = 0x666; iBuffer = (u8*)malloc(EPageSize + EGuardSize); } void CGbaWriterNor::Write(u32 address, const u8* buffer, u32 size) { if (iBuffer) { if (iCurPage == 0x666) // first time { iCurPage = address & ~0x3ffff; Fill(); } u32 newPage = address & ~0x3ffff; if (newPage == iCurPage) { memcpy(iBuffer + (address - iCurPage), buffer, size); } else { Commit(); u8 overlap[EGuardSize]; bool overlaped = false; memcpy(overlap, iBuffer + EPageSize, EGuardSize); if (memcmp(overlap, (void*)(0x08000000 + iCurPage + EPageSize), EGuardSize)) { if (newPage - iCurPage == EPageSize) overlaped = true; else { iCurPage += EPageSize; Fill(); memcpy(iBuffer, overlap, EGuardSize); Commit(); } } iCurPage = newPage; Fill(); if (overlaped) memcpy(iBuffer, overlap, EGuardSize); Write(address, buffer, size); } } } void CGbaWriterNor::Close(void) { if (iBuffer) { if (iCurPage != 0x666) { Commit(); } free(iBuffer); } } u32 CGbaWriterNor::MaxSize(void) { return 32 * 1024 * 1024; } // writers < CGbaLoader::CGbaLoader(const std::string& aFileName) : iFileName(aFileName) {} bool CGbaLoader::Load(bool aForce, bool aNotStart) { if (!expansion().IsValid()) return false; bool load = false, nor = false; struct stat st; if (-1 == stat(iFileName.c_str(), &st)) { return false; } iSize = st.st_size; if (iSize > MAX_PSRAM) { nor = true; } else if (iSize > MAX_NOR) { akui::messageBox(NULL, LANG("gba warn", "title"), LANG("gba warn", "text"), MB_OK); return false; } FILE* gbaFile = fopen(iFileName.c_str(), "rb"); if (gbaFile) { sGBAHeader header; fread(&header, 1, sizeof(header), gbaFile); fclose(gbaFile); if (header.is96h == 0x96) { load = LoadInternal(nor, aForce); } } if (!aNotStart && load && !CheckLink()) { StartGBA(); } return load; } bool CGbaLoader::CheckPSRAM(u32 aSize) { return (aSize > MAX_PSRAM) ? false : true; } void CGbaLoader::StartGBA(void) { LoadBorder(); BootGBA(); while (true) swiWaitForVBlank(); } bool CGbaLoader::CheckLink(void) { u32* data = (u32*)0x08000000; if (data[0] == 0xffffffff && data[43] == 0x4a42425a) return true; return false; } void CGbaLoader::LoadBorder(void) { videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE); videoSetModeSub(MODE_5_2D | DISPLAY_BG3_ACTIVE); vramSetPrimaryBanks(VRAM_A_MAIN_BG_0x06000000, VRAM_B_MAIN_BG_0x06020000, VRAM_C_SUB_BG_0x06200000, VRAM_D_LCD); // for the main screen REG_BG3CNT = BG_BMP16_256x256 | BG_BMP_BASE(0) | BG_WRAP_OFF; REG_BG3PA = 1 << 8; // scale x REG_BG3PB = 0; // rotation x REG_BG3PC = 0; // rotation y REG_BG3PD = 1 << 8; // scale y REG_BG3X = 0; // translation x REG_BG3Y = 0; // translation y memset((void*)BG_BMP_RAM(0), 0, 0x18000); memset((void*)BG_BMP_RAM(8), 0, 0x18000); cBMP15 frameBMP = createBMP15FromFile(SFN_GBAFRAME); if (frameBMP.valid() && frameBMP.width() == SCREEN_WIDTH && frameBMP.height() == SCREEN_HEIGHT) { DC_FlushRange(frameBMP.buffer(), SCREEN_WIDTH * SCREEN_HEIGHT * 2); dmaCopy(frameBMP.buffer(), (void*)BG_BMP_RAM(0), SCREEN_WIDTH * SCREEN_HEIGHT * 2); dmaCopy(frameBMP.buffer(), (void*)BG_BMP_RAM(8), SCREEN_WIDTH * SCREEN_HEIGHT * 2); } } void CGbaLoader::BootGBA(void) { sysSetBusOwners(BUS_OWNER_ARM7, BUS_OWNER_ARM7); if (PersonalData->gbaScreen) REG_POWERCNT = 1; else REG_POWERCNT = (POWER_SWAP_LCDS | 1) & 0xffff; fifoSendValue32(FIFO_USER_01, MENU_MSG_GBA); } bool CGbaLoader::LoadPSRAM(void) { bool load = false; int gbaFile = open(iFileName.c_str(), O_RDONLY); if (gbaFile >= 0) { u8* buf = (u8*)malloc(LEN); if (buf) { REG_EXMEMCNT &= ~0x0880; cExpansion::SetRompage(381); cExpansion::OpenNorWrite(); cExpansion::SetSerialMode(); progressWnd().setTipText(LANG("progress window", "gba load")); progressWnd().show(); progressWnd().setPercent(0); for (u32 address = 0; address < iSize && address < MAX_PSRAM; address += LEN) { memset(buf, 0xff, LEN); read(gbaFile, buf, LEN); cExpansion::WritePSRAM(address, buf, LEN); progressWnd().setPercent(address * 100 / iSize); } progressWnd().setPercent(100); progressWnd().hide(); CGbaWriterPSRAM writer; u32 saveSize = CGbaPatcher(iSize, &writer, (u32*)0x08060000, cExpansion::EPsramPage).Patch(); cSram::CreateDefaultFile(iFileName.c_str(), saveSize); cExpansion::CloseNorWrite(); load = true; free(buf); } close(gbaFile); } return load; } bool CGbaLoader::LoadNor(void) { bool load = false; FILE* gbaFile = fopen(iFileName.c_str(), "rb"); if (gbaFile) { u8* buf = (u8*)malloc(LEN_NOR); if (buf) { // erase cExpansion::OpenNorWrite(); cExpansion::SetSerialMode(); progressWnd().setTipText(LANG("progress window", "erase nor")); progressWnd().show(); progressWnd().setPercent(0); for (u32 address = 0; address < iSize && address < MAX_NOR; address += 0x40000) { expansion().Block_Erase(address); progressWnd().setPercent(address * 100 / iSize); } progressWnd().setPercent(100); progressWnd().hide(); // write progressWnd().setTipText(LANG("progress window", "gba load")); progressWnd().show(); progressWnd().setPercent(0); for (u32 address = 0; address < iSize && address < MAX_NOR; address += LEN_NOR) { memset(buf, 0xff, LEN_NOR); fread(buf, LEN_NOR, 1, gbaFile); expansion().WriteNorFlash(address, buf, LEN_NOR); progressWnd().setPercent(address * 100 / iSize); } progressWnd().setPercent(100); progressWnd().hide(); CGbaWriterNor writer; u32 saveSize = CGbaPatcher(iSize, &writer, (u32*)0x08000000, cExpansion::ENorPage).Patch(); cSram::CreateDefaultFile(iFileName.c_str(), saveSize); /* FILE *log; log=fopen("fat:/test.bin","wb"); fwrite((void*)0x08000000,iSize,1,log); fclose(log); // */ cExpansion::CloseNorWrite(); load = true; free(buf); } fclose(gbaFile); } return load; } void CGbaLoader::InitNor(void) { cExpansion::SetRompage(0); expansion().SetRampage(cExpansion::ENorPage); } void CGbaLoader::InitPSRAM(void) { cExpansion::SetRompage(384); expansion().SetRampage(cExpansion::EPsramPage); } bool CGbaLoader::StoreOldSave(std::string& aFileName) { bool res = false; const u8 sign[] = "ACEKARD R.P.G GBA SIGN*"; u8 buffer[sizeof(sign)]; expansion().SetRampage(0); cExpansion::ReadSram(0x0A000000, buffer, sizeof(buffer)); if (memcmp(buffer, sign, sizeof(buffer))) // old save { if (aFileName != "") { cSram::SaveSramToFile(aFileName.c_str(), 0); res = true; } expansion().SetRampage(0); cExpansion::WriteSram(0x0A000000, sign, sizeof(sign)); } return res; } bool CGbaLoader::LoadInternal(bool nor, bool force) { bool load = false; expansion().SoftReset(); std::string oldFile, oldFileNOR; CIniFile f; oldFile = f.LoadIniFile(SFN_LAST_GBA_SAVEINFO) ? f.GetString("Save Info", "lastLoaded", "") : ""; oldFileNOR = f.LoadIniFile(SFN_LAST_GBA_SAVEINFO) ? f.GetString("Save Info", "lastLoadedNOR", "") : ""; if (StoreOldSave(oldFile)) { oldFile = ""; f.SetString("Save Info", "lastLoaded", ""); f.SaveIniFile(SFN_LAST_GBA_SAVEINFO); } u32 state = 0; if (nor) state |= 1; if (oldFile == iFileName) state |= 2; if (oldFileNOR == iFileName) state |= 4; if (force) state |= 8; switch (state) { case 0: if (oldFile != "") cSram::SaveSramToFile(oldFile.c_str(), cExpansion::EPsramPage); cSram::LoadSramFromFile(iFileName.c_str(), cExpansion::EPsramPage); f.SetString("Save Info", "lastLoaded", iFileName); f.SaveIniFile(SFN_LAST_GBA_SAVEINFO); load = LoadPSRAM(); InitPSRAM(); break; case 10: case 11: if (oldFile != "") cSram::SaveSramToFile(oldFile.c_str(), cExpansion::EPsramPage); f.SetString("Save Info", "lastLoaded", ""); case 1: case 8: case 9: if (oldFileNOR != "") cSram::SaveSramToFile(oldFileNOR.c_str(), cExpansion::ENorPage); cSram::LoadSramFromFile(iFileName.c_str(), cExpansion::ENorPage); f.SetString("Save Info", "lastLoadedNOR", iFileName); f.SaveIniFile(SFN_LAST_GBA_SAVEINFO); load = LoadNor(); InitNor(); break; case 2: case 3: load = LoadPSRAM(); InitPSRAM(); break; case 4: case 5: case 6: case 7: case 12: case 13: case 14: case 15: load = true; InitNor(); break; default: expansion().SoftReset(); break; } return load; }