fivefury 0.2.7__tar.gz → 0.2.9__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.
Files changed (345) hide show
  1. {fivefury-0.2.7 → fivefury-0.2.9}/PKG-INFO +43 -1
  2. fivefury-0.2.7/fivefury.egg-info/PKG-INFO → fivefury-0.2.9/README.md +41 -12
  3. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/__init__.py +58 -0
  4. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/bounds/model.py +5 -5
  5. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cache/core.py +42 -3
  6. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cache/io.py +6 -0
  7. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cache/scan.py +1 -1
  8. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cdr/model.py +1 -1
  9. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/gamefile.py +6 -1
  10. fivefury-0.2.9/fivefury/heightmap/__init__.py +31 -0
  11. fivefury-0.2.9/fivefury/heightmap/io.py +373 -0
  12. fivefury-0.2.9/fivefury/heightmap/model.py +535 -0
  13. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rel/model.py +4 -4
  14. fivefury-0.2.9/fivefury/water/__init__.py +30 -0
  15. fivefury-0.2.9/fivefury/water/geometry.py +99 -0
  16. fivefury-0.2.9/fivefury/water/io.py +195 -0
  17. fivefury-0.2.9/fivefury/water/model.py +573 -0
  18. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/builder.py +2 -1
  19. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/model.py +1 -1
  20. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/prepare.py +91 -2
  21. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/writer.py +2 -1
  22. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/entities.py +2 -2
  23. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymt/__init__.py +1 -1
  24. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/mlo.py +2 -2
  25. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/timed_archetype.py +2 -2
  26. fivefury-0.2.7/README.md → fivefury-0.2.9/fivefury.egg-info/PKG-INFO +54 -0
  27. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury.egg-info/SOURCES.txt +10 -1
  28. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury.egg-info/requires.txt +1 -0
  29. {fivefury-0.2.7 → fivefury-0.2.9}/native/py_bindings.h +4 -0
  30. {fivefury-0.2.7 → fivefury-0.2.9}/native/py_crypto.cpp +14 -10
  31. {fivefury-0.2.7 → fivefury-0.2.9}/native/py_index.cpp +14 -8
  32. {fivefury-0.2.7 → fivefury-0.2.9}/native/rpf_archive.cpp +7 -0
  33. {fivefury-0.2.7 → fivefury-0.2.9}/pyproject.toml +5 -2
  34. {fivefury-0.2.7 → fivefury-0.2.9}/setup.py +4 -1
  35. fivefury-0.2.9/tests/test_heightmap.py +160 -0
  36. fivefury-0.2.9/tests/test_python_compat.py +57 -0
  37. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_rpf_streaming_folder.py +63 -1
  38. fivefury-0.2.9/tests/test_water.py +317 -0
  39. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ybn.py +4 -3
  40. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ydr.py +9 -13
  41. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ydr_builder.py +24 -0
  42. fivefury-0.2.7/tests/test_ymap_occluders.py +0 -37
  43. {fivefury-0.2.7 → fivefury-0.2.9}/LICENSE +0 -0
  44. {fivefury-0.2.7 → fivefury-0.2.9}/MANIFEST.in +0 -0
  45. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/_native.py +0 -0
  46. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/assets/__init__.py +0 -0
  47. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/assets/base.py +0 -0
  48. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/assets/ydd.py +0 -0
  49. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/assets/ydr.py +0 -0
  50. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/assets/yft.py +0 -0
  51. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/assets/ypt.py +0 -0
  52. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/awc/__init__.py +0 -0
  53. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/awc/audio.py +0 -0
  54. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/awc/constants.py +0 -0
  55. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/awc/conversion.py +0 -0
  56. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/awc/crypto.py +0 -0
  57. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/awc/io.py +0 -0
  58. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/awc/structures.py +0 -0
  59. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/binary.py +0 -0
  60. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/bounds/__init__.py +0 -0
  61. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/bounds/geometry.py +0 -0
  62. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/bounds/materials.py +0 -0
  63. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/bounds/reader.py +0 -0
  64. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/bounds/writer.py +0 -0
  65. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/buckets.py +0 -0
  66. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cache/__init__.py +0 -0
  67. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cache/assets.py +0 -0
  68. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cache/kinds.py +0 -0
  69. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cache/paths.py +0 -0
  70. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cache/views.py +0 -0
  71. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cdr/__init__.py +0 -0
  72. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cdr/reader.py +0 -0
  73. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cdr/resource.py +0 -0
  74. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cdr/shaders.py +0 -0
  75. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cdr/vertices.py +0 -0
  76. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/colors.py +0 -0
  77. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/common.py +0 -0
  78. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/crypto/__init__.py +0 -0
  79. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/crypto/backends.py +0 -0
  80. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/crypto/keys.py +0 -0
  81. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/__init__.py +0 -0
  82. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/analysis.py +0 -0
  83. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/dsl.py +0 -0
  84. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/events.py +0 -0
  85. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/flags.py +0 -0
  86. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/lights.py +0 -0
  87. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/model.py +0 -0
  88. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/names.py +0 -0
  89. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/payloads.py +0 -0
  90. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/pso.py +0 -0
  91. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/scene/__init__.py +0 -0
  92. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/scene/base.py +0 -0
  93. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/scene/bindings.py +0 -0
  94. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/scene/core.py +0 -0
  95. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/scene/event_api.py +0 -0
  96. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/scene/io.py +0 -0
  97. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/scene/shared.py +0 -0
  98. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/scene/subtitles.py +0 -0
  99. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/scene/timeline.py +0 -0
  100. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/scene/validation.py +0 -0
  101. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/schema.py +0 -0
  102. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/write.py +0 -0
  103. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/cut/xml.py +0 -0
  104. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/data/lut.dat +0 -0
  105. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/data/magic.dat +0 -0
  106. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/data/ng.dat +0 -0
  107. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/dlc/__init__.py +0 -0
  108. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/dlc/enums.py +0 -0
  109. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/dlc/highlevel.py +0 -0
  110. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/dlc/model.py +0 -0
  111. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/dlc/xml.py +0 -0
  112. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/drawable/Shaders.xml +0 -0
  113. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/drawable/__init__.py +0 -0
  114. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/drawable/lod.py +0 -0
  115. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/drawable/model.py +0 -0
  116. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/drawable/shaders.py +0 -0
  117. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/game_target.py +0 -0
  118. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/gtxd.py +0 -0
  119. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/gxt2.py +0 -0
  120. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/hashing.py +0 -0
  121. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/meta/__init__.py +0 -0
  122. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/meta/backed.py +0 -0
  123. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/meta/builder.py +0 -0
  124. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/meta/defs.py +0 -0
  125. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/meta/read.py +0 -0
  126. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/meta/resource.py +0 -0
  127. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/meta/utils.py +0 -0
  128. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/metahash.py +0 -0
  129. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/pso/__init__.py +0 -0
  130. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/pso/codec.py +0 -0
  131. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/pso/model.py +0 -0
  132. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/pso/reader.py +0 -0
  133. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/pso/schema.py +0 -0
  134. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/pso/writer.py +0 -0
  135. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rbf.py +0 -0
  136. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rel/__init__.py +0 -0
  137. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rel/enums.py +0 -0
  138. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rel/io.py +0 -0
  139. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/resolver.py +0 -0
  140. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/resource.py +0 -0
  141. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/resource_textures.py +0 -0
  142. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rpf/__init__.py +0 -0
  143. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rpf/convert.py +0 -0
  144. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rpf/entries.py +0 -0
  145. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rpf/modes.py +0 -0
  146. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rpf/ps3.py +0 -0
  147. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rpf/streaming.py +0 -0
  148. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/rpf/utils.py +0 -0
  149. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/texture.py +0 -0
  150. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/vector.py +0 -0
  151. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/vehiclemeta/__init__.py +0 -0
  152. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/vehiclemeta/carcols.py +0 -0
  153. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/vehiclemeta/common.py +0 -0
  154. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/vehiclemeta/enums.py +0 -0
  155. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/vehiclemeta/handling.py +0 -0
  156. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/vehiclemeta/resource.py +0 -0
  157. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/vehiclemeta/variations.py +0 -0
  158. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/vehiclemeta/vehicles.py +0 -0
  159. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/xml.py +0 -0
  160. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ybn/__init__.py +0 -0
  161. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ybn/mlo.py +0 -0
  162. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ycd/__init__.py +0 -0
  163. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ycd/cutscene.py +0 -0
  164. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ycd/model.py +0 -0
  165. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ycd/reader.py +0 -0
  166. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ycd/sequence_channels.py +0 -0
  167. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ycd/sequence_codec.py +0 -0
  168. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ycd/sequence_tracks.py +0 -0
  169. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ycd/sequences.py +0 -0
  170. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ycd/write.py +0 -0
  171. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydd/__init__.py +0 -0
  172. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydd/model.py +0 -0
  173. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydd/reader.py +0 -0
  174. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydd/rigging.py +0 -0
  175. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydd/writer.py +0 -0
  176. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/ShadersGen9Conversion.xml +0 -0
  177. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/__init__.py +0 -0
  178. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/assimp.py +0 -0
  179. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/build_types.py +0 -0
  180. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/collision.py +0 -0
  181. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/defs.py +0 -0
  182. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/gen9.py +0 -0
  183. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/gen9_shader_enums.py +0 -0
  184. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/gen9_shader_info.py +0 -0
  185. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/materials.py +0 -0
  186. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/read_joints.py +0 -0
  187. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/read_lights.py +0 -0
  188. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/read_materials.py +0 -0
  189. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/read_skeleton.py +0 -0
  190. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/reader.py +0 -0
  191. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/rigging.py +0 -0
  192. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/shader_enums.py +0 -0
  193. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/shader_info.py +0 -0
  194. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/shaders.py +0 -0
  195. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/write_buffers.py +0 -0
  196. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/write_drawable.py +0 -0
  197. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/write_geometry.py +0 -0
  198. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/write_joints.py +0 -0
  199. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/write_lights.py +0 -0
  200. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/write_materials.py +0 -0
  201. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/write_models.py +0 -0
  202. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ydr/write_skeleton.py +0 -0
  203. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yed/__init__.py +0 -0
  204. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yed/audit.py +0 -0
  205. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yed/constants.py +0 -0
  206. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yed/enums.py +0 -0
  207. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yed/instructions.py +0 -0
  208. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yed/model.py +0 -0
  209. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yed/ped.py +0 -0
  210. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yed/reader.py +0 -0
  211. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yed/writer.py +0 -0
  212. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/__init__.py +0 -0
  213. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/cloth.py +0 -0
  214. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/cloth_reader.py +0 -0
  215. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/cloth_writer.py +0 -0
  216. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/constants.py +0 -0
  217. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/corpus.py +0 -0
  218. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/drawable_reader.py +0 -0
  219. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/drawables.py +0 -0
  220. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/events.py +0 -0
  221. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/events_reader.py +0 -0
  222. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/events_writer.py +0 -0
  223. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/fields_reader.py +0 -0
  224. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/fragment.py +0 -0
  225. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/fragment_drawable.py +0 -0
  226. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/glass.py +0 -0
  227. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/glass_reader.py +0 -0
  228. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/glass_writer.py +0 -0
  229. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/io_helpers.py +0 -0
  230. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/matrices.py +0 -0
  231. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/matrices_reader.py +0 -0
  232. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/matrices_writer.py +0 -0
  233. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/model.py +0 -0
  234. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/physics.py +0 -0
  235. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/physics_authoring.py +0 -0
  236. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/physics_reader.py +0 -0
  237. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/physics_writer.py +0 -0
  238. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/pointers.py +0 -0
  239. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/reader.py +0 -0
  240. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/stats.py +0 -0
  241. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/yft/validation.py +0 -0
  242. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/__init__.py +0 -0
  243. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/base.py +0 -0
  244. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/blocks.py +0 -0
  245. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/cargens.py +0 -0
  246. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/defs.py +0 -0
  247. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/enums.py +0 -0
  248. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/extension_defs.py +0 -0
  249. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/extensions.py +0 -0
  250. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/grass.py +0 -0
  251. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/io.py +0 -0
  252. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/lights.py +0 -0
  253. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/mlo_validation.py +0 -0
  254. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/model.py +0 -0
  255. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/occluders.py +0 -0
  256. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/packing.py +0 -0
  257. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/surfaces.py +0 -0
  258. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/timecycle.py +0 -0
  259. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymap/utils.py +0 -0
  260. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymf/__init__.py +0 -0
  261. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymf/builder.py +0 -0
  262. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymf/enums.py +0 -0
  263. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymf/model.py +0 -0
  264. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymf/pso.py +0 -0
  265. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymf/resource.py +0 -0
  266. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymf/schema.py +0 -0
  267. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymf/utils.py +0 -0
  268. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymt/ped_metadata.py +0 -0
  269. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymt/ped_variation.py +0 -0
  270. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymt/scenario.py +0 -0
  271. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ymt/streaming.py +0 -0
  272. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynd/__init__.py +0 -0
  273. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynd/heightmaps.py +0 -0
  274. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynd/model.py +0 -0
  275. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynd/network.py +0 -0
  276. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynd/reader.py +0 -0
  277. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynd/regions.py +0 -0
  278. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynd/writer.py +0 -0
  279. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynv/__init__.py +0 -0
  280. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynv/assimp.py +0 -0
  281. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynv/model.py +0 -0
  282. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynv/reader.py +0 -0
  283. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ynv/writer.py +0 -0
  284. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytd/__init__.py +0 -0
  285. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytd/defs.py +0 -0
  286. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytd/model.py +0 -0
  287. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/__init__.py +0 -0
  288. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/archetypes.py +0 -0
  289. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/asset_types.py +0 -0
  290. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/base_archetype.py +0 -0
  291. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/defs.py +0 -0
  292. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/extension_defs.py +0 -0
  293. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/extensions.py +0 -0
  294. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/flags.py +0 -0
  295. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/helpers.py +0 -0
  296. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/lod.py +0 -0
  297. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/mlo_validation.py +0 -0
  298. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury/ytyp/model.py +0 -0
  299. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury.egg-info/dependency_links.txt +0 -0
  300. {fivefury-0.2.7 → fivefury-0.2.9}/fivefury.egg-info/top_level.txt +0 -0
  301. {fivefury-0.2.7 → fivefury-0.2.9}/native/bounds_algorithms.cpp +0 -0
  302. {fivefury-0.2.7 → fivefury-0.2.9}/native/bounds_algorithms.h +0 -0
  303. {fivefury-0.2.7 → fivefury-0.2.9}/native/bounds_python.cpp +0 -0
  304. {fivefury-0.2.7 → fivefury-0.2.9}/native/bounds_python.h +0 -0
  305. {fivefury-0.2.7 → fivefury-0.2.9}/native/bounds_types.h +0 -0
  306. {fivefury-0.2.7 → fivefury-0.2.9}/native/crypto_magic.cpp +0 -0
  307. {fivefury-0.2.7 → fivefury-0.2.9}/native/crypto_magic.h +0 -0
  308. {fivefury-0.2.7 → fivefury-0.2.9}/native/py_bindings.cpp +0 -0
  309. {fivefury-0.2.7 → fivefury-0.2.9}/native/py_bounds.cpp +0 -0
  310. {fivefury-0.2.7 → fivefury-0.2.9}/native/py_module.cpp +0 -0
  311. {fivefury-0.2.7 → fivefury-0.2.9}/native/py_resource.cpp +0 -0
  312. {fivefury-0.2.7 → fivefury-0.2.9}/native/py_rpf.cpp +0 -0
  313. {fivefury-0.2.7 → fivefury-0.2.9}/native/py_vertex.cpp +0 -0
  314. {fivefury-0.2.7 → fivefury-0.2.9}/native/resource_layout.cpp +0 -0
  315. {fivefury-0.2.7 → fivefury-0.2.9}/native/resource_layout.h +0 -0
  316. {fivefury-0.2.7 → fivefury-0.2.9}/native/rpf_archive.h +0 -0
  317. {fivefury-0.2.7 → fivefury-0.2.9}/native/rpf_crypto.cpp +0 -0
  318. {fivefury-0.2.7 → fivefury-0.2.9}/native/rpf_index.cpp +0 -0
  319. {fivefury-0.2.7 → fivefury-0.2.9}/native/rpf_index.h +0 -0
  320. {fivefury-0.2.7 → fivefury-0.2.9}/native/rpf_read.cpp +0 -0
  321. {fivefury-0.2.7 → fivefury-0.2.9}/native/rpf_scan.cpp +0 -0
  322. {fivefury-0.2.7 → fivefury-0.2.9}/native/rpf_scan.h +0 -0
  323. {fivefury-0.2.7 → fivefury-0.2.9}/setup.cfg +0 -0
  324. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_cdr.py +0 -0
  325. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_cut.py +0 -0
  326. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_cut_dsl.py +0 -0
  327. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_cut_no_template.py +0 -0
  328. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_dlc.py +0 -0
  329. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_drawable.py +0 -0
  330. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_gtxd.py +0 -0
  331. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_hashing.py +0 -0
  332. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_meta_rpf_contracts.py +0 -0
  333. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_mlo.py +0 -0
  334. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_rpf_ps3.py +0 -0
  335. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_vehiclemeta.py +0 -0
  336. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ycd.py +0 -0
  337. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ycd_cutscene_builder.py +0 -0
  338. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ydd.py +0 -0
  339. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ydr_shaders.py +0 -0
  340. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_yft.py +0 -0
  341. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ymf.py +0 -0
  342. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ynd.py +0 -0
  343. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ynv.py +0 -0
  344. {fivefury-0.2.7 → fivefury-0.2.9}/tests/test_ynv_assimp.py +0 -0
  345. {fivefury-0.2.7 → fivefury-0.2.9}/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.7
