adofai 2.13.0 → 2.13.2

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/README.md CHANGED
@@ -1,112 +1,112 @@
1
- # ADOFAI
2
-
3
- A Javascript library for ADOFAI levels.
4
-
5
- ## Usage
6
- Preview / Edit the `.adofai` file.
7
-
8
- Re_ADOJAS(A Level Player of ADOFAI) uses `adofai` to parse ADOFAI Level file.
9
-
10
- ## Installation
11
-
12
- ```bash
13
- npm install adofai
14
- # or
15
- yarn add adofai
16
- # or
17
- pnpm install adofai
18
- ```
19
-
20
- if you want to display highlight of adofai file, you can use `Rhythm Game Syntax Highlighter` vscode extension.
21
-
22
- ## Got Started
23
-
24
- ### Import
25
-
26
- For Commonjs:
27
- ```ts
28
- const adofai = require('adofai');
29
- ```
30
-
31
- For ES6 Modules:
32
- ```ts
33
- import * as adofai from 'adofai';
34
- ```
35
-
36
- ### Create a Level
37
-
38
- ```ts
39
- const file = new adofai.Level(adofaiFileContent);
40
-
41
- //or
42
-
43
- const parser = new adofai.Parsers.StringParser();
44
- const file = new adofai.Level(adofaiFileContent,parser);
45
-
46
- //The advantage of the latter over the former is that it pre-initializes the Parser, avoiding multiple instantiations.
47
- ```
48
-
49
- Format:
50
- ```ts
51
- class Level {
52
- constructor(opt: string | LevelOptions, provider?: ParseProvider)
53
- }
54
-
55
- ```
56
- Available ParseProviders:
57
- `StringParser` `ArrayBufferParser` `BufferParser`
58
-
59
-
60
- Usually,only `StringParser` is needed.
61
- but you can use `BufferParser` to parse ADOFAI files in Node environment.
62
-
63
- On browser, you can also use `ArrayBuffer` to parse ADOFAI files.
64
- (`BufferParser` is not available in browser,but you can use browserify `Buffer` to polyfill)
65
-
66
- ### Load Level
67
- ```ts
68
- file.on('load'() => {
69
- //logic...
70
- })
71
- file.load()
72
- ```
73
-
74
- or you can use `then()`
75
- ```ts
76
- file.load().then(() => {
77
-
78
- })
79
- ```
80
-
81
- ### Export Level
82
- ```ts
83
- type FileType = 'string'|'object'
84
-
85
- file.export(type: FileType = 'string',indent?:number,useAdofaiStyle:boolean = true)
86
- ```
87
-
88
- method `export()` returns a Object or String.
89
-
90
- Object: return ADOFAI Object.
91
- String: return ADOFAI String.
92
-
93
- ```ts
94
- import fs from 'fs'
95
- type FileType = 'string'|'object'
96
-
97
- const content = file.export('string',null,true);
98
- fs.writeFileSync('output.adofai',content)
99
- ```
100
-
101
-
102
- ## Data Operation
103
-
104
- See interfaces to see all data.
105
-
106
- ```ts
107
- //Get AngleDatas:
108
- const angleDatas = file.angleData;
109
-
110
-
111
-
1
+ # ADOFAI
2
+
3
+ A Javascript library for ADOFAI levels.
4
+
5
+ ## Usage
6
+ Preview / Edit the `.adofai` file.
7
+
8
+ Re_ADOJAS(A Level Player of ADOFAI) uses `adofai` to parse ADOFAI Level file.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install adofai
14
+ # or
15
+ yarn add adofai
16
+ # or
17
+ pnpm install adofai
18
+ ```
19
+
20
+ if you want to display highlight of adofai file, you can use `Rhythm Game Syntax Highlighter` vscode extension.
21
+
22
+ ## Got Started
23
+
24
+ ### Import
25
+
26
+ For Commonjs:
27
+ ```ts
28
+ const adofai = require('adofai');
29
+ ```
30
+
31
+ For ES6 Modules:
32
+ ```ts
33
+ import * as adofai from 'adofai';
34
+ ```
35
+
36
+ ### Create a Level
37
+
38
+ ```ts
39
+ const file = new adofai.Level(adofaiFileContent);
40
+
41
+ //or
42
+
43
+ const parser = new adofai.Parsers.StringParser();
44
+ const file = new adofai.Level(adofaiFileContent,parser);
45
+
46
+ //The advantage of the latter over the former is that it pre-initializes the Parser, avoiding multiple instantiations.
47
+ ```
48
+
49
+ Format:
50
+ ```ts
51
+ class Level {
52
+ constructor(opt: string | LevelOptions, provider?: ParseProvider)
53
+ }
54
+
55
+ ```
56
+ Available ParseProviders:
57
+ `StringParser` `ArrayBufferParser` `BufferParser`
58
+
59
+
60
+ Usually,only `StringParser` is needed.
61
+ but you can use `BufferParser` to parse ADOFAI files in Node environment.
62
+
63
+ On browser, you can also use `ArrayBuffer` to parse ADOFAI files.
64
+ (`BufferParser` is not available in browser,but you can use browserify `Buffer` to polyfill)
65
+
66
+ ### Load Level
67
+ ```ts
68
+ file.on('load'() => {
69
+ //logic...
70
+ })
71
+ file.load()
72
+ ```
73
+
74
+ or you can use `then()`
75
+ ```ts
76
+ file.load().then(() => {
77
+
78
+ })
79
+ ```
80
+
81
+ ### Export Level
82
+ ```ts
83
+ type FileType = 'string'|'object'
84
+
85
+ file.export(type: FileType = 'string',indent?:number,useAdofaiStyle:boolean = true)
86
+ ```
87
+
88
+ method `export()` returns a Object or String.
89
+
90
+ Object: return ADOFAI Object.
91
+ String: return ADOFAI String.
92
+
93
+ ```ts
94
+ import fs from 'fs'
95
+ type FileType = 'string'|'object'
96
+
97
+ const content = file.export('string',null,true);
98
+ fs.writeFileSync('output.adofai',content)
99
+ ```
100
+
101
+
102
+ ## Data Operation
103
+
104
+ See interfaces to see all data.
105
+
106
+ ```ts
107
+ //Get AngleDatas:
108
+ const angleDatas = file.angleData;
109
+
110
+
111
+
112
112
  ```
