This commit is contained in:
Amazed 2021-05-30 00:27:11 +02:00
parent e8c81933f1
commit 1477d5263e
8 changed files with 423 additions and 187 deletions

119
README.md
View File

@ -1,119 +0,0 @@
Giants Map Objects Extractor
============================
This tool is used to get what objects are in a map. It outputs the objects model and coords and doesn't do anything else yet.
How it works
------------
Basically maps are just a zip file that contains a terrain file (.gti) and a meta file (.bin) which stores objects on the map.
Objects in this file are stored using the following format:
```
...
2a -> static byte which indicate the start of an object definition
model -> 4 bytes long value to indicate the model of the object
x -> 4 bytes float value
y -> 4 bytes float value
z -> 4 bytes float value
angle -> 4 bytes float value
...
```
Usage
----
```
C:\Projects\gck-map-extract-objects> python .\extract.py
usage: extract.py [-h] map_file
extract.py: error: the following arguments are required: map_file
```
Example
-------
```
extract.py "MvM - Ultimate Winter Duel.gck"
2019-05-24 11:00:00,229 - __main__ - INFO - Opening MvM - Ultimate Winter Duel.gck
2019-05-24 11:00:00,230 - __main__ - INFO - Found file default.gti
2019-05-24 11:00:00,230 - __main__ - INFO - Found file w_M_Mecc_Ultimate Winter Duel.bin
2019-05-24 11:00:00,230 - __main__ - INFO - Found BIN file w_M_Mecc_Ultimate Winter Duel.bin
2019-05-24 11:00:00,230 - __main__ - INFO - Opening w_M_Mecc_Ultimate Winter Duel.bin
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=49 x=-469.0 y=-859.0 z=0.0 angle=0.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=679 x=-7.300000190734863 y=-652.510009765625 z=83.86000061035156 angle=-35.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=679 x=-19.989999771118164 y=-88.2699966430664 z=4.239999771118164 angle=0.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=1319 x=-195.02000427246094 y=-850.3099975585938 z=175.27999877929688 angle=0.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=679 x=-188.91000366210938 y=-23.510000228881836 z=4.239999771118164 angle=0.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=679 x=193.85000610351562 y=-23.510000228881836 z=4.239999771118164 angle=0.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=679 x=-271.79998779296875 y=-937.969970703125 z=180.5800018310547 angle=-312.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=1285 x=-4.340000152587891 y=108.41000366210938 z=33.72999954223633 angle=0.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=522 x=18.65999984741211 y=116.0999984741211 z=34.0099983215332 angle=0.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=679 x=323.6099853515625 y=-876.6900024414062 z=201.82000732421875 angle=-151.6699981689453
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=1319 x=344.6300048828125 y=-904.1099853515625 z=201.82000732421875 angle=0.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=679 x=-260.9599914550781 y=132.4199981689453 z=4.239999771118164 angle=0.0
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=679 x=-153.16000366210938 y=-1101.010009765625 z=83.86000061035156 angle=-322.6700134277344
2019-05-24 11:00:00,231 - __main__ - INFO - Object model=679 x=-172.30999755859375 y=273.4100036621094 z=4.239999771118164 angle=0.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=679 x=285.4599914550781 y=197.58999633789062 z=1.2899999618530273 angle=0.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=1051 x=398.67999267578125 y=131.8300018310547 z=4.239999771118164 angle=0.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=679 x=152.89999389648438 y=308.9200134277344 z=5.579999923706055 angle=0.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=679 x=3.630000114440918 y=342.75 z=4.239999771118164 angle=0.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=1319 x=711.719970703125 y=-443.30999755859375 z=48.470001220703125 angle=0.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=679 x=6.340000152587891 y=-1206.72998046875 z=83.86000061035156 angle=-191.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=1050 x=741.4400024414062 y=-413.0400085449219 z=48.470001220703125 angle=-127.66999816894531
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=981 x=8.890000343322754 y=-1259.68994140625 z=83.86000061035156 angle=180.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=1317 x=-893.8900146484375 y=-381.3900146484375 z=0.0 angle=0.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=1306 x=-267.07000732421875 y=479.6000061035156 z=4.239999771118164 angle=0.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=1050 x=-676.7000122070312 y=379.6300048828125 z=83.86000061035156 angle=81.66999816894531
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=1320 x=-646.2100219726562 y=403.239990234375 z=83.86000061035156 angle=0.0
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=679 x=-399.5199890136719 y=-1388.6600341796875 z=169.30999755859375 angle=-207.3300018310547
2019-05-24 11:00:00,232 - __main__ - INFO - Object model=1317 x=994.530029296875 y=-537.030029296875 z=0.0 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=257 x=-497.5 y=685.260009765625 z=48.130001068115234 angle=56.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=1308 x=-382.2799987792969 y=-1584.050048828125 z=93.37999725341797 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=679 x=139.1699981689453 y=-1660.030029296875 z=269.6499938964844 angle=-191.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=1319 x=178.2899932861328 y=-1661.4200439453125 z=269.6499938964844 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=679 x=77.22000122070312 y=919.5399780273438 z=83.86000061035156 angle=36.66999816894531
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=1319 x=-316.8699951171875 y=950.2999877929688 z=78.8499984741211 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=1319 x=209.67999267578125 y=1057.9200439453125 z=175.27999877929688 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=1317 x=1045.1500244140625 y=585.3200073242188 z=0.0 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=679 x=-261.4700012207031 y=1129.1199951171875 z=201.82000732421875 angle=144.6699981689453
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=1319 x=-318.7300109863281 y=1154.6800537109375 z=201.82000732421875 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=679 x=288.1300048828125 y=1146.27001953125 z=176.6199951171875 angle=-135.6699981689453
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=1317 x=869.0900268554688 y=-1661.699951171875 z=0.0 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=1317 x=-887.760009765625 y=929.8800048828125 z=0.0 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=1317 x=-983.75 y=-1688.030029296875 z=0.0 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=1319 x=-111.12000274658203 y=-2063.580078125 z=169.3800048828125 angle=0.0
2019-05-24 11:00:00,235 - __main__ - INFO - Object model=679 x=188.22000122070312 y=1288.4100341796875 z=83.86000061035156 angle=-135.6699981689453
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=679 x=47.5 y=1457.300048828125 z=83.86000061035156 angle=-5.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=981 x=46.77000045776367 y=1507.9599609375 z=83.86000061035156 angle=0.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=679 x=468.04998779296875 y=1627.6600341796875 z=169.3800048828125 angle=-77.66999816894531
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=1317 x=64.48999786376953 y=-2515.27001953125 z=0.0 angle=0.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=1308 x=422.0299987792969 y=1821.6500244140625 z=89.43000030517578 angle=0.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=679 x=-90.0 y=1902.81005859375 z=269.6499938964844 angle=-5.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=1319 x=-70.79000091552734 y=1924.3399658203125 z=269.6499938964844 angle=0.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=1317 x=1032.449951171875 y=1683.0 z=0.0 angle=0.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=1317 x=-779.9600219726562 y=2074.550048828125 z=0.0 angle=0.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=1317 x=328.82000732421875 y=2709.030029296875 z=0.0 angle=0.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=830 x=61.81365966796875 y=43.321990966796875 z=33.72548294067383 angle=30.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=830 x=-69.09912109375 y=63.499755859375 z=33.72548294067383 angle=270.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=830 x=111.51863098144531 y=117.34283447265625 z=33.72548294067383 angle=180.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=830 x=99.72610473632812 y=189.97406005859375 z=33.72548294067383 angle=59.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=830 x=-57.904052734375 y=136.96914672851562 z=33.72548294067383 angle=80.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=830 x=-30.401458740234375 y=187.08465576171875 z=33.72548294067383 angle=270.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=830 x=-15.68878173828125 y=63.614349365234375 z=33.72548294067383 angle=180.0
2019-05-24 11:00:00,236 - __main__ - INFO - Object model=830 x=37.35343933105469 y=188.96652221679688 z=33.72548294067383 angle=270.0
2019-05-24 11:00:00,237 - __main__ - INFO - Number of objects: 62
2019-05-24 11:00:00,237 - __main__ - INFO - Model 679 was found 22 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 1317 was found 10 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 1319 was found 9 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 830 was found 8 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 1050 was found 2 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 981 was found 2 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 1308 was found 2 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 49 was found 1 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 1285 was found 1 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 522 was found 1 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 1051 was found 1 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 1306 was found 1 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 1320 was found 1 times
2019-05-24 11:00:00,237 - __main__ - INFO - Model 257 was found 1 times
```

