mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge branch 'mod-info' into 'master'
MODINFO lump See merge request kart-krew-dev/ring-racers-internal!2443
This commit is contained in:
commit
c06fc9cccf
5 changed files with 365 additions and 0 deletions
|
|
@ -164,6 +164,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
|||
k_bans.cpp
|
||||
k_endcam.cpp
|
||||
k_credits.cpp
|
||||
k_modinfo.cpp
|
||||
music.cpp
|
||||
music_manager.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__
|
||||
|
|
@ -993,6 +993,28 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup, const
|
|||
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
|
||||
|
||||
//
|
||||
// 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
|
||||
// Read shaders from file
|
||||
if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
|
||||
|
|
@ -2432,6 +2454,9 @@ int W_VerifyNMUSlumps(const char *filename, boolean exit_on_error)
|
|||
|
||||
{"TLG_", 4}, // Generic button legends
|
||||
|
||||
{"MODINFO", 7}, // Addon metadata
|
||||
{"MODICON", 7}, // Addon icon
|
||||
|
||||
#ifdef HWRENDER
|
||||
{"SHADERS", 7},
|
||||
{"SH_", 3},
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
#include "hardware/hw_data.h"
|
||||
#endif
|
||||
|
||||
#include "k_modinfo.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -130,6 +132,8 @@ struct wadfile_t
|
|||
UINT8 md5sum[16];
|
||||
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue