@@ -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
274275void 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+
610647uint32_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
844903void 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