324 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			324 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | import re | |||
|  | 
 | |||
|  | class Dialog: | |||
|  | 
 | |||
|  |     command = re.compile(r"^\{[^}]*\}") | |||
|  |     invalid = re.compile(r"(?<!^)\{[^}]*\}(?!$)", re.MULTILINE) | |||
|  |     digit = re.compile(r"\d") | |||
|  |     uppercaseLetter = re.compile(r"[A-Z]") | |||
|  |     lowercaseLetter = re.compile(r"[a-z]") | |||
|  | 
 | |||
|  |     @staticmethod | |||
|  |     def Simple(text: str): | |||
|  |         maxBytes = 256 | |||
|  |         wrap = 19 | |||
|  | 
 | |||
|  |         bytes = [] | |||
|  |         lines = text.split('\n') | |||
|  |         lineIndex = 0 | |||
|  |         for line in lines: | |||
|  |             bytes.append(0x74 if 0 else 0x75 if 1 else 0x76) | |||
|  |             letters = line[:wrap] if len(line) > wrap else line | |||
|  |             for letter in letters: | |||
|  |                 write = Dialog.LetterToBytes(letter) | |||
|  |                 if (write[0] == 0xFD): | |||
|  |                     bytes += write | |||
|  |                 else: | |||
|  |                     for b in write: | |||
|  |                         bytes += [ 0x00, b ] | |||
|  | 
 | |||
|  |             lineIndex += 1 | |||
|  | 
 | |||
|  |             if (lineIndex % 3 == 0 and lineIndex < len(lines)): | |||
|  |                 bytes.append(0x7E) | |||
|  |             if (lineIndex >= 3 and lineIndex < len(lines)): | |||
|  |                 bytes.append(0x73) | |||
|  | 
 | |||
|  |         bytes.append(0x7F) | |||
|  |         if (len(bytes) > maxBytes): | |||
|  |             return bytes[:maxBytes - 1].append(0x7F) | |||
|  | 
 | |||
|  |         return bytes | |||
|  | 
 | |||
|  |     @staticmethod | |||
|  |     def Compiled(text: str, pause = True): | |||
|  |         maxBytes = 2046 | |||
|  |         wrap = 19 | |||
|  | 
 | |||
|  |         if (Dialog.invalid.match(text)): | |||
|  |             raise Exception("Dialog commands must be placed on separate lines", text) | |||
|  | 
 | |||
|  |         padOut = False | |||
|  | 
 | |||
|  |         bytes = [ 0xFB ] | |||
|  |         lines = Dialog.Wordwrap(text, wrap) | |||
|  |         lineCount = len([l for l in lines if not Dialog.command.match(l)]) | |||
|  |         lineIndex = 0 | |||
|  |         for line in lines: | |||
|  |             match = Dialog.command.match(line) | |||
|  |             if (match is not None): | |||
|  |                 if (match.string == "{NOTEXT}"): | |||
|  |                     return [ 0xFB, 0xFE, 0x6E, 0x00, 0xFE, 0x6B, 0x04 ] | |||
|  |                 if (match.string == "{INTRO}"): | |||
|  |                     padOut = True | |||
|  | 
 | |||
|  |                 bytesMap = { | |||
|  |                             "{SPEED0}" : [ 0xFC, 0x00 ], | |||
|  |                             "{SPEED2}" : [ 0xFC, 0x02 ], | |||
|  |                             "{SPEED6}" : [ 0xFC, 0x06 ], | |||
|  |                             "{PAUSE1}" : [ 0xFE, 0x78, 0x01 ], | |||
|  |                             "{PAUSE3}" : [ 0xFE, 0x78, 0x03 ], | |||
|  |                             "{PAUSE5}" : [ 0xFE, 0x78, 0x05 ], | |||
|  |                             "{PAUSE7}" : [ 0xFE, 0x78, 0x07 ], | |||
|  |                             "{PAUSE9}" : [ 0xFE, 0x78, 0x09 ], | |||
|  |                             "{INPUT}" : [ 0xFA ], | |||
|  |                             "{CHOICE}" : [ 0xFE, 0x68 ], | |||
|  |                             "{ITEMSELECT}" : [ 0xFE, 0x69 ], | |||
|  |                             "{CHOICE2}" : [ 0xFE, 0x71 ], | |||
|  |                             "{CHOICE3}" : [ 0xFE, 0x72 ], | |||
|  |                             "{C:GREEN}" : [ 0xFE, 0x77, 0x07 ], | |||
|  |                             "{C:YELLOW}" : [ 0xFE, 0x77, 0x02 ], | |||
|  |                             "{HARP}" : [ 0xFE, 0x79, 0x2D ], | |||
|  |                             "{MENU}" : [ 0xFE, 0x6D, 0x00 ], | |||
|  |                             "{BOTTOM}" : [ 0xFE, 0x6D, 0x01 ], | |||
|  |                             "{NOBORDER}" : [ 0xFE, 0x6B, 0x02 ], | |||
|  |                             "{CHANGEPIC}" : [ 0xFE, 0x67, 0xFE, 0x67 ], | |||
|  |                             "{CHANGEMUSIC}" : [ 0xFE, 0x67 ], | |||
|  |                             "{INTRO}" : [ 0xFE, 0x6E, 0x00, 0xFE, 0x77, 0x07, 0xFC, 0x03, 0xFE, 0x6B, 0x02, 0xFE, 0x67 ], | |||
|  |                             "{IBOX}" : [ 0xFE, 0x6B, 0x02, 0xFE, 0x77, 0x07, 0xFC, 0x03, 0xF7 ], | |||
|  |                         } | |||
|  |                 result = bytesMap.get(match.string, None) | |||
|  |                 if (result is None): | |||
|  |                     raise Exception(f"Dialog text contained unknown command {match.string}", text) | |||
|  |                 else: | |||
|  |                     bytes += result | |||
|  | 
 | |||
