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 GTA V asset workflows.

It provides practical support for:

  • YDR read/write for static drawable workflows, including materials, drawable models, and lights
  • YCD read support for clip dictionaries and animation metadata
  • YMAP read/write
  • YTYP read/write
  • RPF7 OPEN archives and nested .rpf
  • ZIP -> RPF, RPF -> ZIP, and RPF -> folder
  • opening encrypted standalone .rpf files without preloading game keys
  • fast asset indexing with GameFileCache
  • texture extraction from YTD, GTXD parent chains and embedded dictionaries in YDR, YDD, YFT and YPT

Installation

pip install fivefury

For local development from a checkout:

pip install -e .

Python 3.11+ is required.

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

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, ParticleEffectExtension, Ytyp

ytyp = Ytyp(name="example_types")

archetype = Archetype(
    name="prop_tree_pine_01",
    lod_dist=150.0,
    asset_type=0,
    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()
  • 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")

Create a simple YDR

from fivefury import YdrLight, YdrLightType, 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)]],
        )
    ],
    texture="example_diffuse",
    lights=[
        YdrLight(
            position=(0.0, 0.0, 5.0),
            intensity=3.0,
            light_type=YdrLightType.POINT,
        )
    ],
    name="example_drawable",
)

ydr.save("example_drawable.ydr")

Convert OBJ to YDR

from fivefury import obj_to_ydr

obj_to_ydr(
    r"C:\mods\example.obj",
    r"C:\mods\example.ydr",
    generate_ytyp=True,
)

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

YCD

Read 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)

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 and Gxt2Dict.

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.1.25.tar.gz (584.8 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.1.25-cp311-abi3-win_amd64.whl (633.9 kB view details)

Uploaded CPython 3.11+Windows x86-64

File details

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

File metadata

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

File hashes

Hashes for fivefury-0.1.25.tar.gz
Algorithm Hash digest
SHA256 d75a8dd63a1979823f5a5577f647404d23308f0385962e72ea339c6576b03a49
MD5 c8d31ed84c5f2a0e8dea8ee71797233e
BLAKE2b-256 c28aa292fd6e1535e952c737e0efacf66c6a040a31e00062ca111666b2f8476c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: fivefury-0.1.25-cp311-abi3-win_amd64.whl
  • Upload date:
  • Size: 633.9 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.1.25-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 a35c37c97a6adbe78dc688ee6a0b8663a0bb8c44d12326355b05156ef93ca513
MD5 794f3cf1e64788931932691549c1e435
BLAKE2b-256 b37902e3f6eabf03467361110a19088d219d9bfeb24583789c42832032f255d2

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