/* cheatwnd.cpp Portions copyright (C) 2008 Normmatt, www.normmatt.com, Smiths (www.emuholic.com) Portions copyright (C) 2008 bliss (bliss@hanirc.org) 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 "cheatwnd.h" #include "uisettings.h" #include "windowmanager.h" #include "language.h" #include "msgbox.h" #include "../../share/gamecode.h" #include #include using namespace akui; #define CRCPOLY 0xedb88320 static u32 crc32(const u8* p,size_t len) { u32 crc=-1; while(len--) { crc^=*p++; for(int ii=0;ii<8;++ii) crc=(crc>>1)^((crc&1)?CRCPOLY:0); } return crc; } cCheatWnd::cCheatWnd(s32 x,s32 y,u32 w,u32 h,cWindow* parent,const std::string& text): cForm(x,y,w,h,parent,text), _buttonDeselect(0,0,46,18,this,"\x05 Deselect"), _buttonInfo(0,0,46,18,this,"\x04 Info"), _buttonGenerate(0,0,46,18,this,"\x03 OK"), _buttonCancel(0,0,46,18,this,"\x02 Cancel"), _List(0,0,w-8,h-44,this,"cheat tree") { s16 buttonY=size().y-_buttonCancel.size().y-4; _buttonCancel.setStyle(cButton::press); _buttonCancel.setText("\x02 "+LANG("setting window","cancel")); _buttonCancel.setTextColor(uis().buttonTextColor); _buttonCancel.loadAppearance(SFN_BUTTON3); _buttonCancel.clicked.connect(this,&cCheatWnd::onCancel); addChildWindow(&_buttonCancel); _buttonGenerate.setStyle(cButton::press); _buttonGenerate.setText( "\x03 " + LANG( "setting window", "ok" ) ); _buttonGenerate.setTextColor(uis().buttonTextColor); _buttonGenerate.loadAppearance(SFN_BUTTON2); _buttonGenerate.clicked.connect(this,&cCheatWnd::onGenerate); addChildWindow(&_buttonGenerate); _buttonInfo.setStyle(cButton::press); _buttonInfo.setText( "\x04 " + LANG( "cheats", "info" ) ); _buttonInfo.setTextColor(uis().buttonTextColor); _buttonInfo.loadAppearance(SFN_BUTTON3); _buttonInfo.clicked.connect(this,&cCheatWnd::onInfo); addChildWindow(&_buttonInfo); _buttonDeselect.setStyle(cButton::press); _buttonDeselect.setText( "\x05 " + LANG( "cheats", "deselect" ) ); _buttonDeselect.setTextColor(uis().buttonTextColor); _buttonDeselect.loadAppearance(SFN_BUTTON4); _buttonDeselect.clicked.connect(this,&cCheatWnd::onDeselectAll); addChildWindow(&_buttonDeselect); s16 nextButtonX=size().x; s16 buttonPitch=_buttonCancel.size().x+4; nextButtonX-=buttonPitch; _buttonCancel.setRelativePosition(cPoint(nextButtonX,buttonY)); buttonPitch=_buttonGenerate.size().x+4; nextButtonX-=buttonPitch; _buttonGenerate.setRelativePosition(cPoint(nextButtonX,buttonY)); buttonPitch=_buttonInfo.size().x+4; nextButtonX-=buttonPitch; _buttonInfo.setRelativePosition(cPoint(nextButtonX,buttonY)); buttonPitch=_buttonDeselect.size().x+4; nextButtonX-=buttonPitch; _buttonDeselect.setRelativePosition(cPoint(nextButtonX,buttonY)); _List.setRelativePosition(cPoint(4, 20)); //CIniFile ini(SFN_UI_SETTINGS); //_List.setColors(ini.GetInt("main list","textColor",RGB15(7,7,7)),ini.GetInt("main list","textColorHilight",RGB15(31,0,31)),ini.GetInt("main list","selectionBarColor1",RGB15(16,20,24)),ini.GetInt("main list","selectionBarColor2",RGB15(20,25,0))); _List.insertColumn(0,"icon",EIconWidth); _List.insertColumn(1,"showName",_List.size().x+2-EIconWidth); _List.arangeColumnsSize(); _List.setRowHeight(ERowHeight); _List.selectedRowClicked.connect(this,&cCheatWnd::onItemClicked); _List.ownerDraw.connect(this,&cCheatWnd::onDraw); addChildWindow(&_List); loadAppearance(""); arrangeChildren(); } cCheatWnd::~cCheatWnd() {} void cCheatWnd::draw() { _renderDesc.draw(windowRectangle(),_engine); cForm::draw(); } bool cCheatWnd::process(const akui::cMessage& msg) { bool ret=false; ret=cForm::process(msg); if(!ret) { if(msg.id()>cMessage::keyMessageStart&&msg.id()0) { if(_data[_indexes[ii]]._flags&cParsedItem::EFolder) break; } _List.selectRow(ii); } ret=true; break; case cKeyMessage::UI_KEY_RIGHT: { size_t ii=_List.selectedRowId(),top=_List.getRowCount(); while(++ii::iterator itr=_data.begin(); while(itr!=_data.end()) { (*itr)._flags&=~cParsedItem::ESelected; ++itr; } } void cCheatWnd::onInfo(void) { size_t index=_indexes[_List.selectedRowId()]; std::string body(_data[index]._title); body+="\n\n"; body+=_data[index]._comment; messageBox(this,LANG("cheats","title"),body,MB_OK); } void cCheatWnd::onCancel(void) { cForm::onCancel(); } static void updateDB(u8 value,u32 offset,FILE* db) { u8 oldvalue; if(!db) return; if(!offset) return; if(fseek(db,offset,SEEK_SET)) return; if(fread(&oldvalue,sizeof(oldvalue),1,db)!=1) return; if(oldvalue!=value) { if(fseek(db,offset,SEEK_SET)) return; fwrite(&value,sizeof(value),1,db); } } void cCheatWnd::onGenerate(void) { NandFast(); FILE* db=fopen(SFN_CHEATS,"r+b"); if(db) { std::vector::iterator itr=_data.begin(); while(itr!=_data.end()) { updateDB(((*itr)._flags&cParsedItem::ESelected)?1:0,(*itr)._offset,db); ++itr; } fclose(db); } NandFlush(); cForm::onOK(); } void cCheatWnd::drawMark(const cListView::cOwnerDraw& od,u16 width) { u16 color=gdi().getPenColor(od._engine); u16 size=od._size.y-ESelectTop*2; gdi().fillRect(color,color,od._position.x+((width-size)>>1)-1,od._position.y+ESelectTop,size,size,od._engine); } void cCheatWnd::onDraw(const cListView::cOwnerDraw& od) { size_t index=_indexes[od._row]; u32 flags=_data[index]._flags; if(od._col==EIconColumn) { if(flags&cParsedItem::EFolder) { u16 size=od._size.y-EFolderTop*2; s16 x1=od._position.x+((od._size.x-size)>>1)-1,x2=x1+(size>>1),y1=od._position.y+EFolderTop,y2=y1+(size>>1); gdi().frameRect(x1,y1,size,size,od._engine); gdi().drawLine(x1,y2,x1+size,y2,od._engine); if(!(flags&cParsedItem::EOpen)) gdi().drawLine(x2,y1,x2,y1+size,od._engine); } else if(!(flags&cParsedItem::EInFolder)) { if(flags&cParsedItem::ESelected) drawMark(od,od._size.x); } } else if(od._col==ETextColumn) { if(flags&cParsedItem::EInFolder) { if(flags&cParsedItem::ESelected) drawMark(od,EFolderWidth); } s16 x=od._position.x; u16 w=od._size.x; if(flags&cParsedItem::EInFolder) { x+=EFolderWidth; w-=EFolderWidth; } gdi().textOutRect(x,od._textY,w,od._textHeight,od._text,od._engine); } } bool cCheatWnd::parse(const std::string& aFileName) { bool res=false; _fileName=aFileName; u32 romcrc32,gamecode; if(romData(_fileName,gamecode,romcrc32)) { FILE* dat=fopen(SFN_CHEATS,"rb"); if(dat) { res=parseInternal(dat,gamecode,romcrc32); fclose(dat); } } return res; } bool cCheatWnd::romData(const std::string& aFileName,u32& aGameCode,u32& aCrc32) { bool res=false; FILE* rom=fopen(aFileName.c_str(),"rb"); if(rom) { u8 header[512]; if(1==fread(header,sizeof(header),1,rom)) { aCrc32=crc32(header,sizeof(header)); aGameCode=gamecode((const char*)(header+12)); res=true; } fclose(rom); } return res; } bool cCheatWnd::searchCheatData(FILE* aDat,u32 gamecode,u32 crc32,long& aPos,size_t& aSize) { aPos=0; aSize=0; const char* KHeader="R4 CheatCode"; char header[12]; fread(header,12,1,aDat); if(strncmp(KHeader,header,12)) return false; sDatIndex idx,nidx; fseek(aDat,0,SEEK_END); long fileSize=ftell(aDat); fseek(aDat,0x100,SEEK_SET); fread(&nidx,sizeof(nidx),1,aDat); bool done=false; while(!done) { memcpy(&idx,&nidx,sizeof(idx)); fread(&nidx,sizeof(nidx),1,aDat); if(gamecode==idx._gameCode&&crc32==idx._crc32) { aSize=((nidx._offset)?nidx._offset:fileSize)-idx._offset; aPos=idx._offset; done=true; } if(!nidx._offset) done=true; } return (aPos&&aSize); } bool cCheatWnd::parseInternal(FILE* aDat,u32 gamecode,u32 crc32) { dbg_printf("%x, %x\n",gamecode,crc32); _data.clear(); long dataPos; size_t dataSize; if(!searchCheatData(aDat,gamecode,crc32,dataPos,dataSize)) return false; fseek(aDat,dataPos,SEEK_SET); dbg_printf("record found: %d\n",dataSize); char* buffer=(char*)malloc(dataSize); if(!buffer) return false; fread(buffer,dataSize,1,aDat); char* gameTitle=buffer; u32* ccode=(u32*)(((u32)gameTitle+strlen(gameTitle)+4)&~3); u32 cheatCount=*ccode; cheatCount&=0x0fffffff; ccode+=9; u32 cc=0; while(cc>28)&1) { flagItem|=cParsedItem::EInFolder; if((*ccode>>24)==0x11) flagItem|=cParsedItem::EOne; folderCount=*ccode&0x00ffffff; folderName=(char*)((u32)ccode+4); folderNote=(char*)((u32)folderName+strlen(folderName)+1); _data.push_back(cParsedItem(folderName,folderNote,cParsedItem::EFolder)); cc++; ccode=(u32*)(((u32)folderName+strlen(folderName)+1+strlen(folderNote)+1+3)&~3); } u32 selectValue=cParsedItem::ESelected; for(size_t ii=0;ii::iterator itr=_data.begin(); while(itr!=_data.end()) { std::vector row; row.push_back(""); row.push_back((*itr)._title); _List.insertRow(_List.getRowCount(),row); _indexes.push_back(itr-_data.begin()); u32 flags=(*itr)._flags; ++itr; if((flags&cParsedItem::EFolder)&&(flags&cParsedItem::EOpen)==0) { while(((*itr)._flags&cParsedItem::EInFolder)&&itr!=_data.end()) ++itr; } } } void cCheatWnd::deselectFolder(size_t anIndex) { std::vector::iterator itr=_data.begin()+anIndex; while(--itr>=_data.begin()) { if((*itr)._flags&cParsedItem::EFolder) { ++itr; break; } } while(((*itr)._flags&cParsedItem::EInFolder)&&itr!=_data.end()) { (*itr)._flags&=~cParsedItem::ESelected; ++itr; } }