Files
jak-project/game/overlord/common/fake_iso.cpp
twi 796e0a4e06 Update filesystem.hpp dependency and un-break symlinks (#3990)
The context for this is that i was symlinking files from a mod
installation that were identical to the unmodded game, for space
considerations, because i remembered having success with that in the
past, before the project made the switch to ghc's filesystem library, it
seems. <sub>(I was also much more pressed for space back then, but well,
i thought writing a small program to check for matches and link them for
me would be a good, not-so-difficult exercise...)</sub>

However i quickly started getting weird errors that it couldn't find
GAME.CGO or KERNEL.CGO even when i hadn't touched them. Initially since
i found that it occurred only when symlinking any files that were
earlier alphabetically, i assumed it was some kind of index mismatch,
but it was actually caused by a bug in an older version of
filesystem.hpp causing the `directory_iterator` to read everything after
a symlink as also a symlink, making them fail to be read properly:
https://github.com/gulrak/filesystem/pull/162

Adding `f.is_symlink()` is not as much of a change to the loop's
condition from the previous behavior as it might first appear. It seems
that even without that condition the iterator *was* reading symlinks
before, just incorrectly (and before ghc::filesystem, they were being
read just fine, iirc). With the new version, though, symlinks do have to
be accounted for explicitly.

Given that the library's major and minor version are both the same, i
don't expect there should be any breaking changes to the API, and i
didn't find any when i was testing but there might need to be more
investigation into that before merging. Also, without the added check
for `f.exists()`, the behavior is exactly the same as if, for some
reason, a fakeiso file the game needs just doesn't exist in the first
place (which has no real reason to happen in a normal installation of
the game): there's a decent chance it will either crash at some point
later during runtime, or something will happen like a level failing to
load causing Jak to trip infinitely. I thought it might be helpful to
have it fail right away in the case that, say, someone moved their
vanilla installation and staled their symlinks, but i'll defer to
maintainers' judgement on that point.
2025-07-31 19:53:29 -04:00

154 lines
4.6 KiB
C++

/*!
* @file fake_iso.cpp
* This provides an implementation of IsoFs for reading a "fake iso".
* A "fake iso" is just a map file which maps 8.3 ISO file names to files in the source folder.
* This way we don't need to actually create an ISO.
*
* The game has this compilation unit, but there is nothing in it. Probably it is removed to save
* IOP memory and was only included on TOOL-only builds. So this is my interpretation of how it
* should work.
*/
#include "fake_iso.h"
#include <cstring>
#include "common/log/log.h"
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "game/common/overlord_common.h"
#include "game/overlord/common/isocommon.h"
#include "game/overlord/common/overlord.h"
#include "game/overlord/common/sbank.h"
#include "game/overlord/common/soundcommon.h"
#include "game/overlord/common/srpc.h"
#include "game/runtime.h"
#include "game/sce/iop.h"
#include "game/sound/sndshim.h"
using namespace iop;
BS::thread_pool thpool(4);
/*!
* Map from iso file name to file path in the src folder.
*/
struct FakeIsoEntry {
char iso_name[16];
std::string full_path;
};
FakeIsoEntry fake_iso_entries[MAX_ISO_FILES]; //! List of all known files
static FileRecord sFiles[MAX_ISO_FILES]; //! List of "FileRecords" for IsoFs API consumers
u32 fake_iso_entry_count; //! Total count of fake iso files
void fake_iso_init_globals() {
// init file lists
memset(fake_iso_entries, 0, sizeof(fake_iso_entries));
memset(sFiles, 0, sizeof(sFiles));
fake_iso_entry_count = 0;
}
/*!
* Initialize the file system.
*/
int fake_iso_FS_Init() {
for (const auto& f : fs::directory_iterator(file_util::get_jak_project_dir() / "out" /
game_version_names[g_game_version] / "iso")) {
if (f.is_regular_file() || f.is_symlink()) {
ASSERT(fake_iso_entry_count < MAX_ISO_FILES);
FakeIsoEntry* e = &fake_iso_entries[fake_iso_entry_count];
std::string file_name = f.path().filename().string();
ASSERT(file_name.length() < 16); // should be 8.3.
ASSERT_MSG(f.exists(), // should only happen if the file is a symlink, afaik
fmt::format("[FAKEISO] couldn't find {} -- broken symlink?", file_name));
strcpy(e->iso_name, file_name.c_str());
e->full_path = fmt::format("{}/out/{}/iso/{}", file_util::get_jak_project_dir().string(),
game_version_names[g_game_version], file_name);
fake_iso_entry_count++;
}
}
for (u32 i = 0; i < fake_iso_entry_count; i++) {
MakeISOName(sFiles[i].name, fake_iso_entries[i].iso_name);
// we don't figure out the size yet.
// this is so you can change the file without restarting the game.
sFiles[i].size = -1;
// repurpose "location" as the index.
sFiles[i].location = i;
}
LoadMusicTweaks();
return 0;
}
/*!
* Find a file on the disc and return a FileRecord.
* Find using a "normal" 8.3 name.
* This is an ISO FS API Function
*/
FileRecord* FS_Find(const char* name) {
char name_buff[16];
MakeISOName(name_buff, name);
return FS_FindIN(name_buff);
}
/*!
* Find a file on the disc. Uses the "ISO name" of the file, which is different from the normal 8.3
* name. This can be generated with MakeISOFile.
* This is an ISO FS API Function.
*/
FileRecord* FS_FindIN(const char* iso_name) {
const uint32_t* buff = (const uint32_t*)iso_name;
uint32_t count = 0;
while (count < fake_iso_entry_count) {
const uint32_t* ref = (uint32_t*)sFiles[count].name;
if (ref[0] == buff[0] && ref[1] == buff[1] && ref[2] == buff[2]) {
return sFiles + count;
}
count++;
}
lg::error("[FAKEISO] failed to find {}\n", iso_name);
return nullptr;
}
/*!
* Build a full file path for a FileRecord.
*/
const char* get_file_path(FileRecord* fr) {
ASSERT(fr->location < fake_iso_entry_count);
return fake_iso_entries[fr->location].full_path.c_str();
}
/*!
* Determine the length of a file. This isn't very fast, but nobody checks file sizes extremely
* quickly. This is an ISO FS API Function
*/
uint32_t FS_GetLength(FileRecord* fr) {
const char* path = get_file_path(fr);
file_util::assert_file_exists(path, "fake_iso FS_GetLength");
FILE* fp = file_util::open_file(path, "rb");
ASSERT(fp);
fseek(fp, 0, SEEK_END);
uint32_t len = ftell(fp);
rewind(fp);
fclose(fp);
return len;
}
void LoadMusicTweaks() {
char tweakname[16];
MakeISOName(tweakname, "TWEAKVAL.MUS");
auto file = FS_FindIN(tweakname);
if (file) {
auto fp = file_util::open_file(get_file_path(file), "rb");
fread(&gMusicTweakInfo, sizeof(gMusicTweakInfo), 1, fp);
fclose(fp);
} else {
gMusicTweakInfo.TweakCount = 0;
}
}