From 37c09343d82efec3f758f4ad08ccb59686fd8a10 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 7 Jun 2017 20:32:09 +0200 Subject: Turn VolumeDirectory into DirectoryBlob This lets VolumeDirectory/DirectoryBlob skip implementing various volume functions like GetGameID, GetBanner, etc. It also lets us view extracted discs in the game list. This ends up breaking the boot process for Wii DirectoryBlobs due to workarounds being removed from the boot process, but that will be fixed later by adding proper DirectoryBlob support for things like TMDs. We now expect the directories to be laid out in a certain format (based on the format that WIT uses) instead of requiring the user to set the DVD root and apploader path settings. --- Source/Core/Core/Boot/Boot.cpp | 82 +---- Source/Core/Core/Boot/Boot.h | 7 +- Source/Core/Core/Boot/DolReader.cpp | 6 + Source/Core/Core/Boot/DolReader.h | 6 + Source/Core/Core/Boot/ElfReader.cpp | 7 + Source/Core/Core/Boot/ElfReader.h | 6 + Source/Core/DiscIO/Blob.cpp | 17 +- Source/Core/DiscIO/CMakeLists.txt | 2 +- Source/Core/DiscIO/DirectoryBlob.cpp | 506 ++++++++++++++++++++++++++++ Source/Core/DiscIO/DirectoryBlob.h | 135 ++++++++ Source/Core/DiscIO/DiscIO.vcxproj | 4 +- Source/Core/DiscIO/DiscIO.vcxproj.filters | 8 +- Source/Core/DiscIO/Volume.cpp | 11 - Source/Core/DiscIO/Volume.h | 3 - Source/Core/DiscIO/VolumeDirectory.cpp | 535 ------------------------------ Source/Core/DiscIO/VolumeDirectory.h | 161 --------- 16 files changed, 702 insertions(+), 794 deletions(-) create mode 100644 Source/Core/DiscIO/DirectoryBlob.cpp create mode 100644 Source/Core/DiscIO/DirectoryBlob.h delete mode 100644 Source/Core/DiscIO/VolumeDirectory.cpp delete mode 100644 Source/Core/DiscIO/VolumeDirectory.h (limited to 'Source') diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index bf1f87b3ec..fd442a2a5b 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -132,44 +132,6 @@ bool CBoot::DVDRead(const DiscIO::Volume& volume, u64 dvd_offset, u32 output_add return true; } -void CBoot::Load_FST(bool is_wii, const DiscIO::Volume* volume) -{ - if (!volume) - return; - - const DiscIO::Partition partition = volume->GetGamePartition(); - - // copy first 32 bytes of disc to start of Mem 1 - DVDRead(*volume, /*offset*/ 0, /*address*/ 0, /*length*/ 0x20, DiscIO::PARTITION_NONE); - - // copy of game id - Memory::Write_U32(Memory::Read_U32(0x0000), 0x3180); - - u32 shift = 0; - if (is_wii) - shift = 2; - - const std::optional fst_offset = volume->ReadSwapped(0x0424, partition); - const std::optional fst_size = volume->ReadSwapped(0x0428, partition); - const std::optional max_fst_size = volume->ReadSwapped(0x042c, partition); - if (!fst_offset || !fst_size || !max_fst_size) - return; - - u32 arena_high = Common::AlignDown(0x817FFFFF - (*max_fst_size << shift), 0x20); - Memory::Write_U32(arena_high, 0x00000034); - - // load FST - DVDRead(*volume, *fst_offset << shift, arena_high, *fst_size << shift, partition); - Memory::Write_U32(arena_high, 0x00000038); - Memory::Write_U32(*max_fst_size << shift, 0x0000003c); - - if (is_wii) - { - // the apploader changes IOS MEM1_ARENA_END too - Memory::Write_U32(arena_high, 0x00003110); - } -} - void CBoot::UpdateDebugger_MapLoaded() { Host_NotifyMapLoaded(); @@ -308,15 +270,11 @@ bool CBoot::Load_BS2(const std::string& boot_rom_filename) return true; } -static const DiscIO::Volume* SetDefaultDisc() +static void SetDefaultDisc() { const SConfig& config = SConfig::GetInstance(); - // load default image or create virtual drive from directory - if (!config.m_strDVDRoot.empty()) - return SetDisc(DiscIO::CreateVolumeFromDirectory(config.m_strDVDRoot, config.bWii)); if (!config.m_strDefaultISO.empty()) - return SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO)); - return nullptr; + SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO)); } // Third boot step after BootManager and Core. See Call schedule in BootManager.cpp @@ -358,34 +316,14 @@ bool CBoot::BootUp(std::unique_ptr boot) if (!executable.reader->IsValid()) return false; - const DiscIO::Volume* volume = nullptr; - // VolumeDirectory only works with DOLs. - if (StringEndsWith(executable.path, ".dol")) - { - if (!config.m_strDVDRoot.empty()) - { - NOTICE_LOG(BOOT, "Setting DVDRoot %s", config.m_strDVDRoot.c_str()); - volume = SetDisc(DiscIO::CreateVolumeFromDirectory( - config.m_strDVDRoot, config.bWii, config.m_strApploader, executable.path)); - } - else if (!config.m_strDefaultISO.empty()) - { - NOTICE_LOG(BOOT, "Loading default ISO %s", config.m_strDefaultISO.c_str()); - volume = SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO)); - } - } - else - { - volume = SetDefaultDisc(); - } - if (!executable.reader->LoadIntoMemory()) { PanicAlertT("Failed to load the executable to memory."); return false; } - // Poor man's bootup + SetDefaultDisc(); + if (config.bWii) { HID4.SBE = 1; @@ -393,14 +331,13 @@ bool CBoot::BootUp(std::unique_ptr boot) SetupBAT(config.bWii); // Because there is no TMD to get the requested system (IOS) version from, // we default to IOS58, which is the version used by the Homebrew Channel. - SetupWiiMemory(volume, 0x000000010000003a); + SetupWiiMemory(nullptr, 0x000000010000003a); } else { - EmulatedBS2_GC(volume, true); + EmulatedBS2_GC(nullptr, true); } - Load_FST(config.bWii, volume); PC = executable.reader->GetEntryPoint(); if (executable.reader->LoadSymbols() || LoadMapFromFilename()) @@ -464,8 +401,13 @@ bool CBoot::BootUp(std::unique_ptr boot) } BootExecutableReader::BootExecutableReader(const std::string& file_name) + : BootExecutableReader(File::IOFile{file_name, "rb"}) +{ +} + +BootExecutableReader::BootExecutableReader(File::IOFile file) { - File::IOFile file{file_name, "rb"}; + file.Seek(0, SEEK_SET); m_bytes.resize(file.GetSize()); file.ReadBytes(m_bytes.data(), m_bytes.size()); } diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index 1f52daa1c5..69f784c827 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -15,6 +15,11 @@ #include "DiscIO/Enums.h" #include "DiscIO/Volume.h" +namespace File +{ +class IOFile; +} + struct RegionSetting { const std::string area; @@ -101,7 +106,6 @@ private: static bool EmulatedBS2_Wii(const DiscIO::Volume* volume); static bool EmulatedBS2(bool is_wii, const DiscIO::Volume* volume); static bool Load_BS2(const std::string& boot_rom_filename); - static void Load_FST(bool is_wii, const DiscIO::Volume* volume); static bool SetupWiiMemory(const DiscIO::Volume* volume, u64 ios_title_id); }; @@ -110,6 +114,7 @@ class BootExecutableReader { public: explicit BootExecutableReader(const std::string& file_name); + explicit BootExecutableReader(File::IOFile file); explicit BootExecutableReader(const std::vector& buffer); virtual ~BootExecutableReader(); diff --git a/Source/Core/Core/Boot/DolReader.cpp b/Source/Core/Core/Boot/DolReader.cpp index 391aace573..ef1ece315b 100644 --- a/Source/Core/Core/Boot/DolReader.cpp +++ b/Source/Core/Core/Boot/DolReader.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include "Common/File.h" @@ -18,6 +19,11 @@ DolReader::DolReader(const std::vector& buffer) : BootExecutableReader(buffe m_is_valid = Initialize(buffer); } +DolReader::DolReader(File::IOFile file) : BootExecutableReader(std::move(file)) +{ + m_is_valid = Initialize(m_bytes); +} + DolReader::DolReader(const std::string& filename) : BootExecutableReader(filename) { m_is_valid = Initialize(m_bytes); diff --git a/Source/Core/Core/Boot/DolReader.h b/Source/Core/Core/Boot/DolReader.h index e2daa04733..38f6616ccb 100644 --- a/Source/Core/Core/Boot/DolReader.h +++ b/Source/Core/Core/Boot/DolReader.h @@ -10,10 +10,16 @@ #include "Common/CommonTypes.h" #include "Core/Boot/Boot.h" +namespace File +{ +class IOFile; +} + class DolReader final : public BootExecutableReader { public: explicit DolReader(const std::string& filename); + explicit DolReader(File::IOFile file); explicit DolReader(const std::vector& buffer); ~DolReader(); diff --git a/Source/Core/Core/Boot/ElfReader.cpp b/Source/Core/Core/Boot/ElfReader.cpp index 78a77954ed..99faaa6fda 100644 --- a/Source/Core/Core/Boot/ElfReader.cpp +++ b/Source/Core/Core/Boot/ElfReader.cpp @@ -5,8 +5,10 @@ #include "Core/Boot/ElfReader.h" #include +#include #include "Common/CommonTypes.h" +#include "Common/FileUtil.h" #include "Common/Logging/Log.h" #include "Common/MsgHandler.h" #include "Common/Swap.h" @@ -71,6 +73,11 @@ ElfReader::ElfReader(const std::vector& buffer) : BootExecutableReader(buffe Initialize(m_bytes.data()); } +ElfReader::ElfReader(File::IOFile file) : BootExecutableReader(std::move(file)) +{ + Initialize(m_bytes.data()); +} + ElfReader::ElfReader(const std::string& filename) : BootExecutableReader(filename) { Initialize(m_bytes.data()); diff --git a/Source/Core/Core/Boot/ElfReader.h b/Source/Core/Core/Boot/ElfReader.h index 951f5a7b57..d9ffa78345 100644 --- a/Source/Core/Core/Boot/ElfReader.h +++ b/Source/Core/Core/Boot/ElfReader.h @@ -8,6 +8,11 @@ #include "Core/Boot/Boot.h" #include "Core/Boot/ElfTypes.h" +namespace File +{ +class IOFile; +} + enum KnownElfTypes { KNOWNELF_PSP = 0, @@ -22,6 +27,7 @@ class ElfReader final : public BootExecutableReader { public: explicit ElfReader(const std::string& filename); + explicit ElfReader(File::IOFile file); explicit ElfReader(const std::vector& buffer); ~ElfReader(); u32 Read32(int off) const { return base32[off >> 2]; } diff --git a/Source/Core/DiscIO/Blob.cpp b/Source/Core/DiscIO/Blob.cpp index da8640c7bc..a27f6861e1 100644 --- a/Source/Core/DiscIO/Blob.cpp +++ b/Source/Core/DiscIO/Blob.cpp @@ -16,6 +16,7 @@ #include "DiscIO/Blob.h" #include "DiscIO/CISOBlob.h" #include "DiscIO/CompressedBlob.h" +#include "DiscIO/DirectoryBlob.h" #include "DiscIO/DriveBlob.h" #include "DiscIO/FileBlob.h" #include "DiscIO/TGCBlob.h" @@ -183,12 +184,13 @@ std::unique_ptr CreateBlobReader(const std::string& filename) if (!file.ReadArray(&magic, 1)) return nullptr; - // Conveniently, every supported file format (except for plain disc images) starts - // with a 4-byte magic number that identifies the format, so we just need a simple - // switch statement to create the right blob type. If the magic number doesn't - // match any known magic number, we assume it's a plain disc image. If that - // assumption is wrong, the volume code that runs later will notice the error - // because the blob won't provide valid data when reading the GC/Wii disc header. + // Conveniently, every supported file format (except for plain disc images and + // extracted discs) starts with a 4-byte magic number that identifies the format, + // so we just need a simple switch statement to create the right blob type. If the + // magic number doesn't match any known magic number and the directory structure + // doesn't match the directory blob format, we assume it's a plain disc image. If + // that assumption is wrong, the volume code that runs later will notice the error + // because the blob won't provide the right data when reading the GC/Wii disc header. switch (magic) { @@ -201,6 +203,9 @@ std::unique_ptr CreateBlobReader(const std::string& filename) case WBFS_MAGIC: return WbfsFileReader::Create(std::move(file), filename); default: + if (DirectoryBlobReader::IsValidDirectoryBlob(filename)) + return DirectoryBlobReader::Create(std::move(file), filename); + return PlainFileReader::Create(std::move(file)); } } diff --git a/Source/Core/DiscIO/CMakeLists.txt b/Source/Core/DiscIO/CMakeLists.txt index b59435efa0..ef1598ecd7 100644 --- a/Source/Core/DiscIO/CMakeLists.txt +++ b/Source/Core/DiscIO/CMakeLists.txt @@ -3,6 +3,7 @@ set(SRCS CISOBlob.cpp WbfsBlob.cpp CompressedBlob.cpp + DirectoryBlob.cpp DiscExtractor.cpp DiscScrubber.cpp DriveBlob.cpp @@ -14,7 +15,6 @@ set(SRCS NANDImporter.cpp TGCBlob.cpp Volume.cpp - VolumeDirectory.cpp VolumeGC.cpp VolumeWad.cpp VolumeWii.cpp diff --git a/Source/Core/DiscIO/DirectoryBlob.cpp b/Source/Core/DiscIO/DirectoryBlob.cpp new file mode 100644 index 0000000000..9425869f26 --- /dev/null +++ b/Source/Core/DiscIO/DirectoryBlob.cpp @@ -0,0 +1,506 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DiscIO/DirectoryBlob.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common/Align.h" +#include "Common/Assert.h" +#include "Common/CommonPaths.h" +#include "Common/CommonTypes.h" +#include "Common/File.h" +#include "Common/FileUtil.h" +#include "Common/Logging/Log.h" +#include "Common/StringUtil.h" +#include "Common/Swap.h" +#include "Core/Boot/DolReader.h" +#include "DiscIO/Blob.h" + +namespace DiscIO +{ +static u32 ComputeNameSize(const File::FSTEntry& parent_entry); +static std::string ASCIIToUppercase(std::string str); +static void ConvertUTF8NamesToSHIFTJIS(File::FSTEntry& parent_entry); + +constexpr u64 GAME_PARTITION_ADDRESS = 0x50000; +constexpr u64 PARTITION_TABLE_ADDRESS = 0x40000; +const std::array PARTITION_TABLE = { + {Common::swap32(1), Common::swap32((PARTITION_TABLE_ADDRESS + 0x20) >> 2), 0, 0, 0, 0, 0, 0, + Common::swap32(GAME_PARTITION_ADDRESS >> 2), 0}}; + +const size_t DirectoryBlobReader::MAX_NAME_LENGTH; +const size_t DirectoryBlobReader::MAX_ID_LENGTH; + +static bool PathCharactersEqual(char a, char b) +{ + return a == b +#ifdef _WIN32 + || (a == '/' && b == '\\') || (a == '\\' && b == '/') +#endif + ; +} + +static bool PathEndsWith(const std::string& path, const std::string& suffix) +{ + if (suffix.size() > path.size()) + return false; + + std::string::const_iterator path_iterator = path.cend() - suffix.size(); + std::string::const_iterator suffix_iterator = suffix.cbegin(); + while (path_iterator != path.cend()) + { + if (!PathCharactersEqual(*path_iterator, *suffix_iterator)) + return false; + path_iterator++; + suffix_iterator++; + } + + return true; +} + +bool DirectoryBlobReader::IsValidDirectoryBlob(const std::string& dol_path) +{ + return PathEndsWith(dol_path, "/sys/main.dol"); +} + +std::unique_ptr DirectoryBlobReader::Create(File::IOFile dol, + const std::string& dol_path) +{ + if (!dol || !IsValidDirectoryBlob(dol_path)) + return nullptr; + + const size_t chars_to_remove = std::string("sys/main.dol").size(); + const std::string root_directory = dol_path.substr(0, dol_path.size() - chars_to_remove); + return std::unique_ptr( + new DirectoryBlobReader(std::move(dol), root_directory)); +} + +DirectoryBlobReader::DirectoryBlobReader(File::IOFile dol_file, const std::string& root_directory) + : m_root_directory(root_directory), m_data_start_address(UINT64_MAX), + m_disk_header(DISKHEADERINFO_ADDRESS), + m_disk_header_info(std::make_unique()), m_fst_address(0), m_dol_address(0) +{ + // create the default disk header + SetGameID("AGBJ01"); + SetName("Default name"); + + // Setting the DOL relies on m_dol_address, which is set by SetApploader + if (SetApploader(m_root_directory + "sys/apploader.img")) + SetDOLAndDiskType(std::move(dol_file)); + + BuildFST(); +} + +bool DirectoryBlobReader::ReadPartition(u64 offset, u64 length, u8* buffer) +{ + // header + if (offset < DISKHEADERINFO_ADDRESS) + { + WriteToBuffer(DISKHEADER_ADDRESS, DISKHEADERINFO_ADDRESS, m_disk_header.data(), &offset, + &length, &buffer); + } + // header info + if (offset >= DISKHEADERINFO_ADDRESS && offset < APPLOADER_ADDRESS) + { + WriteToBuffer(DISKHEADERINFO_ADDRESS, sizeof(m_disk_header_info), (u8*)m_disk_header_info.get(), + &offset, &length, &buffer); + } + // apploader + if (offset >= APPLOADER_ADDRESS && offset < APPLOADER_ADDRESS + m_apploader.size()) + { + WriteToBuffer(APPLOADER_ADDRESS, m_apploader.size(), m_apploader.data(), &offset, &length, + &buffer); + } + // dol + if (offset >= m_dol_address && offset < m_dol_address + m_dol.size()) + { + WriteToBuffer(m_dol_address, m_dol.size(), m_dol.data(), &offset, &length, &buffer); + } + // fst + if (offset >= m_fst_address && offset < m_data_start_address) + { + WriteToBuffer(m_fst_address, m_fst_data.size(), m_fst_data.data(), &offset, &length, &buffer); + } + + if (m_virtual_disk.empty()) + return true; + + // Determine which file the offset refers to + std::map::const_iterator fileIter = m_virtual_disk.lower_bound(offset); + if (fileIter->first > offset && fileIter != m_virtual_disk.begin()) + --fileIter; + + // zero fill to start of file data + PadToAddress(fileIter->first, &offset, &length, &buffer); + + while (fileIter != m_virtual_disk.end() && length > 0) + { + _dbg_assert_(DVDINTERFACE, fileIter->first <= offset); + u64 fileOffset = offset - fileIter->first; + const std::string fileName = fileIter->second; + + File::IOFile file(fileName, "rb"); + if (!file) + return false; + + u64 fileSize = file.GetSize(); + + if (fileOffset < fileSize) + { + u64 fileBytes = std::min(fileSize - fileOffset, length); + + if (!file.Seek(fileOffset, SEEK_SET)) + return false; + if (!file.ReadBytes(buffer, fileBytes)) + return false; + + length -= fileBytes; + buffer += fileBytes; + offset += fileBytes; + } + + ++fileIter; + + if (fileIter != m_virtual_disk.end()) + { + _dbg_assert_(DVDINTERFACE, fileIter->first >= offset); + PadToAddress(fileIter->first, &offset, &length, &buffer); + } + } + + return true; +} + +bool DirectoryBlobReader::ReadNonPartition(u64 offset, u64 length, u8* buffer) +{ + // header + if (offset < DISKHEADERINFO_ADDRESS) + { + WriteToBuffer(DISKHEADER_ADDRESS, DISKHEADERINFO_ADDRESS, m_disk_header.data(), &offset, + &length, &buffer); + } + if (offset >= 0x40000) + { + WriteToBuffer(PARTITION_TABLE_ADDRESS, PARTITION_TABLE.size() * sizeof(u32), + reinterpret_cast(PARTITION_TABLE.data()), &offset, &length, &buffer); + } + + // TODO: TMDs, tickets, more headers, the partition contents... + + if (length > 0) + { + ERROR_LOG(DISCIO, "Unsupported raw read in DirectoryBlob at 0x%" PRIx64, offset); + return false; + } + + return true; +} + +bool DirectoryBlobReader::Read(u64 offset, u64 length, u8* buffer) +{ + return m_is_wii ? ReadNonPartition(offset, length, buffer) : + ReadPartition(offset, length, buffer); +} + +bool DirectoryBlobReader::SupportsReadWiiDecrypted() const +{ + return m_is_wii; +} + +bool DirectoryBlobReader::ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64 partition_offset) +{ + if (!m_is_wii || partition_offset != GAME_PARTITION_ADDRESS) + return false; + + return ReadPartition(offset, size, buffer); +} + +void DirectoryBlobReader::SetGameID(const std::string& id) +{ + memcpy(m_disk_header.data(), id.c_str(), std::min(id.length(), MAX_ID_LENGTH)); +} + +void DirectoryBlobReader::SetName(const std::string& name) +{ + size_t length = std::min(name.length(), MAX_NAME_LENGTH); + memcpy(&m_disk_header[0x20], name.c_str(), length); + m_disk_header[length + 0x20] = 0; +} + +BlobType DirectoryBlobReader::GetBlobType() const +{ + return BlobType::DIRECTORY; +} + +u64 DirectoryBlobReader::GetRawSize() const +{ + // Not implemented + return 0; +} + +u64 DirectoryBlobReader::GetDataSize() const +{ + // Not implemented + return 0; +} + +void DirectoryBlobReader::SetDiskTypeWii() +{ + Write32(0x5d1c9ea3, 0x18, &m_disk_header); + memset(&m_disk_header[0x1c], 0, 4); + + m_is_wii = true; + m_address_shift = 2; +} + +void DirectoryBlobReader::SetDiskTypeGC() +{ + memset(&m_disk_header[0x18], 0, 4); + Write32(0xc2339f3d, 0x1c, &m_disk_header); + + m_is_wii = false; + m_address_shift = 0; +} + +bool DirectoryBlobReader::SetApploader(const std::string& apploader) +{ + if (!apploader.empty()) + { + std::string data; + if (!File::ReadFileToString(apploader, data)) + { + PanicAlertT("Apploader unable to load from file"); + return false; + } + size_t apploader_size = 0x20 + Common::swap32(*(u32*)&data.data()[0x14]) + + Common::swap32(*(u32*)&data.data()[0x18]); + if (apploader_size != data.size()) + { + PanicAlertT("Apploader is the wrong size...is it really an apploader?"); + return false; + } + m_apploader.resize(apploader_size); + std::copy(data.begin(), data.end(), m_apploader.begin()); + + // 32byte aligned (plus 0x20 padding) + m_dol_address = Common::AlignUp(APPLOADER_ADDRESS + m_apploader.size() + 0x20, 0x20ull); + return true; + } + else + { + m_apploader.resize(0x20); + // Make sure BS2 HLE doesn't try to run the apploader + Write32(static_cast(-1), 0x10, &m_apploader); + return false; + } +} + +void DirectoryBlobReader::SetDOLAndDiskType(File::IOFile dol_file) +{ + m_dol.resize(dol_file.GetSize()); + dol_file.Seek(0, SEEK_SET); + dol_file.ReadBytes(m_dol.data(), m_dol.size()); + + if (DolReader(std::move(dol_file)).IsWii()) + SetDiskTypeWii(); + else + SetDiskTypeGC(); + + Write32((u32)(m_dol_address >> m_address_shift), 0x0420, &m_disk_header); + + // 32byte aligned (plus 0x20 padding) + m_fst_address = Common::AlignUp(m_dol_address + m_dol.size() + 0x20, 0x20ull); +} + +void DirectoryBlobReader::BuildFST() +{ + m_fst_data.clear(); + + File::FSTEntry rootEntry = File::ScanDirectoryTree(m_root_directory + "files/", true); + + ConvertUTF8NamesToSHIFTJIS(rootEntry); + + u32 name_table_size = Common::AlignUp(ComputeNameSize(rootEntry), 1ull << m_address_shift); + u64 total_entries = rootEntry.size + 1; // The root entry itself isn't counted in rootEntry.size + + m_fst_name_offset = total_entries * ENTRY_SIZE; // offset of name table in FST + m_fst_data.resize(m_fst_name_offset + name_table_size); + + // if FST hasn't been assigned (ie no apploader/dol setup), set to default + if (m_fst_address == 0) + m_fst_address = APPLOADER_ADDRESS + 0x2000; + + // 32 KiB aligned start of data on disk + m_data_start_address = Common::AlignUp(m_fst_address + m_fst_data.size(), 0x8000ull); + u64 current_data_address = m_data_start_address; + + u32 fst_offset = 0; // Offset within FST data + u32 name_offset = 0; // Offset within name table + u32 root_offset = 0; // Offset of root of FST + + // write root entry + WriteEntryData(&fst_offset, DIRECTORY_ENTRY, 0, 0, total_entries, m_address_shift); + + WriteDirectory(rootEntry, &fst_offset, &name_offset, ¤t_data_address, root_offset); + + // overflow check, compare the aligned name offset with the aligned name table size + _assert_(Common::AlignUp(name_offset, 1ull << m_address_shift) == name_table_size); + + // write FST size and location + Write32((u32)(m_fst_address >> m_address_shift), 0x0424, &m_disk_header); + Write32((u32)(m_fst_data.size() >> m_address_shift), 0x0428, &m_disk_header); + Write32((u32)(m_fst_data.size() >> m_address_shift), 0x042c, &m_disk_header); +} + +void DirectoryBlobReader::WriteToBuffer(u64 source_start_address, u64 source_length, + const u8* source, u64* address, u64* length, + u8** buffer) const +{ + if (*length == 0) + return; + + _dbg_assert_(DVDINTERFACE, *address >= source_start_address); + + u64 source_offset = *address - source_start_address; + + if (source_offset < source_length) + { + size_t bytes_to_read = std::min(source_length - source_offset, *length); + + memcpy(*buffer, source + source_offset, bytes_to_read); + + *length -= bytes_to_read; + *buffer += bytes_to_read; + *address += bytes_to_read; + } +} + +void DirectoryBlobReader::PadToAddress(u64 start_address, u64* address, u64* length, + u8** buffer) const +{ + if (start_address > *address && *length > 0) + { + u64 padBytes = std::min(start_address - *address, *length); + memset(*buffer, 0, (size_t)padBytes); + *length -= padBytes; + *buffer += padBytes; + *address += padBytes; + } +} + +void DirectoryBlobReader::Write32(u32 data, u32 offset, std::vector* const buffer) +{ + (*buffer)[offset++] = (data >> 24); + (*buffer)[offset++] = (data >> 16) & 0xff; + (*buffer)[offset++] = (data >> 8) & 0xff; + (*buffer)[offset] = (data)&0xff; +} + +void DirectoryBlobReader::WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, + u64 data_offset, u64 length, u32 address_shift) +{ + m_fst_data[(*entry_offset)++] = type; + + m_fst_data[(*entry_offset)++] = (name_offset >> 16) & 0xff; + m_fst_data[(*entry_offset)++] = (name_offset >> 8) & 0xff; + m_fst_data[(*entry_offset)++] = (name_offset)&0xff; + + Write32((u32)(data_offset >> address_shift), *entry_offset, &m_fst_data); + *entry_offset += 4; + + Write32((u32)length, *entry_offset, &m_fst_data); + *entry_offset += 4; +} + +void DirectoryBlobReader::WriteEntryName(u32* name_offset, const std::string& name) +{ + strncpy((char*)&m_fst_data[*name_offset + m_fst_name_offset], name.c_str(), name.length() + 1); + + *name_offset += (u32)(name.length() + 1); +} + +void DirectoryBlobReader::WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset, + u32* name_offset, u64* data_offset, u32 parent_entry_index) +{ + std::vector sorted_entries = parent_entry.children; + + // Sort for determinism + std::sort(sorted_entries.begin(), sorted_entries.end(), [](const File::FSTEntry& one, + const File::FSTEntry& two) { + const std::string one_upper = ASCIIToUppercase(one.virtualName); + const std::string two_upper = ASCIIToUppercase(two.virtualName); + return one_upper == two_upper ? one.virtualName < two.virtualName : one_upper < two_upper; + }); + + for (const File::FSTEntry& entry : sorted_entries) + { + if (entry.isDirectory) + { + u32 entry_index = *fst_offset / ENTRY_SIZE; + WriteEntryData(fst_offset, DIRECTORY_ENTRY, *name_offset, parent_entry_index, + entry_index + entry.size + 1, 0); + WriteEntryName(name_offset, entry.virtualName); + + WriteDirectory(entry, fst_offset, name_offset, data_offset, entry_index); + } + else + { + // put entry in FST + WriteEntryData(fst_offset, FILE_ENTRY, *name_offset, *data_offset, entry.size, + m_address_shift); + WriteEntryName(name_offset, entry.virtualName); + + // write entry to virtual disk + _dbg_assert_(DVDINTERFACE, m_virtual_disk.find(*data_offset) == m_virtual_disk.end()); + m_virtual_disk.emplace(*data_offset, entry.physicalName); + + // 32 KiB aligned - many games are fine with less alignment, but not all + *data_offset = Common::AlignUp(*data_offset + std::max(entry.size, 1ull), 0x8000ull); + } + } +} + +static u32 ComputeNameSize(const File::FSTEntry& parent_entry) +{ + u32 name_size = 0; + for (const File::FSTEntry& entry : parent_entry.children) + { + if (entry.isDirectory) + name_size += ComputeNameSize(entry); + + name_size += (u32)entry.virtualName.length() + 1; + } + return name_size; +} + +static void ConvertUTF8NamesToSHIFTJIS(File::FSTEntry& parent_entry) +{ + for (File::FSTEntry& entry : parent_entry.children) + { + if (entry.isDirectory) + ConvertUTF8NamesToSHIFTJIS(entry); + + entry.virtualName = UTF8ToSHIFTJIS(entry.virtualName); + } +} + +static std::string ASCIIToUppercase(std::string str) +{ + std::transform(str.begin(), str.end(), str.begin(), + [](char c) { return std::toupper(c, std::locale::classic()); }); + return str; +} + +} // namespace diff --git a/Source/Core/DiscIO/DirectoryBlob.h b/Source/Core/DiscIO/DirectoryBlob.h new file mode 100644 index 0000000000..f79929615d --- /dev/null +++ b/Source/Core/DiscIO/DirectoryBlob.h @@ -0,0 +1,135 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/FileUtil.h" +#include "DiscIO/Blob.h" + +namespace File +{ +struct FSTEntry; +class IOFile; +} + +namespace DiscIO +{ +class DirectoryBlobReader : public BlobReader +{ +public: + static bool IsValidDirectoryBlob(const std::string& dol_path); + static std::unique_ptr Create(File::IOFile dol, const std::string& dol_path); + + bool Read(u64 offset, u64 length, u8* buffer) override; + bool SupportsReadWiiDecrypted() const override; + bool ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64 partition_offset) override; + + BlobType GetBlobType() const override; + u64 GetRawSize() const override; + u64 GetDataSize() const override; + + void SetGameID(const std::string& id); + void SetName(const std::string&); + + void BuildFST(); + +private: + DirectoryBlobReader(File::IOFile dol_file, const std::string& root_directory); + + bool ReadPartition(u64 offset, u64 length, u8* buffer); + bool ReadNonPartition(u64 offset, u64 length, u8* buffer); + + void SetDiskTypeWii(); + void SetDiskTypeGC(); + + bool SetApploader(const std::string& apploader); + + void SetDOLAndDiskType(File::IOFile dol_file); + + // writing to read buffer + void WriteToBuffer(u64 source_start_address, u64 source_length, const u8* source, u64* address, + u64* length, u8** buffer) const; + + void PadToAddress(u64 start_address, u64* address, u64* length, u8** buffer) const; + + void Write32(u32 data, u32 offset, std::vector* const buffer); + + // FST creation + void WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, u64 data_offset, u64 length, + u32 address_shift); + void WriteEntryName(u32* name_offset, const std::string& name); + void WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset, u32* name_offset, + u64* data_offset, u32 parent_entry_index); + + std::string m_root_directory; + + std::map m_virtual_disk; + + bool m_is_wii = false; + + // GameCube has no shift, Wii has 2 bit shift + u32 m_address_shift = 0; + + // first address on disk containing file data + u64 m_data_start_address; + + u64 m_fst_name_offset; + std::vector m_fst_data; + + std::vector m_disk_header; + +#pragma pack(push, 1) + struct SDiskHeaderInfo + { + u32 debug_monitor_size; + u32 simulated_mem_size; + u32 arg_offset; + u32 debug_flag; + u32 track_location; + u32 track_size; + u32 country_code; + u32 unknown; + u32 unknown2; + + // All the data is byteswapped + SDiskHeaderInfo() + { + debug_monitor_size = 0; + simulated_mem_size = 0; + arg_offset = 0; + debug_flag = 0; + track_location = 0; + track_size = 0; + country_code = 0; + unknown = 0; + unknown2 = 0; + } + }; +#pragma pack(pop) + std::unique_ptr m_disk_header_info; + + std::vector m_apploader; + std::vector m_dol; + + u64 m_fst_address; + u64 m_dol_address; + + static constexpr u8 ENTRY_SIZE = 0x0c; + static constexpr u8 FILE_ENTRY = 0; + static constexpr u8 DIRECTORY_ENTRY = 1; + static constexpr u64 DISKHEADER_ADDRESS = 0; + static constexpr u64 DISKHEADERINFO_ADDRESS = 0x440; + static constexpr u64 APPLOADER_ADDRESS = 0x2440; + static const size_t MAX_NAME_LENGTH = 0x3df; + static const size_t MAX_ID_LENGTH = 6; +}; + +} // namespace diff --git a/Source/Core/DiscIO/DiscIO.vcxproj b/Source/Core/DiscIO/DiscIO.vcxproj index 3a978ad262..b7ef5b9875 100644 --- a/Source/Core/DiscIO/DiscIO.vcxproj +++ b/Source/Core/DiscIO/DiscIO.vcxproj @@ -39,6 +39,7 @@ + @@ -50,7 +51,6 @@ - @@ -61,6 +61,7 @@ + @@ -72,7 +73,6 @@ - diff --git a/Source/Core/DiscIO/DiscIO.vcxproj.filters b/Source/Core/DiscIO/DiscIO.vcxproj.filters index 3c1f85ca10..3cebc18705 100644 --- a/Source/Core/DiscIO/DiscIO.vcxproj.filters +++ b/Source/Core/DiscIO/DiscIO.vcxproj.filters @@ -57,8 +57,8 @@ Volume\Blob - - Volume + + Volume\Blob Volume @@ -122,8 +122,8 @@ Volume - - Volume + + Volume\Blob Volume diff --git a/Source/Core/DiscIO/Volume.cpp b/Source/Core/DiscIO/Volume.cpp index 876136e6b9..014eda7d31 100644 --- a/Source/Core/DiscIO/Volume.cpp +++ b/Source/Core/DiscIO/Volume.cpp @@ -22,7 +22,6 @@ #include "DiscIO/Blob.h" #include "DiscIO/Enums.h" -#include "DiscIO/VolumeDirectory.h" #include "DiscIO/VolumeGC.h" #include "DiscIO/VolumeWad.h" #include "DiscIO/VolumeWii.h" @@ -112,14 +111,4 @@ std::unique_ptr CreateVolumeFromFilename(const std::string& filename) return nullptr; } -std::unique_ptr CreateVolumeFromDirectory(const std::string& directory, bool is_wii, - const std::string& apploader, - const std::string& dol) -{ - if (VolumeDirectory::IsValidDirectory(directory)) - return std::make_unique(directory, is_wii, apploader, dol); - - return nullptr; -} - } // namespace diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 209a0d09a9..979a63a23f 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -120,8 +120,5 @@ protected: }; std::unique_ptr CreateVolumeFromFilename(const std::string& filename); -std::unique_ptr CreateVolumeFromDirectory(const std::string& directory, bool is_wii, - const std::string& apploader = "", - const std::string& dol = ""); } // namespace diff --git a/Source/Core/DiscIO/VolumeDirectory.cpp b/Source/Core/DiscIO/VolumeDirectory.cpp deleted file mode 100644 index 854aec3da6..0000000000 --- a/Source/Core/DiscIO/VolumeDirectory.cpp +++ /dev/null @@ -1,535 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Common/Align.h" -#include "Common/Assert.h" -#include "Common/CommonPaths.h" -#include "Common/CommonTypes.h" -#include "Common/File.h" -#include "Common/FileUtil.h" -#include "Common/Logging/Log.h" -#include "Common/StringUtil.h" -#include "DiscIO/Blob.h" -#include "DiscIO/Enums.h" -#include "DiscIO/Volume.h" -#include "DiscIO/VolumeDirectory.h" - -namespace DiscIO -{ -static u32 ComputeNameSize(const File::FSTEntry& parent_entry); -static std::string ASCIIToUppercase(std::string str); -static void ConvertUTF8NamesToSHIFTJIS(File::FSTEntry& parent_entry); - -const size_t VolumeDirectory::MAX_NAME_LENGTH; -const size_t VolumeDirectory::MAX_ID_LENGTH; - -VolumeDirectory::VolumeDirectory(const std::string& directory, bool is_wii, - const std::string& apploader, const std::string& dol) - : m_data_start_address(UINT64_MAX), m_disk_header(DISKHEADERINFO_ADDRESS), - m_disk_header_info(std::make_unique()), m_fst_address(0), m_dol_address(0) -{ - m_root_directory = ExtractDirectoryName(directory); - - // create the default disk header - SetGameID("AGBJ01"); - SetName("Default name"); - - if (is_wii) - SetDiskTypeWii(); - else - SetDiskTypeGC(); - - // Don't load the DOL if we don't have an apploader - if (SetApploader(apploader)) - SetDOL(dol); - - BuildFST(); -} - -VolumeDirectory::~VolumeDirectory() -{ -} - -bool VolumeDirectory::IsValidDirectory(const std::string& directory) -{ - return File::IsDirectory(ExtractDirectoryName(directory)); -} - -bool VolumeDirectory::Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const -{ - bool decrypt = partition != PARTITION_NONE; - - if (!decrypt && (offset + length >= 0x400) && m_is_wii) - { - // Fully supporting this would require re-encrypting every file that's read. - // Only supporting the areas that IOS allows software to read could be more feasible. - // Currently, only the header (up to 0x400) is supported, though we're cheating a bit - // with it by reading the header inside the current partition instead. Supporting the - // header is enough for booting games, but not for running things like the Disc Channel. - return false; - } - - if (decrypt && !m_is_wii) - return false; - - // header - if (offset < DISKHEADERINFO_ADDRESS) - { - WriteToBuffer(DISKHEADER_ADDRESS, DISKHEADERINFO_ADDRESS, m_disk_header.data(), &offset, - &length, &buffer); - } - // header info - if (offset >= DISKHEADERINFO_ADDRESS && offset < APPLOADER_ADDRESS) - { - WriteToBuffer(DISKHEADERINFO_ADDRESS, sizeof(m_disk_header_info), (u8*)m_disk_header_info.get(), - &offset, &length, &buffer); - } - // apploader - if (offset >= APPLOADER_ADDRESS && offset < APPLOADER_ADDRESS + m_apploader.size()) - { - WriteToBuffer(APPLOADER_ADDRESS, m_apploader.size(), m_apploader.data(), &offset, &length, - &buffer); - } - // dol - if (offset >= m_dol_address && offset < m_dol_address + m_dol.size()) - { - WriteToBuffer(m_dol_address, m_dol.size(), m_dol.data(), &offset, &length, &buffer); - } - // fst - if (offset >= m_fst_address && offset < m_data_start_address) - { - WriteToBuffer(m_fst_address, m_fst_data.size(), m_fst_data.data(), &offset, &length, &buffer); - } - - if (m_virtual_disk.empty()) - return true; - - // Determine which file the offset refers to - std::map::const_iterator fileIter = m_virtual_disk.lower_bound(offset); - if (fileIter->first > offset && fileIter != m_virtual_disk.begin()) - --fileIter; - - // zero fill to start of file data - PadToAddress(fileIter->first, &offset, &length, &buffer); - - while (fileIter != m_virtual_disk.end() && length > 0) - { - _dbg_assert_(DVDINTERFACE, fileIter->first <= offset); - u64 fileOffset = offset - fileIter->first; - const std::string fileName = fileIter->second; - - File::IOFile file(fileName, "rb"); - if (!file) - return false; - - u64 fileSize = file.GetSize(); - - if (fileOffset < fileSize) - { - u64 fileBytes = std::min(fileSize - fileOffset, length); - - if (!file.Seek(fileOffset, SEEK_SET)) - return false; - if (!file.ReadBytes(buffer, fileBytes)) - return false; - - length -= fileBytes; - buffer += fileBytes; - offset += fileBytes; - } - - ++fileIter; - - if (fileIter != m_virtual_disk.end()) - { - _dbg_assert_(DVDINTERFACE, fileIter->first >= offset); - PadToAddress(fileIter->first, &offset, &length, &buffer); - } - } - - return true; -} - -std::vector VolumeDirectory::GetPartitions() const -{ - return m_is_wii ? std::vector{GetGamePartition()} : std::vector(); -} - -Partition VolumeDirectory::GetGamePartition() const -{ - return m_is_wii ? Partition(0x50000) : PARTITION_NONE; -} - -std::string VolumeDirectory::GetGameID(const Partition& partition) const -{ - return std::string(m_disk_header.begin(), m_disk_header.begin() + MAX_ID_LENGTH); -} - -void VolumeDirectory::SetGameID(const std::string& id) -{ - memcpy(m_disk_header.data(), id.c_str(), std::min(id.length(), MAX_ID_LENGTH)); -} - -Region VolumeDirectory::GetRegion() const -{ - if (m_is_wii) - return RegionSwitchWii(m_disk_header[3]); - - return RegionSwitchGC(m_disk_header[3]); -} - -Country VolumeDirectory::GetCountry(const Partition& partition) const -{ - return CountrySwitch(m_disk_header[3]); -} - -std::string VolumeDirectory::GetMakerID(const Partition& partition) const -{ - // Not implemented - return "00"; -} - -std::string VolumeDirectory::GetInternalName(const Partition& partition) const -{ - char name[0x60]; - if (Read(0x20, 0x60, (u8*)name, partition)) - return DecodeString(name); - else - return ""; -} - -std::map VolumeDirectory::GetLongNames() const -{ - std::string name = GetInternalName(); - if (name.empty()) - return {}; - return {{Language::LANGUAGE_UNKNOWN, name}}; -} - -std::vector VolumeDirectory::GetBanner(int* width, int* height) const -{ - // Not implemented - *width = 0; - *height = 0; - return std::vector(); -} - -void VolumeDirectory::SetName(const std::string& name) -{ - size_t length = std::min(name.length(), MAX_NAME_LENGTH); - memcpy(&m_disk_header[0x20], name.c_str(), length); - m_disk_header[length + 0x20] = 0; -} - -std::string VolumeDirectory::GetApploaderDate(const Partition& partition) const -{ - // Not implemented - return "VOID"; -} - -Platform VolumeDirectory::GetVolumeType() const -{ - return m_is_wii ? Platform::WII_DISC : Platform::GAMECUBE_DISC; -} - -BlobType VolumeDirectory::GetBlobType() const -{ - // VolumeDirectory isn't actually a blob, but it sort of acts - // like one, so it makes sense that it has its own blob type. - // It should be made into a proper blob in the future. - return BlobType::DIRECTORY; -} - -u64 VolumeDirectory::GetSize() const -{ - // Not implemented - return 0; -} - -u64 VolumeDirectory::GetRawSize() const -{ - // Not implemented - return 0; -} - -std::string VolumeDirectory::ExtractDirectoryName(const std::string& directory) -{ - std::string result = directory; - - size_t last_separator = result.find_last_of(DIR_SEP_CHR); - - if (last_separator != result.size() - 1) - { - // TODO: This assumes that file names will always have a dot in them - // and directory names never will; both assumptions are often - // right but in general wrong. - size_t extension_start = result.find_last_of('.'); - if (extension_start != std::string::npos && extension_start > last_separator) - { - result.resize(last_separator); - } - } - else - { - result.resize(last_separator); - } - - return result; -} - -void VolumeDirectory::SetDiskTypeWii() -{ - Write32(0x5d1c9ea3, 0x18, &m_disk_header); - memset(&m_disk_header[0x1c], 0, 4); - - m_is_wii = true; - m_address_shift = 2; -} - -void VolumeDirectory::SetDiskTypeGC() -{ - memset(&m_disk_header[0x18], 0, 4); - Write32(0xc2339f3d, 0x1c, &m_disk_header); - - m_is_wii = false; - m_address_shift = 0; -} - -bool VolumeDirectory::SetApploader(const std::string& apploader) -{ - if (!apploader.empty()) - { - std::string data; - if (!File::ReadFileToString(apploader, data)) - { - PanicAlertT("Apploader unable to load from file"); - return false; - } - size_t apploader_size = 0x20 + Common::swap32(*(u32*)&data.data()[0x14]) + - Common::swap32(*(u32*)&data.data()[0x18]); - if (apploader_size != data.size()) - { - PanicAlertT("Apploader is the wrong size...is it really an apploader?"); - return false; - } - m_apploader.resize(apploader_size); - std::copy(data.begin(), data.end(), m_apploader.begin()); - - // 32byte aligned (plus 0x20 padding) - m_dol_address = Common::AlignUp(APPLOADER_ADDRESS + m_apploader.size() + 0x20, 0x20ull); - return true; - } - else - { - m_apploader.resize(0x20); - // Make sure BS2 HLE doesn't try to run the apploader - Write32(static_cast(-1), 0x10, &m_apploader); - return false; - } -} - -void VolumeDirectory::SetDOL(const std::string& dol) -{ - if (!dol.empty()) - { - std::string data; - File::ReadFileToString(dol, data); - m_dol.resize(data.size()); - std::copy(data.begin(), data.end(), m_dol.begin()); - - Write32((u32)(m_dol_address >> m_address_shift), 0x0420, &m_disk_header); - - // 32byte aligned (plus 0x20 padding) - m_fst_address = Common::AlignUp(m_dol_address + m_dol.size() + 0x20, 0x20ull); - } -} - -void VolumeDirectory::BuildFST() -{ - m_fst_data.clear(); - - File::FSTEntry rootEntry = File::ScanDirectoryTree(m_root_directory, true); - - ConvertUTF8NamesToSHIFTJIS(rootEntry); - - u32 name_table_size = Common::AlignUp(ComputeNameSize(rootEntry), 1ull << m_address_shift); - u64 total_entries = rootEntry.size + 1; // The root entry itself isn't counted in rootEntry.size - - m_fst_name_offset = total_entries * ENTRY_SIZE; // offset of name table in FST - m_fst_data.resize(m_fst_name_offset + name_table_size); - - // if FST hasn't been assigned (ie no apploader/dol setup), set to default - if (m_fst_address == 0) - m_fst_address = APPLOADER_ADDRESS + 0x2000; - - // 32 KiB aligned start of data on disk - m_data_start_address = Common::AlignUp(m_fst_address + m_fst_data.size(), 0x8000ull); - u64 current_data_address = m_data_start_address; - - u32 fst_offset = 0; // Offset within FST data - u32 name_offset = 0; // Offset within name table - u32 root_offset = 0; // Offset of root of FST - - // write root entry - WriteEntryData(&fst_offset, DIRECTORY_ENTRY, 0, 0, total_entries, m_address_shift); - - WriteDirectory(rootEntry, &fst_offset, &name_offset, ¤t_data_address, root_offset); - - // overflow check, compare the aligned name offset with the aligned name table size - _assert_(Common::AlignUp(name_offset, 1ull << m_address_shift) == name_table_size); - - // write FST size and location - Write32((u32)(m_fst_address >> m_address_shift), 0x0424, &m_disk_header); - Write32((u32)(m_fst_data.size() >> m_address_shift), 0x0428, &m_disk_header); - Write32((u32)(m_fst_data.size() >> m_address_shift), 0x042c, &m_disk_header); -} - -void VolumeDirectory::WriteToBuffer(u64 source_start_address, u64 source_length, const u8* source, - u64* address, u64* length, u8** buffer) const -{ - if (*length == 0) - return; - - _dbg_assert_(DVDINTERFACE, *address >= source_start_address); - - u64 source_offset = *address - source_start_address; - - if (source_offset < source_length) - { - size_t bytes_to_read = std::min(source_length - source_offset, *length); - - memcpy(*buffer, source + source_offset, bytes_to_read); - - *length -= bytes_to_read; - *buffer += bytes_to_read; - *address += bytes_to_read; - } -} - -void VolumeDirectory::PadToAddress(u64 start_address, u64* address, u64* length, u8** buffer) const -{ - if (start_address > *address && *length > 0) - { - u64 padBytes = std::min(start_address - *address, *length); - memset(*buffer, 0, (size_t)padBytes); - *length -= padBytes; - *buffer += padBytes; - *address += padBytes; - } -} - -void VolumeDirectory::Write32(u32 data, u32 offset, std::vector* const buffer) -{ - (*buffer)[offset++] = (data >> 24); - (*buffer)[offset++] = (data >> 16) & 0xff; - (*buffer)[offset++] = (data >> 8) & 0xff; - (*buffer)[offset] = (data)&0xff; -} - -void VolumeDirectory::WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, u64 data_offset, - u64 length, u32 address_shift) -{ - m_fst_data[(*entry_offset)++] = type; - - m_fst_data[(*entry_offset)++] = (name_offset >> 16) & 0xff; - m_fst_data[(*entry_offset)++] = (name_offset >> 8) & 0xff; - m_fst_data[(*entry_offset)++] = (name_offset)&0xff; - - Write32((u32)(data_offset >> address_shift), *entry_offset, &m_fst_data); - *entry_offset += 4; - - Write32((u32)length, *entry_offset, &m_fst_data); - *entry_offset += 4; -} - -void VolumeDirectory::WriteEntryName(u32* name_offset, const std::string& name) -{ - strncpy((char*)&m_fst_data[*name_offset + m_fst_name_offset], name.c_str(), name.length() + 1); - - *name_offset += (u32)(name.length() + 1); -} - -void VolumeDirectory::WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset, - u32* name_offset, u64* data_offset, u32 parent_entry_index) -{ - std::vector sorted_entries = parent_entry.children; - - // Sort for determinism - std::sort(sorted_entries.begin(), sorted_entries.end(), [](const File::FSTEntry& one, - const File::FSTEntry& two) { - const std::string one_upper = ASCIIToUppercase(one.virtualName); - const std::string two_upper = ASCIIToUppercase(two.virtualName); - return one_upper == two_upper ? one.virtualName < two.virtualName : one_upper < two_upper; - }); - - for (const File::FSTEntry& entry : sorted_entries) - { - if (entry.isDirectory) - { - u32 entry_index = *fst_offset / ENTRY_SIZE; - WriteEntryData(fst_offset, DIRECTORY_ENTRY, *name_offset, parent_entry_index, - entry_index + entry.size + 1, 0); - WriteEntryName(name_offset, entry.virtualName); - - WriteDirectory(entry, fst_offset, name_offset, data_offset, entry_index); - } - else - { - // put entry in FST - WriteEntryData(fst_offset, FILE_ENTRY, *name_offset, *data_offset, entry.size, - m_address_shift); - WriteEntryName(name_offset, entry.virtualName); - - // write entry to virtual disk - _dbg_assert_(DVDINTERFACE, m_virtual_disk.find(*data_offset) == m_virtual_disk.end()); - m_virtual_disk.emplace(*data_offset, entry.physicalName); - - // 32 KiB aligned - many games are fine with less alignment, but not all - *data_offset = Common::AlignUp(*data_offset + std::max(entry.size, 1ull), 0x8000ull); - } - } -} - -static u32 ComputeNameSize(const File::FSTEntry& parent_entry) -{ - u32 name_size = 0; - for (const File::FSTEntry& entry : parent_entry.children) - { - if (entry.isDirectory) - name_size += ComputeNameSize(entry); - - name_size += (u32)entry.virtualName.length() + 1; - } - return name_size; -} - -static void ConvertUTF8NamesToSHIFTJIS(File::FSTEntry& parent_entry) -{ - for (File::FSTEntry& entry : parent_entry.children) - { - if (entry.isDirectory) - ConvertUTF8NamesToSHIFTJIS(entry); - - entry.virtualName = UTF8ToSHIFTJIS(entry.virtualName); - } -} - -static std::string ASCIIToUppercase(std::string str) -{ - std::transform(str.begin(), str.end(), str.begin(), - [](char c) { return std::toupper(c, std::locale::classic()); }); - return str; -} - -} // namespace diff --git a/Source/Core/DiscIO/VolumeDirectory.h b/Source/Core/DiscIO/VolumeDirectory.h deleted file mode 100644 index 360570b063..0000000000 --- a/Source/Core/DiscIO/VolumeDirectory.h +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include -#include - -#include "Common/CommonTypes.h" -#include "DiscIO/Volume.h" - -namespace File -{ -struct FSTEntry; -} - -// -// --- this volume type is used for reading files directly from the hard drive --- -// - -namespace DiscIO -{ -enum class BlobType; -enum class Country; -enum class Language; -enum class Region; -enum class Platform; - -class VolumeDirectory : public Volume -{ -public: - VolumeDirectory(const std::string& directory, bool is_wii, const std::string& apploader = "", - const std::string& dol = ""); - - ~VolumeDirectory(); - - static bool IsValidDirectory(const std::string& directory); - - bool Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const override; - std::vector GetPartitions() const override; - Partition GetGamePartition() const override; - - std::string GetGameID(const Partition& partition = PARTITION_NONE) const override; - void SetGameID(const std::string& id); - - std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override; - - std::optional GetRevision(const Partition& partition = PARTITION_NONE) const override - { - return {}; - } - std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override; - std::map GetLongNames() const override; - std::vector GetBanner(int* width, int* height) const override; - void SetName(const std::string&); - - std::string GetApploaderDate(const Partition& partition = PARTITION_NONE) const override; - Platform GetVolumeType() const override; - - Region GetRegion() const override; - Country GetCountry(const Partition& partition = PARTITION_NONE) const override; - - BlobType GetBlobType() const override; - u64 GetSize() const override; - u64 GetRawSize() const override; - - void BuildFST(); - -private: - static std::string ExtractDirectoryName(const std::string& directory); - - void SetDiskTypeWii(); - void SetDiskTypeGC(); - - bool SetApploader(const std::string& apploader); - - void SetDOL(const std::string& dol); - - // writing to read buffer - void WriteToBuffer(u64 source_start_address, u64 source_length, const u8* source, u64* address, - u64* length, u8** buffer) const; - - void PadToAddress(u64 start_address, u64* address, u64* length, u8** buffer) const; - - void Write32(u32 data, u32 offset, std::vector* const buffer); - - // FST creation - void WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, u64 data_offset, u64 length, - u32 address_shift); - void WriteEntryName(u32* name_offset, const std::string& name); - void WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset, u32* name_offset, - u64* data_offset, u32 parent_entry_index); - - std::string m_root_directory; - - std::map m_virtual_disk; - - bool m_is_wii; - - // GameCube has no shift, Wii has 2 bit shift - u32 m_address_shift; - - // first address on disk containing file data - u64 m_data_start_address; - - u64 m_fst_name_offset; - std::vector m_fst_data; - - std::vector m_disk_header; - -#pragma pack(push, 1) - struct SDiskHeaderInfo - { - u32 debug_monitor_size; - u32 simulated_mem_size; - u32 arg_offset; - u32 debug_flag; - u32 track_location; - u32 track_size; - u32 country_code; - u32 unknown; - u32 unknown2; - - // All the data is byteswapped - SDiskHeaderInfo() - { - debug_monitor_size = 0; - simulated_mem_size = 0; - arg_offset = 0; - debug_flag = 0; - track_location = 0; - track_size = 0; - country_code = 0; - unknown = 0; - unknown2 = 0; - } - }; -#pragma pack(pop) - std::unique_ptr m_disk_header_info; - - std::vector m_apploader; - std::vector m_dol; - - u64 m_fst_address; - u64 m_dol_address; - - static constexpr u8 ENTRY_SIZE = 0x0c; - static constexpr u8 FILE_ENTRY = 0; - static constexpr u8 DIRECTORY_ENTRY = 1; - static constexpr u64 DISKHEADER_ADDRESS = 0; - static constexpr u64 DISKHEADERINFO_ADDRESS = 0x440; - static constexpr u64 APPLOADER_ADDRESS = 0x2440; - static const size_t MAX_NAME_LENGTH = 0x3df; - static const size_t MAX_ID_LENGTH = 6; -}; - -} // namespace -- cgit v1.2.3