2024-07-04 05:13:35 -04:00
|
|
|
import { BufferStream, SeekDir } from './buffer.js';
|
2024-07-03 06:00:16 -04:00
|
|
|
|
2024-07-04 05:13:35 -04:00
|
|
|
import { LOCATION } from './structs/core.js';
|
|
|
|
import { AcsCharacterInfo } from './structs/character.js';
|
|
|
|
import { AcsAnimationEntry } from './structs/animation.js';
|
2024-07-04 20:41:11 -04:00
|
|
|
import { AcsImageEntry } from './structs/image.js';
|
2024-07-09 19:47:10 -04:00
|
|
|
import { Agent } from './agent.js';
|
2024-07-03 06:00:16 -04:00
|
|
|
|
2024-07-09 23:50:16 -04:00
|
|
|
// Data
|
2024-07-09 19:47:10 -04:00
|
|
|
export class AcsData {
|
2024-07-04 05:13:35 -04:00
|
|
|
characterInfo = new AcsCharacterInfo();
|
|
|
|
animInfo: AcsAnimationEntry[] = [];
|
2024-07-04 20:41:11 -04:00
|
|
|
images: AcsImageEntry[] = [];
|
2024-07-04 05:13:35 -04:00
|
|
|
}
|
2024-07-03 06:00:16 -04:00
|
|
|
|
2024-07-12 19:13:53 -04:00
|
|
|
// Cache of ACS data per character (for agentCreateCharacterFromUrl)
|
|
|
|
let acsDataCache = new Map<string, AcsData>();
|
|
|
|
|
|
|
|
// Purges the ACS cache.
|
|
|
|
export function agentPurgeACSCache() {
|
|
|
|
acsDataCache.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
export function agentCharacterParseACS(buffer: BufferStream): AcsData {
|
2024-07-04 05:13:35 -04:00
|
|
|
// Make sure the magic is correct for the ACS file.
|
|
|
|
if (buffer.readU32LE() != 0xabcdabc3) {
|
|
|
|
throw new Error('The provided data buffer does not contain valid ACS data.');
|
|
|
|
}
|
|
|
|
|
|
|
|
let acsData = new AcsData();
|
|
|
|
|
|
|
|
// Read the rest of the header.
|
|
|
|
let characterInfoLocation = LOCATION.read(buffer);
|
|
|
|
let animationInfoLocation = LOCATION.read(buffer);
|
|
|
|
let imageInfoLocation = LOCATION.read(buffer);
|
|
|
|
let audioInfoLocation = LOCATION.read(buffer);
|
|
|
|
|
|
|
|
|
|
|
|
buffer.withOffset(characterInfoLocation.offset, () => {
|
|
|
|
acsData.characterInfo = AcsCharacterInfo.read(buffer);
|
|
|
|
});
|
|
|
|
|
|
|
|
buffer.withOffset(animationInfoLocation.offset, () => {
|
|
|
|
acsData.animInfo = buffer.readCountedList(() => {
|
|
|
|
return AcsAnimationEntry.read(buffer);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-07-04 20:41:11 -04:00
|
|
|
buffer.withOffset(imageInfoLocation.offset, () => {
|
|
|
|
acsData.images = buffer.readCountedList(() => {
|
|
|
|
return AcsImageEntry.read(buffer);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-07-09 19:47:10 -04:00
|
|
|
return acsData;
|
2024-07-03 06:00:16 -04:00
|
|
|
}
|
|
|
|
|
2024-07-12 19:13:53 -04:00
|
|
|
export function agentCreateCharacter(data: AcsData): Agent {
|
|
|
|
return new Agent(data);
|
2024-07-03 06:00:16 -04:00
|
|
|
}
|
2024-07-10 00:08:01 -04:00
|
|
|
|
2024-07-12 19:13:53 -04:00
|
|
|
export async function agentCreateCharacterFromUrl(url: string) : Promise<Agent> {
|
|
|
|
// just return the cache object
|
|
|
|
if(acsDataCache.has(url)) {
|
|
|
|
return agentCreateCharacter(acsDataCache.get(url)!);
|
|
|
|
} else {
|
|
|
|
let res = await fetch(url);
|
|
|
|
let data = await res.arrayBuffer();
|
|
|
|
|
|
|
|
let buffer = new Uint8Array(data);
|
|
|
|
let acsData = agentCharacterParseACS(new BufferStream(buffer));
|
|
|
|
|
|
|
|
acsDataCache.set(url, acsData);
|
|
|
|
return agentCreateCharacter(acsData);
|
|
|
|
}
|
|
|
|
}
|