Skip to content

Commit

Permalink
Add support for props (#23)
Browse files Browse the repository at this point in the history
* Support reading Transforms and Quats
* Support writing Transforms and Quats
  • Loading branch information
scottanderson authored Sep 15, 2023
1 parent 53db3f3 commit 7a48344
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 60 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"eqeqeq": "error",
"indent": "off", // @typescript-eslint/indent
"max-len": ["error", { "code": 120 }],
"no-constant-condition": "off",
"no-undef": "error",
"no-unused-vars": "off", // @typescript-eslint/no-unused-vars
"require-jsdoc": "off",
Expand Down
8 changes: 7 additions & 1 deletion ts/Gvas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {Rotator} from './Rotator';
import {Transform} from './Transform';
import {Vector} from './Vector';

/**
Expand All @@ -19,6 +20,7 @@ export interface Gvas {
strings: Record<string, GvasString>;
vectorArrays: Record<string, Vector[]>;
rotatorArrays: Record<string, Rotator[]>;
transformArrays: Record<string, Transform[]>;
textArrays: Record<string, GvasText[]>;
}

Expand All @@ -28,14 +30,18 @@ export type GvasTypes =
| ['FloatProperty']
| ['IntProperty']
| ['StrProperty']
| ['StructProperty', 'Quat']
| ['StructProperty', 'Vector']
| ['ArrayProperty', 'BoolProperty']
| ['ArrayProperty', 'ByteProperty']
| ['ArrayProperty', 'FloatProperty']
| ['ArrayProperty', 'IntProperty']
| ['ArrayProperty', 'StrProperty']
| ['ArrayProperty', 'StructProperty', 'Rotator']
| ['ArrayProperty', 'StructProperty', 'Transform']
| ['ArrayProperty', 'StructProperty', 'Vector']
| ['ArrayProperty', 'TextProperty'];
| ['ArrayProperty', 'TextProperty']
;

export type GvasString = string | null;

Expand Down
10 changes: 9 additions & 1 deletion ts/Railroad.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {GvasHeader, GvasString, GvasTypes} from './Gvas';
import {GvasHeader, GvasString, GvasText, GvasTypes} from './Gvas';
import {IndustryType} from './IndustryType';
import {Rotator} from './Rotator';
import {Transform} from './Transform';
import {Vector} from './Vector';

/**
Expand All @@ -15,6 +16,7 @@ export interface Railroad {
frames: Frame[];
industries: Industry[];
players: Player[];
props: Prop[];
removedVegetationAssets: Vector[];
sandhouses: Sandhouse[];
saveGame: {
Expand Down Expand Up @@ -101,6 +103,12 @@ export interface Player {
xp: number;
}

export interface Prop {
name: GvasString;
text: GvasText;
transform: Transform;
}

export interface Sandhouse {
location: Vector;
rotation: Rotator;
Expand Down
8 changes: 8 additions & 0 deletions ts/Transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {Quaternion} from './Quaternion';
import {Vector} from './Vector';

export interface Transform {
translation: Vector;
rotation: Quaternion;
scale3d: Vector;
}
74 changes: 59 additions & 15 deletions ts/exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import {CustomData, EngineVersion, Gvas, GvasHeader, GvasString, GvasText, GvasTypes, RichTextFormat} from './Gvas';
import {Railroad} from './Railroad';
import {Rotator} from './Rotator';
import {Transform} from './Transform';
import {Vector} from './Vector';
import {stringToText} from './util';

Expand Down Expand Up @@ -53,6 +54,9 @@ const exportKeys = [
'PlayerNameArray',
'PlayerRotationArray',
'PlayerXPArray',
'PropsNameArray',
'PropsTextArray',
'PropsTransformArray',
'RegulatorValueArray',
'RemovedVegetationAssetsArray',
'ReverserValueArray',
Expand Down Expand Up @@ -147,6 +151,7 @@ export function railroadToGvas(railroad: Railroad): Gvas {
const stringArrays: Record<string, GvasString[]> = {};
const strings: Record<string, GvasString> = {};
const rotatorArrays: Record<string, Rotator[]> = {};
const transformArrays: Record<string, Transform[]> = {};
const vectorArrays: Record<string, Vector[]> = {};
const textArrays: Record<string, GvasText[]> = {};
const orderLowerCase = railroad._order.map((s) => s.toLowerCase());
Expand Down Expand Up @@ -316,6 +321,15 @@ export function railroadToGvas(railroad: Railroad): Gvas {
case 'playerxparray':
intArrays[propertyName] = railroad.players.map((p) => p.xp);
break;
case 'propsnamearray':
stringArrays[propertyName] = railroad.props.map((p) => p.name);
break;
case 'propstextarray':
textArrays[propertyName] = railroad.props.map((p) => p.text);
break;
case 'propstransformarray':
transformArrays[propertyName] = railroad.props.map((p) => p.transform);
break;
case 'regulatorvaluearray':
floatArrays[propertyName] = railroad.frames.map((f) => f.state.regulatorValue);
break;
Expand Down Expand Up @@ -493,6 +507,7 @@ export function railroadToGvas(railroad: Railroad): Gvas {
stringArrays,
strings,
textArrays,
transformArrays,
vectorArrays,
};
}
Expand Down Expand Up @@ -547,6 +562,9 @@ function propertyType(propertyName: string): GvasTypes {
// case 'playernamearray': return ['ArrayProperty', 'StrProperty'];
case 'playerrotationarray': return ['ArrayProperty', 'FloatProperty'];
// case 'playerxparray': return ['ArrayProperty', 'IntProperty'];
// case 'propsnamearray': return ['ArrayProperty', 'StrProperty'];
// case 'propstextarray': return ['ArrayProperty', 'TextProperty'];
// case 'propstransformarray': return ['ArrayProperty', 'StructProperty', 'Transform'];
case 'regulatorvaluearray': return ['ArrayProperty', 'FloatProperty'];
// case 'removedvegetationassetsarray': return ['ArrayProperty', 'StructProperty', 'Vector'];
case 'reverservaluearray': return ['ArrayProperty', 'FloatProperty'];
Expand Down Expand Up @@ -783,30 +801,27 @@ function structPropertyToBlob(structType: string, gvas: Gvas, propertyName: stri
} else if (structType === 'Rotator') {
structs = gvas.rotatorArrays[propertyName] || [];
structSize = largeWorldCoords ? 24 : 12;
} else if (structType === 'Transform') {
structs = gvas.transformArrays[propertyName] || [];
structSize = largeWorldCoords ? 293 : 253;
} else {
throw new Error('Unexpected structType: ' + structType);
}
// Omit empty properties
if (structs.length === 0) return;
// struct_array:
// seq:
// - id: entry_count
// type: u4
// (u32) entry count
data.push(new Uint32Array([structs.length]));
// - id: property_name
// type: string
// (str) property name
data.push(stringToBlob(propertyName));
// - id: struct_property
// contents: [15, 0, 0, 0, "StructProperty", 0]
// (str) StructProperty
data.push(stringToBlob('StructProperty'));
// - id: field_size
// type: u8
// (u64) length
const fieldSize = structs.length * structSize;
data.push(new Uint32Array([fieldSize, 0]));
// - id: field_name
// type: string
// (str) data type
data.push(stringToBlob(structType));
// - id: reserved
// size: 17
// (u128) guid
// (u8) terminator
data.push(new Uint8Array(17));
// - id: data
// size: field_size / entry_count
Expand All @@ -822,13 +837,42 @@ function structPropertyToBlob(structType: string, gvas: Gvas, propertyName: stri
if (structType === 'Vector') {
const v = struct as Vector;
floats = [v.x, v.y, v.z];
data.push(new (largeWorldCoords ? Float64Array : Float32Array)(floats));
} else if (structType === 'Transform') {
const t = struct as Transform;
// Rotation
data.push(stringToBlob('Rotation'));
data.push(stringToBlob('StructProperty'));
data.push(new Uint32Array([largeWorldCoords ? 32 : 16, 0]));
data.push(stringToBlob('Quat'));
data.push(new Uint8Array(17)); // guid, terminator
data.push(new (largeWorldCoords ? Float64Array : Float32Array)([
t.rotation.x, t.rotation.y, t.rotation.z, t.rotation.w]));
// Translation
data.push(stringToBlob('Translation'));
data.push(stringToBlob('StructProperty'));
data.push(new Uint32Array([largeWorldCoords ? 24 : 12, 0]));
data.push(stringToBlob('Vector'));
data.push(new Uint8Array(17)); // guid, terminator
data.push(new (largeWorldCoords ? Float64Array : Float32Array)([
t.translation.x, t.translation.y, t.translation.z]));
// Scale3D
data.push(stringToBlob('Scale3D'));
data.push(stringToBlob('StructProperty'));
data.push(new Uint32Array([largeWorldCoords ? 24 : 12, 0]));
data.push(stringToBlob('Vector'));
data.push(new Uint8Array(17)); // guid, terminator
data.push(new (largeWorldCoords ? Float64Array : Float32Array)([
t.scale3d.x, t.scale3d.y, t.scale3d.z]));
// End of properties list
data.push(stringToBlob('None'));
} else if (structType === 'Rotator') {
const r = struct as Rotator;
floats = [r.pitch, r.yaw, r.roll];
data.push(new (largeWorldCoords ? Float64Array : Float32Array)(floats));
} else {
throw new Error(structType);
}
data.push(new (largeWorldCoords ? Float64Array : Float32Array)(floats));
}
return new Blob(data);
}
Expand Down
21 changes: 21 additions & 0 deletions ts/importer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
Frame,
Industry,
Player,
Prop,
Railroad,
Sandhouse,
Spline,
Expand Down Expand Up @@ -251,6 +252,25 @@ export function gvasToRailroad(gvas: Gvas): Railroad {
players.push(player);
}
}
// Read props
const props: Prop[] = [];
const propNames = optionalMap(gvas.stringArrays, 'PropsNameArray');
const propTransforms = optionalMap(gvas.transformArrays, 'PropsTransformArray');
const propText = optionalMap(gvas.textArrays, 'PropsTextArray');
if (propNames || propTransforms || propText) {
if (!propNames || !propTransforms || !propText) {
throw new Error('Some prop values are missing');
}
enforceEqualLengths([propNames, propTransforms, propText]);
for (let i = 0; i < propNames.length; i++) {
const prop: Prop = {
name: propNames[i],
transform: propTransforms[i],
text: propText[i],
};
props.push(prop);
}
}
// Read sandhouses
const sandhouses: Sandhouse[] = [];
const sandhouseLocation = optionalMap(gvas.vectorArrays, 'SandhouseLocationArray');
Expand Down Expand Up @@ -469,6 +489,7 @@ export function gvasToRailroad(gvas: Gvas): Railroad {
frames,
industries,
players,
props,
removedVegetationAssets,
sandhouses,
saveGame,
Expand Down
Loading

0 comments on commit 7a48344

Please sign in to comment.