makemessage.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from __future__ import with_statement
  4. import argparse
  5. import os
  6. import traceback
  7. import struct
  8. def main():
  9. parser = argparse.ArgumentParser(description='Generate a translatable language file that can be used by SDLPAL.')
  10. parser.add_argument('gamepath', help='Game path where SSS.MKF & M.MSG & WORD.DAT are located.')
  11. parser.add_argument('outputfile', help='Path of the output message file.')
  12. parser.add_argument('encoding', help='Text encoding name (such as "gb2312" or "big5").')
  13. parser.add_argument('-w','--width', dest='wordwidth', help='Word width in bytes')
  14. parser.add_argument("-c", "--comment", action="store_true", help='Automatically generate comments')
  15. options = parser.parse_args()
  16. if options.gamepath[-1] != '/' and options.gamepath[-1] != '\\':
  17. options.gamepath += '/'
  18. if options.wordwidth == None:
  19. options.wordwidth = 10
  20. else:
  21. options.wordwidth = int(options.wordwidth)
  22. script_bytes = []
  23. index_bytes = []
  24. msg_bytes = []
  25. word_bytes = []
  26. is_msg_group = 0 #是否正在处理文字组的标示。
  27. msg_count = 0
  28. last_index = -1
  29. temp = ""
  30. comment = ""
  31. message = ""
  32. for file_ in os.listdir(options.gamepath):
  33. if file_.lower() == 'sss.mkf':
  34. try:
  35. with open(options.gamepath + file_, 'rb') as f:
  36. f.seek(12, os.SEEK_SET)
  37. offset_begin, script_begin, file_end = struct.unpack('<III', f.read(12))
  38. f.seek(offset_begin, os.SEEK_SET)
  39. index_bytes = f.read(script_begin - offset_begin)
  40. script_bytes = f.read(file_end - script_begin)
  41. except:
  42. traceback.print_exc()
  43. return
  44. elif file_.lower() == 'm.msg':
  45. try:
  46. with open(options.gamepath + file_, 'rb') as f:
  47. msg_bytes = f.read()
  48. except:
  49. traceback.print_exc()
  50. return
  51. elif file_.lower() == 'word.dat':
  52. try:
  53. with open(options.gamepath + file_, 'rb') as f:
  54. data_bytes=f.read()
  55. except:
  56. traceback.print_exc()
  57. return
  58. if len(data_bytes) % options.wordwidth != 0:
  59. data_bytes += [0x20 for i in range(0, options.wordwidth - len(data_bytes) % options.wordwidth)]
  60. output = "# All lines, except those inside [BEIGN MESSAGE] and [END MESSAGE], can be commented by adding the sharp '#' mark at the first of the line.\n\n"
  61. output += "# This section contains the information that will be displayed when a user finishes the game.\n"
  62. output += "# Only the keys listed here are valid. Other keys will be ignored.\n"
  63. output += "[BEGIN CREDITS]\n"
  64. output += "# Place the translated text of 'Classical special build' here in no more than 24 half-wide characters.\n"
  65. output += "1= Classical special build\n"
  66. output += "# Place the translated porting information template at the following two lines. Be aware that each replaced line will be truncated into at most 40 half-wide characters.\n"
  67. output += "6= ${platform} port by ${author}, ${year}.\n"
  68. output += "7=\n"
  69. output += "# Place the translated GNU licensing information at the following three lines. Be aware that each line will be truncated into at most 40 half-wide characters.\n"
  70. output += "8= This is a free software and it is\n"
  71. output += "9= published under GNU General Public\n"
  72. output += "10= License v3.\n"
  73. output += "# Place the translated text at the following line. Be aware that each line will be truncated into at most 40 half-wide characters.\n"
  74. output += "11= ...Press Enter to continue\n"
  75. output += "[END CREDITS]\n\n"
  76. output += "# This section contains the words used by the game.\n"
  77. output += "[BEGIN WORDS]\n"
  78. output += "# Each line is a pattern of 'key=value', where key is an integer and value is a string.\n"
  79. for i in range(0, len(data_bytes) / options.wordwidth):
  80. temp = data_bytes[i * options.wordwidth: (i + 1) * options.wordwidth].rstrip('\x20\x00').decode(options.encoding).encode('utf-8')
  81. if options.comment: output += "# Original word: %d=%s\n" % (i, temp)
  82. output += "%d=%s\n" % (i, temp)
  83. output += "# This is the only addtional word for ATB in SDLPAL. It is not used in classical mode.\n"
  84. output += "65530=Battle Speed\n"
  85. output += "[END WORDS]\n\n"
  86. output += "# The following sections contain dialog/description texts used by the game.\n\n"
  87. print "Now Processing. Please wait..."
  88. for i in range(0, len(script_bytes) / 8):
  89. op, w1, w2, w3 = struct.unpack('<HHHH', script_bytes[i * 8 : (i + 1) * 8])
  90. if op == 0xFFFF:
  91. if is_msg_group == 0:
  92. is_msg_group = 1
  93. message = "%s %d\n" % ('[BEGIN MESSAGE]', w1)
  94. if options.comment: comment = "# Original message: %d\n" % w1
  95. last_index = w1
  96. msg_count += 1
  97. msg_begin, msg_end = struct.unpack("<II",index_bytes[w1 * 4 : (w1 + 2) * 4])
  98. try:
  99. temp = "%s\n" % (msg_bytes[msg_begin : msg_end].decode(options.encoding, 'replace').encode('utf-8'))
  100. message += temp
  101. if options.comment: comment += "# " + temp
  102. except:
  103. traceback.print_exc()
  104. elif op == 0x008E:
  105. if is_msg_group == 1:
  106. temp = "%s\n" % ('[CLEAR MESSAGE]')
  107. message += temp
  108. if options.comment: comment += "# " + temp
  109. else:
  110. if is_msg_group == 1:
  111. is_msg_group = 0
  112. temp = "%s %d\n\n" % ('[END MESSAGE]', last_index)
  113. message += temp
  114. output += comment + message
  115. try:
  116. with open(options.outputfile, "wt") as f:
  117. f.write(output)
  118. except:
  119. traceback.print_exc()
  120. print "OK! Extraction finished!"
  121. print "Original Dialog script count: " + str(msg_count)
  122. if __name__ == '__main__':
  123. main()