Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
yijunj authored Dec 30, 2021
1 parent 7a9ffde commit c0d27a9
Show file tree
Hide file tree
Showing 11 changed files with 593 additions and 126 deletions.
6 changes: 5 additions & 1 deletion boss.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,18 @@ def human_readable(self):
class BitSetFlagHabitatType(OrderedAttibuteClass):
def __init__(self):
self.flag = ['u32']
def human_readable(self):
if len(self.flag) == 1 and type(self.flag[0]) == 'int':
self.flag = bin(self.flag[0])[2:]
self.flag = [int(p)+1 for p, c in enumerate(self.flag) if c == '1']

class BossMonsterData(OrderedAttibuteClass):
def __init__(self):
self.em_type = 'u32'
self.family_type = 'u32'
self.habitat_area = 'BitSetFlagHabitatType'
self.is_limit_open_lv = 'u8'
self.part_Table_data = ['PartData']
self.part_table_data = ['PartData']
def human_readable(self):
self.em_type = enum_EmTypes(self.em_type)
self.family_type = utils.u32_to_i32(self.family_type)
Expand Down
20 changes: 20 additions & 0 deletions collision.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import utils
from utils import OrderedAttibuteClass
from enums import *

# This correspondes to part of MHRice collision.rs
# Classes for monster collision data, and part names.

class RequestSetColliderUserData(OrderedAttibuteClass):
def __init__(self):
self.name = 'string'
self.zero = 'u32'

class PhysicsUserData(OrderedAttibuteClass):
def __init__(self):
self.name = 'string'

class EmHitDamageRsData(OrderedAttibuteClass):
def __init__(self):
self.base = 'PhysicsUserData'
self.parts_group = 'u16'
8 changes: 5 additions & 3 deletions enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ def enum_BaseHitMarkType(type_id):
########################################
# These are listed in MHRice common.rs
def bytes_Guid(text_bytes, bit_num):
return [int(hex(text_bytes)[2*i+2:2*i+4], 16) for i in reversed(range(int(bit_num/8)))]
byte_num = int(bit_num / 8)
hex_bytes = hex(text_bytes)[2:].zfill(2*byte_num)
return [int(hex_bytes[2*i:2*i+2], 16) for i in reversed(range(byte_num))]

########################################
# These are listed in MHRice condition_damage_data.rs
Expand Down Expand Up @@ -161,9 +163,9 @@ def enum_ItemId(item_id):
elif item_id == 0x04000000:
return None
elif item_id & 0xFFFF0000 == 0x04100000:
return 'Normal({})'.format(item_id & 0x0000FFFF)
return 'Normal({})'.format(item_id & 0x0000FFFF) # Normal items
else:
return 'Ec({})'.format(item_id & 0x0000FFFF)
return 'Ec({})'.format(item_id & 0x0000FFFF) # Environmental creatures

def newtype_RareTypes(type):
return type + 1
Expand Down
53 changes: 47 additions & 6 deletions monster.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from utils import OrderedAttibuteClass
from os.path import exists
import user
import rcol
import utils
import json

class Monster(OrderedAttibuteClass):
Expand All @@ -10,14 +12,28 @@ def __init__(self, id, sub_id, is_large_monster=True):
self.id = id
self.sub_id = sub_id
self.enemy_type = None
self.data_base = None
self.data_tune = None
self.meat_data = None
self.condition_damage_data = None
self.anger_data = None
self.parts_break_data = None
self.parts_map = []
self.boss_init_set_data = None
self.collider_mapping = None # Rcol not implemented, mhrice::extract::pedia::ColliderMapping
self.drop_item = None
self.parts_break_reward = None

self.get_data_base()
self.get_data_tune()
self.get_meat_data()
self.get_condition_damage_data()
self.get_anger_data()
self.get_parts_break_data()
self.get_parts_map()
self.get_boss_init_set_data()
self.get_drop_item()
self.get_parts_break_reward()
if not self.boss_init_set_data is None:
self.enemy_type = self.boss_init_set_data.enemy_type

Expand Down Expand Up @@ -93,6 +109,18 @@ def get_parts_break_data(self):
else:
self.parts_break_data = None

def get_parts_map(self):
if self.is_large_monster:
filename = 'rcol\\em{:03}_{:02}_colliders.rcol.18'\
.format(self.id, self.sub_id)
else:
filename = 'rcol\\ems{:03}_{:02}_colliders.rcol.18'\
.format(self.id, self.sub_id)
if exists(filename):
self.parts_map = rcol.read_rcol_file(filename)
else:
self.parts_map = None

