Hash grant attr and power keys, read grant values signed
Power and skill grants read every sub-array as (u32 key, int32 value) (sb.exe 0x6D7640). Skill keys were already hashed; attr and power keys now are too, and the values plus attribute/skill/resource amounts and power action arguments are read signed. Shared pair read/write and key hashing moved into helpers; load_json no longer mutates its input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0adbfe0d99
commit
ca4d2b2565
1 file changed
+71
-106
@@ -1,5 +1,5 @@
|
|||||||
# • ▌ ▄ ·. ▄▄▄▄· ▄▄▄ .·▄▄▄▄ ▪ ▄▄▄▄▄ ▄▄▄ ▄▄▄·▄▄▄
|
# • ▌ ▄ ·. ▄▄▄▄· ▄▄▄ .·▄▄▄▄ ▪ ▄▄▄▄▄ ▄▄▄ ▄▄▄·▄▄▄
|
||||||
# ·██ ▐███▪▐█ ▀█▪ ▀▄.▀·██▪ ██ ██ •██ ▪ ▀▄ █· ▐█ ▄█▀▄ █·▪
|
# ·██ ▐███▪▐█ ▀█▪ ▀▄.▀·██▪ ██ ██ •██ ▪ ▀▄ █· ▐█ ▄█▀▄ █·▪
|
||||||
# ▐█ ▌▐▌▐█·▐█▀▀█▄ ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄ ██▀·▐▀▀▄ ▄█▀▄
|
# ▐█ ▌▐▌▐█·▐█▀▀█▄ ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄ ██▀·▐▀▀▄ ▄█▀▄
|
||||||
# ██ ██▌▐█▌██▄▪▐█ ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌ ▐█▪·•▐█•█▌▐█▌.▐▌
|
# ██ ██▌▐█▌██▄▪▐█ ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌ ▐█▪·•▐█•█▌▐█▌.▐▌
|
||||||
# ▀▀ █▪▀▀▀·▀▀▀▀ ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀ ▀█▄▀▪.▀ ▀ .▀ .▀ ▀ ▀█▄▀▪
|
# ▀▀ █▪▀▀▀·▀▀▀▀ ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀ ▀█▄▀▪.▀ ▀ .▀ .▀ ▀ ▀█▄▀▪
|
||||||
@@ -15,11 +15,11 @@ from arcane.util import ResStream
|
|||||||
class AttributeValue:
|
class AttributeValue:
|
||||||
def load_binary(self, stream: ResStream):
|
def load_binary(self, stream: ResStream):
|
||||||
self.attr_type = stream.read_dword()
|
self.attr_type = stream.read_dword()
|
||||||
self.attr_value = stream.read_dword()
|
self.attr_value = stream.read_long()
|
||||||
|
|
||||||
def save_binary(self, stream: ResStream):
|
def save_binary(self, stream: ResStream):
|
||||||
stream.write_dword(self.attr_type)
|
stream.write_dword(self.attr_type)
|
||||||
stream.write_dword(self.attr_value)
|
stream.write_long(self.attr_value)
|
||||||
|
|
||||||
def load_json(self, data):
|
def load_json(self, data):
|
||||||
self.attr_type = string_to_hash(data['attr_type'])
|
self.attr_type = string_to_hash(data['attr_type'])
|
||||||
@@ -35,13 +35,13 @@ class AttributeValue:
|
|||||||
class PowerArgument:
|
class PowerArgument:
|
||||||
def load_binary(self, stream: ResStream):
|
def load_binary(self, stream: ResStream):
|
||||||
self.power_arguments = [
|
self.power_arguments = [
|
||||||
stream.read_dword(),
|
stream.read_long(),
|
||||||
stream.read_dword(),
|
stream.read_long(),
|
||||||
]
|
]
|
||||||
|
|
||||||
def save_binary(self, stream: ResStream):
|
def save_binary(self, stream: ResStream):
|
||||||
stream.write_dword(self.power_arguments[0])
|
stream.write_long(self.power_arguments[0])
|
||||||
stream.write_dword(self.power_arguments[1])
|
stream.write_long(self.power_arguments[1])
|
||||||
|
|
||||||
def load_json(self, data):
|
def load_json(self, data):
|
||||||
self.power_arguments = data['power_arguments']
|
self.power_arguments = data['power_arguments']
|
||||||
@@ -55,11 +55,11 @@ class PowerArgument:
|
|||||||
class SkillLevel:
|
class SkillLevel:
|
||||||
def load_binary(self, stream: ResStream):
|
def load_binary(self, stream: ResStream):
|
||||||
self.skill_type = stream.read_dword()
|
self.skill_type = stream.read_dword()
|
||||||
self.skill_level = stream.read_dword()
|
self.skill_level = stream.read_long()
|
||||||
|
|
||||||
def save_binary(self, stream: ResStream):
|
def save_binary(self, stream: ResStream):
|
||||||
stream.write_dword(self.skill_type)
|
stream.write_dword(self.skill_type)
|
||||||
stream.write_dword(self.skill_level)
|
stream.write_long(self.skill_level)
|
||||||
|
|
||||||
def load_json(self, data):
|
def load_json(self, data):
|
||||||
self.skill_type = string_to_hash(data['skill_type'])
|
self.skill_type = string_to_hash(data['skill_type'])
|
||||||
@@ -75,11 +75,11 @@ class SkillLevel:
|
|||||||
class ResourceInfo:
|
class ResourceInfo:
|
||||||
def load_binary(self, stream: ResStream):
|
def load_binary(self, stream: ResStream):
|
||||||
self.resource_type = stream.read_dword()
|
self.resource_type = stream.read_dword()
|
||||||
self.resource_value = stream.read_dword()
|
self.resource_value = stream.read_long()
|
||||||
|
|
||||||
def save_binary(self, stream: ResStream):
|
def save_binary(self, stream: ResStream):
|
||||||
stream.write_dword(self.resource_type)
|
stream.write_dword(self.resource_type)
|
||||||
stream.write_dword(self.resource_value)
|
stream.write_long(self.resource_value)
|
||||||
|
|
||||||
def load_json(self, data):
|
def load_json(self, data):
|
||||||
self.resource_type = string_to_hash(data['resource_type'])
|
self.resource_type = string_to_hash(data['resource_type'])
|
||||||
@@ -124,31 +124,44 @@ class PowerAction:
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def _read_granted_pairs(stream: ResStream):
|
||||||
|
num_pairs = stream.read_dword()
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
stream.read_dword(),
|
||||||
|
stream.read_long(),
|
||||||
|
] for _ in range(num_pairs)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _write_granted_pairs(stream: ResStream, pairs):
|
||||||
|
stream.write_dword(len(pairs))
|
||||||
|
for pair in pairs:
|
||||||
|
stream.write_dword(pair[0])
|
||||||
|
stream.write_long(pair[1])
|
||||||
|
|
||||||
|
|
||||||
|
def _hash_pair_keys(pairs):
|
||||||
|
pairs_copy = copy.deepcopy(pairs)
|
||||||
|
for pair in pairs_copy:
|
||||||
|
pair[0] = hash_to_string(pair[0])
|
||||||
|
return pairs_copy
|
||||||
|
|
||||||
|
|
||||||
|
def _unhash_pair_keys(pairs):
|
||||||
|
pairs_copy = copy.deepcopy(pairs)
|
||||||
|
for pair in pairs_copy:
|
||||||
|
pair[0] = string_to_hash(pair[0])
|
||||||
|
return pairs_copy
|
||||||
|
|
||||||
|
|
||||||
class PowerGrant:
|
class PowerGrant:
|
||||||
def load_binary(self, stream: ResStream):
|
def load_binary(self, stream: ResStream):
|
||||||
self.power_type = stream.read_dword()
|
self.power_type = stream.read_dword()
|
||||||
self.power_value = stream.read_dword()
|
self.power_value = stream.read_long()
|
||||||
num_attrs = stream.read_dword()
|
self.power_granted_attrs = _read_granted_pairs(stream)
|
||||||
self.power_granted_attrs = [
|
self.power_granted_skills = _read_granted_pairs(stream)
|
||||||
[
|
self.power_granted_powers = _read_granted_pairs(stream)
|
||||||
stream.read_dword(),
|
|
||||||
stream.read_dword(),
|
|
||||||
] for _ in range(num_attrs)
|
|
||||||
]
|
|
||||||
num_skills = stream.read_dword()
|
|
||||||
self.power_granted_skills = [
|
|
||||||
[
|
|
||||||
stream.read_dword(),
|
|
||||||
stream.read_dword(),
|
|
||||||
] for _ in range(num_skills)
|
|
||||||
]
|
|
||||||
num_powers = stream.read_dword()
|
|
||||||
self.power_granted_powers = [
|
|
||||||
[
|
|
||||||
stream.read_dword(),
|
|
||||||
stream.read_dword(),
|
|
||||||
] for _ in range(num_powers)
|
|
||||||
]
|
|
||||||
num_monster_types = stream.read_dword()
|
num_monster_types = stream.read_dword()
|
||||||
self.power_monster_types = [
|
self.power_monster_types = [
|
||||||
stream.read_dword() for _ in range(num_monster_types)
|
stream.read_dword() for _ in range(num_monster_types)
|
||||||
@@ -156,19 +169,10 @@ class PowerGrant:
|
|||||||
|
|
||||||
def save_binary(self, stream: ResStream):
|
def save_binary(self, stream: ResStream):
|
||||||
stream.write_dword(self.power_type)
|
stream.write_dword(self.power_type)
|
||||||
stream.write_dword(self.power_value)
|
stream.write_long(self.power_value)
|
||||||
stream.write_dword(len(self.power_granted_attrs))
|
_write_granted_pairs(stream, self.power_granted_attrs)
|
||||||
for attr in self.power_granted_attrs:
|
_write_granted_pairs(stream, self.power_granted_skills)
|
||||||
stream.write_dword(attr[0])
|
_write_granted_pairs(stream, self.power_granted_powers)
|
||||||
stream.write_dword(attr[1])
|
|
||||||
stream.write_dword(len(self.power_granted_skills))
|
|
||||||
for skill in self.power_granted_skills:
|
|
||||||
stream.write_dword(skill[0])
|
|
||||||
stream.write_dword(skill[1])
|
|
||||||
stream.write_dword(len(self.power_granted_powers))
|
|
||||||
for power in self.power_granted_powers:
|
|
||||||
stream.write_dword(power[0])
|
|
||||||
stream.write_dword(power[1])
|
|
||||||
stream.write_dword(len(self.power_monster_types))
|
stream.write_dword(len(self.power_monster_types))
|
||||||
for value in self.power_monster_types:
|
for value in self.power_monster_types:
|
||||||
stream.write_dword(value)
|
stream.write_dword(value)
|
||||||
@@ -176,24 +180,18 @@ class PowerGrant:
|
|||||||
def load_json(self, data):
|
def load_json(self, data):
|
||||||
self.power_type = string_to_hash(data['power_type'])
|
self.power_type = string_to_hash(data['power_type'])
|
||||||
self.power_value = data['power_value']
|
self.power_value = data['power_value']
|
||||||
self.power_granted_attrs = data['power_granted_attrs']
|
self.power_granted_attrs = _unhash_pair_keys(data['power_granted_attrs'])
|
||||||
grants_copy = data['power_granted_skills']
|
self.power_granted_skills = _unhash_pair_keys(data['power_granted_skills'])
|
||||||
for subarr in grants_copy:
|
self.power_granted_powers = _unhash_pair_keys(data['power_granted_powers'])
|
||||||
subarr[0] = string_to_hash(subarr[0])
|
|
||||||
self.power_granted_skills = grants_copy
|
|
||||||
self.power_granted_powers = data['power_granted_powers']
|
|
||||||
self.power_monster_types = data['power_monster_types']
|
self.power_monster_types = data['power_monster_types']
|
||||||
|
|
||||||
def save_json(self):
|
def save_json(self):
|
||||||
data = OrderedDict()
|
data = OrderedDict()
|
||||||
data['power_type'] = hash_to_string(self.power_type)
|
data['power_type'] = hash_to_string(self.power_type)
|
||||||
data['power_value'] = self.power_value
|
data['power_value'] = self.power_value
|
||||||
data['power_granted_attrs'] = self.power_granted_attrs
|
data['power_granted_attrs'] = _hash_pair_keys(self.power_granted_attrs)
|
||||||
grants_copy = copy.deepcopy(self.power_granted_skills)
|
data['power_granted_skills'] = _hash_pair_keys(self.power_granted_skills)
|
||||||
for subarr in grants_copy:
|
data['power_granted_powers'] = _hash_pair_keys(self.power_granted_powers)
|
||||||
subarr[0] = hash_to_string(subarr[0])
|
|
||||||
data['power_granted_skills'] = grants_copy
|
|
||||||
data['power_granted_powers'] = self.power_granted_powers
|
|
||||||
data['power_monster_types'] = self.power_monster_types
|
data['power_monster_types'] = self.power_monster_types
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@@ -201,28 +199,10 @@ class PowerGrant:
|
|||||||
class SkillGrant:
|
class SkillGrant:
|
||||||
def load_binary(self, stream: ResStream):
|
def load_binary(self, stream: ResStream):
|
||||||
self.skill_type = stream.read_dword()
|
self.skill_type = stream.read_dword()
|
||||||
self.skill_value = stream.read_dword()
|
self.skill_value = stream.read_long()
|
||||||
num_attrs = stream.read_dword()
|
self.skill_granted_attrs = _read_granted_pairs(stream)
|
||||||
self.skill_granted_attrs = [
|
self.skill_granted_skills = _read_granted_pairs(stream)
|
||||||
[
|
self.skill_granted_powers = _read_granted_pairs(stream)
|
||||||
stream.read_dword(),
|
|
||||||
stream.read_dword(),
|
|
||||||
] for _ in range(num_attrs)
|
|
||||||
]
|
|
||||||
num_skills = stream.read_dword()
|
|
||||||
self.skill_granted_skills = [
|
|
||||||
[
|
|
||||||
stream.read_dword(),
|
|
||||||
stream.read_dword(),
|
|
||||||
] for _ in range(num_skills)
|
|
||||||
]
|
|
||||||
num_powers = stream.read_dword()
|
|
||||||
self.skill_granted_powers = [
|
|
||||||
[
|
|
||||||
stream.read_dword(),
|
|
||||||
stream.read_dword(),
|
|
||||||
] for _ in range(num_powers)
|
|
||||||
]
|
|
||||||
num_monster_types = stream.read_dword()
|
num_monster_types = stream.read_dword()
|
||||||
self.skill_monster_types = [
|
self.skill_monster_types = [
|
||||||
stream.read_dword() for _ in range(num_monster_types)
|
stream.read_dword() for _ in range(num_monster_types)
|
||||||
@@ -230,19 +210,10 @@ class SkillGrant:
|
|||||||
|
|
||||||
def save_binary(self, stream: ResStream):
|
def save_binary(self, stream: ResStream):
|
||||||
stream.write_dword(self.skill_type)
|
stream.write_dword(self.skill_type)
|
||||||
stream.write_dword(self.skill_value)
|
stream.write_long(self.skill_value)
|
||||||
stream.write_dword(len(self.skill_granted_attrs))
|
_write_granted_pairs(stream, self.skill_granted_attrs)
|
||||||
for attr in self.skill_granted_attrs:
|
_write_granted_pairs(stream, self.skill_granted_skills)
|
||||||
stream.write_dword(attr[0])
|
_write_granted_pairs(stream, self.skill_granted_powers)
|
||||||
stream.write_dword(attr[1])
|
|
||||||
stream.write_dword(len(self.skill_granted_skills))
|
|
||||||
for skill in self.skill_granted_skills:
|
|
||||||
stream.write_dword(skill[0])
|
|
||||||
stream.write_dword(skill[1])
|
|
||||||
stream.write_dword(len(self.skill_granted_powers))
|
|
||||||
for power in self.skill_granted_powers:
|
|
||||||
stream.write_dword(power[0])
|
|
||||||
stream.write_dword(power[1])
|
|
||||||
stream.write_dword(len(self.skill_monster_types))
|
stream.write_dword(len(self.skill_monster_types))
|
||||||
for value in self.skill_monster_types:
|
for value in self.skill_monster_types:
|
||||||
stream.write_dword(value)
|
stream.write_dword(value)
|
||||||
@@ -250,23 +221,17 @@ class SkillGrant:
|
|||||||
def load_json(self, data):
|
def load_json(self, data):
|
||||||
self.skill_type = string_to_hash(data['skill_type'])
|
self.skill_type = string_to_hash(data['skill_type'])
|
||||||
self.skill_value = data['skill_value']
|
self.skill_value = data['skill_value']
|
||||||
self.skill_granted_attrs = data['skill_granted_attrs']
|
self.skill_granted_attrs = _unhash_pair_keys(data['skill_granted_attrs'])
|
||||||
grants_copy = data['skill_granted_skills']
|
self.skill_granted_skills = _unhash_pair_keys(data['skill_granted_skills'])
|
||||||
for subarr in grants_copy:
|
self.skill_granted_powers = _unhash_pair_keys(data['skill_granted_powers'])
|
||||||
subarr[0] = string_to_hash(subarr[0])
|
|
||||||
self.skill_granted_skills = grants_copy
|
|
||||||
self.skill_granted_powers = data['skill_granted_powers']
|
|
||||||
self.skill_monster_types = data['skill_monster_types']
|
self.skill_monster_types = data['skill_monster_types']
|
||||||
|
|
||||||
def save_json(self):
|
def save_json(self):
|
||||||
data = OrderedDict()
|
data = OrderedDict()
|
||||||
data['skill_type'] = hash_to_string(self.skill_type)
|
data['skill_type'] = hash_to_string(self.skill_type)
|
||||||
data['skill_value'] = self.skill_value
|
data['skill_value'] = self.skill_value
|
||||||
data['skill_granted_attrs'] = self.skill_granted_attrs
|
data['skill_granted_attrs'] = _hash_pair_keys(self.skill_granted_attrs)
|
||||||
grants_copy = copy.deepcopy(self.skill_granted_skills)
|
data['skill_granted_skills'] = _hash_pair_keys(self.skill_granted_skills)
|
||||||
for subarr in grants_copy:
|
data['skill_granted_powers'] = _hash_pair_keys(self.skill_granted_powers)
|
||||||
subarr[0] = hash_to_string(subarr[0])
|
|
||||||
data['skill_granted_skills'] = grants_copy
|
|
||||||
data['skill_granted_powers'] = self.skill_granted_powers
|
|
||||||
data['skill_monster_types'] = self.skill_monster_types
|
data['skill_monster_types'] = self.skill_monster_types
|
||||||
return data
|
return data
|
||||||
Reference in new issue
Block a user