Skip to content

Commit

Permalink
GvasTextAsNumber (#240)
Browse files Browse the repository at this point in the history
  • Loading branch information
scottanderson authored Dec 9, 2024
1 parent c745af6 commit 9a02683
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 152 deletions.
50 changes: 43 additions & 7 deletions ts/Gvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ export interface CustomData {
value: number;
}

export type GvasText = GvasTextNone | GvasTextArgumentFormat | GvasTextBase;
export type GvasText =
| GvasTextNone
| GvasTextBase
| GvasTextArgumentFormat
| GvasTextAsNumber
;

// Component type 255
export interface GvasTextNone {
Expand All @@ -98,13 +103,44 @@ export interface GvasTextBase {
// Component type 3
export interface GvasTextArgumentFormat {
flags: number;
guid: GvasString;
pattern: GvasString;
args: FormatArgumentValue[];
sourceFormat: GvasText;
args: FormatArgumentValueMap[];
}

// Component type 4
export interface GvasTextAsNumber {
flags: number;
sourceValue: FormatArgumentValue;
formatOptions?: NumberFormattingOptions | undefined;
targetCulture: GvasString;
}

export interface FormatArgumentValue {
export interface FormatArgumentValueMap {
name: GvasString;
contentType: number;
values: GvasString[];
value: FormatArgumentValue;
}

export type FormatArgumentValue =
| ['Int', number]
| ['Text', GvasText]
;

export interface NumberFormattingOptions {
alwaysIncludeSign: boolean;
useGrouping: boolean;
roundingMode: RoundingMode;
minimumIntegralDigits: number;
maximumIntegralDigits: number;
minimumFractionalDigits: number;
maximumFractionalDigits: number;
}

export enum RoundingMode {
HalfToEven = 0,
HalfFromZero = 1,
HalfToZero = 2,
FromZero = 3,
ToZero = 4,
ToNegativeInfinity = 5,
ToPositiveInfinity = 6,
}
91 changes: 63 additions & 28 deletions ts/exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
GvasString,
GvasText,
GvasTypes,
NumberFormattingOptions,
} from './Gvas';
import {Permission} from './Permission';
import {Quaternion} from './Quaternion';
Expand Down Expand Up @@ -1159,10 +1160,11 @@ function stringToBlob(str: GvasString): BlobPart {

function textToBlob(text: GvasText, largeWorldCoords: boolean): BlobPart {
if ('values' in text) {
if (text.values.length > 1) throw new Error('Only zero or one values are allowed');
// (u32) Flags
// (u8) Component Type (None = 255)
// (u32) Component Count
// (str*) Component Array
// (b32) Has Culture Invariant String
// (str?) Culture Invariant String
return new Blob([
new Uint32Array([text.flags]),
new Uint8Array([255]),
Expand All @@ -1180,46 +1182,79 @@ function textToBlob(text: GvasText, largeWorldCoords: boolean): BlobPart {
new Uint8Array([0]),
new Blob([text.namespace, text.key, text.value].map(stringToBlob)),
]);
} else if ('pattern' in text) {
} else if ('args' in text) {
// (u32) Flags
// (u8) Component Type (Argument Format = 3)
// (u8) Unknown (8)
// (u32) Unknown (0)
// (str) Unknown
// (str) GUID
// (str) Pattern
// (u8) Component Type (ArgumentFormat = 3)
// (txt) Source Format
// (u32) TextFormat Count
// (...) TextFormat Array
return new Blob([
new Uint32Array([text.flags]),
new Uint8Array([3]),
new Uint8Array([8, 0, 0, 0, 0]),
stringToBlob(largeWorldCoords ? '' : null),
stringToBlob(text.guid),
stringToBlob(text.pattern),
textToBlob(text.sourceFormat, largeWorldCoords),
new Uint32Array([text.args.length]),
new Blob(text.args.map(rtfToBlob)),
new Blob(text.args.map((favm) => new Blob([
stringToBlob(favm.name),
formatArgumentValueToBlob(favm.value),
]))),
]);
} else if ('targetCulture' in text) {
// (u32) Flags
// (u8) Component Type (AsNumber = 4)
// (FAV) Source Value
// (b32) Has Number Formatting Options
// (NFO?) Number Formatting Options
// (str) Target Culture
const formatOptions = [text.formatOptions]
.filter(Boolean)
.map(numberFormattingOptionsToBlob);
return new Blob([
new Uint32Array([text.flags]),
new Uint8Array([4]),
formatArgumentValueToBlob(text.sourceValue),
new Uint32Array([formatOptions.length]),
...formatOptions,
stringToBlob(text.targetCulture),
]);
} else {
throw new Error('Unexpected text type');
}
}

function rtfToBlob(rtf: FormatArgumentValue): BlobPart {
// TextFormat:
// (str) Format Key
// (u8) Unknown (4)
// (u32) Content Type
// (u8) Unknown (255)
// (u32) Values Count
// (str*) Values Array
function formatArgumentValueToBlob(value: FormatArgumentValue): BlobPart {
const largeWorldCoords = true; // FIXME
if (value[0] === 'Int') {
// Int (0)
return new Blob([
new Uint8Array([0]),
new Int32Array([value[1], 0]),
]);
} else if (value[0] === 'Text') {
// Text (4)
return new Blob([
new Uint8Array([4]),
textToBlob(value[1], largeWorldCoords),
]);
} else {
throw new Error(`Unexpected FormatArgumentValue ${value}`);
}
}

function numberFormattingOptionsToBlob(value: NumberFormattingOptions): BlobPart {
return new Blob([
stringToBlob(rtf.name),
new Uint8Array([4]),
new Uint32Array([rtf.contentType]),
new Uint8Array([255]),
new Uint32Array([rtf.values.length]),
new Blob(rtf.values.map(stringToBlob)),
new Uint32Array([
value.alwaysIncludeSign ? 1 : 0,
value.useGrouping ? 1 : 0,
]),
new Uint8Array([
value.roundingMode,
]),
new Int32Array([
value.minimumIntegralDigits,
value.maximumIntegralDigits,
value.minimumFractionalDigits,
value.maximumFractionalDigits,
]),
]);
}

Expand Down
Loading

0 comments on commit 9a02683

Please sign in to comment.