|  |                 if (len(bytes) > maxBytes): | |||
|  |                     raise Exception("Command overflowed maximum byte length", text) | |||
|  | 
 | |||
|  |                 continue | |||
|  | 
 | |||
|  |             if (lineIndex == 1): | |||
|  |                 bytes.append(0xF8); #// row 2 | |||
|  |             elif (lineIndex >= 3 and lineIndex < lineCount): | |||
|  |                 bytes.append(0xF6); #// scroll | |||
|  |             elif (lineIndex >= 2): | |||
|  |                 bytes.append(0xF9); #// row 3 | |||
|  | 
 | |||
|  |             #// The first box needs to fill the full width with spaces as the palette is loaded weird. | |||
|  |             letters = line + (" " * wrap) if padOut and lineIndex < 3 else line | |||
|  |             for letter in letters: | |||
|  |                 bytes += Dialog.LetterToBytes(letter) | |||
|  | 
 | |||
|  |             lineIndex += 1 | |||
|  | 
 | |||
|  |             if (pause and lineIndex % 3 == 0 and lineIndex < lineCount): | |||
|  |                 bytes.append(0xFA) #// wait for input | |||
|  | 
 | |||
|  |         return bytes[:maxBytes] | |||
|  | 
 | |||
|  |     @staticmethod | |||
|  |     def Wordwrap(text: str, width: int): | |||
|  |         result = [] | |||
|  |         for line in text.split('\n'): | |||
|  |             line = line.rstrip() | |||
|  |             if (len(line) <= width): | |||
|  |                 result.append(line) | |||
|  |             else: | |||
|  |                 words = line.split(' ') | |||
|  |                 lines = [ "" ] | |||
|  |                 for word in words: | |||
|  |                     line = lines.pop() | |||
|  |                     if (len(line) + len(word) <= width): | |||
|  |                         line = f"{line}{word} " | |||
|  |                     else: | |||
|  |                         if (len(line) > 0): | |||
|  |                             lines.append(line) | |||
|  |                         line = word | |||
|  |                         while (len(line) > width): | |||
|  |                             lines.append(line[:width]) | |||
|  |                             line = line[width:] | |||
|  |                         line = f"{line} " | |||
|  |                     lines.append(line) | |||
|  |                 #lines.reverse() | |||
|  |                 result += [l.strip() for l in lines] | |||
|  |         return result | |||
|  | 
 | |||
|  |     @staticmethod | |||
|  |     def LetterToBytes(c: str): | |||
|  |         if Dialog.digit.match(c): return [(ord(c) - ord('0') + 0xA0) ] | |||
|  |         elif Dialog.uppercaseLetter.match(c): return [ (ord(c) - ord('A') + 0xAA) ] | |||
|  |         elif Dialog.lowercaseLetter.match(c): return [ (ord(c) - ord('a') + 0x30) ] | |||
|  |         else: | |||
|  |             value = Dialog.letters.get(c, None) | |||
|  |             return value if value else [ 0xFF ] | |||
|  | 
 | |||
|  |         #region letter bytes lookup | |||
|  | 
 | |||
|  |     letters = { | |||
|  |         ' ' : [ 0x4F ], | |||
|  |         '?' : [ 0xC6 ], | |||
|  |         '!' : [ 0xC7 ], | |||
|  |         ',' : [ 0xC8 ], | |||
|  |         '-' : [ 0xC9 ], | |||
|  |         '…' : [ 0xCC ], | |||
|  |         '.' : [ 0xCD ], | |||
|  |         '~' : [ 0xCE ], | |||
|  |         '~' : [ 0xCE ], | |||
|  |         '\'' : [ 0xD8 ], | |||
|  |         '’' : [ 0xD8 ], | |||
|  |         '"' : [ 0xD8 ], | |||
|  |         ':' : [ 0x4A ], | |||
|  |         '@' : [ 0x4B ], | |||
|  |         '#' : [ 0x4C ], | |||
|  |         '¤' : [ 0x4D, 0x4E ], #// Morphing ball | |||
|  |         '_' : [ 0xFF ], #// Full width space | |||
|  |         '£' : [ 0xFE, 0x6A ], #// link's name compressed | |||
|  |         '>' : [ 0xD2, 0xD3 ], #// link face | |||
|  |         '%' : [ 0xDD ], #// Hylian Bird | |||
|  |         '^' : [ 0xDE ], #// Hylian Ankh | |||
|  |         '=' : [ 0xDF ], #// Hylian Wavy lines | |||
|  |         '↑' : [ 0xE0 ], | |||
|  |         '↓' : [ 0xE1 ], | |||
|  |         '→' : [ 0xE2 ], | |||
|  |         '←' : [ 0xE3 ], | |||
|  |         '≥' : [ 0xE4 ], #// cursor | |||
|  |         '¼' : [ 0xE5, 0xE7 ], #// 1/4 heart | |||
|  |         '½' : [ 0xE6, 0xE7 ], #// 1/2 heart | |||
|  |         '¾' : [ 0xE8, 0xE9 ], #// 3/4 heart | |||
|  |         '♥' : [ 0xEA, 0xEB ], #// full heart | |||
|  |         'ᚋ' : [ 0xFE, 0x6C, 0x00 ], #// var 0 | |||
|  |         'ᚌ' : [ 0xFE, 0x6C, 0x01 ], #// var 1 | |||
|  |         'ᚍ' : [ 0xFE, 0x6C, 0x02 ], #// var 2 | |||
|  |         'ᚎ' : [ 0xFE, 0x6C, 0x03 ], #// var 3 | |||
|  | 
 | |||
|  |         'あ' : [ 0x00 ], | |||
|  |         'い' : [ 0x01 ], | |||
|  |         'う' : [ 0x02 ], | |||
|  |         'え' : [ 0x03 ], | |||
|  |         'お' : [ 0x04 ], | |||
|  |         'や' : [ 0x05 ], | |||
|  |         'ゆ' : [ 0x06 ], | |||
|  |         'よ' : [ 0x07 ], | |||
|  |         'か' : [ 0x08 ], | |||
|  |         'き' : [ 0x09 ], | |||
|  |         'く' : [ 0x0A ], | |||
|  |         'け' : [ 0x0B ], | |||
|  |         'こ' : [ 0x0C ], | |||
|  |         'わ' : [ 0x0D ], | |||
|  |         'を' : [ 0x0E ], | |||
|  |         'ん' : [ 0x0F ], | |||
|  |         'さ' : [ 0x10 ], | |||
|  |         'し' : [ 0x11 ], | |||
|  |         'す' : [ 0x12 ], | |||
|  |         'せ' : [ 0x13 ], | |||
|  |         'そ' : [ 0x14 ], | |||
|  |         'が' : [ 0x15 ], | |||
|  |         'ぎ' : [ 0x16 ], | |||
|  |         'ぐ' : [ 0x17 ], | |||
|  |         'た' : [ 0x18 ], | |||
|  |         'ち' : [ 0x19 ], | |||
|  |         'つ' : [ 0x1A ], | |||
|  |         'て' : [ 0x1B ], | |||
|  |         'と' : [ 0x1C ], | |||
|  |         'げ' : [ 0x1D ], | |||
|  |         'ご' : [ 0x1E ], | |||
|  |         'ざ' : [ 0x1F ], | |||
|  |         'な' : [ 0x20 ], | |||
|  |         'に' : [ 0x21 ], | |||
|  |         'ぬ' : [ 0x22 ], | |||
|  |         'ね' : [ 0x23 ], | |||
|  |         'の' : [ 0x24 ], | |||
|  |         'じ' : [ 0x25 ], | |||
|  |         'ず' : [ 0x26 ], | |||
|  |         'ぜ' : [ 0x27 ], | |||
|  |         'は' : [ 0x28 ], | |||
|  |         'ひ' : [ 0x29 ], | |||
|  |         'ふ' : [ 0x2A ], | |||
|  |         'へ' : [ 0x2B ], | |||
|  |         'ほ' : [ 0x2C ], | |||
|  |         'ぞ' : [ 0x2D ], | |||
|  |         'だ' : [ 0x2E ], | |||
|  |         'ぢ' : [ 0x2F ], | |||
|  | 
 | |||
|  |         'ア' : [ 0x50 ], | |||
|  |         'イ' : [ 0x51 ], | |||
|  |         'ウ' : [ 0x52 ], | |||
|  |         'エ' : [ 0x53 ], | |||
|  |         'オ' : [ 0x54 ], | |||
|  |         'ヤ' : [ 0x55 ], | |||
|  |         'ユ' : [ 0x56 ], | |||
|  |         'ヨ' : [ 0x57 ], | |||
|  |         'カ' : [ 0x58 ], | |||
|  |         'キ' : [ 0x59 ], | |||
|  |         'ク' : [ 0x5A ], | |||
|  |         'ケ' : [ 0x5B ], | |||
|  |         'コ' : [ 0x5C ], | |||
|  |         'ワ' : [ 0x5D ], | |||
|  |         'ヲ' : [ 0x5E ], | |||
|  |         'ン' : [ 0x5F ], | |||
|  |         'サ' : [ 0x60 ], | |||
|  |         'シ' : [ 0x61 ], | |||
|  |         'ス' : [ 0x62 ], | |||
|  |         'セ' : [ 0x63 ], | |||
|  |         'ソ' : [ 0x64 ], | |||
|  |         'ガ' : [ 0x65 ], | |||
|  |         'ギ' : [ 0x66 ], | |||
|  |         'グ' : [ 0x67 ], | |||
|  |         'タ' : [ 0x68 ], | |||
|  |         'チ' : [ 0x69 ], | |||
|  |         'ツ' : [ 0x6A ], | |||
|  |         'テ' : [ 0x6B ], | |||
|  |         'ト' : [ 0x6C ], | |||
|  |         'ゲ' : [ 0x6D ], | |||
|  |         'ゴ' : [ 0x6E ], | |||
|  |         'ザ' : [ 0x6F ], | |||
|  |         'ナ' : [ 0x70 ], | |||
|  |         'ニ' : [ 0x71 ], | |||
|  |         'ヌ' : [ 0x72 ], | |||
|  |         'ネ' : [ 0x73 ], | |||
|  |         'ノ' : [ 0x74 ], | |||
|  |         'ジ' : [ 0x75 ], | |||
|  |         'ズ' : [ 0x76 ], | |||
|  |         'ゼ' : [ 0x77 ], | |||
|  |         'ハ' : [ 0x78 ], | |||
|  |         'ヒ' : [ 0x79 ], | |||
|  |         'フ' : [ 0x7A ], | |||
|  |         'ヘ' : [ 0x7B ], | |||
|  |         'ホ' : [ 0x7C ], | |||
|  |         'ゾ' : [ 0x7D ], | |||
|  |         'ダ' : [ 0x7E ], | |||
|  |         'マ' : [ 0x80 ], | |||
|  |         'ミ' : [ 0x81 ], | |||
|  |         'ム' : [ 0x82 ], | |||
|  |         'メ' : [ 0x83 ], | |||
|  |         'モ' : [ 0x84 ], | |||
|  |         'ヅ' : [ 0x85 ], | |||
|  |         'デ' : [ 0x86 ], | |||
|  |         'ド' : [ 0x87 ], | |||
|  |         'ラ' : [ 0x88 ], | |||
|  |         'リ' : [ 0x89 ], | |||
|  |         'ル' : [ 0x8A ], | |||
|  |         'レ' : [ 0x8B ], | |||
|  |         'ロ' : [ 0x8C ], | |||
|  |         'バ' : [ 0x8D ], | |||
|  |         'ビ' : [ 0x8E ], | |||
|  |         'ブ' : [ 0x8F ], | |||
|  |         'ベ' : [ 0x90 ], | |||
|  |         'ボ' : [ 0x91 ], | |||
|  |         'パ' : [ 0x92 ], | |||
|  |         'ピ' : [ 0x93 ], | |||
|  |         'プ' : [ 0x94 ], | |||
|  |         'ペ' : [ 0x95 ], | |||
|  |         'ポ' : [ 0x96 ], | |||
|  |         'ャ' : [ 0x97 ], | |||
|  |         'ュ' : [ 0x98 ], | |||
|  |         'ョ' : [ 0x99 ], | |||
|  |         'ッ' : [ 0x9A ], | |||
|  |         'ァ' : [ 0x9B ], | |||
|  |         'ィ' : [ 0x9C ], | |||
|  |         'ゥ' : [ 0x9D ], | |||
|  |         'ェ' : [ 0x9E ], | |||
|  |         'ォ' : [ 0x9F ], | |||
|  |     } |