-
Notifications
You must be signed in to change notification settings - Fork 75
/
xnbcli.js
245 lines (203 loc) · 7.33 KB
/
xnbcli.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
const fs = require('fs');
const path = require('path');
const { program } = require('commander');
const Log = require('./app/Log');
const Xnb = require('./app/Xnb');
const { exportFile, resolveImports } = require('./app/Porter');
const chalk = require('chalk');
const mkdirp = require('mkdirp');
const walk = require('walk');
const got = require('got');
const compareVersions = require('compare-versions');
// used for displaying the tally of success and fail
let success = 0;
let fail = 0;
// define the version number
const VERSION = '1.0.7';
// check for updates
async function checkUpdate() {
try {
// fetch the package.json to see if there's a new version available
const response = await got('https://raw.githubusercontent.com/LeonBlade/xnbcli/master/package.json', { json: true });
const remoteVersion = response.body.version;
// compare remote version with the current version
if (compareVersions(remoteVersion, VERSION) > 0) {
// NOTE: this bugs the user every time they run the tool, not exactly a bad idea but maybe should think
// of a different approach to not hit github every time? idk maybe it doesn't matter though
Log.info(`${chalk.bold.green(`xnbcli v${remoteVersion} is available!`)} Visit ${chalk.blue('https://github.com/LeonBlade/xnbcli/releases')} to get the latest release.`);
}
}
catch (error) {
Log.error('Failed to search for a new update. Application should still function normally.');
Log.error(error);
}
}
(() => {
// call the init function to get the party started
init();
})();
// initialize function called after we fetch the newest version
function init() {
// create the program and set version number
program.version(VERSION);
// turn on debug printing
program.option('--debug', 'Enables debug verbose printing.', () => Log.setMode(Log.DEBUG, true));
// only display errors
program.option('--errors', 'Only prints error messages.', () => Log.setMode(Log.INFO | Log.WARN | Log.DEBUG, false));
// display nothing
program.option('--silent', 'Prints nothing at all.', () => Log.setMode(Log.INFO | Log.WARN | Log.DEBUG | Log.ERROR, false));
// XNB unpack command
program
.command('unpack <input> [output]')
.description('Used to unpack XNB files.')
.action((input, output) => {
// process the unpack
processFiles(processUnpack, input, output, details);
});
// XNB pack Command
program
.command('pack <input> [output]')
.description('Used to pack XNB files.')
.action((input, output) => {
// process the pack
processFiles(processPack, input, output, details);
});
// default action
program.action(() => program.help());
// parse the input and run the commander program
program.parse(process.argv);
// show help if we didn't specify any valid input
if (!process.argv.slice(2).length)
program.help();
}
/**
* Display the results of the processing
*/
function details() {
// give a final analysis of the files
console.log(`${chalk.bold.green('Success')} ${success}`);
console.log(`${chalk.bold.red('Fail')} ${fail}`);
}
/**
* Takes input and processes input for unpacking.
* @param {String} input
* @param {String} output
*/
function processUnpack(input, output) {
// catch any exceptions to keep a batch of files moving
try {
// ensure that the input file has the right extension
if (path.extname(input).toLocaleLowerCase() != '.xnb')
return;
// create new instance of XNB
const xnb = new Xnb();
// load the XNB and get the object from it
const result = xnb.load(input);
// save the file
if (!exportFile(output, result)) {
Log.error(`File ${output} failed to save!`);
return fail++;
}
// log that the file was saved
Log.info(`Output file saved: ${output}`);
// increase success count
success++;
}
catch (ex) {
// log out the error
Log.error(`Filename: ${input}\n${ex.stack}\n`);
// increase fail count
fail++;
}
}
/**
* Process the pack of files to xnb
* @param {String} input
* @param {String} output
* @param {Function} done
*/
function processPack(input, output) {
try {
// ensure that the input file has the right extension
if (path.extname(input).toLocaleLowerCase() != '.json')
return;
Log.info(`Reading file "${input}" ...`);
// create instance of xnb
const xnb = new Xnb();
// resolve the imports
const json = resolveImports(input);
// convert the JSON to XNB
const buffer = xnb.convert(json);
// write the buffer to the output
fs.writeFileSync(output, buffer);
// log that the file was saved
Log.info(`Output file saved: ${output}`);
// increase success count
success++;
}
catch (ex) {
// log out the error
Log.error(`Filename: ${input}\n${ex.stack}\n`);
// increase fail count
fail++;
}
}
/**
* Used to walk a path with input/output for processing
* @param {Function} fn
* @param {String} input
* @param {String} output
* @param {Function} cb
*/
function processFiles(fn, input, output, cb) {
// if this isn't a directory then just run the function
if (!fs.statSync(input).isDirectory()) {
// get the extension from the original path name
const ext = path.extname(input);
// get the new extension
const newExt = (ext == '.xnb' ? '.json' : '.xnb');
// output is undefined or is a directory
if (output == undefined) {
output = path.join(path.dirname(input), path.basename(input, ext) + newExt);
}
// output is a directory
else if (fs.statSync(output).isDirectory())
output = path.join(output, path.basename(input, ext) + newExt);
// call the function
return fn(input, output);
}
// output is undefined
if (output == undefined)
output = input;
// get out grandpa's walker
const walker = walk.walk(input);
// when we encounter a file
walker.on('file', (root, stats, next) => {
// get the extension
const ext = path.extname(stats.name).toLocaleLowerCase();
// skip files that aren't JSON or XNB
if (ext != '.json' && ext != '.xnb')
return next();
// swap the input base directory with the base output directory for our target directory
const target = root.replace(input, output);
// get the source path
const inputFile = path.join(root, stats.name);
// get the target ext
const targetExt = ext == '.xnb' ? '.json' : '.xnb';
// form the output file path
const outputFile = path.join(target, path.basename(stats.name, ext) + targetExt);
// ensure the path to the output file exists
if (!fs.existsSync(path.dirname(inputFile)))
mkdirp.sync(outputFile);
// run the function
fn(inputFile, outputFile);
// next file
next();
});
// any errors that happen
walker.on('errors', (root, stats, next) => {
next();
});
// done walking the dog
walker.on('end', cb);
}