Merge pull request #7616 from AdmiralCurtiss/memcard-cleanup
Clean up GCMemcard a bit.
This commit is contained in:
commit
fd3ef7ebc5
File diff suppressed because it is too large
Load Diff
|
@ -5,6 +5,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -92,42 +93,68 @@ protected:
|
|||
struct GCMBlock
|
||||
{
|
||||
GCMBlock() { Erase(); }
|
||||
void Erase() { memset(block, 0xFF, BLOCK_SIZE); }
|
||||
u8 block[BLOCK_SIZE];
|
||||
void Erase() { memset(m_block.data(), 0xFF, m_block.size()); }
|
||||
std::array<u8, BLOCK_SIZE> m_block;
|
||||
};
|
||||
|
||||
void calc_checksumsBE(const u16* buf, u32 length, u16* csum, u16* inv_csum);
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Header // Offset Size Description
|
||||
struct Header
|
||||
{
|
||||
// Serial in libogc
|
||||
u8 serial[12]; // 0x0000 12 ?
|
||||
u64 formatTime; // 0x000c 8 Time of format (OSTime value)
|
||||
u32 SramBias; // 0x0014 4 SRAM bias at time of format
|
||||
u32 SramLang; // 0x0018 4 SRAM language
|
||||
u8 Unk2[4]; // 0x001c 4 ? almost always 0
|
||||
// end Serial in libogc
|
||||
u8 deviceID[2]; // 0x0020 2 0 if formated in slot A 1 if formated in slot B
|
||||
u8 SizeMb[2]; // 0x0022 2 Size of memcard in Mbits
|
||||
u16 Encoding; // 0x0024 2 Encoding (Windows-1252 or Shift JIS)
|
||||
u8 Unused1[468]; // 0x0026 468 Unused (0xff)
|
||||
u16 UpdateCounter; // 0x01fa 2 Update Counter (?, probably unused)
|
||||
u16 Checksum; // 0x01fc 2 Additive Checksum
|
||||
u16 Checksum_Inv; // 0x01fe 2 Inverse Checksum
|
||||
u8 Unused2[7680]; // 0x0200 0x1e00 Unused (0xff)
|
||||
// NOTE: libogc refers to 'Serial' as the first 0x20 bytes of the header,
|
||||
// so the data from m_serial until m_unknown_2 (inclusive)
|
||||
|
||||
// 12 bytes at 0x0000
|
||||
std::array<u8, 12> m_serial;
|
||||
|
||||
// 8 bytes at 0x000c: Time of format (OSTime value)
|
||||
Common::BigEndianValue<u64> m_format_time;
|
||||
|
||||
// 4 bytes at 0x0014; SRAM bias at time of format
|
||||
u32 m_sram_bias;
|
||||
|
||||
// 4 bytes at 0x0018: SRAM language
|
||||
Common::BigEndianValue<u32> m_sram_language;
|
||||
|
||||
// 4 bytes at 0x001c: ? almost always 0
|
||||
std::array<u8, 4> m_unknown_2;
|
||||
|
||||
// 2 bytes at 0x0020: 0 if formated in slot A, 1 if formated in slot B
|
||||
Common::BigEndianValue<u16> m_device_id;
|
||||
|
||||
// 2 bytes at 0x0022: Size of memcard in Mbits
|
||||
Common::BigEndianValue<u16> m_size_mb;
|
||||
|
||||
// 2 bytes at 0x0024: Encoding (Windows-1252 or Shift JIS)
|
||||
Common::BigEndianValue<u16> m_encoding;
|
||||
|
||||
// 468 bytes at 0x0026: Unused (0xff)
|
||||
std::array<u8, 468> m_unused_1;
|
||||
|
||||
// 2 bytes at 0x01fa: Update Counter (?, probably unused)
|
||||
u16 m_update_counter;
|
||||
|
||||
// 2 bytes at 0x01fc: Additive Checksum
|
||||
u16 m_checksum;
|
||||
|
||||
// 2 bytes at 0x01fe: Inverse Checksum
|
||||
u16 m_checksum_inv;
|
||||
|
||||
// 0x1e00 bytes at 0x0200: Unused (0xff)
|
||||
std::array<u8, 7680> m_unused_2;
|
||||
|
||||
void CARD_GetSerialNo(u32* serial1, u32* serial2) const
|
||||
{
|
||||
u32 _serial[8];
|
||||
u32 serial[8];
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
memcpy(&_serial[i], (u8*)this + (i * 4), 4);
|
||||
memcpy(&serial[i], (u8*)this + (i * 4), 4);
|
||||
}
|
||||
|
||||
*serial1 = _serial[0] ^ _serial[2] ^ _serial[4] ^ _serial[6];
|
||||
*serial2 = _serial[1] ^ _serial[3] ^ _serial[5] ^ _serial[7];
|
||||
*serial1 = serial[0] ^ serial[2] ^ serial[4] ^ serial[6];
|
||||
*serial2 = serial[1] ^ serial[3] ^ serial[5] ^ serial[7];
|
||||
}
|
||||
|
||||
// Nintendo format algorithm.
|
||||
|
@ -136,41 +163,53 @@ struct Header // Offset Size Description
|
|||
explicit Header(int slot = 0, u16 sizeMb = MemCard2043Mb, bool shift_jis = false)
|
||||
{
|
||||
memset(this, 0xFF, BLOCK_SIZE);
|
||||
*(u16*)SizeMb = BE16(sizeMb);
|
||||
Encoding = BE16(shift_jis ? 1 : 0);
|
||||
m_size_mb = sizeMb;
|
||||
m_encoding = shift_jis ? 1 : 0;
|
||||
u64 rand = Common::Timer::GetLocalTimeSinceJan1970() - ExpansionInterface::CEXIIPL::GC_EPOCH;
|
||||
formatTime = Common::swap64(rand);
|
||||
m_format_time = rand;
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
rand = (((rand * (u64)0x0000000041c64e6dULL) + (u64)0x0000000000003039ULL) >> 16);
|
||||
serial[i] = (u8)(g_SRAM.settings_ex.flash_id[slot][i] + (u32)rand);
|
||||
m_serial[i] = (u8)(g_SRAM.settings_ex.flash_id[slot][i] + (u32)rand);
|
||||
rand = (((rand * (u64)0x0000000041c64e6dULL) + (u64)0x0000000000003039ULL) >> 16);
|
||||
rand &= (u64)0x0000000000007fffULL;
|
||||
}
|
||||
SramBias = g_SRAM.settings.rtc_bias;
|
||||
SramLang = BE32(g_SRAM.settings.language);
|
||||
// TODO: determine the purpose of Unk2 1 works for slot A, 0 works for both slot A and slot B
|
||||
*(u32*)&Unk2 = 0; // = _viReg[55]; static vu16* const _viReg = (u16*)0xCC002000;
|
||||
*(u16*)&deviceID = 0;
|
||||
calc_checksumsBE((u16*)this, 0xFE, &Checksum, &Checksum_Inv);
|
||||
m_sram_bias = g_SRAM.settings.rtc_bias;
|
||||
m_sram_language = static_cast<u32>(g_SRAM.settings.language);
|
||||
// TODO: determine the purpose of m_unknown_2
|
||||
// 1 works for slot A, 0 works for both slot A and slot B
|
||||
memset(m_unknown_2.data(), 0,
|
||||
m_unknown_2.size()); // = _viReg[55]; static vu16* const _viReg = (u16*)0xCC002000;
|
||||
m_device_id = 0;
|
||||
calc_checksumsBE((u16*)this, 0xFE, &m_checksum, &m_checksum_inv);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Header) == BLOCK_SIZE);
|
||||
|
||||
struct DEntry
|
||||
{
|
||||
static const u8 DENTRY_SIZE = 0x40;
|
||||
DEntry() { memset(this, 0xFF, DENTRY_SIZE); }
|
||||
std::string GCI_FileName() const
|
||||
{
|
||||
std::string filename = std::string((char*)Makercode, 2) + '-' +
|
||||
std::string((char*)Gamecode, 4) + '-' + (char*)Filename + ".gci";
|
||||
std::string filename =
|
||||
std::string(reinterpret_cast<const char*>(m_makercode.data()), m_makercode.size()) + '-' +
|
||||
std::string(reinterpret_cast<const char*>(m_gamecode.data()), m_gamecode.size()) + '-' +
|
||||
reinterpret_cast<const char*>(m_filename.data()) + ".gci";
|
||||
return Common::EscapeFileName(filename);
|
||||
}
|
||||
|
||||
u8 Gamecode[4]; // 0x00 0x04 Gamecode
|
||||
u8 Makercode[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)
|
||||
static constexpr std::array<u8, 4> UNINITIALIZED_GAMECODE{{0xFF, 0xFF, 0xFF, 0xFF}};
|
||||
|
||||
// 4 bytes at 0x00: Gamecode
|
||||
std::array<u8, 4> m_gamecode;
|
||||
|
||||
// 2 bytes at 0x04: Makercode
|
||||
std::array<u8, 2> m_makercode;
|
||||
|
||||
// 1 byte at 0x06: reserved/unused (always 0xff, has no effect)
|
||||
u8 m_unused_1;
|
||||
|
||||
// 1 byte at 0x07: 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!
|
||||
|
@ -180,98 +219,133 @@ struct DEntry
|
|||
// 01 CI8 banner
|
||||
// 10 RGB5A3 banner
|
||||
// 11 ? maybe ==00? Time Splitters 2 and 3 have it and don't have banner
|
||||
//
|
||||
u8 Filename[DENTRY_STRLEN]; // 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)
|
||||
u8 m_banner_and_icon_flags;
|
||||
|
||||
// 0x20 bytes at 0x08: Filename
|
||||
std::array<u8, DENTRY_STRLEN> m_filename;
|
||||
|
||||
// 4 bytes at 0x28: Time of file's last modification in seconds since 12am, January 1st, 2000
|
||||
Common::BigEndianValue<u32> m_modification_time;
|
||||
|
||||
// 4 bytes at 0x2c: image data offset
|
||||
Common::BigEndianValue<u32> m_image_offset;
|
||||
|
||||
// 2 bytes at 0x30: 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)
|
||||
Common::BigEndianValue<u16> m_icon_format;
|
||||
|
||||
// 2 bytes at 0x32: Animation speed (2bits per icon)
|
||||
// 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
|
||||
Common::BigEndianValue<u16> m_animation_speed;
|
||||
|
||||
// 1 byte at 0x34: 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)
|
||||
u8 m_file_permissions;
|
||||
|
||||
// 1 byte at 0x35: Copy counter
|
||||
u8 m_copy_counter;
|
||||
|
||||
// 2 bytes at 0x36: Block number of first block of file (0 == offset 0)
|
||||
Common::BigEndianValue<u16> m_first_block;
|
||||
|
||||
// 2 bytes at 0x38: File-length (number of blocks in file)
|
||||
Common::BigEndianValue<u16> m_block_count;
|
||||
|
||||
// 2 bytes at 0x3a: Reserved/unused (always 0xffff, has no effect)
|
||||
std::array<u8, 2> m_unused_2;
|
||||
|
||||
// 4 bytes at 0x3c: Address of the two comments within the file data
|
||||
Common::BigEndianValue<u32> m_comments_address;
|
||||
};
|
||||
static_assert(sizeof(DEntry) == DENTRY_SIZE);
|
||||
|
||||
struct Directory
|
||||
{
|
||||
DEntry Dir[DIRLEN]; // 0x0000 Directory Entries (max 127)
|
||||
u8 Padding[0x3a];
|
||||
u16 UpdateCounter; // 0x1ffa 2 Update Counter
|
||||
u16 Checksum; // 0x1ffc 2 Additive Checksum
|
||||
u16 Checksum_Inv; // 0x1ffe 2 Inverse Checksum
|
||||
std::array<DEntry, DIRLEN> m_dir_entries; // 0x0000 Directory Entries (max 127)
|
||||
std::array<u8, 0x3a> m_padding;
|
||||
Common::BigEndianValue<u16> m_update_counter; // 0x1ffa 2 Update Counter
|
||||
u16 m_checksum; // 0x1ffc 2 Additive Checksum
|
||||
u16 m_checksum_inv; // 0x1ffe 2 Inverse Checksum
|
||||
Directory()
|
||||
{
|
||||
memset(this, 0xFF, BLOCK_SIZE);
|
||||
UpdateCounter = 0;
|
||||
Checksum = BE16(0xF003);
|
||||
Checksum_Inv = 0;
|
||||
m_update_counter = 0;
|
||||
m_checksum = BE16(0xF003);
|
||||
m_checksum_inv = 0;
|
||||
}
|
||||
void Replace(DEntry d, int idx)
|
||||
{
|
||||
Dir[idx] = d;
|
||||
m_dir_entries[idx] = d;
|
||||
fixChecksums();
|
||||
}
|
||||
void fixChecksums() { calc_checksumsBE((u16*)this, 0xFFE, &Checksum, &Checksum_Inv); }
|
||||
void fixChecksums() { calc_checksumsBE((u16*)this, 0xFFE, &m_checksum, &m_checksum_inv); }
|
||||
};
|
||||
static_assert(sizeof(Directory) == BLOCK_SIZE);
|
||||
|
||||
struct BlockAlloc
|
||||
{
|
||||
u16 Checksum; // 0x0000 2 Additive Checksum
|
||||
u16 Checksum_Inv; // 0x0002 2 Inverse Checksum
|
||||
u16 UpdateCounter; // 0x0004 2 Update Counter
|
||||
u16 FreeBlocks; // 0x0006 2 Free Blocks
|
||||
u16 LastAllocated; // 0x0008 2 Last allocated Block
|
||||
u16 Map[BAT_SIZE]; // 0x000a 0x1ff8 Map of allocated Blocks
|
||||
// 2 bytes at 0x0000: Additive Checksum
|
||||
u16 m_checksum;
|
||||
|
||||
// 2 bytes at 0x0002: Inverse Checksum
|
||||
u16 m_checksum_inv;
|
||||
|
||||
// 2 bytes at 0x0004: Update Counter
|
||||
Common::BigEndianValue<u16> m_update_counter;
|
||||
|
||||
// 2 bytes at 0x0006: Free Blocks
|
||||
Common::BigEndianValue<u16> m_free_blocks;
|
||||
|
||||
// 2 bytes at 0x0008: Last allocated Block
|
||||
Common::BigEndianValue<u16> m_last_allocated_block;
|
||||
|
||||
// 0x1ff8 bytes at 0x000a: Map of allocated Blocks
|
||||
std::array<Common::BigEndianValue<u16>, BAT_SIZE> m_map;
|
||||
|
||||
u16 GetNextBlock(u16 Block) const;
|
||||
u16 NextFreeBlock(u16 MaxBlock, u16 StartingBlock = MC_FST_BLOCKS) const;
|
||||
bool ClearBlocks(u16 StartingBlock, u16 Length);
|
||||
void fixChecksums() { calc_checksumsBE((u16*)&UpdateCounter, 0xFFE, &Checksum, &Checksum_Inv); }
|
||||
void fixChecksums()
|
||||
{
|
||||
calc_checksumsBE((u16*)&m_update_counter, 0xFFE, &m_checksum, &m_checksum_inv);
|
||||
}
|
||||
explicit BlockAlloc(u16 sizeMb = MemCard2043Mb)
|
||||
{
|
||||
memset(this, 0, BLOCK_SIZE);
|
||||
// UpdateCounter = 0;
|
||||
FreeBlocks = BE16((sizeMb * MBIT_TO_BLOCKS) - MC_FST_BLOCKS);
|
||||
LastAllocated = BE16(4);
|
||||
m_free_blocks = (sizeMb * MBIT_TO_BLOCKS) - MC_FST_BLOCKS;
|
||||
m_last_allocated_block = 4;
|
||||
fixChecksums();
|
||||
}
|
||||
u16 AssignBlocksContiguous(u16 length)
|
||||
{
|
||||
u16 starting = BE16(LastAllocated) + 1;
|
||||
if (length > BE16(FreeBlocks))
|
||||
u16 starting = m_last_allocated_block + 1;
|
||||
if (length > m_free_blocks)
|
||||
return 0xFFFF;
|
||||
u16 current = starting;
|
||||
while ((current - starting + 1) < length)
|
||||
{
|
||||
Map[current - 5] = BE16(current + 1);
|
||||
m_map[current - 5] = current + 1;
|
||||
current++;
|
||||
}
|
||||
Map[current - 5] = 0xFFFF;
|
||||
LastAllocated = BE16(current);
|
||||
FreeBlocks = BE16(BE16(FreeBlocks) - length);
|
||||
m_map[current - 5] = 0xFFFF;
|
||||
m_last_allocated_block = current;
|
||||
m_free_blocks = m_free_blocks - length;
|
||||
fixChecksums();
|
||||
return BE16(starting);
|
||||
return starting;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(BlockAlloc) == BLOCK_SIZE);
|
||||
#pragma pack(pop)
|
||||
|
||||
class GCIFile
|
||||
|
@ -280,9 +354,11 @@ public:
|
|||
bool LoadSaveBlocks();
|
||||
bool HasCopyProtection() const
|
||||
{
|
||||
if ((strcmp((char*)m_gci_header.Filename, "PSO_SYSTEM") == 0) ||
|
||||
(strcmp((char*)m_gci_header.Filename, "PSO3_SYSTEM") == 0) ||
|
||||
(strcmp((char*)m_gci_header.Filename, "f_zero.dat") == 0))
|
||||
if ((strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "PSO_SYSTEM") ==
|
||||
0) ||
|
||||
(strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "PSO3_SYSTEM") ==
|
||||
0) ||
|
||||
(strcmp(reinterpret_cast<const char*>(m_gci_header.m_filename.data()), "f_zero.dat") == 0))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -300,20 +376,28 @@ class GCMemcard
|
|||
{
|
||||
private:
|
||||
bool m_valid;
|
||||
std::string m_fileName;
|
||||
std::string m_filename;
|
||||
|
||||
u32 maxBlock;
|
||||
u16 m_sizeMb;
|
||||
u32 m_size_blocks;
|
||||
u16 m_size_mb;
|
||||
|
||||
Header hdr;
|
||||
Directory dir, dir_backup, *CurrentDir, *PreviousDir;
|
||||
BlockAlloc bat, bat_backup, *CurrentBat, *PreviousBat;
|
||||
Header m_header_block;
|
||||
std::array<Directory, 2> m_directory_blocks;
|
||||
std::array<BlockAlloc, 2> m_bat_blocks;
|
||||
std::vector<GCMBlock> m_data_blocks;
|
||||
|
||||
std::vector<GCMBlock> mc_data_blocks;
|
||||
int m_active_directory;
|
||||
int m_active_bat;
|
||||
|
||||
u32 ImportGciInternal(File::IOFile&& gci, const std::string& inputFile,
|
||||
const std::string& outputFile);
|
||||
void InitDirBatPointers();
|
||||
void InitActiveDirBat();
|
||||
|
||||
const Directory& GetActiveDirectory() const;
|
||||
const BlockAlloc& GetActiveBat() const;
|
||||
|
||||
void UpdateDirectory(const Directory& directory);
|
||||
void UpdateBat(const BlockAlloc& bat);
|
||||
|
||||
public:
|
||||
explicit GCMemcard(const std::string& fileName, bool forceCreation = false,
|
||||
|
@ -366,8 +450,9 @@ public:
|
|||
u32 DEntry_CommentsAddress(u8 index) const;
|
||||
std::string GetSaveComment1(u8 index) const;
|
||||
std::string GetSaveComment2(u8 index) const;
|
||||
// Copies a DEntry from u8 index to DEntry& data
|
||||
bool GetDEntry(u8 index, DEntry& dest) const;
|
||||
|
||||
// Fetches a DEntry from the given file index.
|
||||
std::optional<DEntry> GetDEntry(u8 index) const;
|
||||
|
||||
u32 GetSaveData(u8 index, std::vector<GCMBlock>& saveBlocks) const;
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_
|
|||
}
|
||||
}
|
||||
|
||||
u16 num_blocks = BE16(gci.m_gci_header.BlockCount);
|
||||
u16 num_blocks = gci.m_gci_header.m_block_count;
|
||||
// largest number of free blocks on a memory card
|
||||
// in reality, there are not likely any valid gci files > 251 blocks
|
||||
if (num_blocks > 2043)
|
||||
|
@ -79,7 +79,7 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_
|
|||
return NO_INDEX;
|
||||
}
|
||||
|
||||
if (m_game_id == BE32(gci.m_gci_header.Gamecode))
|
||||
if (m_game_id == BE32(gci.m_gci_header.m_gamecode.data()))
|
||||
{
|
||||
gci.LoadSaveBlocks();
|
||||
}
|
||||
|
@ -89,8 +89,8 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_
|
|||
{
|
||||
return NO_INDEX;
|
||||
}
|
||||
int total_blocks = BE16(m_hdr.SizeMb) * MBIT_TO_BLOCKS - MC_FST_BLOCKS;
|
||||
int free_blocks = BE16(m_bat1.FreeBlocks);
|
||||
int total_blocks = m_hdr.m_size_mb * MBIT_TO_BLOCKS - MC_FST_BLOCKS;
|
||||
int free_blocks = m_bat1.m_free_blocks;
|
||||
if (total_blocks > free_blocks * 10)
|
||||
{
|
||||
PanicAlertT("%s\nwas not loaded because there is less than 10%% free blocks available on "
|
||||
|
@ -108,7 +108,7 @@ int GCMemcardDirectory::LoadGCI(const std::string& file_name, bool current_game_
|
|||
file_name.c_str());
|
||||
return NO_INDEX;
|
||||
}
|
||||
*(u16*)&gci.m_gci_header.FirstBlock = first_block;
|
||||
gci.m_gci_header.m_first_block = first_block;
|
||||
if (gci.HasCopyProtection() && gci.LoadSaveBlocks())
|
||||
{
|
||||
GCMemcard::PSO_MakeSaveGameValid(m_hdr, gci.m_gci_header, gci.m_save_data);
|
||||
|
@ -151,7 +151,7 @@ std::vector<std::string> GCMemcardDirectory::GetFileNamesForGameID(const std::st
|
|||
if (std::find(loaded_saves.begin(), loaded_saves.end(), gci_filename) != loaded_saves.end())
|
||||
continue;
|
||||
|
||||
const u16 num_blocks = BE16(gci.m_gci_header.BlockCount);
|
||||
const u16 num_blocks = gci.m_gci_header.m_block_count;
|
||||
// largest number of free blocks on a memory card
|
||||
// in reality, there are not likely any valid gci files > 251 blocks
|
||||
if (num_blocks > 2043)
|
||||
|
@ -166,7 +166,7 @@ std::vector<std::string> GCMemcardDirectory::GetFileNamesForGameID(const std::st
|
|||
// card (see above method), but since we're only loading the saves for one GameID here, we're
|
||||
// definitely not going to run out of space.
|
||||
|
||||
if (game_code == BE32(gci.m_gci_header.Gamecode))
|
||||
if (game_code == BE32(gci.m_gci_header.m_gamecode.data()))
|
||||
{
|
||||
loaded_saves.push_back(gci_filename);
|
||||
filenames.push_back(file_name);
|
||||
|
@ -429,16 +429,17 @@ inline void GCMemcardDirectory::SyncSaves()
|
|||
{
|
||||
Directory* current = &m_dir2;
|
||||
|
||||
if (BE16(m_dir1.UpdateCounter) > BE16(m_dir2.UpdateCounter))
|
||||
if (m_dir1.m_update_counter > m_dir2.m_update_counter)
|
||||
{
|
||||
current = &m_dir1;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < DIRLEN; ++i)
|
||||
{
|
||||
if (BE32(current->Dir[i].Gamecode) != 0xFFFFFFFF)
|
||||
if (current->m_dir_entries[i].m_gamecode != DEntry::UNINITIALIZED_GAMECODE)
|
||||
{
|
||||
INFO_LOG(EXPANSIONINTERFACE, "Syncing save 0x%x", *(u32*)&(current->Dir[i].Gamecode));
|
||||
INFO_LOG(EXPANSIONINTERFACE, "Syncing save 0x%x",
|
||||
BE32(current->m_dir_entries[i].m_gamecode.data()));
|
||||
bool added = false;
|
||||
while (i >= m_saves.size())
|
||||
{
|
||||
|
@ -447,20 +448,22 @@ inline void GCMemcardDirectory::SyncSaves()
|
|||
added = true;
|
||||
}
|
||||
|
||||
if (added || memcmp((u8*)&(m_saves[i].m_gci_header), (u8*)&(current->Dir[i]), DENTRY_SIZE))
|
||||
if (added ||
|
||||
memcmp((u8*)&(m_saves[i].m_gci_header), (u8*)&(current->m_dir_entries[i]), DENTRY_SIZE))
|
||||
{
|
||||
m_saves[i].m_dirty = true;
|
||||
u32 gamecode = BE32(m_saves[i].m_gci_header.Gamecode);
|
||||
u32 new_gamecode = BE32(current->Dir[i].Gamecode);
|
||||
u32 old_start = BE16(m_saves[i].m_gci_header.FirstBlock);
|
||||
u32 new_start = BE16(current->Dir[i].FirstBlock);
|
||||
u32 gamecode = BE32(m_saves[i].m_gci_header.m_gamecode.data());
|
||||
u32 new_gamecode = BE32(current->m_dir_entries[i].m_gamecode.data());
|
||||
u32 old_start = m_saves[i].m_gci_header.m_first_block;
|
||||
u32 new_start = current->m_dir_entries[i].m_first_block;
|
||||
|
||||
if ((gamecode != 0xFFFFFFFF) && (gamecode != new_gamecode))
|
||||
{
|
||||
PanicAlertT("Game overwrote with another games save. Data corruption ahead 0x%x, 0x%x",
|
||||
BE32(m_saves[i].m_gci_header.Gamecode), BE32(current->Dir[i].Gamecode));
|
||||
BE32(m_saves[i].m_gci_header.m_gamecode.data()),
|
||||
BE32(current->m_dir_entries[i].m_gamecode.data()));
|
||||
}
|
||||
memcpy((u8*)&(m_saves[i].m_gci_header), (u8*)&(current->Dir[i]), DENTRY_SIZE);
|
||||
memcpy((u8*)&(m_saves[i].m_gci_header), (u8*)&(current->m_dir_entries[i]), DENTRY_SIZE);
|
||||
if (old_start != new_start)
|
||||
{
|
||||
INFO_LOG(EXPANSIONINTERFACE, "Save moved from 0x%x to 0x%x", old_start, new_start);
|
||||
|
@ -476,8 +479,8 @@ inline void GCMemcardDirectory::SyncSaves()
|
|||
else if ((i < m_saves.size()) && (*(u32*)&(m_saves[i].m_gci_header) != 0xFFFFFFFF))
|
||||
{
|
||||
INFO_LOG(EXPANSIONINTERFACE, "Clearing and/or deleting save 0x%x",
|
||||
BE32(m_saves[i].m_gci_header.Gamecode));
|
||||
*(u32*)&(m_saves[i].m_gci_header.Gamecode) = 0xFFFFFFFF;
|
||||
BE32(m_saves[i].m_gci_header.m_gamecode.data()));
|
||||
m_saves[i].m_gci_header.m_gamecode = DEntry::UNINITIALIZED_GAMECODE;
|
||||
m_saves[i].m_save_data.clear();
|
||||
m_saves[i].m_used_blocks.clear();
|
||||
m_saves[i].m_dirty = true;
|
||||
|
@ -488,7 +491,7 @@ inline s32 GCMemcardDirectory::SaveAreaRW(u32 block, bool writing)
|
|||
{
|
||||
for (u16 i = 0; i < m_saves.size(); ++i)
|
||||
{
|
||||
if (BE32(m_saves[i].m_gci_header.Gamecode) != 0xFFFFFFFF)
|
||||
if (m_saves[i].m_gci_header.m_gamecode != DEntry::UNINITIALIZED_GAMECODE)
|
||||
{
|
||||
if (m_saves[i].m_used_blocks.size() == 0)
|
||||
{
|
||||
|
@ -500,7 +503,7 @@ inline s32 GCMemcardDirectory::SaveAreaRW(u32 block, bool writing)
|
|||
{
|
||||
if (!m_saves[i].LoadSaveBlocks())
|
||||
{
|
||||
int num_blocks = BE16(m_saves[i].m_gci_header.BlockCount);
|
||||
int num_blocks = m_saves[i].m_gci_header.m_block_count;
|
||||
while (num_blocks)
|
||||
{
|
||||
m_saves[i].m_save_data.emplace_back();
|
||||
|
@ -514,7 +517,7 @@ inline s32 GCMemcardDirectory::SaveAreaRW(u32 block, bool writing)
|
|||
}
|
||||
|
||||
m_last_block = block;
|
||||
m_last_block_address = m_saves[i].m_save_data[idx].block;
|
||||
m_last_block_address = m_saves[i].m_save_data[idx].m_block.data();
|
||||
return m_last_block;
|
||||
}
|
||||
}
|
||||
|
@ -546,12 +549,12 @@ s32 GCMemcardDirectory::DirectoryWrite(u32 dest_address, u32 length, const u8* s
|
|||
bool GCMemcardDirectory::SetUsedBlocks(int save_index)
|
||||
{
|
||||
BlockAlloc* current_bat;
|
||||
if (BE16(m_bat2.UpdateCounter) > BE16(m_bat1.UpdateCounter))
|
||||
if (m_bat2.m_update_counter > m_bat1.m_update_counter)
|
||||
current_bat = &m_bat2;
|
||||
else
|
||||
current_bat = &m_bat1;
|
||||
|
||||
u16 block = BE16(m_saves[save_index].m_gci_header.FirstBlock);
|
||||
u16 block = m_saves[save_index].m_gci_header.m_first_block;
|
||||
while (block != 0xFFFF)
|
||||
{
|
||||
m_saves[save_index].m_used_blocks.push_back(block);
|
||||
|
@ -563,7 +566,7 @@ bool GCMemcardDirectory::SetUsedBlocks(int save_index)
|
|||
}
|
||||
}
|
||||
|
||||
u16 num_blocks = BE16(m_saves[save_index].m_gci_header.BlockCount);
|
||||
u16 num_blocks = m_saves[save_index].m_gci_header.m_block_count;
|
||||
u16 blocks_from_bat = (u16)m_saves[save_index].m_used_blocks.size();
|
||||
if (blocks_from_bat != num_blocks)
|
||||
{
|
||||
|
@ -585,7 +588,7 @@ void GCMemcardDirectory::FlushToFile()
|
|||
{
|
||||
if (m_saves[i].m_dirty)
|
||||
{
|
||||
if (BE32(m_saves[i].m_gci_header.Gamecode) != 0xFFFFFFFF)
|
||||
if (m_saves[i].m_gci_header.m_gamecode != DEntry::UNINITIALIZED_GAMECODE)
|
||||
{
|
||||
m_saves[i].m_dirty = false;
|
||||
if (m_saves[i].m_save_data.size() == 0)
|
||||
|
@ -654,7 +657,7 @@ void GCMemcardDirectory::FlushToFile()
|
|||
// simultaneously
|
||||
// this ensures that the save data for all of the current games gci files are stored in the
|
||||
// savestate
|
||||
u32 gamecode = BE32(m_saves[i].m_gci_header.Gamecode);
|
||||
u32 gamecode = BE32(m_saves[i].m_gci_header.m_gamecode.data());
|
||||
if (gamecode != m_game_id && gamecode != 0xFFFFFFFF && m_saves[i].m_save_data.size())
|
||||
{
|
||||
INFO_LOG(EXPANSIONINTERFACE, "Flushing savedata to disk for %s",
|
||||
|
@ -703,7 +706,7 @@ bool GCIFile::LoadSaveBlocks()
|
|||
|
||||
INFO_LOG(EXPANSIONINTERFACE, "Reading savedata from disk for %s", m_filename.c_str());
|
||||
save_file.Seek(DENTRY_SIZE, SEEK_SET);
|
||||
u16 num_blocks = BE16(m_gci_header.BlockCount);
|
||||
u16 num_blocks = m_gci_header.m_block_count;
|
||||
m_save_data.resize(num_blocks);
|
||||
if (!save_file.ReadBytes(m_save_data.data(), num_blocks * BLOCK_SIZE))
|
||||
{
|
||||
|
|
|
@ -199,10 +199,12 @@ void GCMemcardManager::UpdateSlotTable(int slot)
|
|||
auto* icon = new QTableWidgetItem;
|
||||
icon->setData(Qt::DecorationRole, frames[0]);
|
||||
|
||||
DEntry d;
|
||||
memcard->GetDEntry(file_index, d);
|
||||
std::optional<DEntry> entry = memcard->GetDEntry(file_index);
|
||||
|
||||
const auto speed = ((d.AnimSpeed[0] & 1) << 2) + (d.AnimSpeed[1] & 1);
|
||||
// TODO: This is wrong, the animation speed is not static and is already correctly calculated in
|
||||
// GetIconFromSaveFile(), just not returned
|
||||
const u16 animation_speed = entry ? entry->m_animation_speed : 1;
|
||||
const auto speed = (((animation_speed >> 8) & 1) << 2) + (animation_speed & 1);
|
||||
|
||||
m_slot_active_icons[slot].push_back({speed, frames});
|
||||
|
||||
|
|
Loading…
Reference in New Issue