Parse manifest.xml into JSON

This commit is contained in:
C-3PO 2018-06-23 21:33:35 +02:00
parent 77e039901a
commit 82804f6657
Signed by: c3po
GPG key ID: 62993C4BB4D86F24
3 changed files with 51 additions and 21 deletions

View file

@ -2,6 +2,7 @@ import { TextDecoder } from 'util';
import * as xmlJs from 'xml-js';
import getUrlContents from '../cdn/getUrlContents';
import { Product } from '../interfaces/ISettings';
import parsePatchmanifest from '../ssn/parsePatchmanifest';
import verifyPatchmanifest from '../ssn/verifyPatchmanifest';
import verifyProductName from '../ssn/verifyProductName';
import extractFile from './extractFile';
@ -21,6 +22,7 @@ export default async function getPatchmanifest(product: Product): Promise<any> {
//Parse .patchmanifest file
const fileEntries = readSsnFile(ssnFile);
//Verify .patchmanifest file
if (fileEntries.length !== 1) {
throw new Error(`Expected .patchmanifest to contain 1 file but it had "${fileEntries.length}" files.`);
}
@ -32,10 +34,18 @@ export default async function getPatchmanifest(product: Product): Promise<any> {
//Extract manifest.xml file
const patchmanifestFile = await extractFile(firstFile, [new DataView(ssnFile)]);
//Convert ArrayBuffer to string
const patchmanifestXml = Decoder.decode(patchmanifestFile);
//convert XML to JSON-converted XML
const patchManifestJson = xmlJs.xml2js(patchmanifestXml) as xmlJs.Element;
//verify that XML file contains all required elements and attributes
verifyPatchmanifest(patchManifestJson, product);
return patchManifestJson;
//convert JSON-converted XML to an easier to read JSON
const patchManifestSimple = parsePatchmanifest(patchManifestJson);
return patchManifestSimple;
}

View file

@ -0,0 +1,20 @@
/** Receives a JSON-converted version of the manifest.xml file, and returns an easier to read JSON file */
export default function parsePatchManifest(manifestFile: any): any {
const out: any = {};
const PatchManifest = manifestFile.elements[0];
//<RequiredRelease>289</RequiredRelease>
const RequiredRelease = PatchManifest.elements[2];
out.current = Number(RequiredRelease.elements[0].text);
//<Releases><Release><Id>0</Id><SHA1>53678f8057e52896a8145dca5c188ab3f24fa55f</SHA1></Release></Releases>
const Releases = PatchManifest.elements[7];
out.releases = Releases.elements.map((Release: any) => ({ id: Release.elements[0].elements[0].text, sha1: Release.elements[1].elements[0].text }));
const ReleaseUpdatePaths = PatchManifest.elements[8];
out.paths = ReleaseUpdatePaths.elements.map((ReleaseUpdatePath: any) => ({ from: ReleaseUpdatePath.elements[0].elements[0].text, to: ReleaseUpdatePath.elements[1].elements[0].text }));
return out;
}

View file

@ -1,45 +1,45 @@
import * as xmlJs from 'xml-js';
import { Product } from '../interfaces/ISettings';
/** Receives a JSON-converted version of the manifest.xml file */
export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product): any {
/** Receives a JSON-converted version of the manifest.xml file, and verifies that all required elements and attributes are present, and nothing more */
export default function verifyPatchmanifest(manifestFile: xmlJs.Element, product: Product): any {
//<?xml version="1.0" encoding="utf-8"?>
if (obj.declaration === undefined || obj.declaration.attributes === undefined || Object.keys(obj.declaration.attributes).length !== 2 || obj.declaration.attributes.version !== '1.0' || obj.declaration.attributes.encoding !== 'utf-8') {
if (manifestFile.declaration === undefined || manifestFile.declaration.attributes === undefined || Object.keys(manifestFile.declaration.attributes).length !== 2 || manifestFile.declaration.attributes.version !== '1.0' || manifestFile.declaration.attributes.encoding !== 'utf-8') {
throw new Error('Expected declration with version 1.0 and utf-8 encoding.');
}
//<PatchManifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
if (obj.elements === undefined || obj.elements.length !== 1) {
if (manifestFile.elements === undefined || manifestFile.elements.length !== 1) {
throw new Error('Expected one root element.');
}
const root = obj.elements[0];
if (root.type !== 'element' || root.name !== 'PatchManifest') {
throw new Error(`Expected root element to be called PatchManifest but it was "${root.name}".`);
const PatchManifest = manifestFile.elements[0];
if (PatchManifest.type !== 'element' || PatchManifest.name !== 'PatchManifest') {
throw new Error(`Expected root element to be called PatchManifest but it was "${PatchManifest.name}".`);
}
if (root.attributes === undefined || Object.keys(root.attributes).length !== 2 || root.attributes['xmlns:xsi'] !== 'http://www.w3.org/2001/XMLSchema-instance' || root.attributes['xmlns:xsd'] !== 'http://www.w3.org/2001/XMLSchema') {
if (PatchManifest.attributes === undefined || Object.keys(PatchManifest.attributes).length !== 2 || PatchManifest.attributes['xmlns:xsi'] !== 'http://www.w3.org/2001/XMLSchema-instance' || PatchManifest.attributes['xmlns:xsd'] !== 'http://www.w3.org/2001/XMLSchema') {
throw new Error(`Expected root element to have attributes xmlns:xsi and xmlns:xsd.`);
}
if (root.elements === undefined) {
if (PatchManifest.elements === undefined) {
throw new Error(`Expected child elements under PatchManifest but there were none.`);
}
if (root.elements.length !== 9) {
throw new Error(`Expected 9 child elements under PatchManifest but there were ${root.elements.length}.`);
if (PatchManifest.elements.length !== 9) {
throw new Error(`Expected 9 child elements under PatchManifest but there were ${PatchManifest.elements.length}.`);
}
//<Dependencies />
const Dependencies = root.elements[0];
const Dependencies = PatchManifest.elements[0];
if (Dependencies.type !== 'element' || Dependencies.name !== 'Dependencies' || Dependencies.attributes !== undefined || Dependencies.elements !== undefined) {
throw new Error('Expected Dependencies element with no child elements and no attributes.');
}
//<Name>assets_swtor_de_de</Name>
const Name = root.elements[1];
const Name = PatchManifest.elements[1];
if (Name.type !== 'element' || Name.name !== 'Name' || Name.attributes !== undefined || Name.elements === undefined || Name.elements.length !== 1 || Name.elements[0].type !== 'text' || Name.elements[0].text !== product) {
throw new Error('Expected Name element with one child element equal to product and no attributes.');
}
//<RequiredRelease>289</RequiredRelease>
const RequiredRelease = root.elements[2];
const RequiredRelease = PatchManifest.elements[2];
if (RequiredRelease.type !== 'element' || RequiredRelease.name !== 'RequiredRelease' || RequiredRelease.attributes !== undefined || RequiredRelease.elements === undefined) {
throw new Error('Expected RequiredRelease element.');
}
@ -48,7 +48,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product
}
//<UpcomingRelease>-1</UpcomingRelease>
const UpcomingRelease = root.elements[3];
const UpcomingRelease = PatchManifest.elements[3];
if (UpcomingRelease.type !== 'element' || UpcomingRelease.name !== 'UpcomingRelease' || UpcomingRelease.attributes !== undefined || UpcomingRelease.elements === undefined) {
throw new Error('Expected UpcomingRelease element.');
}
@ -57,7 +57,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product
}
//<TargetDirectory>{ModulePath}</TargetDirectory>
const TargetDirectory = root.elements[4];
const TargetDirectory = PatchManifest.elements[4];
if (TargetDirectory.type !== 'element' || TargetDirectory.name !== 'TargetDirectory' || TargetDirectory.attributes !== undefined || TargetDirectory.elements === undefined) {
throw new Error('Expected TargetDirectory element.');
}
@ -66,7 +66,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product
}
//<RequiresElevation>false</RequiresElevation>
const RequiresElevation = root.elements[5];
const RequiresElevation = PatchManifest.elements[5];
if (RequiresElevation.type !== 'element' || RequiresElevation.name !== 'RequiresElevation' || RequiresElevation.attributes !== undefined || RequiresElevation.elements === undefined) {
throw new Error('Expected RequiresElevation element.');
}
@ -75,7 +75,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product
}
//<Maintenance>false</Maintenance>
const Maintenance = root.elements[6];
const Maintenance = PatchManifest.elements[6];
if (Maintenance.type !== 'element' || Maintenance.name !== 'Maintenance' || Maintenance.attributes !== undefined || Maintenance.elements === undefined) {
throw new Error('Expected Maintenance element.');
}
@ -84,7 +84,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product
}
//<Releases>
const Releases = root.elements[7];
const Releases = PatchManifest.elements[7];
if (Releases.type !== 'element' || Releases.name !== 'Releases' || Releases.attributes !== undefined || Releases.elements === undefined) {
throw new Error('Expected Releases element.');
}
@ -112,7 +112,7 @@ export default function verifyPatchmanifest(obj: xmlJs.Element, product: Product
}
//<ReleaseUpdatePaths>
const ReleaseUpdatePaths = root.elements[8];
const ReleaseUpdatePaths = PatchManifest.elements[8];
if (ReleaseUpdatePaths.type !== 'element' || ReleaseUpdatePaths.name !== 'ReleaseUpdatePaths' || ReleaseUpdatePaths.attributes !== undefined || ReleaseUpdatePaths.elements === undefined) {
throw new Error('Expected ReleaseUpdatePaths element.');
}