mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-31 01:59:52 -06:00
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@473 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
655
Source/Core/DolphinWX/Src/MemoryCards/GCMemcard.cpp
Normal file
655
Source/Core/DolphinWX/Src/MemoryCards/GCMemcard.cpp
Normal file
@ -0,0 +1,655 @@
|
||||
// gcmc.cpp: define el punto de entrada de la aplicaci<63>n de consola.
|
||||
//
|
||||
#ifdef _WIN32
|
||||
#include "stdafx.h"
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <memory.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "GCMemcard.h"
|
||||
|
||||
void GCMemcard::calc_checksumsBE(u16 *buf, u32 num, u16 *c1, u16 *c2)
|
||||
{
|
||||
*c1 = 0;*c2 = 0;
|
||||
for (u32 i = 0; i < num; ++i)
|
||||
{
|
||||
//weird warnings here
|
||||
*c1 += bswap16(buf[i]);
|
||||
*c2 += bswap16((u16)(buf[i] ^ 0xffff));
|
||||
}
|
||||
if (*c1 == 0xffff)
|
||||
{
|
||||
*c1 = 0;
|
||||
}
|
||||
if (*c2 == 0xffff)
|
||||
{
|
||||
*c2 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 GCMemcard::GetNumFiles()
|
||||
{
|
||||
if(!mcdFile) return 0;
|
||||
|
||||
for(int i=0;i<126;i++)
|
||||
{
|
||||
if(BE32(dir.Dir[i].Gamecode)==0xFFFFFFFF)
|
||||
return i;
|
||||
}
|
||||
return 127;
|
||||
}
|
||||
|
||||
bool GCMemcard::RemoveFile(u32 index) //index in the directory array
|
||||
{
|
||||
if(!mcdFile) return false;
|
||||
|
||||
//backup the directory and bat (not really needed here but meh :P
|
||||
dir_backup=dir;
|
||||
bat_backup=bat;
|
||||
|
||||
int totalspace = (((u32)BE16(hdr.Size)*16)-5);
|
||||
|
||||
//free the blocks
|
||||
int blocks_left = BE16(dir.Dir[index].BlockCount);
|
||||
int block = BE16(dir.Dir[index].FirstBlock);
|
||||
do
|
||||
{
|
||||
int cbi = block-5;
|
||||
int nextblock=bswap16(bat.Map[cbi]);
|
||||
//assert(nextblock!=0);
|
||||
if(nextblock==0)
|
||||
{
|
||||
nextblock = block+1;
|
||||
}
|
||||
|
||||
bat.Map[cbi]=0;
|
||||
|
||||
block=nextblock;
|
||||
blocks_left--;
|
||||
}
|
||||
while((block!=0xffff)&&(blocks_left>0));
|
||||
|
||||
//delete directory entry
|
||||
for(int i=index;i<125;i++)
|
||||
{
|
||||
dir.Dir[i]=dir.Dir[i+1];
|
||||
}
|
||||
memset(&(dir.Dir[125]),0xFF,sizeof(DEntry));
|
||||
|
||||
//pack blocks to remove free space partitioning, assume no fragmentation.
|
||||
u8 *mc_data2 = new u8[mc_data_size];
|
||||
|
||||
int firstFree=0;
|
||||
for(int i=0;i<126;i++)
|
||||
{
|
||||
if(BE32(dir.Dir[i].Gamecode)==0xFFFFFFFF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int fb = BE16(dir.Dir[i].FirstBlock);
|
||||
int bc = BE16(dir.Dir[i].BlockCount);
|
||||
|
||||
u8* src = mc_data + (fb-5)*0x2000;
|
||||
u8* dst = mc_data2 + firstFree*0x2000;
|
||||
|
||||
memcpy(dst,src,bc*0x2000);
|
||||
|
||||
for(int j=0;j<bc;j++)
|
||||
{
|
||||
bat.Map[firstFree+j] = bswap16(u16(firstFree+j+6));
|
||||
}
|
||||
bat.Map[firstFree+bc-1] = 0xFFFF;
|
||||
|
||||
dir.Dir[i].FirstBlock[0] = u8((firstFree+5)>>8);
|
||||
dir.Dir[i].FirstBlock[1] = u8((firstFree+5));
|
||||
|
||||
firstFree += bc;
|
||||
}
|
||||
|
||||
for(int j=firstFree;j<totalspace;j++)
|
||||
{
|
||||
bat.Map[j] = 0;
|
||||
}
|
||||
|
||||
firstFree+=4;
|
||||
bat.LastAllocated[0] = u8(firstFree>>8);
|
||||
bat.LastAllocated[1] = u8(firstFree);
|
||||
|
||||
delete [] mc_data;
|
||||
mc_data = mc_data2;
|
||||
//--
|
||||
|
||||
//update freespace counter
|
||||
int freespace1 = totalspace - firstFree;
|
||||
bat.FreeBlocks[0] = u8(freespace1>>8);
|
||||
bat.FreeBlocks[1] = u8(freespace1);
|
||||
|
||||
// ... and update counter
|
||||
int updateCtr = BE16(dir.UpdateCounter)+1;
|
||||
dir.UpdateCounter[0] = u8(updateCtr>>8);
|
||||
dir.UpdateCounter[1] = u8(updateCtr);
|
||||
|
||||
//fix checksums
|
||||
u16 csum1=0,csum2=0;
|
||||
calc_checksumsBE((u16*)&dir,0xFFE,&csum1,&csum2);
|
||||
dir.CheckSum1[0]=u8(csum1>>8);
|
||||
dir.CheckSum1[1]=u8(csum1);
|
||||
dir.CheckSum2[0]=u8(csum2>>8);
|
||||
dir.CheckSum2[1]=u8(csum2);
|
||||
calc_checksumsBE((u16*)(((u8*)&bat)+4),0xFFE,&csum1,&csum2);
|
||||
bat.CheckSum1[0]=u8(csum1>>8);
|
||||
bat.CheckSum1[1]=u8(csum1);
|
||||
bat.CheckSum2[0]=u8(csum2>>8);
|
||||
bat.CheckSum2[1]=u8(csum2);
|
||||
|
||||
dir_backup=dir;
|
||||
bat_backup=bat;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 GCMemcard::ImportFile(DEntry& direntry, u8* contents)
|
||||
{
|
||||
if(!mcdFile) return 0;
|
||||
|
||||
if(BE16(bat.FreeBlocks)<BE16(direntry.BlockCount))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// find first free data block -- assume no freespace fragmentation
|
||||
int totalspace = (((u32)BE16(hdr.Size)*16)-5);
|
||||
|
||||
int firstFree1 = BE16(bat.LastAllocated)+1;
|
||||
|
||||
int firstFree2 = 0;
|
||||
for(int i=0;i<totalspace;i++)
|
||||
{
|
||||
if(bat.Map[i]==0)
|
||||
{
|
||||
firstFree2=i+5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int firstFree3 = 0;
|
||||
for(int i=0;i<126;i++)
|
||||
{
|
||||
if(BE32(dir.Dir[i].Gamecode)==0xFFFFFFFF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
firstFree3 = max(firstFree3,BE16(dir.Dir[i].FirstBlock) + BE16(dir.Dir[i].BlockCount));
|
||||
}
|
||||
}
|
||||
|
||||
if(firstFree2 > firstFree1) firstFree1 = firstFree2;
|
||||
if(firstFree3 > firstFree1) firstFree1 = firstFree3;
|
||||
|
||||
if(firstFree1>=126)
|
||||
{
|
||||
// TODO: show messagebox about the error
|
||||
return 0;
|
||||
}
|
||||
|
||||
// find first free dir entry
|
||||
int index=-1;
|
||||
for(int i=0;i<127;i++)
|
||||
{
|
||||
if(BE32(dir.Dir[i].Gamecode)==0xFFFFFFFF)
|
||||
{
|
||||
index=i;
|
||||
dir.Dir[i] = direntry;
|
||||
dir.Dir[i].FirstBlock[0] = u8(firstFree1>>8);
|
||||
dir.Dir[i].FirstBlock[1] = u8(firstFree1);
|
||||
dir.Dir[i].CopyCounter = dir.Dir[i].CopyCounter+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// keep assuming no freespace fragmentation, and copy over all the data
|
||||
u8*destination = mc_data + (firstFree1-5)*0x2000;
|
||||
|
||||
int fileBlocks=BE16(direntry.BlockCount);
|
||||
memcpy(destination,contents,0x2000*fileBlocks);
|
||||
|
||||
|
||||
//update freespace counter
|
||||
int freespace1 = totalspace - firstFree1;
|
||||
bat.FreeBlocks[0] = u8(freespace1>>8);
|
||||
bat.FreeBlocks[1] = u8(freespace1);
|
||||
|
||||
// ... and update counter
|
||||
int updateCtr = BE16(dir.UpdateCounter)+1;
|
||||
dir.UpdateCounter[0] = u8(updateCtr>>8);
|
||||
dir.UpdateCounter[1] = u8(updateCtr);
|
||||
|
||||
//fix checksums
|
||||
u16 csum1=0,csum2=0;
|
||||
calc_checksumsBE((u16*)&dir,0xFFE,&csum1,&csum2);
|
||||
dir.CheckSum1[0]=u8(csum1>>8);
|
||||
dir.CheckSum1[1]=u8(csum1);
|
||||
dir.CheckSum2[0]=u8(csum2>>8);
|
||||
dir.CheckSum2[1]=u8(csum2);
|
||||
calc_checksumsBE((u16*)(((u8*)&bat)+4),0xFFE,&csum1,&csum2);
|
||||
bat.CheckSum1[0]=u8(csum1>>8);
|
||||
bat.CheckSum1[1]=u8(csum1);
|
||||
bat.CheckSum2[0]=u8(csum2>>8);
|
||||
bat.CheckSum2[1]=u8(csum2);
|
||||
|
||||
return fileBlocks;
|
||||
}
|
||||
|
||||
bool GCMemcard::GetFileData(u32 index, u8*dest) //index in the directory array
|
||||
{
|
||||
if(!mcdFile) return false;
|
||||
|
||||
int block = BE16(dir.Dir[index].FirstBlock);
|
||||
assert((block!=0xFFFF)&&(block>0));
|
||||
do
|
||||
{
|
||||
int nextblock=bswap16(bat.Map[block-5]);
|
||||
assert(nextblock>0);
|
||||
|
||||
memcpy(dest,mc_data + 0x2000*(block-5),0x2000);
|
||||
dest+=0x2000;
|
||||
|
||||
block=nextblock;
|
||||
}
|
||||
while(block!=0xffff);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 GCMemcard::GetFileSize(u32 index) //index in the directory array
|
||||
{
|
||||
if(!mcdFile) return 0;
|
||||
|
||||
return BE16(dir.Dir[index].BlockCount);
|
||||
}
|
||||
|
||||
bool GCMemcard::GetFileInfo(u32 index, GCMemcard::DEntry& info) //index in the directory array
|
||||
{
|
||||
if(!mcdFile) return false;
|
||||
|
||||
info = dir.Dir[index];
|
||||
return true;
|
||||
}
|
||||
bool GCMemcard::GetFileName(u32 index, char *fn) //index in the directory array
|
||||
{
|
||||
if(!mcdFile) return false;
|
||||
|
||||
memcpy(fn,(const char*)dir.Dir[index].Filename,32);
|
||||
fn[31]=0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GCMemcard::GetComment1(u32 index, char *fn) //index in the directory array
|
||||
{
|
||||
if(!mcdFile) return false;
|
||||
|
||||
u32 Comment1 =BE32(dir.Dir[index].CommentsAddr);
|
||||
u32 DataBlock =BE16(dir.Dir[index].FirstBlock)-5;
|
||||
if(Comment1==0xFFFFFFFF)
|
||||
{
|
||||
fn[0]=0;
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(fn,mc_data +(DataBlock*0x2000) + Comment1,32);
|
||||
fn[31]=0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GCMemcard::GetComment2(u32 index, char *fn) //index in the directory array
|
||||
{
|
||||
if(!mcdFile) return false;
|
||||
|
||||
u32 Comment1 =BE32(dir.Dir[index].CommentsAddr);
|
||||
u32 Comment2 =Comment1+32;
|
||||
u32 DataBlock =BE16(dir.Dir[index].FirstBlock)-5;
|
||||
if(Comment1==0xFFFFFFFF)
|
||||
{
|
||||
fn[0]=0;
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(fn,mc_data +(DataBlock*0x2000) + Comment2,32);
|
||||
fn[31]=0;
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 decode5A3(u16 val)
|
||||
{
|
||||
const int lut5to8[] = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39,
|
||||
0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B,
|
||||
0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD,
|
||||
0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF};
|
||||
const int lut4to8[] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
|
||||
0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF};
|
||||
const int lut3to8[] = { 0x00,0x24,0x48,0x6D,0x91,0xB6,0xDA,0xFF};
|
||||
|
||||
|
||||
int r,g,b,a;
|
||||
if ((val&0x8000))
|
||||
{
|
||||
r=lut5to8[(val>>10) & 0x1f];
|
||||
g=lut5to8[(val>>5 ) & 0x1f];
|
||||
b=lut5to8[(val ) & 0x1f];
|
||||
a=0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
a=lut3to8[(val>>12) & 0x7];
|
||||
r=lut4to8[(val>>8 ) & 0xf];
|
||||
g=lut4to8[(val>>4 ) & 0xf];
|
||||
b=lut4to8[(val ) & 0xf];
|
||||
}
|
||||
return (a<<24) | (r<<16) | (g<<8) | b;
|
||||
}
|
||||
|
||||
void decode5A3image(u32* dst, u16* src, int width, int height)
|
||||
{
|
||||
for (int y = 0; y < height; y += 4)
|
||||
{
|
||||
for (int x = 0; x < width; x += 4)
|
||||
{
|
||||
for (int iy = 0; iy < 4; iy++, src += 4)
|
||||
{
|
||||
for (int ix = 0; ix < 4; ix++)
|
||||
{
|
||||
u32 RGBA = decode5A3(bswap16(src[ix]));
|
||||
dst[(y + iy) * width + (x + ix)] = RGBA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decodeCI8image(u32* dst, u8* src, u16* pal, int width, int height)
|
||||
{
|
||||
for (int y = 0; y < height; y += 4)
|
||||
{
|
||||
for (int x = 0; x < width; x += 8)
|
||||
{
|
||||
for (int iy = 0; iy < 4; iy++, src += 8)
|
||||
{
|
||||
u32 *tdst = dst+(y+iy)*width+x;
|
||||
for (int ix = 0; ix < 8; ix++)
|
||||
{
|
||||
tdst[ix] = decode5A3(bswap16(pal[src[ix]]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GCMemcard::ReadBannerRGBA8(u32 index, u32* buffer)
|
||||
{
|
||||
if(!mcdFile) return false;
|
||||
|
||||
int flags = dir.Dir[index].BIFlags;
|
||||
|
||||
int bnrFormat = (flags&3);
|
||||
|
||||
if(bnrFormat==0)
|
||||
return false;
|
||||
|
||||
u32 DataOffset=BE32(dir.Dir[index].ImageOffset);
|
||||
u32 DataBlock =BE16(dir.Dir[index].FirstBlock)-5;
|
||||
|
||||
if(DataOffset==0xFFFFFFFF)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const int pixels = 96*32;
|
||||
|
||||
if(bnrFormat&1)
|
||||
{
|
||||
u8 *pxdata = (u8* )(mc_data +(DataBlock*0x2000) + DataOffset);
|
||||
u16 *paldata = (u16*)(mc_data +(DataBlock*0x2000) + DataOffset + pixels);
|
||||
|
||||
decodeCI8image(buffer,pxdata,paldata,96,32);
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 *pxdata = (u16*)(mc_data +(DataBlock*0x2000) + DataOffset);
|
||||
|
||||
decode5A3image(buffer,pxdata,96,32);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 GCMemcard::ReadAnimRGBA8(u32 index, u32* buffer, u8 *delays)
|
||||
{
|
||||
if(!mcdFile) return 0;
|
||||
|
||||
int formats = BE16(dir.Dir[index].IconFmt);
|
||||
int fdelays = BE16(dir.Dir[index].AnimSpeed);
|
||||
|
||||
int flags = dir.Dir[index].BIFlags;
|
||||
|
||||
int bnrFormat = (flags&3);
|
||||
|
||||
u32 DataOffset=BE32(dir.Dir[index].ImageOffset);
|
||||
u32 DataBlock =BE16(dir.Dir[index].FirstBlock)-5;
|
||||
|
||||
if(DataOffset==0xFFFFFFFF)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8* animData=(u8*)(mc_data +(DataBlock*0x2000) + DataOffset);
|
||||
|
||||
switch(bnrFormat)
|
||||
{
|
||||
case 1:
|
||||
case 3:
|
||||
animData+=96*32 + 2*256; // image+palette
|
||||
break;
|
||||
case 2:
|
||||
animData+=96*32*2;
|
||||
break;
|
||||
}
|
||||
|
||||
int fmts[8];
|
||||
u8* data[8];
|
||||
int frames = 0;
|
||||
|
||||
|
||||
for(int i=0;i<8;i++)
|
||||
{
|
||||
fmts[i] = (formats>>(2*i))&3;
|
||||
delays[i] = ((fdelays>>(2*i))&3)<<2;
|
||||
data[i] = animData;
|
||||
|
||||
switch(fmts[i])
|
||||
{
|
||||
case 1: // CI8 with shared palette
|
||||
animData+=32*32;
|
||||
frames++;
|
||||
break;
|
||||
case 2: // RGB5A3
|
||||
animData+=32*32*2;
|
||||
frames++;
|
||||
break;
|
||||
case 3: // CI8 with own palette
|
||||
animData+=32*32 + 2*256;
|
||||
frames++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
u16* sharedPal = (u16*)(animData);
|
||||
|
||||
for(int i=0;i<8;i++)
|
||||
{
|
||||
switch(fmts[i])
|
||||
{
|
||||
case 1: // CI8 with shared palette
|
||||
decodeCI8image(buffer,data[i],sharedPal,32,32);
|
||||
buffer+=32*32;
|
||||
break;
|
||||
case 2: // RGB5A3
|
||||
decode5A3image(buffer,(u16*)(data[i]),32,32);
|
||||
break;
|
||||
case 3: // CI8 with own palette
|
||||
u16 *paldata = (u16*)(data[i]+32*32);
|
||||
decodeCI8image(buffer,data[i],paldata,32,32);
|
||||
buffer+=32*32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
u32 GCMemcard::TestChecksums()
|
||||
{
|
||||
if(!mcdFile) return 0xFFFFFFFF;
|
||||
|
||||
u16 csum1=0,csum2=0;
|
||||
|
||||
u32 results = 0;
|
||||
|
||||
calc_checksumsBE((u16*)&hdr, 0xFE ,&csum1,&csum2);
|
||||
if(BE16(hdr.CheckSum1)!=csum1) results |= 1;
|
||||
if(BE16(hdr.CheckSum2)!=csum2) results |= 1;
|
||||
|
||||
calc_checksumsBE((u16*)&dir,0xFFE,&csum1,&csum2);
|
||||
if(BE16(dir.CheckSum1)!=csum1) results |= 2;
|
||||
if(BE16(dir.CheckSum2)!=csum2) results |= 2;
|
||||
|
||||
calc_checksumsBE((u16*)&dir_backup,0xFFE,&csum1,&csum2);
|
||||
if(BE16(dir_backup.CheckSum1)!=csum1) results |= 4;
|
||||
if(BE16(dir_backup.CheckSum2)!=csum2) results |= 4;
|
||||
|
||||
calc_checksumsBE((u16*)(((u8*)&bat)+4),0xFFE,&csum1,&csum2);
|
||||
if(BE16(bat.CheckSum1)!=csum1) results |= 8;
|
||||
if(BE16(bat.CheckSum2)!=csum2) results |= 8;
|
||||
|
||||
calc_checksumsBE((u16*)(((u8*)&bat_backup)+4),0xFFE,&csum1,&csum2);
|
||||
if(BE16(bat_backup.CheckSum1)!=csum1) results |= 16;
|
||||
if(BE16(bat_backup.CheckSum2)!=csum2) results |= 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 GCMemcard::CopyFrom(GCMemcard& source, u32 index)
|
||||
{
|
||||
if(!mcdFile) return 0;
|
||||
|
||||
DEntry d;
|
||||
if(!source.GetFileInfo(index,d)) return 0;
|
||||
|
||||
u8 *t = new u8[source.GetFileSize(index)*0x2000];
|
||||
|
||||
if(!source.GetFileData(index,t)) return 0;
|
||||
u32 ret = ImportFile(d,t);
|
||||
|
||||
delete[] t;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool GCMemcard::Save()
|
||||
{
|
||||
if(!mcdFile) return false;
|
||||
|
||||
FILE *mcd=(FILE*)mcdFile;
|
||||
fseek(mcd,0,SEEK_SET);
|
||||
fwrite(&hdr,1,0x2000,mcd);
|
||||
fwrite(&dir,1,0x2000,mcd);
|
||||
fwrite(&dir_backup,1,0x2000,mcd);
|
||||
fwrite(&bat,1,0x2000,mcd);
|
||||
fwrite(&bat_backup,1,0x2000,mcd);
|
||||
fwrite(mc_data,1,mc_data_size,mcd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GCMemcard::IsOpen()
|
||||
{
|
||||
return (mcdFile!=NULL);
|
||||
}
|
||||
|
||||
GCMemcard::GCMemcard(const char *filename)
|
||||
{
|
||||
FILE *mcd=fopen(filename,"r+b");
|
||||
mcdFile=mcd;
|
||||
if(!mcd) return;
|
||||
|
||||
fseek(mcd,0x0000,SEEK_SET);
|
||||
assert(fread(&hdr, 1,0x2000,mcd)==0x2000);
|
||||
assert(fread(&dir, 1,0x2000,mcd)==0x2000);
|
||||
assert(fread(&dir_backup,1,0x2000,mcd)==0x2000);
|
||||
assert(fread(&bat, 1,0x2000,mcd)==0x2000);
|
||||
assert(fread(&bat_backup,1,0x2000,mcd)==0x2000);
|
||||
|
||||
u32 csums = TestChecksums();
|
||||
|
||||
if(csums&1)
|
||||
{
|
||||
// header checksum error!
|
||||
// TODO: fail to load
|
||||
}
|
||||
|
||||
if(csums&2) // directory checksum error!
|
||||
{
|
||||
if(csums&4)
|
||||
{
|
||||
// backup is also wrong!
|
||||
// TODO: fail to load
|
||||
}
|
||||
else
|
||||
{
|
||||
// backup is correct, restore
|
||||
dir = dir_backup;
|
||||
bat = bat_backup;
|
||||
|
||||
// update checksums
|
||||
csums = TestChecksums();
|
||||
}
|
||||
}
|
||||
|
||||
if(csums&8) // BAT checksum error!
|
||||
{
|
||||
if(csums&16)
|
||||
{
|
||||
// backup is also wrong!
|
||||
// TODO: fail to load
|
||||
}
|
||||
else
|
||||
{
|
||||
// backup is correct, restore
|
||||
dir = dir_backup;
|
||||
bat = bat_backup;
|
||||
|
||||
// update checksums
|
||||
csums = TestChecksums();
|
||||
}
|
||||
}
|
||||
|
||||
if(BE16(dir_backup.UpdateCounter) > BE16(dir.UpdateCounter)) //check if the backup is newer
|
||||
{
|
||||
dir = dir_backup;
|
||||
bat = bat_backup; // needed?
|
||||
}
|
||||
|
||||
fseek(mcd,0xa000,SEEK_SET);
|
||||
|
||||
mc_data_size=(((u32)BE16(hdr.Size)*16)-5)*0x2000;
|
||||
mc_data = new u8[mc_data_size];
|
||||
|
||||
u32 read = fread(mc_data,1,mc_data_size,mcd);
|
||||
assert(mc_data_size==read);
|
||||
}
|
||||
|
||||
GCMemcard::~GCMemcard()
|
||||
{
|
||||
fclose((FILE*)mcdFile);
|
||||
}
|
170
Source/Core/DolphinWX/Src/MemoryCards/GCMemcard.h
Normal file
170
Source/Core/DolphinWX/Src/MemoryCards/GCMemcard.h
Normal file
@ -0,0 +1,170 @@
|
||||
#pragma once
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
|
||||
u16 __inline bswap16(u16 s)
|
||||
{
|
||||
return (s>>8) | (s<<8);
|
||||
}
|
||||
|
||||
u32 __inline bswap32(u32 s)
|
||||
{
|
||||
return (u32)bswap16((u16)(s>>16)) | ((u32)bswap16((u16)s)<<16);
|
||||
}
|
||||
|
||||
#ifndef max
|
||||
template<class T>
|
||||
T __inline max(T a, T b)
|
||||
{
|
||||
return (b>a)?b:a;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define BE16(x) ((u16((x)[0])<<8) | u16((x)[1]))
|
||||
#define BE32(x) ((u32((x)[0])<<24) | (u32((x)[1])<<16) | (u32((x)[2])<<8) | u32((x)[3]))
|
||||
|
||||
class GCMemcard
|
||||
{
|
||||
void* mcdFile;
|
||||
|
||||
u32 mc_data_size;
|
||||
u8* mc_data;
|
||||
|
||||
void calc_checksumsBE(u16 *buf, u32 num, u16 *c1, u16 *c2);
|
||||
|
||||
public:
|
||||
|
||||
#pragma pack(push,1)
|
||||
struct OSTime {
|
||||
u32 low;
|
||||
u32 high;
|
||||
};
|
||||
|
||||
struct Header { //Offset Size Description
|
||||
u8 Unk[12]; //0x0000 12 ?
|
||||
OSTime fmtTime; //0x000c 8 time of format (OSTime value)
|
||||
u8 UID[12]; //0x0014 12 unique card id (?)
|
||||
u8 Pad1[2]; //0x0020 2 padding zeroes
|
||||
u8 Size[2]; //0x0022 2 size of memcard in Mbits
|
||||
u8 Encoding[2]; //0x0024 2 encoding (ASCII or japanese)
|
||||
u8 Unused1[468]; //0x0026 468 unused (0xff)
|
||||
u8 UpdateCounter[2];//0x01fa 2 update Counter (?, probably unused)
|
||||
u8 CheckSum1[2]; //0x01fc 2 Checksum 1 (?)
|
||||
u8 CheckSum2[2]; //0x01fe 2 Checksum 2 (?)
|
||||
u8 Unused2[7680]; //0x0200 0x1e00 unused (0xff)
|
||||
} hdr;
|
||||
|
||||
struct DEntry {
|
||||
u8 Gamecode[4]; //0x00 0x04 Gamecode
|
||||
u8 Markercode[2]; //0x04 0x02 Makercode
|
||||
u8 Unused1; //0x06 0x01 reserved/unused (always 0xff, has no effect)
|
||||
u8 BIFlags; //0x07 0x01 banner gfx format and icon animation (Image Key)
|
||||
// bit(s) description
|
||||
// 2 Icon Animation 0: forward 1: ping-pong
|
||||
// 1 [--0: No Banner 1: Banner present--] WRONG! YAGCD LIES!
|
||||
// 0 [--Banner Color 0: RGB5A3 1: CI8--] WRONG! YAGCD LIES!
|
||||
// bits 0 and 1: image format
|
||||
// 00 no banner
|
||||
// 01 CI8 banner
|
||||
// 01 RGB5A3 banner
|
||||
// 11 ? maybe ==01? haven't seen it
|
||||
//
|
||||
u8 Filename[32]; //0x08 0x20 filename
|
||||
u8 ModTime[4]; //0x28 0x04 Time of file's last modification in seconds since 12am, January 1st, 2000
|
||||
u8 ImageOffset[4]; //0x2c 0x04 image data offset
|
||||
u8 IconFmt[2]; //0x30 0x02 icon gfx format (2bits per icon)
|
||||
// bits Description
|
||||
// 00 no icon
|
||||
// 01 CI8 with a shared color palette after the last frame
|
||||
// 10 RGB5A3
|
||||
// 11 CI8 with a unique color palette after itself
|
||||
//
|
||||
u8 AnimSpeed[2]; //0x32 0x02 animation speed (2bits per icon) (*1)
|
||||
// bits Description
|
||||
// 00 no icon
|
||||
// 01 Icon lasts for 4 frames
|
||||
// 10 Icon lasts for 8 frames
|
||||
// 11 Icon lasts for 12 frames
|
||||
//
|
||||
u8 Permissions; //0x34 0x01 file-permissions
|
||||
// bit permission Description
|
||||
// 4 no move File cannot be moved by the IPL
|
||||
// 3 no copy File cannot be copied by the IPL
|
||||
// 2 public Can be read by any game
|
||||
//
|
||||
u8 CopyCounter; //0x35 0x01 copy counter (*2)
|
||||
u8 FirstBlock[2]; //0x36 0x02 block no of first block of file (0 == offset 0)
|
||||
u8 BlockCount[2]; //0x38 0x02 file-length (number of blocks in file)
|
||||
u8 Unused2[2]; //0x3a 0x02 reserved/unused (always 0xffff, has no effect)
|
||||
u8 CommentsAddr[4]; //0x3c 0x04 Address of the two comments within the file data (*3)
|
||||
};
|
||||
|
||||
struct Directory {
|
||||
DEntry Dir[127]; //0x0000 Directory Entries (max 127)
|
||||
u8 Padding[0x3a];
|
||||
u8 UpdateCounter[2];//0x1ffa 2 update Counter
|
||||
u8 CheckSum1[2]; //0x1ffc 2 Checksum 1
|
||||
u8 CheckSum2[2]; //0x1ffe 2 Checksum 2
|
||||
} dir, dir_backup;
|
||||
|
||||
struct BlockAlloc {
|
||||
u8 CheckSum1[2]; //0x0000 2 Checksum 1
|
||||
u8 CheckSum2[2]; //0x0002 2 Checksum 2
|
||||
u8 UpdateCounter[2];//0x0004 2 update Counter
|
||||
u8 FreeBlocks[2]; //0x0006 2 free Blocks
|
||||
u8 LastAllocated[2];//0x0008 2 last allocated Block
|
||||
u16 Map[0xFFB]; //0x000a 0x1ff8 Map of allocated Blocks
|
||||
} bat,bat_backup;
|
||||
#pragma pack(pop)
|
||||
|
||||
// constructor
|
||||
GCMemcard(const char* fileName);
|
||||
|
||||
// destructor
|
||||
~GCMemcard();
|
||||
|
||||
bool IsOpen();
|
||||
|
||||
u32 TestChecksums();
|
||||
|
||||
// get number of file entries in the directory
|
||||
u32 GetNumFiles();
|
||||
|
||||
// read directory entry
|
||||
bool GetFileInfo(u32 index, DEntry& data);
|
||||
|
||||
// buffer needs to be a char[32] or bigger
|
||||
bool GetFileName(u32 index, char* buffer);
|
||||
|
||||
// buffer needs to be a char[32] or bigger
|
||||
bool GetComment1(u32 index, char* buffer);
|
||||
|
||||
// buffer needs to be a char[32] or bigger
|
||||
bool GetComment2(u32 index, char* buffer);
|
||||
|
||||
// get file length un bytes
|
||||
u32 GetFileSize(u32 index);
|
||||
|
||||
// assumes there's enough space in buffer
|
||||
bool GetFileData(u32 index, u8* buffer);
|
||||
|
||||
// delete a file from the directory
|
||||
bool RemoveFile(u32 index);
|
||||
|
||||
// adds the file to the directory and copies its contents
|
||||
u32 ImportFile(DEntry& direntry, u8* contents);
|
||||
|
||||
// reads a save from another memcard, and imports the data into this memcard
|
||||
u32 CopyFrom(GCMemcard& source, u32 index);
|
||||
|
||||
// reads the banner image
|
||||
bool ReadBannerRGBA8(u32 index, u32* buffer);
|
||||
|
||||
// reads the animation frames
|
||||
u32 ReadAnimRGBA8(u32 index, u32* buffer, u8 *delays);
|
||||
|
||||
bool Save();
|
||||
|
||||
};
|
Reference in New Issue
Block a user