6
anm.py
View File

@ -1,14 +1,16 @@
from lib.skn_anm import * from lib.skn_anm import *
import os
def read_skn(filepath) -> AnmAnim: def read_anm(filepath) -> AnmAnim:
with open(filepath, "rb") as fp: with open(filepath, "rb") as fp:
init_filetrack(os.fstat(fp.fileno()).st_size)
r = AnmFile.parse(fp, AnmAnim) r = AnmFile.parse(fp, AnmAnim)
return r return r
def main(): def main():
anm = read_skn("/home/tasty/Projects/gck-map-extract-objects/anm_skn/Mc_salute.anm") anm = read_anm("/home/tasty/Projects/gck-map-extract-objects/anm_skn/verm_fly.anm")
pprint(anm) pprint(anm)

View File

@ -1,19 +1,52 @@
import struct import struct
file_track = []
print_debug = False
def debug_print(s):
if print_debug:
print(s)
def init_filetrack(size):
global file_track
file_track = [0] * size
def advance_track(offset, size):
if not file_track:
return
for i in range(size):
file_track[offset+i] = 1
def stat_track():
r = 0
total = len(file_track)
for i in file_track:
if i == 1:
r += 1
return r/total*100
def read_int(fp): def read_int(fp):
advance_track(fp.tell(), 4)
return struct.unpack("<L", fp.read(4))[0] return struct.unpack("<L", fp.read(4))[0]
def read_byte(fp): def read_byte(fp):
advance_track(fp.tell(), 1)
return struct.unpack("<B", fp.read(1))[0] return struct.unpack("<B", fp.read(1))[0]
def read_float(fp): def read_float(fp):
advance_track(fp.tell(), 4)
return struct.unpack("<f", fp.read(4))[0] return struct.unpack("<f", fp.read(4))[0]
def read_short(fp): def read_short(fp):
advance_track(fp.tell(), 2)
return struct.unpack("<H", fp.read(2))[0] return struct.unpack("<H", fp.read(2))[0]
@ -22,6 +55,7 @@ def read_string(fp, size):
def read_bytes(fp, size): def read_bytes(fp, size):
advance_track(fp.tell(), size)
return bytes(struct.unpack('<' + str(size) + 'B', fp.read(size))) return bytes(struct.unpack('<' + str(size) + 'B', fp.read(size)))
@ -74,29 +108,29 @@ def decompress(compressed_bytes, original_size: int):
def read_int_and_print(n, b): def read_int_and_print(n, b):
a = read_int(b) a = read_int(b)
print("%s=%s" % (n, a)) debug_print("%s=%s" % (n, a))
return a return a
def read_float_and_print(n, b): def read_float_and_print(n, b):
a = read_float(b) a = read_float(b)
print("%s=%s" % (n, a)) debug_print("%s=%s" % (n, a))
return a return a
def read_byte_and_print(n, b): def read_byte_and_print(n, b):
a = read_byte(b) a = read_byte(b)
print("%s=%s" % (n, a)) debug_print("%s=%s" % (n, a))
return a return a
def read_short_and_print(n, b): def read_short_and_print(n, b):
a = read_short(b) a = read_short(b)
print("%s=%s" % (n, a)) debug_print("%s=%s" % (n, a))
return a return a
def read_string_until_none_and_print(n, b): def read_string_until_none_and_print(n, b):
s = read_string_until_none(b) s = read_string_until_none(b)
print("%s=%s" % (n, s)) debug_print("%s=%s" % (n, s))
return s return s

