From 9cbc80e1ef2f90991b210fc8aa6a48e8e32c83a4 Mon Sep 17 00:00:00 2001 From: Casper Warden <216465704+casperwardensl@users.noreply.github.com> Date: Mon, 20 Nov 2023 20:44:21 +0000 Subject: [PATCH] Improve LLLindenText parsing --- lib/classes/InventoryItem.ts | 8 ++--- lib/classes/LLLindenText.spec.ts | 14 ++++++++- lib/classes/LLLindenText.ts | 51 ++++++++++++++------------------ lib/classes/Utils.ts | 11 +++++++ lib/classes/public/GameObject.ts | 7 +++-- package-lock.json | 4 +-- package.json | 2 +- 7 files changed, 57 insertions(+), 40 deletions(-) diff --git a/lib/classes/InventoryItem.ts b/lib/classes/InventoryItem.ts index a777842..1441538 100644 --- a/lib/classes/InventoryItem.ts +++ b/lib/classes/InventoryItem.ts @@ -71,7 +71,7 @@ export class InventoryItem groupOwned: false }; - static fromAsset(lineObj: { lines: string[], lineNum: number }, container?: GameObject | InventoryFolder, agent?: Agent): InventoryItem + static fromEmbeddedAsset(lineObj: { lines: string[], lineNum: number, pos: number }, container?: GameObject | InventoryFolder, agent?: Agent): InventoryItem { const item: InventoryItem = new InventoryItem(container, agent); let contMetadata = false; @@ -79,7 +79,7 @@ export class InventoryItem let contDesc = false; while (lineObj.lineNum < lineObj.lines.length) { - let line = lineObj.lines[lineObj.lineNum++]; + let line = Utils.getNotecardLine(lineObj); if (contMetadata) { @@ -162,7 +162,7 @@ export class InventoryItem { while (lineObj.lineNum < lineObj.lines.length) { - result = Utils.parseLine(lineObj.lines[lineObj.lineNum++]); + result = Utils.parseLine(Utils.getNotecardLine(lineObj)); if (result.key !== null) { if (result.key === '{') @@ -229,7 +229,7 @@ export class InventoryItem { while (lineObj.lineNum < lineObj.lines.length) { - result = Utils.parseLine(lineObj.lines[lineObj.lineNum++]); + result = Utils.parseLine(Utils.getNotecardLine(lineObj)); if (result.key !== null) { if (result.key === '{') diff --git a/lib/classes/LLLindenText.spec.ts b/lib/classes/LLLindenText.spec.ts index a29f181..a46c331 100644 --- a/lib/classes/LLLindenText.spec.ts +++ b/lib/classes/LLLindenText.spec.ts @@ -8,6 +8,18 @@ describe('LLLindenText', () => const buf = Buffer.from('TGluZGVuIHRleHQgdmVyc2lvbiAyCnsKTExFbWJlZGRlZEl0ZW1zIHZlcnNpb24gMQp7CmNvdW50IDAKfQpUZXh0IGxlbmd0aCA5NDAKCgogICAgSGV5LCB0aGFua3MgZm9yIHlvdXIgcHVyY2hhc2Ugb2YgdGhlIENhc3BlclZlbmQgQ29tcGxldGUgRmF0IFBhY2suCiAgICAKICAgIEVhY2ggY29tcG9uZW50IGluIHRoaXMgZmF0IHBhY2sgaXMgYSBzZXBhcmF0ZSBwcm9kdWN0IHdpdGggaXRzIG93biBpbnN0cnVjdGlvbnMuCiAgICAKICAgIElmIHlvdSdyZSBuZXcgdG8gQ2FzcGVyVmVuZCwgdGhlIGZpcnN0IHN0ZXAgaXMgdG8gc2V0IHVwIHRoZSAiUHJlbWl1bSBWZW5kb3JzIiAtIHRoaXMgaXMgCiAgICB5b3VyIG1haW4gc3lzdGVtIHRoYXQgZXZlcnl0aGluZyBlbHNlIHBsdWdzIGluIHRvLiAKICAgIAogICAgQ2FzcGVyVmVuZCAyIGhhcyBhIGNvbXBsZXRlIG9ubGluZSBtYW51YWwgYXZhaWxhYmxlIGhlcmU6CiAgICAKICAgIGh0dHBzOi8vd2lraS5jYXNwZXJkbnMuY29tL2luZGV4LnBocD90aXRsZT1DYXNwZXJWZW5kXzIKICAgIAogICAgT3VyIG1hbnVhbCBpcyBjb25zdGFudGx5IGJlaW5nIHVwZGF0ZWQgd2l0aCBtb3JlIGhlbHAsIGd1aWRlcyBhbmQgaW5mb3JtYXRpb24uIAogICAgCiAgICBEb24ndCBmb3JnZXQsIGlmIHlvdSBnZXQgc3R1Y2sgLSB3ZSd2ZSBnb3QgYSB1c2VyIHN1cHBvcnQgZ3JvdXAgZnVsbCBvZiB2ZXJ5IGhlbHBmdWwgCiAgICBhbmQgYXdlc29tZSBwZW9wbGUuIFlvdSB3ZXJlIHNlbnQgYW4gaW52aXRlIHRvIHRoZSBncm91cCB3aGVuIHlvdSBwdXJjaGFzZWQsCiAgICBob3dldmVyIGlmIHlvdSBtaXNzZWQgdGhpcywgcGxlYXNlIElNIENhc3BlciBXYXJkZW4gZm9yIGFuIGludml0ZS4KICAgIAogICAgWW91IGNhbiBhbHNvIElNIG1lIGRpcmVjdGx5IGZvciBoZWxwIChDYXNwZXIgV2FyZGVuKS4gSWYgSSdtIG5vdCBvbmxpbmUsCiAgICB5b3Ugd2lsbCByZWNlaXZlIGEgbGluayB3aGljaCBhbGxvd3MgeW91IHRvIHN1Ym1pdCBhIHN1cHBvcnQgdGlja2V0Ln0KAA==', 'base64'); const text = new LLLindenText(buf); const expected = Buffer.from('CgogICAgSGV5LCB0aGFua3MgZm9yIHlvdXIgcHVyY2hhc2Ugb2YgdGhlIENhc3BlclZlbmQgQ29tcGxldGUgRmF0IFBhY2suCiAgICAKICAgIEVhY2ggY29tcG9uZW50IGluIHRoaXMgZmF0IHBhY2sgaXMgYSBzZXBhcmF0ZSBwcm9kdWN0IHdpdGggaXRzIG93biBpbnN0cnVjdGlvbnMuCiAgICAKICAgIElmIHlvdSdyZSBuZXcgdG8gQ2FzcGVyVmVuZCwgdGhlIGZpcnN0IHN0ZXAgaXMgdG8gc2V0IHVwIHRoZSAiUHJlbWl1bSBWZW5kb3JzIiAtIHRoaXMgaXMgCiAgICB5b3VyIG1haW4gc3lzdGVtIHRoYXQgZXZlcnl0aGluZyBlbHNlIHBsdWdzIGluIHRvLiAKICAgIAogICAgQ2FzcGVyVmVuZCAyIGhhcyBhIGNvbXBsZXRlIG9ubGluZSBtYW51YWwgYXZhaWxhYmxlIGhlcmU6CiAgICAKICAgIGh0dHBzOi8vd2lraS5jYXNwZXJkbnMuY29tL2luZGV4LnBocD90aXRsZT1DYXNwZXJWZW5kXzIKICAgIAogICAgT3VyIG1hbnVhbCBpcyBjb25zdGFudGx5IGJlaW5nIHVwZGF0ZWQgd2l0aCBtb3JlIGhlbHAsIGd1aWRlcyBhbmQgaW5mb3JtYXRpb24uIAogICAgCiAgICBEb24ndCBmb3JnZXQsIGlmIHlvdSBnZXQgc3R1Y2sgLSB3ZSd2ZSBnb3QgYSB1c2VyIHN1cHBvcnQgZ3JvdXAgZnVsbCBvZiB2ZXJ5IGhlbHBmdWwgCiAgICBhbmQgYXdlc29tZSBwZW9wbGUuIFlvdSB3ZXJlIHNlbnQgYW4gaW52aXRlIHRvIHRoZSBncm91cCB3aGVuIHlvdSBwdXJjaGFzZWQsCiAgICBob3dldmVyIGlmIHlvdSBtaXNzZWQgdGhpcywgcGxlYXNlIElNIENhc3BlciBXYXJkZW4gZm9yIGFuIGludml0ZS4KICAgIAogICAgWW91IGNhbiBhbHNvIElNIG1lIGRpcmVjdGx5IGZvciBoZWxwIChDYXNwZXIgV2FyZGVuKS4gSWYgSSdtIG5vdCBvbmxpbmUsCiAgICB5b3Ugd2lsbCByZWNlaXZlIGEgbGluayB3aGljaCBhbGxvd3MgeW91IHRvIHN1Ym1pdCBhIHN1cHBvcnQgdGlja2V0Lg==', 'base64'); - assert.equal(text.body, expected.toString('ascii')); + assert.equal(text.body, expected.toString('utf-8')); + const reassembled = text.toAsset(); + assert.equal(reassembled.toString('base64'), buf.toString('base64')); + }); + + it('can successfully parse and reassemble a notecard asset with embedded assets', () => + { + const buf = Buffer.from('TGluZGVuIHRleHQgdmVyc2lvbiAyCnsKTExFbWJlZGRlZEl0ZW1zIHZlcnNpb24gMQp7CmNvdW50IDIKewpleHQgY2hhciBpbmRleCAwCglpbnZfaXRlbQkwCgl7CgkJaXRlbV9pZAkyZjVhYzI4MC0yYjIwLTI0MmEtY2IwNS1jNGJjOGVlYzA2NDgKCQlwYXJlbnRfaWQJOWJmNmRhNTYtNTE1NS00OTEwLWJhODMtYTg2OWY3YTc0MTdjCglwZXJtaXNzaW9ucyAwCgl7CgkJYmFzZV9tYXNrCTdmZmZmZmZmCgkJb3duZXJfbWFzawk3ZmZmZmZmZgoJCWdyb3VwX21hc2sJN2ZmZmZmZmYKCQlldmVyeW9uZV9tYXNrCTdmZmZiZmZmCgkJbmV4dF9vd25lcl9tYXNrCTdmZmZmZmZmCgkJY3JlYXRvcl9pZAlkMWNkNWI3MS02MjA5LTQ1OTUtOWJmMC03NzFiZjY4OWNlMDAKCQlvd25lcl9pZAlkMWNkNWI3MS02MjA5LTQ1OTUtOWJmMC03NzFiZjY4OWNlMDAKCQlsYXN0X293bmVyX2lkCTAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMAoJCWdyb3VwX2lkCTAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMAoJfQoJCWFzc2V0X2lkCWMwYmQ5NWMxLWVmM2EtZmQ2My0xYTk5LTBhNDVkNmYwNjdhZgoJCXR5cGUJdGV4dHVyZQoJCWludl90eXBlCXRleHR1cmUKCQlmbGFncwkwMDAwMDAwMAoJc2FsZV9pbmZvCTAKCXsKCQlzYWxlX3R5cGUJbm90CgkJc2FsZV9wcmljZQkxMAoJfQoJCW5hbWUJUGVybWlzc2lvbnMgcmVxdWVzdCBkaWFsb2d8CgkJZGVzYwkoTm8gRGVzY3JpcHRpb24pfAoJCWNyZWF0aW9uX2RhdGUJMTM0MDc0Njc0NAoJfQp9CnsKZXh0IGNoYXIgaW5kZXggMQoJaW52X2l0ZW0JMAoJewoJCWl0ZW1faWQJZjNmMzYwYTgtNTkwMS00MTE3LTFiMDYtZjk4NTA0ODU5ZWJhCgkJcGFyZW50X2lkCTliZjZkYTU2LTUxNTUtNDkxMC1iYTgzLWE4NjlmN2E3NDE3YwoJcGVybWlzc2lvbnMgMAoJewoJCWJhc2VfbWFzawk3ZmZmZmZmZgoJCW93bmVyX21hc2sJN2ZmZmZmZmYKCQlncm91cF9tYXNrCTdmZmZmZmZmCgkJZXZlcnlvbmVfbWFzawk3ZmZmYmZmZgoJCW5leHRfb3duZXJfbWFzawk3ZmZmZmZmZgoJCWNyZWF0b3JfaWQJZDFjZDViNzEtNjIwOS00NTk1LTliZjAtNzcxYmY2ODljZTAwCgkJb3duZXJfaWQJZDFjZDViNzEtNjIwOS00NTk1LTliZjAtNzcxYmY2ODljZTAwCgkJbGFzdF9vd25lcl9pZAkwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAKCQlncm91cF9pZAkwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAKCX0KCQlhc3NldF9pZAk3M2QyNmE5Yy03MWQxLTYzN2MtMGFkMi00ZGVkOWYzZjU5ZWMKCQl0eXBlCXRleHR1cmUKCQlpbnZfdHlwZQl0ZXh0dXJlCgkJZmxhZ3MJMDAwMDAwMDAKCXNhbGVfaW5mbwkwCgl7CgkJc2FsZV90eXBlCW5vdAoJCXNhbGVfcHJpY2UJMTAKCX0KCQluYW1lCVBlcm1pc3Npb25zIHJlcXVlc3QgZGlhbG9nIChOZXdlcil8CgkJZGVzYwkoTm8gRGVzY3JpcHRpb24pfAoJCWNyZWF0aW9uX2RhdGUJMTM0MDc0Njc1MQoJfQp9Cn0KVGV4dCBsZW5ndGggMjM1MgoKICAgIENhc3BlclZlbmQgdXNlcyBhbmQgcmVxdWlyZXMgIkRlYml0IFBlcm1pc3Npb25zIiB0byBmdW5jdGlvbi4KICAgIAogICAgVGhlc2UgcGVybWlzc2lvbnMgYXJlIHJlcXVlc3RlZCB1c2luZyBhIGRpYWxvZyBzaW1pbGFyIHRvIHRoaXM6CiAgICAKICAgICD0gICAICAoQ2xpY2sgdGhlIGxpbmsgdG8gb3BlbiB0aGUgaW1hZ2UpCiAgICAgCiAgICAgb3IgbWF5IHBlcmhhcHMgYmUgd29yZGVkIGFzIGZvbGxvd3M6CiAgICAgCiAgICAg9ICAgQogICAgIAogICAgIAogICAgIEFzIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIHN1Z2dlc3QsIHRoaXMgcGVybWlzc2lvbiBhbGxvd3MgdGhlIG9iamVjdCBhY2Nlc3MgdG8geW91ciBMaW5kZW4gRG9sbGFyIChMJCkKICAgICBhY2NvdW50LCBnaXZpbmcgaXQgdGhlIGFiaWxpdHkgdG8gc2VuZCBtb25leSBmcm9tIHlvdXIgYWNjb3VudCB0byBhbnkgb3RoZXIgYXZhdGFyLgogICAgIAogICAgIFRoaXMgcGVybWlzc2lvbiBpcyByZXF1aXJlZCBmb3Igb3VyIHZlbmRvcnMgdG8gZnVuY3Rpb24uICBXZSB1c2UgaXQgZm9yIHRoZSBmb2xsb3dpbmcgcHVycG9zZXM6CiAgICAgCiAgICAgICAgICAgICogIFRvIHJlZnVuZCBzb21lIG1vbmV5IHRvIHRoZSBjdXN0b21lciBpZiB0aGV5IHBheSB0b28gbXVjaC4KICAgICAgICAgICAgCiAgICAgICAgICAgICogIFRvIHJlZnVuZCB0aGUgY3VzdG9tZXIgaWYgd2UgY2FuJ3QgY29tcGxldGUgdGhlIGRlbGl2ZXJ5IGZvciBzb21lIHJlYXNvbi4KICAgICAgICAgICAgCiAgICAgICAgICAgICogIFRvIHJlZnVuZCB0aGUgY3VzdG9tZXIgaWYgdGhlIGl0ZW0gaXMgbm8gbG9uZ2VyIGF2YWlsYWJsZSAobGltaXRlZCBlZGl0aW9uIGl0ZW0pCiAgICAgICAgICAgIAogICAgICAgICAgICAqICBUbyBwYXkgZGlzY291bnRzIHRvIGN1c3RvbWVycyBpZiB5b3UgY2hvb3NlIHRvIHVzZSBvdXIgcmV3YXJkIHN5c3RlbS4KICAgICAgICAgICAgCiAgICAgICAgICAgICogIFRvIHBheSBwcm9maXQgc2hhcmVzIHRvIHlvdXIgcGFydG5lcnMsIGlmIHlvdSBuZWVkIHRvIHVzZSBhIHNwbGl0IHBheSBzeXN0ZW0uCiAgICAgICAgICAgIAogICAgICAgICAgICAqICBUbyBwYXkgeW91ciBjb21taXNzaW9uIGZlZSBpbiB0aGUgY2FzZSBvZiBmcmVlIHZlbmRvcnMgb3IgYWZmaWxpYXRlcy4KICAgICAgICAgICAgCiAgICAgICAgICAgICogIEluIHRoZSBjYXNlIG9mIGFmZmlsaWF0ZXMsIHRvIGZvcndhcmQgdGhlIG1vbmV5IGZyb20gdGhlIGFmZmlsaWF0ZSB0byB5b3UuCiAgICAgICAgICAgIAogICAgICBDYXNwZXJWZW5kIGlzIHVzZWQgYnkgdGVucyBvZiB0aG91c2FuZHMgb2YgbWVyY2hhbnRzIGluIFNlY29uZCBMaWZlLCBhbGwgb2Ygd2hvbSBoYXZlIGdyYW50ZWQgdGhpcwogICAgICBwZXJtaXNzaW9uLiBXZSBhcmUgYSByZXNwb25zaWJsZSBhbmQgc2VjdXJlIHNlcnZpY2UgcHJvdmlkZXIuCiAgICAgIAogICAgICBBbGwgdmVuZG9ycyBpbiBTZWNvbmQgTGlmZSByZXF1aXJlIHRoaXMgcGVybWlzc2lvbiBpbiBvcmRlciB0byBwcm9wZXJseSBmdW5jdGlvbi4gSG93ZXZlciwgaXQncyB2ZXJ5CiAgICAgIGltcG9ydGFudCB0aGF0IHlvdSBwYXkgYXR0ZW50aW9uIGV2ZXJ5IHRpbWUgeW91IHJlY2VpdmUgdGhpcyBkaWFsb2cgLSBhIG1hbGljaW91cyBvYmplY3QgY2FuIHZlcnkgZWFzaWx5IAogICAgICBlbXB0eSBhbGwgdGhlIG1vbmV5IG91dCBvZiB5b3VyIGFjY291bnQuCiAgICAgIAogICAgICBJZiB5b3UgcmVjZWl2ZSBhICJkZWJpdCBwZXJtaXNzaW9ucyIgZGlhbG9nLCBtYWtlIHN1cmUgdGhhdCBpdCBpcyBiZWluZyBzZW50IGJ5IGFuIG9iamVjdCB5b3Uga25vdyBhbmQgdHJ1c3QsCiAgICAgIGxpa2UgYSBDYXNwZXJWZW5kIHZlbmRvci4gCiAgICAgIAogICAgICBDYXNwZXJWZW5kIHZlbmRvcnMgd2lsbCByZXF1ZXN0IHBlcm1pc3Npb25zIGZyb20geW91IG9uIHRoZXNlIG9jY2FzaW9uczoKICAgICAgCiAgICAgICAgICAgIC0gV2hlbiB0aGUgdmVuZG9yIGlzIHJlenplZAogICAgICAgICAgICAKICAgICAgICAgICAgLSBXaGVuIHRoZSB2ZW5kb3IncyBzY3JpcHRzIGFyZSByZXNldAogICAgICAgICAgICAKICAgICAgICAgICAgLSBXaGVuIHRoZSB2ZW5kb3IgaXMgYmVpbmcgdXBkYXRlZCBieSBhbiBVcGdyYWRlQmVlLgogICAgICAgICAgICAKICAgIEZvciB0aGUgaGlnaGVzdCBsZXZlbCBvZiBzZWN1cml0eSwgb25lIG1pZ2h0IGNob29zZSB0byB1c2UgYSBzZXBhcmF0ZSBhdmF0YXIgZm9yIHNjcmlwdHMgdGhhdCByZXF1aXJlIGRlYml0CiAgICBwZXJtaXNzaW9ucywgd2hpY2ggd2lsbCBhdm9pZCBnaXZpbmcgYWNjZXNzIHRvIHlvdXIgcGVyc29uYWwgYmFsYW5jZS59CgA=', 'base64'); + const text = new LLLindenText(buf); + const expected = Buffer.from('CiAgICBDYXNwZXJWZW5kIHVzZXMgYW5kIHJlcXVpcmVzICJEZWJpdCBQZXJtaXNzaW9ucyIgdG8gZnVuY3Rpb24uCiAgICAKICAgIFRoZXNlIHBlcm1pc3Npb25zIGFyZSByZXF1ZXN0ZWQgdXNpbmcgYSBkaWFsb2cgc2ltaWxhciB0byB0aGlzOgogICAgCiAgICAg9ICAgCAgKENsaWNrIHRoZSBsaW5rIHRvIG9wZW4gdGhlIGltYWdlKQogICAgIAogICAgIG9yIG1heSBwZXJoYXBzIGJlIHdvcmRlZCBhcyBmb2xsb3dzOgogICAgIAogICAgIPSAgIEKICAgICAKICAgICAKICAgICBBcyB0aGUgd2FybmluZyBtZXNzYWdlcyBzdWdnZXN0LCB0aGlzIHBlcm1pc3Npb24gYWxsb3dzIHRoZSBvYmplY3QgYWNjZXNzIHRvIHlvdXIgTGluZGVuIERvbGxhciAoTCQpCiAgICAgYWNjb3VudCwgZ2l2aW5nIGl0IHRoZSBhYmlsaXR5IHRvIHNlbmQgbW9uZXkgZnJvbSB5b3VyIGFjY291bnQgdG8gYW55IG90aGVyIGF2YXRhci4KICAgICAKICAgICBUaGlzIHBlcm1pc3Npb24gaXMgcmVxdWlyZWQgZm9yIG91ciB2ZW5kb3JzIHRvIGZ1bmN0aW9uLiAgV2UgdXNlIGl0IGZvciB0aGUgZm9sbG93aW5nIHB1cnBvc2VzOgogICAgIAogICAgICAgICAgICAqICBUbyByZWZ1bmQgc29tZSBtb25leSB0byB0aGUgY3VzdG9tZXIgaWYgdGhleSBwYXkgdG9vIG11Y2guCiAgICAgICAgICAgIAogICAgICAgICAgICAqICBUbyByZWZ1bmQgdGhlIGN1c3RvbWVyIGlmIHdlIGNhbid0IGNvbXBsZXRlIHRoZSBkZWxpdmVyeSBmb3Igc29tZSByZWFzb24uCiAgICAgICAgICAgIAogICAgICAgICAgICAqICBUbyByZWZ1bmQgdGhlIGN1c3RvbWVyIGlmIHRoZSBpdGVtIGlzIG5vIGxvbmdlciBhdmFpbGFibGUgKGxpbWl0ZWQgZWRpdGlvbiBpdGVtKQogICAgICAgICAgICAKICAgICAgICAgICAgKiAgVG8gcGF5IGRpc2NvdW50cyB0byBjdXN0b21lcnMgaWYgeW91IGNob29zZSB0byB1c2Ugb3VyIHJld2FyZCBzeXN0ZW0uCiAgICAgICAgICAgIAogICAgICAgICAgICAqICBUbyBwYXkgcHJvZml0IHNoYXJlcyB0byB5b3VyIHBhcnRuZXJzLCBpZiB5b3UgbmVlZCB0byB1c2UgYSBzcGxpdCBwYXkgc3lzdGVtLgogICAgICAgICAgICAKICAgICAgICAgICAgKiAgVG8gcGF5IHlvdXIgY29tbWlzc2lvbiBmZWUgaW4gdGhlIGNhc2Ugb2YgZnJlZSB2ZW5kb3JzIG9yIGFmZmlsaWF0ZXMuCiAgICAgICAgICAgIAogICAgICAgICAgICAqICBJbiB0aGUgY2FzZSBvZiBhZmZpbGlhdGVzLCB0byBmb3J3YXJkIHRoZSBtb25leSBmcm9tIHRoZSBhZmZpbGlhdGUgdG8geW91LgogICAgICAgICAgICAKICAgICAgQ2FzcGVyVmVuZCBpcyB1c2VkIGJ5IHRlbnMgb2YgdGhvdXNhbmRzIG9mIG1lcmNoYW50cyBpbiBTZWNvbmQgTGlmZSwgYWxsIG9mIHdob20gaGF2ZSBncmFudGVkIHRoaXMKICAgICAgcGVybWlzc2lvbi4gV2UgYXJlIGEgcmVzcG9uc2libGUgYW5kIHNlY3VyZSBzZXJ2aWNlIHByb3ZpZGVyLgogICAgICAKICAgICAgQWxsIHZlbmRvcnMgaW4gU2Vjb25kIExpZmUgcmVxdWlyZSB0aGlzIHBlcm1pc3Npb24gaW4gb3JkZXIgdG8gcHJvcGVybHkgZnVuY3Rpb24uIEhvd2V2ZXIsIGl0J3MgdmVyeQogICAgICBpbXBvcnRhbnQgdGhhdCB5b3UgcGF5IGF0dGVudGlvbiBldmVyeSB0aW1lIHlvdSByZWNlaXZlIHRoaXMgZGlhbG9nIC0gYSBtYWxpY2lvdXMgb2JqZWN0IGNhbiB2ZXJ5IGVhc2lseSAKICAgICAgZW1wdHkgYWxsIHRoZSBtb25leSBvdXQgb2YgeW91ciBhY2NvdW50LgogICAgICAKICAgICAgSWYgeW91IHJlY2VpdmUgYSAiZGViaXQgcGVybWlzc2lvbnMiIGRpYWxvZywgbWFrZSBzdXJlIHRoYXQgaXQgaXMgYmVpbmcgc2VudCBieSBhbiBvYmplY3QgeW91IGtub3cgYW5kIHRydXN0LAogICAgICBsaWtlIGEgQ2FzcGVyVmVuZCB2ZW5kb3IuIAogICAgICAKICAgICAgQ2FzcGVyVmVuZCB2ZW5kb3JzIHdpbGwgcmVxdWVzdCBwZXJtaXNzaW9ucyBmcm9tIHlvdSBvbiB0aGVzZSBvY2Nhc2lvbnM6CiAgICAgIAogICAgICAgICAgICAtIFdoZW4gdGhlIHZlbmRvciBpcyByZXp6ZWQKICAgICAgICAgICAgCiAgICAgICAgICAgIC0gV2hlbiB0aGUgdmVuZG9yJ3Mgc2NyaXB0cyBhcmUgcmVzZXQKICAgICAgICAgICAgCiAgICAgICAgICAgIC0gV2hlbiB0aGUgdmVuZG9yIGlzIGJlaW5nIHVwZGF0ZWQgYnkgYW4gVXBncmFkZUJlZS4KICAgICAgICAgICAgCiAgICBGb3IgdGhlIGhpZ2hlc3QgbGV2ZWwgb2Ygc2VjdXJpdHksIG9uZSBtaWdodCBjaG9vc2UgdG8gdXNlIGEgc2VwYXJhdGUgYXZhdGFyIGZvciBzY3JpcHRzIHRoYXQgcmVxdWlyZSBkZWJpdAogICAgcGVybWlzc2lvbnMsIHdoaWNoIHdpbGwgYXZvaWQgZ2l2aW5nIGFjY2VzcyB0byB5b3VyIHBlcnNvbmFsIGJhbGFuY2Uu', 'base64'); + assert.equal(text.body, expected.toString('utf-8')); + const reassembled = text.toAsset(); + assert.equal(reassembled.toString('base64'), buf.toString('base64')); }); }); diff --git a/lib/classes/LLLindenText.ts b/lib/classes/LLLindenText.ts index c0ad72f..ce25017 100644 --- a/lib/classes/LLLindenText.ts +++ b/lib/classes/LLLindenText.ts @@ -1,4 +1,5 @@ import { InventoryItem } from './InventoryItem'; +import { Utils } from './Utils'; export class LLLindenText { @@ -6,14 +7,14 @@ export class LLLindenText private lineObj: { lines: string[], - lineNum: number + lineNum: number, + pos: number } = { lines: [], - lineNum: 0 + lineNum: 0, + pos: 0 }; - private pos = 0; - body = ''; embeddedItems: { [key: number]: InventoryItem } = {}; @@ -23,9 +24,9 @@ export class LLLindenText { const initial = data.toString('ascii'); this.lineObj.lines = initial.split('\n'); - this.pos = 0; + this.lineObj.pos = 0; - let line = this.getLine(); + let line = Utils.getNotecardLine(this.lineObj); if (!line.startsWith('Linden text version')) { throw new Error('Invalid Linden Text header'); @@ -40,28 +41,28 @@ export class LLLindenText const v2 = data.toString('utf-8'); this.lineObj.lines = v2.replace(/\r\n/g, '\n').split('\n'); } - line = this.getLine(); + line = Utils.getNotecardLine(this.lineObj); if (line !== '{') { throw new Error('Error parsing Linden Text file'); } - line = this.getLine(); + line = Utils.getNotecardLine(this.lineObj); if (line.startsWith('LLEmbeddedItems')) { this.parseEmbeddedItems(); - line = this.getLine(); + line = Utils.getNotecardLine(this.lineObj); } if (!line.startsWith('Text length')) { throw new Error('Error parsing Linden Text file: ' + line); } const textLength = parseInt(this.getLastToken(line), 10); - this.body = data.slice(this.pos, this.pos + textLength).toString(); - const remainingBuffer = data.slice(this.pos + textLength).toString('ascii'); + this.body = data.slice(this.lineObj.pos, this.lineObj.pos + textLength).toString(); + const remainingBuffer = data.slice(this.lineObj.pos + textLength).toString('ascii'); this.lineObj.lines = remainingBuffer.split('\n'); this.lineObj.lineNum = 0; - line = this.getLine(); + line = Utils.getNotecardLine(this.lineObj); if (line !== '}') { throw new Error('Error parsing Linden Text file'); @@ -93,17 +94,17 @@ export class LLLindenText { return Buffer.from(lines.join('\n') + '\n', 'ascii'); } - return Buffer.from(lines.join('\n') + '\n', 'utf-8'); + return Buffer.from(lines.join('\n') + '\n\0', 'utf-8'); } private parseEmbeddedItems(): void { - let line = this.getLine(); + let line = Utils.getNotecardLine(this.lineObj); if (line !== '{') { throw new Error('Invalid LLEmbeddedItems format (no opening brace)'); } - line = this.getLine(); + line = Utils.getNotecardLine(this.lineObj); if (!line.startsWith('count')) { throw new Error('Invalid LLEmbeddedItems format (no count)'); @@ -111,31 +112,31 @@ export class LLLindenText const itemCount = parseInt(this.getLastToken(line), 10); for (let x = 0; x < itemCount; x++) { - line = this.getLine(); + line = Utils.getNotecardLine(this.lineObj); if (line !== '{') { throw new Error('Invalid LLEmbeddedItems format (no item opening brace)'); } - line = this.getLine(); + line = Utils.getNotecardLine(this.lineObj); if (!line.startsWith('ext char index')) { throw new Error('Invalid LLEmbeddedItems format (no ext char index)'); } const index = parseInt(this.getLastToken(line), 10); - line = this.getLine(); + line = Utils.getNotecardLine(this.lineObj); if (!line.startsWith('inv_item')) { throw new Error('Invalid LLEmbeddedItems format (no inv_item)'); } - const item = InventoryItem.fromAsset(this.lineObj); + const item = InventoryItem.fromEmbeddedAsset(this.lineObj); this.embeddedItems[index] = item; - line = this.getLine(); + line = Utils.getNotecardLine(this.lineObj); if (line !== '}') { throw new Error('Invalid LLEmbeddedItems format (no closing brace)'); } } - line = this.getLine(); + line = Utils.getNotecardLine(this.lineObj); if (line !== '}') { throw new Error('Error parsing Linden Text file'); @@ -154,12 +155,4 @@ export class LLLindenText return input.substr(index + 1); } } - - private getLine(): string - { - const line = this.lineObj.lines[this.lineObj.lineNum++]; - this.pos += Buffer.byteLength(line) + 1; - return line.replace(/\r/, '').trim().replace(/[\t ]+/g, ' '); - } - } diff --git a/lib/classes/Utils.ts b/lib/classes/Utils.ts index c54c003..2f20cfa 100644 --- a/lib/classes/Utils.ts +++ b/lib/classes/Utils.ts @@ -978,4 +978,15 @@ export class Utils }); }); } + + public static getNotecardLine(lineObj: { + lines: string[], + lineNum: number, + pos: number + }): string + { + const line = lineObj.lines[lineObj.lineNum++]; + lineObj.pos += Buffer.byteLength(line) + 1; + return line.replace(/\r/, '').trim().replace(/[\t ]+/g, ' '); + } } diff --git a/lib/classes/public/GameObject.ts b/lib/classes/public/GameObject.ts index 329e020..db9e978 100644 --- a/lib/classes/public/GameObject.ts +++ b/lib/classes/public/GameObject.ts @@ -990,11 +990,12 @@ export class GameObject implements IGameObjectData const str = file.toString('utf-8'); const lineObj = { lines: str.replace(/\r\n/g, '\n').split('\n'), - lineNum: 0 + lineNum: 0, + pos: 0 }; while (lineObj.lineNum < lineObj.lines.length) { - const line = lineObj.lines[lineObj.lineNum++]; + const line = Utils.getNotecardLine(lineObj); const result = Utils.parseLine(line); if (result.key !== null) { @@ -1047,7 +1048,7 @@ export class GameObject implements IGameObjectData */ break; case 'inv_item': - this.inventory.push(InventoryItem.fromAsset(lineObj, this, this.region.agent)); + this.inventory.push(InventoryItem.fromEmbeddedAsset(lineObj, this, this.region.agent)); break; } } diff --git a/package-lock.json b/package-lock.json index 7bf722f..f08264c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@caspertech/node-metaverse", - "version": "0.6.19", + "version": "0.6.22", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@caspertech/node-metaverse", - "version": "0.6.19", + "version": "0.6.22", "license": "MIT", "dependencies": { "@caspertech/llsd": "^1.0.5", diff --git a/package.json b/package.json index 781a89d..f87c60d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@caspertech/node-metaverse", - "version": "0.6.21", + "version": "0.6.22", "description": "A node.js interface for Second Life.", "main": "dist/lib/index.js", "types": "dist/lib/index.d.ts",