Skip to content

Commit

Permalink
create a tool to lowercase the xml tags
Browse files Browse the repository at this point in the history
  • Loading branch information
valorad committed Oct 19, 2020
1 parent e25045f commit 3aa7653
Show file tree
Hide file tree
Showing 7 changed files with 332 additions and 1 deletion.
5 changes: 4 additions & 1 deletion deps/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ Data:
- Data.bar

Sound:
- Sound.bar
- Sound.bar

## Note
Turn out you don't really need a .bar file to make the mod work. You just need to place your mod with the xml files into local mods folder under directory `User/Games/Age of Empires 3 DE/[ID]/mods/local/`
2 changes: 2 additions & 0 deletions tools/xml-tag-to-lowercase/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
workshop/**
!.gitkeep
68 changes: 68 additions & 0 deletions tools/xml-tag-to-lowercase/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
std_fs,
std_path,
} from './deps.ts';

import {parse, serialize, Document as XMLDocument, Xml} from './xml.ts';

const {walk} = std_fs;
const {extname} = std_path;

class App {

files: std_fs.WalkEntry[] = [];

tagToLowerCase = (parent?: Xml) => {
if (!parent) {
return;
}

parent.name = parent.name.toLowerCase();

for (let child of parent.children) {
this.tagToLowerCase(child);
}
};

markFiles = async () => {
for await (const entry of walk("./workshop")) {
if (entry.isFile && extname(entry.name) === ".xml") {

this.files.push(entry);

}
}
};

transform = async () => {

for (let file of this.files) {
let text: string = await Deno.readTextFile(file.path);

const deserializedObject = parse(text);

if (deserializedObject.root) {
this.tagToLowerCase(deserializedObject.root);
await Deno.writeTextFile(file.path, serialize(deserializedObject.root));
}

}

};

initialize = async () => {
await this.markFiles();
await this.transform();
}

/**
*
*/
constructor() {
this.initialize();
}


}

export default new App();
2 changes: 2 additions & 0 deletions tools/xml-tag-to-lowercase/deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * as std_fs from 'https://deno.land/std@0.74.0/fs/mod.ts';
export * as std_path from "https://deno.land/std@0.74.0/path/mod.ts";
47 changes: 47 additions & 0 deletions tools/xml-tag-to-lowercase/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# xml-tag-to-lowercase @ AOE3NativeED
Transform all the xml tags to lower case, but leave all the values of attributes and xml contents untouched.

e.g.

Before:

``` xml
<ProtoAction>
<Name>MeleeHandAttack</Name>
<Damage>20.000000</Damage>
<DamageType>Hand</DamageType>
<ROF>1.00000</ROF>
<DamageBonus type="AbstractCavalry">2.000000</DamageBonus>
</ProtoAction>
```

After: (after beautification)
``` xml
<protoaction>
<name>MeleeHandAttack</name>
<damage>20.000000</damage>
<damagetype>Hand</damagetype>
<rof>1.00000</rof>
<damagebonus type="AbstractCavalry">2.000000</damagebonus>
</protoaction>
```

(Note: The result will be an one-line xml, so you may need a beautifier to do the further formatings.)

## Prerequisite

[deno](https://github.com/denoland/deno) > 1.0.0

## How to use this tool

Step 1: Place all your xml files under `workshop` folder, and this tool will scan all the files in that folder, then filter out the xml files for you.

(You don't need to worry about folder structure, as in the end the files will be overwritten in place.)

Step 2: Run the following command:

``` shell
deno run --allow-read --allow-write --unstable app.ts
```

Step 3: Check out the `workshop` folder, and you can see the transformation is complete.
Empty file.
209 changes: 209 additions & 0 deletions tools/xml-tag-to-lowercase/xml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
export interface Document {
declaration: {
attributes: {};
};
root: {
name: string;
attributes: {};
children: any[];
} | undefined;
}

export interface Xml {
name: string;
attributes: any;
content?: string;
children: Xml[];
}

/**
* Parse the given string of `xml`.
*
* @param {String} xml
* @return {Object}
* @api public
*/

export function parse(xml: string): Document {
xml = xml.trim();

// strip comments
xml = xml.replace(/<!--[\s\S]*?-->/g, "");

return document();

/**
* XML document.
*/

function document(): Document {
return {
declaration: declaration(),
root: tag()
};
}

/**
* Declaration.
*/

function declaration() {
var m = match(/^<\?xml\s*/);
if (!m) return;

// tag
var node: any = {
attributes: {}
};

// attributes
while (!(eos() || is("?>"))) {
var attr = attribute();
if (!attr) return node;
node.attributes[attr.name] = attr.value;
}

match(/\?>\s*/);

return node;
}

/**
* Tag.
*/

function tag() {
var m = match(/^<([\w-:.]+)\s*/);
if (!m) return;

// name
var node: Xml = {
name: m[1],
attributes: {},
children: []
};

// attributes
while (!(eos() || is(">") || is("?>") || is("/>"))) {
var attr = attribute();
if (!attr) return node;
node.attributes[attr.name] = attr.value;
}

// self closing tag
if (match(/^\s*\/>\s*/)) {
return node;
}

match(/\??>\s*/);

// content
node.content = content();

// children
var child;
while ((child = tag())) {
node.children.push(child);
}

// closing
match(/^<\/[\w-:.]+>\s*/);

return node;
}

/**
* Text content.
*/

function content() {
var m = match(/^([^<]*)/);
if (m) return m[1];
return "";
}

/**
* Attribute.
*/

function attribute() {
var m = match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/);
if (!m) return;
return { name: m[1], value: strip(m[2]) };
}

/**
* Strip quotes from `val`.
*/

function strip(val: string) {
return val.replace(/^['"]|['"]$/g, "");
}

/**
* Match `re` and advance the string.
*/

function match(re: RegExp) {
var m = xml.match(re);
if (!m) return;
xml = xml.slice(m[0].length);
return m;
}

/**
* End-of-source.
*/

function eos() {
return 0 == xml.length;
}

/**
* Check for `prefix`.
*/

function is(prefix: string) {
return 0 == xml.indexOf(prefix);
}
}

export function serialize(...nodes: (Xml[])): string {

const get_children = (tag: Xml) =>
(tag.content)
? tag.content
: tag.children
.map((child) => serialize(child))
.join("");

const format_attributes = (node: Xml) => {

let attributesString = "";

for (let key in node.attributes) {
attributesString += ` ${key}="${node.attributes[key]}"`;
}

return attributesString;
}


const format_tag = (name: string, attributes: string, content?: string) =>
`<${name}${attributes.length > 0
? `${attributes}`
: ``}>${content}</${name}>`;


return (nodes as Array<Xml>)
.map((node: Xml) => {

const attributes = format_attributes(node);

const tag = node as Xml;
const children = get_children(tag);
return format_tag(tag.name, attributes, children);

})
.join("");
}

0 comments on commit 3aa7653

Please # to comment.