3
+ Version: 0.2.9
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,8 @@ 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
+ | `heightmap.dat` | Full | World-height grids, native quantization, row RLE, water masks, spatial queries |
46
+ | `water.xml` | Full | Water surfaces, triangle corners, calming regions, wave amplitude and direction |
44
47
  | `CUT` | Full | Cutscenes, plus the readable `.cuts` script format for round-trip authoring |
45
48
  | `GXT2` | Full | Hashed text tables with binary read/write and text import/export |
46
49
  | `AWC` | Full | Audio containers: PCM/WAV extraction, authoring from WAV/MP3/OGG/FLAC |
@@ -114,6 +117,45 @@ save_ybn(bound, "floor_collision.ybn")
114
117
 
115
118
  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
119
 
120
+ ### Author water
121
+
122
+ ```python
123
+ from fivefury import WaterData, WaterQuad, WaterWaveQuad
124
+
125
+ water = WaterData()
126
+ water.add(
127
+ WaterQuad.rectangle(
128
+ center=(100.0, 200.0, 12.5),
129
+ size=(80.0, 40.0),
130
+ alpha=26,
131
+ limited_depth=True,
132
+ )
133
+ )
134
+ water.add(
135
+ WaterWaveQuad.from_angle(
136
+ bounds=(60, 180, 140, 220),
137
+ amplitude=1.2,
138
+ degrees=90.0,
139
+ )
140
+ )
141
+ water.translate(x=500, y=-200, z=20.0).save("water.xml")
142
+ ```
143
+
144
+ ### Author a world height map
145
+
146
+ ```python
147
+ from fivefury import HeightMap, HeightMapBounds
148
+
149
+ heightmap = HeightMap.empty(
150
+ columns=10,
151
+ rows=10,
152
+ bounds=HeightMapBounds(0.0, 0.0, 0.0, 500.0, 500.0, 800.0),
153
+ )
154
+ heightmap.set_height(4, 6, minimum=32.0, maximum=78.0)
155
+ heightmap.set_water(4, 6)
156
+ heightmap.save("heightmap.dat")
157
+ ```
158
+
117
159
  ### Convert audio to AWC
