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

Add zip structure checks for user friendliness

If a level zip is named LEVELNAME.zip, the level file inside it must
also be named LEVELNAME.vvvvvv, else custom assets won't work.

This is because when we mount the zip file, we simply add
LEVELNAME.vvvvvv to the levels directory. Then whenever we load
LEVELNAME.vvvvvv, we look at the filename, remove the extension, and
look for the assets inside the zip of the same name, LEVELNAME.zip.

As a result, if someone were to make a level zip with assets but
mismatch the filename, the assets wouldn't load. Furthermore, if someone
were to add extra levels in the same zip, they wouldn't have any assets
load for them as well, which could be confusing.

To make things crystal-clear to the user, we now filter out any zips
that have incorrect structures like that, and print a message to the
terminal. Unfortunately nothing gets shown for non-terminal users, but
at least doing this and filtering out the zips is less confusing than
letting them through but with the issues mentioned above.
This commit is contained in:
Misa 2021-05-20 14:20:05 -07:00 committed by Ethan Lee
parent 153a5c4c3a
commit 96660cd235

View file

@ -11,6 +11,7 @@
#include "Exit.h" #include "Exit.h"
#include "Graphics.h" #include "Graphics.h"
#include "Maths.h" #include "Maths.h"
#include "Unused.h"
#include "UtilityClass.h" #include "UtilityClass.h"
/* These are needed for PLATFORM_* crap */ /* These are needed for PLATFORM_* crap */
@ -309,10 +310,148 @@ static bool FILESYSTEM_mountAssetsFrom(const char *fname)
return true; return true;
} }
struct ArchiveState
{
const char* filename;
bool has_extension;
bool other_level_files;
};
static PHYSFS_EnumerateCallbackResult zipCheckCallback(
void* data,
const char* origdir,
const char* filename
) {
struct ArchiveState* state = (struct ArchiveState*) data;
const bool has_extension = endsWith(filename, ".vvvvvv");
UNUSED(origdir);
if (!state->has_extension)
{
state->has_extension = has_extension;
}
if (!state->other_level_files && has_extension)
{
state->other_level_files = SDL_strcmp(
state->filename,
filename
) != 0;
}
if (state->has_extension && state->other_level_files)
{
/* We don't need to check any more files. */
return PHYSFS_ENUM_STOP;
}
return PHYSFS_ENUM_OK;
}
/* For technical reasons, the level file inside a zip named LEVELNAME.zip must
* be named LEVELNAME.vvvvvv, else its custom assets won't work;
* if there are .vvvvvv files other than LEVELNAME.vvvvvv, they would be loaded
* too but they won't load any assets
*
* For user-friendliness, we check this upfront and reject all zips that don't
* conform to this (regardless of them containing assets or not) - otherwise a
* level zip with assets can be played but its assets mysteriously won't work
*/
static bool checkZipStructure(const char* filename)
{
const char* real_dir = PHYSFS_getRealDir(filename);
char base_name[MAX_PATH];
char real_path[MAX_PATH];
char mount_path[MAX_PATH];
char check_path[MAX_PATH];
char random_str[6 + 1];
bool success;
struct ArchiveState zip_state;
if (real_dir == NULL)
{
printf(
"Could not check %s: real directory doesn't exist\n",
filename
);
return false;
}
SDL_snprintf(real_path, sizeof(real_path), "%s/%s", real_dir, filename);
generateBase36(random_str, sizeof(random_str));
SDL_snprintf(mount_path, sizeof(mount_path), ".vvv-mnt-temp-%s/", random_str);
if (!PHYSFS_mount(real_path, mount_path, 1))
{
printf(
"Error mounting and checking %s: %s\n",
filename,
PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())
);
return false;
}
VVV_between(filename, "levels/", base_name, ".zip");
SDL_snprintf(
check_path,
sizeof(check_path),
"%s%s.vvvvvv",
mount_path,
base_name
);
success = PHYSFS_exists(check_path);
SDL_zero(zip_state);
zip_state.filename = check_path;
PHYSFS_enumerate(mount_path, zipCheckCallback, (void*) &zip_state);
/* If no .vvvvvv files in zip, don't print warning. */
if (!success && zip_state.has_extension)
{
/* FIXME: How do we print this for non-terminal users? */
printf(
"%s.zip is not structured correctly! It is missing %s.vvvvvv.\n",
base_name,
base_name
);
}
success &= !zip_state.other_level_files;
/* ...But if other .vvvvvv file(s), do print warning. */
if (zip_state.other_level_files)
{
/* FIXME: How do we print this for non-terminal users? */
printf(
"%s.zip is not structured correctly! It has .vvvvvv file(s) other than %s.vvvvvv.\n",
base_name,
base_name
);
}
if (!PHYSFS_unmount(real_path))
{
printf(
"Could not unmount %s: %s\n",
mount_path,
PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())
);
}
return success;
}
void FILESYSTEM_loadZip(const char* filename) void FILESYSTEM_loadZip(const char* filename)
{ {
PHYSFS_File* zip = PHYSFS_openRead(filename); PHYSFS_File* zip = PHYSFS_openRead(filename);
if (!checkZipStructure(filename))
{
return;
}
if (!PHYSFS_mountHandle(zip, filename, "levels", 1)) if (!PHYSFS_mountHandle(zip, filename, "levels", 1))
{ {
printf( printf(