Skip to content

Commit b3aa775

Browse files
authored
Merge branch 'Fast-64:main' into main
2 parents 2833ae0 + fe819e2 commit b3aa775

2 files changed

Lines changed: 135 additions & 8 deletions

File tree

fast64_internal/z64/f3d_writer.py

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from ..utility import CData, getGroupIndexFromname, readFile, writeFile
88
from ..f3d.flipbook import flipbook_to_c, flipbook_2d_to_c, flipbook_data_to_c
99
from ..f3d.f3d_material import createF3DMat, F3DMaterial_UpdateLock, update_preset_manual
10-
from .utility import replaceMatchContent, getOOTScale
10+
from .utility import replaceMatchContent, getOOTScale, ootStripComments
1111
from .texture_array import TextureFlipbook
1212

1313
from ..f3d.f3d_writer import (
@@ -334,6 +334,131 @@ def writeTextureArraysExisting2D(data: str, flipbook: TextureFlipbook, flipbookA
334334
return newData
335335

336336

337+
def ootParseScaleLiteral(scaleExpr: str) -> Optional[float]:
338+
scaleExpr = scaleExpr.strip().removesuffix("f")
339+
340+
try:
341+
return float(scaleExpr)
342+
except ValueError:
343+
return None
344+
345+
346+
def ootSplitTopLevel(expr: str) -> list[str]:
347+
entries = []
348+
start = 0
349+
depth = 0
350+
351+
for i, char in enumerate(expr):
352+
if char in "({[":
353+
depth += 1
354+
elif char in ")}]":
355+
depth -= 1
356+
elif char == "," and depth == 0:
357+
entries.append(expr[start:i].strip())
358+
start = i + 1
359+
360+
finalEntry = expr[start:].strip()
361+
if finalEntry != "":
362+
entries.append(finalEntry)
363+
364+
return entries
365+
366+
367+
def ootResolveStructArrayFieldScale(actorData: str, scaleExpr: str) -> Optional[float]:
368+
match = re.fullmatch(r"([A-Za-z_]\w*)\s*\[\s*[^\]]+\s*\]\s*\.\s*([A-Za-z_]\w*)", scaleExpr.strip())
369+
if match is None:
370+
return None
371+
372+
tableName, fieldName = match.groups()
373+
actorData = ootStripComments(actorData)
374+
375+
tableMatch = re.search(
376+
rf"(?:static\s+)?(?P<struct_name>[A-Za-z_]\w*)\s+{re.escape(tableName)}\s*\[\s*\]\s*=\s*\{{",
377+
actorData,
378+
re.DOTALL,
379+
)
380+
if tableMatch is None:
381+
return None
382+
383+
structMatch = re.search(
384+
rf"typedef\s+struct(?:\s+{re.escape(tableMatch.group('struct_name'))})?\s*\{{(?P<body>.*?)\}}\s*{re.escape(tableMatch.group('struct_name'))}\s*;",
385+
actorData,
386+
re.DOTALL,
387+
)
388+
if structMatch is None:
389+
return None
390+
391+
fieldNames = [
392+
fieldMatch.group(1)
393+
for fieldMatch in re.finditer(
394+
r"[A-Za-z_]\w*(?:\s*\*+)?\s+([A-Za-z_]\w*)\s*(?:\[[^\]]+\])?\s*;", structMatch.group("body")
395+
)
396+
]
397+
if fieldName not in fieldNames:
398+
return None
399+
400+
fieldIndex = fieldNames.index(fieldName)
401+
values = set()
402+
depth = 0
403+
entryStart = None
404+
405+
for i in range(tableMatch.end() - 1, len(actorData)):
406+
char = actorData[i]
407+
if char == "{":
408+
if depth == 1:
409+
entryStart = i + 1
410+
depth += 1
411+
elif char == "}":
412+
depth -= 1
413+
if depth == 1 and entryStart is not None:
414+
entryValues = ootSplitTopLevel(actorData[entryStart:i])
415+
if fieldIndex >= len(entryValues):
416+
return None
417+
418+
value = ootParseScaleLiteral(entryValues[fieldIndex])
419+
if value is None:
420+
return None
421+
422+
values.add(value)
423+
if len(values) > 1:
424+
return None
425+
entryStart = None
426+
elif depth == 0:
427+
break
428+
429+
return values.pop() if len(values) == 1 else None
430+
431+
432+
def ootResolveScaleExpression(actorData: str, scaleExpr: str) -> Optional[float]:
433+
scaleExpr = scaleExpr.strip()
434+
literalValue = ootParseScaleLiteral(scaleExpr)
435+
if literalValue is not None:
436+
return literalValue
437+
438+
while scaleExpr.startswith("(") and scaleExpr.endswith(")"):
439+
scaleExpr = scaleExpr[1:-1].strip()
440+
literalValue = ootParseScaleLiteral(scaleExpr)
441+
if literalValue is not None:
442+
return literalValue
443+
444+
depth = 0
445+
for i, char in enumerate(scaleExpr):
446+
if char in "({[":
447+
depth += 1
448+
elif char in ")}]":
449+
depth -= 1
450+
elif depth == 0 and char in "*/":
451+
leftValue = ootResolveScaleExpression(actorData, scaleExpr[:i])
452+
rightValue = ootResolveScaleExpression(actorData, scaleExpr[i + 1 :])
453+
if leftValue is None or rightValue is None:
454+
return None
455+
if char == "*":
456+
return leftValue * rightValue
457+
return None if rightValue == 0 else leftValue / rightValue
458+
459+
return ootResolveStructArrayFieldScale(actorData, scaleExpr)
460+
461+
337462
# Note this does not work well with actors containing multiple "parts". (z_en_honotrap)
338463
def ootReadActorScale(basePath: str, overlayName: str, isLink: bool) -> Optional[float]:
339464
if not isLink:
@@ -344,16 +469,16 @@ def ootReadActorScale(basePath: str, overlayName: str, isLink: bool) -> Optional
344469
chainInitMatch = re.search(r"CHAIN_VEC3F_DIV1000\s*\(\s*scale\s*,\s*(.*?)\s*,", actorData, re.DOTALL)
345470
if chainInitMatch is not None:
346471
scale = chainInitMatch.group(1).strip()
347-
if scale[-1] == "f":
348-
scale = scale[:-1]
349-
return getOOTScale(1 / (float(scale) / 1000))
472+
resolvedScale = ootResolveScaleExpression(actorData, scale)
473+
if resolvedScale is not None:
474+
return getOOTScale(1 / (resolvedScale / 1000))
350475

351476
actorScaleMatch = re.search(r"Actor\_SetScale\s*\(.*?,\s*(.*?)\s*\)", actorData, re.DOTALL)
352477
if actorScaleMatch is not None:
353478
scale = actorScaleMatch.group(1).strip()
354-
if scale[-1] == "f":
355-
scale = scale[:-1]
356-
return getOOTScale(1 / float(scale))
479+
resolvedScale = ootResolveScaleExpression(actorData, scale)
480+
if resolvedScale is not None:
481+
return getOOTScale(1 / resolvedScale)
357482

358483
print("WARNING: auto-detection failed, defaulting to this panel's actor scale property value")
359484
return None

fast64_internal/z64/utility.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,9 @@ def ootGetObjectPath(isCustomExport: bool, exportPath: str, folderName: str, inc
498498
def ootGetObjectHeaderPath(isCustomExport: bool, exportPath: str, folderName: str, include_extracted: bool) -> str:
499499
extracted = bpy.context.scene.fast64.oot.get_extracted_path() if include_extracted else "."
500500
if isCustomExport:
501-
filepath = exportPath
501+
# The custom .c references _WIDTH/_HEIGHT defines from its sibling .h, read that too.
502+
root, ext = os.path.splitext(exportPath)
503+
filepath = root + ".h" if ext == ".c" else exportPath
502504
else:
503505
filepath = os.path.join(
504506
ootGetPath(exportPath, isCustomExport, f"{extracted}/assets/objects/", folderName, False, False),

0 commit comments

Comments
 (0)