View File

@ -4,8 +4,9 @@ from typing import List, Union
import math import math
import prettyprinter import prettyprinter
from prettyprinter import pprint from prettyprinter import pprint
import sys
prettyprinter.install_extras() prettyprinter.install_extras(exclude=["ipython", "django", "ipython_repr_pretty", "attrs"])
def with_offset(func): def with_offset(func):
@ -35,6 +36,28 @@ ANM_NODE_FLAG_SCL_ANIMATED = (1 << 3)
ANM_NODE_FLAG_AFF_ANIMATED = (1 << 4) ANM_NODE_FLAG_AFF_ANIMATED = (1 << 4)
ANM_NODE_FLAG_USR_ANIMATED = (1 << 5) ANM_NODE_FLAG_USR_ANIMATED = (1 << 5)
ANM_NODE_FLAG_ANIMATED = ((1 << 6) - 1) ANM_NODE_FLAG_ANIMATED = ((1 << 6) - 1)
NAM_STRING_KEY = 0
NAM_HANDLE_KEY = 1
class ANM_KeyType:
@classmethod
def get_name(cls, i):
for d in cls.__dict__:
if cls.__dict__[d] == i:
return d
return None
ANM_KEYS_TYPE_NULL = 0
ANM_KEYS_TYPE_REAL_TCB = 1
ANM_KEYS_TYPE_REAL_BEZ = 2
ANM_KEYS_TYPE_REAL_LIN = 3
ANM_KEYS_TYPE_VEC3_TCB = 4
ANM_KEYS_TYPE_VEC3_BEZ = 5
ANM_KEYS_TYPE_VEC3_LIN = 6
ANM_KEYS_TYPE_QUAT_TCB = 7
ANM_KEYS_TYPE_QUAT_BEZ = 8
ANM_KEYS_TYPE_QUAT_LIN = 9
ANM_KEYS_TYPE_AFFINE_SAM = 10
@dataclass @dataclass
@ -62,13 +85,28 @@ class Vec3Indexed(Vec3):
@classmethod @classmethod
@with_offset @with_offset
def create(cls, b): def create(cls, b):
x = read_float_and_print("Vec3->x", b) x = read_float_and_print("Vec3Indexed->x", b)
y = read_float_and_print("Vec3->y", b) y = read_float_and_print("Vec3Indexed->y", b)
z = read_float_and_print("Vec3->z", b) z = read_float_and_print("Vec3Indexed->z", b)
index = read_int_and_print("Vec3->index", b) index = read_int_and_print("Vec3Indexed->index", b)
return cls(x=x, y=y, z=z, index=index) return cls(x=x, y=y, z=z, index=index)
@dataclass
class Vec3IndexedWeighted(Vec3Indexed):
weight: float
@classmethod
@with_offset
def create(cls, b):
x = read_float_and_print("Vec3IndexedWeighted->x", b)
y = read_float_and_print("Vec3IndexedWeighted->y", b)
z = read_float_and_print("Vec3IndexedWeighted->z", b)
index = read_int_and_print("Vec3IndexedWeighted->index", b)
weight = read_float_and_print("Vec3IndexedWeighted->weight", b)
return cls(x=x, y=y, z=z, index=index, weight=weight)
@dataclass @dataclass
class Vec4(Vec3): class Vec4(Vec3):
w: float w: float
@ -108,6 +146,7 @@ class AnmKeys:
@with_offset @with_offset
def create(cls, b): def create(cls, b):
keys_type = read_byte_and_print("ANM_Keys->Type", b) keys_type = read_byte_and_print("ANM_Keys->Type", b)
debug_print("ANM_KeyType: %s" % ANM_KeyType.get_name(keys_type))
ort_before = read_byte_and_print("ANM_Keys->ORTBefore", b) ort_before = read_byte_and_print("ANM_Keys->ORTBefore", b)
ort_after = read_byte_and_print("ANM_Keys->ORTAfter", b) ort_after = read_byte_and_print("ANM_Keys->ORTAfter", b)
flags = read_byte_and_print("ANM_Keys->Flags", b) flags = read_byte_and_print("ANM_Keys->Flags", b)
@ -161,7 +200,7 @@ class AnmObj:
class AnmCluster: class AnmCluster:
custom: int = field(repr=False) custom: int = field(repr=False)
obj: Union[AnmObj, None] = field(repr=False) obj: Union[AnmObj, None] = field(repr=False)
handle: int = field(repr=False) handle: int
bounding_box: List[Vec3] = field(repr=False) # size 2 bounding_box: List[Vec3] = field(repr=False) # size 2
num_vertices: int num_vertices: int
vertices: List[Vec3Indexed] = field(repr=False) vertices: List[Vec3Indexed] = field(repr=False)
@ -196,58 +235,101 @@ class NamDictionnary:
def create(cls, b, depth=1): def create(cls, b, depth=1):
r = {} r = {}
read_int_and_print("NAM_Dictionnary->pMemGroup", b) read_int_and_print("NAM_Dictionnary->MemGroup", b) # MAP_MemGroup*
namemapper = read_int_and_print("NAM_Dictionnary->pNameMapper", b) namemapper = read_int_and_print("NAM_Dictionnary->pNameMapper", b) # MAP_Mapper*
flags = read_int_and_print("NAM_Dictionnary->Flags", b) flags = read_int_and_print("NAM_Dictionnary->Flags", b) # X_BOOL
print("Flags: NAM_FlagHasHandles: %s, NAM_FlagCaseInsensitive: %s" % ( debug_print("Flags: NAM_FlagHasHandles: %s, NAM_FlagCaseInsensitive: %s" % (
flags & NAM_FlagHasHandles, flags & NAM_FlagCaseInsensitive)) flags & NAM_FlagHasHandles, flags & NAM_FlagCaseInsensitive))
read_short_and_print("NAM_Dictionnary->NameHandle", b) read_short_and_print("NAM_Dictionnary->NameHandle", b) # NAM_NameHandle -> X_UINT16
b.seek(namemapper) b.seek(namemapper)
read_int_and_print("pMemGroup", b) read_int_and_print("MemGroup*", b) # MAP_MemGroup*
read_int_and_print("MemBaseSize", b) read_int_and_print("MemBaseSize", b) # X_UINT
read_int_and_print("MemBlocks", b) read_int_and_print("MemBlocks", b) # X_UINT
read_int_and_print("MemSize", b) read_int_and_print("MemSize", b) # X_UINT
read_int_and_print("MemFree", b) read_int_and_print("MemFree", b) # X_UINT
read_int_and_print("pMem", b) read_int_and_print("Mem*", b) # MAP_Mapping*
read_int_and_print("MaxMappings", b) read_int_and_print("MaxMappings", b) # X_UINT
totalmappings = read_int_and_print("TotalMappings", b) totalmappings = read_int_and_print("TotalMappings", b) # X_UINT
read_int_and_print("TotalKeys", b) total_keys = read_int_and_print("TotalKeys", b) # X_UINT
p_key_contexts = read_int_and_print("pKeyContexts", b) p_key_contexts = read_int_and_print("KeyContexts*", b) # MAP_KeyContext*
b.seek(p_key_contexts) for key in range(total_keys):
_ = read_int_and_print("\tSize", b) key_context_size = [
read_int_and_print("\tpCallback", b) 4, # X_UINT Size
read_int_and_print("\tIndex", b) 4, # MAP_BinSearchCallback* Callback
read_int_and_print("\tTest", b) 4, # X_UINT Index
p_lut = read_int_and_print("pLUT", b) 4, # X_INT Test
read_int_and_print("Reserve", b) 4, # MAP_Mapping **LUT
read_int_and_print("pData", b) 4, # X_UINT Reserve
4, # X_VOID *Data
]
key_context_size = sum(key_context_size)
key_context_begin = p_key_contexts + key * key_context_size
debug_print("getting keys for type %s" % key)
b.seek(key_context_begin) # NAM_Dictionnary.KeyContexts[KEY]
# we are now in the KeyContext for type "key"
debug_print("Current keycontext: %s" % b.tell())
keytype_size = read_int_and_print("\tSize", b) # MAP_BinSearchContext -> X_UINT
read_int_and_print("\t\tpCallback", b) # MAP_BinSearchContext -> MAP_BinSearchCallback*
read_int_and_print("\t\tIndex", b) # MAP_BinSearchContext -> X_UINT
read_int_and_print("\t\tTest", b) # MAP_BinSearchContext -> X_INT
p_lut = read_int_and_print("\tMAP_Mapping** LUT", b) # MAP_Mapping**
read_int_and_print("\tReserve", b) # X_UINT
read_int_and_print("\tpData", b) # X_VOID*
for i in range(totalmappings): # i=0; tant que l'addresse que renvoie MAP_MappingGet (Dictionary->NameMapper, i, NAM_STRING_KEY) != 0; i++
b.seek(p_lut) # mappingget: return Mapper->KeyContexts[Key].LUT[Index];
pindx = read_int(b)
for _ in range(i): """
pindx = read_int(b)
b.seek(pindx) """
if not flags & NAM_FlagHasHandles: key_index = 0
read_short_and_print("pPtr", b) while True:
read_short_and_print("pPtr2", b) if key_index >= totalmappings:
val = read_int_and_print("Value", b) break
b.seek(p_lut + key_index * 4) # NAM_Dictionnary.KeyContexts[KEY].LUT[INDEX] -> LA je suis au MAP_Mapping* == NAM_Name*
debug_print("MAP_Mapping*")
addr_begin_mapping = read_int_and_print("MAP_Mapping*", b) # MAP_MapIndex* -> X_UINT16*
if addr_begin_mapping == 0:
break
b.seek(addr_begin_mapping)
# we are now in MAP_Mapping == NAM_Name header
"""
struct _MAP_Mapping
{
MAP_MapIndex TotalSize;
};
typedef X_UINT16 MAP_MapIndex;
"""
debug_print("MAP_Mapping")
read_short_and_print("TotalSize", b) # MAP_MapIndex* -> X_UINT16*
after_mapping = b.tell()
b.seek(after_mapping + key * 2)
debug_print("MAP_MapIndex at %s" % b.tell())
index = read_short_and_print("MAP_MapIndex value", b)
b.seek(addr_begin_mapping+index)
debug_print("STRINGDATA* OU HANDLEDATA* at %s" % b.tell())
# pdata = read_int_and_print("Data*", b)
# b.seek(pdata)
if key == NAM_STRING_KEY:
val = read_int_and_print("ValueOrCustom", b) # X_INT or X_VOID*
name = read_string_until_none_and_print("name", b) name = read_string_until_none_and_print("name", b)
if depth > 1: if flags & NAM_FlagHasHandles:
r[key_index] = {"Name": name}
elif depth > 1:
item = NamDictionnary.create(cls, b, depth - 1, offset=val) item = NamDictionnary.create(cls, b, depth - 1, offset=val)
r[name] = item
else: else:
item = cls.create(b, offset=val) item = cls.create(b, offset=val)
r[name] = item r[name] = item
else: if key == NAM_HANDLE_KEY:
refcnt = read_short_and_print("RefCnt", b) read_short_and_print("RefCnt", b) # NAM_NameRefCnt == X_UINT16
handle = read_short_and_print("Handle", b) handle = read_short_and_print("Handle", b) # NAM_NameHandle == X_UINT16
read_int_and_print("Test", b) r[key_index]["Handle"] = handle
read_int_and_print("Test", b) key_index += 1
bone_name = read_string_until_none_and_print("BoneName", b) debug_print("Key_index=%s" % key_index)
r[str(i)] = {"BoneName": bone_name}
return r return r
@ -438,11 +520,15 @@ class AnmSkin:
@staticmethod @staticmethod
def link(nodedict, defaultobjtree): def link(nodedict, defaultobjtree):
m = {}
for node_index in nodedict:
node = nodedict[node_index]
m[node["Handle"]] = node["Name"]
for obj_name, obj in defaultobjtree.items(): for obj_name, obj in defaultobjtree.items():
for cluster in obj.clusters: for cluster in obj.clusters:
cluster_handle = cluster.handle cluster.bone_name = m[cluster.handle]
k = str(cluster_handle - 1)
cluster.bone_name = nodedict[k]["BoneName"]
@classmethod @classmethod
@with_offset @with_offset
@ -463,7 +549,7 @@ class AnmSkin:
treedict = NamDictionnary.create(AnmObj, b, depth=2, offset=tree) treedict = NamDictionnary.create(AnmObj, b, depth=2, offset=tree)
defaultobjdict = NamDictionnary.create(AnmObj, b, offset=defaultobj) defaultobjdict = NamDictionnary.create(AnmObj, b, offset=defaultobj)
# AnmSkin.link(nodedict, defaultobjdict) AnmSkin.link(nodedict, defaultobjdict)
return cls(custom=custom, file_version=version, image=image, memgroup=memgroup, node_dictionnary=nodedict, return cls(custom=custom, file_version=version, image=image, memgroup=memgroup, node_dictionnary=nodedict,
tree_dictionnary=treedict, default_obj_dictionnary=defaultobjdict, total_number_trees=num_trees, tree_dictionnary=treedict, default_obj_dictionnary=defaultobjdict, total_number_trees=num_trees,
total_number_objs=num_objs, total_number_clusters=num_clusters, name=name) total_number_objs=num_objs, total_number_clusters=num_clusters, name=name)

