@karaplay/file-coder 1.1.0

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.
Files changed (75) hide show
  1. package/BROWSER_API.md +318 -0
  2. package/README.md +216 -0
  3. package/REFACTORING_SUMMARY.txt +250 -0
  4. package/WORKFLOW_SUMMARY.txt +78 -0
  5. package/bin/ncntokar-cli.js +39 -0
  6. package/dist/client.d.ts +26 -0
  7. package/dist/client.js +74 -0
  8. package/dist/emk/client-decoder.d.ts +22 -0
  9. package/dist/emk/client-decoder.js +133 -0
  10. package/dist/emk/server-decode.d.ts +21 -0
  11. package/dist/emk/server-decode.js +123 -0
  12. package/dist/emk-to-kar.browser.d.ts +51 -0
  13. package/dist/emk-to-kar.browser.js +139 -0
  14. package/dist/emk-to-kar.d.ts +53 -0
  15. package/dist/emk-to-kar.js +210 -0
  16. package/dist/index.d.ts +12 -0
  17. package/dist/index.js +64 -0
  18. package/dist/kar-reader.browser.d.ts +46 -0
  19. package/dist/kar-reader.browser.js +209 -0
  20. package/dist/kar-reader.d.ts +42 -0
  21. package/dist/kar-reader.js +197 -0
  22. package/dist/ncntokar.browser.d.ts +99 -0
  23. package/dist/ncntokar.browser.js +296 -0
  24. package/dist/ncntokar.d.ts +88 -0
  25. package/dist/ncntokar.js +340 -0
  26. package/examples/NextJSComponent.tsx +175 -0
  27. package/libs/emk/client-decoder.ts +142 -0
  28. package/libs/emk/server-decode.ts +133 -0
  29. package/libs/ncntokar.js +256 -0
  30. package/package.json +79 -0
  31. package/songs/.gitkeep +3 -0
  32. package/songs/cur/BPL3457.cur +0 -0
  33. package/songs/cur/Z2510006.cur +0 -0
  34. package/songs/cur/Z2510008.cur +0 -0
  35. package/songs/cur/Z2510136.cur +0 -0
  36. package/songs/cur/Z2510137.cur +0 -0
  37. package/songs/cur/Z2510138.cur +0 -0
  38. package/songs/cur/Z2510139.cur +0 -0
  39. package/songs/cur/Z2510140.cur +0 -0
  40. package/songs/cur/Z2510141.cur +0 -0
  41. package/songs/cur/song.cur +0 -0
  42. package/songs/emk/Z2510001.emk +0 -0
  43. package/songs/emk/Z2510002.emk +0 -0
  44. package/songs/emk/Z2510003.emk +0 -0
  45. package/songs/emk/Z2510004.emk +0 -0
  46. package/songs/emk/Z2510005.emk +0 -0
  47. package/songs/emk/Z2510006.emk +0 -0
  48. package/songs/kar/bpl3457.kar +0 -0
  49. package/songs/kar/z2510006.kar +0 -0
  50. package/songs/kar/z2510008.kar +0 -0
  51. package/songs/kar/z2510136.kar +0 -0
  52. package/songs/kar/z2510137.kar +0 -0
  53. package/songs/kar/z2510138.kar +0 -0
  54. package/songs/kar/z2510139.kar +0 -0
  55. package/songs/kar/z2510140.kar +0 -0
  56. package/songs/kar/z2510141.kar +0 -0
  57. package/songs/lyr/BPL3457.lyr +57 -0
  58. package/songs/lyr/Z2510006.lyr +53 -0
  59. package/songs/lyr/Z2510008.lyr +57 -0
  60. package/songs/lyr/Z2510136.lyr +54 -0
  61. package/songs/lyr/Z2510137.lyr +66 -0
  62. package/songs/lyr/Z2510138.lyr +62 -0
  63. package/songs/lyr/Z2510139.lyr +60 -0
  64. package/songs/lyr/Z2510140.lyr +41 -0
  65. package/songs/lyr/Z2510141.lyr +48 -0
  66. package/songs/lyr/song.lyr +37 -0
  67. package/songs/midi/BPL3457.MID +0 -0
  68. package/songs/midi/Z2510006.mid +0 -0
  69. package/songs/midi/Z2510008.mid +0 -0
  70. package/songs/midi/Z2510136.mid +0 -0
  71. package/songs/midi/Z2510137.mid +0 -0
  72. package/songs/midi/Z2510138.mid +0 -0
  73. package/songs/midi/Z2510139.mid +0 -0
  74. package/songs/midi/Z2510140.mid +0 -0
  75. package/songs/midi/Z2510141.mid +0 -0
