Skip to content

Instantly share code, notes, and snippets.

@wallabyway
Last active March 17, 2022 04:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wallabyway/b83d84e4d2449faf83ad3cbd04aea6ac to your computer and use it in GitHub Desktop.
Save wallabyway/b83d84e4d2449faf83ad3cbd04aea6ac to your computer and use it in GitHub Desktop.
Run "Tag Equipment To Room". Take a fragmentlist.pack file and list of room dbids, and output a list of object dbids, tagged with room dbids
/*
// INSTALL:
// run the following commands:
// > npm install node-fetch fs-extra aabb-3d
// download a rme-advanced/FragmentList.pack file using https://gist.github.com/wallabyway/51a7bf50f5fe2ec19657278494fcd076
// RUNNING:
// Run this command, and add a comma list of roomIDs, like this:
// > node tagEquipmentToRoom.js [{dbid:9011},{dbid:9261},{dbid:9132}]
// RESULT:
// this will output a list of object DBIDs tagged with roomIds, like this...
// [{roomdbid:9011, equip:[13779,13394,13217]}, {roomdbid:9261, equip:[422,456]}, ...
// Dependencies
// AABB library: https://gist.github.com/yomotsu/d845f21e2e1eb49f647f
*/
const zlib = require('zlib');
const fs = require('fs-extra');
const { read } = require('fs');
var aabb = require('aabb-3d');
class PackFileReader {
constructor(buff) {
if (buff[0] === 31 && buff[1] === 139) {
this.buff = zlib.gunzipSync(buff);
} else {
this.buff = buff;
}
this.stream = new InputStream(this.buff);
const len = this.stream.getInt32();
this.type = this.stream.getString(len);
this.version = this.stream.getInt32();
this.parseContents();
}
zipBuffer() {
return zlib.gzipSync(this.buff);
}
parseContents() {
// Get offsets to TOC and type sets from the end of the file
const originalOffset = this.stream.offset;
this.stream.seek(this.stream.length - 8);
const entriesOffset = this.stream.getUint32();
const typesOffset = this.stream.getUint32();
// Populate entries
this.entries = []; // offsets to individual entries in the pack file
this.stream.seek(entriesOffset);
const entriesCount = this.stream.getVarint();
for (let i = 0; i < entriesCount; i++) {
this.entries.push(this.stream.getUint32());
}
// Populate type sets
this.types = []; // types of all entries in the pack file
this.stream.seek(typesOffset);
const typesCount = this.stream.getVarint();
for (let i = 0; i < typesCount; i++) {
this.types.push({
_class: this.getString(),
_type: this.getString(),
version: this.stream.getVarint()
});
}
// Restore offset
this.stream.seek(originalOffset);
}
numEntries() {
return this.entries.length;
}
seekEntry(i) {
if (i >= this.numEntries()) {
return null;
}
// Read the type index and populate the entry data
this.stream.seek(this.entries[i]);
const type = this.stream.getUint32();
if (type >= this.types.length) {
return null;
}
return this.types[type];
}
getString() {
return this.stream.getString(this.stream.getVarint());
}
}
class InputStream {
constructor(buffer) {
this.buffer = buffer;
this.offset = 0;
this.length = buffer.length;
}
seek(offset) {
this.offset = offset;
}
setUint8(value) {
this.buffer.writeUInt8(value, this.offset);
}
getUint8() {
const val = this.buffer.readUInt8(this.offset);
this.offset += 1;
return val;
}
getUint32() {
const val = this.buffer.readUInt32LE(this.offset);
this.offset += 4;
return val;
}
getInt32() {
const val = this.buffer.readInt32LE(this.offset);
this.offset += 4;
return val;
}
getFloat32() {
const val = this.buffer.readFloatLE(this.offset);
this.offset += 4;
return val;
}
getVarint() {
let byte, val = 0, shift = 0;
do {
byte = this.buffer[this.offset++];
val |= (byte & 0x7f) << shift;
shift += 7;
} while (byte & 0x80);
return val;
}
getString(len) {
const val = this.buffer.toString('utf8', this.offset, this.offset + len);
this.offset += len;
return val;
}
}
let dbidtoFrag = [];
class FragmentReader extends PackFileReader {
constructor(buff, configs) {
super(buff);
this.parseFragments();
}
parseFragments() {
const entries = this.numEntries();
this.fragments = [];
for (let i = 0; i < entries; i++) {
const entryType = this.seekEntry(i);
console.assert(entryType);
console.assert(entryType.version > 4);
const fragment = {
visible: true,
materialID: -1,
geometryID: -1,
dbID: -1,
bbox: [0, 0, 0, 0, 0, 0],
glMatrix: null,
};
// Flags
const flags = this.stream.getUint8();
fragment.visible = (flags & 0x01) !== 0;
// Material
fragment.materialID = this.stream.getVarint();
// Geometry
fragment.geometryID = this.stream.getVarint();
// Transform
fragment.xformType = this.stream.getUint8();
// Bounding box
for (let i = 0; i < 6; i++) {
fragment.bbox[i] = this.stream.getFloat32();
}
fragment.aabb = aabb([fragment.bbox[0], fragment.bbox[1], fragment.bbox[2]], [fragment.bbox[3]-fragment.bbox[0], fragment.bbox[4]-fragment.bbox[1], fragment.bbox[5]-fragment.bbox[2]])
// Database ID
fragment.dbID = this.stream.getVarint();
dbidtoFrag[fragment.dbID] = fragment;
this.fragments.push(fragment);
}
}
}
function tagEquipment(uri, rooms) {
const reader = new FragmentReader(fs.readFileSync(uri));
reader.fragments.map( frag => {
rooms.map( room => {
const roomAABB = dbidtoFrag[room.dbid].aabb;
if (frag.aabb.intersects(roomAABB))
(room.equip.indexOf(frag.dbID) === -1) && room.equip.push(frag.dbID);
})
});
console.log(rooms);
}
const rooms = [{dbid:9531, equip:[]},{dbid:9532, equip:[]},{dbid:12438, equip:[]}];
tagEquipment("rme-advanced/FragmentList.pack", rooms);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment