Spaces:
Running
Running
| import * as CONST from '../constants'; | |
| import * as segmentHandlers from './segmentHandlers/'; | |
| import { bytesToAscii } from './segmentHandlers/common'; | |
| export default class sc2 { | |
| import (buffer) { | |
| let size = CONST.MAP_SIZE; | |
| let x = 0; | |
| let y = 0; | |
| let map = { | |
| cells: [], | |
| _segmentData: {} | |
| }; | |
| for (let i = 0; i < size * size; i++) { | |
| map.cells.push({ x: x, y: y, _segmentData: {} }); | |
| if (y === 127) { | |
| y = 0; | |
| x += 1; | |
| } else { | |
| y += 1; | |
| } | |
| } | |
| // iterate through each data segment and parse | |
| let bytes = buffer.subarray(12); | |
| let segments = this.splitSegments(bytes); | |
| Object.keys(segments).sort().forEach((title) => { | |
| let type = title; | |
| // handle special case for scenario | |
| // text sections | |
| if (title.startsWith('TEXT')) | |
| type = 'TEXT'; | |
| let handler = segmentHandlers[type]; | |
| if (handler) | |
| handler(segments[title], map); | |
| else | |
| console.log('Unknown Segment:',type); | |
| }); | |
| // city metadata | |
| map.info = { | |
| name: map._segmentData.CNAM.text || 'Default', | |
| height: size, | |
| width: size, | |
| rotation: map._segmentData.MISC.rotation, | |
| waterLevel: map._segmentData.MISC.globalSeaLevel, | |
| }; | |
| // populate data cells | |
| for (let i = 0; i < map.cells.length; i++) { | |
| let cell = map.cells[i]; | |
| let data = cell._segmentData; | |
| cell.tiles = { _list: [] }; | |
| if (data.XTER.terrain) cell.tiles._list.push({ id: data.XTER.terrain, type: CONST.T_TERRAIN }); | |
| if (data.XTER.water) cell.tiles._list.push({ id: data.XTER.water, type: CONST.T_WATER }); | |
| if (cell.x == 0 || cell.x == 127 || cell.y == 0 || cell.y == 127) | |
| if (data.XTER.terrain) cell.tiles._list.push({ id: data.XTER.terrain, type: CONST.T_EDGE }); | |
| //if (data.XTER.terrain) cell.tiles._list.push({ id: data.XTER.terrain, type: CONST.T_HEIGHTMAP }); | |
| //if (data.XZON.zone) cell.tiles._list.push({ id: data.XZON.zone, type: CONST.T_ZONE }); | |
| if (data.XBLD.id != 0) cell.tiles._list.push(data.XBLD); | |
| //if (data.XUND.subway) cell.tiles._list.push({ id: data.XUND.subway, type: CONST.T_SUBWAY }); | |
| //if (data.XUND.pipes) cell.tiles._list.push({ id: data.XUND.pipes, type: CONST.T_PIPES }); | |
| cell.corners = { | |
| left: data.XZON.left, | |
| top: data.XZON.top, | |
| bottom: data.XZON.bottom, | |
| right: data.XZON.right, | |
| none: data.XZON.none, | |
| }; | |
| cell.zone = { | |
| id: data.XZON.zone, | |
| type: data.XZON.zoneType, | |
| }; | |
| cell.z = data.ALTM.altitude; | |
| cell.rotate = data.XBIT.rotate; | |
| cell.power = { | |
| wired: data.XBIT.wired, | |
| powered: data.XBIT.powered, | |
| }; | |
| cell.pipes = { | |
| piped: data.XBIT.piped, | |
| watered: data.XBIT.watered, | |
| }; | |
| cell.water = { | |
| type: data.XTER.type, | |
| covered: data.XBIT.waterCovered, | |
| salt: data.XBIT.saltWater, | |
| }; | |
| map.cells[i] = cell; | |
| } | |
| return map; | |
| } | |
| splitSegments (bytes) { | |
| let segments = {}; | |
| while (bytes.length > 0) { | |
| let title = bytesToAscii(bytes.subarray(0x00, 0x04)); | |
| let length = new DataView(bytes.subarray(0x04, 0x08).buffer).getUint32(bytes.subarray(0x04, 0x08).byteOffset); | |
| let contents = bytes.subarray(0x08, 0x08 + length); | |
| if (!['ALTM','CNAM','TEXT','PICT','SCEN','TMPL'].includes(title)) | |
| contents = this.decompressSegment(contents); | |
| // can have multilpe TEXT segments | |
| if (title == 'TEXT') { | |
| let type = bytes.subarray(0x08, 0x09); | |
| if (type == 0x80) | |
| title = 'TEXT_1'; | |
| else if (type == 0x81) | |
| title = 'TEXT_2'; | |
| } | |
| segments[title] = contents; | |
| bytes = bytes.subarray(0x08 + length); | |
| } | |
| return segments; | |
| } | |
| decompressSegment (bytes) { | |
| let output = []; | |
| let dataCount = 0; | |
| for (let i = 0; i < bytes.length; i++) { | |
| if (dataCount > 0) { | |
| output.push(bytes[i]); | |
| dataCount -= 1; | |
| continue; | |
| } | |
| // data bytes | |
| if (bytes[i] < 128) { | |
| dataCount = bytes[i]; | |
| // run-length encoded byte | |
| } else { | |
| let repeatCount = bytes[i] - 127; | |
| let repeated = bytes[i + 1]; | |
| for (let i = 0; i < repeatCount; i++) | |
| output.push(repeated); | |
| // skip the next byte | |
| i += 1; | |
| } | |
| } | |
| return Uint8Array.from(output); | |
| } | |
| } |