mirror of
https://github.com/izzy2lost/Diddy-Kong-Racing.git
synced 2026-06-19 01:16:26 -07:00
Added a hint system to the struct parser
This commit is contained in:
+16
-1
@@ -46,7 +46,8 @@ typedef enum Vehicle {
|
||||
VEHICLE_BUBBLER,
|
||||
VEHICLE_WIZPIG,
|
||||
VEHICLE_ROCKET,
|
||||
|
||||
|
||||
VEHICLE_NO_OVERRIDE = -1, // Only used in Object Maps for the setup point.
|
||||
NUMBER_OF_VEHICLE_TYPES,
|
||||
NUMBER_OF_PLAYER_VEHICLES = 3
|
||||
} Vehicle;
|
||||
@@ -100,6 +101,20 @@ typedef enum ObjectModelType {
|
||||
OBJECT_MODEL_TYPE_MISC
|
||||
} ObjectModelType;
|
||||
|
||||
typedef enum WarpFlag {
|
||||
WARP_FLAG_NORMAL = -1, // Not a boss warp
|
||||
WARP_FLAG_BOSS_1,
|
||||
WARP_FLAG_BOSS_2
|
||||
} WarpFlag;
|
||||
|
||||
typedef enum BalloonType {
|
||||
BALLOON_TYPE_BOOST,
|
||||
BALLOON_TYPE_MISSILE,
|
||||
BALLOON_TYPE_TRAP,
|
||||
BALLOON_TYPE_SHIELD,
|
||||
BALLOON_TYPE_MAGNET
|
||||
} BalloonType;
|
||||
|
||||
typedef enum HorizontalAlignmentFlags {
|
||||
HORZ_ALIGN_LEFT = 0,
|
||||
HORZ_ALIGN_RIGHT = 1,
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
#ifndef _LEVEL_OBJECT_ENTRIES_H_
|
||||
#define _LEVEL_OBJECT_ENTRIES_H_
|
||||
|
||||
/* Size: 8 bytes. Shared across every entry. */
|
||||
|
||||
/**
|
||||
* Only used in the DKR asset tool to help it figure out how to parse certain struct members.
|
||||
* Shouldn't affect the actual game code.
|
||||
* Also: It sucks that C didn't support variadic args in macros till c99. Double parenthesis is needed as a workaround.
|
||||
*/
|
||||
#define Hint(args)
|
||||
|
||||
/* Size: 8 bytes. Shared across every entry. */
|
||||
typedef struct LevelObjectEntryCommon {
|
||||
u8 objectID; // 9-bit object ID to load (uses size's MSB).
|
||||
u8 size; // 7-bit total entry length (MSB is used in object_id).
|
||||
@@ -10,9 +18,9 @@ typedef struct LevelObjectEntryCommon {
|
||||
|
||||
typedef struct LevelObjectEntry_Racer {
|
||||
/* 0x00 */ LevelObjectEntryCommon common;
|
||||
/* 0x08 */ s16 angleZ;
|
||||
/* 0x0A */ s16 angleX;
|
||||
/* 0x0C */ s16 angleY;
|
||||
/* 0x08 */ s16 angleZ;
|
||||
/* 0x0A */ s16 angleX;
|
||||
/* 0x0C */ s16 angleY;
|
||||
/* 0x0E */ s16 playerIndex;
|
||||
} LevelObjectEntry_Racer;
|
||||
|
||||
@@ -42,7 +50,7 @@ typedef struct LevelObjectEntry_Smoke {
|
||||
|
||||
typedef struct LevelObjectEntry_Exit {
|
||||
/* 0x00 */ LevelObjectEntryCommon common;
|
||||
/* 0x08 */ u8 destinationMapId;
|
||||
/* 0x08 */ u8 destinationMapId; Hint((AssetId:ASSET_LEVEL_HEADERS))
|
||||
/* 0x09 */ u8 pad9;
|
||||
|
||||
// Defines the location in overworld the player will spawn at.
|
||||
@@ -56,8 +64,8 @@ typedef struct LevelObjectEntry_Exit {
|
||||
/* 0x0A */ s8 overworldSpawnIndex;
|
||||
|
||||
/* 0x0B */ u8 padB[5];
|
||||
/* 0x10 */ u8 radius; // Activation radius.
|
||||
/* 0x11 */ u8 angleY;
|
||||
/* 0x10 */ u8 radius; Hint((Scale, DivideBy:128)) // Activation radius.
|
||||
/* 0x11 */ u8 angleY; Hint((Angle, DivideBy:64))
|
||||
/* 0x12 */ u8 pad12[5];
|
||||
|
||||
// Defines the location in a hub world the player will spawn at when exiting `destinationMapId`.
|
||||
@@ -72,7 +80,7 @@ typedef struct LevelObjectEntry_Exit {
|
||||
// -1: Doesn't warp to a boss race
|
||||
// 0: Warps to a boss 1 race
|
||||
// 1: Warps to a boss 2 race
|
||||
/* 0x18 */ s8 bossFlag;
|
||||
/* 0x18 */ s8 bossFlag; Hint((Enum:WarpFlag))
|
||||
/* 0x19 */ u8 pad19;
|
||||
} LevelObjectEntry_Exit;
|
||||
|
||||
@@ -112,8 +120,8 @@ typedef struct LevelObjectEntry_SetupPoint {
|
||||
/* 0x00 */ LevelObjectEntryCommon common;
|
||||
/* 0x08 */ u8 unk8;
|
||||
/* 0x09 */ u8 unk9;
|
||||
/* 0x0A */ u8 angleY;
|
||||
/* 0x0B */ s8 vehicle;
|
||||
/* 0x0A */ u8 angleY; Hint((Angle, DivideBy:64))
|
||||
/* 0x0B */ s8 vehicle; Hint((Enum:Vehicle))
|
||||
} LevelObjectEntry_SetupPoint;
|
||||
|
||||
typedef struct LevelObjectEntry_Dino_Whale {
|
||||
@@ -146,8 +154,8 @@ typedef struct LevelObjectEntry_Checkpoint {
|
||||
|
||||
typedef struct LevelObjectEntry_Door {
|
||||
/* 0x00 */ LevelObjectEntryCommon common;
|
||||
/* 0x08 */ u8 closedRotation;
|
||||
/* 0x09 */ u8 openRotation;
|
||||
/* 0x08 */ u8 closedRotation; //Hint((Angle, DivideBy:64))
|
||||
/* 0x09 */ u8 openRotation; //Hint((Angle, DivideBy:64))
|
||||
/* 0x0A */ u8 modelIndex;
|
||||
/* 0x0B */ u8 distanceToOpen;
|
||||
/* 0x0C */ s8 unkC;
|
||||
@@ -182,17 +190,11 @@ typedef struct LevelObjectEntry_AiNode {
|
||||
/* 0x0F */ s8 padF;
|
||||
} LevelObjectEntry_AiNode;
|
||||
|
||||
#define BALLOON_TYPE_BOOST 0
|
||||
#define BALLOON_TYPE_MISSILE 1
|
||||
#define BALLOON_TYPE_TRAP 2
|
||||
#define BALLOON_TYPE_SHIELD 3
|
||||
#define BALLOON_TYPE_MAGNET 4
|
||||
|
||||
typedef struct LevelObjectEntry_WeaponBalloon {
|
||||
/* 0x00 */ LevelObjectEntryCommon common;
|
||||
/* 0x08 */ u8 unk8; // Unused?
|
||||
/* 0x09 */ u8 balloonType;
|
||||
/* 0x0A */ u8 radius; // This is divided by 64 to get actual scale.
|
||||
/* 0x09 */ u8 balloonType; Hint((Enum:BalloonType))
|
||||
/* 0x0A */ u8 radius; Hint((Scale, DivideBy:64)) // This is divided by 64 to get actual scale.
|
||||
/* 0x0B */ u8 padB;
|
||||
} LevelObjectEntry_WeaponBalloon;
|
||||
|
||||
@@ -362,10 +364,10 @@ typedef struct LevelObjectEntry_CharacterFlag {
|
||||
*/
|
||||
typedef struct LevelObjectEntry_Animation {
|
||||
/* 0x00 */ LevelObjectEntryCommon common;
|
||||
/* 0x08 */ u8 z_rotation;
|
||||
/* 0x09 */ u8 x_rotation;
|
||||
/* 0x0A */ u8 y_rotation;
|
||||
/* 0x0B */ u8 scale;
|
||||
/* 0x08 */ u8 z_rotation; //Hint((Angle, DivideBy:256))
|
||||
/* 0x09 */ u8 x_rotation; //Hint((Angle, DivideBy:256))
|
||||
/* 0x0A */ u8 y_rotation; //Hint((Angle, DivideBy:256))
|
||||
/* 0x0B */ u8 scale; Hint((Scale, DivideBy:64))
|
||||
/* 0x0C */ s16 objectIdToSpawn;
|
||||
/* 0x0E */ s16 animationStartDelay;
|
||||
/* 0x10 */ s8 actorIndex;
|
||||
@@ -977,4 +979,6 @@ typedef struct LevelObjectEntry {
|
||||
};
|
||||
} LevelObjectEntry;
|
||||
|
||||
#undef Hint
|
||||
|
||||
#endif
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -471,7 +471,9 @@ void DkrExtract::_generate_obj_behavior_to_entry_json_file() {
|
||||
|
||||
for(int i = 0; i < 128; i++) {
|
||||
std::string symbol;
|
||||
objBehaviors->get_symbol_of_value(i, symbol);
|
||||
DebugHelper::assert_(objBehaviors->get_symbol_of_value(i, symbol),
|
||||
"(DkrExtract::_generate_obj_behavior_to_entry_json_file) Could not get a symbol for the value ", i, " in the ObjectBehaviors enum.");
|
||||
|
||||
|
||||
CStruct *entryStruct = _c_context.get_struct(defaultObjEntriesOrder[i]);
|
||||
DebugHelper::assert_(entryStruct != nullptr,
|
||||
|
||||
@@ -85,10 +85,22 @@ CEnum::CEnum(CContext *context, const std::string &rawCode) : _context(context)
|
||||
if(StringHelper::starts_with(rest, "=")) {
|
||||
std::string enumValue = match.get_group(3).get_text();
|
||||
int parsedEnumValue = _parse_enum_value(enumValue);
|
||||
if(_values.find(parsedEnumValue) == _values.end()) {
|
||||
// New vector in values map.
|
||||
_values[parsedEnumValue] = {};
|
||||
}
|
||||
_members[enumLabel] = parsedEnumValue;
|
||||
// _values map is ordered by which the values were added.
|
||||
_values[parsedEnumValue].push_back(enumLabel);
|
||||
nextValue = parsedEnumValue + 1;
|
||||
continue;
|
||||
}
|
||||
if(_values.find(nextValue) == _values.end()) {
|
||||
// New vector in values map.
|
||||
_values[nextValue] = {};
|
||||
}
|
||||
// _values map is ordered by which the values were added.
|
||||
_values[nextValue].push_back(enumLabel);
|
||||
_members[enumLabel] = nextValue;
|
||||
nextValue++;
|
||||
}
|
||||
@@ -110,7 +122,6 @@ void CEnum::copy_members_to_map(std::unordered_map<std::string, int> &inputMap)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CEnum::get_value_of_member(const std::string &memberKey, int &out) {
|
||||
if(_members.find(memberKey) == _members.end()) {
|
||||
return false;
|
||||
@@ -120,14 +131,12 @@ bool CEnum::get_value_of_member(const std::string &memberKey, int &out) {
|
||||
}
|
||||
|
||||
bool CEnum::get_symbol_of_value(int value, std::string &outSymbol) {
|
||||
for (auto& it : _members) {
|
||||
if (it.second == value) {
|
||||
outSymbol = it.first; // Return the first symbol that matches the value.
|
||||
return true;
|
||||
}
|
||||
if(_values.find(value) == _values.end()) {
|
||||
DebugHelper::warn("(CEnum::get_symbol_of_value) Could not find the value ", value, " for the enum \"", _name, "\"");
|
||||
return false;
|
||||
}
|
||||
// value was not found in the map.
|
||||
return false;
|
||||
outSymbol = _values[value][0]; // Return the first symbol of the value.
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t CEnum::get_member_count() {
|
||||
|
||||
@@ -34,6 +34,9 @@ private:
|
||||
CContext *_context;
|
||||
std::unordered_map<std::string, int> _members;
|
||||
|
||||
// Multiple keys may have the same value, that is the reason for the vector here.
|
||||
std::unordered_map<int, std::vector<std::string>> _values;
|
||||
|
||||
std::string _name;
|
||||
std::string _typedefName;
|
||||
|
||||
|
||||
@@ -1,23 +1,143 @@
|
||||
#include "cStructGltfHelper.h"
|
||||
|
||||
#include "helpers/c/cContext.h"
|
||||
#include "helpers/c/cTypes.h"
|
||||
|
||||
enum StructHintType {
|
||||
NO_HINT,
|
||||
ENUM, // An enum from include/enums.h
|
||||
ASSET_ID,
|
||||
ANGLE, // Converts angle byte into degrees.
|
||||
SCALE // Divide by some number to get a float representation of scale.
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, StructHintType> hintTypes = {
|
||||
{ "Enum", StructHintType::ENUM },
|
||||
{ "Angle", StructHintType::ANGLE }
|
||||
};
|
||||
|
||||
// Gets an enum symbol from a struct member and an integer value.
|
||||
// Assumption: structMember has the "Enum" hint.
|
||||
std::string get_hint_enum_value(CStructEntry *structMember, int value) {
|
||||
std::string enumName = structMember->get_hint_value("Enum");
|
||||
CContext *context = structMember->get_context();
|
||||
CEnum *hintEnum = context->get_enum(enumName);
|
||||
std::string enumValue;
|
||||
|
||||
DebugHelper::assert_(hintEnum->get_symbol_of_value(value, enumValue),
|
||||
"(get_hint_enum_value) Enum ", enumName,
|
||||
" does not have a symbol for the value ", value);
|
||||
|
||||
return enumValue;
|
||||
}
|
||||
|
||||
// Get the integer value from an enum value string.
|
||||
// Assumption: structMember has the "Enum" hint.
|
||||
int get_value_from_hint_enum(CStructEntry *structMember, std::string &enumValue) {
|
||||
std::string enumName = structMember->get_hint_value("Enum");
|
||||
CContext *context = structMember->get_context();
|
||||
CEnum *hintEnum = context->get_enum(enumName);
|
||||
int value;
|
||||
|
||||
DebugHelper::assert_(hintEnum->get_value_of_member(enumValue, value),
|
||||
"(get_value_from_hint_enum) Enum ", enumName,
|
||||
" does not have a value for the symbol ", enumValue);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
double get_hint_angle(CStructEntry *structMember, int value) {
|
||||
std::string divideByStr = structMember->get_hint_value("DivideBy", "64");
|
||||
double divideBy = std::stod(divideByStr);
|
||||
|
||||
double angle = (value / divideBy) * 360.0; // Convert value to degrees
|
||||
|
||||
// *Probably* meant to be negative; convert the unsigned value to a signed value.
|
||||
if((angle > 360.0) && (!CTypes::is_signed_type(structMember->type))) {
|
||||
int64_t maxValuePlusOne = CTypes::get_max_int(structMember->type) + 1;
|
||||
double maxAngle = (maxValuePlusOne / divideBy) * 360.0;
|
||||
angle -= maxAngle; // convert angle to a signed value.
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
int get_value_from_hint_angle(CStructEntry *structMember, double angle) {
|
||||
std::string divideByStr = structMember->get_hint_value("DivideBy", "64");
|
||||
double divideBy = std::stod(divideByStr);
|
||||
|
||||
if((angle < 0.0) && (!CTypes::is_signed_type(structMember->type))) {
|
||||
// convert angle back to a unsigned type.
|
||||
int64_t maxValuePlusOne = CTypes::get_max_int(structMember->type) + 1;
|
||||
double maxAngle = (maxValuePlusOne / divideBy) * 360.0;
|
||||
angle += maxAngle; // convert angle back to unsigned.
|
||||
}
|
||||
|
||||
int out = (int)((angle / 360.0) * divideBy); // Convert from degrees
|
||||
return CTypes::clamp_int(structMember->type, out);
|
||||
}
|
||||
|
||||
void CStructGltfHelper::put_struct_entry_into_gltf_node_extra(CStructEntry *structMember, WriteableGltfFile &gltfFile, int gltfNode, uint8_t *bytes) {
|
||||
CStructEntry::InternalType internalType = structMember->get_internal_type();
|
||||
|
||||
StructHintType hintType = StructHintType::NO_HINT;
|
||||
if(structMember->has_hint()) {
|
||||
hintType = hintTypes[structMember->get_hint_type()];
|
||||
}
|
||||
switch(internalType) {
|
||||
case CStructEntry::InternalType::INTEGER:
|
||||
{
|
||||
int value = structMember->get_integer_from_data(bytes);
|
||||
gltfFile.set_node_extra<int>(gltfNode, structMember->name, value);
|
||||
}
|
||||
switch(hintType) {
|
||||
case StructHintType::NO_HINT:
|
||||
gltfFile.set_node_extra<int>(gltfNode, structMember->name, value);
|
||||
break;
|
||||
case StructHintType::ENUM:
|
||||
{
|
||||
std::string enumValue = get_hint_enum_value(structMember, value);
|
||||
gltfFile.set_node_extra<std::string>(gltfNode, structMember->name, enumValue);
|
||||
break;
|
||||
}
|
||||
case StructHintType::ANGLE:
|
||||
{
|
||||
double angle = get_hint_angle(structMember, value);
|
||||
gltfFile.set_node_extra<double>(gltfNode, structMember->name, angle);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DebugHelper::error("Unimplemented hint type: ", structMember->get_hint_type());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CStructEntry::InternalType::ARRAY_INTEGER:
|
||||
{
|
||||
std::vector<int64_t> values;
|
||||
structMember->get_values_from_data_array<int64_t>(values, bytes);
|
||||
tinygltf::Value::Array arrayOfValues;
|
||||
for(int64_t val : values) {
|
||||
arrayOfValues.push_back(tinygltf::Value((int)val));
|
||||
for(int64_t value : values) {
|
||||
// TODO: This looks pretty similar to the InternalType::INTEGER case. Can I make this a common function?
|
||||
switch(hintType) {
|
||||
case StructHintType::NO_HINT:
|
||||
arrayOfValues.push_back(tinygltf::Value((int)value));
|
||||
break;
|
||||
case StructHintType::ENUM:
|
||||
{
|
||||
std::string enumValue = get_hint_enum_value(structMember, value);
|
||||
arrayOfValues.push_back(tinygltf::Value(enumValue));
|
||||
break;
|
||||
}
|
||||
case StructHintType::ANGLE:
|
||||
{
|
||||
double angle = get_hint_angle(structMember, value);
|
||||
arrayOfValues.push_back(tinygltf::Value(angle));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DebugHelper::error("Unimplemented hint type: ", structMember->get_hint_type());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gltfFile.set_node_extra<tinygltf::Value::Array>(gltfNode, structMember->name, arrayOfValues);
|
||||
}
|
||||
break;
|
||||
@@ -31,37 +151,66 @@ void CStructGltfHelper::put_struct_entry_into_gltf_node_extra(CStructEntry *stru
|
||||
|
||||
void CStructGltfHelper::put_gltf_node_extra_into_struct_entry(CStructEntry *structMember, GltfFile &gltfFile, int gltfNode, uint8_t *bytes) {
|
||||
CStructEntry::InternalType internalType = structMember->get_internal_type();
|
||||
StructHintType hintType = StructHintType::NO_HINT;
|
||||
if(structMember->has_hint()) {
|
||||
hintType = hintTypes[structMember->get_hint_type()];
|
||||
}
|
||||
|
||||
switch(internalType) {
|
||||
case CStructEntry::InternalType::INTEGER:
|
||||
{
|
||||
int value = gltfFile.get_node_extra<int>(gltfNode, structMember->name, 0);
|
||||
int value = 0;
|
||||
switch(hintType) {
|
||||
case StructHintType::NO_HINT:
|
||||
value = gltfFile.get_node_extra<int>(gltfNode, structMember->name, 0);
|
||||
break;
|
||||
case StructHintType::ENUM:
|
||||
{
|
||||
std::string enumValue = gltfFile.get_node_extra<std::string>(gltfNode, structMember->name, "");
|
||||
value = get_value_from_hint_enum(structMember, enumValue);
|
||||
break;
|
||||
}
|
||||
case StructHintType::ANGLE:
|
||||
{
|
||||
double angle = gltfFile.get_node_extra<double>(gltfNode, structMember->name, 0.0);
|
||||
value = get_value_from_hint_angle(structMember, angle);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DebugHelper::error("Unimplemented hint type: ", structMember->get_hint_type());
|
||||
break;
|
||||
}
|
||||
structMember->set_integer_to_data(bytes, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CStructEntry::InternalType::ARRAY_INTEGER:
|
||||
{
|
||||
tinygltf::Value::Array arrayOfValues = gltfFile.get_node_extra<tinygltf::Value::Array>(gltfNode, structMember->name, {});
|
||||
std::vector<int64_t> values;
|
||||
for(tinygltf::Value &val : arrayOfValues) {
|
||||
values.push_back(val.GetNumberAsInt());
|
||||
switch(hintType) {
|
||||
case StructHintType::NO_HINT:
|
||||
values.push_back(val.GetNumberAsInt());
|
||||
break;
|
||||
case StructHintType::ENUM:
|
||||
values.push_back(get_value_from_hint_enum(structMember, val.Get<std::string>()));
|
||||
break;
|
||||
case StructHintType::ANGLE:
|
||||
values.push_back(get_value_from_hint_angle(structMember, val.Get<double>()));
|
||||
break;
|
||||
default:
|
||||
DebugHelper::error("Unimplemented hint type: ", structMember->get_hint_type());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
structMember->set_values_to_data_array<int64_t>(values, bytes);
|
||||
/*
|
||||
std::vector<int64_t> values;
|
||||
structMember->get_values_from_data_array<int64_t>(values, bytes);
|
||||
tinygltf::Value::Array arrayOfValues;
|
||||
for(int64_t val : values) {
|
||||
arrayOfValues.push_back(tinygltf::Value((int)val));
|
||||
}
|
||||
gltfFile.set_node_extra<tinygltf::Value::Array>(gltfNode, structMember->name, arrayOfValues);
|
||||
*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CStructEntry::InternalType::UNKNOWN:
|
||||
break; // Do nothing (for now)
|
||||
default:
|
||||
DebugHelper::error("(CStructGltfHelper::put_struct_entry_into_gltf_node_extra) Type not implemented: ", internalType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "cTypes.h"
|
||||
|
||||
CStructEntry::CStructEntry(CContext *context, CStruct *parent, std::string &type, std::string &pointer, std::string &name,
|
||||
std::string &arrayBrackets) : type(type), pointer(pointer), name(name), arrayBrackets(arrayBrackets),
|
||||
std::string &arrayBrackets, std::string hint) : type(type), pointer(pointer), name(name), arrayBrackets(arrayBrackets),
|
||||
_context(context), _parent(parent) {
|
||||
// Make sure the strings have no whitespace. Makes processing easier.
|
||||
StringHelper::remove_all_whitespace(this->type);
|
||||
@@ -20,6 +20,12 @@ CStructEntry::CStructEntry(CContext *context, CStruct *parent, std::string &type
|
||||
StringHelper::remove_all_whitespace(this->name);
|
||||
StringHelper::remove_all_whitespace(this->arrayBrackets);
|
||||
_calc_array_multiplier();
|
||||
|
||||
if(!hint.empty()) {
|
||||
_generate_hint_map(hint);
|
||||
}
|
||||
|
||||
_isSignedType = CTypes::is_signed_type(type);
|
||||
}
|
||||
|
||||
bool CStructEntry::is_pointer_to_type() {
|
||||
@@ -30,6 +36,67 @@ bool CStructEntry::is_array() {
|
||||
return !arrayBrackets.empty();
|
||||
}
|
||||
|
||||
bool CStructEntry::has_hint() {
|
||||
return !_hintMap.empty();
|
||||
}
|
||||
|
||||
std::string CStructEntry::get_hint_type() {
|
||||
DebugHelper::assert(has_hint(), "(CStructEntry::get_hint_type) ", name, " does not have a hint!");
|
||||
DebugHelper::assert(_hintMap.find("_hintType") != _hintMap.end(), "(CStructEntry::get_hint_type) ", name
|
||||
, " does not have a hint type! (Should not be possible?)");
|
||||
return _hintMap["_hintType"];
|
||||
}
|
||||
|
||||
std::string CStructEntry::get_hint_value(const std::string hintKey) {
|
||||
DebugHelper::assert(has_hint(), "(CStructEntry::get_hint_type) ", name, " does not have a hint!");
|
||||
DebugHelper::assert(_hintMap.find(hintKey) != _hintMap.end(), "(CStructEntry::get_hint_type) ", name
|
||||
, " does not have the hint key: \"", hintKey, "\"");
|
||||
return _hintMap[hintKey];
|
||||
}
|
||||
|
||||
std::string CStructEntry::get_hint_value(const std::string hintKey, std::string defaultValue) {
|
||||
DebugHelper::assert(has_hint(), "(CStructEntry::get_hint_type) ", name, " does not have a hint!");
|
||||
if(_hintMap.find(hintKey) == _hintMap.end()) {
|
||||
return defaultValue;
|
||||
}
|
||||
return _hintMap[hintKey];
|
||||
}
|
||||
|
||||
void CStructEntry::_generate_hint_map(std::string &hint) {
|
||||
std::vector<std::string> hintArgs;
|
||||
StringHelper::split(hint, ',', hintArgs);
|
||||
size_t numArgs = hintArgs.size();
|
||||
|
||||
std::string &firstArg = hintArgs[0];
|
||||
|
||||
// Arg 0 is always the hint type. Might have a colon to indicate the sub-type.
|
||||
if(StringHelper::has(firstArg, ":")) {
|
||||
size_t colonIndex = firstArg.find_first_of(':');
|
||||
std::string hintType = firstArg.substr(0, colonIndex);
|
||||
_hintMap["_hintType"] = hintType;
|
||||
_hintMap[hintType] = firstArg.substr(colonIndex + 1);
|
||||
} else {
|
||||
_hintMap["_hintType"] = firstArg;
|
||||
}
|
||||
|
||||
// Rest of the args
|
||||
for(size_t i = 1; i < numArgs; i++) {
|
||||
std::string &arg = hintArgs[i];
|
||||
|
||||
size_t colonIndex = arg.find_first_of(':');
|
||||
|
||||
if(colonIndex == std::string::npos) {
|
||||
// TODO: Support other hint types besides just a std::string?
|
||||
DebugHelper::error("(CStructEntry::_generate_hint_map) Invalid hint argument: \"", arg, "\"");
|
||||
}
|
||||
|
||||
std::string hintKey = arg.substr(0, colonIndex);
|
||||
DebugHelper::assert(!hintKey.empty(), "(CStructEntry::_generate_hint_map) Hint arg ", i,
|
||||
" for \"", name, "\" does not have a key!");
|
||||
_hintMap[hintKey] = arg.substr(colonIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::string CStructEntry::to_string() {
|
||||
std::stringstream ss;
|
||||
ss << type << " " << pointer << name << arrayBrackets << ";";
|
||||
@@ -151,6 +218,13 @@ int64_t CStructEntry::get_integer_from_data(uint8_t *data, bool isBigEndian) {
|
||||
value |= ((int64_t)data[index]) << (int64_t)(i * 8);
|
||||
}
|
||||
|
||||
if(_isSignedType) {
|
||||
// Convert unsigned value to signed value.
|
||||
int shiftBy = (sizeof(int64_t) - numBytes) * 8;
|
||||
value <<= shiftBy;
|
||||
value >>= shiftBy;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -248,6 +322,10 @@ void CStructEntry::_calc_array_multiplier() {
|
||||
}
|
||||
}
|
||||
|
||||
CContext *CStructEntry::get_context() {
|
||||
return _context;
|
||||
}
|
||||
|
||||
CStruct::CStruct(CContext *context) : _context(context) {
|
||||
}
|
||||
|
||||
@@ -273,6 +351,9 @@ struct CStructMemberParse {
|
||||
bool isStruct; // type is a struct/union
|
||||
int structStart;
|
||||
int structEnd;
|
||||
bool hasHint;
|
||||
int hintStart;
|
||||
int hintEnd;
|
||||
|
||||
void reset() {
|
||||
start = -1;
|
||||
@@ -280,6 +361,9 @@ struct CStructMemberParse {
|
||||
isStruct = false;
|
||||
structStart = -1;
|
||||
structEnd = -1;
|
||||
hasHint = false;
|
||||
hintStart = -1;
|
||||
hintEnd = -1;
|
||||
}
|
||||
|
||||
std::string to_string() {
|
||||
@@ -304,9 +388,19 @@ struct CStructMemberParse {
|
||||
size_t length = structEnd - structStart;
|
||||
return structCode.substr(structStart, length);
|
||||
}
|
||||
|
||||
std::string get_hint_substring(const std::string &structCode) {
|
||||
if(!hasHint) {
|
||||
return "";
|
||||
}
|
||||
size_t length = hintEnd - hintStart;
|
||||
std::string out = structCode.substr(hintStart, length);
|
||||
StringHelper::remove_all_whitespace(out); // Should be no spaces in a hint.
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
const std::string C_STRUCT_END_CHAR = R"(^((?:.|\n)*?([;{])))";
|
||||
const std::string C_STRUCT_END_CHAR = R"(^((?:.|\n)*?([;{]))(?:\s*Hint[(]\s*[(]?([^)\n]*)[)](?:\s*[)])?)?)";
|
||||
|
||||
int get_next_member(const std::string &structCode, int offsetInCode, CStructMemberParse &out) {
|
||||
|
||||
@@ -340,6 +434,17 @@ int get_next_member(const std::string &structCode, int offsetInCode, CStructMemb
|
||||
// Not a struct type, so just return the start/end points.
|
||||
out.start = offsetInCode;
|
||||
out.end = endCharOffset + 1;
|
||||
|
||||
// Check for hint.
|
||||
out.hintStart = match->get_group(3).get_offset();
|
||||
|
||||
if(out.hintStart != -1) {
|
||||
// Hint data was found!
|
||||
out.hintStart += offsetInCode;
|
||||
out.hintEnd = out.hintStart + match->get_group(3).get_text_length();
|
||||
out.hasHint = true;
|
||||
}
|
||||
|
||||
delete match;
|
||||
return out.end;
|
||||
}
|
||||
@@ -358,17 +463,16 @@ int get_next_member(const std::string &structCode, int offsetInCode, CStructMemb
|
||||
|
||||
offsetInCode = out.structEnd;
|
||||
|
||||
// Now that the struct was found, get the rest of the member.
|
||||
//bool success = get_next_member_end_character(structCode, offsetInCode, endCharOffset, endChar, false);
|
||||
//DebugHelper::assert(success, "(get_next_member) Couldn't find an ending colon for the struct! At pos ", offsetInCode);
|
||||
|
||||
delete match;
|
||||
|
||||
// Now that the struct was found, get the rest of the member.
|
||||
endCharOffset = structCode.find_first_of(';', offsetInCode);
|
||||
|
||||
out.start = offsetInCode;
|
||||
out.end = endCharOffset + 1;
|
||||
|
||||
// Don't bother checking hints for the struct. (Maybe at a later date?)
|
||||
|
||||
return out.end;
|
||||
}
|
||||
|
||||
@@ -407,6 +511,9 @@ CStruct::CStruct(CContext *context, const std::string &rawCode) : _context(conte
|
||||
std::string type;
|
||||
std::string rest;
|
||||
|
||||
// Should be empty if there is no hint.
|
||||
std::string hint = parseInfo.get_hint_substring(structCode);
|
||||
|
||||
if(parseInfo.isStruct) {
|
||||
std::string innerStructCode = parseInfo.get_struct_substring(structCode);
|
||||
innerStruct = new CStruct(_context, innerStructCode);
|
||||
@@ -443,7 +550,7 @@ CStruct::CStruct(CContext *context, const std::string &rawCode) : _context(conte
|
||||
delete match; // done with match.
|
||||
}
|
||||
|
||||
_entries.push_back(new CStructEntry(_context, this, type, pointers, name, arrayBrackets));
|
||||
_entries.push_back(new CStructEntry(_context, this, type, pointers, name, arrayBrackets, hint));
|
||||
|
||||
if(parseInfo.isStruct) {
|
||||
_entries.back()->innerStruct = innerStruct;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <any>
|
||||
|
||||
class CContext;
|
||||
@@ -38,10 +39,17 @@ struct CStructEntry {
|
||||
CStruct *innerStruct = nullptr; // Only used in struct/union type.
|
||||
|
||||
CStructEntry(CContext *context, CStruct *parent, std::string &type, std::string &pointer, std::string &name,
|
||||
std::string &arrayBrackets);
|
||||
std::string &arrayBrackets, std::string hint);
|
||||
bool is_pointer_to_type();
|
||||
bool is_array();
|
||||
|
||||
bool has_hint();
|
||||
std::string get_hint_type(); // What type of hint does this member have?
|
||||
std::string get_hint_value(const std::string hintKey);
|
||||
|
||||
// Returns the default value if the key isn't in the hint map.
|
||||
std::string get_hint_value(const std::string hintKey, std::string defaultValue);
|
||||
|
||||
std::string to_string();
|
||||
|
||||
bool is_type_trivial();
|
||||
@@ -74,11 +82,18 @@ struct CStructEntry {
|
||||
int get_offset();
|
||||
void set_offset(int newOffset); // Note: Should only be called by the parent cstruct.
|
||||
|
||||
CContext *get_context();
|
||||
private:
|
||||
// The hint map is specifically used for Object Maps.
|
||||
std::unordered_map<std::string, std::string> _hintMap;
|
||||
void _generate_hint_map(std::string &hint);
|
||||
|
||||
CContext *_context;
|
||||
CStruct *_parent;
|
||||
InternalType _internalType = InternalType::NOT_SET;
|
||||
int _offset = -1; // Is only calculated if get_offset is called.
|
||||
bool _isSignedType;
|
||||
|
||||
|
||||
size_t _arrayMultiplier;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "cTypes.h"
|
||||
|
||||
#include <climits>
|
||||
#include <unordered_map>
|
||||
#include "helpers/debugHelper.h"
|
||||
|
||||
@@ -8,48 +9,66 @@ enum CTypeCategory {
|
||||
CTYPE_FLOAT
|
||||
};
|
||||
|
||||
struct MinMaxInfo {
|
||||
int64_t minInt;
|
||||
uint64_t maxInt;
|
||||
};
|
||||
|
||||
struct CTypeInfo {
|
||||
size_t size;
|
||||
CTypeCategory category;
|
||||
bool isSigned;
|
||||
MinMaxInfo minMaxInfo;
|
||||
};
|
||||
|
||||
// Min/Max ranges
|
||||
const MinMaxInfo mmi_s8 = { INT8_MIN, INT8_MAX };
|
||||
const MinMaxInfo mmi_u8 = { 0, UINT8_MAX };
|
||||
const MinMaxInfo mmi_s16 = { SHRT_MIN, SHRT_MAX };
|
||||
const MinMaxInfo mmi_u16 = { 0, USHRT_MAX };
|
||||
const MinMaxInfo mmi_s32 = { LONG_MIN, LONG_MAX };
|
||||
const MinMaxInfo mmi_u32 = { 0, ULONG_MAX };
|
||||
const MinMaxInfo mmi_s64 = { LLONG_MIN, LLONG_MAX };
|
||||
const MinMaxInfo mmi_u64 = { 0, ULLONG_MAX };
|
||||
|
||||
|
||||
// TODO: Is there a better way to do this?
|
||||
// Note: sizes according to N64 hardware.
|
||||
std::unordered_map<std::string, CTypeInfo> typeInfos = {
|
||||
// 1 byte types
|
||||
{ "char", {1, CTYPE_INTEGER}},
|
||||
{ "signed char", {1, CTYPE_INTEGER}},
|
||||
{ "unsigned char", {1, CTYPE_INTEGER}},
|
||||
{ "s8", {1, CTYPE_INTEGER}},
|
||||
{ "u8", {1, CTYPE_INTEGER}},
|
||||
{ "char", {1, CTYPE_INTEGER, true, mmi_s8}},
|
||||
{ "signed char", {1, CTYPE_INTEGER, true, mmi_s8}},
|
||||
{ "unsigned char", {1, CTYPE_INTEGER, false, mmi_u8}},
|
||||
{ "s8", {1, CTYPE_INTEGER, true, mmi_s8}},
|
||||
{ "u8", {1, CTYPE_INTEGER, false, mmi_u8}},
|
||||
// 2 byte types
|
||||
{ "short", {2, CTYPE_INTEGER}},
|
||||
{ "signed short", {2, CTYPE_INTEGER}},
|
||||
{ "unsigned short", {2, CTYPE_INTEGER}},
|
||||
{ "short int", {2, CTYPE_INTEGER}},
|
||||
{ "signed short int", {2, CTYPE_INTEGER}},
|
||||
{ "unsigned short int", {2, CTYPE_INTEGER}},
|
||||
{ "s16", {2, CTYPE_INTEGER}},
|
||||
{ "u16", {2, CTYPE_INTEGER}},
|
||||
{ "short", {2, CTYPE_INTEGER, true, mmi_s16}},
|
||||
{ "signed short", {2, CTYPE_INTEGER, true, mmi_s16}},
|
||||
{ "unsigned short", {2, CTYPE_INTEGER, false, mmi_u16}},
|
||||
{ "short int", {2, CTYPE_INTEGER, true, mmi_s16}},
|
||||
{ "signed short int", {2, CTYPE_INTEGER, true, mmi_s16}},
|
||||
{ "unsigned short int", {2, CTYPE_INTEGER, false, mmi_u16}},
|
||||
{ "s16", {2, CTYPE_INTEGER, true, mmi_s16}},
|
||||
{ "u16", {2, CTYPE_INTEGER, false, mmi_u16}},
|
||||
// 4 byte types
|
||||
{ "int", {4, CTYPE_INTEGER}},
|
||||
{ "signed int", {4, CTYPE_INTEGER}},
|
||||
{ "unsigned int", {4, CTYPE_INTEGER}},
|
||||
{ "long", {4, CTYPE_INTEGER}}, // long is 32-bits on N64
|
||||
{ "signed long", {4, CTYPE_INTEGER}},
|
||||
{ "unsigned long", {4, CTYPE_INTEGER}},
|
||||
{ "s32", {4, CTYPE_INTEGER}},
|
||||
{ "u32", {4, CTYPE_INTEGER}},
|
||||
{ "float", {4, CTYPE_FLOAT}},
|
||||
{ "f32", {4, CTYPE_FLOAT}},
|
||||
{ "int", {4, CTYPE_INTEGER, true, mmi_s32}},
|
||||
{ "signed int", {4, CTYPE_INTEGER, true, mmi_s32}},
|
||||
{ "unsigned int", {4, CTYPE_INTEGER, false, mmi_u32}},
|
||||
{ "long", {4, CTYPE_INTEGER, true, mmi_s32}}, // long is 32-bits on N64
|
||||
{ "signed long", {4, CTYPE_INTEGER, true, mmi_s32}},
|
||||
{ "unsigned long", {4, CTYPE_INTEGER, false, mmi_u32}},
|
||||
{ "s32", {4, CTYPE_INTEGER, true, mmi_s32}},
|
||||
{ "u32", {4, CTYPE_INTEGER, false, mmi_u32}},
|
||||
{ "float", {4, CTYPE_FLOAT, false}},
|
||||
{ "f32", {4, CTYPE_FLOAT, false}},
|
||||
// 8 byte types
|
||||
{ "long long int", {8, CTYPE_INTEGER}},
|
||||
{ "signed long long int", {8, CTYPE_INTEGER}},
|
||||
{ "unsigned long long int", {8, CTYPE_INTEGER}},
|
||||
{ "s64", {8, CTYPE_INTEGER}},
|
||||
{ "u64", {8, CTYPE_INTEGER}},
|
||||
{ "double", {8, CTYPE_FLOAT}},
|
||||
{ "f64", {8, CTYPE_FLOAT}},
|
||||
{ "long long int", {8, CTYPE_INTEGER, true, mmi_s64}},
|
||||
{ "signed long long int", {8, CTYPE_INTEGER, true, mmi_s64}},
|
||||
{ "unsigned long long int", {8, CTYPE_INTEGER, false, mmi_u64}},
|
||||
{ "s64", {8, CTYPE_INTEGER, true, mmi_s64}},
|
||||
{ "u64", {8, CTYPE_INTEGER, false, mmi_u64}},
|
||||
{ "double", {8, CTYPE_FLOAT, false}},
|
||||
{ "f64", {8, CTYPE_FLOAT, false}},
|
||||
};
|
||||
|
||||
bool CTypes::is_standard_type(const std::string &typeName) {
|
||||
@@ -84,7 +103,41 @@ bool CTypes::is_float_type(const std::string &typeName) {
|
||||
return typeInfos[typeName].category == CTypeCategory::CTYPE_FLOAT;
|
||||
}
|
||||
|
||||
bool CTypes::is_signed_type(const std::string &typeName) {
|
||||
if(!CTypes::is_standard_type(typeName)) {
|
||||
return false;
|
||||
}
|
||||
return typeInfos[typeName].isSigned;
|
||||
}
|
||||
|
||||
int64_t CTypes::get_min_int(const std::string &typeName) {
|
||||
DebugHelper::assert(CTypes::is_standard_type(typeName),
|
||||
"(CTypes::get_min_int) Type ", typeName, " is not a standard type!");
|
||||
return typeInfos[typeName].minMaxInfo.minInt;
|
||||
}
|
||||
|
||||
int64_t CTypes::get_max_int(const std::string &typeName) {
|
||||
DebugHelper::assert(CTypes::is_standard_type(typeName),
|
||||
"(CTypes::get_max_int) Type ", typeName, " is not a standard type!");
|
||||
return typeInfos[typeName].minMaxInfo.maxInt;
|
||||
}
|
||||
|
||||
size_t CTypes::size_of_pointer() {
|
||||
return 4; // Note: Assuming N64 platform.
|
||||
}
|
||||
|
||||
int64_t CTypes::clamp_int(const std::string &typeName, int64_t value) {
|
||||
if(!CTypes::is_standard_type(typeName)) {
|
||||
return value;
|
||||
}
|
||||
if(typeInfos[typeName].category != CTypeCategory::CTYPE_INTEGER) {
|
||||
return value;
|
||||
}
|
||||
if(value < typeInfos[typeName].minMaxInfo.minInt) {
|
||||
return typeInfos[typeName].minMaxInfo.minInt;
|
||||
}
|
||||
if(value > typeInfos[typeName].minMaxInfo.maxInt) {
|
||||
return typeInfos[typeName].minMaxInfo.maxInt;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,15 @@ public:
|
||||
static bool is_integer_type(const std::string &typeName);
|
||||
static bool is_float_type(const std::string &typeName);
|
||||
|
||||
static bool is_signed_type(const std::string &typeName);
|
||||
|
||||
static int64_t get_min_int(const std::string &typeName);
|
||||
static int64_t get_max_int(const std::string &typeName);
|
||||
|
||||
static size_t size_of_pointer();
|
||||
|
||||
static bool does_type_have_an_entry(const std::string &typeName);
|
||||
|
||||
static int64_t clamp_int(const std::string &typeName, int64_t value);
|
||||
|
||||
};
|
||||
@@ -6,7 +6,7 @@
|
||||
// These are functions that I would've expected to be a part of std::string. (Except for a few)
|
||||
class StringHelper {
|
||||
public:
|
||||
static bool has(const std::string &input, const char* substring);
|
||||
static bool has(const std::string &input, const char *substring);
|
||||
|
||||
static void make_lowercase(std::string &input);
|
||||
static void make_uppercase(std::string &input);
|
||||
|
||||
@@ -273,17 +273,24 @@ void test_cstruct_helper_engine(RegexEngine engine, DkrAssetsSettings &settings)
|
||||
|
||||
input = R"(
|
||||
typedef struct LevelObjectEntry_Racer {
|
||||
LevelObjectEntryCommon common;
|
||||
s16 angleZ;
|
||||
s16 angleX;
|
||||
s16 angleY;
|
||||
s16 playerIndex;
|
||||
/* 0x00 */ LevelObjectEntryCommon common;
|
||||
/* 0x08 */ s16 angleZ; Hint((Angle))
|
||||
/* 0x0A */ s16 angleX; Hint(( Angle))
|
||||
/* 0x0C */ s16 angleY; Hint(( Angle ))
|
||||
/* 0x0E */ s16 playerIndex;
|
||||
} LevelObjectEntry_Racer;
|
||||
)";
|
||||
|
||||
CStruct test1(&ctx, input);
|
||||
DebugHelper::assert(!test1.is_trivial(), "Failed test 1.0 (struct was trivial)");
|
||||
DebugHelper::assert(test1.get_byte_size() == 16, "Failed test 1.0 (struct size incorrect, was ", test1.get_byte_size(), " when it should be 16)");
|
||||
DebugHelper::assert(test1.get_byte_size() == 16, "Failed test 1.1 (struct size incorrect, was ", test1.get_byte_size(), " when it should be 16)");
|
||||
DebugHelper::assert(test1.get_entry(1)->get_hint_type() == "Angle", "Failed test 1.2 (Invalid hint type, was \"", test1.get_entry(1)->get_hint_type(),
|
||||
"\" when it should've been \"Angle\")");
|
||||
DebugHelper::assert(test1.get_entry(2)->get_hint_type() == "Angle", "Failed test 1.3 (Invalid hint type, was \"", test1.get_entry(2)->get_hint_type(),
|
||||
"\" when it should've been \"Angle\")");
|
||||
DebugHelper::assert(test1.get_entry(3)->get_hint_type() == "Angle", "Failed test 1.4 (Invalid hint type, was \"", test1.get_entry(3)->get_hint_type(),
|
||||
"\" when it should've been \"Angle\")");
|
||||
|
||||
|
||||
// Clear structs that were created in the previous test.
|
||||
ctx.clear_structs();
|
||||
@@ -331,7 +338,7 @@ void test_cstruct_helper_engine(RegexEngine engine, DkrAssetsSettings &settings)
|
||||
// -1: Doesn't warp to a boss race
|
||||
// 0: Warps to a boss 1 race
|
||||
// 1: Warps to a boss 2 race
|
||||
s8 bossFlag;
|
||||
s8 bossFlag; Hint( ( Enum : WarpFlag ) )
|
||||
|
||||
// Stuff added for testing purposes
|
||||
char *stringTest;
|
||||
@@ -376,6 +383,8 @@ void test_cstruct_helper_engine(RegexEngine engine, DkrAssetsSettings &settings)
|
||||
CStruct test2(&ctx, input);
|
||||
|
||||
DebugHelper::assert(!test2.is_trivial(), "Failed test 2.0 (struct was trivial)");
|
||||
|
||||
DebugHelper::assert(test2.get_entry(9)->get_hint_value("Enum") == "WarpFlag", "Failed test 2.1 ()");
|
||||
// TODO: More tests needed!
|
||||
|
||||
// Clear structs that were created in the previous test.
|
||||
|
||||
Reference in New Issue
Block a user