Files
mbEditorPro/mbEditorPro2.0/JSON_CHANGES.md
T
MagicBotandClaude Opus 5.5 7bcbed08a8 Accept unsigned values in signed fields when packing
Dumps made before the signed-read change store values like -1 as
4294967295. write_long now converts 2147483648..4294967295 to the
signed equivalent (same bytes), so existing community dumps still
pack. Verified: all 10332 files of an older dump repack identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 13:09:05 -04:00

9.5 KiB

 • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        
 ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     
 ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ 
 ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌
 ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪
               Magicbane Emulator Project © 2013 - 2022
                          www.magicbane.com

COBJECTS JSON format changes

This note covers the COBJECTS JSON produced by unpack cobjects after the client parser audit on branch mbeditor-update. It is written for anything that reads or writes that JSON: the server COBJECTS loader, database imports, the GUI, and hand edits.

The binary .cache format did not change. Every resource in cachefiles/CObjects.cache still round-trips byte for byte.

Summary

Change Effect on the JSON Action for readers
Signed 32-bit fields Values that were 4294967295 are now -1, 4294967286 is now -10, and so on Accept negative integers
Grant attribute and power keys First element of each pair is now a name string when the hash is known Accept a string or an integer; convert names with string_to_hash

Old JSON dumps still pack. 4294967295 and -1 are the same four bytes, and ResStream.write_long accepts either form: values from 2147483648 to 4294967295 are converted to their signed equivalent before packing. Integer grant keys in old dumps also pack unchanged. Re-running unpack cobjects is only needed to see the new signed values and grant key names in the JSON.

1. Signed 32-bit fields

The client reads 4-byte integers through two entry points. Values read with ResStream_ReadInt32 (sb.exe 0x7E1700) are now loaded with ResStream.read_long and written with write_long. Fields the client reads through the folded ATL wrapper (0x54C700, used for hash and enum fields), list counts, and the item flag words (0x7E1720, not yet classified) stay unsigned.

Fields marked (enum) are written to JSON as a name, so their JSON text does not change.

ArcObj (all object types)

JSON path Notes
obj_sound_events[].sound_type (enum)
obj_arc_hardpoint_list[][0], [][1] the two integers before the 10 floats
obj_sparse_data.<tag> and rune_sparse_data.<tag> for VAL_LONG tags client type LoadSaveBehavior<long>; tags: ADMIN_SNOOPLEVEL, GEN_TABLE_ID, ADMIN_REMOVEEQUIPMENTEFFECTSLEVEL, trainerSkillCost, ADMIN_TOKENREADACCESSLEVEL, MAX_POWER_LEVEL, ADMIN_ACCESSLEVEL, banker, expLost, ADMIN_CHANNELACCESSLEVEL, ADMIN_TOKENWRITEACCESSLEVEL, ADMIN_REMOVEPOWEREFFECTSLEVEL

ArcCombatObj (static, item, rune, character and subclasses)

JSON path Notes
combat_powers[][1] value; [0] stays unsigned

ArcStaticObject

JSON path
static_sound.sound_chance, static_sound.sound_reserved

ArcItem (also rune, container, deed, key)

JSON path Notes
item_type (enum)
item_eq_slots_value
item_eq_slots_or, item_eq_slots_and (bitmask written as names)
item_value, item_wt
item_parry_anim_id
item_defense_rating armor only
item_initial_charges
item_level_req, item_rank_req
item_sex_req (enum)
item_sheath_slot (enum)
item_bane_rank
item_user_power_action[].arguments[] power stays a hash
item_skill_req[].skill_level
item_attr_req[].attr_value
item_resource_costs[].resource_value
item_offering_info[].offering_value, item_offering_adjustments[].offering_value
item_power_action[].power_actions[].power_arguments[]
item_power_grant[] see section 2
item_weapon.weapon_combat_idle_anim
item_weapon.weapon_damage[].damage_type (enum)
item_weapon.weapon_damage[].damage_min, damage_max
item_weapon.weapon_attack_anim_right[][], weapon_attack_anim_left[][]

ArcRune

