1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-12-23 01:59:43 +01:00

Switch assets mounting to dedicated directory

This fixes an issue where you would be able to mount things other than
custom assets in per-level custom asset directories and zips.

To be fair, the effects of this issue were fairly limited - about the
only thing I could do with it was to override a user-made quicksave of a
custom level with one of my own. However, since the quicksave check
happens before assets are mounted, if the user didn't have an existing
quicksave then they wouldn't be able load my quicksave. Furthermore,
mounting things like settings.vvv simply doesn't work because assets
only get mounted when the level gets loaded, but the game only reads
from settings.vvv on startup.

Still, this is an issue, and just because it only has one effect doesn't
mean we should single-case patch that one effect only. So what can we
do?

I was thinking that we should (1) mount custom assets in a dedicated
directory, and then from there (2) mount each specific asset directly -
namely, mount the graphics/ and sounds/ folders, and mount the
vvvvvvmusic.vvv and mmmmmm.vvv files. For (1), assets are now mounted at
a (non-existent) location named .vvv-mnt/assets/. However, (2) doesn't
fully work due to how PhysFS works.

What DOES work is being able to mount the graphics/ and sounds/ folders,
but only if the custom assets directory is a directory. And, you
actually have to use the real directory where those graphics/ and
sounds/ folders are located, and not the mounted directory, because
PHYSFS_mount() only accepts real directories. (In which case why bother
mounting the directory in the first place if we have to use real
directories anyway?) So already this seems like having different
directory and zip mounting paths, which I don't want...

I tried to unify the directory and zip paths and get around the real
directory limitation. So for mounting each individual asset (i.e.
graphics/, sounds/, but especially vvvvvvmusic.vvv and mmmmmm.vvv), I
tried doing PHYSFS_openRead() followed by PHYSFS_mountHandle() with that
PHYSFS_File, but this simply doesn't work, because PHYSFS_mountHandle()
will always create a PHYSFS_Io object, and pass it to a PhysFS internal
helper function named openDirectory() which will only attempt to treat
it as a directory if the PHYSFS_Io* passed is NULL. Since
PHYSFS_mountHandle() always passes a non-NULL PHYSFS_Io*,
openDirectory() will always treat it like a zip file and never as a
directory - in contrast, PHYSFS_mount() will always pass a NULL
PHYSFS_Io* to openDirectory(), so PHYSFS_mount() is the only function
that works for mounting directories.

(And even if this did work, having to keep the file open (because of the
PHYSFS_openRead()) results in the user being unable to touch the file on
Windows until it gets closed, which I also don't want.)

As for zip files, PHYSFS_mount() works just fine on them, but then we
run into the issue of accessing the individual assets inside it. As
covered above, PHYSFS_mount() only accepts real directories, so we can't
use it to access the assets inside, but then if we do the
PHYSFS_openRead() and PHYSFS_mountHandle() approach,
PHYSFS_mountHandle() will treat the assets inside as zip files instead
of just mounting them normally!

So in short, PhysFS only seems to be able to mount directories and zip
files, and not any loose individual files (like vvvvvvmusic.vvv and
mmmmmm.vvv). Furthermore, directories inside directories works, but
directories inside zip files doesn't (only zip files inside zip files
work).

It seems like our asset paths don't really work well with PhysFS's
design. Currently, graphics/, sounds/, vvvvvvmusic.vvv, and mmmmmm.vvv
all live at the root directory of the VVVVVV folder. But what would work
better is if all of those items were organized into a subfolder, for
example, a folder named assets/. So the previous assets mounting system
before this patch would just have mounted assets/ and be done with it,
and there would be no risk of mounting extraneous files that could do
bad things. However, due to our unorganized asset paths, the previous
system has to mount assets at the root of the VVVVVV folder, which
invites the possibility of those extraneous bad files being mounted.

Well, we can't change the asset paths now, that would be a pretty big
API break (maybe it should be a 2.4 thing). So what can we do?