View File

@ -391,7 +391,6 @@ def convert_obj(path):
output = "%s/%s.gbs" % (os.path.dirname(os.path.abspath(path)), os.path.basename(path)) output = "%s/%s.gbs" % (os.path.dirname(os.path.abspath(path)), os.path.basename(path))
print("Done! Output: %s" % output) print("Done! Output: %s" % output)
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("path") parser.add_argument("path")

173
ps2_read.py Normal file
View File

@ -0,0 +1,173 @@
import argparse
import os.path
from lib.fileutils import *
from lib.packet import Packet
def ftell(fp):
return hex(fp.tell())
def read_bin(_input: str, _output: str):
with open(_input, "rb") as fp:
# read 0x30 bytes
fileversion = read_int(fp)
render_data = read_int(fp)
texturesmemory_size = read_int(fp)
unk2_size = read_int(fp)
objects_size = read_int(fp)
some_bytes = read_int(fp)
fx_size = read_int(fp)
world_size = read_int(fp)
huds_size = read_int(fp)
worldflags_size = read_int(fp)
unk = read_int(fp)
unk = read_int(fp)
print("[%s] Game is %s bytes" % (ftell(fp), render_data))
read_bytes(fp, render_data)
print("[%s] World is %s bytes..." % (ftell(fp), world_size))
read_bytes(fp, world_size)
print("[%s] Texturesmemory is %s bytes..." % (ftell(fp), texturesmemory_size))
texturesmemory = read_bytes(fp, texturesmemory_size)
print("[%s] FX %s bytes..." % (ftell(fp), fx_size))
read_bytes(fp, fx_size)
print("[%s] unk2 %s bytes..." % (ftell(fp), unk2_size))
read_bytes(fp, unk2_size)
print("[%s] worldflags %s bytes..." % (ftell(fp), worldflags_size))
read_bytes(fp, worldflags_size)
print("[%s] huds %s bytes..." % (ftell(fp), huds_size))
read_bytes(fp, huds_size)
print("[%s] objects %s bytes..." % (ftell(fp), objects_size))
objects = read_bytes(fp, objects_size)
print("[%s] textures_attributes? %s bytes..." % (ftell(fp), some_bytes))
# textures_attributes = read_bytes(fp, some_bytes)
first_int = read_int(fp)
print("[%s] First int: %s" % (ftell(fp), first_int))
second_int = read_int(fp)
print("Second int: %s" % second_int)
with open(_output, "wb") as fp:
fp.write(texturesmemory)
print("Done")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("input_bin", help="PS2 bin file to read")
parser.add_argument("output_file", help="output bin for objects")
args = parser.parse_args()
read_bin(args.input_bin, args.output_file)
def read_textures(file_input):
print("Reading %s" % file_input)
curr = 0
with open(file_input, "rb") as fp:
content = fp.read()
while curr < len(content):
byte_index = content.find(b"END-OF-FIL", curr)
if byte_index != -1:
byte_index += 0xc
print("content: %s (%s)" % (content[byte_index], hex(content[byte_index])))
while content[byte_index] == 0x2a or content[byte_index] == 0x00:
byte_index += 1
print("found texture at %s: %s %s" % (hex(byte_index), hex(content[byte_index]), hex(content[byte_index+1])))
# after CLUTS
byte_index += 0x20
p = Packet(content)
p.seek(byte_index)
# psm = p.get_short()
psm = content[byte_index]
psm = psm << 8 & 0x3f
d_comp = p.get_long()
d_comp = p.get_long()
is_compressed = d_comp >> 0x12 & 0xf
compare = d_comp >> 0xe & 0xf
if is_compressed != compare:
print(hex(d_comp))
new_compare = compare - is_compressed
if new_compare <= 0:
print("ERROOOOOOOOOR")
if new_compare == 1:
decomp_bytes = 2
elif new_compare == 2:
decomp_bytes = 3
elif new_compare == 3:
decomp_bytes = 4
else:
print("ERROOOOOOOOOOR: %s" % new_compare)
print("decomp_bytes: %s" % decomp_bytes)
print("psm: %s -> %s (compressed: %s, compare: %s)" % (psm, get_pixel_format(psm), is_compressed, compare))
curr = byte_index+1
else:
curr = len(content)
def get_pixel_format(psm) -> str:
_data = {
0: "PSMCT32",
1: "PSMCT24",
2: "PSMCT16",
10: "PSMCT16S",
19: "PSMT8",
20: "PSMT4",
27: "PSMT8H",
26: "PSMT4HL",
44: "PSMT4HH",
48: "PSMZ32",
49: "PSMZ24",
50: "PSMZ16",
58: "PSMZ16S",
}
return _data.get(psm, "UNKNOWN")
def read_sizes():
print("=====================================================================================")
print("=====================================================================================")
print("=====================================================================================")
print("=====================================================================================")
print("=====================================================================================")
print("=====================================================================================")
print("=====================================================================================")
parser = argparse.ArgumentParser()
parser.add_argument("input_bin", help="PS2 bin file to read")
args = parser.parse_args()
all_num = 0
if os.path.isdir(args.input_bin):
for currdir, dirs, files in os.walk(args.input_bin):
for file in files:
if file.lower().endswith(".bin"):
read_textures(currdir+"/"+file)
else:
read_textures(args.input_bin)
print("textures:%s" % all_num)
if __name__ == '__main__':
# main()
read_sizes()