118
160
 
119
161
  ```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,8 @@ 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
+ | `heightmap.dat` | Full | World-height grids, native quantization, row RLE, water masks, spatial queries |
33
+ | `water.xml` | Full | Water surfaces, triangle corners, calming regions, wave amplitude and direction |
44
34
  | `CUT` | Full | Cutscenes, plus the readable `.cuts` script format for round-trip authoring |
45
35
  | `GXT2` | Full | Hashed text tables with binary read/write and text import/export |
46
36
  | `AWC` | Full | Audio containers: PCM/WAV extraction, authoring from WAV/MP3/OGG/FLAC |
@@ -114,6 +104,45 @@ save_ybn(bound, "floor_collision.ybn")
114
104
 
115
105
  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
106
 
107
+ ### Author water
108
+
109
+ ```python
110
+ from fivefury import WaterData, WaterQuad, WaterWaveQuad
111
+
112
+ water = WaterData()
113
+ water.add(
114
+ WaterQuad.rectangle(
115
+ center=(100.0, 200.0, 12.5),
116
+ size=(80.0, 40.0),
117
+ alpha=26,
118
+ limited_depth=True,
119
+ )
120
+ )
121
+ water.add(
122
+ WaterWaveQuad.from_angle(
123
+ bounds=(60, 180, 140, 220),
124
+ amplitude=1.2,
125
+ degrees=90.0,
126
+ )
127
+ )
128
+ water.translate(x=500, y=-200, z=20.0).save("water.xml")
129
+ ```
130
+
131
+ ### Author a world height map
132
+
133
+ ```python
134
+ from fivefury import HeightMap, HeightMapBounds
135
+
136
+ heightmap = HeightMap.empty(
137
+ columns=10,
138
+ rows=10,
139
+ bounds=HeightMapBounds(0.0, 0.0, 0.0, 500.0, 500.0, 800.0),
140
+ )
141
+ heightmap.set_height(4, 6, minimum=32.0, maximum=78.0)
142
+ heightmap.set_water(4, 6)
143
+ heightmap.save("heightmap.dat")
144
+ ```
145
+
117
146
  ### Convert audio to AWC
118
147
 
119
148
  ```python