@@ -0,0 +1,133 @@
1
+
2
+ /**
3
+ * @fileoverview Main decoding logic for .emk (Extreme Karaoke) files.
4
+ * This is the SERVER-SIDE implementation using Node.js's 'zlib'.
5
+ * It is used by the GET /api/emk/import endpoint for playback.
6
+ */
7
+
8
+ import { inflateSync } from 'zlib';
9
+
10
+ const XOR_KEY = Buffer.from([0xAF, 0xF2, 0x4C, 0x9C, 0xE9, 0xEA, 0x99, 0x43]);
11
+ const MAGIC_SIGNATURE = '.SFDS';
12
+
13
+ export interface DecodedEmkParts {
14
+ header?: Buffer;
15
+ songInfo?: Buffer;
16
+ midi?: Buffer;
17
+ lyric?: Buffer;
18
+ cursor?: Buffer;
19
+ }
20
+
21
+ const ZLIB_SECOND_BYTES = new Set<number>([0x01, 0x5E, 0x9C, 0xDA, 0x7D, 0x20, 0xBB]);
22
+
23
+ function xorDecrypt(data: Buffer): Buffer {
24
+ const decrypted = Buffer.alloc(data.length);
25
+ for (let i = 0; i < data.length; i++) {
26
+ decrypted[i] = data[i] ^ XOR_KEY[i % XOR_KEY.length];
27
+ }
28
+ return decrypted;
29
+ }
30
+
31
+ function looksLikeText(buf: Buffer): boolean {
32
+ const sample = buf.subarray(0, Math.min(64, buf.length));
33
+ for (let i = 0; i < sample.length; i++) {
34
+ const c = sample[i];
35
+ if (c === 0x0a || c === 0x0d || c === 0x09 || (c >= 0x20 && c <= 0x7e) || c >= 0x80) {
36
+ continue;
37
+ }
38
+ return false;
39
+ }
40
+ return true;
41
+ }
42
+
43
+ export function decodeEmk(fileBuffer: Buffer): DecodedEmkParts {
44
+ const decryptedBuffer = xorDecrypt(fileBuffer);
45
+
46
+ const magic = decryptedBuffer.subarray(0, MAGIC_SIGNATURE.length).toString('utf-8');
47
+ if (magic !== MAGIC_SIGNATURE) {
48
+ throw new Error(`Invalid EMK file signature. Expected '${MAGIC_SIGNATURE}' but got '${magic}'.`);
49
+ }
50
+
51
+ const result: DecodedEmkParts = {};
52
+ const inflatedParts: Buffer[] = [];
53
+
54
+ for (let i = 0; i < decryptedBuffer.length - 2; i++) {
55
+ const b0 = decryptedBuffer[i];
56
+ const b1 = decryptedBuffer[i + 1];
57
+
58
+ if (b0 !== 0x78 || !ZLIB_SECOND_BYTES.has(b1)) continue;
59
+
60
+ try {
61
+ const inflated = inflateSync(decryptedBuffer.subarray(i));
62
+ inflatedParts.push(inflated);
63
+ } catch {
64
+ continue;
65
+ }
66
+ }
67
+
68
+ if (inflatedParts.length < 3) {
69
+ throw new Error(`Invalid EMK structure: expected at least 3 zlib blocks, found ${inflatedParts.length}.`);
70
+ }
71
+
72
+ for (const inflated of inflatedParts) {
73
+ const asciiPrefix = inflated.subarray(0, 16).toString('ascii');
74
+
75
+ if (asciiPrefix.startsWith('SIGNATURE=')) {
76
+ result.header = inflated;
77
+ } else if (asciiPrefix.startsWith('CODE=')) {
78
+ result.songInfo = inflated;
79
+ } else if (inflated.subarray(0, 4).toString('ascii') === 'MThd') {
80
+ result.midi = inflated;
81
+ } else if (looksLikeText(inflated)) {
82
+ if (!result.lyric) {
83
+ result.lyric = inflated;
84
+ } else {
85
+ result.cursor = inflated;
86
+ }
87
+ } else {
88
+ if (!result.cursor) {
89
+ result.cursor = inflated;
90
+ }
91
+ }
92
+ }
93
+
94
+ if (!result.midi) throw new Error('MIDI data block not found in EMK file.');
95
+ if (!result.lyric) throw new Error('Lyric data block not found in EMK file.');
96
+ if (!result.cursor) throw new Error('Cursor data block not found in EMK file.');
97
+
98
+ if (!result.songInfo) {
99
+ result.songInfo = Buffer.from('CODE=\nTITLE=Unknown Title\nARTIST=Unknown Artist');
100
+ }
101
+
102
+ return result;
103
+ }
104
+
105
+
106
+ /**
107
+ * Parses the "song info" block from a Buffer.
108
+ * @param songInfoBuffer The buffer containing the song info data.
109
+ * @returns A record containing TITLE, ARTIST, and CODE.
110
+ */
111
+ export function parseSongInfo(songInfoBuffer: Buffer | undefined): Record<string, string> {
112
+ if (!songInfoBuffer) return {};
113
+ try {
114
+ const text = new TextDecoder('windows-874', { fatal: false }).decode(songInfoBuffer);
115
+ const lines = text.split(/\r?\n/);
116
+ const info: Record<string, string> = {};
117
+ for (const line of lines) {
118
+ const parts = line.split('=');
119
+ if (parts.length >= 2) {
120
+ const key = parts[0].trim().toUpperCase();
121
+ const value = parts.slice(1).join('=').trim();
122
+ // Only store relevant keys to keep it focused
123
+ if (key === 'TITLE' || key === 'ARTIST' || key === 'CODE' || key === 'title' || key === 'artist' || key === 'code') {
124
+ info[key.toUpperCase()] = value;
125
+ }
126
+ }
127
+ }
128
+ return info;
129
+ } catch (e) {
130
+ console.error("Failed to parse song info", e);
131
+ return {};
132
+ }
133
+ }
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * NCN (.mid + .lyr + .cur) -> .kar (MIDI with embedded karaoke/lyric/title/artist tracks)
4
+ * - .lyr decoded as TIS-620 (Thai encoding)
5
+ * - hard-coded input/output paths
6
+ * - meta events use midi-file format: { deltaTime, type: <subtype>, text }
7
+ */
8
+
9
+ const fs = require("fs");
10
+ const path = require("path");
11
+ const iconv = require("iconv-lite");
12
+ const GraphemeSplitter = require("grapheme-splitter");
13
+ const { parseMidi, writeMidi } = require("midi-file");
14
+
15
+ // ===============================
16
+ // ✅ HARD-CODED FILE PATHS HERE
17
+ // ===============================
18
+ const INPUT_MIDI = "songs/midi/Z2510006.mid"; // <-- change
19
+ const INPUT_LYR = "songs/lyr/Z2510006.lyr"; // <-- change (TIS-620)
20
+ const INPUT_CUR = "songs/cur/Z2510006.cur"; // <-- change
21
+ const OUTPUT_KAR = "out/Z25100066.kar"; // <-- change (must NOT exist)
22
+ // titles.txt will be created/appended in same folder as OUTPUT_KAR:
23
+ const TITLES_TXT = path.join(path.dirname(OUTPUT_KAR), "titles.txt");
24
+
25
+ // ===============================
26
+
27
+ function die(msg) {
28
+ console.error(`[error] ${msg}`);
29
+ process.exit(1);
30
+ }
31
+ function warn(msg) {
32
+ console.warn(`[warn] ${msg}`);
33
+ }
34
+
35
+ function ensureReadableFile(p) {
36
+ try {
37
+ fs.accessSync(p, fs.constants.R_OK);
38
+ if (!fs.statSync(p).isFile()) die(`Not a file: ${p}`);
39
+ } catch {
40
+ die(`Cannot read file: ${p}`);
41
+ }
42
+ }
43
+
44
+ function ensureOutputDoesNotExist(p) {
45
+ if (fs.existsSync(p)) die(`Output already exists: ${p}`);
46
+ const dir = path.dirname(p);
47
+ if (dir && dir !== "." && !fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
48
+ }
49
+
50
+ function readFileTextTIS620(p) {
51
+ const buf = fs.readFileSync(p);
52
+ return iconv.decode(buf, "tis-620");
53
+ }
54
+
55
+ function splitLinesKeepEndings(text) {
56
+ return text.match(/[^\n]*\n|[^\n]+$/g) || [];
57
+ }
58
+ function trimLineEndings(s) {
59
+ return s.replace(/[\r\n]+$/g, "");
60
+ }
61
+
62
+ class CursorReader {
63
+ constructor(buf) {
64
+ this.buf = buf;
65
+ this.off = 0;
66
+ }
67
+ readU16LE() {
68
+ if (this.off + 2 > this.buf.length) return null;
69
+ const v = this.buf.readUInt16LE(this.off);
70
+ this.off += 2;
71
+ return v;
72
+ }
73
+ readU8() {
74
+ if (this.off + 1 > this.buf.length) return null;
75
+ const v = this.buf[this.off];
76
+ this.off += 1;
77
+ return v;
78
+ }
79
+ remaining() {
80
+ return this.buf.length - this.off;
81
+ }
82
+ eof() {
83
+ return this.off >= this.buf.length;
84
+ }
85
+ }
86
+
87
+ // ===== META EVENT BUILDERS (midi-file compatible) =====
88
+ // midi-file expects: { deltaTime, type: <subtype>, text }
89
+ // where subtype is "trackName", "text", "endOfTrack", etc.
90
+ // For Thai text (originally TIS-620), we encode back to TIS-620 bytes
91
+ // because the karaoke software expects TIS-620 encoding
92
+ function metaEvent(subtype, deltaTime, text) {
93
+ // Encode text back to TIS-620 bytes (original encoding) to preserve Thai characters
94
+ // This is necessary because the original .lyr files are in TIS-620
95
+ // and karaoke software may expect TIS-620 encoding in MIDI files
96
+ const tis620Bytes = iconv.encode(text, 'tis-620');
97
+
98
+ // Map subtype to metatypeByte
99
+ const metaTypeMap = {
100
+ 'text': 0x01,
101
+ 'trackName': 0x03,
102
+ 'copyrightNotice': 0x02,
103
+ 'instrumentName': 0x04,
104
+ 'lyrics': 0x05,
105
+ 'marker': 0x06,
106
+ 'cuePoint': 0x07,
107
+ };
108
+
109
+ const metatypeByte = metaTypeMap[subtype];
110
+ if (metatypeByte !== undefined) {
111
+ // Use unknownMeta with proper metatypeByte to write TIS-620 bytes correctly
112
+ // This bypasses the broken writeString function that doesn't handle multi-byte chars
113
+ return {
114
+ deltaTime,
115
+ type: 'unknownMeta',
116
+ metatypeByte: metatypeByte,
117
+ data: Array.from(tis620Bytes)
118
+ };
119
+ }
120
+
121
+ // Fallback to standard format for other types
122
+ return { deltaTime, type: subtype, text };
123
+ }
124
+
125
+ function endOfTrack(deltaTime = 0) {
126
+ return { deltaTime, type: "endOfTrack" };
127
+ }
128
+
129
+ function main() {
130
+ ensureReadableFile(INPUT_MIDI);
131
+ ensureReadableFile(INPUT_LYR);
132
+ ensureReadableFile(INPUT_CUR);
133
+ ensureOutputDoesNotExist(OUTPUT_KAR);
134
+
135
+ const midi = parseMidi(fs.readFileSync(INPUT_MIDI));
136
+
137
+ const ticksPerBeat = midi.header && midi.header.ticksPerBeat;
138
+ if (!ticksPerBeat) {
139
+ die("Only ticks-per-beat MIDI timing is supported (SMPTE timing not supported).");
140
+ }
141
+
142
+ const lyricText = readFileTextTIS620(INPUT_LYR);
143
+ const linesWithEndings = splitLinesKeepEndings(lyricText);
144
+
145
+ if (linesWithEndings.length < 2) die("Lyric file has too few lines (need title and artist).");
146
+
147
+ const songTitle = trimLineEndings(linesWithEndings[0] ?? "");
148
+ const artistName = trimLineEndings(linesWithEndings[1] ?? "");
149
+
150
+ // Perl discards line 3 & 4; lyrics start from line 5 (index 4)
151
+ let fullLyric = "";
152
+ for (let i = 4; i < linesWithEndings.length; i++) fullLyric += linesWithEndings[i];
153
+
154
+ const cursor = new CursorReader(fs.readFileSync(INPUT_CUR));
155
+ const splitter = new GraphemeSplitter();
156
+
157
+ // Track: Words
158
+ const karaokeTrack = [];
159
+ karaokeTrack.push(metaEvent("trackName", 0, "Words"));
160
+ karaokeTrack.push(metaEvent("text", 0, "@T" + songTitle));
161
+ karaokeTrack.push(metaEvent("text", 0, "@T" + artistName));
162
+
163
+ let previousAbsoluteTimestamp = 0;
164
+
165
+ for (let i = 4; i < linesWithEndings.length; i++) {
166
+ const trimmed = trimLineEndings(linesWithEndings[i]);
167
+ if (!trimmed || trimmed.length === 0) continue;
168
+
169
+ const lineForTiming = "/" + trimmed;
170
+ const graphemes = splitter.splitGraphemes(lineForTiming);
171
+
172
+ for (const g of graphemes) {
173
+ let absoluteTimestamp = previousAbsoluteTimestamp;
174
+
175
+ // Read 2 bytes per codepoint inside grapheme (matches Perl logic)
176
+ for (const _cp of Array.from(g)) {
177
+ const v = cursor.readU16LE();
178
+ if (v === null) {
179
+ warn("ran out of timing info; reusing previous timestamp");
180
+ absoluteTimestamp = previousAbsoluteTimestamp;
181
+ } else {
182
+ absoluteTimestamp = v;
183
+ }
184
+ }
185
+
186
+ // Same conversion as Perl: * (ticksPerBeat / 24)
187
+ absoluteTimestamp = Math.floor(absoluteTimestamp * (ticksPerBeat / 24));
188
+
189
+ if (absoluteTimestamp < previousAbsoluteTimestamp) {
190
+ warn("timestamp out of order - clamping");
191
+ absoluteTimestamp = previousAbsoluteTimestamp;
192
+ }
193
+
194
+ const relativeTimestamp = absoluteTimestamp - previousAbsoluteTimestamp;
195
+ karaokeTrack.push(metaEvent("text", relativeTimestamp, g));
196
+ previousAbsoluteTimestamp = absoluteTimestamp;
197
+ }
198
+ }
199
+
200
+ // Terminator check (Perl warns if EOF / no 0xFF)
201
+ const terminator = cursor.readU8();
202
+ if (terminator === null) {
203
+ warn("EOF without terminator while reading timing info");
204
+ } else {
205
+ const leftover = cursor.remaining();
206
+ if (terminator !== 0xff || leftover > 0) {
207
+ warn(
208
+ `${leftover} bytes (${leftover / 2} values) of unused timing info, or timing info ended without 0xFF terminator`
209
+ );
210
+ }
211
+ }
212
+
213
+ karaokeTrack.push(endOfTrack(0));
214
+
215
+ // Track: Lyric (embed full lyric blob)
216
+ const lyricTrack = [
217
+ metaEvent("trackName", 0, "Lyric"),
218
+ metaEvent("text", 0, fullLyric),
219
+ endOfTrack(0),
220
+ ];
221
+
222
+ // Track: Artist
223
+ const artistTrack = [
224
+ metaEvent("trackName", 0, "Artist"),
225
+ metaEvent("text", 0, artistName),
226
+ endOfTrack(0),
227
+ ];
228
+
229
+ // Track: SongTitle
230
+ const titleTrack = [
231
+ metaEvent("trackName", 0, "SongTitle"),
232
+ metaEvent("text", 0, songTitle),
233
+ endOfTrack(0),
234
+ ];
235
+
236
+ // Insert as second track (index 1) like Perl
237
+ const originalTracks = midi.tracks || [];
238
+ const newTracks = originalTracks.slice();
239
+ newTracks.splice(1, 0, karaokeTrack, lyricTrack, artistTrack, titleTrack);
240
+ midi.tracks = newTracks;
241
+
242
+ const outBytes = writeMidi(midi);
243
+ fs.writeFileSync(OUTPUT_KAR, Buffer.from(outBytes));
244
+
245
+ fs.appendFileSync(
246
+ TITLES_TXT,
247
+ `${path.basename(OUTPUT_KAR)}\t${songTitle}\t${artistName}\n`,
248
+ "utf8"
249
+ );
250
+
251
+ console.log("✅ Done");
252
+ console.log(`- Output: ${OUTPUT_KAR}`);
253
+ console.log(`- Titles: ${TITLES_TXT}`);
254
+ }
255
+
256
+ main();
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@karaplay/file-coder",
3
+ "version": "1.1.0",
4
+ "description": "A comprehensive library for encoding/decoding karaoke files (.emk, .kar, MIDI) with Next.js support. Convert EMK to KAR, read/write karaoke files, full browser and server support.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "ncntokar": "./bin/ncntokar-cli.js"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "./client": {
16
+ "types": "./dist/client.d.ts",
17
+ "default": "./dist/client.js"
18
+ }
19
+ },
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "test": "node_modules/.bin/jest",
23
+ "test:watch": "node_modules/.bin/jest --watch",
24
+ "test:coverage": "node_modules/.bin/jest --coverage",
25
+ "prepare": "npm run build"
26
+ },
27
+ "keywords": [
28
+ "karaoke",
29
+ "midi",
30
+ "emk",
31
+ "kar",
32
+ "nextjs",
33
+ "decoder",
34
+ "encoder",
35
+ "thai",
36
+ "tis-620",
37
+ "karaoke-converter",
38
+ "midi-karaoke",
39
+ "browser",
40
+ "client-side",
41
+ "file-converter"
42
+ ],
43
+ "author": "karaplay",
44
+ "license": "MIT",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/karaplay/file-coder.git"
48
+ },
49
+ "homepage": "https://github.com/karaplay/file-coder#readme",
50
+ "bugs": {
51
+ "url": "https://github.com/karaplay/file-coder/issues"
52
+ },
53
+ "dependencies": {
54
+ "grapheme-splitter": "^1.0.4",
55
+ "iconv-lite": "^0.6.3",
56
+ "midi-file": "^1.2.4",
57
+ "pako": "^2.1.0"
58
+ },
59
+ "devDependencies": {
60
+ "@types/jest": "^29.5.11",
61
+ "@types/node": "^20.10.6",
62
+ "@types/pako": "^2.0.3",
63
+ "jest": "^29.7.0",
64
+ "ts-jest": "^29.1.1",
65
+ "typescript": "^5.3.3"
66
+ },
67
+ "peerDependencies": {
68
+ "next": ">=13.0.0",
69
+ "react": ">=18.0.0"
70
+ },
71
+ "peerDependenciesMeta": {
72
+ "next": {
73
+ "optional": true
74
+ },
75
+ "react": {
76
+ "optional": true
77
+ }
78
+ }
79
+ }
package/songs/.gitkeep ADDED
@@ -0,0 +1,3 @@
1
+ # Place your .mid, .lyr, and .cur files here
2
+ # Example: demo.mid, demo.lyr, demo.cur
3
+
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,57 @@
1
+ ����������ͻ���
2
+ �Դ �Ե���
3
+ E
4
+
5
+ . E .
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
+ .
@@ -0,0 +1,53 @@
1
+ Move On Ẻ�
2
+ ���� �����ɰ�
3
+ E
4
+
5
+ ...... Intro ......
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
+ ...... Solo ......
32
+ �ѧ�ٿ�͹����ç������
33
+ �͹����ҫ���Ѻ������ҧ����
34
+ ����ѹ���� ������ �����
35
+ �ѧ���� ������ �����
36
+ ... ���¹�ѹ��駷ء � ���ҧ
37
+ ��ҡ��˹�˹�������ҧ
38
+ ���������������ѡ���ҧ
39
+ ������ѹź���仨ҡ�
40
+ �ѡ�ѹ���ҹ
41
+ ��������Ҩ��µ�ͧ����ѹ���˹
42
+ ... ���¹�ѹ��駷ء � ���ҧ
43
+ ������վ����ͧ�ըҡ
44
+ �������͹�պҧ���ҧ
45
+ �ѹ������ź����ҡ����
46
+ �ѡ��������ѹ
47
+ ���о���������ѹ�ѡ���˹
48
+ ����¨з���ѹ������
49
+ ...... Music ......
50
+ ����ѹ������
51
+ ...... Music .......
52
+ �ѧ�ٿ�͹����ç������
53
+ ...... Outtro ......