Read client signed int32 zone fields as signed

Use read_long/write_long for every CZone value the client loads with
ResStream_ReadInt32: basic zone level/room/dungeon indexes (default -1),
zone width/material thresholds and indexes, sea level index, pattern
and texture ints, terrain octaves and seeds, terrain object ints, goal
delay and points, mobile group size/level/exp, shopkeeper gold, buy and
sell list values, prop light ints, city prop rank/funds and container
lock/bar/trap values. Hash, enum, flag and count fields stay unsigned.

The binary is unchanged; on the sample cache JSON only changes from
4294967295 to -1 in the five basic zone index fields. Older dumps still
pack because write_long accepts the unsigned form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MagicBotandClaude Opus 5.5 committed 2026-10-24 11:02:34 -04:00
1 parent 2e1cef3da8
commit 876484947c
9 files changed
+80 -80

No files matched your search

+20 -20
View File
@@ -27,8 +27,8 @@ class ArcPropInfo:
if self.prop_is_light_data:
self.prop_light_color = [stream.read_float() for _ in range(4)]
self.prop_light_radius = stream.read_float()
self.prop_light_flicker = stream.read_dword()
self.prop_light_cube_map = stream.read_dword()
self.prop_light_flicker = stream.read_long()
self.prop_light_cube_map = stream.read_long()
num_content_props = stream.read_dword()
self.prop_content_props = [ArcProp() for _ in range(num_content_props)]
for prop in self.prop_content_props:
@@ -60,8 +60,8 @@ class ArcPropInfo:
for i in range(4):
stream.write_float(self.prop_light_color[i])
stream.write_float(self.prop_light_radius)
stream.write_dword(self.prop_light_flicker)
stream.write_dword(self.prop_light_cube_map)
stream.write_long(self.prop_light_flicker)
stream.write_long(self.prop_light_cube_map)
stream.write_dword(len(self.prop_content_props))
for prop in self.prop_content_props:
@@ -154,15 +154,15 @@ class ArcCityPropInfo(ArcPropInfo):
super().load_binary(stream)
self.city_prop_gm_guild = stream.read_dword()
self.city_prop_gm_nation = stream.read_dword()
self.city_prop_asset_rank = stream.read_dword()
self.city_prop_asset_funds = stream.read_dword()
self.city_prop_asset_rank = stream.read_long()
self.city_prop_asset_funds = stream.read_long()
def save_binary(self, stream: ResStream):
super().save_binary(stream)
stream.write_dword(self.city_prop_gm_guild)
stream.write_dword(self.city_prop_gm_nation)
stream.write_dword(self.city_prop_asset_rank)
stream.write_dword(self.city_prop_asset_funds)
stream.write_long(self.city_prop_asset_rank)
stream.write_long(self.city_prop_asset_funds)
def save_json(self):
data = super().save_json()
@@ -183,12 +183,12 @@ class ArcCityPropInfo(ArcPropInfo):
class ArcContainerInfo(ArcPropInfo):
def load_binary(self, stream: ResStream):
super().load_binary(stream)
self.container_locked = stream.read_dword()
self.container_locked_rank = stream.read_dword()
self.container_barred = stream.read_dword()
self.container_barred_rank = stream.read_dword()
self.container_trapped = stream.read_dword()
self.container_trapped_rank = stream.read_dword()
self.container_locked = stream.read_long()
self.container_locked_rank = stream.read_long()
self.container_barred = stream.read_long()
self.container_barred_rank = stream.read_long()
self.container_trapped = stream.read_long()
self.container_trapped_rank = stream.read_long()
num_booties = stream.read_dword()
self.container_booties = [Inventory() for _ in range(num_booties)]
for inventory in self.container_booties:
@@ -196,12 +196,12 @@ class ArcContainerInfo(ArcPropInfo):
def save_binary(self, stream: ResStream):
super().save_binary(stream)
stream.write_dword(self.container_locked)
stream.write_dword(self.container_locked_rank)
stream.write_dword(self.container_barred)
stream.write_dword(self.container_barred_rank)
stream.write_dword(self.container_trapped)
stream.write_dword(self.container_trapped_rank)
stream.write_long(self.container_locked)
stream.write_long(self.container_locked_rank)
stream.write_long(self.container_barred)
stream.write_long(self.container_barred_rank)
stream.write_long(self.container_trapped)
stream.write_long(self.container_trapped_rank)
stream.write_dword(len(self.container_booties))
for inventory in self.container_booties:
inventory.save_binary(stream)
+6 -6
View File
@@ -21,12 +21,12 @@ class ArcTerrainGen:
self.terrain_max_z = stream.read_float()
self.terrain_h = stream.read_float()
self.terrain_lacunarity = stream.read_float()
self.terrain_octaves = stream.read_dword()
self.terrain_octaves = stream.read_long()
self.terrain_offset = stream.read_float()
self.terrain_gain = stream.read_float()
self.terrain_seeds = [
stream.read_dword(),
stream.read_dword(),
stream.read_long(),
stream.read_long(),
]
if self.terrain_type == 4:
self.terrain_max_x = stream.read_float()
@@ -54,11 +54,11 @@ class ArcTerrainGen:
stream.write_float(self.terrain_max_z)
stream.write_float(self.terrain_h)
stream.write_float(self.terrain_lacunarity)
stream.write_dword(self.terrain_octaves)
stream.write_long(self.terrain_octaves)
stream.write_float(self.terrain_offset)
stream.write_float(self.terrain_gain)
stream.write_dword(self.terrain_seeds[0])
stream.write_dword(self.terrain_seeds[1])
stream.write_long(self.terrain_seeds[0])
stream.write_long(self.terrain_seeds[1])
if self.terrain_type == 4:
stream.write_float(self.terrain_max_x)
stream.write_float(self.terrain_max_z)
+18 -18
View File
@@ -29,10 +29,10 @@ class ArcZone:
self.zone_type = stream.read_dword()
self.zone_name = stream.read_string()
self.zone_custom_texture = stream.read_qword()
self.zone_width_threshold = stream.read_dword()
self.zone_material_threshold = stream.read_dword()
self.zone_max_width_index = stream.read_dword()
self.zone_max_material_index = stream.read_dword()
self.zone_width_threshold = stream.read_long()
self.zone_material_threshold = stream.read_long()
self.zone_max_width_index = stream.read_long()
self.zone_max_material_index = stream.read_long()
self.zone_custom_texture_wrap = stream.read_bool()
self.zone_peace_zone = stream.read_bool()
self.zone_guild_zone = stream.read_bool()
@@ -49,7 +49,7 @@ class ArcZone:
self.zone_tile_cord_type = stream.read_dword()
self.zone_tile_pattern_prob = stream.read_float()
self.zone_pattern_type = stream.read_dword()
self.zone_sea_level_index = stream.read_dword()
self.zone_sea_level_index = stream.read_long()
self.zone_sea_level = stream.read_float()
self.zone_sea_level_type = stream.read_dword()
self.zone_grad = stream.read_float()
@@ -74,7 +74,7 @@ class ArcZone:
num_patterns = stream.read_dword()
self.zone_patterns = [
[
stream.read_dword(),
stream.read_long(),
stream.read_float(),
] for _ in range(num_patterns)
]
@@ -132,9 +132,9 @@ class ArcZone:
stream.read_float(),
stream.read_float(),
stream.read_float(),
stream.read_dword(),
stream.read_dword(),
stream.read_dword(),
stream.read_long(),
stream.read_long(),
stream.read_long(),
] for _ in range(num_textures)
]
@@ -142,10 +142,10 @@ class ArcZone:
stream.write_dword(self.zone_type)
stream.write_string(self.zone_name)
stream.write_qword(self.zone_custom_texture)
stream.write_dword(self.zone_width_threshold)
stream.write_dword(self.zone_material_threshold)
stream.write_dword(self.zone_max_width_index)
stream.write_dword(self.zone_max_material_index)
stream.write_long(self.zone_width_threshold)
stream.write_long(self.zone_material_threshold)
stream.write_long(self.zone_max_width_index)
stream.write_long(self.zone_max_material_index)
stream.write_bool(self.zone_custom_texture_wrap)
stream.write_bool(self.zone_peace_zone)
stream.write_bool(self.zone_guild_zone)
@@ -162,7 +162,7 @@ class ArcZone:
stream.write_dword(self.zone_tile_cord_type)
stream.write_float(self.zone_tile_pattern_prob)
stream.write_dword(self.zone_pattern_type)
stream.write_dword(self.zone_sea_level_index)
stream.write_long(self.zone_sea_level_index)
stream.write_float(self.zone_sea_level)
stream.write_dword(self.zone_sea_level_type)
stream.write_float(self.zone_grad)
@@ -182,7 +182,7 @@ class ArcZone:
self.zone_terrain_gen.save_binary(stream)
stream.write_dword(len(self.zone_patterns))
for pattern in self.zone_patterns:
stream.write_dword(pattern[0])
stream.write_long(pattern[0])
stream.write_float(pattern[1])
stream.write_dword(len(self.zone_alts))
for alt in self.zone_alts:
@@ -234,9 +234,9 @@ class ArcZone:
stream.write_float(texture[3])
stream.write_float(texture[4])
stream.write_float(texture[5])
stream.write_dword(texture[6])
stream.write_dword(texture[7])
stream.write_dword(texture[8])
stream.write_long(texture[6])
stream.write_long(texture[7])
stream.write_long(texture[8])
def save_json(self):
data = OrderedDict()
+4 -4
View File
@@ -15,12 +15,12 @@ from arcane.util import ResStream
class ArcZoneGoalPoint:
def load_binary(self, stream: ResStream):
self.zone_goal_point_vectors = [stream.read_dword() for _ in range(6)]
self.zone_goal_point_vectors = [stream.read_long() for _ in range(6)]
self.zone_goal_point_location = stream.read_tuple()
def save_binary(self, stream: ResStream):
for i in range(6):
stream.write_dword(self.zone_goal_point_vectors[i])
stream.write_long(self.zone_goal_point_vectors[i])
stream.write_tuple(self.zone_goal_point_location)
def save_json(self):
@@ -39,7 +39,7 @@ class ArcZoneGoal:
self.zone_goal_type = stream.read_dword()
self.zone_goal_value = stream.read_dword()
self.zone_goal_name = stream.read_string()
self.zone_goal_delay = stream.read_dword()
self.zone_goal_delay = stream.read_long()
self.zone_goal_has_teleport_registry = stream.read_bool()
if self.zone_goal_has_teleport_registry:
self.zone_goal_teleport_registry = stream.read_dword()
@@ -53,7 +53,7 @@ class ArcZoneGoal:
stream.write_dword(self.zone_goal_type)
stream.write_dword(self.zone_goal_value)
stream.write_string(self.zone_goal_name)
stream.write_dword(self.zone_goal_delay)
stream.write_long(self.zone_goal_delay)
stream.write_bool(self.zone_goal_has_teleport_registry)
if self.zone_goal_has_teleport_registry:
stream.write_dword(self.zone_goal_teleport_registry)
@@ -17,16 +17,16 @@ class TerrainObjectInfo:
self.terrain_object_id = stream.read_qword()
self.terrain_object_h = stream.read_float()
self.terrain_object_lacunarity = stream.read_float()
self.terrain_object_octaves = stream.read_dword()
self.terrain_object_octaves = stream.read_long()
self.terrain_object_offset = stream.read_float()
self.terrain_object_gain = stream.read_float()
self.terrain_object_min_alt = stream.read_float()
self.terrain_object_max_alt = stream.read_float()
self.terrain_object_min_slope = stream.read_float()
self.terrain_object_max_slope = stream.read_float()
self.terrain_object_max_pop = stream.read_dword()
self.terrain_object_max_pop = stream.read_long()
self.terrain_object_is_fractal_pop = stream.read_bool()
self.terrain_object_unknown1 = stream.read_dword()
self.terrain_object_unknown1 = stream.read_long()
self.terrain_object_y_offset = stream.read_float()
self.terrain_object_pop_image_id = stream.read_qword()
self.terrain_object_image_min_y = stream.read_float()
@@ -36,16 +36,16 @@ class TerrainObjectInfo:
stream.write_qword(self.terrain_object_id)
stream.write_float(self.terrain_object_h)
stream.write_float(self.terrain_object_lacunarity)
stream.write_dword(self.terrain_object_octaves)
stream.write_long(self.terrain_object_octaves)
stream.write_float(self.terrain_object_offset)
stream.write_float(self.terrain_object_gain)
stream.write_float(self.terrain_object_min_alt)
stream.write_float(self.terrain_object_max_alt)
stream.write_float(self.terrain_object_min_slope)
stream.write_float(self.terrain_object_max_slope)
stream.write_dword(self.terrain_object_max_pop)
stream.write_long(self.terrain_object_max_pop)
stream.write_bool(self.terrain_object_is_fractal_pop)
stream.write_dword(self.terrain_object_unknown1)
stream.write_long(self.terrain_object_unknown1)
stream.write_float(self.terrain_object_y_offset)
stream.write_qword(self.terrain_object_pop_image_id)
stream.write_float(self.terrain_object_image_min_y)
@@ -22,12 +22,12 @@ class ArcBasicZoneObjectInfo:
self.basic_zone_y_rot = stream.read_float()
self.basic_zone_template_id = stream.read_qword()
self.basic_zone_unknown1 = stream.read_qword()
self.basic_zone_level_number = stream.read_dword()
self.basic_zone_room_number = stream.read_dword()
self.basic_zone_level_number = stream.read_long()
self.basic_zone_room_number = stream.read_long()
self.basic_zone_unknown2 = stream.read_qword()
self.basic_zone_dungeon_level = stream.read_dword()
self.basic_zone_dungeon_row = stream.read_dword()
self.basic_zone_dungeon_column = stream.read_dword()
self.basic_zone_dungeon_level = stream.read_long()
self.basic_zone_dungeon_row = stream.read_long()
self.basic_zone_dungeon_column = stream.read_long()
self.basic_zone_sparse_data = SparseData()
self.basic_zone_sparse_data.load_binary(stream)
@@ -39,12 +39,12 @@ class ArcBasicZoneObjectInfo:
stream.write_float(self.basic_zone_y_rot)
stream.write_qword(self.basic_zone_template_id)
stream.write_qword(self.basic_zone_unknown1)
stream.write_dword(self.basic_zone_level_number)
stream.write_dword(self.basic_zone_room_number)
stream.write_long(self.basic_zone_level_number)
stream.write_long(self.basic_zone_room_number)
stream.write_qword(self.basic_zone_unknown2)
stream.write_dword(self.basic_zone_dungeon_level)
stream.write_dword(self.basic_zone_dungeon_row)
stream.write_dword(self.basic_zone_dungeon_column)
stream.write_long(self.basic_zone_dungeon_level)
stream.write_long(self.basic_zone_dungeon_row)
stream.write_long(self.basic_zone_dungeon_column)
self.basic_zone_sparse_data.save_binary(stream)
def save_json(self):
@@ -23,12 +23,12 @@ class MerchantInfo(MobileInfo):
num_buy_list = stream.read_dword()
self.merchant_buy_list = [[
stream.read_qword(),
stream.read_dword(),
stream.read_long(),
] for _ in range(num_buy_list)]
num_sell_list = stream.read_dword()
self.merchant_sell_list = [[
stream.read_qword(),
stream.read_dword(),
stream.read_long(),
] for _ in range(num_sell_list)]
def save_binary(self, stream: ResStream):
@@ -43,11 +43,11 @@ class MerchantInfo(MobileInfo):
stream.write_dword(len(self.merchant_buy_list))
for l in self.merchant_buy_list:
stream.write_qword(l[0])
stream.write_dword(l[1])
stream.write_long(l[1])
stream.write_dword(len(self.merchant_sell_list))
for l in self.merchant_sell_list:
stream.write_qword(l[0])
stream.write_dword(l[1])
stream.write_long(l[1])
def save_json(self):
data = super().save_json()
@@ -22,12 +22,12 @@ class MobileInfo:
def load_binary(self, stream: ResStream):
self.mobile_base_zone = ArcBasicZoneObjectInfo()
self.mobile_base_zone.load_binary(stream)
self.mobile_number_in_group = stream.read_dword()
self.mobile_number_in_group = stream.read_long()
self.mobile_change_to_find = stream.read_float()
self.mobile_level = stream.read_dword()
self.mobile_level = stream.read_long()
self.mobile_behavior_file = stream.read_string()
self.mobile_dialog_override = stream.read_dword()
self.mobile_base_exp_reward = stream.read_dword()
self.mobile_base_exp_reward = stream.read_long()
self.mobile_tenacity = stream.read_float()
self.mobile_courage = stream.read_float()
self.mobile_has_group_tactics = stream.read_bool()
@@ -85,12 +85,12 @@ class MobileInfo:
def save_binary(self, stream: ResStream):
self.mobile_base_zone.save_binary(stream)
stream.write_dword(self.mobile_number_in_group)
stream.write_long(self.mobile_number_in_group)
stream.write_float(self.mobile_change_to_find)
stream.write_dword(self.mobile_level)
stream.write_long(self.mobile_level)
stream.write_string(self.mobile_behavior_file)
stream.write_dword(self.mobile_dialog_override)
stream.write_dword(self.mobile_base_exp_reward)
stream.write_long(self.mobile_base_exp_reward)
stream.write_float(self.mobile_tenacity)
stream.write_float(self.mobile_courage)
stream.write_bool(self.mobile_has_group_tactics)
@@ -14,7 +14,7 @@ class ShopKeeperInfo(MobileInfo):
def load_binary(self, stream: ResStream):
super().load_binary(stream)
self.shopkeeper_shop_type = stream.read_dword()
self.shopkeeper_start_gold = stream.read_dword()
self.shopkeeper_start_gold = stream.read_long()
self.shopkeeper_std_buy_percent = stream.read_float()
self.shopkeeper_std_sell_percent = stream.read_float()
self.shopkeeper_nation_buy_percent = stream.read_float()
@@ -22,18 +22,18 @@ class ShopKeeperInfo(MobileInfo):
num_buy_list = stream.read_dword()
self.shopkeeper_buy_list = [[
stream.read_qword(),
stream.read_dword(),
stream.read_long(),
] for _ in range(num_buy_list)]
num_sell_list = stream.read_dword()
self.shopkeeper_sell_list = [[
stream.read_qword(),
stream.read_dword(),
stream.read_long(),
] for _ in range(num_sell_list)]
def save_binary(self, stream: ResStream):
super().save_binary(stream)
stream.write_dword(self.shopkeeper_shop_type)
stream.write_dword(self.shopkeeper_start_gold)
stream.write_long(self.shopkeeper_start_gold)
stream.write_float(self.shopkeeper_std_buy_percent)
stream.write_float(self.shopkeeper_std_sell_percent)
stream.write_float(self.shopkeeper_nation_buy_percent)
@@ -41,11 +41,11 @@ class ShopKeeperInfo(MobileInfo):
stream.write_dword(len(self.shopkeeper_buy_list))
for l in self.shopkeeper_buy_list:
stream.write_qword(l[0])
stream.write_dword(l[1])
stream.write_long(l[1])
stream.write_dword(len(self.shopkeeper_sell_list))
for l in self.shopkeeper_sell_list:
stream.write_qword(l[0])
stream.write_dword(l[1])
stream.write_long(l[1])
def save_json(self):
data = super().save_json()