Files
UnleashedRecomp-hedge-dev/UnleashedRecomp/install/iso_file_system.cpp

199 lines
5.7 KiB
C++

// Referenced from: https://github.com/xenia-canary/xenia-canary/blob/canary_experimental/src/xenia/vfs/devices/disc_image_device.cc
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2023 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "iso_file_system.h"
#include <stack>
ISOFileSystem::ISOFileSystem(const std::filesystem::path &isoPath)
{
mappedFile.open(isoPath);
if (!mappedFile.isOpen())
{
return;
}
name = (const char *)(isoPath.filename().u8string().data());
// Find root sector.
const uint8_t *mappedFileData = mappedFile.data();
uint32_t gameOffset = 0;
const size_t XeSectorSize = 2048;
static const size_t PossibleOffsets[] = { 0x00000000, 0x0000FB20, 0x00020600, 0x02080000, 0x0FD90000, };
bool magicFound = false;
const char RefMagic[] = "MICROSOFT*XBOX*MEDIA";
for (size_t i = 0; i < std::size(PossibleOffsets); i++)
{
size_t fileOffset = PossibleOffsets[i] + (32 * XeSectorSize);
if ((fileOffset + strlen(RefMagic)) > mappedFile.size())
{
continue;
}
if (std::memcmp(&mappedFileData[fileOffset], RefMagic, strlen(RefMagic)) == 0)
{
gameOffset = PossibleOffsets[i];
magicFound = true;
}
}
size_t rootInfoOffset = gameOffset + (32 * XeSectorSize) + 20;
if (!magicFound || (rootInfoOffset + 8) > mappedFile.size())
{
mappedFile.close();
return;
}
// Parse root information.
uint32_t rootSector = *(uint32_t *)(&mappedFileData[rootInfoOffset + 0]);
uint32_t rootSize = *(uint32_t *)(&mappedFileData[rootInfoOffset + 4]);
size_t rootOffset = gameOffset + (rootSector * XeSectorSize);
const uint32_t MinRootSize = 13;
const uint32_t MaxRootSize = 32 * 1024 * 1024;
if ((rootSize < MinRootSize) || (rootSize > MaxRootSize))
{
mappedFile.close();
return;
}
struct IterationStep
{
std::string fileNameBase;
size_t nodeOffset = 0;
size_t entryOffset = 0;
IterationStep() = default;
IterationStep(std::string fileNameBase, size_t nodeOffset, size_t entryOffset) : fileNameBase(fileNameBase), nodeOffset(nodeOffset), entryOffset(entryOffset) { }
};
std::stack<IterationStep> iterationStack;
iterationStack.emplace("", rootOffset, 0);
IterationStep step;
uint16_t nodeL, nodeR;
uint32_t sector, length;
uint8_t attributes, nameLength;
char fileName[256];
const uint8_t FileAttributeDirectory = 0x10;
while (!iterationStack.empty())
{
step = iterationStack.top();
iterationStack.pop();
size_t infoOffset = step.nodeOffset + step.entryOffset;
if ((infoOffset + 14) > mappedFile.size())
{
mappedFile.close();
return;
}
nodeL = *(uint16_t *)(&mappedFileData[infoOffset + 0]);
nodeR = *(uint16_t *)(&mappedFileData[infoOffset + 2]);
sector = *(uint32_t *)(&mappedFileData[infoOffset + 4]);
length = *(uint32_t *)(&mappedFileData[infoOffset + 8]);
attributes = *(uint8_t *)(&mappedFileData[infoOffset + 12]);
nameLength = *(uint8_t *)(&mappedFileData[infoOffset + 13]);
size_t nameOffset = infoOffset + 14;
if ((nameOffset + nameLength) > mappedFile.size())
{
mappedFile.close();
return;
}
memcpy(fileName, &mappedFileData[nameOffset], nameLength);
fileName[nameLength] = '\0';
if (nodeL)
{
iterationStack.emplace(step.fileNameBase, step.nodeOffset, nodeL * 4);
}
if (nodeR)
{
iterationStack.emplace(step.fileNameBase, step.nodeOffset, nodeR * 4);
}
std::string fileNameUTF8 = step.fileNameBase + fileName;
if (attributes & FileAttributeDirectory)
{
if (length > 0)
{
iterationStack.emplace(fileNameUTF8 + "/", gameOffset + sector * XeSectorSize, 0);
}
}
else
{
fileMap[fileNameUTF8] = { gameOffset + sector * XeSectorSize, length};
}
}
}
bool ISOFileSystem::load(const std::string &path, uint8_t *fileData, size_t fileDataMaxByteCount) const
{
auto it = fileMap.find(path);
if (it != fileMap.end())
{
if (fileDataMaxByteCount < std::get<1>(it->second))
{
return false;
}
const uint8_t *mappedFileData = mappedFile.data();
memcpy(fileData, &mappedFileData[std::get<0>(it->second)], std::get<1>(it->second));
return true;
}
else
{
return false;
}
}
size_t ISOFileSystem::getSize(const std::string &path) const
{
auto it = fileMap.find(path);
if (it != fileMap.end())
{
return std::get<1>(it->second);
}
else
{
return 0;
}
}
bool ISOFileSystem::exists(const std::string &path) const
{
return fileMap.find(path) != fileMap.end();
}
const std::string &ISOFileSystem::getName() const
{
return name;
}
bool ISOFileSystem::empty() const
{
return !mappedFile.isOpen();
}
std::unique_ptr<ISOFileSystem> ISOFileSystem::create(const std::filesystem::path &isoPath) {
std::unique_ptr<ISOFileSystem> isoFs = std::make_unique<ISOFileSystem>(isoPath);
if (!isoFs->empty())
{
return isoFs;
}
else
{
return nullptr;
}
}