/* gbaloader.cpp Copyright (C) 2007-2009 somebody Copyright (C) 2009 yellow wood goblin This program 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. This program 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 this program. If not, see . */ #include #include #include #include #include #include #include #include "exptools.h" #include "../../share/fifotool.h" #include "progresswnd.h" #include "language.h" #include "sram.h" #include "gbapatcher.h" #include "gbaloader.h" #include #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(); ELM_Unmount(); 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); vramSetMainBanks(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