Added a hint system to the struct parser

This commit is contained in:
David
2024-04-10 15:02:22 -04:00
parent ecfd38f686
commit d206ef30f3
18 changed files with 473 additions and 99 deletions
+16 -1
View File
@@ -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,
+28 -24
View File
@@ -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
@@ -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;
+83 -30
View File
@@ -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);
+16 -7
View File
@@ -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.