Advertisement
einstein95

Updated 3dstools/msbt.py

Jan 4th, 2017
157
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 18.33 KB | None | 0 0
  1. #!/usr/bin/python
  2. import argparse
  3. import json
  4. import os.path
  5. import re
  6. import struct
  7. import sys
  8. from binascii import hexlify
  9.  
  10. MSBT_HEADER_LEN = 0x20
  11. LBL1_HEADER_LEN = 0x14
  12. ATR1_HEADER_LEN = 0x14
  13. TXT2_HEADER_LEN = 0x14
  14.  
  15. MSBT_MAGIC = b'MsgStdBn'
  16. LBL1_MAGIC = b'LBL1'
  17. ATR1_MAGIC = b'ATR1'
  18. TXT2_MAGIC = b'TXT2'
  19.  
  20. MSBT_HEADER_STRUCT = '=8s2H2B2HI10s'
  21. LBL1_HEADER_STRUCT = '%s4sI8sI'
  22. ATR1_HEADER_STRUCT = '%s4s4I'
  23. TXT2_HEADER_STRUCT = '%s4s4I'
  24.  
  25. SECTION_END_MAGIC = b'\xAB'
  26. COLOR_ESCAPE = b'\x03\x00\x04\x00'
  27.  
  28. ENCODING_UTF8 = 0x00
  29. ENCODING_UTF16 = 0x01
  30.  
  31. ENCODINGS = {
  32.     ENCODING_UTF8: "UTF-8",
  33.     ENCODING_UTF16: "UTF-16"
  34. }
  35.  
  36.  
  37. class Msbt:
  38.     order = None
  39.     invalid = False
  40.     filename = ''
  41.     file_size = 0
  42.     header_unknowns = []
  43.     sections = {}
  44.     section_order = []
  45.     section_count = 0
  46.     encoding = ENCODING_UTF16
  47.  
  48.     def __init__(self, verbose=False, debug=False, colors=False):
  49.         self.verbose = verbose
  50.         self.debug = debug
  51.         self.colors = colors
  52.  
  53.     def read(self, filename):
  54.         self.filename = filename
  55.         self.file_size = os.stat(filename).st_size
  56.         data = open(self.filename, 'rb').read()
  57.  
  58.         self._parse_header(data[:MSBT_HEADER_LEN])
  59.         if self.invalid:
  60.             return
  61.         position = MSBT_HEADER_LEN
  62.  
  63.         sections_left = self.section_count
  64.         while sections_left > 0 and position < self.file_size:
  65.             magic = data[position:position + 4]
  66.  
  67.             if magic == LBL1_MAGIC:
  68.                 self._parse_lbl1_header(data[position:position + LBL1_HEADER_LEN])
  69.                 position += LBL1_HEADER_LEN
  70.                 if self.invalid:
  71.                     return
  72.                 self._parse_lbl1_data(data[position:position + self.sections['LBL1']['header']['size']])
  73.                 position += self.sections['LBL1']['header']['size']
  74.  
  75.             elif magic == ATR1_MAGIC:
  76.                 self._parse_atr1_header(data[position:position + ATR1_HEADER_LEN])
  77.                 position += ATR1_HEADER_LEN
  78.                 if self.invalid:
  79.                     return
  80.  
  81.                 # TODO: parse ATR1 data?
  82.                 position += self.sections['ATR1']['header']['size']
  83.  
  84.             elif magic == TXT2_MAGIC:
  85.                 self._parse_txt2_header(data[position:position + TXT2_HEADER_LEN])
  86.                 position += TXT2_HEADER_LEN
  87.                 if self.invalid:
  88.                     return
  89.                 self._parse_txt2_data(data[position:position + self.sections['TXT2']['header']['size']])
  90.                 position += self.sections['TXT2']['header']['size']
  91.  
  92.             # TODO:
  93.             # elif magic == NLI1_MAGIC:
  94.  
  95.             else:
  96.                 position += struct.unpack('%sI' % self.order, data[position + 4:position + 8])[0]
  97.                 position += TXT2_HEADER_LEN
  98.                 if self.debug:
  99.                     print('\nUnknown section skipped')
  100.                     print('Unknown section Magic bytes\n: %s' % magic)
  101.  
  102.             sections_left -= 1
  103.  
  104.             self.section_order.append(magic)
  105.  
  106.             while position < self.file_size:
  107.                 if data[position] != '\xAB':
  108.                     break
  109.                 position += 1
  110.  
  111.     def save(self, filename):
  112.         output = open(filename, 'wb')
  113.  
  114.         bom = 0
  115.         if self.order == '>':
  116.             bom = 0xFFFE
  117.         elif self.order == '<':
  118.             bom = 0xFEFF
  119.  
  120.         if self.debug:
  121.             print('\nMSBT Magic: %s' % MSBT_MAGIC)
  122.             print('MSBT Byte-order marker: 0x%x' % bom)
  123.             print('MSBT Unknown1: 0x%x' % self.header_unknowns[0])
  124.             print('MSBT Encoding: %d (%s)' % (self.encoding, ENCODINGS[self.encoding]))
  125.             print('MSBT Unknown2: 0x%x' % self.header_unknowns[1])
  126.             print('MSBT Sections: %d' % self.section_count)
  127.             print('MSBT Unknown3: 0x%x' % self.header_unknowns[2])
  128.             print('MSBT File size: (unknown)')
  129.             print('MSBT Unknown4: 0x%s\n' % self.header_unknowns[3].encode('hex'))
  130.  
  131.         msbt_header = struct.pack(MSBT_HEADER_STRUCT, MSBT_MAGIC, bom, self.header_unknowns[0], self.encoding,
  132.                                   self.header_unknowns[1], self.section_count, self.header_unknowns[2],
  133.                                   0, str(self.header_unknowns[3]))
  134.         output.write(msbt_header)
  135.  
  136.         for section in self.section_order:
  137.             data = {
  138.                 'LBL1': self._serialize_lbl1,
  139.                 'ATR1': self._serialize_atr1,
  140.                 'TXT2': self._serialize_txt2
  141.             }[section]()
  142.             output.write(data)
  143.  
  144.             position = output.tell()
  145.             # write the section end bytes until the next 0x10 alignment
  146.             padding = (16 - (position % 16))
  147.             if padding < 16:
  148.                 output.write(SECTION_END_MAGIC * padding)
  149.  
  150.         # update the size in the header with the final size
  151.         size = output.tell()
  152.         output.seek(0x12)
  153.         output.write(struct.pack('=I', size))
  154.  
  155.         output.close()
  156.  
  157.         print('\nMSBT File size: %d' % size)
  158.  
  159.     def to_json(self, filename):
  160.         output = {
  161.             'strings': {},
  162.             'structure': {}
  163.         }
  164.  
  165.         try:
  166.             label_lists = self.sections['LBL1']['data']
  167.             for label_list in label_lists:
  168.                 for label in label_list[0]:
  169.                     id_ = label[0]
  170.                     name = label[1]
  171.                     value = self.sections['TXT2']['data'][id_]
  172.                     output['strings'][name] = value
  173.         except KeyError:
  174.             value = self.sections['TXT2']['data']
  175.             output['strings'] = value
  176.  
  177.         output['structure']['MSBT'] = {
  178.             'header': {
  179.                 'byte_order': self.order,
  180.                 'encoding': ENCODINGS[self.encoding],
  181.                 'sections': self.section_count,
  182.                 'section_order': self.section_order,
  183.                 'unknowns': self.header_unknowns
  184.             }
  185.         }
  186.  
  187.         for section in self.sections.keys():
  188.             output['structure'][section] = {
  189.                 'header': self.sections[section]['header']
  190.             }
  191.  
  192.         try:
  193.             output['structure']['LBL1']['lists'] = self.sections['LBL1']['data']
  194.         except KeyError:
  195.             output['structure'] = self.sections
  196.  
  197.         json.dump(output, open(filename, 'w'), indent=2, sort_keys=True, ensure_ascii=False)
  198.  
  199.     def from_json(self, filename):
  200.         json_data = json.load(open(filename, 'r'))
  201.         strings = json_data['strings']
  202.         structure = json_data['structure']
  203.  
  204.         lbl1 = structure['LBL1']
  205.         self.sections['LBL1'] = {
  206.             'header': lbl1['header'],
  207.             'data': lbl1['lists']
  208.         }
  209.  
  210.         self.sections['ATR1'] = {
  211.             'header': json_data['structure']['ATR1']['header']
  212.         }
  213.  
  214.         self.sections['TXT2'] = {
  215.             'header': json_data['structure']['TXT2']['header'],
  216.             'data': []
  217.         }
  218.  
  219.         msbt_header = json_data['structure']['MSBT']['header']
  220.         self.order = msbt_header['byte_order']
  221.         self.encoding = msbt_header['encoding']
  222.         self.section_order = msbt_header['section_order']
  223.         self.section_count = msbt_header['sections']
  224.         self.header_unknowns = msbt_header['unknowns']
  225.  
  226.         for encoding in ENCODINGS:
  227.             if self.encoding == ENCODINGS[encoding]:
  228.                 self.encoding = encoding
  229.  
  230.         for i in range(len(json_data['strings'])):
  231.             self.sections['TXT2']['data'].append('')
  232.  
  233.         label_lists = self.sections['LBL1']['data']
  234.         for label_list in label_lists:
  235.             for label in label_list[0]:
  236.                 id_ = label[0]
  237.                 name = label[1]
  238.                 value = strings[name]
  239.  
  240.                 self.sections['TXT2']['data'][id_] = value
  241.  
  242.     def _parse_header(self, data):
  243.         magic, bom, unknown1, encoding, unknown2, sections, unknown3, file_size, unknown4 = struct.unpack(
  244.                 MSBT_HEADER_STRUCT, data)
  245.  
  246.         if magic != MSBT_MAGIC:
  247.             print('Invalid header magic bytes: %s (expected %s)' % (magic, MSBT_MAGIC))
  248.             self.invalid = True
  249.             return
  250.  
  251.         if bom == 0xFFFE:
  252.             self.order = '>'
  253.         elif bom == 0xFEFF:
  254.             self.order = '<'
  255.  
  256.         if self.order is None:
  257.             print('Invalid byte-order marker: 0x%x (expected either 0xFFFE or 0xFEFF)' % bom)
  258.             self.invalid = True
  259.             return
  260.  
  261.         if file_size != self.file_size:
  262.             print('Invalid file size reported: %d (OS reports %d)' % (file_size, self.file_size))
  263.  
  264.         self.section_count = sections
  265.         self.encoding = encoding
  266.         # save for repacking
  267.         self.header_unknowns = [
  268.             unknown1,
  269.             unknown2,
  270.             unknown3,
  271.             unknown4
  272.         ]
  273.  
  274.         if self.debug:
  275.             print('MSBT Magic bytes: %s' % magic)
  276.             print('MSBT Byte-order: %s' % self.order)
  277.             print('MSBT Sections: %d' % sections)
  278.             print('MSBT File size: %s' % file_size)
  279.  
  280.             print('\nUnknown1: 0x%x' % unknown1)
  281.             print('Unknown2: 0x%x' % unknown2)
  282.             print('Unknown3: 0x%x' % unknown3)
  283.             print('Unknown4: 0x%s\n' % hexlify(unknown4).decode('utf-8'))
  284.  
  285.     def _parse_lbl1_header(self, data):
  286.         magic, size, unknown, entries = struct.unpack(LBL1_HEADER_STRUCT % self.order, data)
  287.  
  288.         if magic != LBL1_MAGIC:
  289.             print('Invalid LBL1 magic bytes: %s (expected %s)' % (magic, LBL1_MAGIC))
  290.             self.invalid = True
  291.             return
  292.  
  293.         # -4 from size since we're reading the entries as part of the header
  294.         self.sections['LBL1'] = {
  295.             'header': {
  296.                 'size': size - 4,
  297.                 'entries': entries,
  298.                 'unknown': unknown
  299.             }
  300.         }
  301.  
  302.         if self.debug:
  303.             print('LBL1 Magic bytes: %s' % magic)
  304.             print('LBL1 Size: %d' % size)
  305.             print('LBL1 Entries: %d' % entries)
  306.  
  307.             print('\nLBL1 Unknown: 0x%s\n' % unknown.encode('hex'))
  308.  
  309.     def _parse_lbl1_data(self, data):
  310.         entries = self.sections['LBL1']['header']['entries']
  311.         position = 0
  312.  
  313.         lists = []
  314.  
  315.         if self.debug:
  316.             print('\nLBL1 Entries:')
  317.  
  318.         entry = 1
  319.         while entries > 0:
  320.             count, offset = struct.unpack('%s2I' % self.order, data[position:position + 8])
  321.             if self.debug:
  322.                 print('\n#%d' % entry)
  323.                 entry += 1
  324.                 print('List length: %d' % count)
  325.                 print('First offset: 0x%x' % offset)
  326.  
  327.             position += 8
  328.             entries -= 1
  329.             offset -= 4
  330.  
  331.             list_ = []
  332.  
  333.             for i in range(count):
  334.                 length = ord(data[offset])
  335.                 name_end = offset + length + 1
  336.                 name = data[offset + 1:name_end]
  337.                 id_offset = name_end
  338.                 id_ = struct.unpack('%sI' % self.order, data[id_offset:id_offset + 4])[0]
  339.                 list_.append((id_, name))
  340.                 offset = id_offset + 4
  341.  
  342.                 if self.debug:
  343.                     print('  %d: %s' % (id_, name))
  344.  
  345.             lists.append((list_, offset))
  346.  
  347.         if self.debug:
  348.             print('')
  349.  
  350.         self.sections['LBL1']['data'] = lists
  351.  
  352.     def _parse_atr1_header(self, data):
  353.         magic, size, unknown1, unknown2, entries = struct.unpack(ATR1_HEADER_STRUCT % self.order, data)
  354.  
  355.         if magic != ATR1_MAGIC:
  356.             print('Invalid ATR1 magic bytes: %s (expected %s)' % (magic, ATR1_MAGIC))
  357.             self.invalid = True
  358.             return
  359.  
  360.         # -4 from size since we're reading the entries as part of the header
  361.         self.sections['ATR1'] = {
  362.             'header': {
  363.                 'size': size - 4,
  364.                 'entries': entries,
  365.                 'unknown1': unknown1,
  366.                 'unknown2': unknown2
  367.             }
  368.         }
  369.  
  370.         if self.debug:
  371.             print('ATR1 Magic bytes: %s' % magic)
  372.             print('ATR1 Size: %d' % size)
  373.             print('ATR1 Entries: %d' % entries)
  374.  
  375.             print('\nATR1 Unknown1: 0x%x' % unknown1)
  376.             print('ATR1 Unknown2: 0x%x\n' % unknown2)
  377.  
  378.     def _parse_txt2_header(self, data):
  379.         magic, size, unknown1, unknown2, entries = struct.unpack(TXT2_HEADER_STRUCT % self.order, data)
  380.  
  381.         if magic != TXT2_MAGIC:
  382.             print('Invalid TXT2 magic bytes: %s (expected %s)' % (magic, TXT2_MAGIC))
  383.             self.invalid = True
  384.             return
  385.  
  386.         # -4 from size since we're reading the entries as part of the header
  387.         self.sections['TXT2'] = {
  388.             'header': {
  389.                 'size': size - 4,
  390.                 'entries': entries,
  391.                 'unknown1': unknown1,
  392.                 'unknown2': unknown2
  393.             }
  394.         }
  395.  
  396.         if self.debug:
  397.             print('TXT2 Magic bytes: %s' % magic)
  398.             print('TXT2 Size: %d' % size)
  399.             print('TXT2 Entries: %d' % entries)
  400.  
  401.             print('\nTXT2 Unknown1: 0x%x' % unknown1)
  402.             print('TXT2 Unknown2: 0x%x\n' % unknown2)
  403.  
  404.     def _parse_txt2_data(self, data):
  405.         entries = self.sections['TXT2']['header']['entries']
  406.         data_len = len(data)
  407.  
  408.         offsets = []
  409.         strings = []
  410.  
  411.         for i in range(entries):
  412.             start = i * 4
  413.             end = (i + 1) * 4
  414.             offsets.append(struct.unpack('%sI' % self.order, data[start:end])[0] - 4)
  415.  
  416.         for i in range(entries):
  417.             start = offsets[i]
  418.             if i < entries - 1:
  419.                 end = offsets[i + 1]
  420.             else:
  421.                 end = data_len
  422.  
  423.             string_data = data[start:end]
  424.  
  425.             position = 0
  426.             string = b''
  427.             substrings = []
  428.             while position < len(string_data):
  429.                 if self.colors and len(string) >= 4 and string[-4:] == COLOR_ESCAPE:
  430.                     # save color information
  431.                     color = struct.unpack('%sI' % self.order, string_data[position:position + 4])[0]
  432.                     position += 4
  433.                     string += (b'[#%08x]' % color).encode('utf-16-%s' % ({'<':'le', '>':'be'}[self.order]))
  434.                     continue
  435.  
  436.                 utf16char = string_data[position:position + 2]
  437.                 if utf16char != b'\x00\x00':
  438.                     string += utf16char
  439.                 else:
  440.                     substrings.append(string.decode('utf-16', 'replace'))
  441.                     string = b''
  442.                 position += 2
  443.  
  444.             strings.append(substrings)
  445.  
  446.         self.sections['TXT2']['data'] = strings
  447.  
  448.     def _serialize_lbl1(self):
  449.         entries = self.sections['LBL1']['header']['entries']
  450.  
  451.         header_bytes = struct.pack(LBL1_HEADER_STRUCT % self.order, LBL1_MAGIC, 0,
  452.                                    str(self.sections['LBL1']['header']['unknown']), entries)
  453.         section1_bytes = ''
  454.         section2_bytes = ''
  455.  
  456.         # each section 1 entry is 8 bytes long
  457.         # but we're including the entries data in the header bytes so we need to compensate for that
  458.         section2_offset = (entries * 8) + 4
  459.  
  460.         for label_list in self.sections['LBL1']['data']:
  461.             count = len(label_list[0])
  462.             offset = len(section2_bytes)
  463.             for label in label_list[0]:
  464.                 length = len(label[1])
  465.                 section2_bytes += struct.pack('%sB%dsI' % (self.order, length), length, str(label[1]), label[0])
  466.             section1_bytes += struct.pack('%s2I' % self.order, count, section2_offset + offset)
  467.  
  468.         size = len(section1_bytes) + len(section2_bytes) + 4
  469.         header_bytes = header_bytes[:4] + struct.pack('%sI' % self.order, size) + header_bytes[8:]
  470.  
  471.         if self.debug:
  472.             print('\nLBL1 Magic: %s' % LBL1_MAGIC)
  473.             print('LBL1 Size: %d' % size)
  474.             print('LBL1 Unknown: 0x%s' % self.sections['LBL1']['header']['unknown'].encode('hex'))
  475.             print('LBL1 Entries: %d\n' % entries)
  476.  
  477.         return header_bytes + section1_bytes + section2_bytes
  478.  
  479.     def _serialize_atr1(self):
  480.         # ATR1 is unknown right now so we're going to just pad the section
  481.         # (which is all we've got in Rhythm Tengoku string files
  482.  
  483.         header = self.sections['ATR1']['header']
  484.  
  485.         if self.debug:
  486.             print('\nATR1 Magic: %s' % ATR1_MAGIC)
  487.             print('ATR1 Size: %d' % (header['size'] + 4))
  488.             print('ATR1 Unknown1: 0x%d' % header['unknown1'])
  489.             print('ATR1 Unknown2: 0x%d' % header['unknown2'])
  490.             print('ATR1 Entries: %d\n' % header['entries'])
  491.  
  492.         header_bytes = struct.pack(ATR1_HEADER_STRUCT % self.order, ATR1_MAGIC, header['size'] + 4, header['unknown1'],
  493.                                    header['unknown2'], header['entries'])
  494.  
  495.         atr1_data_bytes = struct.pack('%s%ds' % (self.order, header['size']), '\0' * header['size'])
  496.  
  497.         return header_bytes + atr1_data_bytes
  498.  
  499.     def _serialize_txt2(self):
  500.         # section 1: offsets for each index to the data section
  501.         # section 2: utf-16 strings with a null terminator
  502.  
  503.         strings = self.sections['TXT2']['data']
  504.         entries = len(strings)
  505.         header = self.sections['TXT2']['header']
  506.  
  507.         header_bytes = struct.pack(TXT2_HEADER_STRUCT % self.order, TXT2_MAGIC, 0, header['unknown1'],
  508.                                    header['unknown2'], entries)
  509.         section1_bytes = ''
  510.         section2_bytes = ''
  511.  
  512.         # each entry is a single 32-bit integer representing an offset from the start of section1 to an area in section2
  513.         section1_length = entries * 4
  514.  
  515.         order = ''
  516.         if self.order == '<':
  517.             order = '-le'
  518.         elif self.order == '>':
  519.             order = '-be'
  520.  
  521.         for string_list in strings:
  522.             section1_bytes += struct.pack('%sI' % self.order, section1_length + len(section2_bytes) + 4)
  523.             for string in string_list:
  524.                 utf16string = string.encode('utf-16%s' % order)
  525.  
  526.                 if self.colors:
  527.                     haystack = string
  528.                     matcher = ''
  529.                     utf16string = ''
  530.  
  531.                     while matcher is not None:
  532.                         matcher = re.search('(?P<pre>.*)\\[#(?P<color>[a-fA-F0-9]{8})\\](?P<post>.*)', haystack,
  533.                                             re.DOTALL)
  534.  
  535.                         if matcher is not None:
  536.                             pre = matcher.group('pre')
  537.                             color = matcher.group('color')
  538.                             color_value = int(color, 16)
  539.                             post = matcher.group('post')
  540.                             utf16string += pre.encode('utf-16%s' % order)
  541.                             utf16string += struct.pack('%sI' % self.order, color_value)
  542.                             haystack = post
  543.                         else:
  544.                             utf16string += haystack.encode('utf-16%s' % order)
  545.  
  546.                 section2_bytes += struct.pack('=%ds' % len(utf16string), utf16string)
  547.                 section2_bytes += '\x00\x00'
  548.  
  549.         size = len(section1_bytes) + len(section2_bytes) + 4
  550.         header_bytes = header_bytes[:4] + struct.pack('%sI' % self.order, size) + header_bytes[8:]
  551.  
  552.         if self.debug:
  553.             print('TXT2 Magic: %s' % TXT2_MAGIC)
  554.             print('TXT2 Size: %d' % size)
  555.             print('TXT2 Unknown1: 0x%x' % header['unknown1'])
  556.             print('TXT2 Unknown2: 0x%x' % header['unknown2'])
  557.             print('TXT2 Entries: %d' % entries)
  558.  
  559.         return header_bytes + section1_bytes + section2_bytes
  560.  
  561.  
  562. def prompt_yes_no(prompt):
  563.     answer_ = None
  564.     while answer_ not in ('y', 'n'):
  565.         if answer_ is not None:
  566.             print('Please answer "y" or "n"')
  567.  
  568.         answer_ = raw_input(prompt).lower()
  569.  
  570.         if len(answer_) == 0:
  571.             answer_ = 'n'
  572.  
  573.     return answer_
  574.  
  575.  
  576. if __name__ == '__main__':
  577.     parser = argparse.ArgumentParser(description='MsgStdBn Parser')
  578.     parser.add_argument('-v', '--verbose', help='print more data when working', action='store_true', default=False)
  579.     parser.add_argument('-d', '--debug', help='print debug information', action='store_true', default=False)
  580.     parser.add_argument('-c', '--colors', help='decode colors in strings', action='store_true', default=False)
  581.     group = parser.add_mutually_exclusive_group(required=True)
  582.     group.add_argument('-x', '--extract', help='extract MSBT to plain text', action='store_true', default=False)
  583.     group.add_argument('-p', '--pack', help='pack plain text into an MSBT file', action='store_true', default=False)
  584.     parser.add_argument('-y', '--yes', help='answer "Yes" to any questions (overwriting files)', action='store_true',
  585.                         default=False)
  586.     parser.add_argument('-j', '--json', help='JSON document to read from or write to', required=True)
  587.     parser.add_argument('msbt_file', help='MSBT file to parse')
  588.  
  589.     args = parser.parse_args()
  590.  
  591.     if args.extract and not os.path.exists(args.msbt_file):
  592.         print('MSBT file not found!')
  593.         print(args.msbt_file)
  594.         sys.exit(1)
  595.  
  596.     if args.extract and os.path.exists(args.json) and not args.yes:
  597.         print('JSON output file exists.')
  598.         answer = prompt_yes_no('Overwrite? (y/N) ')
  599.  
  600.         if answer == 'n':
  601.             print('Aborted.')
  602.             sys.exit(1)
  603.  
  604.     json_dirname = os.path.dirname(args.json)
  605.     if len(json_dirname) > 0 and not os.path.exists(json_dirname):
  606.         print('Folder not found: %s' % json_dirname)
  607.         sys.exit(1)
  608.  
  609.     if args.pack and not os.path.exists(args.json):
  610.         print('JSON file not found!')
  611.         print(args.json)
  612.         sys.exit(1)
  613.  
  614.     if args.pack and os.path.exists(args.msbt_file) and not args.yes:
  615.         print('MSBT output file exists.')
  616.         answer = prompt_yes_no('Overwrite? (y/N) ')
  617.  
  618.         if answer == 'n':
  619.             print('Aborted.')
  620.             sys.exit(1)
  621.  
  622.     msbt = Msbt(verbose=args.verbose, debug=args.debug, colors=args.colors)
  623.  
  624.     if args.pack:
  625.         msbt.from_json(args.json)
  626.         msbt.save(args.msbt_file)
  627.     elif args.extract:
  628.         msbt.read(args.msbt_file)
  629.         if msbt.invalid:
  630.             print('Invalid MSBT file!')
  631.             sys.exit(1)
  632.         msbt.to_json(args.json)
  633.         print('All good!')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement