mirror of
https://github.com/coderkei/akmenu-next.git
synced 2025-06-18 08:55:46 -04:00
510 lines
13 KiB
C++
510 lines
13 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "cheatwnd.h"
|
|
#include "uisettings.h"
|
|
#include "windowmanager.h"
|
|
#include "language.h"
|
|
#include "msgbox.h"
|
|
#include "../../share/gamecode.h"
|
|
#include <sys/stat.h>
|
|
#include <elm.h>
|
|
|
|
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()<cMessage::keyMessageEnd)
|
|
{
|
|
ret=processKeyMessage((cKeyMessage&)msg);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool cCheatWnd::processKeyMessage(const cKeyMessage& msg)
|
|
{
|
|
bool ret=false;
|
|
if(msg.id()==cMessage::keyDown)
|
|
{
|
|
switch(msg.keyCode())
|
|
{
|
|
case cKeyMessage::UI_KEY_DOWN:
|
|
_List.selectNext();
|
|
ret=true;
|
|
break;
|
|
case cKeyMessage::UI_KEY_UP:
|
|
_List.selectPrev();
|
|
ret=true;
|
|
break;
|
|
case cKeyMessage::UI_KEY_LEFT:
|
|
{
|
|
size_t ii=_List.selectedRowId();
|
|
while(--ii>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<top)
|
|
{
|
|
if(_data[_indexes[ii]]._flags&cParsedItem::EFolder) break;
|
|
}
|
|
_List.selectRow(ii);
|
|
}
|
|
ret=true;
|
|
break;
|
|
case cKeyMessage::UI_KEY_A:
|
|
onSelect();
|
|
ret=true;
|
|
break;
|
|
case cKeyMessage::UI_KEY_B:
|
|
onCancel();
|
|
ret=true;
|
|
break;
|
|
case cKeyMessage::UI_KEY_X:
|
|
onGenerate();
|
|
ret=true;
|
|
break;
|
|
case cKeyMessage::UI_KEY_Y:
|
|
onInfo();
|
|
ret=true;
|
|
break;
|
|
case cKeyMessage::UI_KEY_L:
|
|
onDeselectAll();
|
|
ret=true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
cWindow& cCheatWnd::loadAppearance(const std::string& aFileName )
|
|
{
|
|
_renderDesc.loadData(SFN_FORM_TITLE_L,SFN_FORM_TITLE_R,SFN_FORM_TITLE_M);
|
|
_renderDesc.setTitleText(_text);
|
|
return *this;
|
|
}
|
|
|
|
void cCheatWnd::onItemClicked(u32 index)
|
|
{
|
|
(void)index;
|
|
onSelect();
|
|
}
|
|
|
|
void cCheatWnd::onSelect(void)
|
|
{
|
|
size_t index=_indexes[_List.selectedRowId()];
|
|
if(_data[index]._flags&cParsedItem::EFolder)
|
|
{
|
|
_data[index]._flags^=cParsedItem::EOpen;
|
|
u32 first=_List.firstVisibleRowId(),row=_List.selectedRowId();
|
|
generateList();
|
|
_List.setFirstVisibleIdAndSelectRow(first,row);
|
|
}
|
|
else
|
|
{
|
|
bool deselect=(_data[index]._flags&(cParsedItem::EOne|cParsedItem::ESelected))==(cParsedItem::EOne|cParsedItem::ESelected);
|
|
if(_data[index]._flags&cParsedItem::EOne) deselectFolder(index);
|
|
if(!deselect) _data[index]._flags^=cParsedItem::ESelected;
|
|
}
|
|
}
|
|
|
|
void cCheatWnd::onDeselectAll(void)
|
|
{
|
|
std::vector<cParsedItem>::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<cParsedItem>::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<cheatCount)
|
|
{
|
|
u32 folderCount=1;
|
|
char* folderName=NULL;
|
|
char* folderNote=NULL;
|
|
u32 flagItem=0;
|
|
if((*ccode>>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<folderCount;++ii)
|
|
{
|
|
char* cheatName=(char*)((u32)ccode+4);
|
|
char* cheatNote=(char*)((u32)cheatName+strlen(cheatName)+1);
|
|
u32* cheatData=(u32*)(((u32)cheatNote+strlen(cheatNote)+1+3)&~3);
|
|
u32 cheatDataLen=*cheatData++;
|
|
|
|
if(cheatDataLen)
|
|
{
|
|
_data.push_back(cParsedItem(cheatName,cheatNote,flagItem|((*ccode&0xff000000)?selectValue:0),dataPos+(((char*)ccode+3)-buffer)));
|
|
if((*ccode&0xff000000)&&(flagItem&cParsedItem::EOne)) selectValue=0;
|
|
for(size_t jj=0;jj<cheatDataLen;++jj)
|
|
{
|
|
_data.back()._cheat+=formatString("%08X",*(cheatData+jj));
|
|
_data.back()._cheat+=((jj+1)%2)?" ":"\n";
|
|
}
|
|
if(cheatDataLen%2) _data.back()._cheat+="\n";
|
|
}
|
|
cc++;
|
|
ccode=(u32*)((u32)ccode+(((*ccode&0x00ffffff)+1)*4));
|
|
}
|
|
}
|
|
free(buffer);
|
|
generateList();
|
|
return true;
|
|
}
|
|
|
|
void cCheatWnd::generateList(void)
|
|
{
|
|
_indexes.clear();
|
|
_List.removeAllRows();
|
|
|
|
std::vector<cParsedItem>::iterator itr=_data.begin();
|
|
while(itr!=_data.end())
|
|
{
|
|
std::vector<std::string> 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<cParsedItem>::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;
|
|
}
|
|
}
|