def get_boss_init_set_data(self):
if self.is_large_monster:
filename = 'user\\monster\\em{:03}\\{:02}\\em{:03}_{:02}_boss_init_set_data.user.2'\
Expand All @@ -117,6 +145,18 @@ def get_drop_item(self):
else:
self.drop_item = None

def get_parts_break_reward(self):
if self.is_large_monster:
filename = 'user\\monster\\em{:03}\\{:02}\\em{:03}_{:02}_parts_break_reward_data.user.2'\
.format(self.id, self.sub_id, self.id, self.sub_id)
else:
filename = 'user\\monster\\ems{:03}\\{:02}\\ems{:03}_{:02}_parts_break_reward_data.user.2'\
.format(self.id, self.sub_id, self.id, self.sub_id)
if exists(filename):
self.parts_break_reward = user.read_user_file(filename)
else:
self.parts_break_reward = None

def read_large_monster_data():
large_monsters = []
with open('user\\large_monster_ids.json', 'r') as f:
Expand All @@ -139,10 +179,11 @@ def read_small_monster_data():

if __name__ == '__main__':
# monster = Monster(1, 0, is_large_monster=True)
# user.print_hierarchical_object(monster)
# utils.print_hierarchical_object(monster)
large_monster_ids, large_monsters = read_large_monster_data()
for i in large_monsters:
user.print_hierarchical_object(i)
small_monster_ids, small_monsters = read_small_monster_data()
for i in small_monsters:
user.print_hierarchical_object(i)
# for i in large_monsters:
# utils.print_hierarchical_object(i)
utils.print_hierarchical_object(large_monsters[0])
# small_monster_ids, small_monsters = read_small_monster_data()
# for i in small_monsters:
# utils.print_hierarchical_object(i)
175 changes: 175 additions & 0 deletions msg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
from bitstring import BitStream
import enums
import utils
from utils import OrderedAttibuteClass
import mmh3

class MsgEntry(OrderedAttibuteClass):
def __init__(self, name, guid, hash, attributes, content):
self.name = name
self.guid = guid
self.hash = hash
self.attributes = attributes
self.content = content

class MsgAttributeHeader(OrderedAttibuteClass):
def __init__(self, j, name):
self.j = j
self.name = name

class Msg(OrderedAttibuteClass):
def __init__(self, attribute_headers, entries):
self.attribute_headers = attribute_headers
self.entries = entries

# Reads meta data of a msg data file
def read_msg_head(data):
version = data.read('uintle:32')
if version != 0x11:
raise Exception('Version should be 0x10')
magic = data.read('uintle:32')
if magic != 0x47534D47: # little-endian version of GMSG
raise Exception('User magic should be "USR"')
temp = data.read('uintle:64')
if temp != 0x10: # little-endian version of GMSG
raise Exception('Next 32 bits after magic should be 0x10')
count_a = data.read('uintle:32')
attribute_count = data.read('uintle:32')
language_count = data.read('uintle:32')
data = utils.pad_to_multiple_of(64, data)

data_offset = data.read('uintle:64')
p_offset = data.read('uintle:64')
q_offset = data.read('uintle:64')
attribute_js_offset = data.read('uintle:64')
attribute_names_offset = data.read('uintle:64')

entries = []
for i in range(count_a):
entries.append(data.read('uintle:64'))

data.pos = p_offset * 8
p = data.read('uintle:64')
if p != 0:
raise Exception('p should be zero')

data.pos = q_offset * 8
languages = []
for i in range(language_count):
languages.append(data.read('uintle:32'))

data.pos = attribute_js_offset * 8
attribute_js = []
for i in range(attribute_count):
attribute_js.append(data.read('iintle:32')) # i32 copied from MHRice

# Some alignment check?
data.pos = attribute_names_offset * 8
attribute_names = []
for i in range(attribute_count):
attribute_js.append(data.read('uintle:64'))

# MsgEntry
for i in range(len(entries)):
data.pos = entries[i] * 8
guid = data.read('uintle:128')
guid = enums.bytes_Guid(guid, 128)
data.read('uintle:32')
hash = data.read('uintle:32')
name = data.read('uintle:64')
attributes = data.read('uintle:64')
content = []
for j in range(language_count):
content.append(data.read('uintle:64'))