What I've done is, after mounting the assets at .vvv-mnt/assets/, when
the game loads an asset, it checks if there's an override available
inside .vvv-mnt/assets/, and if so, the game will load that asset
instead of the regular one. This is basically reimplementing what PhysFS
SHOULD be able to do for us, but can't. This fixes the issue of being
able to mount a quicksave for a custom level inside its asset directory.

I should also note, the unorganized asset paths issue also means that
for .zip files (which contain the level file), the level file itself is
also technically mounted at .vvv-mnt/assets/. This is harmless (because
when we load a level file, we never load it as an asset) but it's still
a bit ugly. Changing the asset paths now seems more and more like a good
thing to do...
This commit is contained in:
Misa 2021-04-05 11:32:10 -07:00 committed by Ethan Lee
parent 798bf9e490
commit 300f1b7919

View file

@ -9,6 +9,7 @@
#include "Exit.h" #include "Exit.h"
#include "Graphics.h" #include "Graphics.h"
#include "Maths.h"
#include "UtilityClass.h" #include "UtilityClass.h"
/* These are needed for PLATFORM_* crap */ /* These are needed for PLATFORM_* crap */
@ -27,6 +28,7 @@ static char saveDir[MAX_PATH] = {'\0'};
static char levelDir[MAX_PATH] = {'\0'}; static char levelDir[MAX_PATH] = {'\0'};
static char assetDir[MAX_PATH] = {'\0'}; static char assetDir[MAX_PATH] = {'\0'};
static char virtualMountPath[MAX_PATH] = {'\0'};
static void PLATFORM_getOSDirectory(char* output); static void PLATFORM_getOSDirectory(char* output);
static void PLATFORM_migrateSaveData(char* output); static void PLATFORM_migrateSaveData(char* output);
@ -213,6 +215,33 @@ static bool FILESYSTEM_exists(const char *fname)
return PHYSFS_exists(fname); return PHYSFS_exists(fname);
} }
static void generateVirtualMountPath(char* path, const size_t path_size)
{
char random[6 + 1] = {'\0'};
size_t i;
for (i = 0; i < SDL_arraysize(random) - 1; ++i)
{
/* Generate a-z0-9 (base 36) */
char randchar = fRandom() * 36;
if (randchar <= 26)
{
randchar += 'a';
}
else
{
randchar -= 26;
randchar += '0';
}
random[i] = randchar;
}
SDL_snprintf(
path,
path_size,
".vvv-mnt-virtual-%s/custom-assets/",
random
);
}
static bool FILESYSTEM_mountAssetsFrom(const char *fname) static bool FILESYSTEM_mountAssetsFrom(const char *fname)
{ {
const char* real_dir = PHYSFS_getRealDir(fname); const char* real_dir = PHYSFS_getRealDir(fname);
@ -229,7 +258,9 @@ static bool FILESYSTEM_mountAssetsFrom(const char *fname)
SDL_snprintf(path, sizeof(path), "%s/%s", real_dir, fname); SDL_snprintf(path, sizeof(path), "%s/%s", real_dir, fname);
if (!PHYSFS_mount(path, NULL, 0)) generateVirtualMountPath(virtualMountPath, sizeof(virtualMountPath));
if (!PHYSFS_mount(path, virtualMountPath, 0))
{ {
printf( printf(
"Error mounting %s: %s\n", "Error mounting %s: %s\n",
@ -452,8 +483,28 @@ void FILESYSTEM_loadAssetToMemory(
const bool addnull const bool addnull
) { ) {
const char* path; const char* path;
const bool assets_mounted = assetDir[0] != '\0';
char mounted_path[MAX_PATH];
path = name; if (assets_mounted)
{
SDL_snprintf(
mounted_path,
sizeof(mounted_path),
"%s%s",
virtualMountPath,
name
);
}
if (assets_mounted && PHYSFS_exists(mounted_path))
{
path = mounted_path;
}
else
{
path = name;
}
FILESYSTEM_loadFileToMemory(path, mem, len, addnull); FILESYSTEM_loadFileToMemory(path, mem, len, addnull);
} }