mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
MODINFO lump
Lets you set a bunch of metadata for your add-on, and a minimum required Ring Racers version (to prevent annoying tech support questions for mod authors). This version value can also be leveraged to add in backwards compatibility behaviors for older mods. Currently the values of the metadata are printed out when a mod is loaded, and aren't used otherwise. Fix pointers for C interface copied strings Apparently this was luck that it worked earlier. I love C++'s strings! And conversely, I hate C's strings.
This commit is contained in:
parent
59eb36eb91
commit
ae59ee0ab9
5 changed files with 362 additions and 0 deletions
|
|
@ -164,6 +164,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
||||||
k_bans.cpp
|
k_bans.cpp
|
||||||
k_endcam.cpp
|
k_endcam.cpp
|
||||||
k_credits.cpp
|
k_credits.cpp
|
||||||
|
k_modinfo.cpp
|
||||||
music.cpp
|
music.cpp
|
||||||
music_manager.cpp
|
music_manager.cpp
|
||||||
sanitize.cpp
|
sanitize.cpp
|
||||||
|
|
|
||||||
210
src/k_modinfo.cpp
Normal file
210
src/k_modinfo.cpp
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
// DR. ROBOTNIK'S RING RACERS
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (C) 2024 by Sally "TehRealSalt" Cochenour
|
||||||
|
// Copyright (C) 2024 by Kart Krew
|
||||||
|
//
|
||||||
|
// This program is free software distributed under the
|
||||||
|
// terms of the GNU General Public License, version 2.
|
||||||
|
// See the 'LICENSE' file for more details.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
/// \file k_modinfo.cpp
|
||||||
|
/// \brief Mod metadata
|
||||||
|
|
||||||
|
#include "k_modinfo.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include "w_wad.h"
|
||||||
|
#include "z_zone.h"
|
||||||
|
|
||||||
|
using nlohmann::json;
|
||||||
|
|
||||||
|
void mod_metadata_t::init_defaults(void)
|
||||||
|
{
|
||||||
|
// Initialize default fields.
|
||||||
|
_game_version = 2; // Defaults to the latest version
|
||||||
|
_game_subversion = 3; // that did not support MODINFO lumps.
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mod_metadata_t::parse_info_json(const char *contents, size_t contents_len)
|
||||||
|
{
|
||||||
|
if (contents_len == 0)
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_ERROR, "could not parse MODINFO; contents are empty\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
json info_obj = json::parse(contents, contents + static_cast<unsigned int>(contents_len));
|
||||||
|
if (info_obj.is_object() == false)
|
||||||
|
{
|
||||||
|
CONS_Alert(CONS_ERROR, "could not parse MODINFO; is not a JSON object\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_name = info_obj.value("name", _name);
|
||||||
|
_author = info_obj.value("author", _author);
|
||||||
|
_description = info_obj.value("description", _description);
|
||||||
|
_info_url = info_obj.value("info_url", _info_url);
|
||||||
|
_version = info_obj.value("version", _version);
|
||||||
|
|
||||||
|
std::string game_version_str = info_obj.value("game_version", "");
|
||||||
|
if (game_version_str.empty() == false)
|
||||||
|
{
|
||||||
|
int game_version_int = 0, game_subversion_int = 0;
|
||||||
|
int result = sscanf(game_version_str.c_str(), "%d.%d", &game_version_int, &game_subversion_int);
|
||||||
|
|
||||||
|
if (result >= 1 && game_version_int >= 0 && game_version_int <= UINT16_MAX)
|
||||||
|
{
|
||||||
|
_game_version = static_cast<UINT16>(game_version_int);
|
||||||
|
_game_subversion = 0; // Just inputting "2" should be the same as "2.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result >= 2 && game_subversion_int >= 0 && game_subversion_int <= UINT16_MAX)
|
||||||
|
{
|
||||||
|
_game_subversion = static_cast<UINT16>(game_subversion_int);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create mod metadata from a WAD file ID.
|
||||||
|
// There is the possibility of allocating mod_metadata_t
|
||||||
|
// for an unloaded file for the addons menu, which should
|
||||||
|
// be a different constructor.
|
||||||
|
mod_metadata_t::mod_metadata_t(size_t wad_id)
|
||||||
|
{
|
||||||
|
init_defaults();
|
||||||
|
|
||||||
|
// Default to the WAD's name
|
||||||
|
_name = wadfiles[wad_id]->filename;
|
||||||
|
|
||||||
|
lumpnum_t lump_index;
|
||||||
|
lumpinfo_t *lump_p = wadfiles[wad_id]->lumpinfo;
|
||||||
|
|
||||||
|
lumpnum_t icon_lump = LUMPERROR;
|
||||||
|
lumpnum_t info_lump = LUMPERROR;
|
||||||
|
|
||||||
|
for (lump_index = 0; lump_index < wadfiles[wad_id]->numlumps; lump_index++, lump_p++)
|
||||||
|
{
|
||||||
|
if (info_lump == LUMPERROR && memcmp(lump_p->name, "MODINFO", 8) == 0)
|
||||||
|
{
|
||||||
|
info_lump = lump_index;
|
||||||
|
}
|
||||||
|
else if (icon_lump == LUMPERROR && memcmp(lump_p->name, "MODICON", 8) == 0)
|
||||||
|
{
|
||||||
|
icon_lump = lump_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info_lump != LUMPERROR && icon_lump != LUMPERROR)
|
||||||
|
{
|
||||||
|
// Found all lumps, exit early.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info_lump != LUMPERROR)
|
||||||
|
{
|
||||||
|
// Parse info JSON
|
||||||
|
size_t info_lump_len = W_LumpLengthPwad(wad_id, info_lump);
|
||||||
|
char *info_contents = static_cast<char *>( W_CacheLumpNumPwad(wad_id, info_lump, PU_STATIC) );
|
||||||
|
|
||||||
|
parse_info_json(info_contents, info_lump_len);
|
||||||
|
|
||||||
|
Z_Free(info_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icon_lump != LUMPERROR)
|
||||||
|
{
|
||||||
|
// We have an icon lump. Cache it!
|
||||||
|
_icon = static_cast<patch_t *>( W_CachePatchNumPwad(wad_id, icon_lump, PU_STATIC) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make incompatibile WADs exit early and unload?
|
||||||
|
// It would require that we pass in the temporary lumpinfo and handle.
|
||||||
|
// It doesn't look hard, just very tedious, and I can't be assed rn.
|
||||||
|
mod_compat_e is_compatible = Compatible();
|
||||||
|
switch (is_compatible)
|
||||||
|
{
|
||||||
|
case MOD_INCOMPATIBLE_FUTURE:
|
||||||
|
{
|
||||||
|
CONS_Alert(
|
||||||
|
CONS_ERROR,
|
||||||
|
"Mod '%s' was designed for a newer version of Ring Racers (mod is for v%d.%d, you have v%d.%d). Update your copy!\n",
|
||||||
|
_name.c_str(),
|
||||||
|
_game_version, _game_subversion,
|
||||||
|
VERSION, SUBVERSION
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MOD_INCOMPATIBLE_PAST:
|
||||||
|
{
|
||||||
|
CONS_Alert(
|
||||||
|
CONS_ERROR,
|
||||||
|
"Mod '%s' was designed for a backwards-incompatible version of Ring Racers (mod is for v%d.%d, you have v%d.%d). This mod will need updated before it can be used.\n",
|
||||||
|
_name.c_str(),
|
||||||
|
_game_version, _game_subversion,
|
||||||
|
VERSION, SUBVERSION
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// Mod is compatible!
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// C interface functions
|
||||||
|
//
|
||||||
|
|
||||||
|
patch_t *ModMetadata_GetIcon(mod_metadata_t *meta)
|
||||||
|
{
|
||||||
|
return meta->icon();
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT16 ModMetadata_GetGameVersion(mod_metadata_t *meta)
|
||||||
|
{
|
||||||
|
return meta->game_version();
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT16 ModMetadata_GetGameSubVersion(mod_metadata_t *meta)
|
||||||
|
{
|
||||||
|
return meta->game_subversion();
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ModMetadata_GetName(mod_metadata_t *meta)
|
||||||
|
{
|
||||||
|
return Z_StrDup( meta->name().c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ModMetadata_GetAuthor(mod_metadata_t *meta)
|
||||||
|
{
|
||||||
|
return Z_StrDup( meta->author().c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ModMetadata_GetDescription(mod_metadata_t *meta)
|
||||||
|
{
|
||||||
|
return Z_StrDup( meta->description().c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ModMetadata_GetInfoURL(mod_metadata_t *meta)
|
||||||
|
{
|
||||||
|
return Z_StrDup( meta->info_url().c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ModMetadata_GetVersion(mod_metadata_t *meta)
|
||||||
|
{
|
||||||
|
return Z_StrDup( meta->version().c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
mod_compat_e ModMetadata_Compatible(mod_metadata_t *meta)
|
||||||
|
{
|
||||||
|
return meta->Compatible();
|
||||||
|
}
|
||||||
125
src/k_modinfo.h
Normal file
125
src/k_modinfo.h
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
// DR. ROBOTNIK'S RING RACERS
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (C) 2024 by Sally "TehRealSalt" Cochenour
|
||||||
|
// Copyright (C) 2024 by Kart Krew
|
||||||
|
//
|
||||||
|
// This program is free software distributed under the
|
||||||
|
// terms of the GNU General Public License, version 2.
|
||||||
|
// See the 'LICENSE' file for more details.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
/// \file k_modinfo.h
|
||||||
|
/// \brief Mod metadata
|
||||||
|
|
||||||
|
#ifndef __K_MODINFO_H__
|
||||||
|
#define __K_MODINFO_H__
|
||||||
|
|
||||||
|
#include "typedef.h"
|
||||||
|
#include "doomtype.h"
|
||||||
|
#include "doomdef.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
MOD_COMPATIBLE = 0, // Fully compatible.
|
||||||
|
MOD_INCOMPATIBLE_FUTURE, // For a future version.
|
||||||
|
MOD_INCOMPATIBLE_PAST // For a way way too old version.
|
||||||
|
} mod_compat_e;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class mod_metadata_t
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
patch_t *_icon;
|
||||||
|
UINT16 _game_version;
|
||||||
|
UINT16 _game_subversion;
|
||||||
|
|
||||||
|
std::string _name;
|
||||||
|
std::string _author;
|
||||||
|
std::string _description;
|
||||||
|
std::string _info_url;
|
||||||
|
std::string _version;
|
||||||
|
|
||||||
|
void init_defaults(void);
|
||||||
|
bool parse_info_json(const char *contents, size_t contents_len);
|
||||||
|
|
||||||
|
public:
|
||||||
|
mod_metadata_t(size_t wad_id);
|
||||||
|
|
||||||
|
patch_t *icon() const { return _icon; }
|
||||||
|
UINT16 game_version() const { return _game_version; }
|
||||||
|
UINT16 game_subversion() const { return _game_subversion; }
|
||||||
|
|
||||||
|
std::string name() const { return _name; }
|
||||||
|
std::string author() const { return _author; }
|
||||||
|
std::string description() const { return _description; }
|
||||||
|
std::string info_url() const { return _info_url; }
|
||||||
|
std::string version() const { return _version; }
|
||||||
|
|
||||||
|
mod_compat_e Compatible() const
|
||||||
|
{
|
||||||
|
#if (VERSION == 0 && SUBVERSION == 0)
|
||||||
|
// DEVELOP builds don't have any relevant version info.
|
||||||
|
// Just disable the checks entirely, you probably
|
||||||
|
// know what you're doing.
|
||||||
|
return MOD_COMPATIBLE;
|
||||||
|
#else
|
||||||
|
if (_game_version > VERSION || (_game_version == VERSION && _game_subversion > SUBVERSION))
|
||||||
|
{
|
||||||
|
// This mod might use features that don't exist
|
||||||
|
// in earlier versions, so tell the user to
|
||||||
|
// update their game to play with this mod.
|
||||||
|
return MOD_INCOMPATIBLE_FUTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_game_version < VERSION)
|
||||||
|
{
|
||||||
|
// Whenever VERSION is incremented, it's because mod
|
||||||
|
// backwards compatibility has been broken beyond
|
||||||
|
// repair. This mod is too old to support!
|
||||||
|
return MOD_INCOMPATIBLE_PAST;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MOD_COMPATIBLE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// C compatibility interface
|
||||||
|
struct mod_metadata_t;
|
||||||
|
typedef struct mod_metadata_t mod_metadata_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
patch_t *ModMetadata_GetIcon(mod_metadata_t *meta);
|
||||||
|
UINT16 ModMetadata_GetGameVersion(mod_metadata_t *meta);
|
||||||
|
UINT16 ModMetadata_GetGameSubVersion(mod_metadata_t *meta);
|
||||||
|
|
||||||
|
char *ModMetadata_GetName(mod_metadata_t *meta);
|
||||||
|
char *ModMetadata_GetAuthor(mod_metadata_t *meta);
|
||||||
|
char *ModMetadata_GetDescription(mod_metadata_t *meta);
|
||||||
|
char *ModMetadata_GetInfoURL(mod_metadata_t *meta);
|
||||||
|
char *ModMetadata_GetVersion(mod_metadata_t *meta);
|
||||||
|
|
||||||
|
mod_compat_e ModMetadata_Compatible(mod_metadata_t *meta);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __K_MODINFO_H__
|
||||||
|
|
@ -919,6 +919,28 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup)
|
||||||
wadfiles[numwadfiles] = wadfile;
|
wadfiles[numwadfiles] = wadfile;
|
||||||
numwadfiles++; // must come BEFORE W_LoadDehackedLumps, so any addfile called by COM_BufInsertText called by Lua doesn't overwrite what we just loaded
|
numwadfiles++; // must come BEFORE W_LoadDehackedLumps, so any addfile called by COM_BufInsertText called by Lua doesn't overwrite what we just loaded
|
||||||
|
|
||||||
|
//
|
||||||
|
// fill out metadata
|
||||||
|
//
|
||||||
|
if (mainfile == false) // main files do not need a MODINFO
|
||||||
|
{
|
||||||
|
wadfile->metadata = new mod_metadata_t(numwadfiles - 1);
|
||||||
|
|
||||||
|
CONS_Printf(
|
||||||
|
"== %s (version %s) - by %s ==\n"
|
||||||
|
"%s\n"
|
||||||
|
"More @ %s\n"
|
||||||
|
"[ DESIGNED FOR RING RACERS %d.%d ]\n",
|
||||||
|
wadfile->metadata->name().c_str(),
|
||||||
|
wadfile->metadata->version().c_str(),
|
||||||
|
wadfile->metadata->author().c_str(),
|
||||||
|
wadfile->metadata->description().c_str(),
|
||||||
|
wadfile->metadata->info_url().c_str(),
|
||||||
|
wadfile->metadata->game_version(),
|
||||||
|
wadfile->metadata->game_subversion()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HWRENDER
|
#ifdef HWRENDER
|
||||||
// Read shaders from file
|
// Read shaders from file
|
||||||
if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
|
if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
#include "hardware/hw_data.h"
|
#include "hardware/hw_data.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "k_modinfo.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -130,6 +132,8 @@ struct wadfile_t
|
||||||
UINT8 md5sum[16];
|
UINT8 md5sum[16];
|
||||||
|
|
||||||
boolean important; // also network - !W_VerifyNMUSlumps
|
boolean important; // also network - !W_VerifyNMUSlumps
|
||||||
|
|
||||||
|
mod_metadata_t *metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define WADFILENUM(lumpnum) (UINT16)((lumpnum)>>16) // wad flumpnum>>16) // wad file number in upper word
|
#define WADFILENUM(lumpnum) (UINT16)((lumpnum)>>16) // wad flumpnum>>16) // wad file number in upper word
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue