Skip to main content

Python library for GTA V asset workflows including YMAP, YTYP, RPF, YTD and GameFileCache utilities.

Project description

FiveFury

FiveFury is a Python library for authoring, reading, writing, indexing, and packaging GTA V asset files.

It focuses on practical modding workflows: building drawable assets, collision resources, map metadata, animation dictionaries, nav data, texture dictionaries, text tables, and RPF archives from Python without forcing every user to work directly with binary layouts.

Highlights

  • Read, edit, build, and write core GTA V formats such as YDR, YDD, YBN, YCD, YMAP, YTYP, YTD, YND, YNV, CUT, GXT2, AWC, REL, and RPF.
  • Use declarative high-level helpers for common authoring tasks while still keeping access to lower-level binary/resource details.
  • Index game installs, loose folders, and archives with GameFileCache, including typed lookups by asset name, hash, and format.
  • Extract texture dictionaries from YTD, GTXD parent chains, and embedded dictionaries in resource assets.
  • Share common RSC7, META, hashing, material, bounds, resource, and archive layers across formats.
  • Use optional native acceleration for heavier bounds and archive operations when the compiled extension is available.

Installation

pip install fivefury

For local development from a checkout:

pip install -e .

Python 3.11+ is required.

Assimp-backed import helpers such as assimp_to_ydr(...), obj_to_ydr(...), fbx_to_ydr(...), and obj_to_nav(...) also require:

  • the Python package impasse
  • a working native assimp library discoverable by the current process

FiveFury does not currently probe common install locations on its own. The native library must already be reachable through the environment, usually via PATH.

Format Support

Support levels:

Status Meaning
Full Has practical read/write support and public high-level helpers for normal workflows.
Partial Recognized or parsed enough for selected workflows, but not complete authoring support.
Indexed Detected by GameFileCache and RPF tooling, but no dedicated high-level parser/writer yet.
Not implemented Known GTA V format, but FiveFury does not currently expose dedicated support.

Full Support

Format Scope
YDR Drawable resources: materials, shaders, samplers, numeric parameters, drawable models, LODs, render masks, lights, embedded textures, embedded bounds, skeletons, skinning, rigid bone bindings, shader inspection, and skeleton hash recalculation.
YDD Drawable dictionaries with multiple embedded drawables and high-level creation from named YDR drawables.
YBN Bounds/collisions: primitive bounds, composite bounds, geometry bounds, BVH bounds, octants, material names, material colors, and generated collision chunks from triangle meshes.
YCD Clip dictionaries: parsed metadata, sequence rebuilds, known track types, UV clip bindings, object animation metadata, skeletal tracks, root motion, camera tracks, and facial samples.
YMAP Map metadata: entities, car generators, timecycle modifiers, occluders, content flags, entity flags, LOD lights, distant lights, and typed metadata.
YTYP Archetypes: base/time/MLO archetypes, extensions, rooms, portals, entity sets, typed asset metadata, flags, LOD distances, physics dictionaries, and cutscene prop helpers.
YTD Texture dictionaries: read/write, resource texture payload preservation, cache extraction, and embedded-asset helpers.
YND Path node resources: nodes, links, typed flags/enums, area helpers, automatic area ID calculation, and network partitioning.
YNV Navmesh resources: sectors, polys, points, portals, typed metadata, validation, and basic Assimp/OBJ partitioning.
CUT Cutscene files: cameras, tracks, events, props, peds, vehicles, lights, high-level scene conversion, .cuts script authoring, and .cut to .cuts export.
GXT2 Hashed UTF-8 text tables with binary read/write, CodeWalker-style text import/export, mapping-style helpers, and GameFileCache loading.
RPF RPF7 OPEN archives, nested .rpf, folder/ZIP conversion, extraction modes, and encrypted standalone RPF opening when keys are available.

Partial Or Indexed Support

Format Current behavior
YFT Resource texture dictionaries can be discovered/extracted from fragments, but full fragment authoring is not implemented.
YPT Resource texture dictionaries can be discovered/extracted from particle dictionaries, but full particle authoring is not implemented.
AWC Audio wave containers can be read/written structurally, opened through GameFileCache, exported back to WAV for PCM/ADPCM streams, and built from mono or multichannel 16-bit PCM inputs decoded through miniaudio (.wav, .mp3, .ogg, .flac). Playback metadata lives in .rel banks.
REL Audio metadata banks can be read/written structurally, opened through GameFileCache, and round-tripped with unknown entries preserved. dat10.rel modular synth presets/synths, dat16.rel curves, dat22.rel categories, and common dat54.rel sound graph entries have typed models, including simple AWC-backed sounds, wrappers, sequential/multitrack/streaming child lists, randomized variations, modular synth sounds, automation/MIDI sounds, note maps, variable-curve and conditional routing, directional/kinetic routing, variable blocks, math operations, parameter transforms, fluctuators, external streams, sound sets, sound-set lists, and sound-hash lists. Other REL item families currently stay as raw entries.
YMF, YMT, YWR, YVR, YED Recognized/indexed by GameFileCache and RPF tooling, but no complete dedicated high-level reader/writer is exposed.
GTXD metadata Used by cache texture lookup and parent-chain resolution, but it is not a standalone binary asset format like .gxt2.

Not Implemented Yet

Format family Notes
YLD, YFD, YPDB, MRF Known game file types, currently no dedicated high-level support.
Heightmap and watermap resources Recognized as game concepts, but no complete public reader/writer yet.
Vehicle/ped audio REL specializations REL files can be loaded structurally, but specialized semantic authoring beyond the initial synth/curve/category/sound subset is not currently exposed.

Audio AWC Conversion

fivefury.awc can decode common desktop audio formats through miniaudio and write PCM .awc files. Mono input is written as a normal single-channel AWC; stereo or multichannel input is written as a real multichannel AWC with a STREAM_FORMAT source stream and logical channel streams.

from fivefury import Awc, convert_audio_to_awc

# Direct file-to-file conversion. The stream name defaults to the source stem.
convert_audio_to_awc("music/stinger.mp3", "stream/stinger.awc")

# Force stereo output if the source is mono or has more channels than you need.
convert_audio_to_awc("music/song.flac", "stream/song.awc", channels=2)

# In-memory authoring when you also need to inspect or post-process the AWC.
awc = Awc.from_audio("radio_intro", "audio/radio_intro.ogg")
awc.save("stream/radio_intro.awc")

The converter currently normalizes input to signed 16-bit PCM and preserves the source channel count unless channels= is provided. Use .rel metadata to expose the resulting .awc stream as a playable sound, radio entry, cutscene audio, or other game audio object.

CutScript Conversion

.cuts is FiveFury's readable cutscene authoring format. It can compile back to .cut, and existing .cut files can be exported to .cuts for inspection or editing.

from fivefury import GameFileCache, save_cut_as_cutscript, save_cutscript

# Export a binary cutscene to a readable script.
save_cut_as_cutscript("stream/sample.cut", "stream/sample.cuts")

# Optional: resolve more hashes by scanning a game/resource folder first.
cache = GameFileCache("stream")
cache.scan()
cache.populate_resolver()
save_cut_as_cutscript("stream/sample.cut", "stream/sample_resolved.cuts")

# Compile the script back to a binary .cut.
save_cutscript("stream/sample.cuts", destination="stream/sample_from_script.cut")

The exporter resolves known hashes through HashResolver and automatically registers sibling filenames when the source is a path. Unknown hashes stay as safe 0x???????? tokens. It also preserves cutscene flags, camera quaternions, and high-level streamed-model metadata such as CNAME, ANIM_BASE, ANIM_STREAMING_BASE, animation export specs, and typeFile/YTYP.

CutScript distinguishes static and animated cutscene props:

STATIC_PROP stage:
  MODEL stage01
  YTYP sample_meta

ANIMATED_PROP miku:
  MODEL miku_hatsune_metal
  YTYP sample_meta
  CNAME mmd_model_001
  ANIM_BASE miku_hatsune_metal
  PRESET COMMON_PROP

MODEL is the streamed .ydr asset. CNAME is the logical cutscene/YCD binding name; it may match MODEL, but only when the YCD was authored with the same object name.

API Style

The preferred high-level authoring style is now:

  • add_* for collections
  • set_* for single assignments or bindings
  • build() to normalize derived state before serialization
  • validate() to collect consistency issues

Enums are preferred where the game format has stable names: shaders, LODs, render masks, archetype asset types, bound material types, YND flags, YCD track formats, and skeleton flag-name mappings all expose typed values on the public API.

Some newer high-level helpers were renamed to match that convention. If you were using recent pre-release YDR helpers, notable renames are:

  • create_bone(...) -> add_bone(...)
  • embed_texture(...) -> add_embedded_texture(...)
  • unembed_texture(...) -> remove_embedded_texture(...)
  • use_bound(...) -> set_bound(...)
  • skin_model(...) -> set_model_skin(...)

Quick Start

Create a YMAP

from fivefury import Ymap

ymap = Ymap(name="example_map")

# Entities
ymap.entity("prop_tree_pine_01", position=(100, 200, 0), lod_dist=150.0)
ymap.entity("prop_bench_01a", position=(105, 200, 0), lod_dist=80.0)

# Car generators
ymap.car_gen("sultan", (110, 205, 0), heading=90)
ymap.car_gen("adder", (115, 205, 0), heading=90, body_colors=(5, 10), livery=2)

# Time cycle modifiers (center + size)
ymap.time_cycle_modifier("interior_dark", (100, 200, 5), (50, 50, 20), hours=(20, 6))

# Box occluders (position + size + angle in degrees)
ymap.box_occluder(position=(100, 200, 0), size=(10, 10, 10), angle=45)

# Occlude models
ymap.occlude_box((-5, -5, 0), (5, 5, 10))
ymap.occlude_quad([(0, 0, 0), (10, 0, 0), (10, 0, 10), (0, 0, 10)])

ymap.save("example_map.ymap", auto_extents=True)

If you want an internal resource path, set ymap.resource_name before saving.

Load a YMAP

from pathlib import Path

from fivefury import Ymap

ymap = Ymap.from_bytes(Path("example_map.ymap").read_bytes())

print(len(ymap.entities))
print(len(ymap.car_generators))
print(ymap.flags, ymap.content_flags)

for cg in ymap.car_generators:
    print(cg.car_model, cg.heading, cg.body_colors)

Create a YTYP

from fivefury import Archetype, ArchetypeAssetType, ParticleEffectExtension, Ytyp

ytyp = Ytyp(name="example_types")

archetype = Archetype(
    name="prop_tree_pine_01",
    lod_dist=150.0,
    asset_type=ArchetypeAssetType.DRAWABLE,
    bb_min=(-1.5, -1.5, -0.5),
    bb_max=(1.5, 1.5, 8.0),
    bs_centre=(0.0, 0.0, 3.5),
    bs_radius=5.0,
)
archetype.add_extension(
    ParticleEffectExtension(
        name="fx_tree",
        fx_name="scr_wheel_burnout",
        fx_type=2,
        scale=0.8,
    )
)

ytyp.add_archetype(archetype)
ytyp.save("example_types.ytyp")

Pack Assets into an RPF

from fivefury import Ymap, create_rpf

ymap = Ymap(name="packed_map")
ymap.entity("prop_tree_pine_01", position=(0.0, 0.0, 0.0), lod_dist=120.0)

archive = create_rpf("mods.rpf")
archive.add("stream/packed_map.ymap", ymap)
archive.add("docs/readme.txt", b"hello from fivefury")
archive.save("mods.rpf")

Convert between ZIP, RPF, and folders

from fivefury import RpfExportMode, rpf_to_folder, rpf_to_zip, zip_to_rpf

zip_to_rpf("unpacked_mod_folder", "packed_mod.rpf")
rpf_to_zip("packed_mod.rpf", "packed_mod.zip", mode=RpfExportMode.STANDALONE)
rpf_to_folder("packed_mod.rpf", "packed_mod", mode=RpfExportMode.STANDALONE)

Directories ending in .rpf are packed as nested archives.

Open an encrypted standalone RPF

from fivefury import RpfArchive

archive = RpfArchive.from_path(r"C:\mods\dlc.rpf")
print(len(archive.all_entries))

Encrypted standalone archives can be opened directly. FiveFury initializes the bundled GTA V crypto context automatically.

Export mode overview

from fivefury import RpfArchive, RpfExportMode

archive = RpfArchive.from_path("packed_mod.rpf")

archive.to_folder("out_standalone", mode=RpfExportMode.STANDALONE)
archive.to_folder("out_logical", mode=RpfExportMode.LOGICAL)
archive.to_zip("out_stored.zip", mode=RpfExportMode.STORED)

print(RpfExportMode.STANDALONE.description)

RpfExportMode controls what gets written:

  • STORED: raw entry bytes as stored in the archive
  • STANDALONE: valid standalone files, including RSC7 containers for resources
  • LOGICAL: logical payloads with resource containers removed

YDR

Read and edit a YDR

from fivefury import BoundSphere, BoundType, TextureFormat, read_ydr

ydr = read_ydr("prop_example.ydr")

print(ydr.model_count)
print(len(ydr.lights))
print(ydr.materials[0].shader_name)

ydr.update_material(
    0,
    shader="spec.sps",
    textures={
        "DiffuseSampler": "prop_example_d",
        "SpecSampler": "prop_example_s",
        "BumpSampler": None,
    },
    parameters={
        "specularIntensityMult": 2.0,
    },
)

ydr.add_embedded_texture(
    name="prop_example_d",
    data=bytes([255, 255, 255, 255] * 16),
    width=4,
    height=4,
    format=TextureFormat.A8R8G8B8,
)

ydr.set_bound(
    BoundSphere(
        bound_type=BoundType.SPHERE,
        box_min=(-0.5, -0.5, -0.5),
        box_max=(0.5, 0.5, 0.5),
        box_center=(0.0, 0.0, 0.0),
        sphere_center=(0.0, 0.0, 0.0),
        sphere_radius=0.75,
        margin=0.05,
    )
)

issues = ydr.validate()
print(issues)

ydr.save("prop_example_out.ydr")

FiveFury exposes:

  • global ydr.materials
  • per-model views through ydr.models
  • parsed ydr.lights
  • editable material shaders, samplers, and numeric parameters
  • embedded texture helpers through add_embedded_texture(...) and remove_embedded_texture(...)
  • embedded collision helpers through set_bound(...) and clear_bound()
  • skeleton helpers for bones, skinning, rigid bone bindings, and explicit skeleton hash recalculation
  • build() / validate() helpers for authoring flows

Skin a YDR model declaratively

from fivefury import read_ydr

ydr = read_ydr("weapon_example.ydr")

root = ydr.add_bone("root", tag=0)
child = ydr.add_bone("child", parent=root, tag=1)
ydr.ensure_skeleton().build()

ydr.set_model_skin(0, bone_index=0, palette_size=0xFF)
mesh = ydr.meshes[0]
mesh.set_skin(
    bone_ids=[root, child],
    weights=[
        (1.0, 0.0, 0.0, 0.0),
        (0.5, 0.5, 0.0, 0.0),
        (0.0, 1.0, 0.0, 0.0),
    ],
    indices=[
        (0, 0, 0, 0),
        (0, 1, 0, 0),
        (1, 0, 0, 0),
    ],
)

print(ydr.validate())
ydr.save("weapon_example_out.ydr")

Write skeleton hashes for animated YDRs

Some animated YDRs, especially rigid object rigs where drawable models are bound to bones without vertex weights, need skeleton hash fields derived from bone tags, flags, and transforms. FiveFury preserves existing values by default for safe read/edit/write roundtrips. When authoring a skeleton from scratch, opt in explicitly:

from fivefury import YdrBoneFlags, YdrSkeleton, YdrSkeletonBinding, create_ydr

skeleton = YdrSkeleton.create()
root = skeleton.add_bone(
    "root",
    tag=0,
    flags=YdrBoneFlags.ROT_X | YdrBoneFlags.ROT_Y | YdrBoneFlags.ROT_Z,
)
skeleton.add_bone(
    "moving_part",
    parent=root,
    tag=1,
    flags=YdrBoneFlags.ROT_X | YdrBoneFlags.TRANS_Y,
    translation=(0.0, 0.25, 0.0),
)
skeleton.build()

build = create_ydr(
    meshes=[...],
    material_textures={"DiffuseSampler": "animated_prop_d"},
    skeleton=skeleton,
    skeleton_binding=YdrSkeletonBinding.rigid(bone_index=0),
    name="animated_prop",
)

# Recalculate only for this write. The in-memory skeleton is not mutated.
build.save("animated_prop.ydr", recalculate_skeleton_hashes=True)

If you want to store the values on the skeleton object before writing:

from fivefury import calculate_skeleton_unknown_hashes

hashes = calculate_skeleton_unknown_hashes(skeleton)
print(hashes)

skeleton.recalculate_unknown_hashes()
build.save("animated_prop.ydr")

The formal flag-name mapping used by the hash helper is exposed through YdrBoneFlagName and skeleton_bone_flag_names(...).

Create a simple YDR

from fivefury import YdrLight, YdrMeshInput, create_ydr

ydr = create_ydr(
    meshes=[
        YdrMeshInput(
            positions=[(0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)],
            indices=[0, 1, 2],
            texcoords=[[(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)]],
        )
    ],
    material_textures={"DiffuseSampler": "example_diffuse"},
    lights=[YdrLight.point(position=(0.0, 0.0, 5.0), intensity=3.0)],
    name="example_drawable",
)

ydr.add_light(YdrLight.spot(
    position=(0.0, 2.0, 5.0),
    direction=(0.0, 0.0, -1.0),
    cone_outer_angle=0.6,
))

ydr.save("example_drawable.ydr")

Convert Assimp-supported meshes to YDR

from fivefury import assimp_to_ydr, obj_to_ydr

assimp_to_ydr(
    r"C:\mods\example.fbx",
    r"C:\mods\example.ydr",
    generate_ytyp=True,
)

obj_to_ydr(
    r"C:\mods\example.obj",
    r"C:\mods\example_obj.ydr",
)

assimp_to_ydr(...) is now the unified import path for any source format that Assimp can read. obj_to_ydr(...) and fbx_to_ydr(...) are thin wrappers over that same pipeline.

This can also emit a companion YTYP with lowercase naming and textureDictionary set to <model>_txd.

These helpers require impasse plus a native assimp library that is already discoverable by the current process.

Inspect and choose YDR shaders

from fivefury import YdrShader, print_ydr_shader_info, read_ydr

print_ydr_shader_info(YdrShader.NORMAL_SPEC_CUTOUT)

ydr = read_ydr("prop_example.ydr")
ydr.update_material(
    0,
    shader=YdrShader.NORMAL_SPEC_CUTOUT,
    textures={
        "DiffuseSampler": "prop_example_d",
        "BumpSampler": "prop_example_n",
        "SpecSampler": "prop_example_s",
    },
)
ydr.save("prop_example_cutout.ydr")

YdrShader is generated from the bundled shader definitions, so IDEs can autocomplete known .sps names. Shader info helpers expose render bucket, vertex layout, texture slots, and numeric parameters. If an authoring path provides SpecularSampler, FiveFury normalizes it to the drawable slot name SpecSampler.

Read and write a YDD

from fivefury import Ydd, read_ydd

ydd = read_ydd("uppr_001_u.ydd")

for entry in ydd.iter_drawables():
    drawable = entry.drawable
    print(entry.name, drawable.model_count, len(drawable.materials))

out = Ydd.from_drawables({ydd.drawables[0].name: ydd.drawables[0].drawable}, version=165)
out.save("single_drawable.ydd")

YBN and Bounds

Create primitive bounds

from fivefury import BoundBox, BoundMaterialType, Ybn

bound = BoundBox.from_center_size(
    center=(0.0, 0.0, 1.0),
    size=(4.0, 4.0, 2.0),
    material_index=BoundMaterialType.CONCRETE,
)

ybn = Ybn.from_bound(bound)
print(ybn.validate())
ybn.save("simple_collision.ybn")

Primitive helpers are available for BoundSphere, BoundBox, BoundDisc, BoundCylinder, and BoundCloth. Material indices accept BoundMaterialType enum values instead of requiring raw integers.

Build collision from triangles

from fivefury import BoundMaterial, BoundMaterialType, build_bound_from_triangles, save_ybn

triangles = [
    ((0.0, 0.0, 0.0), (4.0, 0.0, 0.0), (0.0, 4.0, 0.0)),
    ((4.0, 0.0, 0.0), (4.0, 4.0, 0.0), (0.0, 4.0, 0.0)),
]

bound = build_bound_from_triangles(
    triangles,
    material=BoundMaterial(type=BoundMaterialType.CONCRETE),
)
save_ybn(bound, "floor_collision.ybn")

Generated geometry is chunked when needed, gets BVH data, and includes octants for BoundGeometry children. The same bounds model is used by standalone YBN files and embedded YDR collisions.

YCD

Read and write a YCD clip dictionary

from fivefury import read_ycd

ycd = read_ycd("maude_mcs_1-0.ycd")

print(len(ycd.clips))
print(len(ycd.animations))
print(ycd.clips[0].short_name)
print(ycd.animations[0].duration)

ycd.build()
ycd.save("maude_mcs_1-0_roundtrip.ycd")

FiveFury preserves parsed clip and animation metadata, rebuilds sequence data through typed channels, and hardens known skeletal/object animation fields before export. UV clips use the runtime binding convention <object>_uv_<slot_index> and MetaHash(object) + slot_index + 1.

Create or inspect UV clip bindings

from fivefury import build_ycd_uv_clip_hash, build_ycd_uv_clip_name, create_ycd_uv_clip

clip_name = build_ycd_uv_clip_name("prop_sign", 0)
clip_hash = build_ycd_uv_clip_hash("prop_sign", 0)
clip = create_ycd_uv_clip(object_name="prop_sign", slot_index=0, start_time=0.0, end_time=1.0)

print(clip_name, clip_hash, clip.short_name)

YND

Build path nodes and partition by area

from fivefury import YndLink, YndNetwork, YndNode

node_a = YndNode(key="a", position=(0.0, 0.0, 0.0))
node_b = YndNode(key="b", position=(600.0, 0.0, 0.0))

node_a.links.append(YndLink(target_key="b"))
node_b.links.append(YndLink(target_key="a"))

for ynd in YndNetwork.from_nodes([node_a, node_b]).build_ynds():
    ynd.save(f"nodes_{ynd.area_id}.ynd")

YndNetwork computes each node's area_id from its world position, assigns local node IDs per area, and resolves links by target_key. Use Ynd.from_nodes(...) directly when you already know all nodes belong to one area.

YNV

Read and validate a YNV

from fivefury import read_ynv

ynv = read_ynv("navmesh[120][120].ynv")

print(ynv.area_id)
print(len(ynv.polys))
print(len(ynv.vertices))
print(ynv.validate())

YNV support currently includes:

  • typed YnvAdjacencyType, YnvPointType, and YnvPortalType
  • editable vertices, indices, edges, polys, portals, and sector_tree
  • build() to normalize derived fields such as points_start_id and content flags
  • validate() to catch invalid poly spans, portal-link spans, and sector metadata mismatches before writing

Split an OBJ into per-cell navmeshes

from fivefury import obj_to_nav

paths = obj_to_nav(
    "test.obj",
    "out_navmeshes",
)

print(len(paths))
print(paths[0].name)

obj_to_nav(...) is a simple Assimp-backed helper that:

  • reads geometry through the shared Assimp pipeline
  • clips triangles against GTA V navmesh cells
  • writes one YNV per touched cell
  • names outputs as navmesh[file_x][file_y].ynv

This is intentionally a basic geometry partitioner, not a full navgen pipeline. It does not yet generate advanced navigation semantics such as cover, climb/drop adjacencies, portals, or point placement.

GameFileCache

Scan a Game Installation

from fivefury import GameFileCache

cache = GameFileCache(
    r"C:\Program Files (x86)\Steam\steamapps\common\Grand Theft Auto V",
    scan_workers=8,
    max_loaded_files=16,
)
cache.scan_game(use_index_cache=True)

print(cache.asset_count)
print(cache.stats_by_kind())

GameFileCache indexes loose files and archive contents, then loads supported formats lazily.

Control DLC and Scan Scope

from fivefury import GameFileCache

cache = GameFileCache(
    r"C:\Program Files (x86)\Steam\steamapps\common\Grand Theft Auto V",
    dlc_level="mpbattle",
    exclude_folders="mods;scratch",
    load_audio=False,
    load_vehicles=True,
    load_peds=True,
)
cache.scan_game(use_index_cache=True)

Useful scan options:

  • dlc_level: limit active DLCs
  • exclude_folders: ignore folders by prefix
  • load_audio: skip audio-related assets during scan
  • load_vehicles: skip vehicle-related assets during scan
  • load_peds: skip ped-related assets during scan
  • use_index_cache: reuse the persisted scan index for faster startup

Look Up Assets by Name and Type

asset = cache.get_asset("prop_tree_pine_01", kind=".ydr")
print(asset.path)
print(asset.short_name_hash)