View File

@ -49,9 +49,9 @@ def extract_gzp(gzp_file, extensions, output_dir):
logger.info("Checksum OK") logger.info("Checksum OK")
meta_info_offset = read_int(gzp_fp) meta_info_offset = read_int(gzp_fp)
gzp_fp.seek(meta_info_offset) gzp_fp.seek(meta_info_offset)
unk = read_int(gzp_fp) unk = read_int(gzp_fp)
print(unk)
entries_count = read_int(gzp_fp) entries_count = read_int(gzp_fp)
if entries_count == 0: if entries_count == 0:
@ -69,6 +69,9 @@ def extract_gzp(gzp_file, extensions, output_dir):
compression = read_byte(gzp_fp) # compression: 1 if compressed else 0 compression = read_byte(gzp_fp) # compression: 1 if compressed else 0
name_length = read_byte(gzp_fp) name_length = read_byte(gzp_fp)
name = read_bytes(gzp_fp, name_length).decode("utf8").strip('\x00') name = read_bytes(gzp_fp, name_length).decode("utf8").strip('\x00')
file_without_ext = ".".join(name.split(".")[0:-1])
ext = name.split(".")[-1]
logger.info(name + " compression: " + str(compression) + ", filesize: " + sizeof_fmt( logger.info(name + " compression: " + str(compression) + ", filesize: " + sizeof_fmt(
original_size) + ", start: " + str(content_offset)) original_size) + ", start: " + str(content_offset))
@ -91,6 +94,7 @@ def extract_gzp(gzp_file, extensions, output_dir):
logger.info("File is compressed, decompressing it") logger.info("File is compressed, decompressing it")
buffer = decompress(buffer, original_size) buffer = decompress(buffer, original_size)
name = "%s.%s.%s" % (file_without_ext, os.path.basename(gzp_file), ext)
logger.info("Writing file " + name) logger.info("Writing file " + name)
if not os.path.exists(output_dir): if not os.path.exists(output_dir):
os.mkdir(output_dir) os.mkdir(output_dir)