data.pos = attributes * 8
attributes = []
for attrubutes in range(attribute_count):
attributes.append(data.read('uintle:64'))
entries[i] = MsgEntry(name, guid, hash, attributes, content)

# Data
data.pos = data_offset * 8
data_list = []
while data.pos < data.len:
data_list.append(data.read('uintle:8'))

return entries, data_offset, data_list, attribute_js, attribute_names

# Reads actual data of a msg data file
def read_msg_data(entries, data_offset, data_list):
# First parse data_list
key = [0xCF, 0xCE, 0xFB, 0xF8, 0xEC, 0x0A, 0x33, 0x66, 0x93, 0xA9, 0x1D, 0x93, 0x50, 0x39, 0x5F, 0x09]
prev = 0
data_list_parsed = []
for i in range(len(data_list)):
byte = data_list[i]
cur = byte
byte ^= prev ^ key[i & 0xF]
prev = cur
data_list_parsed.append(byte)
# data_list_parsed is a list of u8, every two entries define a char
# char_list is a list of char
# hash takes a subset of data_list_parsed, while name takes a subset of str_list
str_parsed = utils.u8_list_to_u16_str(data_list_parsed)

for entry in entries:
entry.name = utils.read_str_until_x00(str_parsed[int((entry.name-data_offset)/2):])
hash_name = utils.str_to_u16_hashable(entry.name)
if utils.i32_to_u32(mmh3.hash(hash_name, 0xFFFFFFFF)) != entry.hash:
raise Exception('Wrong hash')

for i in range(len(entry.attributes)):
entry.attributes[i] =\
utils.read_str_until_x00(str_parsed[int((entry.attributes[i]-data_offset)/2):])

for i in range(len(entry.content)):
entry.content[i] =\
utils.read_str_until_x00(str_parsed[int((entry.content[i]-data_offset)/2):])
return entries, str_parsed

# Reads attribute header data of a msg data file
def read_msg_attr_header(attr_js, attr_names, data_offset, str_parsed):
attribute_headers = []
for i in range(len(attr_names)):
name = attr_names[i]
name = utils.read_str_until_x00(str_parsed[int((name-data_offset)/2):])
attribute_headers.append(MsgAttributeHeader(attr_js[i], name))
return attribute_headers

# # Combines everything together
def read_msg_file(filename):
data = BitStream(filename = filename)
entries, data_offset, data_list, attr_js, attr_names = read_msg_head(data)
entries, str_parsed = read_msg_data(entries, data_offset, data_list)
attr_headers = read_msg_attr_header(attr_js, attr_names, data_offset, str_parsed)
return Msg(attr_headers, entries)

if __name__ == '__main__':
# filename = 'msg\\HN_Hunternote_Menu.msg.17'
# filename = 'msg\\Tag_EM_Name.msg.17'
# filename = 'msg\\Tag_EM_Name_Alias.msg.17'
# filename = 'msg\\QuestData_Hall.msg.17'
# filename = 'msg\\QuestData_Village.msg.17'
# filename = 'msg\\QuestData_Tutorial.msg.17'
# filename = 'msg\\QuestData_Arena.msg.17'
# filename = 'msg\\A_Head_Name.msg.17'
# filename = 'msg\\A_Chest_Name.msg.17'
# filename = 'msg\\A_Arm_Name.msg.17'
# filename = 'msg\\A_Waist_Name.msg.17'
# filename = 'msg\\A_Leg_Name.msg.17'
# filename = 'msg\\ArmorSeries_Hunter_Name.msg.17'
# filename = 'msg\\PlayerSkill_Detail.msg.17'
# filename = 'msg\\PlayerSkill_Explain.msg.17'
# filename = 'msg\\PlayerSkill_Name.msg.17'
# filename = 'msg\\HyakuryuSkill_Name.msg.17'
# filename = 'msg\\HyakuryuSkill_Explain.msg.17'
# filename = 'msg\\Decorations_Name.msg.17'
# filename = 'msg\\ItemName.msg.17'
# filename = 'msg\\ItemExplain.msg.17'
# filename = 'msg\\ItemCategoryType_Name.msg.17'
# filename = 'msg\\GreatSword_Name.msg.17'
# filename = 'msg\\GreatSword_Explain.msg.17'
filename = 'msg\\Horn_UniqueParam.msg.17'
msg = read_msg_file(filename)
utils.print_hierarchical_object(msg)
Loading

0 comments on commit c0d27a9

Please sign in to comment.