2022-01-26 03:28:10 +00:00
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import sys
|
|
|
|
from extract_structs import *
|
2022-02-19 21:51:02 +00:00
|
|
|
from extract_object_fields import *
|
2022-01-26 04:28:34 +00:00
|
|
|
from common import *
|
2022-01-26 03:28:10 +00:00
|
|
|
|
|
|
|
in_files = [
|
2024-05-19 13:38:22 +00:00
|
|
|
"include/types.h",
|
|
|
|
"src/game/area.h",
|
|
|
|
"src/game/camera.h",
|
|
|
|
"src/game/characters.h",
|
|
|
|
"src/engine/surface_collision.h",
|
|
|
|
"src/pc/network/network_player.h",
|
|
|
|
"src/pc/djui/djui_hud_utils.h",
|
|
|
|
"src/game/object_helpers.h",
|
|
|
|
"src/game/mario_step.h",
|
|
|
|
"src/pc/lua/utils/smlua_anim_utils.h",
|
|
|
|
"src/pc/lua/utils/smlua_misc_utils.h",
|
|
|
|
"src/pc/lua/utils/smlua_collision_utils.h",
|
|
|
|
"src/pc/lua/utils/smlua_level_utils.h",
|
|
|
|
"src/game/spawn_sound.h",
|
|
|
|
"src/pc/network/network.h",
|
|
|
|
"src/game/hardcoded.h",
|
|
|
|
"src/pc/mods/mod.h",
|
|
|
|
"src/pc/lua/utils/smlua_audio_utils.h",
|
|
|
|
"src/game/paintings.h",
|
|
|
|
"src/pc/djui/djui_types.h",
|
|
|
|
"src/game/first_person_cam.h",
|
|
|
|
"src/game/player_palette.h"
|
2022-01-26 03:28:10 +00:00
|
|
|
]
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
out_filename_c = 'src/pc/lua/smlua_cobject_autogen.c'
|
|
|
|
out_filename_h = 'src/pc/lua/smlua_cobject_autogen.h'
|
|
|
|
out_filename_docs = 'docs/lua/structs.md'
|
|
|
|
out_filename_defs = 'autogen/lua_definitions/structs.lua'
|
2022-01-26 03:28:10 +00:00
|
|
|
|
2022-01-26 04:28:34 +00:00
|
|
|
c_template = """/* THIS FILE IS AUTOGENERATED */
|
|
|
|
/* SHOULD NOT BE MANUALLY CHANGED */
|
|
|
|
$[INCLUDES]
|
2022-02-21 04:23:10 +00:00
|
|
|
#include "include/object_fields.h"
|
|
|
|
|
2022-01-26 03:28:10 +00:00
|
|
|
$[BODY]
|
|
|
|
struct LuaObjectField* smlua_get_object_field_autogen(u16 lot, const char* key) {
|
|
|
|
struct LuaObjectTable* ot = &sLuaObjectAutogenTable[lot - LOT_AUTOGEN_MIN - 1];
|
2022-04-23 01:51:25 +00:00
|
|
|
return smlua_get_object_field_from_ot(ot, key);
|
2022-01-26 03:28:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2022-01-26 04:28:34 +00:00
|
|
|
h_template = """/* THIS FILE IS AUTOGENERATED */
|
|
|
|
/* SHOULD NOT BE MANUALLY CHANGED */
|
|
|
|
#ifndef SMLUA_COBJECT_AUTOGEN_H
|
2022-01-26 03:28:10 +00:00
|
|
|
#define SMLUA_COBJECT_AUTOGEN_H
|
|
|
|
|
|
|
|
$[BODY]
|
|
|
|
struct LuaObjectField* smlua_get_object_field_autogen(u16 lot, const char* key);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
"""
|
|
|
|
|
|
|
|
override_field_names = {
|
|
|
|
}
|
|
|
|
|
|
|
|
override_field_types = {
|
|
|
|
"Surface": { "normal": "Vec3f" },
|
2023-11-11 00:12:21 +00:00
|
|
|
"Object": { "oAnimations": "ObjectAnimPointer*" },
|
2022-01-26 03:28:10 +00:00
|
|
|
}
|
|
|
|
|
2022-03-11 02:23:25 +00:00
|
|
|
override_field_mutable = {
|
Arbitrary shirt, pants, glove colors + settings menu (#145)
* Support for more granular player colors
You can now configure RGB values for shirt, pants, gloves, and shoes.
Due to some limitations, configuring shoes does nothing at the moment.
* Remove paletteIndex and friends
Restructured and filled in some remaining code to account for that.
* Add Edit Palette panel to Player panel
* Change PlayerPalette contents to an enum-indexed array, remove shoes
This gets rid of all the hokey code doing switch cases on the
different parts.
* Fix goof with player model selection box
Should actually have affect now even if a custom palette is being used.
* Fix gap in player color display list commands
The extra space was leftover from when I was trying to get shoes
working. Forgot to clean it up.
* Standardize PlayerParts enum, including for lua constants autogen
* djui_panel_player.c: Properly hook sending palette changes on unpause
Editing the palette and then unpausing should send out the packet to
everyone with the new palette changes (and update the palette preset
selection box), but since we weren't hooking that situation before, it
would stay changed only for you. You would have had to press the Back
button for it to work right.
* Allow Lua mods to continue using `paletteIndex`, `overridePaletteIndex`
This lets mod code like this still work unchanged:
if s.team == 2 then
np.overridePaletteIndex = 7
elseif s.team == 1 then
np.overridePaletteIndex = 15
else
np.overridePaletteIndex = np.paletteIndex
end
It's essentially faked, and would work strangely if the value of either
variable was inspected more closely directly. This should at least
handle the typical use case, though.
Every frame, `overridePaletteIndex` is checked to see if it was modified
from its previous value. If so, `overridePalette` is set to the preset
corresponding to the index. `paletteIndex` contains a special value that
when used to assign to `overridePaletteIndex`, it copies `palette` into
`overridePalette` to restore the real colors, which of course may not
follow the presets at all.
* characters.h: Pack `PlayerPalette` to eliminate size differences between computers
* mario_misc.c: Remove remaining "TODO GAG"
2022-08-07 22:13:19 +00:00
|
|
|
"NetworkPlayer": [
|
|
|
|
"overrideModelIndex",
|
|
|
|
"overridePalette",
|
|
|
|
"overridePaletteIndex",
|
|
|
|
],
|
2023-04-28 23:56:18 +00:00
|
|
|
"Animation": [
|
|
|
|
"values",
|
|
|
|
"index",
|
|
|
|
],
|
2022-03-11 02:23:25 +00:00
|
|
|
}
|
|
|
|
|
2022-04-23 01:44:59 +00:00
|
|
|
override_field_invisible = {
|
2023-02-08 15:51:07 +00:00
|
|
|
"Mod": [ "files" ],
|
|
|
|
"MarioState": [ "visibleToEnemies" ],
|
2023-10-27 23:42:27 +00:00
|
|
|
"NetworkPlayer": [ "gag", "moderator"],
|
2023-05-17 04:49:13 +00:00
|
|
|
"GraphNode": [ "_guard1", "_guard2" ],
|
2024-06-06 07:24:28 +00:00
|
|
|
"Object": [ "firstSurface" ],
|
2022-04-23 01:44:59 +00:00
|
|
|
}
|
|
|
|
|
2023-11-07 07:25:34 +00:00
|
|
|
override_field_deprecated = {
|
|
|
|
"ServerSettings": [ "enableCheats" ],
|
2023-12-12 23:14:37 +00:00
|
|
|
"NetworkPlayer": [ "paletteIndex", "overridePaletteIndex", "overridePaletteIndexLp" ]
|
2023-11-07 07:25:34 +00:00
|
|
|
}
|
|
|
|
|
2022-01-26 04:28:34 +00:00
|
|
|
override_field_immutable = {
|
2024-05-13 23:45:33 +00:00
|
|
|
"MarioState": [ "playerIndex", "controller", "marioObj", "marioBodyState", "statusForCamera", "area", "dialogId" ],
|
2023-05-15 08:15:20 +00:00
|
|
|
"MarioAnimation": [ "animDmaTable" ],
|
2023-05-10 20:25:41 +00:00
|
|
|
"ObjectNode": [ "next", "prev" ],
|
2022-01-26 04:28:34 +00:00
|
|
|
"Character": [ "*" ],
|
2022-02-03 03:24:51 +00:00
|
|
|
"NetworkPlayer": [ "*" ],
|
2022-02-16 06:13:10 +00:00
|
|
|
"TextureInfo": [ "*" ],
|
2024-06-06 07:24:28 +00:00
|
|
|
"Object": ["oSyncID", "coopFlags", "oChainChompSegments", "oWigglerSegments", "oHauntedChairUnk100", "oTTCTreadmillBigSurface", "oTTCTreadmillSmallSurface", "bhvStackIndex", "respawnInfoType", "numSurfaces" ],
|
2022-03-04 02:31:45 +00:00
|
|
|
"GlobalObjectAnimations": [ "*"],
|
2022-03-10 02:59:55 +00:00
|
|
|
"SpawnParticlesInfo": [ "model" ],
|
2022-03-26 04:39:03 +00:00
|
|
|
"MarioBodyState": [ "updateTorsoTime" ],
|
2023-05-15 22:55:16 +00:00
|
|
|
"Area": [ "localAreaTimer", "nextSyncID", "unk04", "objectSpawnInfos", "paintingWarpNodes", "warpNodes" ],
|
2022-04-23 01:44:59 +00:00
|
|
|
"Mod": [ "*" ],
|
|
|
|
"ModFile": [ "*" ],
|
2023-03-27 18:50:32 +00:00
|
|
|
"Painting": [ "id", "imageCount", "textureType", "textureWidth", "textureHeight" ],
|
2023-05-15 08:15:20 +00:00
|
|
|
"SpawnInfo": [ "syncID", "next", "unk18" ],
|
2023-05-10 20:25:41 +00:00
|
|
|
"CustomLevelInfo": [ "next" ],
|
2023-05-15 08:19:10 +00:00
|
|
|
"GraphNode": [ "children", "next", "parent", "prev", "type" ],
|
2023-10-30 05:03:36 +00:00
|
|
|
"GraphNodeObject": [ "angle", "animInfo", "cameraToObject", "node", "pos", "prevAngle", "prevPos", "prevScale", "prevScaleTimestamp", "prevShadowPos", "prevShadowPosTimestamp", "prevThrowMatrix", "prevThrowMatrixTimestamp", "prevTimestamp", "scale", "shadowPos", "sharedChild", "skipInterpolationTimestamp", "throwMatrixPrev", "unk4C", ],
|
2023-05-10 20:25:41 +00:00
|
|
|
"ObjectWarpNode": [ "next "],
|
2023-05-10 22:20:52 +00:00
|
|
|
"Animation": [ "length" ],
|
|
|
|
"AnimationTable": [ "count" ],
|
2023-12-18 19:27:48 +00:00
|
|
|
"Controller": [ "controllerData", "statusData" ],
|
|
|
|
"FirstPersonCamera": [ "enabled" ],
|
2022-01-26 04:28:34 +00:00
|
|
|
}
|
2022-01-26 03:28:10 +00:00
|
|
|
|
2023-05-03 07:54:25 +00:00
|
|
|
override_field_version_excludes = {
|
|
|
|
"oCameraLakituUnk104": "VERSION_JP",
|
|
|
|
"oCoinUnk1B0": "VERSION_JP"
|
|
|
|
}
|
|
|
|
|
2022-03-26 08:08:15 +00:00
|
|
|
override_allowed_structs = {
|
2024-05-19 13:38:22 +00:00
|
|
|
"src/pc/network/network.h": [ "ServerSettings", "NametagsSettings" ],
|
|
|
|
"src/pc/djui/djui_types.h": [ "DjuiColor" ],
|
|
|
|
"src/game/player_palette.h": [ "PlayerPalette" ]
|
2022-03-26 08:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sLuaManuallyDefinedStructs = [{
|
|
|
|
'path': 'n/a',
|
|
|
|
'structs': [
|
|
|
|
'struct Vec3f { float x; float y; float z; }',
|
2022-09-27 02:11:51 +00:00
|
|
|
'struct Vec3s { s16 x; s16 y; s16 z; }',
|
2023-11-21 00:36:25 +00:00
|
|
|
'struct Color { u8 r; u8 g; u8 b; }'
|
2022-03-26 08:08:15 +00:00
|
|
|
]
|
|
|
|
}]
|
2022-01-30 06:47:22 +00:00
|
|
|
|
2022-02-05 21:33:22 +00:00
|
|
|
total_structs = 0
|
|
|
|
total_fields = 0
|
|
|
|
|
2022-01-26 04:28:34 +00:00
|
|
|
############################################################################
|
2022-01-26 03:28:10 +00:00
|
|
|
|
2023-10-28 01:59:00 +00:00
|
|
|
def promote_block(before_block, after_block):
|
|
|
|
inside = 1
|
|
|
|
idx = -1
|
|
|
|
|
|
|
|
for character in after_block:
|
|
|
|
idx += 1
|
|
|
|
if character == '{':
|
|
|
|
inside += 1
|
|
|
|
elif character == '}':
|
|
|
|
inside -= 1
|
|
|
|
if inside <= 0:
|
|
|
|
break
|
|
|
|
if inside == 0 and idx > -1 and after_block[idx+1] == ';':
|
|
|
|
return before_block + after_block[:idx] + after_block[idx+2:]
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
def strip_anonymous_blocks(body):
|
|
|
|
while 'union {' in body:
|
|
|
|
before_union = body.split('union {', 1)[0]
|
|
|
|
after_union = body.split('union {', 1)[-1]
|
|
|
|
promoted = promote_block(before_union, after_union)
|
|
|
|
if promoted == None:
|
|
|
|
break
|
|
|
|
body = promoted
|
|
|
|
|
|
|
|
while 'struct {' in body:
|
|
|
|
before_union = body.split('struct {', 1)[0]
|
|
|
|
after_union = body.split('struct {', 1)[-1]
|
|
|
|
promoted = promote_block(before_union, after_union)
|
|
|
|
if promoted == None:
|
|
|
|
break
|
|
|
|
body = promoted
|
|
|
|
|
|
|
|
return body
|
|
|
|
|
2022-01-26 03:28:10 +00:00
|
|
|
def strip_internal_blocks(body):
|
|
|
|
# strip internal structs/enums/etc
|
|
|
|
tmp = body
|
|
|
|
body = ''
|
|
|
|
inside = 0
|
2023-10-28 01:59:00 +00:00
|
|
|
stripped = ''
|
2022-01-26 03:28:10 +00:00
|
|
|
for character in tmp:
|
|
|
|
if character == '{':
|
|
|
|
body += '{ ... }'
|
|
|
|
inside += 1
|
|
|
|
|
|
|
|
if inside == 0:
|
|
|
|
body += character
|
2023-10-28 01:59:00 +00:00
|
|
|
else:
|
|
|
|
stripped += character
|
2022-01-26 03:28:10 +00:00
|
|
|
|
|
|
|
if character == '}':
|
|
|
|
inside -= 1
|
|
|
|
|
|
|
|
return body
|
|
|
|
|
|
|
|
def identifier_to_caps(identifier):
|
|
|
|
caps = ''
|
|
|
|
was_cap = True
|
|
|
|
for c in identifier:
|
|
|
|
if c >= 'A' and c <= 'Z':
|
|
|
|
if not was_cap:
|
|
|
|
caps += '_'
|
|
|
|
was_cap = True
|
|
|
|
else:
|
|
|
|
was_cap = False
|
|
|
|
caps += c.upper()
|
|
|
|
return caps
|
|
|
|
|
|
|
|
def table_to_string(table):
|
|
|
|
count = 0
|
|
|
|
columns = 0
|
|
|
|
column_width = []
|
|
|
|
for c in table[0]:
|
|
|
|
column_width.append(0)
|
|
|
|
columns += 1
|
|
|
|
|
|
|
|
for row in table:
|
|
|
|
for i in range(columns):
|
2023-05-03 07:54:25 +00:00
|
|
|
if '#' in row[i]:
|
|
|
|
continue
|
2022-01-26 03:28:10 +00:00
|
|
|
if len(row[i]) > column_width[i]:
|
|
|
|
column_width[i] = len(row[i])
|
|
|
|
|
|
|
|
s = ''
|
|
|
|
for row in table:
|
|
|
|
line = ''
|
|
|
|
for i in range(columns):
|
|
|
|
line += row[i].ljust(column_width[i])
|
|
|
|
if '???' in line:
|
|
|
|
line = '//' + line[2:] + ' <--- UNIMPLEMENTED'
|
|
|
|
else:
|
|
|
|
count += 1
|
|
|
|
s += line + '\n'
|
|
|
|
return s, count
|
|
|
|
|
|
|
|
############################################################################
|
|
|
|
|
|
|
|
def parse_struct(struct_str):
|
|
|
|
struct = {}
|
|
|
|
identifier = struct_str.split(' ')[1]
|
|
|
|
struct['identifier'] = identifier
|
|
|
|
|
|
|
|
body = struct_str.split('{', 1)[1].rsplit('}', 1)[0]
|
2023-10-28 01:59:00 +00:00
|
|
|
body = strip_anonymous_blocks(body)
|
2022-01-26 03:28:10 +00:00
|
|
|
body = strip_internal_blocks(body)
|
|
|
|
|
|
|
|
struct['fields'] = []
|
|
|
|
field_strs = body.split(';')
|
|
|
|
for field_str in field_strs:
|
|
|
|
if len(field_str.strip()) == 0:
|
|
|
|
continue
|
|
|
|
|
|
|
|
if '*' in field_str:
|
|
|
|
field_type, field_id = field_str.strip().rsplit('*', 1)
|
|
|
|
field_type = field_type.strip() + '*'
|
|
|
|
else:
|
|
|
|
field_type, field_id = field_str.strip().rsplit(' ', 1)
|
|
|
|
|
|
|
|
if '[' in field_id:
|
|
|
|
array_str = '[' + field_id.split('[', 1)[1]
|
|
|
|
field_id = field_id.split('[', 1)[0]
|
|
|
|
if array_str != '[1]':
|
|
|
|
field_type += ' ' + array_str
|
|
|
|
|
|
|
|
field = {}
|
|
|
|
field['type'] = field_type.strip()
|
|
|
|
field['identifier'] = field_id.strip()
|
|
|
|
field['field_str'] = field_str
|
|
|
|
|
|
|
|
struct['fields'].append(field)
|
|
|
|
|
2022-02-19 21:51:02 +00:00
|
|
|
if identifier == 'Object':
|
|
|
|
struct['fields'] += extract_object_fields()
|
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
struct['fields'] = sorted(struct['fields'], key=lambda d: d['identifier'])
|
|
|
|
|
2022-01-26 03:28:10 +00:00
|
|
|
return struct
|
|
|
|
|
2022-03-26 08:08:15 +00:00
|
|
|
def parse_structs(extracted):
|
2022-01-26 03:28:10 +00:00
|
|
|
structs = []
|
2022-03-26 08:08:15 +00:00
|
|
|
for e in extracted:
|
|
|
|
for struct in e['structs']:
|
|
|
|
parsed = parse_struct(struct)
|
|
|
|
if e['path'] in override_allowed_structs:
|
|
|
|
if parsed['identifier'] not in override_allowed_structs[e['path']]:
|
|
|
|
continue
|
|
|
|
structs.append(parsed)
|
2022-01-26 03:28:10 +00:00
|
|
|
return structs
|
|
|
|
|
|
|
|
############################################################################
|
|
|
|
|
2023-10-30 05:03:36 +00:00
|
|
|
fuzz_from = ""
|
|
|
|
fuzz_to = ""
|
2023-05-15 08:15:20 +00:00
|
|
|
fuzz_structs = ""
|
|
|
|
fuzz_structs_calls = ""
|
|
|
|
fuzz_template_str = None
|
|
|
|
|
|
|
|
def output_fuzz_struct_calls(struct):
|
|
|
|
sid = struct['identifier']
|
|
|
|
global fuzz_template_str
|
|
|
|
if fuzz_template_str == None:
|
|
|
|
with open(fuzz_from) as f:
|
|
|
|
fuzz_template_str = f.read()
|
|
|
|
|
|
|
|
global fuzz_structs_calls
|
|
|
|
|
|
|
|
rnd_call = 'rnd_' + sid + '()'
|
|
|
|
if rnd_call in fuzz_template_str:
|
|
|
|
fuzz_structs_calls += ' function() Fuzz' + sid + '(rnd_' + sid + '()) end,\n'
|
|
|
|
else:
|
|
|
|
fuzz_structs_calls += ' -- function() Fuzz' + sid + '(rnd_' + sid + '()) end,\n'
|
|
|
|
|
2023-05-11 07:19:01 +00:00
|
|
|
def output_fuzz_struct(struct):
|
2023-05-15 08:15:20 +00:00
|
|
|
output_fuzz_struct_calls(struct)
|
2023-05-11 07:19:01 +00:00
|
|
|
sid = struct['identifier']
|
2023-05-15 08:15:20 +00:00
|
|
|
|
|
|
|
s_out = 'function Fuzz' + sid + "(struct)\n"
|
|
|
|
|
|
|
|
s_out += ' local funcs = {\n'
|
2023-05-11 07:19:01 +00:00
|
|
|
for field in struct['fields']:
|
|
|
|
fid, ftype, fimmutable, lvt, lot = get_struct_field_info(struct, field)
|
|
|
|
if fimmutable == 'true':
|
|
|
|
continue
|
|
|
|
if sid in override_field_invisible:
|
|
|
|
if fid in override_field_invisible[sid]:
|
|
|
|
continue
|
2023-05-15 08:15:20 +00:00
|
|
|
|
|
|
|
if '(' in fid or '[' in fid or ']' in fid:
|
|
|
|
continue
|
|
|
|
|
|
|
|
ptype, plink = translate_type_to_lua(ftype)
|
|
|
|
rnd_line = translate_type_to_rnd(ptype)
|
|
|
|
|
|
|
|
s_out += ' function() '
|
|
|
|
|
2023-05-11 07:19:01 +00:00
|
|
|
if lvt == 'LVT_COBJECT':
|
2023-05-15 08:15:20 +00:00
|
|
|
s_out += 'Fuzz' + ftype.replace('struct ', '') + '(struct.' + fid + ')'
|
2023-05-11 07:19:01 +00:00
|
|
|
elif lvt == 'LVT_COBJECT_P':
|
2023-05-15 08:15:20 +00:00
|
|
|
s_out += 'struct.' + fid + ' = ' + rnd_line + ''
|
2023-05-11 07:19:01 +00:00
|
|
|
else:
|
2023-05-15 08:15:20 +00:00
|
|
|
s_out += 'struct.' + fid + ' = ' + rnd_line + ''
|
|
|
|
|
|
|
|
s_out += ' end,\n'
|
|
|
|
s_out += ' }\n'
|
|
|
|
|
|
|
|
s_out += """
|
|
|
|
for i = #funcs, 2, -1 do
|
|
|
|
local j = math.random(i)
|
|
|
|
funcs[i], funcs[j] = funcs[j], funcs[i]
|
|
|
|
end
|
|
|
|
|
|
|
|
for k,v in pairs(funcs) do
|
|
|
|
v()
|
|
|
|
end
|
|
|
|
"""
|
|
|
|
|
|
|
|
s_out += 'end\n\n'
|
|
|
|
|
|
|
|
global fuzz_structs
|
|
|
|
fuzz_structs += s_out
|
|
|
|
|
|
|
|
def output_fuzz_file():
|
|
|
|
global fuzz_structs
|
|
|
|
global fuzz_structs_calls
|
|
|
|
with open(fuzz_from) as f:
|
|
|
|
file_str = f.read()
|
|
|
|
with open(fuzz_to, 'w') as f:
|
|
|
|
f.write(file_str.replace('-- $[STRUCTS]', fuzz_structs).replace('-- $[FUZZ-STRUCTS]', fuzz_structs_calls))
|
2023-05-11 07:19:01 +00:00
|
|
|
|
|
|
|
############################################################################
|
|
|
|
|
2022-01-26 03:28:10 +00:00
|
|
|
sLuaObjectTable = []
|
|
|
|
sLotAutoGenList = []
|
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
def get_struct_field_info(struct, field):
|
2022-01-26 03:28:10 +00:00
|
|
|
sid = struct['identifier']
|
2022-01-30 06:47:22 +00:00
|
|
|
fid = field['identifier']
|
|
|
|
ftype = field['type']
|
2022-01-26 03:28:10 +00:00
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
if sid in override_field_names and fid in override_field_names[sid]:
|
|
|
|
fid = override_field_names[sid][fid]
|
2022-01-26 03:28:10 +00:00
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
if sid in override_field_types and fid in override_field_types[sid]:
|
|
|
|
ftype = override_field_types[sid][fid]
|
2022-01-26 03:28:10 +00:00
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
lvt = translate_type_to_lvt(ftype)
|
|
|
|
lot = translate_type_to_lot(ftype)
|
2022-02-26 02:27:58 +00:00
|
|
|
fimmutable = str(lvt == 'LVT_COBJECT' or 'const ' in ftype).lower()
|
2022-04-09 06:01:41 +00:00
|
|
|
if lvt.startswith('LVT_') and lvt.endswith('_P') and 'OBJECT' not in lvt and 'COLLISION' not in lvt and 'TRAJECTORY' not in lvt:
|
2022-02-26 02:27:58 +00:00
|
|
|
fimmutable = 'true'
|
2022-01-26 03:28:10 +00:00
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
if sid in override_field_immutable:
|
|
|
|
if fid in override_field_immutable[sid] or '*' in override_field_immutable[sid]:
|
|
|
|
fimmutable = 'true'
|
2022-01-26 04:28:34 +00:00
|
|
|
|
2022-03-11 02:23:25 +00:00
|
|
|
if sid in override_field_mutable:
|
|
|
|
if fid in override_field_mutable[sid] or '*' in override_field_mutable[sid]:
|
|
|
|
fimmutable = 'false'
|
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
return fid, ftype, fimmutable, lvt, lot
|
|
|
|
|
|
|
|
def build_struct(struct):
|
2023-05-11 07:19:01 +00:00
|
|
|
# debug print out lua fuzz functions
|
|
|
|
if len(sys.argv) >= 2 and sys.argv[1] == 'fuzz':
|
|
|
|
output_fuzz_struct(struct)
|
2023-05-10 20:25:41 +00:00
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
sid = struct['identifier']
|
|
|
|
|
|
|
|
# build up table and track column width
|
|
|
|
field_table = []
|
|
|
|
for field in struct['fields']:
|
|
|
|
fid, ftype, fimmutable, lvt, lot = get_struct_field_info(struct, field)
|
2022-01-26 04:28:34 +00:00
|
|
|
|
2022-04-23 01:44:59 +00:00
|
|
|
if sid in override_field_invisible:
|
|
|
|
if fid in override_field_invisible[sid]:
|
|
|
|
continue
|
2023-10-27 23:42:27 +00:00
|
|
|
|
2023-05-03 07:54:25 +00:00
|
|
|
version = None
|
2022-04-23 01:44:59 +00:00
|
|
|
|
2022-01-26 03:28:10 +00:00
|
|
|
row = []
|
2023-10-27 23:42:27 +00:00
|
|
|
|
2023-05-03 07:54:25 +00:00
|
|
|
startStr = ''
|
|
|
|
endStr = ' },'
|
|
|
|
if fid in override_field_version_excludes:
|
|
|
|
startStr += '#ifndef ' + override_field_version_excludes[fid] + '\n'
|
|
|
|
endStr += '\n#endif'
|
|
|
|
startStr += ' { '
|
|
|
|
row.append(startStr )
|
2022-01-26 04:28:34 +00:00
|
|
|
row.append('"%s", ' % fid )
|
|
|
|
row.append('%s, ' % lvt )
|
|
|
|
row.append('offsetof(struct %s, %s), ' % (sid, field['identifier']) )
|
|
|
|
row.append('%s, ' % fimmutable )
|
|
|
|
row.append("%s" % lot )
|
2023-05-03 07:54:25 +00:00
|
|
|
row.append(endStr )
|
2022-01-26 03:28:10 +00:00
|
|
|
field_table.append(row)
|
|
|
|
|
|
|
|
field_table_str, field_count = table_to_string(field_table)
|
|
|
|
field_count_define = 'LUA_%s_FIELD_COUNT' % identifier_to_caps(sid)
|
|
|
|
struct_lot = 'LOT_%s' % sid.upper()
|
|
|
|
|
|
|
|
s = "#define %s $[STRUCTFIELDCOUNT]\n" % field_count_define
|
|
|
|
s += "static struct LuaObjectField s%sFields[%s] = {\n" % (sid, field_count_define)
|
|
|
|
s += field_table_str
|
|
|
|
s += '};\n'
|
|
|
|
|
|
|
|
s = s.replace('$[STRUCTFIELDCOUNT]', str(field_count))
|
|
|
|
|
|
|
|
global sLuaObjectTable
|
|
|
|
struct_row = []
|
|
|
|
struct_row.append(' { ' )
|
|
|
|
struct_row.append('%s, ' % struct_lot )
|
|
|
|
struct_row.append('s%sFields, ' % sid )
|
|
|
|
struct_row.append('%s ' % field_count_define )
|
|
|
|
struct_row.append('},' )
|
|
|
|
sLuaObjectTable.append(struct_row)
|
|
|
|
|
|
|
|
global sLotAutoGenList
|
|
|
|
sLotAutoGenList.append(struct_lot)
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
|
|
|
def build_structs(structs):
|
|
|
|
global sLuaObjectTable
|
|
|
|
sLuaObjectTable = []
|
|
|
|
|
|
|
|
global sLotAutoGenList
|
|
|
|
sLotAutoGenList = []
|
|
|
|
|
|
|
|
s = ''
|
|
|
|
for struct in structs:
|
|
|
|
if struct['identifier'] in exclude_structs:
|
|
|
|
continue
|
|
|
|
s += build_struct(struct) + '\n'
|
|
|
|
return s
|
|
|
|
|
|
|
|
def build_body(parsed):
|
|
|
|
built = build_structs(parsed)
|
|
|
|
obj_table_row_built, obj_table_count = table_to_string(sLuaObjectTable)
|
|
|
|
|
|
|
|
obj_table_built = 'struct LuaObjectTable sLuaObjectAutogenTable[LOT_AUTOGEN_MAX - LOT_AUTOGEN_MIN] = {\n'
|
|
|
|
obj_table_built += obj_table_row_built
|
|
|
|
obj_table_built += '};\n'
|
|
|
|
|
|
|
|
return built + obj_table_built
|
|
|
|
|
|
|
|
def build_lot_enum():
|
|
|
|
s = 'enum LuaObjectAutogenType {\n'
|
|
|
|
s += ' LOT_AUTOGEN_MIN = 1000,\n'
|
|
|
|
|
|
|
|
global sLotAutoGenList
|
|
|
|
for lot in sLotAutoGenList:
|
|
|
|
s += ' ' + lot + ',\n'
|
|
|
|
|
|
|
|
s += ' LOT_AUTOGEN_MAX,\n'
|
|
|
|
s += '};\n'
|
|
|
|
return s
|
|
|
|
|
|
|
|
def build_includes():
|
|
|
|
s = '#include "smlua.h"\n'
|
|
|
|
for in_file in in_files:
|
|
|
|
s += '#include "%s"\n' % in_file
|
|
|
|
return s
|
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
|
|
|
|
############################################################################
|
|
|
|
|
|
|
|
def doc_struct_index(structs):
|
|
|
|
s = '# Supported Structs\n'
|
|
|
|
for struct in structs:
|
|
|
|
sid = struct['identifier']
|
|
|
|
s += '- [%s](#%s)\n' % (sid, sid)
|
2022-02-05 21:33:22 +00:00
|
|
|
global total_structs
|
|
|
|
total_structs += 1
|
2022-01-30 06:47:22 +00:00
|
|
|
s += '\n<br />\n\n'
|
|
|
|
return s
|
|
|
|
|
2022-02-19 21:51:02 +00:00
|
|
|
def doc_struct_field(struct, field):
|
|
|
|
fid, ftype, fimmutable, lvt, lot = get_struct_field_info(struct, field)
|
|
|
|
|
2022-04-23 01:44:59 +00:00
|
|
|
sid = struct['identifier']
|
|
|
|
if sid in override_field_invisible:
|
|
|
|
if fid in override_field_invisible[sid]:
|
|
|
|
return ''
|
|
|
|
|
2023-11-07 07:25:34 +00:00
|
|
|
if sid in override_field_deprecated:
|
|
|
|
if fid in override_field_deprecated[sid]:
|
|
|
|
return ''
|
|
|
|
|
2022-02-19 21:51:02 +00:00
|
|
|
if '???' in lvt or '???' in lot:
|
|
|
|
return ''
|
|
|
|
|
2022-03-05 06:22:31 +00:00
|
|
|
ftype, flink = translate_type_to_lua(ftype)
|
2022-02-19 21:51:02 +00:00
|
|
|
|
|
|
|
restrictions = ('', 'read-only')[fimmutable == 'true']
|
|
|
|
|
|
|
|
global total_fields
|
|
|
|
total_fields += 1
|
|
|
|
|
2022-03-05 06:22:31 +00:00
|
|
|
if flink:
|
|
|
|
return '| %s | [%s](%s) | %s |\n' % (fid, ftype, flink, restrictions)
|
2022-02-19 21:51:02 +00:00
|
|
|
|
|
|
|
return '| %s | %s | %s |\n' % (fid, ftype, restrictions)
|
|
|
|
|
|
|
|
|
|
|
|
def doc_struct_object_fields(struct):
|
|
|
|
fields = extract_object_fields()
|
|
|
|
|
|
|
|
s = '\n### Object-Independent Data Fields\n'
|
|
|
|
s += "| Field | Type | Access |\n"
|
|
|
|
s += "| ----- | ---- | ------ |\n"
|
|
|
|
for field in fields:
|
|
|
|
if field['identifier'] == 'oPathedStartWaypoint':
|
|
|
|
s += '\n### Object-Dependent Data Fields\n'
|
|
|
|
s += "| Field | Type | Access |\n"
|
|
|
|
s += "| ----- | ---- | ------ |\n"
|
|
|
|
|
|
|
|
s += doc_struct_field(struct, field)
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
def doc_struct(struct):
|
|
|
|
sid = struct['identifier']
|
|
|
|
s = '## [%s](#%s)\n\n' % (sid, sid)
|
2022-02-04 09:32:19 +00:00
|
|
|
s += "| Field | Type | Access |\n"
|
|
|
|
s += "| ----- | ---- | ------ |\n"
|
2022-01-30 06:47:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
# build doc table
|
|
|
|
field_table = []
|
|
|
|
for field in struct['fields']:
|
2022-02-19 21:51:02 +00:00
|
|
|
if 'object_field' in field and field['object_field'] == True:
|
2022-01-30 06:47:22 +00:00
|
|
|
continue
|
2022-02-19 21:51:02 +00:00
|
|
|
s += doc_struct_field(struct, field)
|
2022-01-30 06:47:22 +00:00
|
|
|
|
2022-02-19 21:51:02 +00:00
|
|
|
if sid == 'Object':
|
|
|
|
s += doc_struct_object_fields(struct)
|
2022-02-05 21:33:22 +00:00
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
s += '\n[:arrow_up_small:](#)\n\n<br />\n'
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
|
|
|
def doc_structs(structs):
|
|
|
|
structs.extend(parse_structs(sLuaManuallyDefinedStructs))
|
2022-02-03 08:43:08 +00:00
|
|
|
structs = sorted(structs, key=lambda d: d['identifier'])
|
2022-01-30 06:47:22 +00:00
|
|
|
|
|
|
|
s = '## [:rewind: Lua Reference](lua.md)\n\n'
|
|
|
|
s += doc_struct_index(structs)
|
|
|
|
for struct in structs:
|
|
|
|
if struct['identifier'] in exclude_structs:
|
|
|
|
continue
|
|
|
|
s += doc_struct(struct) + '\n'
|
|
|
|
|
2022-09-27 02:28:26 +00:00
|
|
|
with open(get_path(out_filename_docs), 'w', newline='\n') as out:
|
2022-03-13 05:28:57 +00:00
|
|
|
out.write(s)
|
|
|
|
|
|
|
|
############################################################################
|
|
|
|
|
|
|
|
def_pointers = []
|
|
|
|
|
|
|
|
def def_struct(struct):
|
|
|
|
sid = struct['identifier']
|
|
|
|
|
|
|
|
stype = translate_to_def(sid)
|
|
|
|
if stype.startswith('Pointer_') and stype not in def_pointers:
|
|
|
|
def_pointers.append(stype)
|
|
|
|
|
|
|
|
s = '\n--- @class %s\n' % stype
|
|
|
|
|
|
|
|
for field in struct['fields']:
|
|
|
|
fid, ftype, fimmutable, lvt, lot = get_struct_field_info(struct, field)
|
|
|
|
|
2022-04-23 01:44:59 +00:00
|
|
|
if sid in override_field_invisible:
|
|
|
|
if fid in override_field_invisible[sid]:
|
|
|
|
continue
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
if '???' in lvt or '???' in lot:
|
|
|
|
continue
|
|
|
|
|
|
|
|
ftype, flink = translate_type_to_lua(ftype)
|
|
|
|
|
|
|
|
ftype = translate_to_def(ftype)
|
|
|
|
if ftype.startswith('Pointer_') and ftype not in def_pointers:
|
|
|
|
def_pointers.append(ftype)
|
|
|
|
|
|
|
|
s += '--- @field public %s %s\n' % (fid, ftype)
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
|
|
|
def def_structs(structs):
|
2023-11-21 00:36:25 +00:00
|
|
|
s = '-- AUTOGENERATED FOR CODE EDITORS --\n'
|
2022-03-13 05:28:57 +00:00
|
|
|
|
|
|
|
for struct in structs:
|
|
|
|
if struct['identifier'] in exclude_structs:
|
|
|
|
continue
|
|
|
|
s += def_struct(struct)
|
|
|
|
|
|
|
|
s += '\n'
|
|
|
|
for def_pointer in def_pointers:
|
|
|
|
s += '--- @class %s\n' % def_pointer
|
|
|
|
|
2022-09-27 02:28:26 +00:00
|
|
|
with open(get_path(out_filename_defs), 'w', newline='\n') as out:
|
2022-01-30 06:47:22 +00:00
|
|
|
out.write(s)
|
|
|
|
|
2022-01-26 03:28:10 +00:00
|
|
|
############################################################################
|
|
|
|
|
|
|
|
def build_files():
|
|
|
|
extracted = []
|
|
|
|
for in_file in in_files:
|
|
|
|
path = get_path(in_file)
|
2022-03-26 08:08:15 +00:00
|
|
|
extracted.append({
|
|
|
|
'path': in_file,
|
|
|
|
'structs': extract_structs(path)
|
|
|
|
})
|
2022-01-26 03:28:10 +00:00
|
|
|
|
|
|
|
parsed = parse_structs(extracted)
|
2022-02-03 08:43:08 +00:00
|
|
|
parsed = sorted(parsed, key=lambda d: d['identifier'])
|
2022-01-30 06:47:22 +00:00
|
|
|
|
2022-01-26 03:28:10 +00:00
|
|
|
built_body = build_body(parsed)
|
|
|
|
built_enum = build_lot_enum()
|
|
|
|
built_include = build_includes()
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
out_c_filename = get_path(out_filename_c)
|
2022-09-27 02:28:26 +00:00
|
|
|
with open(out_c_filename, 'w', newline='\n') as out:
|
2022-01-26 03:28:10 +00:00
|
|
|
out.write(c_template.replace("$[BODY]", built_body).replace('$[INCLUDES]', built_include))
|
|
|
|
|
2022-03-13 05:28:57 +00:00
|
|
|
out_h_filename = get_path(out_filename_h)
|
2022-09-27 02:28:26 +00:00
|
|
|
with open(out_h_filename, 'w', newline='\n') as out:
|
2022-01-26 03:28:10 +00:00
|
|
|
out.write(h_template.replace("$[BODY]", built_enum))
|
|
|
|
|
2022-01-30 06:47:22 +00:00
|
|
|
doc_structs(parsed)
|
2022-03-13 05:28:57 +00:00
|
|
|
def_structs(parsed)
|
2022-01-30 06:47:22 +00:00
|
|
|
|
2023-05-15 08:15:20 +00:00
|
|
|
if len(sys.argv) >= 2 and sys.argv[1] == 'fuzz':
|
|
|
|
output_fuzz_file()
|
|
|
|
|
2022-02-05 21:33:22 +00:00
|
|
|
global total_structs
|
|
|
|
global total_fields
|
|
|
|
|
|
|
|
print("Total structs: " + str(total_structs))
|
|
|
|
print("Total fields: " + str(total_fields))
|
|
|
|
|
2022-01-26 04:28:34 +00:00
|
|
|
############################################################################
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
build_files()
|