Advertisement
luckytyphlosion

gfx.py diff

Oct 29th, 2015
292
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Diff 62.42 KB | None | 0 0
  1. 7a8
  2. > import operator
  3. 12c13
  4. < import pokemon_constants
  5. ---
  6. > from pokemon_constants import pokemon_constants
  7. 15a17
  8. > from lz import Compressed, Decompressed
  9. 17,19c19,28
  10. < def load_rom():
  11. <     rom = romstr.RomStr.load(filename=config.rom_path)
  12. <     return rom
  13. ---
  14. >
  15. >
  16. > def load_rom(filename=config.rom_path):
  17. >     rom = romstr.RomStr.load(filename=filename)
  18. >     return bytearray(rom)
  19. >
  20. > def rom_offset(bank, address):
  21. >     if address < 0x4000 or address >= 0x8000:
  22. >         return address
  23. >     return bank * 0x4000 + address - 0x4000 * bool(bank)
  24. 69a79,83
  25. >
  26. >       00 01 02 03     00 04 08
  27. >       04 05 06 07 <-> 01 05 09
  28. >       08 09 0a 0b     02 06 0a
  29. >                       03 07 0b
  30. 117c131,137
  31. < def condense_tiles_to_map(image):
  32. ---
  33. > def condense_image_to_map(image, pic=0):
  34. >     """
  35. >     Reduce an image of adjacent frames to an image containing a base frame and any unrepeated tiles.
  36. >     Returns the new image and the corresponding tilemap used to reconstruct the input image.
  37. >
  38. >     If <pic> is 0, ignore the concept of frames. This behavior might be better off as another function.
  39. >     """
  40. 119,124c139
  41. <     new_tiles = []
  42. <     tilemap = []
  43. <     for tile in tiles:
  44. <         if tile not in new_tiles:
  45. <             new_tiles += [tile]
  46. <         tilemap += [new_tiles.index(tile)]
  47. ---
  48. >     new_tiles, tilemap = condense_tiles_to_map(tiles, pic)
  49. 127a143,146
  50. > def condense_tiles_to_map(tiles, pic=0):
  51. >     """
  52. >     Reduce a sequence of tiles representing adjacent frames to a base frame and any unrepeated tiles.
  53. >     Returns the new tiles and the corresponding tilemap used to reconstruct the input tile sequence.
  54. 129,422c148,149
  55. < def to_file(filename, data):
  56. <     file = open(filename, 'wb')
  57. <     for byte in data:
  58. <         file.write('%c' % byte)
  59. <     file.close()
  60. <
  61. <
  62. <
  63. < """
  64. < A rundown of Pokemon Crystal's compression scheme:
  65. <
  66. < Control commands occupy bits 5-7.
  67. < Bits 0-4 serve as the first parameter <n> for each command.
  68. < """
  69. < lz_commands = {
  70. <     'literal':   0, # n values for n bytes
  71. <     'iterate':   1, # one value for n bytes
  72. <     'alternate': 2, # alternate two values for n bytes
  73. <     'blank':     3, # zero for n bytes
  74. < }
  75. <
  76. < """
  77. < Repeater commands repeat any data that was just decompressed.
  78. < They take an additional signed parameter <s> to mark a relative starting point.
  79. < These wrap around (positive from the start, negative from the current position).
  80. < """
  81. < lz_commands.update({
  82. <     'repeat':    4, # n bytes starting from s
  83. <     'flip':      5, # n bytes in reverse bit order starting from s
  84. <     'reverse':   6, # n bytes backwards starting from s
  85. < })
  86. <
  87. < """
  88. < The long command is used when 5 bits aren't enough. Bits 2-4 contain a new control code.
  89. < Bits 0-1 are appended to a new byte as 8-9, allowing a 10-bit parameter.
  90. < """
  91. < lz_commands.update({
  92. <     'long':      7, # n is now 10 bits for a new control code
  93. < })
  94. < max_length = 1 << 10 # can't go higher than 10 bits
  95. < lowmax = 1 << 5 # standard 5-bit param
  96. <
  97. < """
  98. < If 0xff is encountered instead of a command, decompression ends.
  99. < """
  100. < lz_end = 0xff
  101. <
  102. <
  103. < class Compressed:
  104. <
  105. <     """
  106. <     Compress arbitrary data, usually 2bpp.
  107. <     """
  108. <
  109. <     def __init__(self, image=None, mode='horiz', size=None):
  110. <         assert image, 'need something to compress!'
  111. <         image = list(image)
  112. <         self.image = image
  113. <         self.pic = []
  114. <         self.animtiles = []
  115. <
  116. <         # only transpose pic (animtiles were never transposed in decompression)
  117. <         if size != None:
  118. <             for byte in range((size*size)*16):
  119. <                 self.pic += image[byte]
  120. <             for byte in range(((size*size)*16),len(image)):
  121. <                 self.animtiles += image[byte]
  122. <         else:
  123. <             self.pic = image
  124. <
  125. <         if mode == 'vert':
  126. <             self.tiles = get_tiles(self.pic)
  127. <             self.tiles = transpose(self.tiles)
  128. <             self.pic = connect(self.tiles)
  129. <
  130. <         self.image = self.pic + self.animtiles
  131. <
  132. <         self.end = len(self.image)
  133. <
  134. <         self.byte = None
  135. <         self.address = 0
  136. <
  137. <         self.stream = []
  138. <
  139. <         self.zeros = []
  140. <         self.alts = []
  141. <         self.iters = []
  142. <         self.repeats = []
  143. <         self.flips = []
  144. <         self.reverses = []
  145. <         self.literals = []
  146. <
  147. <         self.output = []
  148. <
  149. <         self.compress()
  150. <
  151. <
  152. <     def compress(self):
  153. <         """
  154. <         Incomplete, but outputs working compressed data.
  155. <         """
  156. <
  157. <         self.address = 0
  158. <
  159. <         # todo
  160. <         #self.scanRepeats()
  161. <
  162. <         while ( self.address < self.end ):
  163. <
  164. <             #if (self.repeats):
  165. <             #   self.doRepeats()
  166. <
  167. <             #if (self.flips):
  168. <             #   self.doFlips()
  169. <
  170. <             #if (self.reverses):
  171. <             #   self.doReverses
  172. <
  173. <             if (self.checkWhitespace()):
  174. <                 self.doLiterals()
  175. <                 self.doWhitespace()
  176. <
  177. <             elif (self.checkIter()):
  178. <                 self.doLiterals()
  179. <                 self.doIter()
  180. <
  181. <             elif (self.checkAlts()):
  182. <                 self.doLiterals()
  183. <                 self.doAlts()
  184. <
  185. <             else: # doesn't fit any pattern -> literal
  186. <                 self.addLiteral()
  187. <                 self.next()
  188. <
  189. <             self.doStream()
  190. <
  191. <         # add any literals we've been sitting on
  192. <         self.doLiterals()
  193. <
  194. <         # done
  195. <         self.output.append(lz_end)
  196. <
  197. <
  198. <     def getCurByte(self):
  199. <         if self.address < self.end:
  200. <             self.byte = ord(self.image[self.address])
  201. <         else: self.byte = None
  202. <
  203. <     def next(self):
  204. <         self.address += 1
  205. <         self.getCurByte()
  206. <
  207. <     def addLiteral(self):
  208. <         self.getCurByte()
  209. <         self.literals.append(self.byte)
  210. <         if len(self.literals) > max_length:
  211. <             raise Exception, "literals exceeded max length and the compressor didn't catch it"
  212. <         elif len(self.literals) == max_length:
  213. <             self.doLiterals()
  214. <
  215. <     def doLiterals(self):
  216. <         if len(self.literals) > lowmax:
  217. <             self.output.append( (lz_commands['long'] << 5) | (lz_commands['literal'] << 2) | ((len(self.literals) - 1) >> 8) )
  218. <             self.output.append( (len(self.literals) - 1) & 0xff )
  219. <         elif len(self.literals) > 0:
  220. <             self.output.append( (lz_commands['literal'] << 5) | (len(self.literals) - 1) )
  221. <         for byte in self.literals:
  222. <             self.output.append(byte)
  223. <         self.literals = []
  224. <
  225. <     def doStream(self):
  226. <         for byte in self.stream:
  227. <             self.output.append(byte)
  228. <         self.stream = []
  229. <
  230. <
  231. <     def scanRepeats(self):
  232. <         """
  233. <         Works, but doesn't do flipped/reversed streams yet.
  234. <
  235. <         This takes up most of the compress time and only saves a few bytes.
  236. <         It might be more effective to exclude it entirely.
  237. <         """
  238. <
  239. <         self.repeats = []
  240. <         self.flips = []
  241. <         self.reverses = []
  242. <
  243. <         # make a 5-letter word list of the sequence
  244. <         letters = 5 # how many bytes it costs to use a repeat over a literal
  245. <         # any shorter and it's not worth the trouble
  246. <         num_words = len(self.image) - letters
  247. <         words = []
  248. <         for i in range(self.address,num_words):
  249. <             word = []
  250. <             for j in range(letters):
  251. <                 word.append( ord(self.image[i+j]) )
  252. <             words.append((word, i))
  253. <
  254. <             zeros = []
  255. <             for zero in range(letters):
  256. <                 zeros.append( 0 )
  257. <
  258. <         # check for matches
  259. <         def get_matches():
  260. <         # TODO:
  261. <         # append to 3 different match lists instead of yielding to one
  262. <         #
  263. <         #flipped = []
  264. <         #for byte in enumerate(this[0]):
  265. <         #   flipped.append( sum(1<<(7-i) for i in range(8) if (this[0][byte])>>i&1) )
  266. <         #reversed = this[0][::-1]
  267. <         #
  268. <             for whereabout, this in enumerate(words):
  269. <                 for that in range(whereabout+1,len(words)):
  270. <                     if words[that][0] == this[0]:
  271. <                         if words[that][1] - this[1] >= letters:
  272. <                             # remove zeros
  273. <                             if this[0] != zeros:
  274. <                                 yield [this[0], this[1], words[that][1]]
  275. <
  276. <         matches = list(get_matches())
  277. <
  278. <         # remove more zeros
  279. <         buffer = []
  280. <         for match in matches:
  281. <             # count consecutive zeros in a word
  282. <             num_zeros = 0
  283. <             highest = 0
  284. <             for j in range(letters):
  285. <                 if match[0][j] == 0:
  286. <                     num_zeros += 1
  287. <                 else:
  288. <                     if highest < num_zeros: highest = num_zeros
  289. <                     num_zeros = 0
  290. <             if highest < 4:
  291. <                 # any more than 3 zeros in a row isn't worth it
  292. <                 # (and likely to already be accounted for)
  293. <                 buffer.append(match)
  294. <         matches = buffer
  295. <
  296. <         # combine overlapping matches
  297. <         buffer = []
  298. <         for this, match in enumerate(matches):
  299. <             if this < len(matches) - 1: # special case for the last match
  300. <                 if matches[this+1][1] <= (match[1] + len(match[0])): # check overlap
  301. <                     if match[1] + len(match[0]) < match[2]:
  302. <                         # next match now contains this match's bytes too
  303. <                         # this only appends the last byte (assumes overlaps are +1
  304. <                         match[0].append(matches[this+1][0][-1])
  305. <                         matches[this+1] = match
  306. <                     elif match[1] + len(match[0]) == match[2]:
  307. <                         # we've run into the thing we matched
  308. <                         buffer.append(match)
  309. <                     # else we've gone past it and we can ignore it
  310. <                 else: # no more overlaps
  311. <                     buffer.append(match)
  312. <             else: # last match, so there's nothing to check
  313. <                 buffer.append(match)
  314. <         matches = buffer
  315. <
  316. <         # remove alternating sequences
  317. <         buffer = []
  318. <         for match in matches:
  319. <             for i in range(6 if letters > 6 else letters):
  320. <                 if match[0][i] != match[0][i&1]:
  321. <                     buffer.append(match)
  322. <                     break
  323. <         matches = buffer
  324. <
  325. <         self.repeats = matches
  326. <
  327. <
  328. <     def doRepeats(self):
  329. <         """doesn't output the right values yet"""
  330. <
  331. <         unusedrepeats = []
  332. <         for repeat in self.repeats:
  333. <             if self.address >= repeat[2]:
  334. <
  335. <                 # how far in we are
  336. <                 length = (len(repeat[0]) - (self.address - repeat[2]))
  337. <
  338. <                 # decide which side we're copying from
  339. <                 if (self.address - repeat[1]) <= 0x80:
  340. <                     self.doLiterals()
  341. <                     self.stream.append( (lz_commands['repeat'] << 5) | length - 1 )
  342. <
  343. <                     # wrong?
  344. <                     self.stream.append( (((self.address - repeat[1])^0xff)+1)&0xff )
  345. <
  346. <                 else:
  347. <                     self.doLiterals()
  348. <                     self.stream.append( (lz_commands['repeat'] << 5) | length - 1 )
  349. ---
  350. >     If <pic> is 0, ignore the concept of frames. This behavior might be better off as another function.
  351. >     """
  352. 424,457c151,153
  353. <                     # wrong?
  354. <                     self.stream.append(repeat[1]>>8)
  355. <                     self.stream.append(repeat[1]&0xff)
  356. <
  357. <                 #print hex(self.address) + ': ' + hex(len(self.output)) + ' ' + hex(length)
  358. <                 self.address += length
  359. <
  360. <             else: unusedrepeats.append(repeat)
  361. <
  362. <         self.repeats = unusedrepeats
  363. <
  364. <
  365. <     def checkWhitespace(self):
  366. <         self.zeros = []
  367. <         self.getCurByte()
  368. <         original_address = self.address
  369. <
  370. <         if ( self.byte == 0 ):
  371. <             while ( self.byte == 0 ) & ( len(self.zeros) <= max_length ):
  372. <                 self.zeros.append(self.byte)
  373. <                 self.next()
  374. <             if len(self.zeros) > 1:
  375. <                 return True
  376. <         self.address = original_address
  377. <         return False
  378. <
  379. <     def doWhitespace(self):
  380. <         if (len(self.zeros) + 1) >= lowmax:
  381. <             self.stream.append( (lz_commands['long'] << 5) | (lz_commands['blank'] << 2) | ((len(self.zeros) - 1) >> 8) )
  382. <             self.stream.append( (len(self.zeros) - 1) & 0xff )
  383. <         elif len(self.zeros) > 1:
  384. <             self.stream.append( lz_commands['blank'] << 5 | (len(self.zeros) - 1) )
  385. <         else:
  386. <             raise Exception, "checkWhitespace() should prevent this from happening"
  387. ---
  388. >     # Leave the first frame intact for pics.
  389. >     new_tiles = tiles[:pic]
  390. >     tilemap   = range(pic)
  391. 458a155,157
  392. >     for i, tile in enumerate(tiles[pic:]):
  393. >         if tile not in new_tiles:
  394. >             new_tiles.append(tile)
  395. 460,515c159,169
  396. <     def checkAlts(self):
  397. <         self.alts = []
  398. <         self.getCurByte()
  399. <         original_address = self.address
  400. <         num_alts = 0
  401. <
  402. <         # make sure we don't check for alts at the end of the file
  403. <         if self.address+3 >= self.end: return False
  404. <
  405. <         self.alts.append(self.byte)
  406. <         self.alts.append(ord(self.image[self.address+1]))
  407. <
  408. <         # are we onto smething?
  409. <         if ( ord(self.image[self.address+2]) == self.alts[0] ):
  410. <             cur_alt = 0
  411. <             while (ord(self.image[(self.address)+1]) == self.alts[num_alts&1]) & (num_alts <= max_length):
  412. <                 num_alts += 1
  413. <                 self.next()
  414. <             # include the last alternated byte
  415. <             num_alts += 1
  416. <             self.address = original_address
  417. <             if num_alts > lowmax:
  418. <                 return True
  419. <             elif num_alts > 2:
  420. <                 return True
  421. <         return False
  422. <
  423. <     def doAlts(self):
  424. <         original_address = self.address
  425. <         self.getCurByte()
  426. <
  427. <         #self.alts = []
  428. <         #num_alts = 0
  429. <
  430. <         #self.alts.append(self.byte)
  431. <         #self.alts.append(ord(self.image[self.address+1]))
  432. <
  433. <         #i = 0
  434. <         #while (ord(self.image[self.address+1]) == self.alts[i^1]) & (num_alts <= max_length):
  435. <         #   num_alts += 1
  436. <         #   i ^=1
  437. <         #   self.next()
  438. <         ## include the last alternated byte
  439. <         #num_alts += 1
  440. <
  441. <         num_alts = len(self.iters) + 1
  442. <
  443. <         if num_alts > lowmax:
  444. <             self.stream.append( (lz_commands['long'] << 5) | (lz_commands['alternate'] << 2) | ((num_alts - 1) >> 8) )
  445. <             self.stream.append( num_alts & 0xff )
  446. <             self.stream.append( self.alts[0] )
  447. <             self.stream.append( self.alts[1] )
  448. <         elif num_alts > 2:
  449. <             self.stream.append( (lz_commands['alternate'] << 5) | (num_alts - 1) )
  450. <             self.stream.append( self.alts[0] )
  451. <             self.stream.append( self.alts[1] )
  452. ---
  453. >         if pic:
  454. >             # Match the first frame exactly where possible.
  455. >             # This reduces the space needed to replace tiles in pic animations.
  456. >             # For example, if a tile is repeated twice in the first frame,
  457. >             # but at the same relative index as the second tile, use the second index.
  458. >             # When creating a bitmask later, the second index would not require a replacement, but the first index would have.
  459. >             pic_i = i % pic
  460. >             if tile == new_tiles[pic_i]:
  461. >                 tilemap.append(pic_i)
  462. >             else:
  463. >                 tilemap.append(new_tiles.index(tile))
  464. 517,521c171,172
  465. <             raise Exception, "checkAlts() should prevent this from happening"
  466. <
  467. <         self.address = original_address
  468. <         self.address += num_alts
  469. <
  470. ---
  471. >             tilemap.append(new_tiles.index(tile))
  472. >     return new_tiles, tilemap
  473. 523,560c174,186
  474. <     def checkIter(self):
  475. <         self.iters = []
  476. <         self.getCurByte()
  477. <         iter = self.byte
  478. <         original_address = self.address
  479. <         while (self.byte == iter) & (len(self.iters) < max_length):
  480. <             self.iters.append(self.byte)
  481. <             self.next()
  482. <         self.address = original_address
  483. <         if len(self.iters) > 3:
  484. <             # 3 or fewer isn't worth the trouble and actually longer
  485. <             # if part of a larger literal set
  486. <             return True
  487. <
  488. <         return False
  489. <
  490. <     def doIter(self):
  491. <         self.getCurByte()
  492. <         iter = self.byte
  493. <         original_address = self.address
  494. <
  495. <         self.iters = []
  496. <         while (self.byte == iter) & (len(self.iters) < max_length):
  497. <             self.iters.append(self.byte)
  498. <             self.next()
  499. <
  500. <         if (len(self.iters) - 1) >= lowmax:
  501. <             self.stream.append( (lz_commands['long'] << 5) | (lz_commands['iterate'] << 2) | ((len(self.iters)-1) >> 8) )
  502. <             self.stream.append( (len(self.iters) - 1) & 0xff )
  503. <             self.stream.append( iter )
  504. <         elif len(self.iters) > 3:
  505. <             # 3 or fewer isn't worth the trouble and actually longer
  506. <             # if part of a larger literal set
  507. <             self.stream.append( (lz_commands['iterate'] << 5) | (len(self.iters) - 1) )
  508. <             self.stream.append( iter )
  509. <         else:
  510. <             self.address = original_address
  511. <             raise Exception, "checkIter() should prevent this from happening"
  512. ---
  513. > def test_condense_tiles_to_map():
  514. >     test = condense_tiles_to_map(list('abcadbae'))
  515. >     if test != (list('abcde'), [0, 1, 2, 0, 3, 1, 0, 4]):
  516. >         raise Exception(test)
  517. >     test = condense_tiles_to_map(list('abcadbae'), 2)
  518. >     if test != (list('abcde'), [0, 1, 2, 0, 3, 1, 0, 4]):
  519. >         raise Exception(test)
  520. >     test = condense_tiles_to_map(list('abcadbae'), 4)
  521. >     if test != (list('abcade'), [0, 1, 2, 3, 4, 1, 0, 5]):
  522. >         raise Exception(test)
  523. >     test = condense_tiles_to_map(list('abcadbea'), 4)
  524. >     if test != (list('abcade'), [0, 1, 2, 3, 4, 1, 5, 3]):
  525. >         raise Exception(test)
  526. 563c189
  527. < class Decompressed:
  528. ---
  529. > def to_file(filename, data):
  530. 565,574c191
  531. <     Parse compressed data, usually 2bpp.
  532. <
  533. <     parameters:
  534. <         [compressed data]
  535. <         [tile arrangement] default: 'vert'
  536. <         [size of pic] default: None
  537. <         [start] (optional)
  538. <
  539. <     splits output into pic [size] and animation tiles if applicable
  540. <     data can be fed in from rom if [start] is specified
  541. ---
  542. >     Apparently open(filename, 'wb').write(bytearray(data)) won't work.
  543. 575a193,196
  544. >     file = open(filename, 'wb')
  545. >     for byte in data:
  546. >         file.write('%c' % byte)
  547. >     file.close()
  548. 577,626d197
  549. <     def __init__(self, lz=None, mode=None, size=None, start=0):
  550. <         # todo: play nice with Compressed
  551. <
  552. <         assert lz, 'need something to compress!'
  553. <         self.lz = lz
  554. <
  555. <         self.byte = None
  556. <         self.address = 0
  557. <         self.start = start
  558. <
  559. <         self.output = []
  560. <
  561. <         self.decompress()
  562. <
  563. <         debug = False
  564. <         # print tuple containing start and end address
  565. <         if debug: print '(' + hex(self.start) + ', ' + hex(self.start + self.address+1) + '),'
  566. <
  567. <         # only transpose pic
  568. <         self.pic = []
  569. <         self.animtiles = []
  570. <
  571. <         if size != None:
  572. <             self.tiles = get_tiles(self.output)
  573. <             self.pic = connect(self.tiles[:(size*size)])
  574. <             self.animtiles = connect(self.tiles[(size*size):])
  575. <         else: self.pic = self.output
  576. <
  577. <         if mode == 'vert':
  578. <             self.tiles = get_tiles(self.pic)
  579. <             self.tiles = transpose(self.tiles)
  580. <             self.pic = connect(self.tiles)
  581. <
  582. <         self.output = self.pic + self.animtiles
  583. <
  584. <
  585. <     def decompress(self):
  586. <         """
  587. <         Replica of crystal's decompression.
  588. <         """
  589. <
  590. <         self.output = []
  591. <
  592. <         while True:
  593. <             self.getCurByte()
  594. <
  595. <             if (self.byte == lz_end):
  596. <                 break
  597. <
  598. <             self.cmd = (self.byte & 0b11100000) >> 5
  599. 628,733d198
  600. <             if self.cmd == lz_commands['long']: # 10-bit param
  601. <                 self.cmd = (self.byte & 0b00011100) >> 2
  602. <                 self.length = (self.byte & 0b00000011) << 8
  603. <                 self.next()
  604. <                 self.length += self.byte + 1
  605. <             else: # 5-bit param
  606. <                 self.length = (self.byte & 0b00011111) + 1
  607. <
  608. <             # literals
  609. <             if self.cmd == lz_commands['literal']:
  610. <                 self.doLiteral()
  611. <             elif self.cmd == lz_commands['iterate']:
  612. <                 self.doIter()
  613. <             elif self.cmd == lz_commands['alternate']:
  614. <                 self.doAlt()
  615. <             elif self.cmd == lz_commands['blank']:
  616. <                 self.doZeros()
  617. <
  618. <             else: # repeaters
  619. <                 self.next()
  620. <                 if self.byte > 0x7f: # negative
  621. <                     self.displacement = self.byte & 0x7f
  622. <                     self.displacement = len(self.output) - self.displacement - 1
  623. <                 else: # positive
  624. <                     self.displacement = self.byte * 0x100
  625. <                     self.next()
  626. <                     self.displacement += self.byte
  627. <
  628. <                 if self.cmd == lz_commands['flip']:
  629. <                     self.doFlip()
  630. <                 elif self.cmd == lz_commands['reverse']:
  631. <                     self.doReverse()
  632. <                 else: # lz_commands['repeat']
  633. <                     self.doRepeat()
  634. <
  635. <             self.address += 1
  636. <             #self.next() # somewhat of a hack
  637. <
  638. <
  639. <     def getCurByte(self):
  640. <         self.byte = ord(self.lz[self.start+self.address])
  641. <
  642. <     def next(self):
  643. <         self.address += 1
  644. <         self.getCurByte()
  645. <
  646. <     def doLiteral(self):
  647. <         """
  648. <         Copy data directly.
  649. <         """
  650. <         for byte in range(self.length):
  651. <             self.next()
  652. <             self.output.append(self.byte)
  653. <
  654. <     def doIter(self):
  655. <         """
  656. <         Write one byte repeatedly.
  657. <         """
  658. <         self.next()
  659. <         for byte in range(self.length):
  660. <             self.output.append(self.byte)
  661. <
  662. <     def doAlt(self):
  663. <         """
  664. <         Write alternating bytes.
  665. <         """
  666. <         self.alts = []
  667. <         self.next()
  668. <         self.alts.append(self.byte)
  669. <         self.next()
  670. <         self.alts.append(self.byte)
  671. <
  672. <         for byte in range(self.length):
  673. <             self.output.append(self.alts[byte&1])
  674. <
  675. <     def doZeros(self):
  676. <         """
  677. <         Write zeros.
  678. <         """
  679. <         for byte in range(self.length):
  680. <             self.output.append(0x00)
  681. <
  682. <     def doFlip(self):
  683. <         """
  684. <         Repeat flipped bytes from output.
  685. <
  686. <         eg  11100100 -> 00100111
  687. <         quat 3 2 1 0 ->  0 2 1 3
  688. <         """
  689. <         for byte in range(self.length):
  690. <             flipped = sum(1<<(7-i) for i in range(8) if self.output[self.displacement+byte]>>i&1)
  691. <             self.output.append(flipped)
  692. <
  693. <     def doReverse(self):
  694. <         """
  695. <         Repeat reversed bytes from output.
  696. <         """
  697. <         for byte in range(self.length):
  698. <             self.output.append(self.output[self.displacement-byte])
  699. <
  700. <     def doRepeat(self):
  701. <         """
  702. <         Repeat bytes from output.
  703. <         """
  704. <         for byte in range(self.length):
  705. <             self.output.append(self.output[self.displacement+byte])
  706. 756c221
  707. < def make_sizes():
  708. ---
  709. > def make_sizes(num_monsters=251):
  710. 761d225
  711. <     top = 251
  712. 763,766d226
  713. <     # print monster sizes
  714. <     address = base_stats + 0x11
  715. <
  716. <     output = ''
  717. 768,772c228,231
  718. <     for id in range(top):
  719. <         size = (ord(rom[address])) & 0x0f
  720. <         if id % 16 == 0: output += '\n\t'
  721. <         output += str(size) + ', '
  722. <         address += 0x20
  723. ---
  724. >     address = base_stats + 0x11 # pic size
  725. >     sizes   = rom[address : address + 0x20 * num_monsters : 0x20]
  726. >     sizes   = map(lambda x: str(x & 0xf), sizes)
  727. >     return '\n'.join(' ' * 8 + ', '.join(split(sizes, 16)))
  728. 774d232
  729. <     print output
  730. 775a234,236
  731. > def decompress_fx_by_id(i, fxs=0xcfcf6):
  732. >     rom = load_rom()
  733. >     addr = fxs + i * 4
  734. 776a238,240
  735. >     num_tiles = rom[addr]
  736. >     bank      = rom[addr+1]
  737. >     address   = rom[addr+3] * 0x100 + rom[addr+2]
  738. 778,788c242,243
  739. < def decompress_fx_by_id(id, fxs=0xcfcf6):
  740. <     rom = load_rom()
  741. <     address = fxs + id*4 # len_fxptr
  742. <     # get size
  743. <     num_tiles = ord(rom[address]) # # tiles
  744. <     # get pointer
  745. <     bank = ord(rom[address+1])
  746. <     address = (ord(rom[address+3]) << 8) + ord(rom[address+2])
  747. <     address = (bank * 0x4000) + (address & 0x3fff)
  748. <     # decompress
  749. <     fx = Decompressed(rom, 'horiz', num_tiles, address)
  750. ---
  751. >     offset = rom_offset(bank, address)
  752. >     fx = Decompressed(rom, start=offset)
  753. 791,795c246,251
  754. < def decompress_fx(num_fx=40):
  755. <     for id in range(num_fx):
  756. <         fx = decompress_fx_by_id(id)
  757. <         filename = './gfx/fx/' + str(id).zfill(3) + '.2bpp' # ./gfx/fx/039.2bpp
  758. <         to_file(filename, fx.pic)
  759. ---
  760. > def rip_compressed_fx(dest='gfx/fx', num_fx=40, fxs=0xcfcf6):
  761. >     for i in xrange(num_fx):
  762. >         name = '%.3d' % i
  763. >         fx = decompress_fx_by_id(i, fxs)
  764. >         filename = os.path.join(dest, name + '.2bpp.lz')
  765. >         to_file(filename, fx.compressed_data)
  766. 798,801d253
  767. < num_pics = 2
  768. < front = 0
  769. < back = 1
  770. <
  771. 809,823c261,274
  772. < def decompress_monster_by_id(id=0, type=front):
  773. <     rom = load_rom()
  774. <     # no unowns here
  775. <     if id + 1 == unown_dex: return None
  776. <     # get size
  777. <     if type == front:
  778. <         size = sizes[id]
  779. <     else: size = None
  780. <     # get pointer
  781. <     address = monsters + (id*2 + type)*3 # bank, address
  782. <     bank = ord(rom[address]) + 0x36 # crystal
  783. <     address = (ord(rom[address+2]) << 8) + ord(rom[address+1])
  784. <     address = (bank * 0x4000) + (address & 0x3fff)
  785. <     # decompress
  786. <     monster = Decompressed(rom, 'vert', size, address)
  787. ---
  788. > def decompress_monster_by_id(rom, mon=0, face='front', crystal=True):
  789. >     """
  790. >     For Unown, use decompress_unown_by_id instead.
  791. >     """
  792. >     if crystal:
  793. >         bank_offset = 0x36
  794. >     else:
  795. >         bank_offset = 0
  796. >
  797. >     address = monsters + (mon * 2 + {'front': 0, 'back': 1}.get(face, 0)) * 3
  798. >     bank    = rom[address] + bank_offset
  799. >     address = rom[address+2] * 0x100 + rom[address+1]
  800. >     address = bank * 0x4000 + (address - (0x4000 * bool(bank)))
  801. >     monster = Decompressed(rom, start=address)
  802. 826,841c277,281
  803. < def decompress_monsters(type=front):
  804. <     for id in range(num_monsters):
  805. <         # decompress
  806. <         monster = decompress_monster_by_id(id, type)
  807. <         if monster != None: # no unowns here
  808. <             if not type: # front
  809. <                 filename = 'front.2bpp'
  810. <                 folder = './gfx/pics/' + str(id+1).zfill(3) + '/'
  811. <                 to_file(folder+filename, monster.pic)
  812. <                 filename = 'tiles.2bpp'
  813. <                 folder = './gfx/pics/' + str(id+1).zfill(3) + '/'
  814. <                 to_file(folder+filename, monster.animtiles)
  815. <             else: # back
  816. <                 filename = 'back.2bpp'
  817. <                 folder = './gfx/pics/' + str(id+1).zfill(3) + '/'
  818. <                 to_file(folder+filename, monster.pic)
  819. ---
  820. > def rip_compressed_monster_pics(rom, dest='gfx/pics/', face='both', num_mons=num_monsters, crystal=True):
  821. >     """
  822. >     Extract <num_mons> compressed Pokemon pics from <rom> to directory <dest>.
  823. >     """
  824. >     for mon in range(num_mons):
  825. 842a283,284
  826. >         mon_name = pokemon_constants[mon + 1].lower().replace('__','_')
  827. >         size = sizes[mon]
  828. 844,856c286,319
  829. < def decompress_unown_by_id(letter, type=front):
  830. <     rom = load_rom()
  831. <     # get size
  832. <     if type == front:
  833. <         size = sizes[unown_dex-1]
  834. <     else: size = None
  835. <     # get pointer
  836. <     address = unowns + (letter*2 + type)*3 # bank, address
  837. <     bank = ord(rom[address]) + 0x36 # crystal
  838. <     address = (ord(rom[address+2]) << 8) + ord(rom[address+1])
  839. <     address = (bank * 0x4000) + (address & 0x3fff)
  840. <     # decompress
  841. <     unown = Decompressed(rom, 'vert', size, address)
  842. ---
  843. >         if mon + 1 == unown_dex:
  844. >             rip_compressed_unown_pics(
  845. >                 rom=rom,
  846. >                 dest=dest,
  847. >                 face=face,
  848. >                 num_letters=num_unowns,
  849. >                 mon_name=mon_name,
  850. >                 size=size,
  851. >                 crystal=crystal,
  852. >             )
  853. >
  854. >         if face in ['front', 'both']:
  855. >             monster  = decompress_monster_by_id(rom, mon, 'front', crystal)
  856. >             filename = 'front.{0}x{0}.2bpp.lz'.format(size)
  857. >             path     = os.path.join(dest, mon_name, filename)
  858. >             to_file(path, monster.compressed_data)
  859. >
  860. >         if face in ['back', 'both']:
  861. >             monster  = decompress_monster_by_id(rom, mon, 'back', crystal)
  862. >             filename = 'back.6x6.2bpp.lz'
  863. >             path     = os.path.join(dest, mon_name, filename)
  864. >             to_file(path, monster.compressed_data)
  865. >
  866. > def decompress_unown_by_id(rom, letter, face='front', crystal=True):
  867. >     if crystal:
  868. >         bank_offset = 0x36
  869. >     else:
  870. >         bank_offset = 0
  871. >
  872. >     address = unowns + (letter * 2 + {'front': 0, 'back': 1}.get(face, 0)) * 3
  873. >     bank    = rom[address] + bank_offset
  874. >     address = rom[address+2] * 0x100 + rom[address+1]
  875. >     address = (bank * 0x4000) + (address - (0x4000 * bool(bank)))
  876. >     unown   = Decompressed(rom, start=address)
  877. 859,874c322,339
  878. < def decompress_unowns(type=front):
  879. <     for letter in range(num_unowns):
  880. <         # decompress
  881. <         unown = decompress_unown_by_id(letter, type)
  882. <
  883. <         if not type: # front
  884. <             filename = 'front.2bpp'
  885. <             folder = './gfx/pics/' + str(unown_dex).zfill(3) + chr(ord('a') + letter) + '/'
  886. <             to_file(folder+filename, unown.pic)
  887. <             filename = 'tiles.2bpp'
  888. <             folder = './gfx/anim/'
  889. <             to_file(folder+filename, unown.animtiles)
  890. <         else: # back
  891. <             filename = 'back.2bpp'
  892. <             folder = './gfx/pics/' + str(unown_dex).zfill(3) + chr(ord('a') + letter) + '/'
  893. <             to_file(folder+filename, unown.pic)
  894. ---
  895. > def rip_compressed_unown_pics(rom, dest='gfx/pics/', face='both', num_letters=num_unowns, mon_name='unown', size=sizes[201], crystal=True):
  896. >     """
  897. >     Extract <num_letters> compressed Unown pics from <rom> to directory <dest>.
  898. >     """
  899. >     for letter in range(num_letters):
  900. >         name = mon_name + '_{}'.format(chr(ord('A') + letter))
  901. >
  902. >         if face in ['front', 'both']:
  903. >             unown    = decompress_unown_by_id(rom, letter, 'front', crystal)
  904. >             filename = 'front.{0}x{0}.2bpp.lz'.format(size)
  905. >             path     = os.path.join(dest, name, filename)
  906. >             to_file(path, unown.compressed_data)
  907. >
  908. >         if face in ['back', 'both']:
  909. >             unown    = decompress_unown_by_id(rom, letter, 'back', crystal)
  910. >             filename = 'back.6x6.2bpp.lz'
  911. >             path     = os.path.join(dest, name, filename)
  912. >             to_file(path, unown.compressed_data)
  913. 877c342
  914. < trainers = 0x128000
  915. ---
  916. > trainers_offset = 0x128000
  917. 878a344
  918. > trainer_names = [t['constant'] for i, t in trainers.trainer_group_names.items()]
  919. 880c346
  920. < def decompress_trainer_by_id(id):
  921. ---
  922. > def decompress_trainer_by_id(rom, i, crystal=True):
  923. 882,888c348,357
  924. <     # get pointer
  925. <     address = trainers + id*3 # bank, address
  926. <     bank = ord(rom[address]) + 0x36 # crystal
  927. <     address = (ord(rom[address+2]) << 8) + ord(rom[address+1])
  928. <     address = (bank * 0x4000) + (address & 0x3fff)
  929. <     # decompress
  930. <     trainer = Decompressed(rom, 'vert', None, address)
  931. ---
  932. >     if crystal:
  933. >         bank_offset = 0x36
  934. >     else:
  935. >         bank_offset = 0
  936. >
  937. >     address = trainers_offset + i * 3
  938. >     bank    = rom[address] + bank_offset
  939. >     address = rom[address+2] * 0x100 + rom[address+1]
  940. >     address = rom_offset(bank, address)
  941. >     trainer = Decompressed(rom, start=address)
  942. 891,896c360,365
  943. < def decompress_trainers():
  944. <     for id in range(num_trainers):
  945. <         # decompress
  946. <         trainer = decompress_trainer_by_id(id)
  947. <         filename = './gfx/trainers/' + str(id).zfill(3) + '.2bpp' # ./gfx/trainers/066.2bpp
  948. <         to_file(filename, trainer.pic)
  949. ---
  950. > def rip_compressed_trainer_pics(rom):
  951. >     for t in xrange(num_trainers):
  952. >         trainer_name = trainer_names[t].lower().replace('_','')
  953. >         trainer  = decompress_trainer_by_id(t)
  954. >         filename = os.path.join('gfx/trainers/', trainer_name + '.6x6.2bpp.lz')
  955. >         to_file(filename, trainer.compressed_data)
  956. 899c368
  957. < # in order of use (sans repeats)
  958. ---
  959. > # in order of use (besides repeats)
  960. 901,925c370,379
  961. <     ('logo', 0x109407),
  962. <     ('001', 0xE641D), # tilemap
  963. <     ('unowns', 0xE5F5D),
  964. <     ('pulse', 0xE634D),
  965. <     ('002', 0xE63DD), # tilemap
  966. <     ('003', 0xE5ECD), # tilemap
  967. <     ('background', 0xE5C7D),
  968. <     ('004', 0xE5E6D), # tilemap
  969. <     ('005', 0xE647D), # tilemap
  970. <     ('006', 0xE642D), # tilemap
  971. <     ('pichu_wooper', 0xE592D),
  972. <     ('suicune_run', 0xE555D),
  973. <     ('007', 0xE655D), # tilemap
  974. <     ('008', 0xE649D), # tilemap
  975. <     ('009', 0xE76AD), # tilemap
  976. <     ('suicune_jump', 0xE6DED),
  977. <     ('unown_back', 0xE785D),
  978. <     ('010', 0xE764D), # tilemap
  979. <     ('011', 0xE6D0D), # tilemap
  980. <     ('suicune_close', 0xE681D),
  981. <     ('012', 0xE6C3D), # tilemap
  982. <     ('013', 0xE778D), # tilemap
  983. <     ('suicune_back', 0xE72AD),
  984. <     ('014', 0xE76BD), # tilemap
  985. <     ('015', 0xE676D), # tilemap
  986. ---
  987. >     ('logo',          0x109407),
  988. >     ('unowns',         0xE5F5D),
  989. >     ('pulse',          0xE634D),
  990. >     ('background',     0xE5C7D),
  991. >     ('pichu_wooper',   0xE592D),
  992. >     ('suicune_run',    0xE555D),
  993. >     ('suicune_jump',   0xE6DED),
  994. >     ('unown_back',     0xE785D),
  995. >     ('suicune_close',  0xE681D),
  996. >     ('suicune_back',   0xE72AD),
  997. 927d380
  998. <     ('017', 0xE672D), # tilemap
  999. 930,931c383,403
  1000. < def decompress_intro():
  1001. <     rom = load_rom()
  1002. ---
  1003. > intro_tilemaps = [
  1004. >     ('001', 0xE641D),
  1005. >     ('002', 0xE63DD),
  1006. >     ('003', 0xE5ECD),
  1007. >     ('004', 0xE5E6D),
  1008. >     ('005', 0xE647D),
  1009. >     ('006', 0xE642D),
  1010. >     ('007', 0xE655D),
  1011. >     ('008', 0xE649D),
  1012. >     ('009', 0xE76AD),
  1013. >     ('010', 0xE764D),
  1014. >     ('011', 0xE6D0D),
  1015. >     ('012', 0xE6C3D),
  1016. >     ('013', 0xE778D),
  1017. >     ('014', 0xE76BD),
  1018. >     ('015', 0xE676D),
  1019. >     ('017', 0xE672D),
  1020. > ]
  1021. >
  1022. > def rip_compressed_intro(rom, dest='gfx/intro'):
  1023. >
  1024. 933,935c405,410
  1025. <         filename = './gfx/intro/' + name + '.2bpp'
  1026. <         gfx = Decompressed( rom, 'horiz', None, address )
  1027. <         to_file(filename, gfx.output)
  1028. ---
  1029. >         filename = os.path.join(dest, name + '.2bpp.lz')
  1030. >         rip_compressed_gfx(rom, address, filename)
  1031. >
  1032. >     for name, address in intro_tilemaps:
  1033. >         filename = os.path.join(dest, name + '.tilemap.lz')
  1034. >         rip_compressed_gfx(rom, address, filename)
  1035. 940c415
  1036. <     ('logo', 0x10F326),
  1037. ---
  1038. >     ('logo',    0x10F326),
  1039. 944,945c419
  1040. < def decompress_title():
  1041. <     rom = load_rom()
  1042. ---
  1043. > def rip_compressed_title(rom, dest='gfx/title'):
  1044. 947,949c421,422
  1045. <         filename = './gfx/title/' + name + '.2bpp'
  1046. <         gfx = Decompressed( rom, 'horiz', None, address )
  1047. <         to_file(filename, gfx.output)
  1048. ---
  1049. >         filename = os.path.join(dest, name + '.2bpp.lz')
  1050. >         rip_compressed_gfx(rom, address, filename)
  1051. 951,976d423
  1052. < def decompress_tilesets():
  1053. <     rom = load_rom()
  1054. <     tileset_headers = 0x4d596
  1055. <     len_tileset = 15
  1056. <     num_tilesets = 0x25
  1057. <     for tileset in range(num_tilesets):
  1058. <         ptr = tileset*len_tileset + tileset_headers
  1059. <         address = (ord(rom[ptr])*0x4000) + (((ord(rom[ptr+1]))+ord(rom[ptr+2])*0x100)&0x3fff)
  1060. <         tiles = Decompressed( rom, 'horiz', None, address )
  1061. <         filename = './gfx/tilesets/'+str(tileset).zfill(2)+'.2bpp'
  1062. <         to_file( filename, tiles.output )
  1063. <         #print '(' + hex(address) + ', '+ hex(address+tiles.address+1) + '),'
  1064. <
  1065. < misc = [
  1066. <     ('player', 0x2BA1A, 'vert'),
  1067. <     ('dude', 0x2BBAA, 'vert'),
  1068. <     ('town_map', 0xF8BA0, 'horiz'),
  1069. <     ('pokegear', 0x1DE2E4, 'horiz'),
  1070. <     ('pokegear_sprites', 0x914DD, 'horiz'),
  1071. < ]
  1072. < def decompress_misc():
  1073. <     rom = load_rom()
  1074. <     for name, address, mode in misc:
  1075. <         filename = './gfx/misc/' + name + '.2bpp'
  1076. <         gfx = Decompressed( rom, mode, None, address )
  1077. <         to_file(filename, gfx.output)
  1078. 978,990c425,428
  1079. < def decompress_all(debug=False):
  1080. <     """
  1081. <     Decompress all known compressed data in baserom.
  1082. <     """
  1083. <
  1084. <     if debug: print 'fronts'
  1085. <     decompress_monsters(front)
  1086. <     if debug: print 'backs'
  1087. <     decompress_monsters(back)
  1088. <     if debug: print 'unown fronts'
  1089. <     decompress_unowns(front)
  1090. <     if debug: print 'unown backs'
  1091. <     decompress_unowns(back)
  1092. ---
  1093. > def rip_compressed_tilesets(rom, dest='gfx/tilesets'):
  1094. >     tileset_headers = 0x4d596
  1095. >     len_tileset     = 15
  1096. >     num_tilesets    = 0x25
  1097. 992,993c430,431
  1098. <     if debug: print 'trainers'
  1099. <     decompress_trainers()
  1100. ---
  1101. >     for tileset in xrange(num_tilesets):
  1102. >         addr = tileset * len_tileset + tileset_headers
  1103. 995,996c433,435
  1104. <     if debug: print 'fx'
  1105. <     decompress_fx()
  1106. ---
  1107. >         bank     = rom[addr]
  1108. >         address  = rom[addr + 2] * 0x100 + rom[addr + 1]
  1109. >         offset   = rom_offset(bank, address)
  1110. 998,999c437,438
  1111. <     if debug: print 'intro'
  1112. <     decompress_intro()
  1113. ---
  1114. >         filename = os.path.join(dest, tileset_name + '.2bpp.lz')
  1115. >         rip_compressed_gfx(rom, address, filename)
  1116. 1001,1002d439
  1117. <     if debug: print 'title'
  1118. <     decompress_title()
  1119. 1004,1005c441,444
  1120. <     if debug: print 'tilesets'
  1121. <     decompress_tilesets()
  1122. ---
  1123. > misc_pics = [
  1124. >     ('player', 0x2BA1A, '6x6'),
  1125. >     ('dude',   0x2BBAA, '6x6'),
  1126. > ]
  1127. 1007,1008c446,450
  1128. <     if debug: print 'misc'
  1129. <     decompress_misc()
  1130. ---
  1131. > misc = [
  1132. >     ('town_map',         0xF8BA0),
  1133. >     ('pokegear',         0x1DE2E4),
  1134. >     ('pokegear_sprites', 0x914DD),
  1135. > ]
  1136. 1010c452,473
  1137. <     return
  1138. ---
  1139. > def rip_compressed_misc(rom, dest='gfx/misc'):
  1140. >     for name, address in misc:
  1141. >         filename = os.path.join(dest, name+ '.2bpp.lz')
  1142. >         rip_compressed_gfx(rom, address, filename)
  1143. >     for name, address, dimensions in misc_pics:
  1144. >         filename = os.path.join(dest, name + '.' + dimensions + '.2bpp.lz')
  1145. >         rip_compressed_gfx(rom, address, filename)
  1146. >
  1147. >
  1148. > def rip_compressed_gfx(rom, address, filename):
  1149. >     gfx = Decompressed(rom, start=address)
  1150. >     to_file(filename, gfx.compressed_data)
  1151. >
  1152. >
  1153. > def rip_bulk_gfx(rom, dest='gfx', crystal=True):
  1154. >     rip_compressed_monster_pics(rom, dest=os.path.join(dest, 'pics'),     crystal=crystal)
  1155. >     rip_compressed_trainer_pics(rom, dest=os.path.join(dest, 'trainers'), crystal=crystal)
  1156. >     rip_compressed_fx          (rom, dest=os.path.join(dest, 'fx'))
  1157. >     rip_compressed_intro       (rom, dest=os.path.join(dest, 'intro'))
  1158. >     rip_compressed_title       (rom, dest=os.path.join(dest, 'title'))
  1159. >     rip_compressed_tilesets    (rom, dest=os.path.join(dest, 'tilesets'))
  1160. >     rip_compressed_misc        (rom, dest=os.path.join(dest, 'misc'))
  1161. 1013c476
  1162. < def decompress_from_address(address, mode='horiz', filename='de.2bpp', size=None):
  1163. ---
  1164. > def decompress_from_address(address, filename='de.2bpp'):
  1165. 1018,1019c481,482
  1166. <     image = Decompressed(rom, mode, size, address)
  1167. <     to_file(filename, image.pic)
  1168. ---
  1169. >     image = Decompressed(rom, start=address)
  1170. >     to_file(filename, image.output)
  1171. 1022,1029c485,487
  1172. < def decompress_file(filein, fileout, mode='horiz', size=None):
  1173. <     f = open(filein, 'rb')
  1174. <     image = f.read()
  1175. <     f.close()
  1176. <
  1177. <     de = Decompressed(image, mode, size)
  1178. <
  1179. <     to_file(fileout, de.pic)
  1180. ---
  1181. > def decompress_file(filein, fileout=None):
  1182. >     image = bytearray(open(filein).read())
  1183. >     de = Decompressed(image)
  1184. 1030a489,491
  1185. >     if fileout == None:
  1186. >         fileout = os.path.splitext(filein)[0]
  1187. >     to_file(fileout, de.output)
  1188. 1032,1035d492
  1189. < def compress_file(filein, fileout, mode='horiz'):
  1190. <     f = open(filein, 'rb')
  1191. <     image = f.read()
  1192. <     f.close()
  1193. 1037c494,496
  1194. <     lz = Compressed(image, mode)
  1195. ---
  1196. > def compress_file(filein, fileout=None):
  1197. >     image = bytearray(open(filein).read())
  1198. >     lz = Compressed(image)
  1199. 1038a498,499
  1200. >     if fileout == None:
  1201. >         fileout = filein + '.lz'
  1202. 1043,1061d503
  1203. <
  1204. < def compress_monster_frontpic(id, fileout):
  1205. <     mode = 'vert'
  1206. <
  1207. <     fpic = './gfx/pics/' + str(id).zfill(3) + '/front.2bpp'
  1208. <     fanim = './gfx/pics/' + str(id).zfill(3) + '/tiles.2bpp'
  1209. <
  1210. <     pic = open(fpic, 'rb').read()
  1211. <     anim = open(fanim, 'rb').read()
  1212. <     image = pic + anim
  1213. <
  1214. <     lz = Compressed(image, mode, sizes[id-1])
  1215. <
  1216. <     out = './gfx/pics/' + str(id).zfill(3) + '/front.lz'
  1217. <
  1218. <     to_file(out, lz.output)
  1219. <
  1220. <
  1221. <
  1222. 1068,1072c510,512
  1223. <     length = num_tiles*bytes_per_tile
  1224. <     end = start + length
  1225. <     image = []
  1226. <     for address in range(start,end):
  1227. <         image.append(ord(rom[address]))
  1228. ---
  1229. >     length = num_tiles * bytes_per_tile
  1230. >     end    = start + length
  1231. >     image  = rom[start:end]
  1232. 1078c518
  1233. <     red = word & 0b11111
  1234. ---
  1235. >     red   = word & 0b11111
  1236. 1082c522
  1237. <     blue = word & 0b11111
  1238. ---
  1239. >     blue  = word & 0b11111
  1240. 1090,1091c530
  1241. <     with open(filename) as f:
  1242. <         pal = bytearray(f.read())
  1243. ---
  1244. >     pal = bytearray(open(filename).read())
  1245. 1128c567
  1246. <         name     = pokemon_constants.pokemon_constants[mon+1].title().replace('_','')
  1247. ---
  1248. >         name     = pokemon_constants[mon+1].title().replace('_','')
  1249. 1137c576
  1250. <             pal_data.append(ord(rom[address]))
  1251. ---
  1252. >             pal_data.append(rom[address])
  1253. 1149c588
  1254. <             pal_data.append(ord(rom[address]))
  1255. ---
  1256. >             pal_data.append(rom[address])
  1257. 1174c613
  1258. <             pal_data.append(ord(rom[address]))
  1259. ---
  1260. >             pal_data.append(rom[address])
  1261. 1191,1192c630,631
  1262. <         bottom = ord(bottom)
  1263. <         top = ord(top)
  1264. ---
  1265. >         bottom = bottom
  1266. >         top = top
  1267. 1287c726,733
  1268. <     int_args = {
  1269. ---
  1270. >     """
  1271. >     Infer graphics conversion arguments given a filename.
  1272. >
  1273. >     Arguments are separated with '.'.
  1274. >     """
  1275. >     parsed_arguments = {}
  1276. >
  1277. >     int_arguments = {
  1278. 1292,1293c738
  1279. <     parsed_arguments = {}
  1280. <     arguments = os.path.splitext(filename)[0].split('.')[1:]
  1281. ---
  1282. >     arguments = os.path.splitext(filename)[0].lstrip('.').split('.')[1:]
  1283. 1294a740,741
  1284. >
  1285. >         # Check for integer arguments first (i.e. "w128").
  1286. 1298c745
  1287. <             arg = int_args.get(arg, False)
  1288. ---
  1289. >             arg = int_arguments.get(arg, False)
  1290. 1301,1308c748
  1291. <         elif len(argument) == 3:
  1292. <             w, x, h = argument[:3]
  1293. <             if w.isdigit() and h.isdigit() and x == 'x':
  1294. <                 parsed_arguments['pic_dimensions'] = (int(w), int(h))
  1295. <         elif argument == 'interleave':
  1296. <             parsed_arguments['interleave'] = True
  1297. <         elif argument == 'norepeat':
  1298. <             parsed_arguments['norepeat'] = True
  1299. ---
  1300. >
  1301. 1311a752,761
  1302. >
  1303. >         # Pic dimensions (i.e. "6x6").
  1304. >         elif 'x' in argument and any(map(str.isdigit, argument)):
  1305. >             w, h = argument.split('x')
  1306. >             if w.isdigit() and h.isdigit():
  1307. >                 parsed_arguments['pic_dimensions'] = (int(w), int(h))
  1308. >
  1309. >         else:
  1310. >             parsed_arguments[argument] = True
  1311. >
  1312. 1315c765
  1313. < def export_2bpp_to_png(filein, fileout=None, pal_file=None, height=0, width=0, tile_padding=0, pic_dimensions=None):
  1314. ---
  1315. > def export_2bpp_to_png(filein, fileout=None, pal_file=None, height=0, width=0, tile_padding=0, pic_dimensions=None, **kwargs):
  1316. 1354a805,808
  1317. >     image = bytearray(image)
  1318. >
  1319. >     pad_color = bytearray([0])
  1320. >
  1321. 1364c818
  1322. <         image = ''.join(interleave_tiles(image, width / 8))
  1323. ---
  1324. >         image = interleave_tiles(image, width / 8)
  1325. 1367c821
  1326. <     image += chr(0) * 0x10 * tile_padding
  1327. ---
  1328. >     image += pad_color * 0x10 * tile_padding
  1329. 1380,1381c834,835
  1330. <             pic += transpose_tiles(image[i:i+pic_length], w)
  1331. <         image = ''.join(pic) + image[len(image) - trailing:]
  1332. ---
  1333. >             pic += transpose_tiles(image[i:i+pic_length], h)
  1334. >         image = bytearray(pic) + image[len(image) - trailing:]
  1335. 1384c838
  1336. <         image += chr(0) * 0x10 * ((w - (len(image) / 0x10) % h) % w)
  1337. ---
  1338. >         image += pad_color * 0x10 * ((w - (len(image) / 0x10) % h) % w)
  1339. 1394c848
  1340. <         image += chr(0) * 0x10 * more_tile_padding
  1341. ---
  1342. >         image += pad_color * 0x10 * more_tile_padding
  1343. 1399c853
  1344. <         image += chr(0) * 0x10 * more_tile_padding
  1345. ---
  1346. >         image += pad_color * 0x10 * more_tile_padding
  1347. 1405c859
  1348. <         image += chr(0) * 0x10 * more_tile_padding
  1349. ---
  1350. >         image += pad_color * 0x10 * more_tile_padding
  1351. 1442c896,1017
  1352. < def export_png_to_2bpp(filein, fileout=None, palout=None, tile_padding=0, pic_dimensions=None):
  1353. ---
  1354. > def get_pic_animation(tmap, w, h):
  1355. >     """
  1356. >     Generate pic animation data from a combined tilemap of each frame.
  1357. >     """
  1358. >     frame_text = ''
  1359. >     bitmask_text = ''
  1360. >
  1361. >     frames = list(split(tmap, w * h))
  1362. >     base = frames.pop(0)
  1363. >     bitmasks = []
  1364. >
  1365. >     for i in xrange(len(frames)):
  1366. >         frame_text += '\tdw .frame{}\n'.format(i + 1)
  1367. >
  1368. >     for i, frame in enumerate(frames):
  1369. >         bitmask = map(operator.ne, frame, base)
  1370. >         if bitmask not in bitmasks:
  1371. >             bitmasks.append(bitmask)
  1372. >         which_bitmask = bitmasks.index(bitmask)
  1373. >
  1374. >         mask = iter(bitmask)
  1375. >         masked_frame = filter(lambda _: mask.next(), frame)
  1376. >
  1377. >         frame_text += '.frame{}\n'.format(i + 1)
  1378. >         frame_text += '\tdb ${:02x} ; bitmask\n'.format(which_bitmask)
  1379. >         if masked_frame:
  1380. >             frame_text += '\tdb {}\n'.format(', '.join(
  1381. >                 map('${:02x}'.format, masked_frame)
  1382. >             ))
  1383. >
  1384. >     for i, bitmask in enumerate(bitmasks):
  1385. >         bitmask_text += '; {}\n'.format(i)
  1386. >         for byte in split(bitmask, 8):
  1387. >             byte = int(''.join(map(int.__repr__, reversed(byte))), 2)
  1388. >             bitmask_text += '\tdb %{:08b}\n'.format(byte)
  1389. >
  1390. >     return frame_text, bitmask_text
  1391. >
  1392. >
  1393. > def dump_pic_animations(addresses={'bitmasks': 'BitmasksPointers', 'frames': 'FramesPointers'}, pokemon=pokemon_constants, rom=None):
  1394. >     """
  1395. >     The code to dump pic animations from rom is mysteriously absent.
  1396. >     Here it is again, but now it dumps images instead of text.
  1397. >     Said text can then be derived from the images.
  1398. >     """
  1399. >
  1400. >     if rom is None: rom = load_rom()
  1401. >
  1402. >     # Labels can be passed in instead of raw addresses.
  1403. >     for which, offset in addresses.items():
  1404. >         if type(offset) is str:
  1405. >             for line in open('pokecrystal.sym').readlines():
  1406. >                 if offset in line.split():
  1407. >                     addresses[which] = rom_offset(*map(lambda x: int(x, 16), line[:7].split(':')))
  1408. >                     break
  1409. >
  1410. >     for i, name in pokemon.items():
  1411. >         if name.lower() == 'unown': continue
  1412. >
  1413. >         i -= 1
  1414. >
  1415. >         directory = os.path.join('gfx', 'pics', name.lower())
  1416. >         size = sizes[i]
  1417. >
  1418. >         if i > 151 - 1:
  1419. >             bank = 0x36
  1420. >         else:
  1421. >             bank = 0x35
  1422. >         address = addresses['frames'] + i * 2
  1423. >         address = rom_offset(bank, rom[address] + rom[address + 1] * 0x100)
  1424. >         addrs = []
  1425. >         while address not in addrs:
  1426. >             addr = rom[address] + rom[address + 1] * 0x100
  1427. >             addrs.append(rom_offset(bank, addr))
  1428. >             address += 2
  1429. >         num_frames = len(addrs)
  1430. >
  1431. >         # To go any further, we need bitmasks.
  1432. >         # Bitmasks need the number of frames, which we now have.
  1433. >
  1434. >         bank = 0x34
  1435. >         address = addresses['bitmasks'] + i * 2
  1436. >         address = rom_offset(bank, rom[address] + rom[address + 1] * 0x100)
  1437. >         length = size ** 2
  1438. >         num_bytes = (length + 7) / 8
  1439. >         bitmasks = []
  1440. >         for _ in xrange(num_frames):
  1441. >             bitmask = []
  1442. >             bytes_ = rom[ address : address + num_bytes ]
  1443. >             for byte in bytes_:
  1444. >                 bits = map(int, bin(byte)[2:].zfill(8))
  1445. >                 bits.reverse()
  1446. >                 bitmask += bits
  1447. >             bitmasks.append(bitmask)
  1448. >             address += num_bytes
  1449. >
  1450. >         # Back to frames:
  1451. >         frames = []
  1452. >         for addr in addrs:
  1453. >             bitmask = bitmasks[rom[addr]]
  1454. >             num_tiles = len(filter(int, bitmask))
  1455. >             frame = (rom[addr], rom[addr + 1 : addr + 1 + num_tiles])
  1456. >             frames.append(frame)
  1457. >
  1458. >         tmap = range(length) * (len(frames) + 1)
  1459. >         for i, frame in enumerate(frames):
  1460. >             bitmask = bitmasks[frame[0]]
  1461. >             tiles = (x for x in frame[1])
  1462. >             for j, bit in enumerate(bitmask):
  1463. >                 if bit:
  1464. >                     tmap[(i + 1) * length + j] = tiles.next()
  1465. >
  1466. >         filename = os.path.join(directory, 'front.{0}x{0}.2bpp.lz'.format(size))
  1467. >         tiles = get_tiles(Decompressed(open(filename).read()).output)
  1468. >         new_tiles = map(tiles.__getitem__, tmap)
  1469. >         new_image = connect(new_tiles)
  1470. >         filename = os.path.splitext(filename)[0]
  1471. >         to_file(filename, new_image)
  1472. >         export_2bpp_to_png(filename)
  1473. >
  1474. >
  1475. > def export_png_to_2bpp(filein, fileout=None, palout=None, **kwargs):
  1476. 1445,1446c1020,1023
  1477. <         'tile_padding': tile_padding,
  1478. <         'pic_dimensions': pic_dimensions,
  1479. ---
  1480. >         'tile_padding': 0,
  1481. >         'pic_dimensions': None,
  1482. >         'animate': False,
  1483. >         'stupid_bitmask_hack': [],
  1484. 1447a1025
  1485. >     arguments.update(kwargs)
  1486. 1450c1028
  1487. <     image, palette, tmap = png_to_2bpp(filein, **arguments)
  1488. ---
  1489. >     image, arguments = png_to_2bpp(filein, **arguments)
  1490. 1456,1458c1034,1038
  1491. <     if tmap != None:
  1492. <         mapout = os.path.splitext(fileout)[0] + '.tilemap'
  1493. <         to_file(mapout, tmap)
  1494. ---
  1495. >     tmap = arguments.get('tmap')
  1496. >
  1497. >     if tmap != None and arguments['animate'] and arguments['pic_dimensions']:
  1498. >         # Generate pic animation data.
  1499. >         frame_text, bitmask_text = get_pic_animation(tmap, *arguments['pic_dimensions'])
  1500. 1459a1040,1060
  1501. >         frames_path = os.path.join(os.path.split(fileout)[0], 'frames.asm')
  1502. >         with open(frames_path, 'w') as out:
  1503. >             out.write(frame_text)
  1504. >
  1505. >         bitmask_path = os.path.join(os.path.split(fileout)[0], 'bitmask.asm')
  1506. >
  1507. >         # The following Pokemon have a bitmask dummied out.
  1508. >         for exception in arguments['stupid_bitmask_hack']:
  1509. >            if exception in bitmask_path:
  1510. >                 bitmasks = bitmask_text.split(';')
  1511. >                 bitmasks[-1] = bitmasks[-1].replace('1', '0')
  1512. >                 bitmask_text = ';'.join(bitmasks)
  1513. >
  1514. >         with open(bitmask_path, 'w') as out:
  1515. >             out.write(bitmask_text)
  1516. >
  1517. >     elif tmap != None and arguments.get('tilemap', False):
  1518. >         tilemap_path = os.path.splitext(fileout)[0] + '.tilemap'
  1519. >         to_file(tilemap_path, tmap)
  1520. >
  1521. >     palette = arguments.get('palette')
  1522. 1492,1496c1093,1100
  1523. <     tile_padding   = kwargs.get('tile_padding', 0)
  1524. <     pic_dimensions = kwargs.get('pic_dimensions', None)
  1525. <     interleave     = kwargs.get('interleave', False)
  1526. <     norepeat       = kwargs.get('norepeat', False)
  1527. <     tilemap        = kwargs.get('tilemap', False)
  1528. ---
  1529. >     arguments = {
  1530. >         'tile_padding': 0,
  1531. >         'pic_dimensions': False,
  1532. >         'interleave': False,
  1533. >         'norepeat': False,
  1534. >         'tilemap': False,
  1535. >     }
  1536. >     arguments.update(kwargs)
  1537. 1498,1501c1102,1107
  1538. <     with open(filein, 'rb') as data:
  1539. <         width, height, rgba, info = png.Reader(data).asRGBA8()
  1540. <         rgba = list(rgba)
  1541. <         greyscale = info['greyscale']
  1542. ---
  1543. >     if type(filein) is str:
  1544. >         filein = open(filein)
  1545. >
  1546. >     assert type(filein) is file
  1547. >
  1548. >     width, height, rgba, info = png.Reader(filein).asRGBA8()
  1549. 1504c1110
  1550. <     len_px  = 4 # rgba
  1551. ---
  1552. >     len_px  = len('rgba')
  1553. 1510,1514c1116
  1554. <             color = { 'r': line[px  ],
  1555. <                       'g': line[px+1],
  1556. <                       'b': line[px+2],
  1557. <                       'a': line[px+3], }
  1558. <             newline += [color]
  1559. ---
  1560. >             color = dict(zip('rgba', line[px:px+len_px]))
  1561. 1516c1118,1125
  1562. <                 palette += [color]
  1563. ---
  1564. >                 if len(palette) < 4:
  1565. >                     palette += [color]
  1566. >                 else:
  1567. >                     # TODO Find the nearest match
  1568. >                     print 'WARNING: %s: Color %s truncated to' % (filein, color),
  1569. >                     color = sorted(palette, key=lambda x: sum(x.values()))[0]
  1570. >                     print color
  1571. >             newline += [color]
  1572. 1519c1128
  1573. <     assert len(palette) <= 4, 'Palette should be 4 colors, is really %d' % len(palette)
  1574. ---
  1575. >     assert len(palette) <= 4, '%s: palette should be 4 colors, is really %d (%s)' % (filein, len(palette), palette)
  1576. 1522,1523c1131
  1577. <     hues = {
  1578. <         'white': { 'r': 0xff, 'g': 0xff, 'b': 0xff, 'a': 0xff },
  1579. ---
  1580. >     greyscale = {
  1581. 1526a1135
  1582. >         'white': { 'r': 0xff, 'g': 0xff, 'b': 0xff, 'a': 0xff },
  1583. 1528c1137,1138
  1584. <     for hue in hues.values():
  1585. ---
  1586. >     preference = 'white', 'black', 'grey', 'gray'
  1587. >     for hue in map(greyscale.get, preference):
  1588. 1534,1540c1144
  1589. <     # Sort palettes by luminance
  1590. <     def luminance(color):
  1591. <         rough = { 'r':  4.7,
  1592. <                   'g':  1.4,
  1593. <                   'b': 13.8, }
  1594. <         return sum(color[key] * rough[key] for key in rough.keys())
  1595. <     palette.sort(key=luminance)
  1596. ---
  1597. >     palette.sort(key=lambda x: sum(x.values()))
  1598. 1549c1153
  1599. <     pad = [0]
  1600. ---
  1601. >     pad = bytearray([0])
  1602. 1584,1585c1188,1197
  1603. <     if pic_dimensions:
  1604. <         w, h = pic_dimensions
  1605. ---
  1606. >     dim = arguments['pic_dimensions']
  1607. >     if dim:
  1608. >         if type(dim) in (tuple, list):
  1609. >             w, h = dim
  1610. >         else:
  1611. >             # infer dimensions based on width.
  1612. >             w = width / tile_width
  1613. >             h = height / tile_height
  1614. >             if h % w == 0:
  1615. >                 h = w
  1616. 1603c1215,1217
  1617. <     image = image[:len(image) - tile_padding * 0x10]
  1618. ---
  1619. >     image = image[:len(image) - arguments['tile_padding'] * 0x10]
  1620. >
  1621. >     tmap = None
  1622. 1605c1219
  1623. <     if interleave:
  1624. ---
  1625. >     if arguments['interleave']:
  1626. 1608,1611c1222,1227
  1627. <     if norepeat:
  1628. <         image, tmap = condense_tiles_to_map(image)
  1629. <     if not tilemap:
  1630. <         tmap = None
  1631. ---
  1632. >     if arguments['pic_dimensions']:
  1633. >         image, tmap = condense_image_to_map(image, w * h)
  1634. >     elif arguments['norepeat']:
  1635. >         image, tmap = condense_image_to_map(image)
  1636. >         if not arguments['tilemap']:
  1637. >             tmap = None
  1638. 1613c1229,1231
  1639. <     return image, palette, tmap
  1640. ---
  1641. >     arguments.update({ 'palette': palette, 'tmap': tmap, })
  1642. >
  1643. >     return image, arguments
  1644. 1703c1321
  1645. <     image, palette, tmap = png_to_2bpp(filename, **kwargs)
  1646. ---
  1647. >     image, kwargs = png_to_2bpp(filename, **kwargs)
  1648. 1707c1325
  1649. < def mass_to_png(debug=False):
  1650. ---
  1651. > def mass_to_png(directory='gfx'):
  1652. 1710,1713c1328
  1653. <         for name in files:
  1654. <             if debug: print os.path.splitext(name), os.path.join(root, name)
  1655. <             if os.path.splitext(name)[1] == '.2bpp':
  1656. <                 export_2bpp_to_png(os.path.join(root, name))
  1657. ---
  1658. >         convert_to_png(map(lambda x: os.path.join(root, x), files))
  1659. 1715c1330
  1660. < def mass_to_colored_png(debug=False):
  1661. ---
  1662. > def mass_to_colored_png(directory='gfx'):
  1663. 1717,1729c1332
  1664. <     for root, dirs, files in os.walk('./gfx/'):
  1665. <         if 'pics' not in root and 'trainers' not in root:
  1666. <             for name in files:
  1667. <                 if debug: print os.path.splitext(name), os.path.join(root, name)
  1668. <                 if os.path.splitext(name)[1] == '.2bpp':
  1669. <                     export_2bpp_to_png(os.path.join(root, name))
  1670. <                     os.utime(os.path.join(root, name), None)
  1671. <                 elif os.path.splitext(name)[1] == '.1bpp':
  1672. <                     export_1bpp_to_png(os.path.join(root, name))
  1673. <                     os.utime(os.path.join(root, name), None)
  1674. <
  1675. <     # only monster and trainer pics for now
  1676. <     for root, dirs, files in os.walk('./gfx/pics/'):
  1677. ---
  1678. >     for root, dirs, files in os.walk(directory):
  1679. 1731,1737d1333
  1680. <             if debug: print os.path.splitext(name), os.path.join(root, name)
  1681. <             if os.path.splitext(name)[1] == '.2bpp':
  1682. <                 if 'normal.pal' in files:
  1683. <                     export_2bpp_to_png(os.path.join(root, name), None, os.path.join(root, 'normal.pal'))
  1684. <                 else:
  1685. <                     export_2bpp_to_png(os.path.join(root, name))
  1686. <                 os.utime(os.path.join(root, name), None)
  1687. 1739,1741d1334
  1688. <     for root, dirs, files in os.walk('./gfx/trainers/'):
  1689. <         for name in files:
  1690. <             if debug: print os.path.splitext(name), os.path.join(root, name)
  1691. 1743,1744c1336,1343
  1692. <                 export_2bpp_to_png(os.path.join(root, name))
  1693. <                 os.utime(os.path.join(root, name), None)
  1694. ---
  1695. >                 pal = None
  1696. >                 if 'pics' in root:
  1697. >                    pal = 'normal.pal'
  1698. >                 elif 'trainers' in root:
  1699. >                    pal = os.path.splitext(name)[0] + '.pal'
  1700. >                 if pal != None:
  1701. >                     pal = os.path.join(root, pal)
  1702. >                 export_2bpp_to_png(os.path.join(root, name), pal_file=pal)
  1703. 1745a1345,1346
  1704. >             elif os.path.splitext(name)[1] == '.1bpp':
  1705. >                 export_1bpp_to_png(os.path.join(root, name))
  1706. 1747,1769d1347
  1707. < def mass_decompress(debug=False):
  1708. <     for root, dirs, files in os.walk('./gfx/'):
  1709. <         for name in files:
  1710. <             if 'lz' in name:
  1711. <                 if '/pics' in root:
  1712. <                     if 'front' in name:
  1713. <                         id = root.split('pics/')[1][:3]
  1714. <                         if id != 'egg':
  1715. <                             with open(os.path.join(root, name), 'rb') as lz: de = Decompressed(lz.read(), 'vert', sizes[int(id)-1])
  1716. <                         else:
  1717. <                             with open(os.path.join(root, name), 'rb') as lz: de = Decompressed(lz.read(), 'vert', 4)
  1718. <                         to_file(os.path.join(root, 'front.2bpp'), de.pic)
  1719. <                         to_file(os.path.join(root, 'tiles.2bpp'), de.animtiles)
  1720. <                     elif 'back' in name:
  1721. <                         with open(os.path.join(root, name), 'rb') as lz: de = Decompressed(lz.read(), 'vert')
  1722. <                         to_file(os.path.join(root, 'back.2bpp'), de.output)
  1723. <                 elif '/trainers' in root or '/fx' in root:
  1724. <                     with open(os.path.join(root, name), 'rb') as lz: de = Decompressed(lz.read(), 'vert')
  1725. <                     to_file(os.path.join(root, os.path.splitext(name)[0]+'.2bpp'), de.output)
  1726. <                 else:
  1727. <                     with open(os.path.join(root, name), 'rb') as lz: de = Decompressed(lz.read())
  1728. <                     to_file(os.path.join(root, os.path.splitext(name)[0]+'.2bpp'), de.output)
  1729. <                 os.utime(os.path.join(root, name), None)
  1730. 1771,1783c1349
  1731. < def append_terminator_to_lzs(directory):
  1732. <     # fix lzs that were extracted with a missing terminator
  1733. <     for root, dirs, files in os.walk(directory):
  1734. <         for file in files:
  1735. <             if '.lz' in file:
  1736. <                 data = open(root+file,'rb').read()
  1737. <                 if data[-1] != chr(0xff):
  1738. <                     data += chr(0xff)
  1739. <                     new = open(root+file,'wb')
  1740. <                     new.write(data)
  1741. <                     new.close()
  1742. <
  1743. < def export_lz_to_png(filename):
  1744. ---
  1745. > def append_terminator_to_lzs(directory='gfx'):
  1746. 1785c1351
  1747. <     Convert a lz file to png. Dump a 2bpp file too.
  1748. ---
  1749. >     Add a terminator to any lz files that were extracted without one.
  1750. 1787,1814c1353,1367
  1751. <     assert filename[-3:] == ".lz"
  1752. <     lz_data = open(filename, "rb").read()
  1753. <
  1754. <     bpp = Decompressed(lz_data).output
  1755. <     bpp_filename = os.path.splitext(filename)[0]
  1756. <     to_file(bpp_filename, bpp)
  1757. <
  1758. <     export_2bpp_to_png(bpp_filename)
  1759. <
  1760. <     # touch the lz file so it doesn't get remade
  1761. <     os.utime(filename, None)
  1762. <
  1763. < def dump_tileset_pngs():
  1764. <     """
  1765. <     Convert .lz format tilesets into .png format tilesets.
  1766. <
  1767. <     Also, leaves a bunch of wonderful .2bpp files everywhere for your amusement.
  1768. <     """
  1769. <     for tileset_id in range(37):
  1770. <         tileset_filename = "./gfx/tilesets/" + str(tileset_id).zfill(2) + ".lz"
  1771. <         export_lz_to_png(tileset_filename)
  1772. <
  1773. < def decompress_frontpic(lz_file):
  1774. <     """
  1775. <     Convert the pic portion of front.lz to front.2bpp
  1776. <     """
  1777. <     lz = open(lz_file, 'rb').read()
  1778. <     to_file(Decompressed(lz).pic, os.path.splitext(filein)[0] + '.2bpp')
  1779. ---
  1780. >     for root, dirs, files in os.walk(directory):
  1781. >         for filename in files:
  1782. >             path = os.path.join(root, filename)
  1783. >             if os.path.splitext(path)[1] == '.lz':
  1784. >                 data = bytearray(open(path,'rb').read())
  1785. >
  1786. >                 # don't mistake padding for a missing terminator
  1787. >                 i = 1
  1788. >                 while data[-i] == 0:
  1789. >                     i += 1
  1790. >
  1791. >                 if data[-i] != 0xff:
  1792. >                     data += [0xff]
  1793. >                     with open(path, 'wb') as out:
  1794. >                         out.write(data)
  1795. 1816,1821d1368
  1796. < def decompress_frontpic_anim(lz_file):
  1797. <     """
  1798. <     Convert the animation tile portion of front.lz to tiles.2bpp
  1799. <     """
  1800. <     lz = open(lz_file, 'rb').read()
  1801. <     to_file(Decompressed(lz).animtiles, 'tiles.2bpp')
  1802. 1823c1370
  1803. < def expand_pic_palettes():
  1804. ---
  1805. > def expand_binary_pic_palettes(directory):
  1806. 1832,1833c1379,1380
  1807. <     for root, dirs, files in os.walk('./gfx/'):
  1808. <         if 'gfx/pics' in root or 'gfx/trainers' in root:
  1809. ---
  1810. >     for root, dirs, files in os.walk(directory):
  1811. >         if os.path.join(directory, 'pics') in root or os.path.join(directory, '/trainers') in root:
  1812. 1930d1476
  1813. <
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement