Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fields to GhostSampleRecord and CGameGhost #18

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pygbx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pygbx.headers import CGameHeader, CGameCtnCollectorList, CollectorStock, MapBlock, Vector3, CGameChallenge, CGameBlockItem, CGameWaypointSpecialProperty, CGameCommon, CGameReplayRecord, CGameGhost, CGameCtnGhost, ControlEntry, GhostSampleRecord
from pygbx.headers import CGameHeader, CGameCtnCollectorList, CollectorStock, MapBlock, Vector3, Quaternion, CGameChallenge, CGameBlockItem, CGameWaypointSpecialProperty, CGameCommon, CGameReplayRecord, CGameGhost, CGameCtnGhost, ControlEntry, GhostSampleRecord
from pygbx.bytereader import ByteReader
from pygbx.stadium_blocks import STADIUM_BLOCKS
from pygbx.canyon_blocks import CANYON_BLOCKS
Expand Down
44 changes: 43 additions & 1 deletion pygbx/bytereader.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import logging
import math
import struct
from io import IOBase
from pygbx.headers import Vector3
from pygbx.headers import Vector3, Quaternion
from os import SEEK_END

class PositionInfo(object):
Expand Down Expand Up @@ -247,3 +248,44 @@ def read_string_lookback(self):
if inp - 1 >= len(self.stored_strings):
return ''
return self.stored_strings[inp - 1]

def read_quat_6(self):
"""Reads a quaternion from the buffer, the quaternion is stored in 6 bytes.
Returns:
the quaternion read from the buffer
"""
angle = self.read_uint16() * math.pi / 65535.0
axis = self.read_vec3_unit_4()
vec = axis * math.sin(angle)
return Quaternion(math.cos(angle), vec.x, vec.y, vec.z)

def read_vec3_unit_4(self) -> Vector3:
"""Reads a Vector3 from the buffer, the vector is stored in 4 bytes
Returns:
the vector read from the buffer
"""
axis_heading = self.read_int16() * math.pi / 32767.0
axis_pitch = self.read_int16() * (math.pi / 2.0) / 32767.0
return Vector3(math.cos(axis_heading) * math.cos(axis_pitch),
math.sin(axis_heading) * math.cos(axis_pitch),
math.sin(axis_pitch))

def read_vec3_4(self) -> Vector3:
"""Reads a unit Vector3 from the buffer, the vector is stored in 4 bytes
Returns:
the vector read from the buffer
"""
mag16 = self.read_int16()
mag = 0 if mag16 == -32768 else math.exp(mag16 / 1000.0)
return mag * self.read_vec3_unit_2()

def read_vec3_unit_2(self) -> Vector3:
"""Reads a unit Vector3 from the buffer, the vector is stored in 2 bytes
Returns:
the vector read from the buffer
"""
axis_heading = self.read_byte() * math.pi / 127.0
axis_pitch = self.read_byte() * (math.pi / 2.0) / 127.0
return Vector3(math.cos(axis_heading) * math.cos(axis_pitch),
math.sin(axis_heading) * math.cos(axis_pitch),
math.sin(axis_pitch))
57 changes: 38 additions & 19 deletions pygbx/gbx.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import math
from enum import IntEnum

import lzo
Expand Down Expand Up @@ -744,9 +745,11 @@ def read_ghost(game_class, bp):
data = zlib.decompress(comp_data, 0, uncomp_sz)

gr = ByteReader(data)
gr.skip(3 * 4)
game_class.saved_mobil_class_id = gr.read_uint32()
game_class.is_fixed_time_step = True if gr.read_int32() else False
game_class.U01 = gr.read_int32()
game_class.sample_period = gr.read_uint32()
gr.skip(1 * 4)
game_class.version = gr.read_int32()

sample_data_sz = gr.read_uint32()
sample_data_pos = gr.pos
Expand All @@ -769,23 +772,39 @@ def read_ghost(game_class, bp):
gr.pos = sample_data_pos
gr.skip(fso)
for i in range(num_samples):
sample_pos = gr.pos

record = headers.GhostSampleRecord(
gr.read_vec3(), gr.read_uint16(), gr.read_int16(),
gr.read_int16(), gr.read_int16(),
gr.read_int8(), gr.read_int8())

len_sizes = len(sample_sizes)
if i >= len_sizes:
if len_sizes >= 1:
sample_sz = sample_sizes[0]
else:
sample_sz = 0
else:
sample_sz = sample_sizes[i]
gr.read_vec3(), # position
gr.read_quat_6(), # rotation
gr.read_vec3_4(), # velocity
gr.read_vec3_4(), # angular velocity
(gr.read_uint16() / 65535.0 * 11000.0 - 1000.0) * 3.6, # speed forward
gr.read_uint16() / 65535.0 * 2000.0 - 1000.0, # speed sideward
gr.read_uint16() / 65535.0 * 30000.0, # rpm
gr.read_uint16() / 65535.0 * 1608.495, # fl_wheel_rotation
gr.read_uint16() / 65535.0 * 1608.495, # fr_wheel_rotation
gr.read_uint16() / 65535.0 * 1608.495, # rr_wheel_rotation
gr.read_uint16() / 65535.0 * 1608.495, # rl_wheel_rotation
gr.read_byte() / 255.0 * 2.0 - 1.0, # steer
gr.read_byte() / 255.0, # gas
gr.read_byte() / 255.0, # brake
gr.read_byte(), # u11
gr.read_byte(), # u12
gr.read_byte() / 255.0 * 2.0 - 1.0, # u13
gr.read_byte() / 255.0 * 2.0 - 1.0, # u14
gr.read_byte() / 255.0, # turbo_strength
gr.read_byte() / 255.0 * math.pi * 2.0 - math.pi, # steer_front
gr.read_byte() / 255.0 * 4.0 - 2.0, # fl_dampen_len
gr.read_byte(), # fl_ground_contact_material
gr.read_byte() / 255.0 * 4.0 - 2.0, # fr_dampen_len
gr.read_byte(), # fr_ground_contact_material
gr.read_byte() / 255.0 * 4.0 - 2.0, # rr_dampen_len
gr.read_byte(), # rr_ground_contact_material
gr.read_byte() / 255.0 * 4.0 - 2.0, # rl_dampen_len
gr.read_byte(), # rl_ground_contact_material
gr.read_byte(), # u25
0 if (gr.read_byte() & 0x40) == 0 else 1, # u26
gr.read_byte(), # u27
gr.read_byte() / 255.0 # dirt_blend
)

record.raw_data = gr.read(sample_sz - (gr.pos - sample_pos))
# import binascii
# print(f'{i} {binascii.hexlify(record.raw_data)}')
game_class.records.append(record)
90 changes: 83 additions & 7 deletions pygbx/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ def __add__(self, other):
def __sub__(self, other):
return Vector3(self.x - other.x, self.y - other.y, self.z - other.z)

def __mul__(self, other):
return Vector3(self.x * other, self.y * other, self.z * other)

def __rmul__(self, other):
return Vector3(self.x * other, self.y * other, self.z * other)

def __getitem__(self, key):
if key == 0:
return self.x
Expand All @@ -89,6 +95,40 @@ def as_array(self):
return [self.x, self.y, self.z]


class Quaternion(object):
"""The Quaternion class represents a 4D quaternion, usually read directly from the GBX file."""
def __init__(self, w=0, x=0, y=0, z=0):
self.w = w
self.x = x
self.y = y
self.z = z

def __getitem__(self, key):
if key == 0:
return self.w
elif key == 1:
return self.x
elif key == 2:
return self.y
elif key == 3:
return self.z
return None

def __eq__(self, other):
if isinstance(other, list):
return self.w == other[0] and self.x == other[1] and self.y == other[2] and self.z == other[3]

return self.w == other.w and self.x == other.x and self.y == other.y and self.z == other.z

def as_array(self):
"""Returns the quaternion as a list.

Returns:
the quaternion as a list made of 4 elements
"""
return [self.w, self.x, self.y, self.z]


class CGameChallenge(CGameHeader):
"""A header that contains data related to the CGameChallenge class."""
def __init__(self, id):
Expand Down Expand Up @@ -152,7 +192,11 @@ class CGameGhost(CGameHeader):
def __init__(self, id):
self.id = id
self.records = []
self.saved_mobil_class_id = 0
self.is_fixed_time_step = False
self.U01 = 0
self.sample_period = None
self.version = 0


class CGameCtnGhost(CGameGhost):
Expand Down Expand Up @@ -200,14 +244,46 @@ class GhostSampleRecord(object):
BLOCK_SIZE_XZ = 32
BLOCK_SIZE_Y = 8

def __init__(self, position, angle, axis_heading, axis_pitch, speed, vel_heading, vel_pitch):
def __init__(self, position, rotation, velocity, angular_velocity, speed_forward, speed_sidewards,
rpm, fl_wheel_rotation, fr_wheel_rotation, rr_wheel_rotation, rl_wheel_rotation,
steer, gas, brake, u11, u12, u13, u14, turbo_strength, steer_front,
fl_dampen_len, fl_ground_contact_material, fr_dampen_len, fr_ground_contact_material,
rr_dampen_len, rr_ground_contact_material, rl_dampen_len, rl_ground_contact_material,
u25, u26, u27, dirt_blend):
self.position = position
self.angle = angle
self.axis_heading = axis_heading
self.axis_pitch = axis_pitch
self.speed = speed
self.vel_heading = vel_heading
self.vel_pitch = vel_pitch
self.rotation = rotation
self.velocity = velocity # in m/s
self.angular_velocity = angular_velocity
self.speed_forward = speed_forward
self.speed_sidewards = speed_sidewards
self.rpm = rpm
self.fl_wheel_rotation = fl_wheel_rotation
self.fr_wheel_rotation = fr_wheel_rotation
self.rr_wheel_rotation = rr_wheel_rotation
self.rl_wheel_rotation = rl_wheel_rotation
self.steer = steer
self.gas = gas
self.brake = brake
self.u11 = u11
self.u12 = u12
self.u13 = u13
self.u14 = u14
self.turbo_strength = turbo_strength
self.steer_front = steer_front
self.fl_dampen_len = fl_dampen_len
self.fl_ground_contact_material = fl_ground_contact_material
self.fr_dampen_len = fr_dampen_len
self.fr_ground_contact_material = fr_ground_contact_material
self.rr_dampen_len = rr_dampen_len
self.rr_ground_contact_material = rr_ground_contact_material
self.rl_dampen_len = rl_dampen_len
self.rl_ground_contact_material = rl_ground_contact_material
self.u25 = u25
self.horn = self.u25 & 3
self.is_turbo = (self.u25 & 128) != 0
self.u26 = u26
self.u27 = u27
self.dirt_blend = dirt_blend

@property
def display_speed(self):
Expand Down