arx-convert
Version:
Converts various Arx Fatalis formats to JSON or YAML and back
110 lines • 4.73 kB
JavaScript
import { BinaryIO } from '../common/BinaryIO.js';
import { MAP_DEPTH_IN_CELLS, MAP_WIDTH_IN_CELLS } from '../common/constants.js';
import { concatArrayBuffers, times } from '../common/helpers.js';
import { Anchor } from './Anchor.js';
import { Cell } from './Cell.js';
import { FtsHeader } from './FtsHeader.js';
import { Portal } from './Portal.js';
import { Room } from './Room.js';
import { RoomDistance } from './RoomDistance.js';
import { SceneHeader } from './SceneHeader.js';
import { TextureContainer } from './TextureContainer.js';
import { UniqueHeader } from './UniqueHeader.js';
import { addLightIndex, getCellCoords } from './helpers.js';
/**
* Arx Libertatis feature: setting the uncompressed bytes' size in the header to 0 gets
* interpreted as the file being uncompressed
*
* source of discussion: https://arx-libertatis.org/irclogs/2022/%23arx.2022-09-06.log
* and https://arx-libertatis.org/irclogs/2022/%23arx.2022-09-07.log
*
* implemented in: https://github.com/arx/ArxLibertatis/commit/2d2226929780b6202f54982bacc79ddf75dbec53
*
* available in Arx Libertatis 1.3 snapshots that came after `2022-09-17` in https://arx-libertatis.org/files/snapshots/
*/
const IS_AN_UNCOMPRESSED_FTS = 0;
export class FTS {
static load(decompressedFile) {
const file = new BinaryIO(decompressedFile);
const { numberOfUniqueHeaders, ...header } = FtsHeader.readFrom(file);
const uniqueHeaders = times(() => {
return UniqueHeader.readFrom(file);
}, numberOfUniqueHeaders);
const { numberOfTextures, numberOfAnchors, numberOfPortals, numberOfRooms, ...sceneHeader } = SceneHeader.readFrom(file);
const textureContainers = times(() => {
return TextureContainer.readFrom(file);
}, numberOfTextures);
const combinedCells = [];
for (let z = 0; z < MAP_DEPTH_IN_CELLS; z++) {
for (let x = 0; x < MAP_WIDTH_IN_CELLS; x++) {
combinedCells.push(Cell.readFrom(file));
}
}
return {
header,
uniqueHeaders,
sceneHeader,
textureContainers,
cells: combinedCells.map(({ polygons, ...cell }) => {
return cell;
}),
polygons: addLightIndex(combinedCells.flatMap(({ polygons }) => {
return polygons;
})),
anchors: times(() => {
return Anchor.readFrom(file);
}, numberOfAnchors),
portals: times(() => {
return Portal.readFrom(file);
}, numberOfPortals),
rooms: times(() => {
return Room.readFrom(file);
}, numberOfRooms),
roomDistances: times(() => {
return RoomDistance.readFrom(file);
}, numberOfRooms ** 2),
};
}
static save(json, isCompressed = true) {
const { levelIdx } = json.header;
const sceneHeader = SceneHeader.accumulateFrom(json);
const recombinedCells = json.cells.map((cell) => {
return {
...cell,
polygons: [],
};
});
json.polygons.forEach((polygon) => {
const [cellX, cellY] = getCellCoords(polygon.vertices);
const cellIndex = cellY * MAP_WIDTH_IN_CELLS + cellX;
recombinedCells[cellIndex].polygons.push(polygon);
});
const textureContainers = concatArrayBuffers(json.textureContainers.map(TextureContainer.accumulateFrom));
const cells = concatArrayBuffers(recombinedCells.map(Cell.accumulateFrom));
const anchors = concatArrayBuffers(json.anchors.map(Anchor.accumulateFrom));
const portals = concatArrayBuffers(json.portals.map((portal) => {
return Portal.accumulateFrom(portal, levelIdx);
}));
const rooms = concatArrayBuffers(json.rooms.map(Room.accumulateFrom));
const roomDistances = concatArrayBuffers(json.roomDistances.map(RoomDistance.accumulateFrom));
const dataWithoutHeader = concatArrayBuffers([
sceneHeader,
textureContainers,
cells,
anchors,
portals,
rooms,
roomDistances,
]);
let header;
if (isCompressed) {
header = FtsHeader.accumulateFrom(json, dataWithoutHeader.byteLength);
}
else {
header = FtsHeader.accumulateFrom(json, IS_AN_UNCOMPRESSED_FTS);
}
const uniqueHeaders = concatArrayBuffers(json.uniqueHeaders.map(UniqueHeader.accumulateFrom));
return concatArrayBuffers([header, uniqueHeaders, dataWithoutHeader]);
}
}
//# sourceMappingURL=FTS.js.map