You can iterate the cache directly:

for asset in cache:
    print(asset.path, asset.kind)

Or iterate a specific kind:

for ydr in cache.iter_kind(".ydr"):
    print(ydr.path)

Read and Extract Assets

from pathlib import Path

asset = cache.get_asset("prop_tree_pine_01", kind=".ydr")
data = cache.read_bytes(asset, logical=True)
out_path = cache.extract_asset(asset, Path("prop_tree_pine_01.ydr"))

print(len(data))
print(out_path)

Common access patterns:

  • get_asset(...): resolve one asset by path, name or hash
  • read_bytes(...): get bytes directly
  • get_file(...): build a lazy GameFile wrapper
  • extract_asset(...): write the asset to disk

Extraction defaults to standalone file output. For resource assets such as YDR, YDD, YFT, YTD, YMAP and YTYP, this produces a valid standalone RSC7 file.

If you want the logical payload instead:

cache.extract_asset("prop_tree_pine_01", "prop_tree_pine_01_payload.ydr", logical=True)

Extract Textures for an Asset

GameFileCache can resolve textures from:

  • direct YTD files
  • texture_dictionary references from YTYP archetypes
  • parent relationships from gtxd.meta
  • embedded texture dictionaries inside YDR, YDD, YFT and YPT
from pathlib import Path

paths = cache.extract_asset_textures(
    "stt_prop_stunt_bowling_pin.yft",
    Path("bowling_pin_textures"),
)

for path in paths:
    print(path)

You can inspect the texture refs first:

for ref in cache.list_asset_textures("uppr_001_u.ydd"):
    print(ref.origin, ref.container_name, ref.texture.name)

Type Dictionaries

GameFileCache exposes lazy type dictionaries keyed by shortNameHash.

from fivefury import jenk_hash

ydr = cache.YdrDict[jenk_hash("prop_tree_pine_01")]
ytd = cache.YtdDict[jenk_hash("vehshare")]
ybn = cache.YbnDict[jenk_hash("v_carshowroom")]

Available dictionaries include YdrDict, YddDict, YtdDict, YmapDict, YtypDict, YftDict, YbnDict, YcdDict, YptDict, YndDict, YnvDict, YedDict, YwrDict, YvrDict, RelDict, Gxt2Dict, and AwcDict.

Archetype Lookup

GameFileCache also builds a lazy global archetype lookup from indexed YTYP files.

archetype = cache.get_archetype("prop_tree_pine_01")
print(archetype.name)

for archetype in cache.iter_archetypes():
    print(archetype.name)

Global Hash Resolver

from fivefury import register_name, register_names_file, resolve_hash, jenk_hash

register_name("prop_tree_pine_01")
register_names_file("common_names.txt")

print(resolve_hash(jenk_hash("prop_tree_pine_01")))

The resolver is shared and optional. It is useful for display, lookups and tooling.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fivefury-0.2.2.tar.gz (832.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fivefury-0.2.2-cp311-abi3-win_amd64.whl (888.8 kB view details)

Uploaded CPython 3.11+Windows x86-64

File details

Details for the file fivefury-0.2.2.tar.gz.

File metadata

  • Download URL: fivefury-0.2.2.tar.gz
  • Upload date:
  • Size: 832.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for fivefury-0.2.2.tar.gz
Algorithm Hash digest
SHA256 6ee9ce3a841420ce130b6d4282ed3407587254b5f716e4e21a5249ca83feabff
MD5 dd5b47f99fe7ce3b9b11e2603f237b47
BLAKE2b-256 16d00b9480a7b5b4081c87499d3c1bda412e7c945fd826712a2106500de9836d

See more details on using hashes here.

File details

Details for the file fivefury-0.2.2-cp311-abi3-win_amd64.whl.

File metadata

  • Download URL: fivefury-0.2.2-cp311-abi3-win_amd64.whl
  • Upload date:
  • Size: 888.8 kB
  • Tags: CPython 3.11+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for fivefury-0.2.2-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 dc6524083f846e1c2b915b4cd015ecd375dcf6f5163b790b46cc429b6d2ac194
MD5 0595a3d091c315192898afd8f7d2e105
BLAKE2b-256 79867b92b3da3ccfd850ae95a27273a0edd1e151c59d0efcf3783083f2e14757

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page