Skip to content

Commit ba185c5

Browse files
committed
Formats: Fix superblock, FAT/ECC, and PSU metadata for saves
1 parent e336e8f commit ba185c5

3 files changed

Lines changed: 192 additions & 60 deletions

File tree

src/core/formats/ps2mc.cpp

Lines changed: 159 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class PS2MemoryCard::Impl
9494
PS2McDirEntry find_entry(const std::string& path, uint32_t& parent_cluster);
9595
std::vector<PS2McDirEntry> read_dirents(uint32_t dir_cluster);
9696
void write_dirents(uint32_t dir_cluster, const std::vector<PS2McDirEntry>& entries);
97+
void syncParentDirectoryEntryLength(uint32_t child_dir_cluster);
9798
uint32_t allocate_cluster();
9899
std::vector<uint32_t> allocate_clusters(uint32_t count);
99100
void free_cluster_chain(uint32_t start_cluster);
@@ -274,7 +275,7 @@ std::vector<uint32_t> PS2MemoryCard::Impl::read_fat_cluster(uint32_t fat_cluster
274275
void PS2MemoryCard::Impl::read_fat_from_card()
275276
{
276277
fat.clear();
277-
fat.resize(allocatable_cluster_end, PS2MC_FAT_CHAIN_END);
278+
fat.resize(allocatable_cluster_end, PS2MC_FAT_CHAIN_END_UNALLOC);
278279

279280
uint32_t fat_entry = 0;
280281

@@ -349,7 +350,7 @@ void PS2MemoryCard::Impl::read_superblock()
349350

350351
for (int i = 0; i < 32; ++i)
351352
{
352-
indirect_fat_cluster_list[i] = readLE<uint32_t>(sb_page, 76 + i * 4);
353+
indirect_fat_cluster_list[i] = readLE<uint32_t>(sb_page, 80 + i * 4);
353354
}
354355

355356
if (sb_page.size() > 0x44 + 4)
@@ -591,7 +592,7 @@ void PS2MemoryCard::Impl::write_dirents(uint32_t dir_cluster, const std::vector<
591592
if (next >= fat.size() || next == PS2MC_FAT_CHAIN_END_UNALLOC || next == PS2MC_FAT_CHAIN_END)
592593
{
593594
uint32_t new_cluster = allocate_cluster();
594-
fat[current_cluster] = (fat[current_cluster] & ~PS2MC_FAT_CLUSTER_MASK) | new_cluster;
595+
fat[current_cluster] = (fat[current_cluster] & ~PS2MC_FAT_CLUSTER_MASK) | (new_cluster | PS2MC_FAT_ALLOCATED_BIT);
595596
fat[new_cluster] = PS2MC_FAT_CHAIN_END;
596597
current_cluster = new_cluster;
597598
}
@@ -607,6 +608,42 @@ void PS2MemoryCard::Impl::write_dirents(uint32_t dir_cluster, const std::vector<
607608
}
608609
}
609610

611+
void PS2MemoryCard::Impl::syncParentDirectoryEntryLength(uint32_t child_dir_cluster)
612+
{
613+
auto child = read_dirents(child_dir_cluster);
614+
if (child.empty() || child[0].name != ".")
615+
{
616+
return;
617+
}
618+
const uint32_t newLen = child[0].length;
619+
const uint32_t ancestor = child[0].cluster;
620+
const uint32_t slot = child[0].dirEntry;
621+
622+
auto ancestor_entries = read_dirents(ancestor);
623+
bool updated = false;
624+
if (slot < ancestor_entries.size() && (ancestor_entries[slot].mode & DF_DIR) && ancestor_entries[slot].cluster == child_dir_cluster)
625+
{
626+
ancestor_entries[slot].length = newLen;
627+
updated = true;
628+
}
629+
else
630+
{
631+
for (auto& e : ancestor_entries)
632+
{
633+
if ((e.mode & DF_DIR) && (e.mode & DF_EXISTS) && e.cluster == child_dir_cluster)
634+
{
635+
e.length = newLen;
636+
updated = true;
637+
break;
638+
}
639+
}
640+
}
641+
if (updated)
642+
{
643+
write_dirents(ancestor, ancestor_entries);
644+
}
645+
}
646+
610647
uint32_t PS2MemoryCard::Impl::allocate_cluster()
611648
{
612649
for (uint32_t i = 0; i < fat.size(); ++i)
@@ -634,7 +671,7 @@ std::vector<uint32_t> PS2MemoryCard::Impl::allocate_clusters(uint32_t count)
634671

635672
if (i > 0)
636673
{
637-
fat[clusters[i - 1]] = cluster;
674+
fat[clusters[i - 1]] = cluster | PS2MC_FAT_ALLOCATED_BIT;
638675
}
639676
}
640677

@@ -650,11 +687,19 @@ void PS2MemoryCard::Impl::free_cluster_chain(uint32_t start_cluster)
650687
{
651688
uint32_t current = start_cluster;
652689

653-
while (current != PS2MC_FAT_CHAIN_END && current < fat.size())
690+
while (current < fat.size())
654691
{
655-
uint32_t next = fat[current] & PS2MC_FAT_CLUSTER_MASK;
692+
const uint32_t raw = fat[current];
693+
const uint32_t masked = raw & PS2MC_FAT_CLUSTER_MASK;
694+
656695
fat[current] = PS2MC_FAT_CHAIN_END_UNALLOC;
657-
current = next;
696+
697+
if (raw == PS2MC_FAT_CHAIN_END || masked >= fat.size() || masked == PS2MC_FAT_CHAIN_END_UNALLOC)
698+
{
699+
break;
700+
}
701+
702+
current = masked;
658703
}
659704

660705
modified = true;
@@ -770,9 +815,10 @@ void PS2MemoryCard::Impl::calculate_fat_layout(uint32_t& first_ifc, uint32_t& in
770815
uint32_t fat_clusters = (allocatable_clusters_est + epc - 1) / epc;
771816
uint32_t indirect_fat_clusters = (fat_clusters + epc - 1) / epc;
772817
if (indirect_fat_clusters > 32)
818+
{
773819
indirect_fat_clusters = 32;
774-
775-
fat_clusters = indirect_fat_clusters * epc;
820+
fat_clusters = indirect_fat_clusters * epc;
821+
}
776822

777823
allocatable_cluster_offset = first_ifc + indirect_fat_clusters + fat_clusters;
778824

@@ -839,6 +885,19 @@ void PS2MemoryCard::Impl::write_superblock(uint32_t first_ifc, uint32_t indirect
839885
sb[337] = 0x2B;
840886

841887
write_page(0, sb);
888+
889+
const uint64_t backup2_offset = static_cast<uint64_t>(good_block2) * pages_per_erase_block * raw_page_size;
890+
std::vector<uint8_t> erased_page(raw_page_size, 0xFF);
891+
file.seekp(static_cast<std::streamoff>(backup2_offset));
892+
for (uint32_t i = 0; i < pages_per_erase_block; ++i)
893+
{
894+
file.write(reinterpret_cast<const char*>(erased_page.data()),
895+
static_cast<std::streamsize>(erased_page.size()));
896+
if (!file)
897+
{
898+
throw PS2McIOError("Failed to initialize backup erase block");
899+
}
900+
}
842901
}
843902

844903
void PS2MemoryCard::Impl::init_indirect_fat_clusters(uint32_t first_ifc, uint32_t indirect_fat_clusters, uint32_t epc)
@@ -875,7 +934,7 @@ void PS2MemoryCard::Impl::init_root_directory()
875934

876935
std::vector<PS2McDirEntry> rootEntries;
877936

878-
PS2McDirEntry dot;
937+
PS2McDirEntry dot{};
879938
dot.mode = DF_DIR | DF_EXISTS | DF_RWX | DF_0400;
880939
dot.name = ".";
881940
dot.cluster = 0;
@@ -884,7 +943,7 @@ void PS2MemoryCard::Impl::init_root_directory()
884943
dot.modified = dot.created;
885944
rootEntries.push_back(dot);
886945

887-
PS2McDirEntry dotdot;
946+
PS2McDirEntry dotdot{};
888947
dotdot.mode = DF_DIR | DF_EXISTS | DF_WRITE | DF_EXECUTE | DF_0400 | DF_HIDDEN;
889948
dotdot.name = "..";
890949
dotdot.cluster = 0;
@@ -1272,7 +1331,8 @@ void PS2MemoryCard::makeDir(const std::string& path)
12721331
{
12731332
try
12741333
{
1275-
auto entry = pImpl->find_entry(path, pImpl->rootdir_fat_cluster);
1334+
uint32_t unusedParent = 0;
1335+
auto entry = pImpl->find_entry(path, unusedParent);
12761336
if (entry.mode & DF_EXISTS)
12771337
{
12781338
return;
@@ -1299,39 +1359,48 @@ void PS2MemoryCard::makeDir(const std::string& path)
12991359

13001360
uint32_t dir_cluster = pImpl->allocate_cluster();
13011361

1362+
auto parent_entries = pImpl->read_dirents(parent_cluster);
1363+
const uint32_t slot_for_new_dir = static_cast<uint32_t>(parent_entries.size());
1364+
1365+
const auto now = timeToTod(time(nullptr));
1366+
13021367
PS2McDirEntry new_dir_entry = {};
1303-
new_dir_entry.mode = DF_DIR | DF_EXISTS | DF_RWX;
1368+
new_dir_entry.mode = DF_DIR | DF_EXISTS | DF_RWX | DF_0400;
13041369
new_dir_entry.name = dir_name;
13051370
new_dir_entry.cluster = dir_cluster;
1306-
new_dir_entry.length = 0;
1307-
new_dir_entry.created = timeToTod(time(nullptr));
1308-
new_dir_entry.modified = new_dir_entry.created;
1371+
new_dir_entry.length = 2;
1372+
new_dir_entry.created = now;
1373+
new_dir_entry.modified = now;
13091374

13101375
std::vector<PS2McDirEntry> new_dir_entries;
13111376

13121377
PS2McDirEntry dot_entry = {};
1313-
dot_entry.mode = DF_DIR | DF_EXISTS | DF_RWX;
1378+
dot_entry.mode = DF_DIR | DF_EXISTS | DF_RWX | DF_0400;
13141379
dot_entry.name = ".";
1315-
dot_entry.cluster = dir_cluster;
1316-
dot_entry.length = 0;
1317-
dot_entry.created = new_dir_entry.created;
1318-
dot_entry.modified = new_dir_entry.modified;
1319-
dot_entry.dirEntry = 0;
1380+
dot_entry.cluster = parent_cluster;
1381+
dot_entry.length = 2;
1382+
dot_entry.created = now;
1383+
dot_entry.modified = now;
1384+
dot_entry.dirEntry = slot_for_new_dir;
13201385
new_dir_entries.push_back(dot_entry);
13211386

13221387
PS2McDirEntry dotdot_entry = {};
1323-
dotdot_entry.mode = DF_DIR | DF_EXISTS | DF_RWX;
1388+
dotdot_entry.mode = DF_DIR | DF_EXISTS | DF_RWX | DF_0400;
13241389
dotdot_entry.name = "..";
1325-
dotdot_entry.cluster = parent_cluster;
1390+
dotdot_entry.cluster = 0;
13261391
dotdot_entry.length = 0;
1327-
dotdot_entry.created = new_dir_entry.created;
1328-
dotdot_entry.modified = new_dir_entry.modified;
1392+
dotdot_entry.created = now;
1393+
dotdot_entry.modified = now;
13291394
dotdot_entry.dirEntry = 0;
13301395
new_dir_entries.push_back(dotdot_entry);
13311396

13321397
pImpl->write_dirents(dir_cluster, new_dir_entries);
1333-
auto parent_entries = pImpl->read_dirents(parent_cluster);
1398+
13341399
parent_entries.push_back(new_dir_entry);
1400+
if (!parent_entries.empty() && (parent_entries[0].mode & DF_DIR))
1401+
{
1402+
parent_entries[0].length = static_cast<uint32_t>(parent_entries.size());
1403+
}
13351404

13361405
pImpl->write_dirents(parent_cluster, parent_entries);
13371406

@@ -1360,7 +1429,8 @@ void PS2MemoryCard::writeFile(const std::string& path, const std::vector<uint8_t
13601429
{
13611430
try
13621431
{
1363-
auto entry = pImpl->find_entry(path, pImpl->rootdir_fat_cluster);
1432+
uint32_t unusedParent = 0;
1433+
auto entry = pImpl->find_entry(path, unusedParent);
13641434
if (entry.mode & DF_EXISTS)
13651435
{
13661436
throw PS2McIOError("File already exists: " + path);
@@ -1387,7 +1457,11 @@ void PS2MemoryCard::writeFile(const std::string& path, const std::vector<uint8_t
13871457

13881458
// Calculate clusters needed
13891459
uint32_t clusters_needed = (static_cast<uint32_t>(data.size()) + pImpl->cluster_size - 1) / pImpl->cluster_size;
1390-
auto file_clusters = pImpl->allocate_clusters(clusters_needed);
1460+
std::vector<uint32_t> file_clusters;
1461+
if (clusters_needed > 0)
1462+
{
1463+
file_clusters = pImpl->allocate_clusters(clusters_needed);
1464+
}
13911465

13921466
uint32_t offset = 0;
13931467
for (uint32_t cluster : file_clusters)
@@ -1408,16 +1482,21 @@ void PS2MemoryCard::writeFile(const std::string& path, const std::vector<uint8_t
14081482
}
14091483

14101484
PS2McDirEntry file_entry = {};
1411-
file_entry.mode = DF_FILE | DF_EXISTS | DF_RWX;
1485+
file_entry.mode = DF_FILE | DF_EXISTS | DF_RWX | DF_0400;
14121486
file_entry.name = file_name;
1413-
file_entry.cluster = file_clusters[0];
1487+
file_entry.cluster = file_clusters.empty() ? PS2MC_FAT_CHAIN_END : file_clusters[0];
14141488
file_entry.length = static_cast<uint32_t>(data.size());
14151489
file_entry.created = timeToTod(time(nullptr));
14161490
file_entry.modified = file_entry.created;
14171491

14181492
auto parent_entries = pImpl->read_dirents(parent_cluster);
14191493
parent_entries.push_back(file_entry);
1494+
if (!parent_entries.empty() && (parent_entries[0].mode & DF_DIR) && parent_entries[0].name == ".")
1495+
{
1496+
parent_entries[0].length = static_cast<uint32_t>(parent_entries.size());
1497+
}
14201498
pImpl->write_dirents(parent_cluster, parent_entries);
1499+
pImpl->syncParentDirectoryEntryLength(parent_cluster);
14211500

14221501
pImpl->write_fat_to_card();
14231502

@@ -1540,7 +1619,7 @@ bool PS2MemoryCard::importSaveFile(PS2SaveFile& save, bool ignoreExisting, const
15401619

15411620
try
15421621
{
1543-
uint32_t dummy;
1622+
uint32_t dummy = 0;
15441623
pImpl->find_entry(savePath, dummy);
15451624

15461625
if (!ignoreExisting)
@@ -1563,27 +1642,70 @@ bool PS2MemoryCard::importSaveFile(PS2SaveFile& save, bool ignoreExisting, const
15631642

15641643
writeFile(filePath, entry.data);
15651644

1566-
uint32_t parent_cluster = 0;
1567-
auto currentEntry = pImpl->find_entry(filePath, parent_cluster);
1568-
auto parentEntries = pImpl->read_dirents(parent_cluster);
1645+
uint32_t parent_cluster_inner = 0;
1646+
auto currentEntry = pImpl->find_entry(filePath, parent_cluster_inner);
1647+
auto parentEntries = pImpl->read_dirents(parent_cluster_inner);
15691648
bool updated = false;
15701649
for (auto& pe : parentEntries)
15711650
{
15721651
if (pe.name == currentEntry.name)
15731652
{
1653+
const uint16_t psuMode = entry.dirEntry.mode;
1654+
constexpr uint16_t typeMask = DF_FILE | DF_DIR | DF_EXISTS;
1655+
pe.mode = static_cast<uint16_t>((psuMode & ~typeMask) | (pe.mode & typeMask));
1656+
if ((pe.mode & DF_EXISTS) && (pe.mode & (DF_FILE | DF_DIR)) && !(pe.mode & DF_PSX))
1657+
{
1658+
pe.mode |= DF_0400;
1659+
}
1660+
pe.unused = entry.dirEntry.unused;
15741661
pe.created = entry.dirEntry.created;
15751662
pe.modified = entry.dirEntry.modified;
1576-
pe.mode = (entry.dirEntry.mode & ~DF_DIR) | DF_EXISTS | DF_FILE;
1663+
pe.attr = entry.dirEntry.attr;
15771664
updated = true;
15781665
break;
15791666
}
15801667
}
15811668
if (updated)
15821669
{
1583-
pImpl->write_dirents(parent_cluster, parentEntries);
1670+
pImpl->write_dirents(parent_cluster_inner, parentEntries);
1671+
pImpl->syncParentDirectoryEntryLength(parent_cluster_inner);
15841672
}
15851673
}
15861674

1675+
{
1676+
uint32_t saveParentCluster = 0;
1677+
(void)pImpl->find_entry(savePath, saveParentCluster);
1678+
1679+
auto parentRows = pImpl->read_dirents(saveParentCluster);
1680+
const auto& hdr = entries[0].dirEntry;
1681+
constexpr uint16_t typeMask = DF_FILE | DF_DIR | DF_EXISTS;
1682+
for (auto& row : parentRows)
1683+
{
1684+
if (row.name == saveDirName && (row.mode & DF_DIR))
1685+
{
1686+
row.mode = static_cast<uint16_t>((hdr.mode & ~typeMask) | (row.mode & typeMask));
1687+
if ((row.mode & DF_EXISTS) && (row.mode & DF_DIR) && !(row.mode & DF_PSX))
1688+
{
1689+
row.mode |= DF_0400;
1690+
}
1691+
row.unused = hdr.unused;
1692+
row.created = hdr.created;
1693+
row.modified = hdr.modified;
1694+
row.attr = hdr.attr;
1695+
break;
1696+
}
1697+
}
1698+
pImpl->write_dirents(saveParentCluster, parentRows);
1699+
}
1700+
1701+
uint32_t rootParent = pImpl->rootdir_fat_cluster;
1702+
auto finalRoot = pImpl->read_dirents(rootParent);
1703+
if (!finalRoot.empty() && (finalRoot[0].mode & DF_DIR))
1704+
{
1705+
finalRoot[0].length = static_cast<uint32_t>(finalRoot.size());
1706+
pImpl->write_dirents(rootParent, finalRoot);
1707+
}
1708+
15871709
pImpl->write_fat_to_card();
15881710
pImpl->file.flush();
15891711

0 commit comments

Comments
 (0)