mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-12-22 17:49: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:
parent
153a5c4c3a
commit
96660cd235
1 changed files with 139 additions and 0 deletions
|
@ -11,6 +11,7 @@
|
|||
#include "Exit.h"
|
||||
#include "Graphics.h"
|
||||
#include "Maths.h"
|
||||
#include "Unused.h"
|
||||
#include "UtilityClass.h"
|
||||
|
||||
/* These are needed for PLATFORM_* crap */
|
||||
|
@ -309,10 +310,148 @@ static bool FILESYSTEM_mountAssetsFrom(const char *fname)
|
|||
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)
|
||||
{
|
||||
PHYSFS_File* zip = PHYSFS_openRead(filename);
|
||||
|
||||
if (!checkZipStructure(filename))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!PHYSFS_mountHandle(zip, filename, "levels", 1))
|
||||
{
|
||||
printf(
|
||||
|
|
Loading…
Reference in a new issue