fivefury 0.2.7__tar.gz → 0.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fivefury-0.2.7 → fivefury-0.2.8}/PKG-INFO +27 -1
- fivefury-0.2.7/fivefury.egg-info/PKG-INFO → fivefury-0.2.8/README.md +25 -12
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/__init__.py +32 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cache/core.py +41 -3
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cache/io.py +3 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cache/scan.py +1 -1
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/gamefile.py +2 -0
- fivefury-0.2.8/fivefury/water/__init__.py +30 -0
- fivefury-0.2.8/fivefury/water/geometry.py +99 -0
- fivefury-0.2.8/fivefury/water/io.py +195 -0
- fivefury-0.2.8/fivefury/water/model.py +573 -0
- fivefury-0.2.7/README.md → fivefury-0.2.8/fivefury.egg-info/PKG-INFO +38 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury.egg-info/SOURCES.txt +5 -1
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury.egg-info/requires.txt +1 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/rpf_archive.cpp +3 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/pyproject.toml +5 -2
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_rpf_streaming_folder.py +63 -1
- fivefury-0.2.8/tests/test_water.py +317 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ybn.py +4 -3
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ydr.py +9 -13
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ydr_builder.py +4 -0
- fivefury-0.2.7/tests/test_ymap_occluders.py +0 -37
- {fivefury-0.2.7 → fivefury-0.2.8}/LICENSE +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/MANIFEST.in +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/_native.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/assets/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/assets/base.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/assets/ydd.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/assets/ydr.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/assets/yft.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/assets/ypt.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/awc/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/awc/audio.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/awc/constants.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/awc/conversion.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/awc/crypto.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/awc/io.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/awc/structures.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/binary.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/bounds/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/bounds/geometry.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/bounds/materials.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/bounds/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/bounds/reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/bounds/writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/buckets.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cache/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cache/assets.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cache/kinds.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cache/paths.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cache/views.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cdr/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cdr/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cdr/reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cdr/resource.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cdr/shaders.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cdr/vertices.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/colors.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/common.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/crypto/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/crypto/backends.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/crypto/keys.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/analysis.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/dsl.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/events.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/flags.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/lights.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/names.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/payloads.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/pso.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/scene/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/scene/base.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/scene/bindings.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/scene/core.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/scene/event_api.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/scene/io.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/scene/shared.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/scene/subtitles.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/scene/timeline.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/scene/validation.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/schema.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/write.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/cut/xml.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/data/lut.dat +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/data/magic.dat +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/data/ng.dat +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/dlc/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/dlc/enums.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/dlc/highlevel.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/dlc/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/dlc/xml.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/drawable/Shaders.xml +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/drawable/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/drawable/lod.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/drawable/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/drawable/shaders.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/game_target.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/gtxd.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/gxt2.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/hashing.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/meta/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/meta/backed.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/meta/builder.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/meta/defs.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/meta/read.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/meta/resource.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/meta/utils.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/metahash.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/pso/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/pso/codec.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/pso/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/pso/reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/pso/schema.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/pso/writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rbf.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rel/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rel/enums.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rel/io.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rel/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/resolver.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/resource.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/resource_textures.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rpf/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rpf/convert.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rpf/entries.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rpf/modes.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rpf/ps3.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rpf/streaming.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/rpf/utils.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/texture.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/vector.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/vehiclemeta/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/vehiclemeta/carcols.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/vehiclemeta/common.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/vehiclemeta/enums.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/vehiclemeta/handling.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/vehiclemeta/resource.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/vehiclemeta/variations.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/vehiclemeta/vehicles.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/xml.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ybn/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ybn/mlo.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ycd/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ycd/cutscene.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ycd/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ycd/reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ycd/sequence_channels.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ycd/sequence_codec.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ycd/sequence_tracks.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ycd/sequences.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ycd/write.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydd/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydd/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydd/reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydd/rigging.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydd/writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/ShadersGen9Conversion.xml +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/assimp.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/build_types.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/builder.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/collision.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/defs.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/gen9.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/gen9_shader_enums.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/gen9_shader_info.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/materials.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/prepare.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/read_joints.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/read_lights.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/read_materials.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/read_skeleton.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/rigging.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/shader_enums.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/shader_info.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/shaders.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/write_buffers.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/write_drawable.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/write_geometry.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/write_joints.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/write_lights.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/write_materials.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/write_models.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ydr/write_skeleton.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yed/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yed/audit.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yed/constants.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yed/enums.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yed/instructions.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yed/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yed/ped.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yed/reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yed/writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/cloth.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/cloth_reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/cloth_writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/constants.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/corpus.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/drawable_reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/drawables.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/events.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/events_reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/events_writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/fields_reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/fragment.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/fragment_drawable.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/glass.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/glass_reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/glass_writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/io_helpers.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/matrices.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/matrices_reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/matrices_writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/physics.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/physics_authoring.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/physics_reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/physics_writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/pointers.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/stats.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/validation.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/yft/writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/base.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/blocks.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/cargens.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/defs.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/entities.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/enums.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/extension_defs.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/extensions.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/grass.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/io.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/lights.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/mlo_validation.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/occluders.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/packing.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/surfaces.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/timecycle.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymap/utils.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymf/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymf/builder.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymf/enums.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymf/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymf/pso.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymf/resource.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymf/schema.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymf/utils.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymt/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymt/ped_metadata.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymt/ped_variation.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymt/scenario.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ymt/streaming.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynd/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynd/heightmaps.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynd/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynd/network.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynd/reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynd/regions.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynd/writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynv/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynv/assimp.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynv/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynv/reader.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ynv/writer.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytd/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytd/defs.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytd/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/__init__.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/archetypes.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/asset_types.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/base_archetype.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/defs.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/extension_defs.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/extensions.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/flags.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/helpers.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/lod.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/mlo.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/mlo_validation.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/model.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury/ytyp/timed_archetype.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury.egg-info/dependency_links.txt +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/fivefury.egg-info/top_level.txt +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/bounds_algorithms.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/bounds_algorithms.h +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/bounds_python.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/bounds_python.h +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/bounds_types.h +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/crypto_magic.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/crypto_magic.h +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/py_bindings.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/py_bindings.h +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/py_bounds.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/py_crypto.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/py_index.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/py_module.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/py_resource.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/py_rpf.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/py_vertex.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/resource_layout.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/resource_layout.h +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/rpf_archive.h +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/rpf_crypto.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/rpf_index.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/rpf_index.h +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/rpf_read.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/rpf_scan.cpp +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/native/rpf_scan.h +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/setup.cfg +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/setup.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_cdr.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_cut.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_cut_dsl.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_cut_no_template.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_dlc.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_drawable.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_gtxd.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_hashing.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_meta_rpf_contracts.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_mlo.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_rpf_ps3.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_vehiclemeta.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ycd.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ycd_cutscene_builder.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ydd.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ydr_shaders.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_yft.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ymf.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ynd.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ynv.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ynv_assimp.py +0 -0
- {fivefury-0.2.7 → fivefury-0.2.8}/tests/test_ytyp_flags.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fivefury
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.8
|
|
4
4
|
Summary: Python library for GTA V asset workflows including YMAP, YTYP, RPF, YTD and GameFileCache utilities.
|
|
5
5
|
Author: Codex
|
|
6
6
|
License-Expression: CC0-1.0
|
|
@@ -8,6 +8,7 @@ Requires-Python: >=3.11
|
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Requires-Dist: miniaudio>=1.71
|
|
11
|
+
Requires-Dist: numpy>=1.26
|
|
11
12
|
Dynamic: license-file
|
|
12
13
|
|
|
13
14
|
# FiveFury
|
|
@@ -41,6 +42,7 @@ The Assimp-backed import helpers (`assimp_to_ydr`, `obj_to_ydr`, `fbx_to_ydr`, `
|
|
|
41
42
|
| `YTD` | Full | Texture dictionaries: read/write, extraction, embedded-asset helpers |
|
|
42
43
|
| `YND` | Full | Path nodes, links, area partitioning, junction heightmaps |
|
|
43
44
|
| `YNV` | Full | Navmeshes: sectors, polys, portals, validation, basic OBJ partitioning |
|
|
45
|
+
| `water.xml` | Full | Water surfaces, triangle corners, calming regions, wave amplitude and direction |
|
|
44
46
|
| `CUT` | Full | Cutscenes, plus the readable `.cuts` script format for round-trip authoring |
|
|
45
47
|
| `GXT2` | Full | Hashed text tables with binary read/write and text import/export |
|
|
46
48
|
| `AWC` | Full | Audio containers: PCM/WAV extraction, authoring from WAV/MP3/OGG/FLAC |
|
|
@@ -114,6 +116,30 @@ save_ybn(bound, "floor_collision.ybn")
|
|
|
114
116
|
|
|
115
117
|
Generated geometry is chunked as needed and gets BVH and octant data. The same bounds model backs standalone `YBN` files, embedded `YDR` collisions, and `YFT` physics — a drawable's render mesh can also be converted directly with `ydr.ensure_bound_from_render_geometry()`.
|
|
116
118
|
|
|
119
|
+
### Author water
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from fivefury import WaterData, WaterQuad, WaterWaveQuad
|
|
123
|
+
|
|
124
|
+
water = WaterData()
|
|
125
|
+
water.add(
|
|
126
|
+
WaterQuad.rectangle(
|
|
127
|
+
center=(100.0, 200.0, 12.5),
|
|
128
|
+
size=(80.0, 40.0),
|
|
129
|
+
alpha=26,
|
|
130
|
+
limited_depth=True,
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
water.add(
|
|
134
|
+
WaterWaveQuad.from_angle(
|
|
135
|
+
bounds=(60, 180, 140, 220),
|
|
136
|
+
amplitude=1.2,
|
|
137
|
+
degrees=90.0,
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
water.translate(x=500, y=-200, z=20.0).save("water.xml")
|
|
141
|
+
```
|
|
142
|
+
|
|
117
143
|
### Convert audio to AWC
|
|
118
144
|
|
|
119
145
|
```python
|
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: fivefury
|
|
3
|
-
Version: 0.2.7
|
|
4
|
-
Summary: Python library for GTA V asset workflows including YMAP, YTYP, RPF, YTD and GameFileCache utilities.
|
|
5
|
-
Author: Codex
|
|
6
|
-
License-Expression: CC0-1.0
|
|
7
|
-
Requires-Python: >=3.11
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Requires-Dist: miniaudio>=1.71
|
|
11
|
-
Dynamic: license-file
|
|
12
|
-
|
|
13
1
|
# FiveFury
|
|
14
2
|
|
|
15
3
|
FiveFury is a Python library for reading, writing, and packaging GTA V asset files: drawables, collisions, map metadata, animations, navigation data, texture dictionaries, text tables, audio containers, cutscenes, DLC metadata, and RPF archives.
|
|
@@ -41,6 +29,7 @@ The Assimp-backed import helpers (`assimp_to_ydr`, `obj_to_ydr`, `fbx_to_ydr`, `
|
|
|
41
29
|
| `YTD` | Full | Texture dictionaries: read/write, extraction, embedded-asset helpers |
|
|
42
30
|
| `YND` | Full | Path nodes, links, area partitioning, junction heightmaps |
|
|
43
31
|
| `YNV` | Full | Navmeshes: sectors, polys, portals, validation, basic OBJ partitioning |
|
|
32
|
+
| `water.xml` | Full | Water surfaces, triangle corners, calming regions, wave amplitude and direction |
|
|
44
33
|
| `CUT` | Full | Cutscenes, plus the readable `.cuts` script format for round-trip authoring |
|
|
45
34
|
| `GXT2` | Full | Hashed text tables with binary read/write and text import/export |
|
|
46
35
|
| `AWC` | Full | Audio containers: PCM/WAV extraction, authoring from WAV/MP3/OGG/FLAC |
|
|
@@ -114,6 +103,30 @@ save_ybn(bound, "floor_collision.ybn")
|
|
|
114
103
|
|
|
115
104
|
Generated geometry is chunked as needed and gets BVH and octant data. The same bounds model backs standalone `YBN` files, embedded `YDR` collisions, and `YFT` physics — a drawable's render mesh can also be converted directly with `ydr.ensure_bound_from_render_geometry()`.
|
|
116
105
|
|
|
106
|
+
### Author water
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from fivefury import WaterData, WaterQuad, WaterWaveQuad
|
|
110
|
+
|
|
111
|
+
water = WaterData()
|
|
112
|
+
water.add(
|
|
113
|
+
WaterQuad.rectangle(
|
|
114
|
+
center=(100.0, 200.0, 12.5),
|
|
115
|
+
size=(80.0, 40.0),
|
|
116
|
+
alpha=26,
|
|
117
|
+
limited_depth=True,
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
water.add(
|
|
121
|
+
WaterWaveQuad.from_angle(
|
|
122
|
+
bounds=(60, 180, 140, 220),
|
|
123
|
+
amplitude=1.2,
|
|
124
|
+
degrees=90.0,
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
water.translate(x=500, y=-200, z=20.0).save("water.xml")
|
|
128
|
+
```
|
|
129
|
+
|
|
117
130
|
### Convert audio to AWC
|
|
118
131
|
|
|
119
132
|
```python
|
|
@@ -459,6 +459,23 @@ from .rpf import (
|
|
|
459
459
|
zip_to_rpf,
|
|
460
460
|
)
|
|
461
461
|
from .texture import BCFormat
|
|
462
|
+
from .water import (
|
|
463
|
+
WaterAlpha,
|
|
464
|
+
WaterBounds,
|
|
465
|
+
WaterCalmingQuad,
|
|
466
|
+
WaterComponent,
|
|
467
|
+
WaterCornerAlphas,
|
|
468
|
+
WaterData,
|
|
469
|
+
WaterQuad,
|
|
470
|
+
WaterQuadType,
|
|
471
|
+
WaterValidationError,
|
|
472
|
+
WaterWaveQuad,
|
|
473
|
+
build_water_xml,
|
|
474
|
+
coerce_water_data,
|
|
475
|
+
create_water,
|
|
476
|
+
read_water,
|
|
477
|
+
save_water,
|
|
478
|
+
)
|
|
462
479
|
from .ydr import (
|
|
463
480
|
AssimpMaterial,
|
|
464
481
|
AssimpScene,
|
|
@@ -1310,6 +1327,16 @@ __all__ = [
|
|
|
1310
1327
|
"VehicleVariation",
|
|
1311
1328
|
"VehicleWheelType",
|
|
1312
1329
|
"VehicleWindow",
|
|
1330
|
+
"WaterAlpha",
|
|
1331
|
+
"WaterBounds",
|
|
1332
|
+
"WaterCalmingQuad",
|
|
1333
|
+
"WaterComponent",
|
|
1334
|
+
"WaterCornerAlphas",
|
|
1335
|
+
"WaterData",
|
|
1336
|
+
"WaterQuad",
|
|
1337
|
+
"WaterQuadType",
|
|
1338
|
+
"WaterValidationError",
|
|
1339
|
+
"WaterWaveQuad",
|
|
1313
1340
|
"WalkDontWalkExtension",
|
|
1314
1341
|
"WindDisturbanceExtension",
|
|
1315
1342
|
"Ycd",
|
|
@@ -1568,6 +1595,7 @@ __all__ = [
|
|
|
1568
1595
|
"mark_cutscene_prop_archetypes",
|
|
1569
1596
|
"assimp_to_ydr",
|
|
1570
1597
|
"build_rsc7",
|
|
1598
|
+
"build_water_xml",
|
|
1571
1599
|
"build_ynv_bytes",
|
|
1572
1600
|
"build_ynv_system_layout",
|
|
1573
1601
|
"build_bound_from_render_geometry",
|
|
@@ -1589,11 +1617,13 @@ __all__ = [
|
|
|
1589
1617
|
"clear_hash_resolver",
|
|
1590
1618
|
"clear_game_crypto",
|
|
1591
1619
|
"create_rpf",
|
|
1620
|
+
"create_water",
|
|
1592
1621
|
"create_ymf_for_ymaps",
|
|
1593
1622
|
"create_ydd",
|
|
1594
1623
|
"create_ydr",
|
|
1595
1624
|
"convert_audio_to_awc",
|
|
1596
1625
|
"coerce_ped_component",
|
|
1626
|
+
"coerce_water_data",
|
|
1597
1627
|
"ensure_game_crypto",
|
|
1598
1628
|
"get_game_crypto",
|
|
1599
1629
|
"get_hash_resolver",
|
|
@@ -1670,6 +1700,7 @@ __all__ = [
|
|
|
1670
1700
|
"read_ynv",
|
|
1671
1701
|
"read_ymap",
|
|
1672
1702
|
"read_vehicle_meta",
|
|
1703
|
+
"read_water",
|
|
1673
1704
|
"read_ymf",
|
|
1674
1705
|
"read_ymf_xml",
|
|
1675
1706
|
"read_ymt",
|
|
@@ -1699,6 +1730,7 @@ __all__ = [
|
|
|
1699
1730
|
"save_ymt",
|
|
1700
1731
|
"save_ytd",
|
|
1701
1732
|
"save_ytyp",
|
|
1733
|
+
"save_water",
|
|
1702
1734
|
"set_ped_drawable_cloth",
|
|
1703
1735
|
"time_flags",
|
|
1704
1736
|
"save_cut",
|
|
@@ -4,7 +4,7 @@ from collections import OrderedDict
|
|
|
4
4
|
from collections.abc import Mapping, Sequence
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any, Iterator
|
|
7
|
+
from typing import Any, Iterator, Self
|
|
8
8
|
|
|
9
9
|
from .assets import GameFileCacheAssetMixin, TextureRef
|
|
10
10
|
from .io import GameFileCacheIOMixin
|
|
@@ -100,6 +100,12 @@ class GameFileCache(GameFileCacheScanMixin, GameFileCacheAssetMixin, GameFileCac
|
|
|
100
100
|
def __iter__(self) -> Iterator[AssetRecord]:
|
|
101
101
|
return self.iter_assets()
|
|
102
102
|
|
|
103
|
+
def __enter__(self) -> Self:
|
|
104
|
+
return self
|
|
105
|
+
|
|
106
|
+
def __exit__(self, *exc_info: object) -> None:
|
|
107
|
+
self.close()
|
|
108
|
+
|
|
103
109
|
@property
|
|
104
110
|
def scan_complete(self) -> bool:
|
|
105
111
|
return self.last_scan is not None
|
|
@@ -139,7 +145,30 @@ class GameFileCache(GameFileCacheScanMixin, GameFileCacheAssetMixin, GameFileCac
|
|
|
139
145
|
self._texture_parent_view = None
|
|
140
146
|
self._kind_counts_view = None
|
|
141
147
|
|
|
148
|
+
def _close_runtime_archives(self) -> None:
|
|
149
|
+
seen: set[int] = set()
|
|
150
|
+
candidates = [
|
|
151
|
+
*self.archives,
|
|
152
|
+
*self._archive_lookup.values(),
|
|
153
|
+
*self._live_archives.values(),
|
|
154
|
+
]
|
|
155
|
+
for game_file in self.files.values():
|
|
156
|
+
if game_file.archive is not None:
|
|
157
|
+
candidates.append(game_file.archive)
|
|
158
|
+
if isinstance(game_file.parsed, RpfArchive):
|
|
159
|
+
candidates.append(game_file.parsed)
|
|
160
|
+
for archive in candidates:
|
|
161
|
+
identity = id(archive)
|
|
162
|
+
if identity in seen:
|
|
163
|
+
continue
|
|
164
|
+
seen.add(identity)
|
|
165
|
+
archive.close()
|
|
166
|
+
|
|
167
|
+
def close(self) -> None:
|
|
168
|
+
self.clear_runtime_cache(loaded_files=True)
|
|
169
|
+
|
|
142
170
|
def clear(self) -> None:
|
|
171
|
+
self._close_runtime_archives()
|
|
143
172
|
self.archives.clear()
|
|
144
173
|
self.files.clear()
|
|
145
174
|
self.entries.clear()
|
|
@@ -155,6 +184,7 @@ class GameFileCache(GameFileCacheScanMixin, GameFileCacheAssetMixin, GameFileCac
|
|
|
155
184
|
self._invalidate_views()
|
|
156
185
|
|
|
157
186
|
def clear_runtime_cache(self, *, loaded_files: bool = False) -> None:
|
|
187
|
+
self._close_runtime_archives()
|
|
158
188
|
self.archives.clear()
|
|
159
189
|
self.entries.clear()
|
|
160
190
|
self._archive_lookup.clear()
|
|
@@ -300,12 +330,18 @@ class GameFileCache(GameFileCacheScanMixin, GameFileCacheAssetMixin, GameFileCac
|
|
|
300
330
|
normalized = _normalize_key(key)
|
|
301
331
|
limit = max(0, int(self.max_open_archives))
|
|
302
332
|
if limit <= 0:
|
|
333
|
+
for cached in self._archive_lookup.values():
|
|
334
|
+
cached.close()
|
|
303
335
|
self._archive_lookup.clear()
|
|
304
336
|
return
|
|
305
|
-
self._archive_lookup.pop(normalized, None)
|
|
337
|
+
previous = self._archive_lookup.pop(normalized, None)
|
|
338
|
+
if previous is not None and previous is not archive:
|
|
339
|
+
previous.close()
|
|
306
340
|
self._archive_lookup[normalized] = archive
|
|
307
341
|
while len(self._archive_lookup) > limit:
|
|
308
|
-
self._archive_lookup.popitem(last=False)
|
|
342
|
+
_, evicted = self._archive_lookup.popitem(last=False)
|
|
343
|
+
if all(cached is not evicted for cached in self._archive_lookup.values()):
|
|
344
|
+
evicted.close()
|
|
309
345
|
|
|
310
346
|
def _register_asset(
|
|
311
347
|
self,
|
|
@@ -518,6 +554,8 @@ _KIND_DICT_TYPES: dict[str, GameFileType] = {
|
|
|
518
554
|
"GtxdDict": GameFileType.GTXD,
|
|
519
555
|
"AwcDict": GameFileType.AWC,
|
|
520
556
|
"YedDict": GameFileType.YED,
|
|
557
|
+
"WaterDict": GameFileType.WATER,
|
|
558
|
+
"WaterMapDict": GameFileType.WATER,
|
|
521
559
|
}
|
|
522
560
|
|
|
523
561
|
|
|
@@ -22,6 +22,7 @@ from ..rpf import (
|
|
|
22
22
|
_normalize_key,
|
|
23
23
|
)
|
|
24
24
|
from ..vehiclemeta import read_vehicle_meta
|
|
25
|
+
from ..water import read_water
|
|
25
26
|
from ..ybn import read_ybn
|
|
26
27
|
from ..ycd import read_ycd
|
|
27
28
|
from ..ydd import read_ydd
|
|
@@ -65,6 +66,8 @@ def _decode_dynamic(data: bytes, *, module_name: str, attribute: str, kind: Game
|
|
|
65
66
|
def _decode_payload(path: str, data: bytes, *, raw: bytes | None = None) -> tuple[Any, GameFileType]:
|
|
66
67
|
ext = Path(path).suffix.lower()
|
|
67
68
|
name = Path(path).name.lower()
|
|
69
|
+
if name == "water.xml":
|
|
70
|
+
return _decode_or_fallback(GameFileType.WATER, data, data, read_water)
|
|
68
71
|
if name == "gtxd.meta":
|
|
69
72
|
return _decode_or_fallback(GameFileType.GTXD, data, data, read_gtxd)
|
|
70
73
|
vehicle_meta_type = guess_game_file_type(path)
|
|
@@ -40,6 +40,7 @@ class GameFileType(IntEnum):
|
|
|
40
40
|
YED = 25
|
|
41
41
|
YFD = 27
|
|
42
42
|
HEIGHTMAP = 28
|
|
43
|
+
WATER = 29
|
|
43
44
|
WATERMAP = 29
|
|
44
45
|
MRF = 30
|
|
45
46
|
DISTANT_LIGHTS = 31
|
|
@@ -88,6 +89,7 @@ def guess_game_file_type(path: str | Path, default: GameFileType = GameFileType.
|
|
|
88
89
|
"carvariations.meta": GameFileType.CAR_VARIATIONS,
|
|
89
90
|
"vehiclelayouts.meta": GameFileType.VEHICLE_LAYOUTS,
|
|
90
91
|
"peds.meta": GameFileType.PEDS,
|
|
92
|
+
"water.xml": GameFileType.WATER,
|
|
91
93
|
}
|
|
92
94
|
named_type = named_meta_types.get(parsed.name.lower())
|
|
93
95
|
if named_type is not None:
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from .geometry import WaterAlpha, WaterBounds, WaterCornerAlphas
|
|
2
|
+
from .io import build_water_xml, create_water, read_water, save_water
|
|
3
|
+
from .model import (
|
|
4
|
+
WaterCalmingQuad,
|
|
5
|
+
WaterComponent,
|
|
6
|
+
WaterData,
|
|
7
|
+
WaterQuad,
|
|
8
|
+
WaterQuadType,
|
|
9
|
+
WaterValidationError,
|
|
10
|
+
WaterWaveQuad,
|
|
11
|
+
coerce_water_data,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"WaterAlpha",
|
|
16
|
+
"WaterBounds",
|
|
17
|
+
"WaterCalmingQuad",
|
|
18
|
+
"WaterComponent",
|
|
19
|
+
"WaterCornerAlphas",
|
|
20
|
+
"WaterData",
|
|
21
|
+
"WaterQuad",
|
|
22
|
+
"WaterQuadType",
|
|
23
|
+
"WaterValidationError",
|
|
24
|
+
"WaterWaveQuad",
|
|
25
|
+
"build_water_xml",
|
|
26
|
+
"coerce_water_data",
|
|
27
|
+
"create_water",
|
|
28
|
+
"read_water",
|
|
29
|
+
"save_water",
|
|
30
|
+
]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import math
|
|
5
|
+
|
|
6
|
+
WaterBounds = tuple[int, int, int, int]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
|
|
10
|
+
class WaterCornerAlphas:
|
|
11
|
+
southwest: int = 26
|
|
12
|
+
southeast: int = 26
|
|
13
|
+
northeast: int = 26
|
|
14
|
+
northwest: int = 26
|
|
15
|
+
|
|
16
|
+
def values(self) -> tuple[int, int, int, int]:
|
|
17
|
+
return (
|
|
18
|
+
int(self.southwest),
|
|
19
|
+
int(self.southeast),
|
|
20
|
+
int(self.northeast),
|
|
21
|
+
int(self.northwest),
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
WaterAlpha = int | WaterCornerAlphas
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _coerce_alphas(value: WaterAlpha) -> tuple[int, int, int, int]:
|
|
29
|
+
if isinstance(value, int):
|
|
30
|
+
return value, value, value, value
|
|
31
|
+
if isinstance(value, WaterCornerAlphas):
|
|
32
|
+
return value.values()
|
|
33
|
+
raise TypeError("alpha must be an integer or WaterCornerAlphas")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _grid_integer(value: float, *, label: str) -> int:
|
|
37
|
+
number = float(value)
|
|
38
|
+
rounded = round(number)
|
|
39
|
+
if not math.isfinite(number) or not math.isclose(
|
|
40
|
+
number,
|
|
41
|
+
rounded,
|
|
42
|
+
abs_tol=1e-9,
|
|
43
|
+
):
|
|
44
|
+
raise ValueError(f"{label} must be an integer water coordinate")
|
|
45
|
+
return rounded
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _centered_bounds(
|
|
49
|
+
center: tuple[float, float],
|
|
50
|
+
size: tuple[float, float],
|
|
51
|
+
) -> WaterBounds:
|
|
52
|
+
center_x, center_y = (float(component) for component in center)
|
|
53
|
+
width, height = (float(component) for component in size)
|
|
54
|
+
if not all(math.isfinite(value) for value in (center_x, center_y, width, height)):
|
|
55
|
+
raise ValueError("center and size must be finite")
|
|
56
|
+
if width <= 0.0 or height <= 0.0:
|
|
57
|
+
raise ValueError("size components must be greater than zero")
|
|
58
|
+
raw_bounds = (
|
|
59
|
+
center_x - width * 0.5,
|
|
60
|
+
center_y - height * 0.5,
|
|
61
|
+
center_x + width * 0.5,
|
|
62
|
+
center_y + height * 0.5,
|
|
63
|
+
)
|
|
64
|
+
try:
|
|
65
|
+
return tuple(
|
|
66
|
+
_grid_integer(value, label="center and size result") for value in raw_bounds
|
|
67
|
+
)
|
|
68
|
+
except ValueError as exc:
|
|
69
|
+
raise ValueError("center and size must produce integer water bounds") from exc
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _coerce_bounds(bounds: WaterBounds) -> WaterBounds:
|
|
73
|
+
if len(bounds) != 4:
|
|
74
|
+
raise ValueError("bounds must contain min_x, min_y, max_x, max_y")
|
|
75
|
+
return tuple(_grid_integer(value, label="bounds component") for value in bounds)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _contains_xy(
|
|
79
|
+
points: tuple[tuple[float, float, float], ...],
|
|
80
|
+
x: float,
|
|
81
|
+
y: float,
|
|
82
|
+
) -> bool:
|
|
83
|
+
if not points:
|
|
84
|
+
return False
|
|
85
|
+
signs: list[float] = []
|
|
86
|
+
for index, (x1, y1, _) in enumerate(points):
|
|
87
|
+
x2, y2, _ = points[(index + 1) % len(points)]
|
|
88
|
+
signs.append((x2 - x1) * (y - y1) - (y2 - y1) * (x - x1))
|
|
89
|
+
tolerance = 1e-9
|
|
90
|
+
return all(sign >= -tolerance for sign in signs) or all(
|
|
91
|
+
sign <= tolerance for sign in signs
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
__all__ = [
|
|
96
|
+
"WaterAlpha",
|
|
97
|
+
"WaterBounds",
|
|
98
|
+
"WaterCornerAlphas",
|
|
99
|
+
]
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import xml.etree.ElementTree as ET
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from ..xml import (
|
|
7
|
+
add_value,
|
|
8
|
+
child_by_name,
|
|
9
|
+
child_value,
|
|
10
|
+
children_by_name,
|
|
11
|
+
parse_bool,
|
|
12
|
+
parse_xml_root,
|
|
13
|
+
xml_bytes,
|
|
14
|
+
)
|
|
15
|
+
from .model import (
|
|
16
|
+
WaterCalmingQuad,
|
|
17
|
+
WaterComponent,
|
|
18
|
+
WaterData,
|
|
19
|
+
WaterQuad,
|
|
20
|
+
WaterWaveQuad,
|
|
21
|
+
coerce_water_data,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _int_value(element: ET.Element, name: str, default: int = 0) -> int:
|
|
26
|
+
text = child_value(element, name, str(default))
|
|
27
|
+
try:
|
|
28
|
+
return int(text, 0)
|
|
29
|
+
except ValueError:
|
|
30
|
+
return default
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _float_value(element: ET.Element, name: str, default: float = 0.0) -> float:
|
|
34
|
+
text = child_value(element, name, str(default))
|
|
35
|
+
try:
|
|
36
|
+
return float(text)
|
|
37
|
+
except ValueError:
|
|
38
|
+
return default
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _float_text(value: float) -> str:
|
|
42
|
+
text = format(float(value), ".9g")
|
|
43
|
+
return text if "." in text or "e" in text.lower() else f"{text}.0"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _item_elements(root: ET.Element, section_name: str) -> list[ET.Element]:
|
|
47
|
+
section = child_by_name(root, section_name)
|
|
48
|
+
if section is None:
|
|
49
|
+
return []
|
|
50
|
+
return [item for item in children_by_name(section, "Item") if len(item)]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _read_water_quad(item: ET.Element) -> WaterQuad:
|
|
54
|
+
return WaterQuad(
|
|
55
|
+
min_x=_int_value(item, "minX"),
|
|
56
|
+
min_y=_int_value(item, "minY"),
|
|
57
|
+
max_x=_int_value(item, "maxX"),
|
|
58
|
+
max_y=_int_value(item, "maxY"),
|
|
59
|
+
type=_int_value(item, "Type"),
|
|
60
|
+
is_invisible=parse_bool(child_value(item, "IsInvisible")),
|
|
61
|
+
has_limited_depth=parse_bool(child_value(item, "HasLimitedDepth")),
|
|
62
|
+
z=_float_value(item, "z"),
|
|
63
|
+
alpha_sw=_int_value(item, "a1", 26),
|
|
64
|
+
alpha_se=_int_value(item, "a2", 26),
|
|
65
|
+
alpha_ne=_int_value(item, "a3", 26),
|
|
66
|
+
alpha_nw=_int_value(item, "a4", 26),
|
|
67
|
+
no_stencil=parse_bool(child_value(item, "NoStencil")),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _read_calming_quad(item: ET.Element) -> WaterCalmingQuad:
|
|
72
|
+
return WaterCalmingQuad(
|
|
73
|
+
min_x=_int_value(item, "minX"),
|
|
74
|
+
min_y=_int_value(item, "minY"),
|
|
75
|
+
max_x=_int_value(item, "maxX"),
|
|
76
|
+
max_y=_int_value(item, "maxY"),
|
|
77
|
+
dampening=_float_value(item, "fDampening"),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _read_wave_quad(item: ET.Element) -> WaterWaveQuad:
|
|
82
|
+
return WaterWaveQuad(
|
|
83
|
+
min_x=_int_value(item, "minX"),
|
|
84
|
+
min_y=_int_value(item, "minY"),
|
|
85
|
+
max_x=_int_value(item, "maxX"),
|
|
86
|
+
max_y=_int_value(item, "maxY"),
|
|
87
|
+
amplitude=_float_value(item, "Amplitude"),
|
|
88
|
+
direction_x=_float_value(item, "XDirection"),
|
|
89
|
+
direction_y=_float_value(item, "YDirection"),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def read_water(source: bytes | str | Path) -> WaterData:
|
|
94
|
+
root = parse_xml_root(source)
|
|
95
|
+
if root.tag.lower() != "waterdata":
|
|
96
|
+
raise ValueError(f"Expected WaterData XML root, got {root.tag!r}")
|
|
97
|
+
return WaterData(
|
|
98
|
+
water_quads=[
|
|
99
|
+
_read_water_quad(item) for item in _item_elements(root, "WaterQuads")
|
|
100
|
+
],
|
|
101
|
+
calming_quads=[
|
|
102
|
+
_read_calming_quad(item) for item in _item_elements(root, "CalmingQuads")
|
|
103
|
+
],
|
|
104
|
+
wave_quads=[
|
|
105
|
+
_read_wave_quad(item) for item in _item_elements(root, "WaveQuads")
|
|
106
|
+
],
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _write_bounds(item: ET.Element, quad: WaterComponent) -> None:
|
|
111
|
+
add_value(item, "minX", quad.min_x)
|
|
112
|
+
add_value(item, "maxX", quad.max_x)
|
|
113
|
+
add_value(item, "minY", quad.min_y)
|
|
114
|
+
add_value(item, "maxY", quad.max_y)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _water_quad_element(quad: WaterQuad) -> ET.Element:
|
|
118
|
+
item = ET.Element("Item")
|
|
119
|
+
_write_bounds(item, quad)
|
|
120
|
+
add_value(item, "Type", int(quad.type))
|
|
121
|
+
add_value(item, "IsInvisible", quad.is_invisible)
|
|
122
|
+
add_value(item, "HasLimitedDepth", quad.has_limited_depth)
|
|
123
|
+
add_value(item, "z", _float_text(quad.z))
|
|
124
|
+
add_value(item, "a1", quad.alpha_sw)
|
|
125
|
+
add_value(item, "a2", quad.alpha_se)
|
|
126
|
+
add_value(item, "a3", quad.alpha_ne)
|
|
127
|
+
add_value(item, "a4", quad.alpha_nw)
|
|
128
|
+
add_value(item, "NoStencil", quad.no_stencil)
|
|
129
|
+
return item
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _calming_quad_element(quad: WaterCalmingQuad) -> ET.Element:
|
|
133
|
+
item = ET.Element("Item")
|
|
134
|
+
_write_bounds(item, quad)
|
|
135
|
+
add_value(item, "fDampening", _float_text(quad.dampening))
|
|
136
|
+
return item
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _wave_quad_element(quad: WaterWaveQuad) -> ET.Element:
|
|
140
|
+
item = ET.Element("Item")
|
|
141
|
+
_write_bounds(item, quad)
|
|
142
|
+
add_value(item, "Amplitude", _float_text(quad.amplitude))
|
|
143
|
+
add_value(item, "XDirection", _float_text(quad.direction_x))
|
|
144
|
+
add_value(item, "YDirection", _float_text(quad.direction_y))
|
|
145
|
+
return item
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _append_section(
|
|
149
|
+
root: ET.Element,
|
|
150
|
+
name: str,
|
|
151
|
+
items: list[WaterComponent],
|
|
152
|
+
factory,
|
|
153
|
+
) -> None:
|
|
154
|
+
if not items:
|
|
155
|
+
return
|
|
156
|
+
section = ET.SubElement(root, name)
|
|
157
|
+
for item in items:
|
|
158
|
+
section.append(factory(item))
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def build_water_xml(source: WaterData, *, validate: bool = True) -> bytes:
|
|
162
|
+
water = source.build()
|
|
163
|
+
if validate:
|
|
164
|
+
water.ensure_valid()
|
|
165
|
+
root = ET.Element("WaterData")
|
|
166
|
+
_append_section(root, "WaterQuads", water.water_quads, _water_quad_element)
|
|
167
|
+
_append_section(
|
|
168
|
+
root,
|
|
169
|
+
"CalmingQuads",
|
|
170
|
+
water.calming_quads,
|
|
171
|
+
_calming_quad_element,
|
|
172
|
+
)
|
|
173
|
+
_append_section(root, "WaveQuads", water.wave_quads, _wave_quad_element)
|
|
174
|
+
return xml_bytes(root)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def create_water(*items: WaterComponent) -> WaterData:
|
|
178
|
+
return coerce_water_data(list(items))
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def save_water(
|
|
182
|
+
source: WaterData,
|
|
183
|
+
path: str | Path,
|
|
184
|
+
*,
|
|
185
|
+
validate: bool = True,
|
|
186
|
+
) -> Path:
|
|
187
|
+
return source.save(path, validate=validate)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
__all__ = [
|
|
191
|
+
"build_water_xml",
|
|
192
|
+
"create_water",
|
|
193
|
+
"read_water",
|
|
194
|
+
"save_water",
|
|
195
|
+
]
|