JSON path Notes
rune_type (enum)
rune_sex (enum)
rune_creation_cost, rune_rank, rune_pracs_per_level
rune_health, rune_mana, rune_stamina
rune_attack, rune_defense, rune_level
rune_enemy_gender[]
rune_skill_adj[].skill_adjusts[][] skill_type stays a hash
rune_attr_adj[].attr_value, rune_max_attr_adj[].attr_value seen as -10 and -5 in the sample cache
rune_naturalattacks[][2], [3], [6], [7], [8], [9] the integer slots; floats and the projectile id are unchanged
rune_skill_grant[] see section 2

ArcDoorObject (also doors inside structures)

JSON path Notes
door_type, door_movement_axis, door_swing_direction (enum)
door_locked

ArcDungeonUnitObject, ArcDungeonExitObject, ArcDungeonStairObject

JSON path
dungeon_connectable_edges[]

ArcContainerObject

JSON path
container_open, container_locked, container_locked_rank, container_barred, container_barred_rank, container_trapped, container_trappe_rank

container_inventory_contents[] follows the inventory rules below.

ArcDeed

JSON path
deed_type, deed_employment, deed_start_rank, deed_name_lookup

ArcKey

JSON path
key_setting

Inventory entries (rune and container contents)

JSON path
table[1] (booty table entries)

ArcCityAssetTemplate (also template_embed_template)

JSON path Notes
template_max_ranks, template_start_rank, template_asset_type, template_trade_icon, template_landmark
template_creator[], template_terrain[], template_valid_npc_type[], template_valid_npc_cat[]
template_cap_info[][0], [][1] [2] is an ArcCacheID
template_event_rules[][0] [1] stays unsigned
template_resource_limit[][1], [][2] [0] stays unsigned
template_rank_info[].rank_hirelings, rank_shrines, rank_spires, rank_barracks, rank_rank, rank_health rank_hirelings and rank_rank are -1 in many sample records
template_rank_info[].rank_placement_limit[][1] [0] stays unsigned

2. Grant keys

Power grants (item_power_grant[]) and skill grants (rune_skill_grant[]) each carry three lists of [key, value] pairs. The client reads every pair as an unsigned key and a signed value (sb.exe 0x6D7640).

JSON path Before After
power_granted_skills[][0], skill_granted_skills[][0] name string or integer unchanged
power_granted_attrs[][0], skill_granted_attrs[][0] integer name string when the hash is known, otherwise integer
power_granted_powers[][0], skill_granted_powers[][0] integer name string when the hash is known, otherwise integer (example: 428458144 becomes "HLR-021")
every [][1] in those lists unsigned signed
power_value, skill_value unsigned signed

power_monster_types and skill_monster_types are unchanged.

A reader that needs the numeric key should run it through arcane.enums.hashes.string_to_hash, which returns integers unchanged.

Sample data impact

From cachefiles/CObjects.cache, comparing JSON from master with this branch:

JSON path Records changed Example
ASSET.template_rank_info[].rank_hirelings (incl. embedded templates) 539 4294967295 → -1
ASSET.template_rank_info[].rank_rank 182 4294967295 → -1
RUNE.item_power_grant[].power_granted_powers[][0] 100 428458144 → "HLR-021"
RUNE.rune_attr_adj[].attr_value 17 4294967286 → -10
RUNE.rune_max_attr_adj[].attr_value 12 4294967291 → -5

Nothing else differs in that file.

These do not change the JSON layout but affect packing:

  • Sparse MERCHANT_DATA now writes its last field as 4 bytes, matching the client (sb.exe 0x4859B0). Packing it previously corrupted the resource.
  • ArcContainerObject packs again; save_binary used read calls and load_json skipped the item base class.
  • ArcCityAssetTemplate raises NotImplementedError when template_unknown_check1 or template_unknown_check2 is true. The client loads extra trigger-object data in that case, which this tool does not support. No record in the sample cache sets either flag.

Open items

  • The signed/unsigned split depends on which client read entry point each field uses. That split is marked [MB] GUESS: in IDA and unverified in MBDATA/docs/client-map.md.
  • item_flags, item_use_flags, structure floor/level/trigger numbers and region content behavior/state are read through 0x7E1720, which is not yet classified. They stay unsigned.
  • Region byte fields (region_stairs, region_unknown2, region_unknown3) default to 0xFF in the client and may be signed; they are still read as unsigned bytes.
  • cachefiles/CObjects.cache holds 286 resource ids twice (identical bytes). unpack keeps one copy, so a repacked cache has 10,334 entries instead of 10,620.