|
| 1 | +import sys |
| 2 | + |
| 3 | +from sourcehold.tool.convert.aiv.info import * |
| 4 | +try: |
| 5 | + import sourcehold |
| 6 | +except: |
| 7 | + import os, pathlib |
| 8 | + sys.path.insert(0, str(pathlib.Path(".").parent)) |
| 9 | + |
| 10 | +import sourcehold.aivs |
| 11 | +from sourcehold.aivs.AIV import AIV |
| 12 | + |
| 13 | +# from matplotlib import pyplot |
| 14 | + |
| 15 | +import struct |
| 16 | + |
| 17 | +import numpy as np |
| 18 | + |
| 19 | +import json |
| 20 | + |
| 21 | + |
| 22 | + |
| 23 | + |
| 24 | +def to_json(aiv=None, path: str='', include_extra=False, invert_y=True, report=False): |
| 25 | + if aiv == None and not path: |
| 26 | + raise Exception() |
| 27 | + if aiv == None: |
| 28 | + aiv = AIV().from_file(path) |
| 29 | + |
| 30 | + if report: |
| 31 | + print(f"INFO: aiv has version: {aiv.directory.version_number}") |
| 32 | + |
| 33 | + select_all = np.ones((100,100), 'bool') |
| 34 | + |
| 35 | + constructions = get_constructions_matrix() |
| 36 | + constructions[select_all] = struct.unpack(CONSTRUCTIONS_STRUCT_FORMAT, aiv.directory[INDEX_CONSTRUCTIONS].get_data()) |
| 37 | + |
| 38 | + steps = get_steps_matrix() |
| 39 | + steps[select_all] = struct.unpack(STEPS_STRUCT_FORMAT, aiv.directory[INDEX_STEPS].get_data()) |
| 40 | + |
| 41 | +# This value can lie! |
| 42 | + step_count = struct.unpack(STEP_COUNT_STRUCT_FORMAT, aiv.directory[INDEX_STEP_COUNT].get_data())[0] |
| 43 | + step_count_max = steps.max().item() |
| 44 | + if step_count < step_count_max: |
| 45 | + print(f"WARNING: step count ({step_count}) is lower than max step value from steps matrix ({step_count_max}), taking max step value as truth", file=sys.stderr) |
| 46 | + step_count = step_count_max |
| 47 | + |
| 48 | + pauses_raw = aiv.directory[INDEX_PAUSES].get_data() |
| 49 | + if len(pauses_raw) != 50 * 4: |
| 50 | + print(f"WARNING: pauses is not expected size of {50*4} but {len(pauses_raw)}, adjusting", file=sys.stderr) |
| 51 | + pauses = list(struct.unpack(PAUSES_STRUCT_FORMAT_10, pauses_raw)) |
| 52 | + else: |
| 53 | + pauses = list(struct.unpack(PAUSES_STRUCT_FORMAT_50, pauses_raw)) |
| 54 | + |
| 55 | + units = get_units_matrix(list(struct.unpack(f"<{24*10}i", aiv.directory[INDEX_MISC].get_data()))) |
| 56 | + |
| 57 | + pauseDelayAmount = struct.unpack(PAUSE_DELAY_AMOUNT_STRUCT_FORMAT, aiv.directory[INDEX_PAUSE].get_data())[0] |
| 58 | + |
| 59 | + output = {} |
| 60 | + frames = [None] * step_count # + 1 is already stored in the aiv format |
| 61 | + miscItems = [] |
| 62 | + |
| 63 | + output["frames"] = frames |
| 64 | + output["miscItems"] = miscItems |
| 65 | + output["pauseDelayAmount"] = pauseDelayAmount |
| 66 | + |
| 67 | + processed = {} |
| 68 | + |
| 69 | + buildings = 0 |
| 70 | + offset = -1 |
| 71 | + for i in x_range(): |
| 72 | + for j in y_range(invert_y=invert_y): |
| 73 | + offset += 1 |
| 74 | + |
| 75 | + if offset in processed: |
| 76 | + continue |
| 77 | + |
| 78 | + construction = constructions[i, j].item() |
| 79 | + step = steps[i, j].item() |
| 80 | + |
| 81 | + if construction == 0: |
| 82 | + processed[offset] = True |
| 83 | + continue |
| 84 | + |
| 85 | + # Not in source code, but added here |
| 86 | + elif construction == 1: |
| 87 | + processed[offset] = True |
| 88 | + continue |
| 89 | + |
| 90 | + elif construction == 2: |
| 91 | + processed[offset] = True |
| 92 | + continue |
| 93 | + |
| 94 | + elif construction == 38: #keep2 |
| 95 | + processed[offset] = True |
| 96 | + continue |
| 97 | + |
| 98 | + buildingType = convertAIVEnumToMapperEnum(v = construction) |
| 99 | + |
| 100 | + if buildingType in [25, 46, 26, 35, 106, 99]: # [10, 11, 12, 13, 20, 21, 22, 23, 24]: |
| 101 | + continue # not processed yet |
| 102 | + |
| 103 | + processed[offset] = True |
| 104 | + |
| 105 | + if step >= len(frames): |
| 106 | + raise Exception(f"step size too high at ({i}, {j}, {offset}): {step} => {len(frames)}") |
| 107 | + if frames[step] is None: |
| 108 | + buildings += 1 |
| 109 | + shouldPause = step in pauses |
| 110 | + frames[step] = {'itemType': buildingType, 'tilePositionOfsets': [offset], 'shouldPause': shouldPause} |
| 111 | + |
| 112 | + if report: |
| 113 | + print(f"INFO: buildings: {buildings}") |
| 114 | + |
| 115 | + nonbuildings = 0 |
| 116 | + |
| 117 | + offset = -1 |
| 118 | + for i in x_range(): |
| 119 | + for j in y_range(invert_y=invert_y): |
| 120 | + offset += 1 |
| 121 | + if offset not in processed: |
| 122 | + # do all the special stuff |
| 123 | + construction = constructions[i, j].item() |
| 124 | + step = steps[i, j].item() |
| 125 | + buildingType = convertAIVEnumToMapperEnum(v = construction) |
| 126 | + shouldPause = step in pauses |
| 127 | + if frames[step] is None: |
| 128 | + frames[step] = {'itemType': buildingType, 'tilePositionOfsets': [], 'shouldPause': shouldPause} |
| 129 | + frames[step]['tilePositionOfsets'].append(offset) |
| 130 | + nonbuildings += 1 |
| 131 | + |
| 132 | + if report: |
| 133 | + print(f"INFO: walls | pitch | moat: {nonbuildings}") |
| 134 | + |
| 135 | + misc = 0 |
| 136 | + for unitType in range(24): |
| 137 | + for entry in range(10): |
| 138 | + location = units[unitType, entry].item() |
| 139 | + if location == 0: |
| 140 | + continue |
| 141 | + misc += 1 |
| 142 | + miscItems.append({'itemType': unitType, 'positionOfset': location, 'number': entry}) |
| 143 | + |
| 144 | + if report: |
| 145 | + print(f"INFO: units | flags | brazier | misc: {misc}") |
| 146 | + |
| 147 | + emptyFrames = [index for index, frame in enumerate(output['frames']) if not frame] |
| 148 | + knownEmptyFrames = [0,1] |
| 149 | + unexpectedEmptyFrames = list(index for index in emptyFrames if index not in knownEmptyFrames) |
| 150 | + if len(unexpectedEmptyFrames) > 0: |
| 151 | + print(f"WARNING: unexpected count of empty frames, expected one (the first), but there were: {len(unexpectedEmptyFrames)} more: {','.join(str(v) for v in unexpectedEmptyFrames)}", file=sys.stderr) |
| 152 | + output['frames'] = [frame for frame in output['frames'] if frame] |
| 153 | + |
| 154 | + if include_extra: |
| 155 | + output['extra'] = { |
| 156 | + 'version' : aiv.directory.version_number, |
| 157 | + 'directory_size' : aiv.directory.directory_size, |
| 158 | + 'size' : aiv.directory.size, |
| 159 | + 'u1' : aiv.directory.directory_u1, |
| 160 | + 'u7' : aiv.directory.directory_u7, |
| 161 | + 'sections': [i for i in aiv.directory.section_indices if i], |
| 162 | + # 'rng': aiv.directory[2003].get_data() # RNG |
| 163 | + '2001': int.from_bytes(aiv.directory[2001].get_data(), 'little'), |
| 164 | + '2002': int.from_bytes(aiv.directory[2001].get_data(), 'little'), |
| 165 | + 'recent_selected_step_in_editor': int.from_bytes(aiv.directory[2010].get_data(), 'little'), # Last selected step in editor? |
| 166 | + 'step_count': step_count, |
| 167 | + # 'typeData': np.frombuffer(aiv.directory[2004].get_data(), dtype = 'uint8').reshape((100,100)), |
| 168 | + # '2005': # wall edges or something? |
| 169 | + # '2006' : texture noise |
| 170 | + # '2013' : misc and unit locations in tilemap form? |
| 171 | + } |
| 172 | + |
| 173 | + |
| 174 | + return json.dumps(output, indent=2) |
| 175 | + |
0 commit comments