@@ -0,0 +1,32 @@
1
+ interface Tile {
2
+ addDecorations?: any[];
3
+ actions?: Action[];
4
+ [key: string]: any;
5
+ }
6
+ interface Action {
7
+ eventType: string;
8
+ [key: string]: any;
9
+ }
10
+ /**
11
+ * @param {Array} tiles ADOFAI Tiles from ADOFAI.Level
12
+ * @returns {Array} new Tiles
13
+ */
14
+ declare function clearDecorations(tiles: Tile[]): Tile[];
15
+ /**
16
+ * @param {Array} eventTypes Eventlist to remove
17
+ * @param {Array} tiles ADOFAI Tiles from ADOFAI.Level
18
+ * @returns {Array} new Tiles
19
+ */
20
+ declare function clearEvents(eventTypes: string[], tiles: Tile[]): Tile[];
21
+ /**
22
+ * @param {Array} eventTypes Eventlist to keep
23
+ * @param {Array} tiles ADOFAI Tiles from ADOFAI.Level
24
+ * @returns {Array} new Tiles
25
+ */
26
+ declare function keepEvents(eventTypes: string[], tiles: Tile[]): Tile[];
27
+ declare const effectProcessor: {
28
+ clearDecorations: typeof clearDecorations;
29
+ clearEvents: typeof clearEvents;
30
+ keepEvents: typeof keepEvents;
31
+ };
32
+ export default effectProcessor;
@@ -0,0 +1,54 @@
1
+ import * as presets from './presets';
2
+ /**
3
+ * @param {Array} tiles ADOFAI Tiles from ADOFAI.Level
4
+ * @returns {Array} new Tiles
5
+ */
6
+ function clearDecorations(tiles) {
7
+ if (!Array.isArray(tiles)) {
8
+ throw new Error('Arguments are not supported.');
9
+ }
10
+ return tiles.map(tile => {
11
+ const newTile = Object.assign({}, tile);
12
+ if (newTile.hasOwnProperty('addDecorations')) {
13
+ newTile.addDecorations = [];
14
+ }
15
+ if (Array.isArray(newTile.actions)) {
16
+ newTile.actions = newTile.actions.filter(action => !presets.preset_inner_no_deco.events.includes(action.eventType));
17
+ }
18
+ return newTile;
19
+ });
20
+ }
21
+ /**
22
+ * @param {Array} eventTypes Eventlist to remove
23
+ * @param {Array} tiles ADOFAI Tiles from ADOFAI.Level
24
+ * @returns {Array} new Tiles
25
+ */
26
+ function clearEvents(eventTypes, tiles) {
27
+ return tiles.map(tile => {
28
+ const newTile = Object.assign({}, tile);
29
+ if (Array.isArray(newTile.actions)) {
30
+ newTile.actions = newTile.actions.filter(action => !eventTypes.includes(action.eventType));
31
+ }
32
+ return newTile;
33
+ });
34
+ }
35
+ /**
36
+ * @param {Array} eventTypes Eventlist to keep
37
+ * @param {Array} tiles ADOFAI Tiles from ADOFAI.Level
38
+ * @returns {Array} new Tiles
39
+ */
40
+ function keepEvents(eventTypes, tiles) {
41
+ return tiles.map(tile => {
42
+ const newTile = Object.assign({}, tile);
43
+ if (Array.isArray(newTile.actions)) {
44
+ newTile.actions = newTile.actions.filter(action => eventTypes.includes(action.eventType));
45
+ }
46
+ return newTile;
47
+ });
48
+ }
49
+ const effectProcessor = {
50
+ clearDecorations,
51
+ clearEvents,
52
+ keepEvents
53
+ };
54
+ export default effectProcessor;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @param {object} obj - JSON Object
3
+ * @param {number} indent - No usage
4
+ * @param {boolean} isRoot - Is JSON the Root?
5
+ * @returns ADOFAI File Content or Object
6
+ */
7
+ declare function exportAsADOFAI(obj: any, indent?: number, isRoot?: boolean): string;
8
+ export default exportAsADOFAI;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @param {object} obj - JSON Object
3
+ * @param {number} indent - No usage
4
+ * @param {boolean} isRoot - Is JSON the Root?
5
+ * @returns ADOFAI File Content or Object
6
+ */
7
+ function exportAsADOFAI(obj, indent = 0, isRoot = false) {
8
+ if (typeof obj !== 'object' || obj === null) {
9
+ return JSON.stringify(obj);
10
+ }
11
+ if (Array.isArray(obj)) {
12
+ const allPrimitives = obj.every(item => typeof item !== 'object' || item === null);
13
+ if (allPrimitives) {
14
+ return '[' + obj.map(item => exportAsADOFAI(item)).join(',') + ']';
15
+ }
16
+ const spaces = ' '.repeat(indent);
17
+ const arrayItems = obj.map(item => spaces + ' ' + formatAsSingleLine(item)).join(',\n');
18
+ return '[\n' + arrayItems + '\n' + spaces + ']';
19
+ }
20
+ const spaces = ' '.repeat(indent);
21
+ const keys = Object.keys(obj);
22
+ if (isRoot) {
23
+ const objectItems = keys.map(key => ' ' + JSON.stringify(key) + ': ' + exportAsADOFAI(obj[key], 2)).join(',\n');
24
+ return '{\n' + objectItems + '\n}';
25
+ }
26
+ const objectItems = keys.map(key => spaces + ' ' + JSON.stringify(key) + ': ' + exportAsADOFAI(obj[key], indent + 2)).join(',\n');
27
+ return '{\n' + objectItems + '\n' + spaces + '}';
28
+ }
29
+ /**
30
+ * @param {Array} obj Eventlist to keep
31
+ * @returns {string} JSON formated as singleline
32
+ */
33
+ function formatAsSingleLine(obj) {
34
+ if (typeof obj !== 'object' || obj === null) {
35
+ return exportAsADOFAI(obj);
36
+ }
37
+ if (Array.isArray(obj)) {
38
+ return '[' + obj.map(formatAsSingleLine).join(',') + ']';
39
+ }
40
+ const keys = Object.keys(obj);
41
+ const entries = keys.map(key => JSON.stringify(key) + ': ' + formatAsSingleLine(obj[key])).join(', ');
42
+ return '{' + entries + '}';
43
+ }
44
+ export default exportAsADOFAI;
@@ -0,0 +1,9 @@
1
+ import Parser from "./Parser";
2
+ export declare class ArrayBufferParser extends Parser<ArrayBuffer | string, any> {
3
+ parse(input: ArrayBuffer | string): any;
4
+ stringify(obj: any): string;
5
+ }
6
+ export declare function stripBOM(buffer: ArrayBuffer): ArrayBuffer;
7
+ export declare function normalizeJsonArrayBuffer(buffer: ArrayBuffer): ArrayBuffer;
8
+ export declare function decodeStringFromUTF8BOM(buffer: ArrayBuffer): string;
9
+ export default ArrayBufferParser;
@@ -0,0 +1,98 @@
1
+ import Parser from "./Parser";
2
+ import StringParser from "./StringParser";
3
+ const BOM = new Uint8Array([0xef, 0xbb, 0xbf]);
4
+ const COMMA = new Uint8Array([44]); // ASCII for ','
5
+ export class ArrayBufferParser extends Parser {
6
+ parse(input) {
7
+ if (typeof input === "string") {
8
+ return StringParser.prototype.parse.call(StringParser.prototype, input);
9
+ }
10
+ else {
11
+ return StringParser.prototype.parse.call(StringParser.prototype, decodeStringFromUTF8BOM(normalizeJsonArrayBuffer(stripBOM(input))));
12
+ }
13
+ }
14
+ stringify(obj) {
15
+ return JSON.stringify(obj);
16
+ }
17
+ }
18
+ export function stripBOM(buffer) {
19
+ const view = new Uint8Array(buffer);
20
+ if (view.length >= 3 && view[0] === BOM[0] && view[1] === BOM[1] && view[2] === BOM[2]) {
21
+ return buffer.slice(3);
22
+ }
23
+ return buffer;
24
+ }
25
+ export function normalizeJsonArrayBuffer(buffer) {
26
+ const view = new Uint8Array(buffer);
27
+ let builder = [];
28
+ let last = "other";
29
+ let from = 0;
30
+ for (let i = 0; i < view.length; i++) {
31
+ const charCode = view[i];
32
+ if (last == "escape") {
33
+ last = "string";
34
+ }
35
+ else {
36
+ switch (charCode) {
37
+ case 34: // "
38
+ switch (last) {
39
+ case "string":
40
+ last = "other";
41
+ break;
42
+ case "comma":
43
+ builder.push(COMMA);
44
+ default:
45
+ last = "string";
46
+ break;
47
+ }
48
+ break;
49
+ case 92: // \
50
+ if (last === "string")
51
+ last = "escape";
52
+ break;
53
+ case 44: // ,
54
+ builder.push(view.subarray(from, i));
55
+ from = i + 1;
56
+ if (last === "other")
57
+ last = "comma";
58
+ break;
59
+ case 93: // ]
60
+ case 125: // }
61
+ if (last === "comma")
62
+ last = "other";
63
+ break;
64
+ case 9: // \t
65
+ case 10: // \n
66
+ case 11: // \v
67
+ case 12: // \f
68
+ case 13: // \r
69
+ case 32: // space
70
+ break;
71
+ default:
72
+ if (last === "comma") {
73
+ builder.push(COMMA);
74
+ last = "other";
75
+ }
76
+ break;
77
+ }
78
+ }
79
+ }
80
+ builder.push(view.subarray(from));
81
+ let totalLength = 0;
82
+ for (const arr of builder) {
83
+ totalLength += arr.length;
84
+ }
85
+ const result = new Uint8Array(totalLength);
86
+ let offset = 0;
87
+ for (const arr of builder) {
88
+ result.set(arr, offset);
89
+ offset += arr.length;
90
+ }
91
+ return result.buffer;
92
+ }
93
+ export function decodeStringFromUTF8BOM(buffer) {
94
+ const strippedBuffer = stripBOM(buffer);
95
+ const decoder = new TextDecoder('utf-8');
96
+ return decoder.decode(strippedBuffer);
97
+ }
98
+ export default ArrayBufferParser;
@@ -0,0 +1,9 @@
1
+ import Parser from "./Parser";
2
+ export declare class BufferParser extends Parser<Buffer | string, any> {
3
+ parse(input: Buffer | string): any;
4
+ stringify(obj: any): string;
5
+ }
6
+ export declare function stripBOM(buffer: Buffer): Buffer;
7
+ export declare function normalizeJsonBuffer(text: Buffer): Buffer;
8
+ export declare function decodeStringFromUTF8BOM(buffer: Buffer): string;
9
+ export default BufferParser;
@@ -0,0 +1,92 @@
1
+ import Parser from "./Parser";
2
+ import StringParser from "./StringParser";
3
+ let BOM;
4
+ let COMMA;
5
+ try {
6
+ BOM = Buffer.of(0xef, 0xbb, 0xbf);
7
+ COMMA = Buffer.from(",");
8
+ }
9
+ catch (e) {
10
+ console.warn('Buffer is not available in current environment, try to use ArrayBufferParser');
11
+ BOM = { equals: () => false, subarray: () => null };
12
+ COMMA = { equals: () => false, subarray: () => null };
13
+ }
14
+ export class BufferParser extends Parser {
15
+ parse(input) {
16
+ if (typeof input === "string") {
17
+ return StringParser.prototype.parse.call(StringParser.prototype, input);
18
+ }
19
+ else {
20
+ return StringParser.prototype.parse.call(StringParser.prototype, decodeStringFromUTF8BOM(normalizeJsonBuffer(stripBOM(input))));
21
+ }
22
+ }
23
+ stringify(obj) {
24
+ return JSON.stringify(obj);
25
+ }
26
+ }
27
+ export function stripBOM(buffer) {
28
+ if (buffer.length >= 3 && BOM.equals(buffer.subarray(0, 3))) {
29
+ return buffer.subarray(3);
30
+ }
31
+ return buffer;
32
+ }
33
+ export function normalizeJsonBuffer(text) {
34
+ let builder = [];
35
+ let last = "other";
36
+ let from = 0;
37
+ text.forEach((charCode, i) => {
38
+ if (last == "escape") {
39
+ last = "string";
40
+ }
41
+ else {
42
+ switch (charCode) {
43
+ case 34:
44
+ switch (last) {
45
+ case "string":
46
+ last = "other";
47
+ break;
48
+ case "comma":
49
+ builder.push(COMMA);
50
+ default:
51
+ last = "string";
52
+ break;
53
+ }
54
+ break;
55
+ case 92:
56
+ if (last === "string")
57
+ last = "escape";
58
+ break;
59
+ case 44:
60
+ builder.push(text.subarray(from, i));
61
+ from = i + 1;
62
+ if (last === "other")
63
+ last = "comma";
64
+ break;
65
+ case 93:
66
+ case 125:
67
+ if (last === "comma")
68
+ last = "other";
69
+ break;
70
+ case 9:
71
+ case 10:
72
+ case 11:
73
+ case 12:
74
+ case 13:
75
+ case 32:
76
+ break;
77
+ default:
78
+ if (last === "comma") {
79
+ builder.push(COMMA);
80
+ last = "other";
81
+ }
82
+ break;
83
+ }
84
+ }
85
+ });
86
+ builder.push(text.subarray(from));
87
+ return Buffer.concat(builder);
88
+ }
89
+ export function decodeStringFromUTF8BOM(buffer) {
90
+ return stripBOM(buffer).toString("utf-8");
91
+ }
92
+ export default BufferParser;
@@ -0,0 +1,23 @@
1
+ import Parser from "./Parser";
2
+ /**
3
+ * 用于解析和序列化 ArrayBuffer 数据的解析器
4
+ */
5
+ declare class BufferParserX extends Parser<ArrayBuffer, any> {
6
+ /**
7
+ * 解析 ArrayBuffer 为对象
8
+ * @param buffer 输入的 ArrayBuffer
9
+ * @param reviver 可选的 reviver 函数
10
+ */
11
+ parse(buffer: ArrayBuffer, reviver?: (key: string, value: any) => any): any;
12
+ /**
13
+ * 序列化对象为 ArrayBuffer
14
+ * @param value 要序列化的对象
15
+ * @param replacer 可选的 replacer 函数
16
+ * @param space 可选的缩进
17
+ */
18
+ stringify(value: any, replacer?: (key: string, value: any) => any, space?: string | number): ArrayBuffer;
19
+ private static _decodeUtf8;
20
+ private static _encodeUtf8;
21
+ private static _applyReviver;
22
+ }
23
+ export default BufferParserX;
@@ -0,0 +1,77 @@
1
+ import Parser from "./Parser";
2
+ import { ParserX, Serializer } from "./StringParser";
3
+ /**
4
+ * 用于解析和序列化 ArrayBuffer 数据的解析器
5
+ */
6
+ class BufferParserX extends Parser {
7
+ /**
8
+ * 解析 ArrayBuffer 为对象
9
+ * @param buffer 输入的 ArrayBuffer
10
+ * @param reviver 可选的 reviver 函数
11
+ */
12
+ parse(buffer, reviver) {
13
+ if (!buffer)
14
+ return null;
15
+ const text = BufferParserX._decodeUtf8(buffer);
16
+ const result = new StringParserX(text).parseValue();
17
+ if (typeof reviver === "function") {
18
+ return BufferParserX._applyReviver("", result, reviver);
19
+ }
20
+ console.log(result);
21
+ return result;
22
+ }
23
+ /**
24
+ * 序列化对象为 ArrayBuffer
25
+ * @param value 要序列化的对象
26
+ * @param replacer 可选的 replacer 函数
27
+ * @param space 可选的缩进
28
+ */
29
+ stringify(value, replacer, space) {
30
+ const serializer = new SerializerX(replacer, space);
31
+ const str = serializer.serialize(value);
32
+ return BufferParserX._encodeUtf8(str);
33
+ }
34
+ static _decodeUtf8(buffer) {
35
+ return new TextDecoder().decode(new Uint8Array(buffer));
36
+ }
37
+ static _encodeUtf8(str) {
38
+ return new TextEncoder().encode(str).buffer;
39
+ }
40
+ static _applyReviver(key, value, reviver) {
41
+ if (value && typeof value === "object") {
42
+ if (Array.isArray(value)) {
43
+ for (let i = 0; i < value.length; i++) {
44
+ value[i] = BufferParserX._applyReviver(i.toString(), value[i], reviver);
45
+ }
46
+ }
47
+ else {
48
+ for (const prop in value) {
49
+ if (Object.prototype.hasOwnProperty.call(value, prop)) {
50
+ value[prop] = BufferParserX._applyReviver(prop, value[prop], reviver);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ return reviver(key, value);
56
+ }
57
+ }
58
+ // StringParserX 仅用于内部解析字符串,逻辑与 StringParser 类似,但只暴露 parseValue 方法
59
+ class StringParserX {
60
+ constructor(text) {
61
+ // @ts-ignore
62
+ this.parser = new ParserX(text);
63
+ }
64
+ parseValue() {
65
+ return this.parser.parseValue();
66
+ }
67
+ }
68
+ class SerializerX {
69
+ constructor(replacer, space) {
70
+ // @ts-ignore
71
+ this.serializer = new Serializer(replacer, space);
72
+ }
73
+ serialize(obj) {
74
+ return this.serializer.serialize(obj);
75
+ }
76
+ }
77
+ export default BufferParserX;
@@ -0,0 +1,9 @@
1
+ import StringParser from './StringParser';
2
+ declare abstract class FileParser<TOutput = any> {
3
+ protected fileContent: string;
4
+ protected parser: StringParser;
5
+ preload(filePath: string): Promise<void>;
6
+ parse(): TOutput;
7
+ stringify(obj: TOutput): string;
8
+ }
9
+ export default FileParser;