Guest User

Untitled

a guest
Jan 19th, 2018
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.15 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. from collections import OrderedDict
  3. from pprint import pprint
  4. from struct import unpack
  5. import io
  6. import os
  7.  
  8. def read_byte(data):
  9. return data.read(1)
  10.  
  11. def read_char(data):
  12. return unpack('>b', data.read(1))[0]
  13.  
  14. def read_short(data):
  15. return unpack('>h', data.read(2))[0]
  16.  
  17. def read_ushort(data):
  18. return unpack('>H', data.read(2))[0]
  19.  
  20. def read_int(data):
  21. return unpack('>i', data.read(4))[0]
  22.  
  23. def read_string(data):
  24. length = read_short(data)
  25. return unpack(str(length) + 's', data.read(length))[0].decode('shift-jis')
  26.  
  27. class NekoAtsumeData:
  28. # Chunks used to compartmentalize data
  29. entries = []
  30.  
  31. # Dates to display snow on the yard
  32. snow_dates = []
  33.  
  34. # Cats!
  35. cats = OrderedDict()
  36.  
  37. # Mysterious tables from the cat entry
  38. cat_tables = []
  39.  
  40. # Goodies, including food and toys
  41. goodies = OrderedDict()
  42.  
  43. goody_configs = OrderedDict()
  44.  
  45. # Enumeration of goody classifications
  46. goody_types = OrderedDict()
  47.  
  48. # Mapping of cat poses to corresponding sprite sheets
  49. cat_pose_sprites = OrderedDict()
  50.  
  51. # Mementos awarded by cats
  52. mementos = OrderedDict()
  53.  
  54. # Shop goody listings
  55. shop_listings = OrderedDict()
  56.  
  57. def __init__(self, file_path):
  58. with open(file_path, 'rb') as evt:
  59. self.parse_header(evt)
  60. self.parse_entries()
  61.  
  62. def parse_header(self, evt):
  63. entries = []
  64. num_entries = read_char(evt)
  65.  
  66. for i in range(num_entries):
  67. name_data = b''
  68.  
  69. data = read_byte(evt)
  70. while data != b'\x0a':
  71. name_data += data
  72. data = read_byte(evt)
  73.  
  74. entries.append({
  75. 'name': name_data.decode('shift-jis')
  76. })
  77.  
  78. name_block_end = evt.tell()
  79.  
  80. for i in range(num_entries):
  81. entries[i]['offset'] = read_int(evt) - 1
  82. entries[i]['size'] = read_ushort(evt)
  83.  
  84. for i in range(num_entries):
  85. file_offset = name_block_end + entries[i]['offset']
  86. evt.seek(file_offset, os.SEEK_SET)
  87.  
  88. flags = []
  89. num_flags = read_short(evt)
  90. for j in range(num_flags):
  91. flags.append(read_short(evt))
  92.  
  93. entries[i]['flags'] = flags
  94.  
  95. size = entries[i]['size'] - (num_flags * 2 + 2)
  96. data = evt.read(size)
  97.  
  98. entries[i]['data'] = data
  99.  
  100. self.entries = entries
  101.  
  102. def parse_entries(self):
  103. for entry in self.entries:
  104. data = io.BytesIO(entry['data'])
  105.  
  106. magic = read_short(data)
  107. assert magic == 0x3e8
  108.  
  109. entry_type = read_short(data)
  110.  
  111. if entry_type == 0:
  112. self.parse_init_entry(data)
  113. elif entry_type == 1:
  114. self.parse_neko_entry(data)
  115. elif entry_type == 2:
  116. self.parse_goods_entry(data)
  117. elif entry_type == 3:
  118. self.parse_shop_entry(data)
  119. elif entry_type == 4:
  120. self.parse_takara_entry(data)
  121.  
  122. assert data.read(2) == b'\x00\x01'
  123. assert data.tell() == entry['size'] - 4
  124.  
  125. def parse_init_entry(self, data):
  126. while True:
  127. index = read_char(data)
  128. if index < 0:
  129. break
  130.  
  131. unk1 = read_string(data)
  132. unk2 = read_char(data)
  133. unk3 = read_char(data)
  134.  
  135. # coordinate pairs for a rectangle
  136. while True:
  137. index = read_char(data)
  138. if index < 0:
  139. break
  140.  
  141. num_points = read_char(data)
  142.  
  143. # [top left, bottom left, bottom right, top right]
  144. unk1 = [] ##
  145. for x in range(num_points):
  146. unk1.append([read_short(data), read_short(data)])
  147.  
  148. # katakana character map
  149. katakana_map = []
  150. for x in range(90):
  151. katakana_map.append(read_string(data))
  152.  
  153. # sysimg file names
  154. while True:
  155. text_block = read_short(data)
  156. if text_block < 0:
  157. break
  158.  
  159. # upperbound estimate of items
  160. block_size = read_short(data)
  161.  
  162. while True:
  163. index = read_short(data)
  164. if index < 0:
  165. break
  166.  
  167. text = read_string(data)
  168.  
  169. # only some text about Mogg "モグニンジン" that doesn't
  170. # appear to be used anywhere
  171. while True:
  172. index = read_short(data)
  173. if index < 0:
  174. break
  175.  
  176. text = read_string(data)
  177.  
  178. while True:
  179. index = read_char(data)
  180. if index < 0:
  181. break
  182.  
  183. text = read_string(data)
  184.  
  185. # only some capitalized ASCII that is entirely discarded
  186. while True:
  187. index = read_short(data)
  188. if index < 0:
  189. break
  190.  
  191. text = read_string(data)
  192.  
  193. # text for message boxes
  194. for x in range(10):
  195. while True:
  196. index = read_short(data)
  197. if index < 0:
  198. break
  199.  
  200. if index & 1 == 0:
  201. text_jap = read_string(data)
  202. # 0th bit for japanese, 1st bit for english
  203. unk1 = read_char(data) ##
  204. unk2 = read_short(data) ##
  205. text_eng = read_string(data)
  206. unk3 = read_short(data) ##
  207. else:
  208. unk4 = read_short(data) ##
  209. unk5 = read_char(data) ##
  210. unk6 = read_short(data) ##
  211.  
  212. # ditto
  213. while True:
  214. index = read_short(data)
  215. if index < 0:
  216. break
  217.  
  218. while True:
  219. index = read_short(data)
  220. if index < 0:
  221. break
  222.  
  223. if index & 1 == 0:
  224. text_jap = read_string(data)
  225. unk1 = read_char(data) ##
  226. unk2 = read_short(data) ##
  227. text_eng = read_string(data)
  228. unk3 = read_short(data) ##
  229. else:
  230. unk4 = read_short(data) ##
  231. unk5 = read_char(data) ##
  232. unk6 = read_short(data) ##
  233.  
  234. # dates to display snow
  235. snow_dates = []
  236. while True:
  237. year = read_short(data)
  238. if year < 0:
  239. break
  240.  
  241. month = read_short(data)
  242. day = read_short(data)
  243.  
  244. snow_dates.append([year, month, day])
  245.  
  246. self.snow_dates = snow_dates
  247.  
  248.  
  249. def parse_neko_entry(self, data):
  250. num_interests = 0
  251. while True:
  252. index = read_short(data)
  253. if index < 0:
  254. break
  255.  
  256. num_interests += 1
  257.  
  258. # table 1 - appearance multiplier when there is snow
  259. # 2 - month is april or may
  260. # 3 - july or august
  261. # 4 - ?? stacks with table 7 when month is july or august
  262. # 5 - month is september or october
  263. # 6 - december or jan or feb
  264. # 7 - initial appearance factor
  265. cat_tables = []
  266. for x in range(7):
  267. unk1a = []
  268. for y in range(num_interests):
  269. unk1a.append(read_short(data))
  270. cat_tables.append(unk1a)
  271.  
  272. counter2 = 0
  273. while True:
  274. index = read_short(data)
  275. if index < 0:
  276. break
  277.  
  278. counter2 += 1
  279.  
  280. assert num_interests == counter2
  281.  
  282. cats = OrderedDict()
  283. while True:
  284. index = read_short(data)
  285.  
  286. if index < 0:
  287. break
  288.  
  289. name_jap = read_string(data)
  290. appearance_jap = read_string(data)
  291. personality_jap = read_string(data)
  292.  
  293. regular = index < 100
  294. if regular:
  295. bin_name = read_string(data)
  296. else:
  297. special_index = read_short(data)
  298.  
  299. # index for img_face.bin
  300. cat_index = read_short(data)
  301.  
  302. name_eng = read_string(data)
  303. appearance_eng = read_string(data)
  304. personality_eng = read_string(data)
  305.  
  306. power_level = read_short(data)
  307.  
  308. memento_id = read_short(data) - 1
  309.  
  310. # factor to determine fish given upon leaving
  311. fish_gift_factor = read_short(data)
  312.  
  313. # factor applied to all seasonal goody factors
  314. seasonal_modifier_factor = read_short(data)
  315.  
  316. # interests a cat has in a given food
  317. # 0 Thrifty Bitz 1 Frisky Bitz 2 Ritzy Bitz
  318. # 3 Sashimi 4 Deluxe Tuna Bitz 5 Bonito Bitz
  319. food_interests = []
  320. for x in range(6):
  321. food_interests.append(read_short(data))
  322.  
  323. # interests a cat has with a given goody [config]
  324. goody_interests = []
  325. for x in range(num_interests):
  326. goody_interests.append(read_short(data))
  327.  
  328. # for introducing cats but keeping them out of the game
  329. available = not (index >= 130 and index < 140)
  330.  
  331. cats[index] = {
  332. 'name': name_eng,
  333. 'appearance': appearance_eng,
  334. 'personality': personality_eng,
  335. 'name_jap': name_jap,
  336. 'appearance_jap': appearance_jap,
  337. 'personality_jap': personality_jap,
  338. 'power_level': power_level,
  339. 'memento_id': memento_id,
  340. 'regular': regular,
  341. 'available': available,
  342. 'cat_index': cat_index,
  343. 'fish_gift_factor': fish_gift_factor,
  344. 'seasonal_modifier_factor': seasonal_modifier_factor,
  345. 'food_interests': food_interests,
  346. 'goody_interests': goody_interests,
  347. }
  348.  
  349. if regular:
  350. # img_neko_XX name for cat images
  351. cats[index]['file_name'] = bin_name
  352. else:
  353. # index for img_neko_special.bin
  354. cats[index]['special_index'] = special_index
  355.  
  356. self.cats = cats
  357. self.cat_tables = cat_tables
  358.  
  359. def parse_goods_entry(self, data):
  360. goodies = OrderedDict()
  361. while True:
  362. index = read_short(data)
  363. if index < 0:
  364. break
  365.  
  366. name_jap = read_string(data)
  367.  
  368. unk1 = read_short(data) ##
  369.  
  370. shop_desc_jap = read_string(data)
  371. goodies_desc_jap = read_string(data)
  372.  
  373. food = read_char(data) == 1
  374.  
  375. unk2 = read_short(data) ##
  376.  
  377. # all three tend to be very similar
  378. unk3 = read_short(data) ##
  379. unk4 = read_short(data) ##
  380. unk5 = read_short(data) ##
  381.  
  382. # 0 - small spot, 1 - large spot, 3 - food spot
  383. spot = read_char(data)
  384.  
  385. goody_type = read_char(data)
  386.  
  387. name_eng = read_string(data)
  388. shop_desc_eng = read_string(data)
  389. goodies_desc_eng = read_string(data)
  390.  
  391. # this is 1 for Burger Cushion, Arabesque Blanket, and Cowboy Hat
  392. # otherwise it is 0
  393. unk7 = read_char(data) ##
  394.  
  395. # depends on events
  396. unk8 = [] ##
  397. for x in range(12):
  398. unk8.append(read_short(data))
  399.  
  400. goodies[index] = {
  401. 'name': name_eng,
  402. 'name_jap': name_jap,
  403. 'shop_desc': shop_desc_eng,
  404. 'goodies_desc': goodies_desc_eng,
  405. 'shop_desc_jap': shop_desc_jap,
  406. 'goodies_desc_jap': goodies_desc_jap,
  407. 'food': food,
  408. 'spot': spot,
  409. 'type': goody_type,
  410.  
  411. 'unk1': unk1,
  412. 'unk2': unk2,
  413. 'unk3': unk3,
  414. 'unk4': unk4,
  415. 'unk5': unk5,
  416. 'spot': spot,
  417. 'unk7': unk7,
  418. 'unk8': unk8,
  419. }
  420.  
  421. # goody-cat configurations
  422. goody_configs = OrderedDict()
  423. while True:
  424. index = read_short(data)
  425. if index < 0:
  426. break
  427.  
  428. goody_id = read_short(data)
  429. goody_config_id = read_short(data)
  430.  
  431. goody_config = []
  432. for i in range(6):
  433. pose = read_short(data)
  434. position = read_short(data)
  435. goody_config.append([pose, position % 10, position / 10])
  436.  
  437. #if goody_id not in goodies:
  438. # goodies[goody_id] = {'configs': {}}
  439.  
  440. #goodies[goody_id]['configs'][goody_config_id] = goody_config
  441.  
  442. goody_configs[index] = {
  443. 'goody': goody_id,
  444. 'config_num': goody_config_id,
  445. 'config': goody_config
  446. }
  447.  
  448. # cat pose to sprite sheet mapping
  449. cat_pose_sprites = OrderedDict()
  450. while True:
  451. index = read_short(data)
  452. if index < 0:
  453. break
  454.  
  455. spritesheet_num = read_short(data)
  456.  
  457. cat_pose_sprites[index] = spritesheet_num
  458.  
  459. # goody type definitions
  460. goody_types = OrderedDict()
  461. while True:
  462. index = read_short(data)
  463. if index < 0:
  464. break
  465.  
  466. name_jap = read_string(data)
  467.  
  468. unk1 = read_short(data) ##
  469.  
  470. type_id = read_short(data)
  471. assert index == type_id
  472.  
  473. goody_types[index] = {
  474. 'name_jap': name_jap,
  475.  
  476. 'unk1': unk1
  477. }
  478.  
  479. self.goodies = goodies
  480. self.goody_configs = goody_configs
  481. self.cat_pose_sprites = cat_pose_sprites
  482. self.goody_types = goody_types
  483.  
  484. def parse_shop_entry(self, data):
  485. shop_listsings = OrderedDict()
  486. while True:
  487. index = read_short(data)
  488. if index < 0 or index > 250:
  489. break
  490.  
  491. amount = read_short(data)
  492. gold_fish = read_char(data) == 1
  493. price = read_short(data)
  494.  
  495. shop_listsings[index] = {
  496. 'amount': amount,
  497. 'gold_fish': gold_fish,
  498. 'price': price,
  499. }
  500.  
  501. self.shop_listsings = shop_listsings
  502.  
  503. def parse_takara_entry(self, data):
  504. mementos = OrderedDict()
  505. while True:
  506. index = read_short(data)
  507. if index < 0:
  508. break
  509.  
  510. memento_id = index - 1
  511.  
  512. jap_name = read_string(data)
  513. jap_desc = read_string(data)
  514.  
  515. memento_id2 = read_short(data)
  516. assert memento_id == memento_id2
  517.  
  518. eng_name = read_string(data)
  519. eng_desc = read_string(data)
  520.  
  521. mementos[memento_id] = {
  522. 'name': eng_name,
  523. 'name_jap': jap_name,
  524. 'desc': eng_desc,
  525. 'desc_jap': jap_desc,
  526. }
  527.  
  528. self.mementos = mementos
  529.  
  530. if __name__ == '__main__':
  531. data = NekoAtsumeData('evt00_data-1.5.0.evt')
Add Comment
Please, Sign In to add comment