@badisi/xbw 1.0.8 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +36 -12
- package/bin/xbw +54 -0
- package/index.d.ts +11 -10
- package/index.js +92 -40
- package/package.json +12 -6
- package/prebuilds/darwin-x64/node.napi.node +0 -0
- package/prebuilds/linux-x64/node.napi.node +0 -0
- package/prebuilds/win32-ia32/node.napi.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,34 +1,58 @@
|
|
|
1
1
|
# @badisi/xbw
|
|
2
2
|
|
|
3
|
-
NodeJS module utility which provides helpful functions to read and/or verify
|
|
3
|
+
🎮 NodeJS module utility which provides helpful functions to read and/or verify xbox360 backup iso files.
|
|
4
4
|
|
|
5
5
|
[][npm]
|
|
6
|
-
[][npm-dl]
|
|
7
7
|
[][license]
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
## Installation
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
**xbw** is pre-built for `node >= 12` and `electron` on latest ****macos****, ****linux**** and ****windows****.
|
|
15
18
|
|
|
16
19
|
```sh
|
|
17
|
-
|
|
20
|
+
npm install -g @badisi/xbw
|
|
18
21
|
```
|
|
19
22
|
|
|
20
23
|
```sh
|
|
21
|
-
|
|
24
|
+
yarn global add @badisi/xbw
|
|
22
25
|
```
|
|
23
26
|
|
|
24
27
|
## Usage
|
|
25
28
|
|
|
26
|
-
```
|
|
27
|
-
|
|
29
|
+
```
|
|
30
|
+
$ xbw --help
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
$ xbw <command> <file|folder...> [--help] [options]
|
|
34
|
+
|
|
35
|
+
Global Commands:
|
|
36
|
+
verify .... Verify backup iso files integrity against abgx360
|
|
37
|
+
info ...... Extract information from backup iso files
|
|
38
|
+
|
|
39
|
+
Examples:
|
|
40
|
+
$ xbw info backup.iso
|
|
41
|
+
$ xbw info backup1.iso backup2.iso path/to/backups/folder/
|
|
42
|
+
$ xbw verify backup.iso --corrupt --af3 --patchgarbage --patchitanyway
|
|
43
|
+
$ xbw verify backup1.iso backup2.iso path/to/backups/folder
|
|
44
|
+
$ xbw verify backup.iso --html > output-file.html
|
|
28
45
|
```
|
|
29
46
|
|
|
47
|
+
## API
|
|
30
48
|
|
|
31
|
-
|
|
49
|
+
This package can also be installed locally and used as an API.
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
const { getIsosInfo, verifyWithAbgx360 } = require('@badisi/xbw');
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---------------------------------------
|
|
32
56
|
|
|
33
57
|
* [getIsosInfo](#getIsosInfo)
|
|
34
58
|
* [verifyWithAbgx360](#verifyWithAbgx360)
|
|
@@ -87,7 +111,7 @@ console.log(isosInfo);
|
|
|
87
111
|
|
|
88
112
|
---------------------------------------
|
|
89
113
|
|
|
90
|
-
<a name="
|
|
114
|
+
<a name="verifyWithAbgx360"></a>
|
|
91
115
|
|
|
92
116
|
### verifyWithAbgx360(isoPaths: string[], options?: AbgxOptions, onProgress?: (progress: string) => void): Promise<AbgxFile[]>
|
|
93
117
|
|
|
@@ -124,7 +148,7 @@ const abgxOptions = {
|
|
|
124
148
|
html: true
|
|
125
149
|
};
|
|
126
150
|
|
|
127
|
-
verifiedWithAbgx360(files, options,
|
|
151
|
+
verifiedWithAbgx360(files, options, console.log)
|
|
128
152
|
.then(results => console.log(results))
|
|
129
153
|
.catch(error => console.error(error));
|
|
130
154
|
// => [{
|
|
@@ -145,7 +169,7 @@ Credit
|
|
|
145
169
|
License
|
|
146
170
|
-------
|
|
147
171
|
|
|
148
|
-
Copyright © 2013-
|
|
172
|
+
Copyright © 2013-2022 [Badisi](https://github.com/Badisi)
|
|
149
173
|
|
|
150
174
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
151
175
|
a copy of this software and associated documentation files (the
|
package/bin/xbw
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
(async () => {
|
|
6
|
+
const { version } = require('../package.json');
|
|
7
|
+
const { verifyWithAbgx360, getIsosInfo } = require('../index.js');
|
|
8
|
+
const { blue, bold, cyan, dim, gray, green, italic, red, white } = require('@colors/colors/safe');
|
|
9
|
+
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
|
|
12
|
+
// Patch console.error so that it is printed in red color
|
|
13
|
+
const originalConsoleError = console.error;
|
|
14
|
+
console.error = (message) => originalConsoleError(red(message));
|
|
15
|
+
|
|
16
|
+
// Parse command line arguments
|
|
17
|
+
try {
|
|
18
|
+
switch (args.shift()) {
|
|
19
|
+
case 'verify':
|
|
20
|
+
await verifyWithAbgx360(args, undefined, console.log);
|
|
21
|
+
break;
|
|
22
|
+
case 'info':
|
|
23
|
+
console.log(cyan(JSON.stringify(getIsosInfo(args), null, 2)));
|
|
24
|
+
break;
|
|
25
|
+
default:
|
|
26
|
+
console.log(blue(bold(' _')));
|
|
27
|
+
console.log(blue(bold(' __ _| |____ __')));
|
|
28
|
+
console.log(blue(bold(' \\ \\/ / \'_ \\ \\ /\\ / /')));
|
|
29
|
+
console.log(blue(bold(' > <| |_) \\ V V / ')));
|
|
30
|
+
console.log(`${blue(bold(' /_/\\_\\_.__/ \\_/\\_/'))} ${green(italic(`v${version}`))}`);
|
|
31
|
+
console.log();
|
|
32
|
+
console.log(` ${white(bold('Usage:'))}`);
|
|
33
|
+
console.log();
|
|
34
|
+
console.log(` ${gray('$')} ${cyan('xbw <command> <file|folder...>')} ${cyan(dim('[--help] [options]'))}`);
|
|
35
|
+
console.log();
|
|
36
|
+
console.log(` ${white(bold('Global Commands:'))}`);
|
|
37
|
+
console.log();
|
|
38
|
+
console.log(` ${cyan('verify')} ${gray('.'.repeat(4))} ${white('Verify backup iso files integrity against abgx360')}`);
|
|
39
|
+
console.log(` ${cyan('info')} ${gray('.'.repeat(6))} ${white('Extract information from backup iso files')}`);
|
|
40
|
+
console.log();
|
|
41
|
+
console.log(` ${white(bold('Examples:'))}`);
|
|
42
|
+
console.log();
|
|
43
|
+
console.log(` ${gray('$')} ${cyan('xbw info backup.iso')}`);
|
|
44
|
+
console.log(` ${gray('$')} ${cyan('xbw info backup1.iso backup2.iso path/to/backups/folder/')}`);
|
|
45
|
+
console.log(` ${gray('$')} ${cyan('xbw verify backup.iso --corrupt --af3 --patchgarbage --patchitanyway')}`);
|
|
46
|
+
console.log(` ${gray('$')} ${cyan('xbw verify backup1.iso backup2.iso path/to/backups/folder')}`);
|
|
47
|
+
console.log(` ${gray('$')} ${cyan('xbw verify backup.iso --html > output-file.html')}`);
|
|
48
|
+
console.log();
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(error.message);
|
|
53
|
+
}
|
|
54
|
+
})();
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export function verifyWithAbgx360(isoPaths: string[], options?: AbgxOptions, onProgress?: (progress: string) => void): Promise<AbgxFile[]>;
|
|
2
1
|
export function getIsosInfo(isoPaths: string[]): IsoInfo[];
|
|
3
2
|
export enum Region {
|
|
4
3
|
REGION_FREE = 'REGION_FREE',
|
|
@@ -19,6 +18,17 @@ export enum Region {
|
|
|
19
18
|
NTSC_U = 'NTSC_U',
|
|
20
19
|
NTSCU_UNKNOW = 'NTSCU_UNKNOWN'
|
|
21
20
|
}
|
|
21
|
+
export interface IsoInfo {
|
|
22
|
+
file: string;
|
|
23
|
+
titleId: string;
|
|
24
|
+
mediaId: string;
|
|
25
|
+
discCount: number;
|
|
26
|
+
discNumber: number;
|
|
27
|
+
regions: Region[];
|
|
28
|
+
isValid: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function verifyWithAbgx360(isoPaths: string[], options?: AbgxOptions, onProgress?: (progress: string) => void): Promise<AbgxFile[]>;
|
|
22
32
|
export enum AbgxStatus {
|
|
23
33
|
VERIFIED = 0,
|
|
24
34
|
ERROR = -1,
|
|
@@ -124,12 +134,3 @@ export interface AbgxOptions {
|
|
|
124
134
|
lang: number;
|
|
125
135
|
speed: number;
|
|
126
136
|
}
|
|
127
|
-
export interface IsoInfo {
|
|
128
|
-
file: string;
|
|
129
|
-
titleId: string;
|
|
130
|
-
mediaId: string;
|
|
131
|
-
discCount: number;
|
|
132
|
-
discNumber: number;
|
|
133
|
-
regions: Region[];
|
|
134
|
-
isValid: boolean;
|
|
135
|
-
}
|
package/index.js
CHANGED
|
@@ -1,60 +1,112 @@
|
|
|
1
|
-
|
|
1
|
+
// @ts-check
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
.keys(options)
|
|
7
|
-
.map((key) => {
|
|
8
|
-
if (typeof options[key] === 'string') {
|
|
9
|
-
return `--${key} ${options[key]}`;
|
|
10
|
-
} else if (typeof options[key] === 'number') {
|
|
11
|
-
return `--${key} ${String(options[key])}`;
|
|
12
|
-
}
|
|
13
|
-
return `--${key}`;
|
|
14
|
-
})
|
|
15
|
-
.join(' ')
|
|
16
|
-
.split(' ');
|
|
3
|
+
const xbw = require('node-gyp-build')(__dirname);
|
|
4
|
+
const { lstatSync, readdirSync, existsSync } = require('fs');
|
|
5
|
+
const { join } = require('path');
|
|
17
6
|
|
|
7
|
+
/** @param {string[]} args */
|
|
8
|
+
const getIsoFilesFromArgs = (args = []) => {
|
|
9
|
+
const files = [];
|
|
10
|
+
args
|
|
11
|
+
.filter(arg => !arg.startsWith('-'))
|
|
12
|
+
.forEach(arg => {
|
|
13
|
+
if (existsSync(arg) && lstatSync(arg).isDirectory()) {
|
|
14
|
+
files.push(...readdirSync(arg).reduce((files, itemName) => {
|
|
15
|
+
const itemPath = join(arg, itemName);
|
|
16
|
+
if (lstatSync(itemPath).isDirectory()) {
|
|
17
|
+
return [...files, ...getIsoFilesFromArgs([itemPath])];
|
|
18
|
+
}
|
|
19
|
+
return /** @type {string[]} */(itemName.endsWith('.iso') ? [...files, itemPath] : files);
|
|
20
|
+
}, /** @type {string[]} */([])));
|
|
21
|
+
} else {
|
|
22
|
+
files.push(arg);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return files;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** @type {import('./index').verifyWithAbgx360} */
|
|
29
|
+
const verifyWithAbgx360 = (args = [], options, onProgress) => {
|
|
30
|
+
if (Array.isArray(args)) {
|
|
31
|
+
const isoPaths = args.filter(arg => !arg.startsWith('-'));
|
|
32
|
+
const files = getIsoFilesFromArgs(isoPaths);
|
|
33
|
+
if (isoPaths.length && !files.length) {
|
|
34
|
+
throw new Error('Error: xbw.verifyWithAbgx360: no ISO files were found');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let abgxOptions = ['--help'];
|
|
38
|
+
if (files.length) {
|
|
39
|
+
abgxOptions = args.filter(arg => arg.startsWith('-'));
|
|
40
|
+
if (options) {
|
|
41
|
+
/**
|
|
42
|
+
* TypeScript is not smart enough to figure out what filter() does.
|
|
43
|
+
* So we need to cast the result as being string[] and not (string | undefined)[].
|
|
44
|
+
*/
|
|
45
|
+
const items = /** @type {string[]} */(Object
|
|
46
|
+
.keys(options)
|
|
47
|
+
.map(key => {
|
|
48
|
+
if (typeof options[key] === 'string') {
|
|
49
|
+
return `--${key} ${options[key]}`;
|
|
50
|
+
} else if (typeof options[key] === 'number') {
|
|
51
|
+
return `--${key} ${String(options[key])}`;
|
|
52
|
+
} else if (options[key] === false) {
|
|
53
|
+
return undefined; // no-op
|
|
54
|
+
}
|
|
55
|
+
return `--${key}`;
|
|
56
|
+
})
|
|
57
|
+
.filter(option => option)); // filter out the no-op
|
|
58
|
+
abgxOptions.push(...items);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
18
61
|
return new Promise((resolve, reject) => {
|
|
19
62
|
xbw.verifyWithAbgx360(
|
|
20
|
-
abgxOptions.concat(
|
|
21
|
-
(results,
|
|
63
|
+
abgxOptions.concat(files),
|
|
64
|
+
(/** @type {any} */ error, /** @type {any[]} */ results, /** @type {string} */ progress) => {
|
|
65
|
+
if (progress && onProgress) {
|
|
66
|
+
if (progress.includes('<loader>')) {
|
|
67
|
+
progress = progress.replace(' ', ' ');
|
|
68
|
+
}
|
|
69
|
+
onProgress(progress);
|
|
70
|
+
}
|
|
22
71
|
if (error) {
|
|
23
72
|
return reject(error);
|
|
24
73
|
}
|
|
25
|
-
|
|
26
|
-
return
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
(progress) => {
|
|
30
|
-
if (onProgress) {
|
|
31
|
-
onProgress(progress);
|
|
74
|
+
if (results) {
|
|
75
|
+
return resolve(files.map((file, index) => {
|
|
76
|
+
return { file, status: results?.[index] ?? -1 };
|
|
77
|
+
}));
|
|
32
78
|
}
|
|
33
79
|
}
|
|
34
80
|
);
|
|
35
81
|
});
|
|
36
82
|
} else {
|
|
37
|
-
throw 'Error: verifyWithAbgx360: `
|
|
83
|
+
throw new Error('Error: xbw.verifyWithAbgx360: `args` parameter should be of type Array');
|
|
38
84
|
}
|
|
39
|
-
}
|
|
85
|
+
};
|
|
40
86
|
|
|
41
|
-
|
|
87
|
+
/** @type {import('./index').getIsosInfo} */
|
|
88
|
+
const getIsosInfo = (isoPaths = []) => {
|
|
42
89
|
if (Array.isArray(isoPaths)) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
90
|
+
const files = getIsoFilesFromArgs(isoPaths);
|
|
91
|
+
if (files.length) {
|
|
92
|
+
return files.map(file => {
|
|
93
|
+
try {
|
|
94
|
+
return {
|
|
95
|
+
...xbw.getIsoInfo(file),
|
|
96
|
+
isValid: true
|
|
97
|
+
};
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(`Error: xbw.getIsosInfo: ${error.message}\n for file: ${file}`);
|
|
100
|
+
return { file, isValid: false };
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
throw new Error('Error: xbw.getIsosInfo: no ISO files were found');
|
|
105
|
+
}
|
|
54
106
|
} else {
|
|
55
|
-
throw 'Error: getIsosInfo: `
|
|
107
|
+
throw new Error('Error: xbw.getIsosInfo: `isoPaths` parameter should be of type Array');
|
|
56
108
|
}
|
|
57
|
-
}
|
|
109
|
+
};
|
|
58
110
|
|
|
59
111
|
module.exports = {
|
|
60
112
|
verifyWithAbgx360,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@badisi/xbw",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Utility to read and/or verify
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Utility to read and/or verify xbox360 backup iso files",
|
|
5
5
|
"homepage": "https://github.com/Badisi/xbw",
|
|
6
6
|
"license": "SEE LICENSE IN file",
|
|
7
7
|
"author": {
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
},
|
|
10
10
|
"main": "./index.js",
|
|
11
11
|
"types": "./index.d.ts",
|
|
12
|
+
"bin": {
|
|
13
|
+
"xbw": "./bin/xbw"
|
|
14
|
+
},
|
|
12
15
|
"repository": {
|
|
13
16
|
"type": "git",
|
|
14
17
|
"url": "https://github.com/Badisi/xbw.git"
|
|
@@ -19,17 +22,20 @@
|
|
|
19
22
|
"xkey",
|
|
20
23
|
"xbox360",
|
|
21
24
|
"abgx",
|
|
25
|
+
"abgx360",
|
|
22
26
|
"nodejs",
|
|
27
|
+
"iso",
|
|
23
28
|
"v8"
|
|
24
29
|
],
|
|
25
30
|
"engines": {
|
|
26
|
-
"node": ">=
|
|
31
|
+
"node": ">= 12"
|
|
27
32
|
},
|
|
28
33
|
"scripts": {
|
|
29
|
-
"install": "
|
|
34
|
+
"install": "node-gyp-build"
|
|
30
35
|
},
|
|
31
36
|
"dependencies": {
|
|
32
|
-
"
|
|
33
|
-
"
|
|
37
|
+
"@colors/colors": "^1.5.0",
|
|
38
|
+
"node-addon-api": "^5.0.0",
|
|
39
|
+
"node-gyp-build": "^4.5.0"
|
|
34
40
|
}
|
|
35
41
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|