mirror of
https://github.com/zeldaret/oot.git
synced 2025-05-10 11:03:46 +00:00
implement more skeleton-related types, cleanups, fixups
This commit is contained in:
parent
f70a07f8cf
commit
66db26a999
18 changed files with 459 additions and 128 deletions
4
Makefile
4
Makefile
|
@ -790,7 +790,7 @@ setup: venv
|
|||
$(MAKE) -C tools
|
||||
$(PYTHON) tools/decompress_baserom.py $(VERSION)
|
||||
$(PYTHON) tools/extract_baserom.py $(BASEROM_DIR)/baserom-decompressed.z64 $(EXTRACTED_DIR)/baserom -v $(VERSION)
|
||||
$(PYTHON) -m tools.assets.extract -j
|
||||
$(PYTHON) -m tools.assets.extract -j $(EXTRACTED_DIR)/baserom $(EXTRACTED_DIR)
|
||||
$(PYTHON) tools/extract_incbins.py $(EXTRACTED_DIR)/baserom $(EXTRACTED_DIR)/incbin -v $(VERSION)
|
||||
$(PYTHON) tools/extract_text.py $(EXTRACTED_DIR)/baserom $(EXTRACTED_DIR)/text -v $(VERSION)
|
||||
$(PYTHON) tools/extract_audio.py -o $(EXTRACTED_DIR) -v $(VERSION) --read-xml
|
||||
|
@ -985,7 +985,7 @@ $(BUILD_DIR)/src/overlays/%_reloc.o: $(BUILD_DIR)/spec
|
|||
$(AS) $(ASFLAGS) $(@:.o=.s) -o $@
|
||||
|
||||
$(BUILD_DIR)/assets/%.inc.c: assets/%.png
|
||||
false # TODO
|
||||
false # TODO duplicate extracted/ build rules for git-tracked assets/ too
|
||||
|
||||
$(BUILD_DIR)/assets/%.u64.inc.c: $(EXTRACTED_DIR)/assets/%.u64.png
|
||||
$(PYTHON) tools/assets/build_from_png.py $< $(@:.inc.c=.bin)
|
||||
|
|
|
@ -13,7 +13,7 @@ typedef struct BowSlingshotStringData {
|
|||
/* 0x04 */ Vec3f pos;
|
||||
} BowSlingshotStringData; // size = 0x10
|
||||
|
||||
FlexSkeletonHeader* gPlayerSkelHeaders[] = { (void*)&gLinkAdultSkel, (void*)&gLinkChildSkel }; //! FIXME
|
||||
FlexSkeletonHeader* gPlayerSkelHeaders[] = { &gLinkAdultSkel, &gLinkChildSkel };
|
||||
|
||||
s16 sBootData[PLAYER_BOOTS_MAX][17] = {
|
||||
// PLAYER_BOOTS_KOKIRI
|
||||
|
|
|
@ -93,7 +93,7 @@ void EnArrow_Init(Actor* thisx, PlayState* play) {
|
|||
if (this->actor.params <= ARROW_SEED) {
|
||||
|
||||
if (this->actor.params <= ARROW_0E) {
|
||||
SkelAnime_Init(play, &this->skelAnime, (void*)&gArrowSkel, &gArrow2Anim, NULL, NULL, 0);
|
||||
SkelAnime_Init(play, &this->skelAnime, &gArrowSkel, &gArrow2Anim, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
if (this->actor.params <= ARROW_NORMAL) {
|
||||
|
|
|
@ -67,7 +67,7 @@ static AnimationHeader** sAnimationHeaders[] = { sEponaAnimHeaders, sHniAnimHead
|
|||
|
||||
static f32 sPlaybackSpeeds[] = { 2.0f / 3.0f, 2.0f / 3.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f / 3.0f, 2.0f / 3.0f };
|
||||
|
||||
static SkeletonHeader* sSkeletonHeaders[] = { (void*)&gEponaSkel, (void*)&gHorseIngoSkel };
|
||||
static SkeletonHeader* sSkeletonHeaders[] = { &gEponaSkel, &gHorseIngoSkel };
|
||||
|
||||
ActorProfile En_Horse_Profile = {
|
||||
/**/ ACTOR_EN_HORSE,
|
||||
|
|
|
@ -177,7 +177,7 @@ void EnHorseGanon_Init(Actor* thisx, PlayState* play) {
|
|||
this->actor.focus.pos = this->actor.world.pos;
|
||||
this->action = 0;
|
||||
this->actor.focus.pos.y += 70.0f;
|
||||
Skin_Init(play, &this->skin, (void*)&gHorseGanonSkel, &gHorseGanonIdleAnim);
|
||||
Skin_Init(play, &this->skin, &gHorseGanonSkel, &gHorseGanonIdleAnim);
|
||||
this->currentAnimation = 0;
|
||||
Animation_PlayOnce(&this->skin.skelAnime, sAnimations[0]);
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ void EnHorseLinkChild_Init(Actor* thisx, PlayState* play) {
|
|||
this->action = 1;
|
||||
this->actor.focus.pos = this->actor.world.pos;
|
||||
this->actor.focus.pos.y += 70.0f;
|
||||
Skin_Init(play, &this->skin, (void*)&gChildEponaSkel, &gChildEponaGallopingAnim);
|
||||
Skin_Init(play, &this->skin, &gChildEponaSkel, &gChildEponaGallopingAnim);
|
||||
this->animationIdx = 0;
|
||||
Animation_PlayOnce(&this->skin.skelAnime, sAnimations[0]);
|
||||
Collider_InitCylinder(play, &this->bodyCollider);
|
||||
|
|
|
@ -226,7 +226,7 @@ void EnHorseNormal_Init(Actor* thisx, PlayState* play) {
|
|||
return;
|
||||
}
|
||||
this->actor.home.rot.z = this->actor.world.rot.z = this->actor.shape.rot.z = 0;
|
||||
Skin_Init(play, &this->skin, (void*)&gHorseNormalSkel, &gHorseNormalIdleAnim);
|
||||
Skin_Init(play, &this->skin, &gHorseNormalSkel, &gHorseNormalIdleAnim);
|
||||
Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]);
|
||||
if ((this->actor.world.pos.x == -730.0f && this->actor.world.pos.y == 0.0f &&
|
||||
this->actor.world.pos.z == -1100.0f) ||
|
||||
|
@ -240,7 +240,7 @@ void EnHorseNormal_Init(Actor* thisx, PlayState* play) {
|
|||
Actor_Kill(&this->actor);
|
||||
return;
|
||||
} else {
|
||||
Skin_Init(play, &this->skin, (void*)&gHorseNormalSkel, &gHorseNormalIdleAnim);
|
||||
Skin_Init(play, &this->skin, &gHorseNormalSkel, &gHorseNormalIdleAnim);
|
||||
Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]);
|
||||
func_80A6C6B0(this);
|
||||
return;
|
||||
|
@ -248,15 +248,15 @@ void EnHorseNormal_Init(Actor* thisx, PlayState* play) {
|
|||
} else if (play->sceneId == SCENE_GERUDOS_FORTRESS) {
|
||||
if (this->actor.world.pos.x == 3707.0f && this->actor.world.pos.y == 1413.0f &&
|
||||
this->actor.world.pos.z == -665.0f) {
|
||||
Skin_Init(play, &this->skin, (void*)&gHorseNormalSkel, &gHorseNormalIdleAnim);
|
||||
Skin_Init(play, &this->skin, &gHorseNormalSkel, &gHorseNormalIdleAnim);
|
||||
Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]);
|
||||
func_80A6C4CC(this);
|
||||
return;
|
||||
}
|
||||
Skin_Init(play, &this->skin, (void*)&gHorseNormalSkel, &gHorseNormalIdleAnim);
|
||||
Skin_Init(play, &this->skin, &gHorseNormalSkel, &gHorseNormalIdleAnim);
|
||||
Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]);
|
||||
} else {
|
||||
Skin_Init(play, &this->skin, (void*)&gHorseNormalSkel, &gHorseNormalIdleAnim);
|
||||
Skin_Init(play, &this->skin, &gHorseNormalSkel, &gHorseNormalIdleAnim);
|
||||
Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]);
|
||||
}
|
||||
if (PARAMS_GET_NOSHIFT(this->actor.params, 4, 4) == 0x10 && PARAMS_GET_U(this->actor.params, 0, 4) != 0xF) {
|
||||
|
|
|
@ -158,7 +158,7 @@ void EnHorseZelda_Init(Actor* thisx, PlayState* play) {
|
|||
this->actor.focus.pos = this->actor.world.pos;
|
||||
this->action = 0;
|
||||
this->actor.focus.pos.y += 70.0f;
|
||||
Skin_Init(play, &this->skin, (void*)&gHorseZeldaSkel, &gHorseZeldaGallopingAnim);
|
||||
Skin_Init(play, &this->skin, &gHorseZeldaSkel, &gHorseZeldaGallopingAnim);
|
||||
this->animationIndex = 0;
|
||||
Animation_PlayOnce(&this->skin.skelAnime, sAnimationHeaders[0]);
|
||||
Collider_InitCylinder(play, &this->colliderCylinder);
|
||||
|
|
|
@ -102,7 +102,7 @@ void EnTorch2_Init(Actor* thisx, PlayState* play2) {
|
|||
this->currentShield = PLAYER_SHIELD_HYLIAN;
|
||||
this->heldItemAction = this->heldItemId = PLAYER_IA_SWORD_MASTER;
|
||||
Player_SetModelGroup(this, PLAYER_MODELGROUP_SWORD_AND_SHIELD);
|
||||
play->playerInit(this, play, (void*)&gDarkLinkSkel); //! FIXME
|
||||
play->playerInit(this, play, &gDarkLinkSkel);
|
||||
this->actor.naviEnemyId = NAVI_ENEMY_DARK_LINK;
|
||||
this->cylinder.base.acFlags = AC_ON | AC_TYPE_PLAYER;
|
||||
this->meleeWeaponQuads[0].base.atFlags = this->meleeWeaponQuads[1].base.atFlags = AT_ON | AT_TYPE_ENEMY;
|
||||
|
|
|
@ -80,7 +80,7 @@ void EnfHG_Init(Actor* thisx, PlayState* play2) {
|
|||
this->actor.speed = 0.0f;
|
||||
this->actor.focus.pos = this->actor.world.pos;
|
||||
this->actor.focus.pos.y += 70.0f;
|
||||
Skin_Init(play, &this->skin, (void*)&gPhantomHorseSkel, &gPhantomHorseRunningAnim);
|
||||
Skin_Init(play, &this->skin, &gPhantomHorseSkel, &gPhantomHorseRunningAnim);
|
||||
|
||||
if (this->actor.params >= GND_FAKE_BOSS) {
|
||||
EnfHG_SetupApproach(this, play, this->actor.params - GND_FAKE_BOSS);
|
||||
|
|
|
@ -687,6 +687,8 @@ class File:
|
|||
h.writelines(headers_includes)
|
||||
h.write("\n")
|
||||
|
||||
if not self._is_resources_sorted:
|
||||
self.sort_resources()
|
||||
for resource in self._resources:
|
||||
|
||||
if resource.write_c_definition(c):
|
||||
|
@ -720,6 +722,14 @@ class File:
|
|||
+ f"({self.name!r}, data is None={self.data is None}, size={self.size}, {self._resources!r})"
|
||||
)
|
||||
|
||||
def __rich_repr__(self):
|
||||
yield "name", self.name
|
||||
yield "data is None", self.data is None
|
||||
yield "size", self.size
|
||||
yield "resources", self._resources
|
||||
|
||||
__rich_repr__.angular = True
|
||||
|
||||
|
||||
#
|
||||
# resources
|
||||
|
@ -973,7 +983,7 @@ class Resource(abc.ABC):
|
|||
else "..."
|
||||
)
|
||||
),
|
||||
f"file_name={self.file.name!r}",
|
||||
f"file.name={self.file.name!r}",
|
||||
)
|
||||
)
|
||||
+ ")"
|
||||
|
@ -985,7 +995,7 @@ class Resource(abc.ABC):
|
|||
f"0x{self.range_start:08X}-"
|
||||
+ (f"0x{self.range_end:08X}" if self.range_end is not None else "...")
|
||||
)
|
||||
yield "file_name", self.file.name
|
||||
yield "file.name", self.file.name
|
||||
|
||||
__rich_repr__.angular = True
|
||||
|
||||
|
|
|
@ -303,7 +303,10 @@ class CDataArrayResource(CDataResource):
|
|||
return super().try_parse_data(memory_context)
|
||||
|
||||
def get_c_reference(self, resource_offset: int):
|
||||
return self.symbol_name
|
||||
if resource_offset == 0:
|
||||
return self.symbol_name
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
def get_c_expression_length(self, resource_offset: int):
|
||||
if resource_offset == 0:
|
||||
|
|
|
@ -899,7 +899,7 @@ class ColorIndexedTexturesManager:
|
|||
tluts: dict[int, "ColorIndexedTexturesManager.Tlut"]
|
||||
texs: list["ColorIndexedTexturesManager.Tex"]
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, *, HACK_late_SetTextureLUT=False):
|
||||
self.cur_tlut_mode: G_TT = None
|
||||
|
||||
self.cur_tluts_count: int = None
|
||||
|
@ -908,6 +908,11 @@ class ColorIndexedTexturesManager:
|
|||
|
||||
self.ci_states: list[ColorIndexedTexturesManager.CIState] = []
|
||||
|
||||
# Rarely,
|
||||
# gsDPSetTextureLUT comes after gsDPLoadTextureBlock and gsDPLoadTLUT,
|
||||
# instead of before
|
||||
self.HACK_late_SetTextureLUT = HACK_late_SetTextureLUT
|
||||
|
||||
def ci_timg(self, timg, fmt: G_IM_FMT, siz: G_IM_SIZ, width, height, pal):
|
||||
if VERBOSE_ColorIndexedTexturesManager:
|
||||
print(
|
||||
|
@ -920,7 +925,8 @@ class ColorIndexedTexturesManager:
|
|||
pal,
|
||||
)
|
||||
assert fmt == G_IM_FMT.CI
|
||||
assert self.cur_tlut_mode != G_TT.NONE
|
||||
if not self.HACK_late_SetTextureLUT:
|
||||
assert self.cur_tlut_mode != G_TT.NONE
|
||||
|
||||
self.cur_texs.append(
|
||||
ColorIndexedTexturesManager.Tex(timg, fmt, siz, width, height, pal)
|
||||
|
@ -933,7 +939,8 @@ class ColorIndexedTexturesManager:
|
|||
# HACK idx==-1 may be a libgfxd bug?
|
||||
assert count == 256
|
||||
idx = 0
|
||||
assert self.cur_tlut_mode != G_TT.NONE
|
||||
if not self.HACK_late_SetTextureLUT:
|
||||
assert self.cur_tlut_mode != G_TT.NONE
|
||||
if self.cur_tluts_count != count:
|
||||
self.cur_tluts.clear() # TODO ? idk. (at worst it will cause errors)
|
||||
self.cur_tluts_count = count
|
||||
|
@ -943,7 +950,8 @@ class ColorIndexedTexturesManager:
|
|||
if VERBOSE_ColorIndexedTexturesManager:
|
||||
print("ColorIndexedTexturesManager.tlut_mode", tt)
|
||||
if self.cur_tlut_mode != tt:
|
||||
self.cur_tluts.clear() # TODO ? idk. (at worst it will cause errors)
|
||||
if not self.HACK_late_SetTextureLUT:
|
||||
self.cur_tluts.clear() # TODO ? idk. (at worst it will cause errors)
|
||||
self.cur_tlut_mode = tt
|
||||
|
||||
def commit_state(self):
|
||||
|
@ -1101,7 +1109,10 @@ class DListResource(Resource, can_size_be_unknown=True):
|
|||
)
|
||||
return 0
|
||||
|
||||
ci_tex_manager = ColorIndexedTexturesManager()
|
||||
ci_tex_manager = ColorIndexedTexturesManager(
|
||||
# TODO
|
||||
HACK_late_SetTextureLUT=(self.name in {"gEponaHeadLimb_0600AC20_DL"})
|
||||
)
|
||||
|
||||
def timg_cb(timg, fmt, siz, width, height, pal):
|
||||
g_fmt = G_IM_FMT.by_i[fmt]
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
from __future__ import annotations
|
||||
import io
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..extase.memorymap import MemoryContext
|
||||
|
||||
from ..extase import (
|
||||
File,
|
||||
ResourceParseWaiting,
|
||||
)
|
||||
from ..extase.cdata_resources import (
|
||||
CDataResource,
|
||||
CDataArrayResource,
|
||||
CDataExt_Value,
|
||||
CDataExt_Struct,
|
||||
CDataExt_Array,
|
||||
|
@ -40,15 +36,38 @@ class StandardLimbResource(CDataResource):
|
|||
raise ValueError()
|
||||
|
||||
|
||||
class LimbsArrayResource(CDataResource, can_size_be_unknown=True):
|
||||
class LODLimbResource(CDataResource):
|
||||
cdata_ext = CDataExt_Struct(
|
||||
(
|
||||
("jointPos", cdata_ext_Vec3s),
|
||||
("child", CDataExt_Value.u8),
|
||||
("sibling", CDataExt_Value.u8),
|
||||
("dLists", CDataExt_Array(dlist_resources.cdata_ext_gfx_segmented, 2)),
|
||||
)
|
||||
)
|
||||
|
||||
def get_c_declaration_base(self):
|
||||
return f"LodLimb {self.symbol_name}"
|
||||
|
||||
def get_c_reference(self, resource_offset: int):
|
||||
if resource_offset == 0:
|
||||
return f"&{self.symbol_name}"
|
||||
else:
|
||||
raise ValueError()
|
||||
|
||||
|
||||
class LimbsArrayResourceABC(CDataArrayResource):
|
||||
limb_type: type[CDataResource]
|
||||
c_limb_type: str
|
||||
|
||||
def report_limb_element(resource, memory_context: "MemoryContext", v):
|
||||
assert isinstance(v, int)
|
||||
address = v
|
||||
memory_context.report_resource_at_segmented(
|
||||
resource,
|
||||
address,
|
||||
StandardLimbResource,
|
||||
lambda file, offset: StandardLimbResource(
|
||||
resource.limb_type,
|
||||
lambda file, offset: resource.limb_type(
|
||||
file, offset, f"{resource.name}_{address:08X}"
|
||||
),
|
||||
)
|
||||
|
@ -68,51 +87,39 @@ class LimbsArrayResource(CDataResource, can_size_be_unknown=True):
|
|||
.set_write(write_limb_element)
|
||||
)
|
||||
|
||||
def __init__(self, file: File, range_start: int, name: str):
|
||||
super().__init__(file, range_start, name)
|
||||
self.length = None
|
||||
|
||||
def try_parse_data(self, memory_context: "MemoryContext"):
|
||||
if self.length is not None:
|
||||
self.cdata_ext = CDataExt_Array(self.elem_cdata_ext, self.length)
|
||||
self.range_end = self.range_start + self.cdata_ext.size
|
||||
return super().try_parse_data(memory_context)
|
||||
else:
|
||||
raise ResourceParseWaiting(waiting_for=["self.length"])
|
||||
|
||||
def get_c_declaration_base(self):
|
||||
return f"void* {self.symbol_name}[]"
|
||||
|
||||
def get_c_reference(self, resource_offset: int):
|
||||
if resource_offset == 0:
|
||||
return f"{self.symbol_name}"
|
||||
else:
|
||||
raise ValueError()
|
||||
|
||||
def get_c_expression_length(self, resource_offset: int):
|
||||
if resource_offset == 0:
|
||||
return f"ARRAY_COUNT({self.symbol_name})"
|
||||
else:
|
||||
raise ValueError()
|
||||
return f"{self.c_limb_type}* {self.symbol_name}[]"
|
||||
|
||||
|
||||
class SkeletonNormalResource(CDataResource):
|
||||
class StandardLimbsArrayResource(LimbsArrayResourceABC):
|
||||
limb_type = StandardLimbResource
|
||||
c_limb_type = "StandardLimb"
|
||||
|
||||
|
||||
class LODLimbsArrayResource(LimbsArrayResourceABC):
|
||||
limb_type = LODLimbResource
|
||||
c_limb_type = "LodLimb"
|
||||
|
||||
|
||||
class SkeletonResourceABC(CDataResource):
|
||||
limbs_array_type: type[LimbsArrayResourceABC]
|
||||
|
||||
def report_segment(resource, memory_context: "MemoryContext", v):
|
||||
assert isinstance(v, int)
|
||||
address = v
|
||||
resource_limbs = memory_context.report_resource_at_segmented(
|
||||
resource,
|
||||
address,
|
||||
LimbsArrayResource,
|
||||
lambda file, offset: LimbsArrayResource(
|
||||
resource.limbs_array_type,
|
||||
lambda file, offset: resource.limbs_array_type(
|
||||
file,
|
||||
offset,
|
||||
f"{resource.name}_{address:08X}_Limbs",
|
||||
),
|
||||
)
|
||||
resource_limbs.length = resource.get_skeleton_header_cdata_unpacked()[
|
||||
"limbCount"
|
||||
]
|
||||
resource_limbs.set_length(
|
||||
resource.get_skeleton_header_cdata_unpacked()["limbCount"]
|
||||
)
|
||||
|
||||
def write_segment(
|
||||
resource, memory_context: "MemoryContext", v, f: io.TextIOBase, line_prefix
|
||||
|
@ -162,18 +169,32 @@ class SkeletonNormalResource(CDataResource):
|
|||
return f"SkeletonHeader {self.symbol_name}"
|
||||
|
||||
|
||||
class SkeletonFlexResource(CDataResource):
|
||||
cdata_ext = CDataExt_Struct(
|
||||
(
|
||||
class SkeletonNormalResource(SkeletonResourceABC):
|
||||
limbs_array_type = StandardLimbsArrayResource
|
||||
|
||||
|
||||
class SkeletonNormalLODResource(SkeletonResourceABC):
|
||||
limbs_array_type = LODLimbsArrayResource
|
||||
|
||||
|
||||
class SkeletonFlexResourceABC(CDataResource):
|
||||
skeleton_type: type[SkeletonResourceABC]
|
||||
|
||||
# For SkeletonResourceABC.report_segment
|
||||
@property
|
||||
def limbs_array_type(self):
|
||||
return self.skeleton_type.limbs_array_type
|
||||
|
||||
def __init__(self, file, range_start, name):
|
||||
self.cdata_ext = CDataExt_Struct(
|
||||
(
|
||||
"sh",
|
||||
SkeletonNormalResource.cdata_ext,
|
||||
), # TODO FIXME this is bad, it ends up using StandardLimb s (or is it fine?)
|
||||
("dListCount", CDataExt_Value.u8),
|
||||
("pad9", CDataExt_Value.pad8),
|
||||
("pad10", CDataExt_Value.pad16),
|
||||
("sh", self.skeleton_type.cdata_ext),
|
||||
("dListCount", CDataExt_Value.u8),
|
||||
("pad9", CDataExt_Value.pad8),
|
||||
("pad10", CDataExt_Value.pad16),
|
||||
)
|
||||
)
|
||||
)
|
||||
super().__init__(file, range_start, name)
|
||||
|
||||
def get_skeleton_header_cdata_unpacked(self):
|
||||
return self.cdata_unpacked["sh"]
|
||||
|
@ -186,3 +207,11 @@ class SkeletonFlexResource(CDataResource):
|
|||
|
||||
def get_c_declaration_base(self):
|
||||
return f"FlexSkeletonHeader {self.symbol_name}"
|
||||
|
||||
|
||||
class SkeletonFlexResource(SkeletonFlexResourceABC):
|
||||
skeleton_type = SkeletonNormalResource
|
||||
|
||||
|
||||
class SkeletonFlexLODResource(SkeletonFlexResourceABC):
|
||||
skeleton_type = SkeletonNormalLODResource
|
||||
|
|
244
tools/assets/extract/extase_oot64/skeleton_skin_resources.py
Normal file
244
tools/assets/extract/extase_oot64/skeleton_skin_resources.py
Normal file
|
@ -0,0 +1,244 @@
|
|||
import io
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from ..oot64_data.misc_ids import SKIN_LIMB_TYPES
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..extase.memorymap import MemoryContext
|
||||
|
||||
from ..extase.cdata_resources import (
|
||||
CDataResource,
|
||||
CDataArrayResource,
|
||||
CDataExt_Value,
|
||||
CDataExt_Struct,
|
||||
cdata_ext_Vec3s,
|
||||
)
|
||||
|
||||
from . import dlist_resources
|
||||
from . import skeleton_resources
|
||||
|
||||
|
||||
class SkinVertexArrayResource(CDataArrayResource):
|
||||
elem_cdata_ext = CDataExt_Struct(
|
||||
(
|
||||
("index", CDataExt_Value.u16),
|
||||
("s", CDataExt_Value.s16),
|
||||
("t", CDataExt_Value.s16),
|
||||
("normX", CDataExt_Value.s8),
|
||||
("normY", CDataExt_Value.s8),
|
||||
("normZ", CDataExt_Value.s8),
|
||||
("alpha", CDataExt_Value.u8),
|
||||
)
|
||||
)
|
||||
|
||||
def get_c_declaration_base(self):
|
||||
return f"SkinVertex {self.symbol_name}[]"
|
||||
|
||||
|
||||
class SkinTransformationArrayResource(CDataArrayResource):
|
||||
elem_cdata_ext = CDataExt_Struct(
|
||||
(
|
||||
("limbIndex", CDataExt_Value.u8),
|
||||
("pad1", CDataExt_Value.pad8),
|
||||
("x", CDataExt_Value.s16),
|
||||
("y", CDataExt_Value.s16),
|
||||
("z", CDataExt_Value.s16),
|
||||
("scale", CDataExt_Value.u8),
|
||||
("pad9", CDataExt_Value.pad8),
|
||||
)
|
||||
)
|
||||
|
||||
def get_c_declaration_base(self):
|
||||
return f"SkinTransformation {self.symbol_name}[]"
|
||||
|
||||
|
||||
class SkinLimbModifArrayResource(CDataArrayResource):
|
||||
def report_elem(resource, memory_context: "MemoryContext", v):
|
||||
assert isinstance(v, dict)
|
||||
|
||||
address = v["skinVertices"]
|
||||
assert isinstance(address, int)
|
||||
skin_vertices_res = memory_context.report_resource_at_segmented(
|
||||
resource,
|
||||
address,
|
||||
SkinVertexArrayResource,
|
||||
lambda file, offset: SkinVertexArrayResource(
|
||||
file,
|
||||
offset,
|
||||
f"{resource.name}_{offset:08X}_SkinVertices",
|
||||
),
|
||||
)
|
||||
skin_vertices_res.set_length(v["vtxCount"])
|
||||
|
||||
address = v["limbTransformations"]
|
||||
assert isinstance(address, int)
|
||||
skin_vertices_res = memory_context.report_resource_at_segmented(
|
||||
resource,
|
||||
address,
|
||||
SkinTransformationArrayResource,
|
||||
lambda file, offset: SkinTransformationArrayResource(
|
||||
file,
|
||||
offset,
|
||||
f"{resource.name}_{offset:08X}_SkinTransforms",
|
||||
),
|
||||
)
|
||||
skin_vertices_res.set_length(v["transformCount"])
|
||||
|
||||
def write_skinVertices(
|
||||
resource, memory_context: "MemoryContext", v, f: io.TextIOBase, line_prefix
|
||||
):
|
||||
assert isinstance(v, int)
|
||||
address = v
|
||||
f.write(line_prefix)
|
||||
f.write(memory_context.get_c_reference_at_segmented(address))
|
||||
return True
|
||||
|
||||
def write_limbTransformations(
|
||||
resource, memory_context: "MemoryContext", v, f: io.TextIOBase, line_prefix
|
||||
):
|
||||
assert isinstance(v, int)
|
||||
address = v
|
||||
f.write(line_prefix)
|
||||
f.write(memory_context.get_c_reference_at_segmented(address))
|
||||
return True
|
||||
|
||||
elem_cdata_ext = CDataExt_Struct(
|
||||
(
|
||||
("vtxCount", CDataExt_Value.u16),
|
||||
("transformCount", CDataExt_Value.u16),
|
||||
("unk_4", CDataExt_Value.u16),
|
||||
("pad6", CDataExt_Value.pad16),
|
||||
(
|
||||
"skinVertices",
|
||||
(CDataExt_Value("I").set_write(write_skinVertices)),
|
||||
), # SkinVertex*
|
||||
(
|
||||
"limbTransformations",
|
||||
(CDataExt_Value("I").set_write(write_limbTransformations)),
|
||||
), # SkinTransformation*
|
||||
)
|
||||
).set_report(report_elem)
|
||||
|
||||
def get_c_declaration_base(self):
|
||||
return f"SkinLimbModif {self.symbol_name}[]"
|
||||
|
||||
|
||||
class SkinAnimatedLimbDataResource(CDataResource):
|
||||
def report_limbModifications(resource, memory_context: "MemoryContext", v):
|
||||
assert isinstance(v, int)
|
||||
address = v
|
||||
skin_vertices_res = memory_context.report_resource_at_segmented(
|
||||
resource,
|
||||
address,
|
||||
SkinLimbModifArrayResource,
|
||||
lambda file, offset: SkinLimbModifArrayResource(
|
||||
file,
|
||||
offset,
|
||||
f"{resource.name}_{offset:08X}_SkinLimbModifs",
|
||||
),
|
||||
)
|
||||
skin_vertices_res.set_length(resource.cdata_unpacked["limbModifCount"])
|
||||
|
||||
def write_limbModifications(
|
||||
resource, memory_context: "MemoryContext", v, f: io.TextIOBase, line_prefix
|
||||
):
|
||||
assert isinstance(v, int)
|
||||
address = v
|
||||
f.write(line_prefix)
|
||||
f.write(memory_context.get_c_reference_at_segmented(address))
|
||||
return True
|
||||
|
||||
cdata_ext = CDataExt_Struct(
|
||||
(
|
||||
("totalVtxCount", CDataExt_Value.u16),
|
||||
("limbModifCount", CDataExt_Value.u16),
|
||||
(
|
||||
"limbModifications",
|
||||
(
|
||||
CDataExt_Value("I")
|
||||
.set_report(report_limbModifications)
|
||||
.set_write(write_limbModifications)
|
||||
),
|
||||
), # SkinLimbModif*
|
||||
("dlist", dlist_resources.cdata_ext_gfx_segmented),
|
||||
)
|
||||
)
|
||||
|
||||
def get_c_declaration_base(self):
|
||||
return f"SkinAnimatedLimbData {self.symbol_name}"
|
||||
|
||||
def get_c_reference(self, resource_offset: int):
|
||||
if resource_offset == 0:
|
||||
return f"&{self.symbol_name}"
|
||||
else:
|
||||
raise ValueError()
|
||||
|
||||
|
||||
class SkinLimbResource(CDataResource):
|
||||
def report_segment(resource, memory_context: "MemoryContext", v):
|
||||
assert isinstance(v, int)
|
||||
address = v
|
||||
segmentType = resource.cdata_unpacked["segmentType"]
|
||||
if segmentType == 4: # SKIN_LIMB_TYPE_ANIMATED
|
||||
# segment is SkinAnimatedLimbData*
|
||||
assert address != 0
|
||||
memory_context.report_resource_at_segmented(
|
||||
resource,
|
||||
address,
|
||||
SkinAnimatedLimbDataResource, # TODO
|
||||
lambda file, offset: SkinAnimatedLimbDataResource(
|
||||
file, offset, f"{resource.name}_{address:08X}_SkinAnimatedLimbData"
|
||||
),
|
||||
)
|
||||
elif segmentType == 11: # SKIN_LIMB_TYPE_NORMAL
|
||||
# segment is Gfx*
|
||||
assert address != 0
|
||||
dlist_resources.report_gfx_segmented(resource, memory_context, address)
|
||||
|
||||
def write_segment(
|
||||
resource, memory_context: "MemoryContext", v, f: io.TextIOBase, line_prefix
|
||||
):
|
||||
assert isinstance(v, int)
|
||||
address = v
|
||||
f.write(line_prefix)
|
||||
if address == 0:
|
||||
f.write("NULL")
|
||||
else:
|
||||
f.write(memory_context.get_c_reference_at_segmented(address))
|
||||
return True
|
||||
|
||||
cdata_ext = CDataExt_Struct(
|
||||
(
|
||||
("jointPos", cdata_ext_Vec3s),
|
||||
("child", CDataExt_Value.u8),
|
||||
("sibling", CDataExt_Value.u8),
|
||||
(
|
||||
"segmentType",
|
||||
CDataExt_Value("i").set_write_str_v(
|
||||
lambda v: SKIN_LIMB_TYPES.get(v, f"{v}")
|
||||
),
|
||||
),
|
||||
(
|
||||
"segment",
|
||||
CDataExt_Value("I").set_report(report_segment).set_write(write_segment),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
def get_c_declaration_base(self):
|
||||
return f"SkinLimb {self.symbol_name}"
|
||||
|
||||
def get_c_reference(self, resource_offset: int):
|
||||
if resource_offset == 0:
|
||||
return f"&{self.symbol_name}"
|
||||
else:
|
||||
raise ValueError()
|
||||
|
||||
|
||||
class SkinLimbsArrayResource(skeleton_resources.LimbsArrayResourceABC):
|
||||
limb_type = SkinLimbResource
|
||||
c_limb_type = "SkinLimb"
|
||||
|
||||
|
||||
class SkeletonSkinResource(skeleton_resources.SkeletonResourceABC):
|
||||
limbs_array_type = SkinLimbsArrayResource
|
|
@ -1,5 +1,4 @@
|
|||
import dataclasses
|
||||
import functools
|
||||
from pathlib import Path
|
||||
from pprint import pprint
|
||||
|
||||
|
@ -33,15 +32,16 @@ WRITE_EXTRACT = True
|
|||
from ..conf import WRITE_HINTS, I_D_OMEGALUL
|
||||
|
||||
|
||||
OOT_VERSION = "gc-eu-mq-dbg"
|
||||
BASEROM_PATH = Path("extracted") / OOT_VERSION / "baserom"
|
||||
BUILD_PATH = Path("build") / OOT_VERSION
|
||||
EXTRACTED_PATH = Path("extracted") / OOT_VERSION
|
||||
@dataclasses.dataclass
|
||||
class ExtractionContext:
|
||||
oot_version: str
|
||||
version_memctx_base: MemoryContext
|
||||
baserom_path: Path
|
||||
build_path: Path
|
||||
extracted_path: Path
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=200)
|
||||
def get_baserom_file_data(baserom_file_name: str):
|
||||
return memoryview((BASEROM_PATH / baserom_file_name).read_bytes())
|
||||
def get_baserom_file_data(self, baserom_file_name: str):
|
||||
return memoryview((self.baserom_path / baserom_file_name).read_bytes())
|
||||
|
||||
|
||||
def create_file_resources(rescoll: ResourcesDescCollection, file: File):
|
||||
|
@ -92,7 +92,7 @@ def create_file_resources(rescoll: ResourcesDescCollection, file: File):
|
|||
|
||||
|
||||
def process_pool(
|
||||
version_memctx_base: MemoryContext, pool_desc: ResourcesDescCollectionsPool
|
||||
extraction_ctx: ExtractionContext, pool_desc: ResourcesDescCollectionsPool
|
||||
):
|
||||
if VERBOSE2:
|
||||
print("> process_pool")
|
||||
|
@ -112,7 +112,7 @@ def process_pool(
|
|||
for rescoll in pool_desc.collections:
|
||||
if not isinstance(rescoll.backing_memory, BaseromFileBackingMemory):
|
||||
raise NotImplementedError(rescoll.backing_memory)
|
||||
data = get_baserom_file_data(rescoll.backing_memory.name)
|
||||
data = extraction_ctx.get_baserom_file_data(rescoll.backing_memory.name)
|
||||
if rescoll.backing_memory.range is not None:
|
||||
range_start, range_end = rescoll.backing_memory.range
|
||||
data = data[range_start:range_end]
|
||||
|
@ -135,7 +135,7 @@ def process_pool(
|
|||
|
||||
# 2) Build a MemoryContext for each File
|
||||
|
||||
memctx_base = version_memctx_base.copy()
|
||||
memctx_base = extraction_ctx.version_memctx_base.copy()
|
||||
files_by_segment: dict[int, list[File]] = dict()
|
||||
|
||||
for rescoll, file in file_by_rescoll.items():
|
||||
|
@ -228,31 +228,30 @@ def process_pool(
|
|||
|
||||
# 5)
|
||||
|
||||
# TODO this looks jank
|
||||
for rescoll, file in file_by_rescoll.items():
|
||||
file.set_source_path(Path("assets") / rescoll.out_path)
|
||||
|
||||
file.set_resources_paths(
|
||||
EXTRACTED_PATH,
|
||||
BUILD_PATH,
|
||||
extraction_ctx.extracted_path,
|
||||
extraction_ctx.build_path,
|
||||
Path("assets") / rescoll.out_path,
|
||||
)
|
||||
|
||||
for file, file_memctx in memctx_by_file.items():
|
||||
# write to EXTRACTED_PATH
|
||||
# write to extracted/
|
||||
if WRITE_EXTRACT:
|
||||
file.write_resources_extracted(file_memctx)
|
||||
|
||||
# "source" refers to the main .c and .h `#include`ing all the extracted resources
|
||||
if WRITE_SOURCE:
|
||||
# TODO fill referenced_files properly or something
|
||||
file.referenced_files = set(memctx_by_file.keys())
|
||||
file.referenced_files = set(memctx_by_file.keys()) - {file}
|
||||
file.write_source()
|
||||
|
||||
|
||||
def process_pool_wrapped(version_memctx_base, pd):
|
||||
def process_pool_wrapped(extraction_ctx, pd):
|
||||
try:
|
||||
process_pool(version_memctx_base, pd)
|
||||
process_pool(extraction_ctx, pd)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
import sys
|
||||
|
@ -273,6 +272,16 @@ def main():
|
|||
from tools import version_config
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"baserom_segments_dir",
|
||||
type=Path,
|
||||
help="Directory of uncompressed ROM segments",
|
||||
)
|
||||
parser.add_argument(
|
||||
"output_dir",
|
||||
type=Path,
|
||||
help="Output directory to place files in",
|
||||
)
|
||||
parser.add_argument("-v", dest="oot_version", default="gc-eu-mq-dbg")
|
||||
parser.add_argument("-j", dest="use_multiprocessing", action="store_true")
|
||||
parser.add_argument("-s", dest="single", default=None)
|
||||
|
@ -299,6 +308,14 @@ def main():
|
|||
)
|
||||
version_memctx_base.set_direct_file(vc.variables["sShadowTex"], file_sShadowTex)
|
||||
|
||||
extraction_ctx = ExtractionContext(
|
||||
args.oot_version,
|
||||
version_memctx_base,
|
||||
args.baserom_segments_dir,
|
||||
Path("build") / args.oot_version,
|
||||
args.output_dir,
|
||||
)
|
||||
|
||||
z64_resource_handlers.register_resource_handlers()
|
||||
|
||||
# TODO extract only when a pool xml was modified since last extract
|
||||
|
@ -312,7 +329,7 @@ def main():
|
|||
if coll.backing_memory.name == args.single:
|
||||
do_process_pool = True
|
||||
if do_process_pool:
|
||||
process_pool(version_memctx_base, pool_desc)
|
||||
process_pool(extraction_ctx, pool_desc)
|
||||
any_do_process_pool = True
|
||||
if any_do_process_pool:
|
||||
print("OK")
|
||||
|
@ -320,7 +337,7 @@ def main():
|
|||
print("Not found:", args.single)
|
||||
elif not args.use_multiprocessing: # everything on one process
|
||||
for pool_desc in pools_desc:
|
||||
process_pool(version_memctx_base, pool_desc)
|
||||
process_pool(extraction_ctx, pool_desc)
|
||||
print("all OK!!!")
|
||||
else: # multiprocessing
|
||||
import multiprocessing
|
||||
|
@ -328,7 +345,7 @@ def main():
|
|||
with multiprocessing.Pool() as pool:
|
||||
pool.starmap(
|
||||
process_pool_wrapped,
|
||||
zip([version_memctx_base] * len(pools_desc), pools_desc),
|
||||
zip([extraction_ctx] * len(pools_desc), pools_desc),
|
||||
)
|
||||
print("all OK!?")
|
||||
except Exception as e:
|
||||
|
@ -349,16 +366,3 @@ def main():
|
|||
else:
|
||||
print("rich.pretty.pprint(e):")
|
||||
rich.pretty.pprint(e, indent_guides=False)
|
||||
|
||||
# extract_xml(Path("objects/object_ydan_objects"))
|
||||
# extract_xml(Path("objects/object_fd2")) # TODO xml needs TLUT fixing, see VERBOSE_BEST_EFFORT_TLUT_NO_REAL_USER
|
||||
# extract_xml(Path("objects/object_am"))
|
||||
# extract_xml(Path("scenes/indoors/hylia_labo"))
|
||||
# extract_xml(Path("objects/gameplay_keep"))
|
||||
# extract_xml(Path("overlays/ovl_En_Jsjutan")) # The only xml with ~~<Symbol>~~ a <File Extract="False"
|
||||
# extract_xml(Path("overlays/ovl_Magic_Wind")) # SkelCurve
|
||||
# extract_xml(Path("objects/object_link_child")) # The only xml with <Mtx>
|
||||
# extract_xml(Path("scenes/dungeons/ddan")) # cutscene test
|
||||
# extract_xml(Path("scenes/dungeons/ganontikasonogo")) # has a spawn not in the entrance table
|
||||
|
||||
pprint(get_baserom_file_data.cache_info())
|
||||
|
|
|
@ -101,3 +101,8 @@ CAMERA_SETTING_TYPES = {
|
|||
0x40: "CAM_SET_PIVOT_FROM_SIDE",
|
||||
0x41: "CAM_SET_NORMAL4",
|
||||
}
|
||||
|
||||
SKIN_LIMB_TYPES = {
|
||||
4: "SKIN_LIMB_TYPE_ANIMATED",
|
||||
11: "SKIN_LIMB_TYPE_NORMAL",
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ from .extase.cdata_resources import Vec3sArrayResource, S16ArrayResource
|
|||
|
||||
from .extase_oot64 import (
|
||||
skeleton_resources,
|
||||
skeleton_skin_resources,
|
||||
animation_resources,
|
||||
collision_resources,
|
||||
dlist_resources,
|
||||
|
@ -82,16 +83,13 @@ def register_resource_handlers():
|
|||
if resource_desc.limb_type == z64resources.LimbType.STANDARD:
|
||||
pass
|
||||
elif resource_desc.limb_type == z64resources.LimbType.LOD:
|
||||
# TODO
|
||||
if resource_desc.type == z64resources.SkeletonType.NORMAL:
|
||||
# } SkeletonHeader; // size = 0x8
|
||||
return BinaryBlobResource(
|
||||
file, offset, offset + 0x8, resource_desc.symbol_name
|
||||
return skeleton_resources.SkeletonNormalLODResource(
|
||||
file, offset, resource_desc.symbol_name
|
||||
)
|
||||
elif resource_desc.type == z64resources.SkeletonType.FLEX:
|
||||
# } FlexSkeletonHeader; // size = 0xC
|
||||
return BinaryBlobResource(
|
||||
file, offset, offset + 0xC, resource_desc.symbol_name
|
||||
return skeleton_resources.SkeletonFlexLODResource(
|
||||
file, offset, resource_desc.symbol_name
|
||||
)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
|
@ -100,11 +98,9 @@ def register_resource_handlers():
|
|||
resource_desc.type,
|
||||
)
|
||||
elif resource_desc.limb_type == z64resources.LimbType.SKIN:
|
||||
# TODO
|
||||
assert resource_desc.type == z64resources.SkeletonType.NORMAL
|
||||
# } SkeletonHeader; // size = 0x8
|
||||
return BinaryBlobResource(
|
||||
file, offset, offset + 0x8, resource_desc.symbol_name
|
||||
return skeleton_skin_resources.SkeletonSkinResource(
|
||||
file, offset, resource_desc.symbol_name
|
||||
)
|
||||
elif resource_desc.limb_type == z64resources.LimbType.CURVE:
|
||||
assert resource_desc.type == z64resources.SkeletonType.CURVE
|
||||
|
@ -147,16 +143,15 @@ def register_resource_handlers():
|
|||
resource_desc.symbol_name,
|
||||
)
|
||||
if resource_desc.limb_type == z64resources.LimbType.SKIN:
|
||||
# } SkinLimb; // size = 0x10
|
||||
return BinaryBlobResource(
|
||||
file, offset, offset + 0x10, resource_desc.symbol_name
|
||||
return skeleton_skin_resources.SkinLimbResource(
|
||||
file, offset, resource_desc.symbol_name
|
||||
)
|
||||
if resource_desc.limb_type == z64resources.LimbType.LOD:
|
||||
# } LodLimb; // size = 0x10
|
||||
return BinaryBlobResource(
|
||||
file, offset, offset + 0x10, resource_desc.symbol_name
|
||||
return skeleton_resources.LODLimbResource(
|
||||
file, offset, resource_desc.symbol_name
|
||||
)
|
||||
if resource_desc.limb_type == z64resources.LimbType.LEGACY:
|
||||
# TODO LegacyLimbResource
|
||||
# } LegacyLimb; // size = 0x20
|
||||
return BinaryBlobResource(
|
||||
file, offset, offset + 0x20, resource_desc.symbol_name
|
||||
|
@ -171,6 +166,39 @@ def register_resource_handlers():
|
|||
resource_desc.limb_type,
|
||||
)
|
||||
|
||||
def limb_table_handler(
|
||||
file: File,
|
||||
resource_desc: z64resources.LimbTableResourceDesc,
|
||||
):
|
||||
if resource_desc.limb_type == z64resources.LimbType.STANDARD:
|
||||
resource = skeleton_resources.StandardLimbsArrayResource(
|
||||
file, resource_desc.offset, resource_desc.symbol_name
|
||||
)
|
||||
resource.set_length(resource_desc.count)
|
||||
return resource
|
||||
elif resource_desc.limb_type == z64resources.LimbType.SKIN:
|
||||
resource = skeleton_skin_resources.SkinLimbsArrayResource(
|
||||
file, resource_desc.offset, resource_desc.symbol_name
|
||||
)
|
||||
resource.set_length(resource_desc.count)
|
||||
return resource
|
||||
elif resource_desc.limb_type == z64resources.LimbType.LOD:
|
||||
resource = skeleton_resources.LODLimbsArrayResource(
|
||||
file, resource_desc.offset, resource_desc.symbol_name
|
||||
)
|
||||
resource.set_length(resource_desc.count)
|
||||
return resource
|
||||
elif resource_desc.limb_type == z64resources.LimbType.LEGACY:
|
||||
# TODO LegacyLimbsArrayResource
|
||||
return BinaryBlobResource(
|
||||
file,
|
||||
resource_desc.offset,
|
||||
resource_desc.offset + 4,
|
||||
resource_desc.symbol_name,
|
||||
)
|
||||
else:
|
||||
raise NotImplementedError("LimbTable of limb type", resource_desc.limb_type)
|
||||
|
||||
def animation_resource_handler(
|
||||
file: File,
|
||||
resource_desc: z64resources.AnimationResourceDesc,
|
||||
|
@ -412,10 +440,7 @@ def register_resource_handlers():
|
|||
z64resources.LegacyAnimationResourceDesc: get_fixed_size_resource_handler(
|
||||
0xC
|
||||
), # TODO
|
||||
z64resources.LimbTableResourceDesc: get_fixed_size_resource_handler(
|
||||
# idk, probably an array
|
||||
4
|
||||
), # TODO
|
||||
z64resources.LimbTableResourceDesc: limb_table_handler,
|
||||
z64resources.CurveAnimationResourceDesc: CurveAnimation_handler,
|
||||
z64resources.SceneResourceDesc: scene_resource_handler,
|
||||
z64resources.RoomResourceDesc: room_resource_handler,
|
||||
|
|
Loading…
Add table
Reference in a new issue