@@ -459,6 +459,37 @@ from .rpf import (
459
459
  zip_to_rpf,
460
460
  )
461
461
  from .texture import BCFormat
462
+ from .heightmap import (
463
+ HeightCell,
464
+ HeightGrid,
465
+ HeightMap,
466
+ HeightMapBounds,
467
+ HeightMapByteOrder,
468
+ HeightMapCellFormat,
469
+ HeightMapFlags,
470
+ HeightMapValidationError,
471
+ build_heightmap_bytes,
472
+ create_heightmap,
473
+ read_heightmap,
474
+ save_heightmap,
475
+ )
476
+ from .water import (
477
+ WaterAlpha,
478
+ WaterBounds,
479
+ WaterCalmingQuad,
480
+ WaterComponent,
481
+ WaterCornerAlphas,
482
+ WaterData,
483
+ WaterQuad,
484
+ WaterQuadType,
485
+ WaterValidationError,
486
+ WaterWaveQuad,
487
+ build_water_xml,
488
+ coerce_water_data,
489
+ create_water,
490
+ read_water,
491
+ save_water,
492
+ )
462
493
  from .ydr import (
463
494
  AssimpMaterial,
464
495
  AssimpScene,
@@ -1201,6 +1232,14 @@ __all__ = [
1201
1232
  "GameTarget",
1202
1233
  "GameCrypto",
1203
1234
  "GameFileType",
1235
+ "HeightCell",
1236
+ "HeightGrid",
1237
+ "HeightMap",
1238
+ "HeightMapBounds",
1239
+ "HeightMapByteOrder",
1240
+ "HeightMapCellFormat",
1241
+ "HeightMapFlags",
1242
+ "HeightMapValidationError",
1204
1243
  "GXT2_MAGIC",
1205
1244
  "GXT2_MAGIC_BYTES",
1206
1245
  "Gxt2",
@@ -1310,6 +1349,16 @@ __all__ = [
1310
1349
  "VehicleVariation",
1311
1350
  "VehicleWheelType",
1312
1351
  "VehicleWindow",
1352
+ "WaterAlpha",
1353
+ "WaterBounds",
1354
+ "WaterCalmingQuad",
1355
+ "WaterComponent",
1356
+ "WaterCornerAlphas",
1357
+ "WaterData",
1358
+ "WaterQuad",
1359
+ "WaterQuadType",
1360
+ "WaterValidationError",
1361
+ "WaterWaveQuad",
1313
1362
  "WalkDontWalkExtension",
1314
1363
  "WindDisturbanceExtension",
1315
1364
  "Ycd",
@@ -1567,7 +1616,9 @@ __all__ = [
1567
1616
  "infer_archetype_radius",
1568
1617
  "mark_cutscene_prop_archetypes",
1569
1618
  "assimp_to_ydr",
1619
+ "build_heightmap_bytes",
1570
1620
  "build_rsc7",
1621
+ "build_water_xml",
1571
1622
  "build_ynv_bytes",
1572
1623
  "build_ynv_system_layout",
1573
1624
  "build_bound_from_render_geometry",
@@ -1589,11 +1640,14 @@ __all__ = [
1589
1640
  "clear_hash_resolver",
1590
1641
  "clear_game_crypto",
1591
1642
  "create_rpf",
1643
+ "create_heightmap",
1644
+ "create_water",
1592
1645
  "create_ymf_for_ymaps",
1593
1646
  "create_ydd",
1594
1647
  "create_ydr",
1595
1648
  "convert_audio_to_awc",
1596
1649
  "coerce_ped_component",
1650
+ "coerce_water_data",
1597
1651
  "ensure_game_crypto",
1598
1652
  "get_game_crypto",
1599
1653
  "get_hash_resolver",
@@ -1670,6 +1724,8 @@ __all__ = [
1670
1724
  "read_ynv",
1671
1725
  "read_ymap",
1672
1726
  "read_vehicle_meta",
1727
+ "read_water",
1728
+ "read_heightmap",
1673
1729
  "read_ymf",
1674
1730
  "read_ymf_xml",
1675
1731
  "read_ymt",
@@ -1699,6 +1755,8 @@ __all__ = [
1699
1755
  "save_ymt",
1700
1756
  "save_ytd",
1701
1757
  "save_ytyp",
1758
+ "save_water",
1759
+ "save_heightmap",
1702
1760
  "set_ped_drawable_cloth",
1703
1761
  "time_flags",
1704
1762
  "save_cut",
@@ -1054,7 +1054,7 @@ class BoundGeometry(Bound):
1054
1054
  return material
1055
1055
 
1056
1056
  def build(self) -> "BoundGeometry":
1057
- super().build()
1057
+ Bound.build(self)
1058
1058
  for index, polygon in enumerate(self.polygons):
1059
1059
  polygon.index = index
1060
1060
  if isinstance(polygon, BoundPolygonTriangle):
@@ -1079,7 +1079,7 @@ class BoundGeometry(Bound):
1079
1079
  return self
1080
1080
 
1081
1081
  def validate(self) -> list[str]:
1082
- issues = super().validate()
1082
+ issues = Bound.validate(self)
1083
1083
  if len(self.polygon_material_indices) != len(self.polygons):
1084
1084
  issues.append("polygon_material_indices length does not match polygon count")
1085
1085
  for polygon_index, polygon in enumerate(self.polygons):
@@ -1164,7 +1164,7 @@ class BoundComposite(Bound):
1164
1164
  def compute_volume(self) -> float:
1165
1165
  if self.children:
1166
1166
  return sum(child.bound.compute_volume() for child in self.children)
1167
- return super().compute_volume()
1167
+ return Bound.compute_volume(self)
1168
1168
 
1169
1169
  def compute_center_of_gravity(
1170
1170
  self,
@@ -1226,7 +1226,7 @@ class BoundComposite(Bound):
1226
1226
  return self.compute_composite_angular_inertia(mass)
1227
1227
 
1228
1228
  def build(self) -> "BoundComposite":
1229
- super().build()
1229
+ Bound.build(self)
1230
1230
  for child in self.children:
1231
1231
  child.bound.build()
1232
1232
  if child.bounds is None or not _aabb_is_valid(child.bounds):
@@ -1252,7 +1252,7 @@ class BoundComposite(Bound):
1252
1252
  return self
1253
1253
 
1254
1254
  def validate(self) -> list[str]:
1255
- issues = super().validate()
1255
+ issues = Bound.validate(self)
1256
1256
  if not self.children:
1257
1257
  issues.append("Composite bound has no children")
1258
1258
  for index, child in enumerate(self.children):
@@ -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,9 @@ _KIND_DICT_TYPES: dict[str, GameFileType] = {
518
554
  "GtxdDict": GameFileType.GTXD,
519
555
  "AwcDict": GameFileType.AWC,
520
556
  "YedDict": GameFileType.YED,
557
+ "HeightMapDict": GameFileType.HEIGHTMAP,
558
+ "WaterDict": GameFileType.WATER,
559
+ "WaterMapDict": GameFileType.WATER,
521
560
  }
522
561
 
523
562
 
@@ -10,6 +10,7 @@ from ..cut import read_cut
10
10
  from ..gamefile import GameFile, GameFileType, guess_game_file_type
11
11
  from ..gtxd import read_gtxd
12
12
  from ..gxt2 import read_gxt2
13
+ from ..heightmap import read_heightmap
13
14
  from ..hashing import _get_lut
14
15
  from ..metahash import MetaHash
15
16
  from ..rel import read_rel
@@ -22,6 +23,7 @@ from ..rpf import (
22
23
  _normalize_key,
23
24
  )
24
25
  from ..vehiclemeta import read_vehicle_meta
26
+ from ..water import read_water
25
27
  from ..ybn import read_ybn
26
28
  from ..ycd import read_ycd
27
29
  from ..ydd import read_ydd
@@ -65,6 +67,10 @@ def _decode_dynamic(data: bytes, *, module_name: str, attribute: str, kind: Game
65
67
  def _decode_payload(path: str, data: bytes, *, raw: bytes | None = None) -> tuple[Any, GameFileType]:
66
68
  ext = Path(path).suffix.lower()
67
69
  name = Path(path).name.lower()
70
+ if name.startswith("heightmap") and name.endswith(".dat"):
71
+ return _decode_or_fallback(GameFileType.HEIGHTMAP, data, data, read_heightmap)
72
+ if name == "water.xml":
73
+ return _decode_or_fallback(GameFileType.WATER, data, data, read_water)
68
74
  if name == "gtxd.meta":
69
75
  return _decode_or_fallback(GameFileType.GTXD, data, data, read_gtxd)
70
76
  vehicle_meta_type = guess_game_file_type(path)
@@ -15,7 +15,7 @@ from ..gamefile import GameFileType, guess_game_file_type
15
15
  from ..hashing import _get_lut
16
16
  from ..rpf import RpfArchive, RpfFileEntry, _normalize_key
17
17
 
18
- _SCAN_INDEX_VERSION = 8
18
+ _SCAN_INDEX_VERSION = 10
19
19
  _SCAN_GC_INTERVAL = 8
20
20
 
21
21
  _FLAG_LOOSE = 1
@@ -284,7 +284,7 @@ class Cdr(DrawableAsset[CdrMaterial, CdrModel, CdrMesh]):
284
284
  graphics_data: bytes = dataclasses.field(default=b"", repr=False)
285
285
 
286
286
  def __post_init__(self) -> None:
287
- super().__post_init__()
287
+ DrawableAsset.__post_init__(self)
288
288
  self.render_bucket_masks = {
289
289
  DrawableLod(lod): int(mask)
290
290
  for lod, mask in self.render_bucket_masks.items()
@@ -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
@@ -79,6 +80,9 @@ _FILE_TYPE_MAP: dict[str, GameFileType] = {
79
80
 
80
81
  def guess_game_file_type(path: str | Path, default: GameFileType = GameFileType.UNKNOWN) -> GameFileType:
81
82
  parsed = Path(str(path))
83
+ normalized_name = parsed.name.lower()
84
+ if normalized_name.startswith("heightmap") and normalized_name.endswith(".dat"):
85
+ return GameFileType.HEIGHTMAP
82
86
  named_meta_types = {
83
87
  "gtxd.meta": GameFileType.GTXD,
84
88
  "vehicles.meta": GameFileType.VEHICLES,
@@ -88,8 +92,9 @@ def guess_game_file_type(path: str | Path, default: GameFileType = GameFileType.
88
92
  "carvariations.meta": GameFileType.CAR_VARIATIONS,
89
93
  "vehiclelayouts.meta": GameFileType.VEHICLE_LAYOUTS,
90
94
  "peds.meta": GameFileType.PEDS,
95
+ "water.xml": GameFileType.WATER,
91
96
  }
92
- named_type = named_meta_types.get(parsed.name.lower())
97
+ named_type = named_meta_types.get(normalized_name)
93
98
  if named_type is not None:
94
99
  return named_type
95
100
  return _FILE_TYPE_MAP.get(parsed.suffix.lower(), default)
@@ -0,0 +1,31 @@
1
+ from .io import (
2
+ build_heightmap_bytes,
3
+ create_heightmap,
4
+ read_heightmap,
5
+ save_heightmap,
6
+ )
7
+ from .model import (
8
+ HeightCell,
9
+ HeightGrid,
10
+ HeightMap,
11
+ HeightMapBounds,
12
+ HeightMapByteOrder,
13
+ HeightMapCellFormat,
14
+ HeightMapFlags,
15
+ HeightMapValidationError,
16
+ )
17
+
18
+ __all__ = [
19
+ "HeightCell",
20
+ "HeightGrid",
21
+ "HeightMap",
22
+ "HeightMapBounds",
23
+ "HeightMapByteOrder",
24
+ "HeightMapCellFormat",
25
+ "HeightMapFlags",
26
+ "HeightMapValidationError",
27
+ "build_heightmap_bytes",
28
+ "create_heightmap",
29
+ "read_heightmap",
30
+ "save_heightmap",
31
+ ]