61
skn.py
View File

@ -1,14 +1,17 @@
from lib.skn_anm import * from lib.skn_anm import *
from lib.gbs import *
def read_skn(filepath) -> AnmSkin: def read_skn(filepath) -> AnmSkin:
with open(filepath, "rb") as fp: with open(filepath, "rb") as fp:
init_filetrack(os.fstat(fp.fileno()).st_size)
r = AnmFile.parse(fp, AnmSkin) r = AnmFile.parse(fp, AnmSkin)
print("Read %s%% of file" % stat_track())
return r return r
def main(): def test():
f = ["/home/tasty/Projects/gck-map-extract-objects/anm_skn/rp_l0.skn"] f = ["/home/tasty/Projects/gck-map-extract-objects/anm_skn/Verm_l0.skn"]
my_vertex = Vec3(-0.927200, 0.099500, 3.108000) my_vertex = Vec3(-0.927200, 0.099500, 3.108000)
mini_dist_v = Vec3(0, 0, 0) mini_dist_v = Vec3(0, 0, 0)
for p in f: for p in f:
@ -24,5 +27,59 @@ def main():
print("%s: dist=%s" % (mini_dist_v, mini_dist_v.distance(my_vertex))) print("%s: dist=%s" % (mini_dist_v, mini_dist_v.distance(my_vertex)))
def print_vertices():
skn = read_skn("/home/tasty/Projects/gck-map-extract-objects/test/rp_l0.apatch.gzp.skn")
for obj_name in skn.default_obj_dictionnary:
obj = skn.default_obj_dictionnary[obj_name]
for cluster in obj.clusters:
for vertex in cluster.vertices:
print("v %s %s %s" % (vertex.x, vertex.y, vertex.z))
def link_gbs_skn():
basepath = "/home/tasty/Projects/gck-map-extract-objects/test/rp_l0.apatch.gzp"
skn = read_skn(basepath+".skn")
gbs = GbsData()
gbs.read(basepath+".gbs")
vbase = 0
with open("test.obj", "w") as fp:
for obj_name, obj in skn.default_obj_dictionnary.items():
for cluster in obj.clusters:
for vertex in cluster.vertices:
base_vertex_i = vertex.index + vbase
base_vertex = gbs.vertices[base_vertex_i]
new_vertex = Vec3(base_vertex.x + vertex.x, base_vertex.y + vertex.y, base_vertex.z + vertex.z)
fp.write("v %s %s %s\n" % (new_vertex.x, new_vertex.y, new_vertex.z))
vbase += obj.total_number_vertices
def main():
skn = read_skn("/home/tasty/Projects/gck-map-extract-objects/test/rp_l0.apatch.gzp.skn")
pprint(skn)
vbase = 0
for tree in skn.tree_dictionnary:
for obj_name in skn.tree_dictionnary[tree]:
print(obj_name)
obj = skn.tree_dictionnary[tree][obj_name]
for cluster in obj.clusters:
if cluster.num_vertices <= 0:
print("NO VERTICES")
for vertex in cluster.vertices:
pass
# print("vertex %s -> %s" % (vertex.index, vertex.index + vbase))
vbase += obj.total_number_vertices
with open("test.obj", "w") as fp:
vbase = 0
for obj_name, obj in skn.default_obj_dictionnary.items():
for cluster in obj.clusters:
# fp.write("name %s %s %s %s %s %s %s\n" % (cluster.bone_name.strip().replace(" ", ""), cluster.bounding_box[0].x, cluster.bounding_box[0].y, cluster.bounding_box[0].z, cluster.bounding_box[1].x, cluster.bounding_box[1].y, cluster.bounding_box[1].z))
for vertex in cluster.vertices:
fp.write("index %s\n" % (vertex.index + vbase))
# fp.write("offset %s %s %s\n" % (vertex.x, vertex.y, vertex.z))
vbase += obj.total_number_vertices
if __name__ == "__main__": if __name__ == "__main__":
main() main()