@crashcontinuum/crashcart-packager 1.0.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.
- package/README.md +125 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +739 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# crashcart-packager
|
|
2
|
+
|
|
3
|
+
Package Crash BASIC games for distribution on Windows, macOS, and Linux.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx crashcart-packager mygame.crashcart "My Game"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This will:
|
|
12
|
+
1. Stamp your CrashCart with your Crash BASIC Arcade license
|
|
13
|
+
2. Download CrashPlayer for your platform
|
|
14
|
+
3. Create a ready-to-distribute package
|
|
15
|
+
|
|
16
|
+
## Options
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Usage: crashcart-packager [options] <crashcart> <game-name>
|
|
20
|
+
|
|
21
|
+
Arguments:
|
|
22
|
+
crashcart Path to the .crashcart file
|
|
23
|
+
game-name Name of your game
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
-p, --platform <platform> Target platform (windows, macos, linux)
|
|
27
|
+
-i, --icon <path> Path to icon file (PNG for best results)
|
|
28
|
+
-o, --output <dir> Output directory (default: ".")
|
|
29
|
+
--skip-stamp Skip the stamping step (if already stamped)
|
|
30
|
+
--all Build for all platforms
|
|
31
|
+
|
|
32
|
+
macOS Code Signing:
|
|
33
|
+
--apple-team-id <id> Apple Developer Team ID
|
|
34
|
+
--signing-identity <id> Code signing identity
|
|
35
|
+
--apple-id <email> Apple ID for notarization
|
|
36
|
+
--apple-password <pass> App-specific password for notarization
|
|
37
|
+
|
|
38
|
+
-h, --help Display help
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
### Package for current platform
|
|
44
|
+
```bash
|
|
45
|
+
npx crashcart-packager mygame.crashcart "My Awesome Game"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Package for all platforms
|
|
49
|
+
```bash
|
|
50
|
+
npx crashcart-packager mygame.crashcart "My Awesome Game" --all
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Package with custom icon
|
|
54
|
+
```bash
|
|
55
|
+
npx crashcart-packager mygame.crashcart "My Awesome Game" --icon icon.png
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Package for a specific platform
|
|
59
|
+
```bash
|
|
60
|
+
npx crashcart-packager mygame.crashcart "My Awesome Game" --platform windows
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Output
|
|
64
|
+
|
|
65
|
+
The packager creates platform-specific bundles:
|
|
66
|
+
|
|
67
|
+
- **Windows**: Folder containing CrashPlayer.exe, your game, and launcher scripts (.bat and .vbs)
|
|
68
|
+
- **macOS**: A proper .app bundle that can be distributed directly
|
|
69
|
+
- **Linux**: Folder containing CrashPlayer, your game, launcher script, and .desktop file
|
|
70
|
+
|
|
71
|
+
## macOS Code Signing & Notarization
|
|
72
|
+
|
|
73
|
+
For your macOS app to run without security warnings, it must be signed and notarized. The packager will automatically sign and notarize if you provide Apple Developer credentials.
|
|
74
|
+
|
|
75
|
+
### Setup
|
|
76
|
+
|
|
77
|
+
1. **Join the Apple Developer Program** at https://developer.apple.com/programs/
|
|
78
|
+
|
|
79
|
+
2. **Create a Developer ID Application certificate**
|
|
80
|
+
- Open Keychain Access on your Mac
|
|
81
|
+
- Go to developer.apple.com → Certificates
|
|
82
|
+
- Create a "Developer ID Application" certificate
|
|
83
|
+
- Download and install it in your keychain
|
|
84
|
+
|
|
85
|
+
3. **Create an app-specific password**
|
|
86
|
+
- Go to appleid.apple.com → Sign-In and Security
|
|
87
|
+
- Click "App-Specific Passwords" → Generate
|
|
88
|
+
|
|
89
|
+
4. **Find your credentials**
|
|
90
|
+
- **Team ID**: developer.apple.com → Membership → Team ID
|
|
91
|
+
- **Signing Identity**: Run `security find-identity -v -p codesigning`
|
|
92
|
+
Look for "Developer ID Application: Your Name (TEAM_ID)"
|
|
93
|
+
|
|
94
|
+
### Using credentials
|
|
95
|
+
|
|
96
|
+
Via command line options:
|
|
97
|
+
```bash
|
|
98
|
+
npx crashcart-packager mygame.crashcart "My Game" \
|
|
99
|
+
--apple-team-id "YOUR_TEAM_ID" \
|
|
100
|
+
--signing-identity "Developer ID Application: Your Name (TEAM_ID)" \
|
|
101
|
+
--apple-id "your@email.com" \
|
|
102
|
+
--apple-password "xxxx-xxxx-xxxx-xxxx"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Or via environment variables:
|
|
106
|
+
```bash
|
|
107
|
+
export APPLE_TEAM_ID="YOUR_TEAM_ID"
|
|
108
|
+
export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAM_ID)"
|
|
109
|
+
export APPLE_ID="your@email.com"
|
|
110
|
+
export APPLE_PASSWORD="xxxx-xxxx-xxxx-xxxx"
|
|
111
|
+
|
|
112
|
+
npx crashcart-packager mygame.crashcart "My Game"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Requirements
|
|
116
|
+
|
|
117
|
+
- Node.js 18 or later
|
|
118
|
+
- A Crash BASIC Arcade account with an active subscription (Indie or Studio tier)
|
|
119
|
+
- On Windows: PowerShell (for ZIP extraction)
|
|
120
|
+
- On Unix: unzip command (for cross-platform Windows builds)
|
|
121
|
+
- On macOS (for signing): Apple Developer Program membership, Developer ID certificate
|
|
122
|
+
|
|
123
|
+
## License
|
|
124
|
+
|
|
125
|
+
© 2026 Crash Continuum LLC. All rights reserved.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,739 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { program } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import pngToIco from 'png-to-ico';
|
|
6
|
+
import { spawn, execSync } from 'child_process';
|
|
7
|
+
import { createWriteStream, existsSync, mkdirSync, rmSync, copyFileSync, writeFileSync, chmodSync, readFileSync } from 'fs';
|
|
8
|
+
import { join, basename, dirname } from 'path';
|
|
9
|
+
import { pipeline } from 'stream/promises';
|
|
10
|
+
import { createGunzip } from 'zlib';
|
|
11
|
+
import { extract } from 'tar-stream';
|
|
12
|
+
import { createReadStream } from 'fs';
|
|
13
|
+
import { Readable } from 'stream';
|
|
14
|
+
const DEFAULT_ARCADE_URL = 'https://arcade.crashbasic.com';
|
|
15
|
+
// Crash-tan ASCII art data (color, char pairs)
|
|
16
|
+
function printCrashTan() {
|
|
17
|
+
// Helper to convert hex to RGB
|
|
18
|
+
const hex = (h) => {
|
|
19
|
+
const r = parseInt(h.slice(0, 2), 16);
|
|
20
|
+
const g = parseInt(h.slice(2, 4), 16);
|
|
21
|
+
const b = parseInt(h.slice(4, 6), 16);
|
|
22
|
+
return chalk.rgb(r, g, b);
|
|
23
|
+
};
|
|
24
|
+
// Art data: each line is an array of [hexColor, char] pairs
|
|
25
|
+
const art = [
|
|
26
|
+
[['808080', ' '], ['685f6a', '╔'], ['776a77', ','], ['7c767a', '_ '], ['7a787a', '_'], ['736d74', ',']],
|
|
27
|
+
[['808080', ' '], ['767477', '`'], ['52385d', '╫'], ['632968', '╣'], ['763a6c', '▒'], ['825371', '╦'], ['796b75', ', '], ['55415f', '╟'], ['562362', '▌'], ['6f4a6d', '▒']],
|
|
28
|
+
[['808080', ' '], ['736f75', ','], ['716173', '╓'], ['705471', '╗'], ['6b496c', '#'], ['72476d', '▒'], ['79456e', '▒'], ['7f466f', '▒'], ['864a72', '▒'], ['844d71', '╦'], ['7d506f', '╗'], ['775a72', '╔'], ['6e646f', '╓'], ['4c3857', '╫'], ['572364', '╬'], ['5e2566', '╣'], ['772e6c', '╬'], ['7a3e6e', '╠'], ['756b75', ', '], ['5d5164', '#'], ['3f1c55', '▓'], ['3e1854', '▓'], ['522160', '▓'], ['624968', '▒']],
|
|
29
|
+
[['808080', ' '], ['747175', '`'], ['6b6570', '"'], ['605368', '╙'], ['543d60', '▀'], ['4d295c', '╣'], ['4f215e', '▓'], ['562263', '╬'], ['5c2466', '╣'], ['642667', '╣'], ['6c286a', '╣'], ['6d296b', '╬'], ['672869', '╬'], ['53215e', '╣'], ['461c58', '▓'], ['592365', '╣'], ['592364', '╣'], ['542162', '▓'], ['482457', '▓'], ['4a3855', '▓'], ['3c1952', '▓'], ['3b1752', '▓'], ['391751', '▓'], ['4a1e5b', '▓'], ['522f5b', '╬'], ['767076', ','], ['797676', '_'], ['7c7a79', '_'], ['7d7c7b', '_']],
|
|
30
|
+
[['808080', ' '], ['797679', '_'], ['726975', ','], ['6f5c71', '╔'], ['6d5370', '╗'], ['644367', '║'], ['502556', '╬'], ['531f5d', '▓'], ['5b2362', '╣'], ['55205f', '▓'], ['501f5e', '▓'], ['532060', '╬'], ['582364', '╬'], ['532061', '╣'], ['4d1f5e', '▓'], ['552262', '╣'], ['542161', '╣'], ['491c59', '▓'], ['491e54', '▓'], ['491f4f', '▓'], ['51264c', '╣'], ['562b4c', '╣'], ['68405a', '╩'], ['6d4e57', '╜'], ['725955', '╙'], ['76634e', '╙'], ['705e47', '╠'], ['6f5c47', '╚'], ['5c4037', '╣'], ['4b3531', '▓'], ['57433e', '▌'], ['645551', '▄'], ['6f6765', '╓'], ['7a7978', '_ '], ['7a7479', '_'], ['757375', '.']],
|
|
31
|
+
[['808080', ' '], ['736775', '╓'], ['69486d', '#'], ['6d356b', '╬'], ['742c6d', '╬'], ['792c6f', '╬'], ['7b2c6f', '╬'], ['7a2c6f', '╬'], ['782b6e', '╬'], ['752a6c', '╬'], ['742a6d', '╬'], ['6e286b', '╬'], ['612466', '╣'], ['572162', '╣'], ['4d1d5b', '▓'], ['3f1853', '▓'], ['4a1d5a', '▓'], ['4f205a', '▓'], ['4c204a', '▓'], ['421d32', '▓'], ['3b1b20', '█'], ['391b18', '█'], ['2b1414', '█'], ['50312b', '▓'], ['5e4539', '╬'], ['93825c', ','], ['95845d', ','], ['92845e', ','], ['8b7f5c', '┐'], ['605744', '╨'], ['7b6d52', '['], ['583930', '╣'], ['3c221e', '▓'], ['351918', '█'], ['361a18', '█'], ['381b19', '█'], ['3e211e', '▓'], ['4c2e2f', '▓'], ['6e3b54', '▄'], ['7b416c', '▒'], ['7c416c', '▒'], ['7d416e', '▒'], ['793c6e', '▒'], ['683268', '╣'], ['5f4462', '╩']],
|
|
32
|
+
[['808080', ' '], ['6b5e70', '╔'], ['573064', '╣'], ['562465', '╣'], ['592365', '╬'], ['5d2466', '╣'], ['5e2466', '╣'], ['5e2466', '╣'], ['5a2265', '╣'], ['521f5f', '▓'], ['501f5d', '▓'], ['542060', '╣'], ['521f5e', '▓'], ['481c5a', '▓'], ['3e1854', '▓'], ['3a1650', '▓'], ['451b57', '▓'], ['491e4f', '▓'], ['401c2d', '▓'], ['3b1c1a', '█'], ['3a1c19', '█'], ['371a18', '█'], ['331816', '█'], ['2d1514', '█'], ['241112', '█'], ['513127', '▓'], ['6d4b2f', '╬'], ['786143', '▒'], ['78654e', '▄'], ['715a4f', '▄'], ['68534d', '▄'], ['6a514e', '▄'], ['654a4a', '▄'], ['4d2c32', '▓'], ['46213f', '▓'], ['4e1e59', '▓'], ['4d1d56', '▓'], ['491c53', '▓'], ['481c51', '▓'], ['491d51', '▓'], ['51204f', '╬'], ['5f2557', '╬'], ['63265d', '╬'], ['592b55', '╬'], ['655367', '▒']],
|
|
33
|
+
[['808080', ' '], ['777677', '`'], ['6b646f', '"'], ['6f6973', '`'], ['6f6973', '`'], ['6d6672', '`'], ['645b6a', 'T'], ['513c5a', '╫'], ['4a1e59', '▓'], ['542162', '╬'], ['572263', '╬'], ['582264', '╬'], ['582263', '╬'], ['4e1f5e', '▓'], ['3c1753', '▓'], ['331348', '▓'], ['351543', '▓'], ['341626', '█'], ['381b18', '█'], ['381b18', '█'], ['321816', '█'], ['2c1415', '█'], ['231211', '█'], ['372219', '█'], ['634927', '▀'], ['996f3a', '▒'], ['b0783d', '░'], ['a56c36', '▒'], ['411e48', '▓'], ['4e1d5d', '▓'], ['5a245c', '╣'], ['58274c', '╣'], ['662565', '╬'], ['782c6a', '╬'], ['732a69', '╬'], ['73296a', '╬'], ['7b2c6b', '╬'], ['822f6c', '╬'], ['732964', '╣'], ['672562', '╬'], ['762a6a', '╬'], ['702969', '╬'], ['6a2768', '╬'], ['632567', '╣'], ['752c6b', '╣'], ['8d3770', '▒'], ['89496f', '▒'], ['776270', '╔'], ['7c787b', '_']],
|
|
34
|
+
[['808080', ' '], ['67586b', '╗'], ['572c62', '╣'], ['572265', '╬'], ['502060', '▓'], ['501f60', '▓'], ['5b2366', '╬'], ['572263', '╣'], ['451b59', '▓'], ['381650', '▓'], ['311248', '█'], ['1d0b19', '█'], ['220f14', '█'], ['311616', '█'], ['2c1414', '█'], ['301b17', '█'], ['513924', '▓'], ['7d5731', 'Ñ'], ['a7733b', 'Ü'], ['ac7138', '▒'], ['a96832', '▒'], ['af6630', '▒'], ['97572e', '╠'], ['52253d', '▓'], ['4d1c5c', '▓'], ['5b245d', '▌'], ['8c5f40', 'Ö'], ['945b36', '╠'], ['55205f', '▓'], ['562160', '▓'], ['602465', '╣'], ['612466', '╣'], ['602465', '╣'], ['5b2164', '╣'], ['5d2c4c', '╬'], ['653430', '╫'], ['541e5c', '▓'], ['692767', '╣'], ['5b2261', '╣'], ['6d2869', '╬'], ['622567', '╣'], ['5d2365', '╣'], ['612567', '╣'], ['712a6b', '╣'], ['7c346d', '╠'], ['78446d', '▒'], ['7a5973', '╔'], ['756775', '╓'], ['79787a', '_']],
|
|
35
|
+
[['808080', ' '], ['593d63', '╢'], ['4a1f5d', '▓'], ['3c1853', '▓'], ['35154d', '▓'], ['3b1750', '▓'], ['421955', '▓'], ['3d1853', '▓'], ['381650', '▓'], ['36154e', '▓'], ['33144c', '▓'], ['2d1141', '█'], ['1d0c15', '█'], ['2c1415', '█'], ['2a0f16', '█'], ['725631', 'Ñ'], ['b28145', '░'], ['ba7f42', '░'], ['b36e36', '▒'], ['ac6530', 'Ö'], ['b06430', '▒'], ['ad6330', '▒'], ['92562e', '╟'], ['38134b', '▓'], ['491e52', '▓'], ['643d42', '╬'], ['81613c', '╩'], ['886f3f', 'K'], ['a27c43', '░'], ['734339', '╢'], ['4b1a5a', '▓'], ['5a2363', '╬'], ['5c2464', '╣'], ['572261', '╣'], ['511a61', '▓'], ['876242', 'Ü'], ['b37e41', '░'], ['865032', '╠'], ['491d50', '▓'], ['481b58', '▓'], ['582262', '╬'], ['5c2465', '╣'], ['592364', '╣'], ['582264', '╣'], ['501f5b', '▓'], ['533959', '╬'], ['65566b', '╙'], ['695e6e', '"'], ['726a74', '`']],
|
|
36
|
+
[['808080', ' '], ['564b5e', '║'], ['3c224e', '▓'], ['493956', '▀'], ['5a5062', '╨'], ['443150', '╫'], ['34154c', '▓'], ['32144b', '▓'], ['32144b', '▓'], ['33144b', '▓'], ['35143f', '▓'], ['a63f11', '╬'], ['a23b09', '╣'], ['161315', '█'], ['3f1e18', '█'], ['3b1616', '█'], ['5a3a24', '▓'], ['885730', '▄'], ['9b5d2e', '▒'], ['a75f2e', '▒'], ['a45e2e', '▒'], ['93552c', '╢'], ['5a342b', '▓'], ['653b31', '▀'], ['93663d', 'Ü'], ['b18447', '»'], ['b08749', '»'], ['a37e45', '*'], ['a88348', '░'], ['b0894b', '»'], ['b38849', '»'], ['694437', '╫'], ['41175b', '▓'], ['542657', '▓'], ['3c2147', '▓'], ['7f5944', 'Ö'], ['ac8649', '»'], ['a17e45', '░'], ['ac8145', '░'], ['a26a38', '╚'], ['411c3a', '▓'], ['4f1e5d', '▓'], ['582363', '╣'], ['572263', '╣'], ['542161', '▓'], ['50205f', '▓'], ['592564', '╣'], ['69596c', 'm']],
|
|
37
|
+
[['808080', ' '], ['777678', '` '], ['5f5665', '▐'], ['37164f', '▓'], ['34174a', '▓'], ['2c1540', '█'], ['2c1247', '█'], ['571a21', '▓'], ['c44b0a', '╬'], ['c94609', '╣'], ['c24108', '╬'], ['6b230b', '▓'], ['301712', '█'], ['371a17', '█'], ['3a1c17', '█'], ['371917', '█'], ['2f1317', '█'], ['251019', '█'], ['200e14', '█'], ['230d3b', '█'], ['61313a', '▌'], ['aa7337', '╗'], ['775b30', '╣'], ['6c5831', '▀'], ['6c5c34', '▀'], ['776035', 'Φ'], ['9a7c45', '╦'], ['b18b4c', ':'], ['b28b4c', ':'], ['977748', '7'], ['755441', 'Ñ'], ['a98449', '└'], ['ab874a', '»'], ['705b33', '╫'], ['423d32', '▀'], ['383631', '▓'], ['5d584a', '╩'], ['654b2b', '▌'], ['a66733', '╠'], ['501f5c', '▓'], ['52205f', '▓'], ['542162', '▓'], ['552262', '╣'], ['4a2955', '╬'], ['442455', '▓'], ['4e2a5c', '▌']],
|
|
38
|
+
[['808080', ' '], ['584b5f', '╠'], ['4d3a58', '▀'], ['726e72', '`'], ['584149', '╢'], ['9c3914', '╬'], ['dc540b', '╬'], ['ed600b', '╠'], ['e95f0b', '╠'], ['d8550a', '╬'], ['c54609', '╣'], ['af3808', '╬'], ['952f09', '╬'], ['8a2b0a', '▓'], ['842909', '▓'], ['2b0d26', '█'], ['260f38', '█'], ['210d22', '█'], ['411853', '▓'], ['6a3740', '╬'], ['bb7b3b', '░'], ['be773b', 'µ'], ['c2793d', '░'], ['bc8042', '░'], ['b58647', '»'], ['a88348', '└'], ['b18a4c', ':'], ['b28a4c', ':'], ['b28a4c', ':'], ['b38a4b', ':'], ['b28a4b', ':'], ['8d7443', '▐'], ['23353f', '▓'], ['353e48', '▓'], ['47494a', '╬ '], ['61513d', '╢'], ['663c34', '╫'], ['3e1551', '▓'], ['471c58', '▓'], ['4d1e5d', '▓'], ['542262', '╣'], ['624c67', '▒'], ['757376', "'"], ['514558', '╩']],
|
|
39
|
+
[['808080', ' '], ['9f6842', '1'], ['e46210', '╠'], ['f0620c', '╠'], ['f0620c', '╠'], ['f0620c', '╠'], ['ef610b', '╠'], ['ed5f0b', '╠'], ['e5590b', '╬'], ['c94509', '╬'], ['b23808', '╬'], ['731f0a', '▓'], ['26091d', '█'], ['19092d', '█'], ['200c28', '█'], ['240d34', '█'], ['3c1750', '▓'], ['4d1d5b', '▓'], ['8e5839', '▒'], ['c3773a', '░'], ['c3773b', '░'], ['bf7d3f', '░'], ['b78545', '░'], ['ab8448', '░'], ['ad8749', '░'], ['b1894a', ':'], ['b38a4b', ':'], ['b4894a', ':'], ['b4894a', ':'], ['a18049', '░'], ['696b50', '╚'], ['5b5b47', '╩'], ['8c7860', ','], ['a47f53', '='], ['9d723e', '▐'], ['28123c', '█'], ['220e32', '█'], ['3a174f', '▓'], ['4a215a', '▓'], ['5f4a66', '╩']],
|
|
40
|
+
[['808080', ' '], ['7d7570', ','], ['c06022', '╠'], ['ed620c', '╠'], ['ef620b', '╠'], ['f0630b', '╠'], ['ef620b', '╠'], ['ed5f0b', '╠'], ['e3580b', '╢'], ['d9510a', '╬'], ['d04b0a', '╬'], ['a93906', '╣'], ['290a20', '█'], ['1c092d', '█'], ['19092b', '█'], ['200c37', '█'], ['1f0b34', '█'], ['280f41', '█'], ['2d1146', '█'], ['402234', '▓'], ['723e33', '╬'], ['b2763a', 'Ü'], ['bf7f3f', '░'], ['bc8242', '░'], ['8c6332', '║'], ['a2290d', '▓'], ['ae2e10', '╬'], ['a63a14', '╣'], ['9a3e1e', '╣'], ['7a542c', '▄'], ['af8345', '░'], ['bc8142', '»'], ['c3773b', '░'], ['c67037', ']'], ['c2773b', '░'], ['ba7e3f', '░'], ['4a302e', '▓'], ['321349', '▓'], ['3b2547', '▓'], ['301346', '█'], ['4b3857', '▌']],
|
|
41
|
+
[['808080', ' '], ['8b614d', '╚'], ['da520c', '╬'], ['e85c0b', '╠'], ['ee610b', '╠'], ['ed600b', '╠'], ['e95e0b', '╠'], ['e3590b', '╬'], ['d5500a', '╣'], ['c04209', '╣'], ['cf4c09', '╬'], ['b84607', '╬'], ['78340f', '▓'], ['4d3438', '▌'], ['5f4242', '╢'], ['4b161c', '▓'], ['45111a', '█'], ['39122f', '█'], ['39174f', '▓'], ['4b1b58', '▓'], ['372119', '█'], ['835729', '▄'], ['b27638', '▒'], ['bb8141', '░'], ['b58545', '»'], ['ca6f31', '╙'], ['db6022', '╚'], ['dd5a20', '╩'], ['d55f23', '╚'], ['a66e37', '╙'], ['b68545', '»'], ['bc8242', '»'], ['c07d3e', '░'], ['c0783a', '░'], ['9c6637', '▄'], ['492d2f', '▓'], ['3f1754', '▓'], ['52215f', '▓'], ['4f2958', '╬'], ['5a4c63', '╨'], ['787579', '`']],
|
|
42
|
+
[['808080', ' '], ['8b5b48', '╚'], ['b7400f', '╣'], ['cc480a', '╬'], ['dd540a', '╬'], ['e0570b', '╬'], ['d9510a', '╬'], ['d24c0a', '╬'], ['e55a0b', '╬'], ['d4530a', '╣'], ['c74809', '╬'], ['ec650b', '╠'], ['e6650c', '╠'], ['d4550a', '╬'], ['cc4b09', '╬'], ['bb3e09', '╣'], ['b73b08', '╬'], ['952f0e', '╬'], ['6e2123', '▓'], ['5d1c1b', '▓'], ['873309', '▓'], ['73320f', '▓'], ['a55826', '╬'], ['a9652f', '▒'], ['a97139', '╦'], ['aa793f', '╦'], ['a07c42', '╥'], ['9f7840', '╦'], ['986a38', '▄'], ['89582d', '▄'], ['63442b', '▓'], ['503b35', '▌'], ['3e2b3f', '▓'], ['2f1449', '▓'], ['3e284a', '▓'], ['432052', '▓'], ['54235f', '▓'], ['6f5e6e', 'H']],
|
|
43
|
+
[['808080', ' '], ['826f68', "'"], ['9f4a28', '╢'], ['b43b0b', '╣'], ['bb3d0a', '╣'], ['c8460a', '╣'], ['cc480a', '╣'], ['c6450a', '╬'], ['db520b', '╬'], ['e65c0b', '╠'], ['f1650c', '╠'], ['f1670c', '╠'], ['db5e0a', '╬'], ['e45e0b', '╬'], ['ee660c', '╠'], ['df5a0b', '╬'], ['c64a09', '╣'], ['d6520a', '╬'], ['e95f0a', '╠'], ['eb600b', '╠'], ['e95f0a', '╠'], ['e2620c', '╠'], ['ce6915', '╠'], ['b46424', '╠'], ['a65525', '╢'], ['622f15', '▓'], ['471c0b', '█'], ['4d1303', '█'], ['6a2004', '█'], ['c35009', '╬'], ['9e5d23', '╬'], ['7b685c', '░'], ['5b4b57', '╙ '], ['675d69', '╙'], ['685a6b', 'Γ']],
|
|
44
|
+
[['808080', ' '], ['806f69', "'"], ['965237', '╩'], ['ae3c10', '╣'], ['b4390a', '╣'], ['b93b09', '╣'], ['c9470a', '╬'], ['dc540b', '╬'], ['de550b', '╬'], ['e2580b', '╬'], ['ec620b', '╠'], ['cc4e09', '╬'], ['ec630b', '╠'], ['ed640b', '╠'], ['ec690b', '╠'], ['e7650a', '╠'], ['e4610a', '╬'], ['dd5b0a', '╬'], ['d9580a', '╬'], ['de580a', '╬'], ['e45c0a', '╬'], ['e7650a', '╠'], ['d16b12', '╠'], ['ae5f23', '╠'], ['683215', '▓'], ['5c1d04', '█'], ['bc4d08', '╬'], ['e95f0a', '╠'], ['e5600a', '╬'], ['ec670c', '╠'], ['e67210', '╠'], ['9a7041', '▒']],
|
|
45
|
+
[['808080', ' '], ['8c5f4e', '╙'], ['9e4420', '╫'], ['b03a0c', '╣'], ['b53909', '╣'], ['b73a09', '╣'], ['b83b09', '╣'], ['d54e0a', '╬'], ['cd4909', '╣'], ['cc4b0a', '╬'], ['e0580b', '╬'], ['d8560a', '╬'], ['e7620b', '╠'], ['f1690c', '╠'], ['f2690c', '╠'], ['f1670c', '╠'], ['ef660c', '╠'], ['e9630b', '╠'], ['de5b0a', '╬'], ['d8570a', '╬'], ['df600a', '╬'], ['b14e09', '╬'], ['dd5f0a', '╬'], ['dd5a09', '╬'], ['d15409', '╬'], ['e5630a', '╠'], ['e4640a', '╬'], ['e2640a', '╬'], ['d9640a', '╬'], ['c37722', 'D'], ['897b63', ',']],
|
|
46
|
+
[['808080', ' '], ['807571', '`'], ['895b4a', '╙'], ['9c4423', '╣'], ['ac390d', '╣'], ['b23809', '╣'], ['b43908', '╣'], ['b33808', '╣'], ['bc3d09', '╣'], ['c64708', '╬'], ['e75e0b', '╬'], ['f2650c', '╠'], ['c87036', 'Ü'], ['b0774d', '╓'], ['b67448', '░'], ['c16c3d', '░'], ['af774e', '`'], ['bf743f', '╙'], ['e66718', '╠'], ['c67138', '╙'], ['d46c2a', '╚'], ['e76a17', '╩'], ['d57028', '╙'], ['e3691b', '╚'], ['e0681d', '╚'], ['f0690d', '╠'], ['ea660d', '╠'], ['e25e0a', '╢'], ['df7b11', '╠'], ['89795c', '=']],
|
|
47
|
+
[['808080', ' '], ['7b5a4e', '╙'], ['9b330b', '▓'], ['9a2e06', '▓'], ['a73307', '▓'], ['bd3e08', '╣'], ['d9530a', '╬'], ['ec5f0c', '╠'], ['c3703a', '░'], ['b17a4c', '²'], ['c47239', '╙'], ['b86f45', '░'], ['9e7f60', '_'], ['9f805e', '`'], ['bf6c3e', '░'], ['9a8363', '`'], ['9d8060', '`'], ['bb6a41', '░'], ['ab7651', ','], ['b97143', ')'], ['a1805c', '¬'], ['b87044', '"'], ['9c8060', "'"], ['d24913', '╢'], ['eb650f', '╠'], ['de8114', '╠'], ['887c65', ',']],
|
|
48
|
+
[['808080', ' '], ['835b4b', '╙'], ['a9370b', '╣'], ['a53306', '▓'], ['b83b08', '╬'], ['bd3e09', '╬'], ['ca4709', '╬'], ['ea600a', '╠'], ['eb6612', '╠'], ['e66818', '╠'], ['cc6c31', 'Ü'], ['b57348', '²'], ['b67647', '='], ['bd6e40', '░'], ['c4673a', '≡'], ['c0663e', 'U'], ['c86d35', '╦'], ['af754e', '∩'], ['b1784c', '»'], ['bd6e3f', '░'], ['c76635', '['], ['b57247', '░'], ['be5422', '╠'], ['d85209', '╬'], ['de5c0b', '╬'], ['d27e18', '╠'], ['847a69', '.']],
|
|
49
|
+
[['808080', ' '], ['89523c', '╠'], ['b13708', '╬'], ['b13807', '▓'], ['bc3d08', '╬'], ['bf3f09', '╬'], ['d14c0a', '╬'], ['ee630a', '╠'], ['f0650b', '╠'], ['f1660c', '╠'], ['bf713e', '░'], ['aa7b54', '|'], ['e35f1b', '▒'], ['9f815f', '` '], ['ce6c30', '╚'], ['ad7d51', '⌐'], ['998565', '`'], ['c2703c', 'Ü'], ['a47e5a', "'"], ['f3640a', '╠'], ['ea650b', '╠'], ['c34509', '╣'], ['c9460a', '╬'], ['e8630b', '╠'], ['d27a1a', '╠'], ['80776b', '.']],
|
|
50
|
+
[['808080', ' '], ['904e32', '╟'], ['b43908', '╣'], ['af3707', '▓'], ['c84509', '╬'], ['e55a0a', '╬'], ['ec610a', '╠'], ['ed630b', '╠'], ['f36708', '╠'], ['cf6a2d', '▒'], ['ba7244', '╓'], ['d26b2b', '▒'], ['b17a4c', '╓'], ['d1702c', '╦'], ['ab7953', ','], ['ac7851', ','], ['b97444', 'j'], ['a67d57', ','], ['9c8462', '_'], ['ee680f', '╠'], ['f1680b', '╠'], ['d35509', '╣'], ['c13f09', '╬'], ['d9520a', '╬'], ['f16a0a', '╠'], ['ce7215', '╠'], ['8b7d64', '_']],
|
|
51
|
+
[['808080', ' '], ['7f716b', '!'], ['c24d11', '╬'], ['dd5409', '╬'], ['d34f08', '╬'], ['d54d08', '╣'], ['d34b09', '╬'], ['d54d09', '╬'], ['ca4b07', '╬'], ['db5708', '╬'], ['ea620a', '╠'], ['ee650a', '╠'], ['ed670a', '╠'], ['ec670a', '╠'], ['eb6809', '╠'], ['ea650a', '╟'], ['ea6709', '╟'], ['e9660a', '╟'], ['e7650c', '╟'], ['e06313', '╠'], ['e96709', '╟'], ['e66509', '╢'], ['e46309', '╬'], ['c34608', '╣'], ['d04a09', '╬'], ['eb5f09', '╠'], ['ec630a', '╠'], ['df7d11', '╠'], ['8d7c5d', '=']],
|
|
52
|
+
[['808080', ' '], ['7f7875', '_'], ['9a5535', 'Æ'], ['af3908', '╣'], ['b23907', '▓'], ['c94709', '╬'], ['e35909', '╬'], ['e55a09', '╬'], ['d35408', '╬'], ['e55b09', '╬'], ['d15108', '╬'], ['d44d09', '╬'], ['d34d09', '╬'], ['d54f09', '╬'], ['dd5609', '╬'], ['ee630a', '╠'], ['f1650a', '╠'], ['f0640a', '╠'], ['ea5f0a', '╠'], ['e2580a', '╬'], ['e3590a', '╬'], ['ed620a', '╠'], ['f1680a', '╠'], ['e36409', '╬'], ['e46009', '╢'], ['c34409', '╬'], ['d24d09', '╣'], ['e15809', '╬'], ['ed620b', '╠'], ['e27c11', '╠'], ['8a795b', '╓']],
|
|
53
|
+
[['808080', ' '], ['84675a', '╔'], ['ad4517', '╣'], ['c04008', '╣'], ['be4007', '╬'], ['d54f08', '╬'], ['e75c0a', '╬'], ['e65e09', '╢'], ['de5b08', '╬'], ['e85f09', '╠'], ['de5b08', '╬'], ['ea5f0a', '╠'], ['ed610a', '╠'], ['ef630a', '╠'], ['f0640a', '╠'], ['f0650a', '╠'], ['f1660a', '╠'], ['f1660a', '╠'], ['f2660a', '╠'], ['f1660a', '╠'], ['f1650a', '╠'], ['f1650b', '╠'], ['f1660a', '╠'], ['f1660a', '╠'], ['e56309', '╬'], ['ee640a', '╠'], ['d15308', '╣'], ['bc3e08', '╣'], ['d64f09', '╬'], ['eb5f0a', '╠'], ['ef640b', '╠'], ['de7712', '╠'], ['8a7961', '⌐']],
|
|
54
|
+
[['808080', ' '], ['904b2f', '╢'], ['ab3608', '▓'], ['b23a07', '▓'], ['c34408', '╬'], ['d75009', '╬'], ['e05609', '╬'], ['c54a07', '╣'], ['c84807', '╣'], ['c64708', '╬'], ['d14b09', '╬'], ['d54e09', '╬'], ['d65009', '╬'], ['d75009', '╬'], ['d75009', '╬'], ['d54e09', '╬'], ['d54e09', '╬'], ['d75009', '╬'], ['db5309', '╬'], ['e35909', '╬'], ['ec600a', '╠'], ['ec610a', '╠'], ['e55e0a', '╬'], ['d6590b', '╬'], ['bb510d', '╣'], ['ab4c0d', '╣'], ['ac4d10', '╣'], ['a2480f', '╣'], ['a93e0d', '╬'], ['e35c0a', '╬'], ['eb5f0a', '╠'], ['ee620a', '╠'], ['d9620d', '╢'], ['c96d22', 'K'], ['97734d', '╔']],
|
|
55
|
+
[['808080', ' '], ['7f5f53', '1'], ['b43b0a', '╣'], ['b13807', '╣'], ['a73306', '▓'], ['af3606', '▓'], ['b03707', '▓'], ['b13706', '▓'], ['a93405', '▓'], ['ab3506', '▓'], ['b13707', '▓'], ['b63907', '╣'], ['b83a07', '╣'], ['b93b07', '╣'], ['bf3f08', '╣'], ['c44308', '╣'], ['cb4808', '╣'], ['d34d08', '╬'], ['d95108', '╬'], ['d45008', '╣'], ['c14407', '╣'], ['a13808', '▓'], ['331c13', '█'], ['442016', '█'], ['472217', '▓'], ['4d2518', '▓'], ['4f2718', '▓'], ['4f2618', '▓'], ['4d2417', '▓'], ['823711', '▓'], ['d64f09', '╬'], ['d85109', '╬'], ['d75209', '╬'], ['d3530d', '╬'], ['88634c', 'Ü']],
|
|
56
|
+
[['808080', ' '], ['76564a', '╠'], ['7e2f10', '▓'], ['91300a', '▓'], ['9e3307', '▓'], ['ae3707', '╬'], ['b53908', '╬'], ['c24107', '╬'], ['db5109', '╬'], ['d85408', '╬'], ['d55208', '╬'], ['e05509', '╬'], ['d95308', '╬'], ['ce5007', '╬'], ['de5409', '╬'], ['d95108', '╬'], ['c24906', '╬'], ['d54e08', '╬'], ['d54d08', '╬'], ['d14b08', '╬'], ['c04007', '╬'], ['9b3107', '▓'], ['1c1310', '█'], ['482216', '▓'], ['421f15', '█'], ['472216', '▓'], ['5b2815', '▓'], ['7c420f', '▓'], ['4b2412', '█'], ['62210a', '█'], ['952f07', '▓'], ['8d2d08', '▓'], ['682209', '▓'], ['431b0f', '█'], ['4b3933', '▌'], ['777675', '_']],
|
|
57
|
+
[['808080', ' '], ['666b6b', '╓'], ['203a3f', '▓'], ['14373b', '█'], ['153b3c', '▓'], ['153c3b', '▓'], ['183937', '▓'], ['192f2d', '█'], ['1d231d', '█'], ['35261a', '▓'], ['472a16', '▓'], ['563013', '▓'], ['743810', '▓'], ['863f10', '▓'], ['8f410e', '▓'], ['a0460e', '▌'], ['a4490c', '▌'], ['99470a', '▓'], ['b74e0c', '╬'], ['bb4f0b', '╬'], ['ba4c0b', '╬'], ['b9490b', '╬'], ['9f3d09', '╣'], ['20100d', '█'], ['411e15', '█'], ['442015', '█'], ['411f15', '█'], ['431f14', '█'], ['462013', '█'], ['3e1d13', '█'], ['381a12', '█'], ['1d0b0b', '█'], ['210e0e', '█'], ['2d1310', '█'], ['3a1a13', '█'], ['391b12', '█'], ['4d3730', '▌'], ['6c5e58', '╦'], ['76706c', ',']],
|
|
58
|
+
[['808080', ' '], ['525c5e', '▄'], ['18373c', '▓'], ['194441', '▓'], ['205849', '▓'], ['1e5548', '▓'], ['23604d', '╣'], ['23604d', '╣'], ['225d4c', '▓'], ['225d4a', '▓'], ['235f4d', '╬'], ['215a4a', '╬'], ['1d4f46', '▓'], ['1a473f', '▓'], ['1d4a43', '▓'], ['1b4641', '▓'], ['19413f', '▓'], ['143538', '█'], ['18423f', '▓'], ['174643', '▓'], ['164743', '▓'], ['174742', '▓'], ['17453f', '▓'], ['143839', '█'], ['0f151a', '█'], ['231412', '█'], ['301813', '█'], ['381b14', '█'], ['391c14', '█'], ['361b14', '█'], ['311d18', '█'], ['574d4b', 'Ñ'], ['322626', '▓'], ['25100f', '█'], ['311511', '█'], ['462116', '█'], ['4b2518', '▓'], ['4e2719', '▓'], ['4b2517', '▓'], ['52291c', '▓'], ['9d7742', 'Ü'], ['7c7469', '⌐']],
|
|
59
|
+
[['808080', ' '], ['3b4d50', '╣'], ['14353a', '█'], ['1b4c45', '▓'], ['225d4c', '▓'], ['215d4b', '▓'], ['23604e', '╣'], ['23614f', '╣'], ['225f4e', '╣'], ['1e5648', '▓'], ['23604d', '╬'], ['24614e', '╣'], ['24614e', '╣'], ['23614e', '╣'], ['22604c', '▓'], ['24624f', '╣'], ['23624e', '╣'], ['23614e', '╣'], ['1e5648', '▓'], ['23604d', '╬'], ['24624e', '╣'], ['24624f', '╣'], ['24624e', '╣'], ['215d4a', '▓'], ['215c4c', '╣'], ['1b4a44', '▓'], ['194541', '▓'], ['133535', '█'], ['123235', '█'], ['143739', '█'], ['153a3b', '█'], ['143237', '█'], ['4b5b5b', '▒ '], ['646060', '╙'], ['49312a', '▓'], ['723d20', '▌'], ['83552c', '╬'], ['4e2b1b', '▓'], ['4b2417', '▓'], ['865127', 'Ñ'], ['b77236', 'Ü'], ['84715b', 'H']],
|
|
60
|
+
[['808080', ' '], ['6e7272', '`'], ['485d5b', '╨'], ['3a6357', '╣'], ['2b5d4d', '╣'], ['225a47', '▓'], ['23614e', '╣'], ['22624e', '╣'], ['23614e', '╣'], ['215d49', '▓'], ['24634f', '╣'], ['24624e', '╣'], ['24624e', '╣'], ['22604c', '▓'], ['23614d', '▓'], ['23624e', '╣'], ['23624e', '╣'], ['23624e', '╣'], ['215f4b', '▓'], ['23614d', '╣'], ['24624e', '╣'], ['24624e', '╣'], ['24634f', '╣'], ['215c48', '▓'], ['23614e', '╣'], ['23614e', '╣'], ['24624e', '╣'], ['23604c', '╣'], ['225d49', '▓'], ['24614e', '╣'], ['24604d', '╣'], ['1d5045', '▓'], ['204945', '▓'], ['626a69', 'H '], ['7a7471', '`'], ['8f6951', '╙'], ['a3653a', 'Ü'], ['ac703a', 'Ü'], ['8a5b37', '╩'], ['9b6847', '╙'], ['827061', '^']],
|
|
61
|
+
[['808080', ' '], ['8e5c36', '╠'], ['9a5e2d', '╠'], ['88592d', '╬'], ['74552d', '╬'], ['5e552f', '╬'], ['4b5432', '╬'], ['405233', '╣'], ['345133', '╣'], ['2a5035', '▓'], ['295942', '╣'], ['26604c', '╣'], ['25624e', '╣'], ['25634f', '╣'], ['235b48', '▓'], ['255f4b', '╣'], ['23614b', '╣'], ['24624c', '╣'], ['24634d', '╬'], ['225b45', '▓'], ['255f49', '╣'], ['265f49', '╣'], ['285e47', '╣'], ['2a5a42', '╣'], ['2c5540', '▌'], ['3f675a', '╩'], ['486b60', '╩'], ['526b63', '╙'], ['6f7473', '`']],
|
|
62
|
+
[['808080', ' '], ['9a663e', 'Ü'], ['b6773c', 'ù'], ['b4743a', '╙'], ['b36e35', '╙'], ['b16832', '╚'], ['af632e', '╩'], ['ad5f2c', '╠'], ['ac5e2b', '╠'], ['ac5f2c', '╠'], ['8a6344', '╚ '], ['727775', '` '], ['766b63', '!'], ['a15c2c', '╠'], ['a75e2b', '╠'], ['a75e2b', '╠'], ['a95f2c', '╠'], ['ab602d', '╩'], ['ac632e', '╩'], ['ae642e', '╩'], ['b1642f', '╩'], ['95623b', 'Ü']],
|
|
63
|
+
[['808080', ' '], ['9a6e41', 'Ü'], ['b98746', '»'], ['b88847', '»'], ['b88847', '»'], ['b98746', '»'], ['bb8443', '»'], ['bb7d3e', '░'], ['b66c33', '╚'], ['9a6038', '╩ '], ['8a6147', '╚'], ['af612d', '▒'], ['b26630', '╩'], ['b6783b', 'Ü'], ['b88343', '»'], ['b98544', '»'], ['ba8544', '»'], ['bb8443', '»'], ['a27845', ']'], ['757574', '_']],
|
|
64
|
+
[['808080', ' '], ['787470', ','], ['a97840', 'H'], ['b88746', '»'], ['b78847', '»'], ['b88847', '»'], ['b88746', '»'], ['ba8644', '»'], ['bb8543', '»'], ['a97941', '['], ['7c716a', '` '], ['9c633c', '╚'], ['bc8041', '░'], ['b78847', '»'], ['b68948', '»'], ['b78848', '»'], ['b88746', '»'], ['ba8644', '»'], ['aa7e48', '░'], ['797672', '_']],
|
|
65
|
+
[['808080', ' '], ['79756e', ','], ['9e7646', 'Ü'], ['b98545', '»'], ['b78847', '»'], ['bb8645', '»'], ['c17d3d', '░'], ['bc8342', '»'], ['bb8443', '»'], ['ab7e44', '░'], ['7f7568', '⌐ '], ['7d7773', '`'], ['9f7344', 'U'], ['b68847', '»'], ['b78848', '»'], ['b98746', '»'], ['bd8342', '░'], ['bc8443', '»'], ['af8045', '░'], ['7f776d', '_']],
|
|
66
|
+
[['808080', ' '], ['7a746d', ':'], ['a77d46', '░'], ['b78847', '»'], ['b68948', '»'], ['b78948', '»'], ['b88847', '»'], ['ba8544', '»'], ['b98343', '░'], ['987549', '░'], ['7c7873', '` '], ['86725d', 'j'], ['b38646', '░'], ['b68848', '»'], ['b98746', '»'], ['be8141', '░'], ['bd8242', '»'], ['bb8443', '»'], ['95754c', ']'], ['7c7a78', '_']],
|
|
67
|
+
[['808080', ' '], ['8b7354', '░'], ['b78645', '»'], ['b68948', '»'], ['b68a49', '»'], ['b68949', '»'], ['b78948', '»'], ['b98645', '»'], ['b38043', '░'], ['7a7167', '⌐ '], ['7e7165', '|'], ['b28446', '░'], ['b68849', '»'], ['b78948', '»'], ['b88847', '»'], ['b98746', '»'], ['bb8544', '»'], ['b78545', '»'], ['857257', '░']],
|
|
68
|
+
[['808080', ' '], ['94764f', '░'], ['b88746', '»'], ['b68848', '»'], ['b58949', '»'], ['b68949', '»'], ['b78847', '»'], ['b98645', '»'], ['8f734d', '] '], ['8d6f4a', ']'], ['b78847', '»'], ['b78847', '»'], ['b78747', '»'], ['b88746', '»'], ['ba8545', '»'], ['ba8444', '»'], ['a47e49', '░'], ['7e7b75', '_']],
|
|
69
|
+
[['808080', ' '], ['727576', ','], ['4d595c', '▄'], ['514633', '▌'], ['97703b', '▄'], ['a67c42', '╥'], ['ae8145', '░'], ['b08345', '░'], ['b07f42', '░'], ['a17842', ']'], ['7c756b', '⌐ '], ['7c7772', "'"], ['a17743', 'U'], ['b98444', '░'], ['b98545', '»'], ['b98544', '»'], ['ba8443', '»'], ['b88243', '░'], ['af7e42', '░'], ['655d50', '▄'], ['6e7373', ',']],
|
|
70
|
+
[['808080', ' '], ['4e5e5d', '║'], ['1a4944', '▓'], ['1a3b32', '▓'], ['1d3222', '▓'], ['1f241c', '█'], ['282119', '█'], ['33251a', '█'], ['35241a', '█'], ['1e1a17', '█'], ['2a4a46', '▓'], ['707372', '⌐ '], ['6e7374', 'j'], ['233336', '▓'], ['34321f', '▓'], ['473622', '▓'], ['4a3f25', '▓'], ['3f3d25', '▓'], ['2f3822', '▓'], ['213723', '▓'], ['1e4234', '▓'], ['225747', '▓'], ['4b625b', '▒']],
|
|
71
|
+
[['808080', ' '], ['435a54', '╢'], ['215949', '▓'], ['235b49', '▓'], ['245d49', '▓'], ['235945', '▓'], ['1f5847', '▓'], ['295f46', '╣'], ['305d42', '╣'], ['2e5841', '╣'], ['2a4b43', '▓'], ['767877', '_ '], ['727676', "'"], ['26504f', '▓'], ['1c504a', '▓'], ['245d48', '▓'], ['255e48', '▓'], ['255d48', '▓'], ['215844', '▓'], ['245e48', '▓'], ['2b5e47', '╣'], ['33614a', '▀'], ['435a47', 'Ñ'], ['747470', '.']],
|
|
72
|
+
[['808080', ' '], ['3f585a', '╢'], ['19484c', '▓'], ['194748', '▓'], ['1c4b46', '▓'], ['4c5946', '╬'], ['485040', '╫'], ['5d5d47', 'Å'], ['555743', '╬'], ['51523f', '╬'], ['6a624a', '▒'], ['6d695c', 'H '], ['385655', '╫'], ['1a4e4a', '▓'], ['1c4f48', '▓'], ['1c4f48', '▓'], ['1b4d47', '▓'], ['2a4d43', '▓'], ['4f5844', '╢'], ['585542', '╢'], ['4e4b39', '╬'], ['645f47', '╠'], ['656150', '▒'], ['7a7977', '_']],
|
|
73
|
+
[['808080', ' '], ['6f7373', ','], ['25534c', '▓'], ['19494b', '▓'], ['1a4849', '▓'], ['1e4c4a', '▓'], ['3f483e', '╬'], ['354740', '╣'], ['405141', '╬'], ['4b5a45', '╠'], ['455642', '╣'], ['616149', '╩'], ['6d6955', 'Ü'], ['757471', '. '], ['425d5c', '╟'], ['194c4e', '▓'], ['19484b', '▓'], ['1e534a', '▓'], ['215649', '▓'], ['264d45', '▓'], ['3d4d40', '╬'], ['30433b', '▓'], ['47493f', '╬'], ['475242', '╣'], ['515844', '╬'], ['6c674f', 'Ü'], ['736f60', '░'], ['73726c', ','], ['7c7c7b', '_']],
|
|
74
|
+
[['808080', ' '], ['656963', '['], ['425f4e', '╠'], ['2d5147', '▓'], ['29584b', '▓'], ['275c49', '╣'], ['235b49', '▓'], ['215146', '▓'], ['2c4e44', '▓'], ['435441', '╬'], ['556049', '▄'], ['3d5d46', '╣'], ['3a5b43', '╣'], ['395c44', '╣'], ['4c6451', '▒'], ['6d736b', '╓ '], ['5c6359', '╚'], ['425a4a', '╬'], ['32564b', '▀'], ['2f5446', '▀'], ['2e5d49', '╣'], ['295e4a', '╣'], ['235d49', '╬'], ['235746', '▓'], ['1b4e47', '▓'], ['305346', '▌'], ['5d644a', '╠'], ['395f48', '╣'], ['316048', '╣'], ['2e5f46', '╣'], ['355f46', '╣'], ['4c6751', '▒'], ['6f736b', '┐']],
|
|
75
|
+
[['808080', ' '], ['787775', '`'], ['706e62', '╙'], ['6a6555', '╚'], ['65604d', '╠'], ['6b654e', '▒'], ['5e6a4e', '╚'], ['3d6047', '╬'], ['295b48', '╣'], ['19574a', '▓'], ['215647', '▓'], ['255a47', '▓'], ['265a47', '╣'], ['265946', '▓'], ['215749', '▓'], ['25544a', '▓'], ['5d6653', '▒ '], ['7a7a77', '`'], ['736f66', '²'], ['6c685b', '╚'], ['6d6756', 'Ü'], ['706954', 'Ü'], ['746c52', 'Ü'], ['787153', '░'], ['666e4f', '╙'], ['435f47', '╩'], ['3b5745', '▀'], ['2e5445', '▀'], ['2a5447', '▓'], ['2b5446', '▓'], ['2d5847', '╣'], ['315948', '▀'], ['3d5945', '▀'], ['6d6f4f', '╙'], ['787671', '-']],
|
|
76
|
+
[['808080', ' '], ['777673', '`'], ['787263', '!'], ['83775a', '░'], ['8e7d56', '░'], ['8f7f55', '└'], ['847a54', '┘'], ['787652', '┘'], ['797752', '┘'], ['827a54', '┘'], ['8c7d55', '│'], ['8e7e58', ';'], ['7e7660', '= '], ['797874', '`'], ['79756a', '^'], ['7e7661', '='], ['867b5f', '='], ['887b5b', '»'], ['867b5b', '░'], ['887c5b', '░'], ['8a7c59', '»'], ['8b7e5d', '='], ['877c63', '='], ['7c776c', "'"]],
|
|
77
|
+
[['808080', ' '], ['787775', '`'], ['7b7872', '`'], ['7d7a72', '`'], ['7d7a72', '`'], ['7b7973', '`'], ['797976', '`']],
|
|
78
|
+
];
|
|
79
|
+
// Print each line
|
|
80
|
+
for (const line of art) {
|
|
81
|
+
let output = '';
|
|
82
|
+
for (const [color, text] of line) {
|
|
83
|
+
output += hex(color)(text);
|
|
84
|
+
}
|
|
85
|
+
console.log(output);
|
|
86
|
+
}
|
|
87
|
+
console.log();
|
|
88
|
+
}
|
|
89
|
+
function getDownloadUrls(arcadeUrl) {
|
|
90
|
+
return {
|
|
91
|
+
windows: `${arcadeUrl}/downloads/crashplayer-windows-x64.zip`,
|
|
92
|
+
macos: `${arcadeUrl}/downloads/crashplayer-macos-universal.tar.gz`,
|
|
93
|
+
linux: `${arcadeUrl}/downloads/crashplayer-linux-x64.tar.gz`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function buildCrashPlayerArgs(options) {
|
|
97
|
+
const args = [];
|
|
98
|
+
if (options.windowSize) {
|
|
99
|
+
args.push(`--window-size ${options.windowSize}`);
|
|
100
|
+
}
|
|
101
|
+
if (options.fullscreen) {
|
|
102
|
+
args.push('--fullscreen');
|
|
103
|
+
}
|
|
104
|
+
if (options.debug) {
|
|
105
|
+
args.push('--debug');
|
|
106
|
+
}
|
|
107
|
+
if (options.escapeExit === false) {
|
|
108
|
+
args.push('--no-escape-exit');
|
|
109
|
+
}
|
|
110
|
+
return args.join(' ');
|
|
111
|
+
}
|
|
112
|
+
function detectPlatform() {
|
|
113
|
+
switch (process.platform) {
|
|
114
|
+
case 'win32':
|
|
115
|
+
return 'windows';
|
|
116
|
+
case 'darwin':
|
|
117
|
+
return 'macos';
|
|
118
|
+
default:
|
|
119
|
+
return 'linux';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function isAlreadyStamped(crashcartPath) {
|
|
123
|
+
try {
|
|
124
|
+
// Read the file and look for stamp markers
|
|
125
|
+
// Stamps typically include the arcade URL or license info
|
|
126
|
+
const buffer = readFileSync(crashcartPath);
|
|
127
|
+
const content = buffer.toString('utf8', 0, Math.min(buffer.length, 8192));
|
|
128
|
+
// Check for common stamp indicators
|
|
129
|
+
const stampMarkers = [
|
|
130
|
+
'arcade.crashbasic.com',
|
|
131
|
+
'CRASHBASIC_LICENSE',
|
|
132
|
+
'"license":',
|
|
133
|
+
'"stamped":',
|
|
134
|
+
'"tier":',
|
|
135
|
+
];
|
|
136
|
+
for (const marker of stampMarkers) {
|
|
137
|
+
if (content.includes(marker)) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Also check the last 1KB for appended stamp data
|
|
142
|
+
if (buffer.length > 1024) {
|
|
143
|
+
const tail = buffer.toString('utf8', buffer.length - 1024);
|
|
144
|
+
for (const marker of stampMarkers) {
|
|
145
|
+
if (tail.includes(marker)) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function downloadFile(url, destPath) {
|
|
157
|
+
const response = await fetch(url);
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
throw new Error(`Failed to download: ${response.status} ${response.statusText}`);
|
|
160
|
+
}
|
|
161
|
+
const fileStream = createWriteStream(destPath);
|
|
162
|
+
await pipeline(Readable.fromWeb(response.body), fileStream);
|
|
163
|
+
}
|
|
164
|
+
async function extractTarGz(tarPath, destDir) {
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
const extractor = extract();
|
|
167
|
+
const entries = [];
|
|
168
|
+
extractor.on('entry', (header, stream, next) => {
|
|
169
|
+
const chunks = [];
|
|
170
|
+
stream.on('data', (chunk) => chunks.push(chunk));
|
|
171
|
+
stream.on('end', () => {
|
|
172
|
+
entries.push({
|
|
173
|
+
name: header.name,
|
|
174
|
+
data: Buffer.concat(chunks),
|
|
175
|
+
mode: header.mode,
|
|
176
|
+
});
|
|
177
|
+
next();
|
|
178
|
+
});
|
|
179
|
+
stream.resume();
|
|
180
|
+
});
|
|
181
|
+
extractor.on('finish', () => {
|
|
182
|
+
for (const entry of entries) {
|
|
183
|
+
const fullPath = join(destDir, entry.name);
|
|
184
|
+
const dir = dirname(fullPath);
|
|
185
|
+
if (!existsSync(dir)) {
|
|
186
|
+
mkdirSync(dir, { recursive: true });
|
|
187
|
+
}
|
|
188
|
+
if (entry.data.length > 0) {
|
|
189
|
+
writeFileSync(fullPath, entry.data);
|
|
190
|
+
if (entry.mode) {
|
|
191
|
+
chmodSync(fullPath, entry.mode);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
resolve();
|
|
196
|
+
});
|
|
197
|
+
extractor.on('error', reject);
|
|
198
|
+
createReadStream(tarPath)
|
|
199
|
+
.pipe(createGunzip())
|
|
200
|
+
.pipe(extractor);
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
async function extractZip(zipPath, destDir) {
|
|
204
|
+
// Use unzip command on Unix or PowerShell on Windows
|
|
205
|
+
if (process.platform === 'win32') {
|
|
206
|
+
execSync(`powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`, {
|
|
207
|
+
stdio: 'inherit',
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
execSync(`unzip -o "${zipPath}" -d "${destDir}"`, { stdio: 'inherit' });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function runCommand(command, args) {
|
|
215
|
+
return new Promise((resolve, reject) => {
|
|
216
|
+
const proc = spawn(command, args, {
|
|
217
|
+
stdio: 'inherit',
|
|
218
|
+
shell: process.platform === 'win32',
|
|
219
|
+
});
|
|
220
|
+
proc.on('close', (code) => {
|
|
221
|
+
if (code === 0) {
|
|
222
|
+
resolve();
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
reject(new Error(`Command failed with code ${code}`));
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
proc.on('error', reject);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
function sanitizeName(name) {
|
|
232
|
+
return name
|
|
233
|
+
.toLowerCase()
|
|
234
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
235
|
+
.replace(/^-|-$/g, '');
|
|
236
|
+
}
|
|
237
|
+
function getAppleCredentials(options) {
|
|
238
|
+
const teamId = options.appleTeamId || process.env.APPLE_TEAM_ID;
|
|
239
|
+
const signingIdentity = options.signingIdentity || process.env.APPLE_SIGNING_IDENTITY;
|
|
240
|
+
const appleId = options.appleId || process.env.APPLE_ID;
|
|
241
|
+
const password = options.applePassword || process.env.APPLE_PASSWORD;
|
|
242
|
+
if (teamId && signingIdentity && appleId && password) {
|
|
243
|
+
return { teamId, signingIdentity, appleId, password };
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
function printMacOSSigningInstructions() {
|
|
248
|
+
console.log(chalk.yellow('\n ╭─────────────────────────────────────────────────────────────────╮'));
|
|
249
|
+
console.log(chalk.yellow(' │') + chalk.bold(' macOS Code Signing & Notarization ') + chalk.yellow('│'));
|
|
250
|
+
console.log(chalk.yellow(' ╰─────────────────────────────────────────────────────────────────╯\n'));
|
|
251
|
+
console.log(chalk.white(' Your app was created but is ') + chalk.bold.red('NOT signed or notarized') + chalk.white('.'));
|
|
252
|
+
console.log(chalk.white(' Users will see security warnings when trying to open it.\n'));
|
|
253
|
+
console.log(chalk.bold(' To enable code signing and notarization:\n'));
|
|
254
|
+
console.log(chalk.cyan(' 1. Join the Apple Developer Program'));
|
|
255
|
+
console.log(chalk.gray(' https://developer.apple.com/programs/\n'));
|
|
256
|
+
console.log(chalk.cyan(' 2. Create a Developer ID Application certificate'));
|
|
257
|
+
console.log(chalk.gray(' - Open Keychain Access on your Mac'));
|
|
258
|
+
console.log(chalk.gray(' - Go to developer.apple.com → Certificates'));
|
|
259
|
+
console.log(chalk.gray(' - Create a "Developer ID Application" certificate'));
|
|
260
|
+
console.log(chalk.gray(' - Download and install it in your keychain\n'));
|
|
261
|
+
console.log(chalk.cyan(' 3. Create an app-specific password'));
|
|
262
|
+
console.log(chalk.gray(' - Go to appleid.apple.com → Sign-In and Security'));
|
|
263
|
+
console.log(chalk.gray(' - Click "App-Specific Passwords" → Generate\n'));
|
|
264
|
+
console.log(chalk.cyan(' 4. Find your credentials:'));
|
|
265
|
+
console.log(chalk.gray(' - Team ID: developer.apple.com → Membership → Team ID'));
|
|
266
|
+
console.log(chalk.gray(' - Signing Identity: Run ') + chalk.white('security find-identity -v -p codesigning'));
|
|
267
|
+
console.log(chalk.gray(' Look for "Developer ID Application: Your Name (TEAM_ID)"\n'));
|
|
268
|
+
console.log(chalk.bold(' Then run the packager with these options:\n'));
|
|
269
|
+
console.log(chalk.white(' npx crashcart-packager mygame.crashcart "My Game" \\'));
|
|
270
|
+
console.log(chalk.white(' --apple-team-id "YOUR_TEAM_ID" \\'));
|
|
271
|
+
console.log(chalk.white(' --signing-identity "Developer ID Application: Your Name (TEAM_ID)" \\'));
|
|
272
|
+
console.log(chalk.white(' --apple-id "your@email.com" \\'));
|
|
273
|
+
console.log(chalk.white(' --apple-password "xxxx-xxxx-xxxx-xxxx"\n'));
|
|
274
|
+
console.log(chalk.bold(' Or set environment variables:\n'));
|
|
275
|
+
console.log(chalk.white(' export APPLE_TEAM_ID="YOUR_TEAM_ID"'));
|
|
276
|
+
console.log(chalk.white(' export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAM_ID)"'));
|
|
277
|
+
console.log(chalk.white(' export APPLE_ID="your@email.com"'));
|
|
278
|
+
console.log(chalk.white(' export APPLE_PASSWORD="xxxx-xxxx-xxxx-xxxx"\n'));
|
|
279
|
+
}
|
|
280
|
+
async function signAndNotarizeMacApp(appPath, bundleId, credentials) {
|
|
281
|
+
const spinner = ora('Code signing app bundle...').start();
|
|
282
|
+
try {
|
|
283
|
+
// Sign all executables and the app bundle
|
|
284
|
+
const macosDir = join(appPath, 'Contents', 'MacOS');
|
|
285
|
+
// Sign CrashPlayer first
|
|
286
|
+
const crashPlayerPath = join(macosDir, 'CrashPlayer');
|
|
287
|
+
if (existsSync(crashPlayerPath)) {
|
|
288
|
+
execSync(`codesign --force --options runtime --sign "${credentials.signingIdentity}" --timestamp "${crashPlayerPath}"`, { stdio: 'pipe' });
|
|
289
|
+
}
|
|
290
|
+
// Sign the launch script
|
|
291
|
+
const launchScript = join(macosDir, 'launch.sh');
|
|
292
|
+
if (existsSync(launchScript)) {
|
|
293
|
+
execSync(`codesign --force --options runtime --sign "${credentials.signingIdentity}" --timestamp "${launchScript}"`, { stdio: 'pipe' });
|
|
294
|
+
}
|
|
295
|
+
// Sign the entire app bundle
|
|
296
|
+
execSync(`codesign --force --deep --options runtime --sign "${credentials.signingIdentity}" --timestamp "${appPath}"`, { stdio: 'pipe' });
|
|
297
|
+
spinner.succeed('Code signing complete');
|
|
298
|
+
// Create ZIP for notarization
|
|
299
|
+
const zipSpinner = ora('Creating ZIP for notarization...').start();
|
|
300
|
+
const appName = basename(appPath);
|
|
301
|
+
const zipPath = `${appPath}.zip`;
|
|
302
|
+
execSync(`ditto -c -k --keepParent "${appPath}" "${zipPath}"`, { stdio: 'pipe' });
|
|
303
|
+
zipSpinner.succeed('ZIP created');
|
|
304
|
+
// Submit for notarization
|
|
305
|
+
const notarizeSpinner = ora('Submitting to Apple for notarization (this may take a few minutes)...').start();
|
|
306
|
+
try {
|
|
307
|
+
const result = execSync(`xcrun notarytool submit "${zipPath}" ` +
|
|
308
|
+
`--apple-id "${credentials.appleId}" ` +
|
|
309
|
+
`--password "${credentials.password}" ` +
|
|
310
|
+
`--team-id "${credentials.teamId}" ` +
|
|
311
|
+
`--wait`, { stdio: 'pipe', encoding: 'utf-8', timeout: 600000 } // 10 minute timeout
|
|
312
|
+
);
|
|
313
|
+
if (result.includes('status: Accepted')) {
|
|
314
|
+
notarizeSpinner.succeed('Notarization successful');
|
|
315
|
+
}
|
|
316
|
+
else if (result.includes('status: Invalid')) {
|
|
317
|
+
notarizeSpinner.fail('Notarization rejected by Apple');
|
|
318
|
+
console.log(chalk.yellow(' Run with --verbose to see details, or check:'));
|
|
319
|
+
console.log(chalk.gray(' xcrun notarytool log <submission-id> --apple-id ... --password ... --team-id ...'));
|
|
320
|
+
rmSync(zipPath);
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
catch (notaryError) {
|
|
325
|
+
notarizeSpinner.fail('Notarization failed');
|
|
326
|
+
console.error(chalk.red(' ' + (notaryError.message || 'Unknown error')));
|
|
327
|
+
rmSync(zipPath);
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
// Staple the notarization ticket
|
|
331
|
+
const stapleSpinner = ora('Stapling notarization ticket...').start();
|
|
332
|
+
try {
|
|
333
|
+
execSync(`xcrun stapler staple "${appPath}"`, { stdio: 'pipe' });
|
|
334
|
+
stapleSpinner.succeed('Notarization ticket stapled');
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
stapleSpinner.warn('Could not staple ticket (app is still notarized)');
|
|
338
|
+
}
|
|
339
|
+
// Clean up zip
|
|
340
|
+
rmSync(zipPath);
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
spinner.fail('Code signing failed');
|
|
345
|
+
console.error(chalk.red(' ' + (err.message || 'Unknown error')));
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async function packageWindows(crashcartPath, gameName, outputDir, downloadUrl, iconPath, playerOptions) {
|
|
350
|
+
const spinner = ora('Downloading CrashPlayer for Windows...').start();
|
|
351
|
+
try {
|
|
352
|
+
const tempZip = join(outputDir, 'crashplayer.zip');
|
|
353
|
+
await downloadFile(downloadUrl, tempZip);
|
|
354
|
+
spinner.text = 'Extracting CrashPlayer...';
|
|
355
|
+
await extractZip(tempZip, outputDir);
|
|
356
|
+
rmSync(tempZip);
|
|
357
|
+
// Copy crashcart
|
|
358
|
+
const cartName = basename(crashcartPath);
|
|
359
|
+
copyFileSync(crashcartPath, join(outputDir, cartName));
|
|
360
|
+
// Convert icon to ICO if provided
|
|
361
|
+
if (iconPath && existsSync(iconPath)) {
|
|
362
|
+
spinner.text = 'Converting icon to ICO...';
|
|
363
|
+
try {
|
|
364
|
+
const icoBuffer = await pngToIco(iconPath);
|
|
365
|
+
writeFileSync(join(outputDir, 'icon.ico'), icoBuffer);
|
|
366
|
+
}
|
|
367
|
+
catch (iconErr) {
|
|
368
|
+
console.warn(chalk.yellow('\nWarning: Could not convert icon to ICO. Skipping.'));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Build CrashPlayer arguments
|
|
372
|
+
const playerArgs = playerOptions ? buildCrashPlayerArgs(playerOptions) : '';
|
|
373
|
+
const fullArgs = playerArgs ? `${playerArgs} "${cartName}"` : `"${cartName}"`;
|
|
374
|
+
// Create .cmd launcher
|
|
375
|
+
const cmdContent = `@echo off\r\ncd /d "%~dp0"\r\nstart "" "CrashPlayer.exe" ${fullArgs}\r\n`;
|
|
376
|
+
writeFileSync(join(outputDir, `${gameName}.cmd`), cmdContent);
|
|
377
|
+
// Create README with launch instructions
|
|
378
|
+
const launchArgs = playerArgs ? `${playerArgs} "${cartName}"` : `"${cartName}"`;
|
|
379
|
+
const readme = `${gameName}
|
|
380
|
+
${'='.repeat(gameName.length)}
|
|
381
|
+
|
|
382
|
+
TO PLAY
|
|
383
|
+
-------
|
|
384
|
+
Double-click "${gameName}.cmd" to launch the game.
|
|
385
|
+
|
|
386
|
+
To create a desktop shortcut with your custom icon:
|
|
387
|
+
1. Right-click "${gameName}.cmd" > Create shortcut
|
|
388
|
+
2. Right-click the new shortcut > Properties
|
|
389
|
+
3. Click "Change Icon" > Browse > select "icon.ico"
|
|
390
|
+
4. Move the shortcut to your Desktop
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
DISTRIBUTION SETUP
|
|
394
|
+
------------------
|
|
395
|
+
|
|
396
|
+
STEAM:
|
|
397
|
+
1. Upload this folder to Steam
|
|
398
|
+
2. Set launch executable: CrashPlayer.exe
|
|
399
|
+
3. Set launch arguments: ${launchArgs}
|
|
400
|
+
|
|
401
|
+
ITCH.IO:
|
|
402
|
+
1. Zip this folder and upload
|
|
403
|
+
2. Set launch executable: CrashPlayer.exe
|
|
404
|
+
3. Set launch arguments: ${launchArgs}
|
|
405
|
+
|
|
406
|
+
GOG / OTHER STOREFRONTS:
|
|
407
|
+
Configure the launcher to run: CrashPlayer.exe ${launchArgs}
|
|
408
|
+
`;
|
|
409
|
+
writeFileSync(join(outputDir, 'README.txt'), readme.replace(/\n/g, '\r\n'));
|
|
410
|
+
spinner.succeed('Windows package created');
|
|
411
|
+
return outputDir;
|
|
412
|
+
}
|
|
413
|
+
catch (err) {
|
|
414
|
+
spinner.fail('Failed to create Windows package');
|
|
415
|
+
throw err;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
async function packageMacOS(crashcartPath, gameName, outputDir, downloadUrl, iconPath, appleCredentials, playerOptions) {
|
|
419
|
+
const spinner = ora('Creating macOS app bundle...').start();
|
|
420
|
+
try {
|
|
421
|
+
const appName = `${gameName}.app`;
|
|
422
|
+
const appDir = join(outputDir, appName);
|
|
423
|
+
const contentsDir = join(appDir, 'Contents');
|
|
424
|
+
const macosDir = join(contentsDir, 'MacOS');
|
|
425
|
+
const resourcesDir = join(contentsDir, 'Resources');
|
|
426
|
+
mkdirSync(macosDir, { recursive: true });
|
|
427
|
+
mkdirSync(resourcesDir, { recursive: true });
|
|
428
|
+
// Download and extract CrashPlayer
|
|
429
|
+
spinner.text = 'Downloading CrashPlayer for macOS...';
|
|
430
|
+
const tempTar = join(outputDir, 'crashplayer.tar.gz');
|
|
431
|
+
await downloadFile(downloadUrl, tempTar);
|
|
432
|
+
spinner.text = 'Extracting CrashPlayer...';
|
|
433
|
+
await extractTarGz(tempTar, macosDir);
|
|
434
|
+
rmSync(tempTar);
|
|
435
|
+
// Copy crashcart
|
|
436
|
+
const cartName = basename(crashcartPath);
|
|
437
|
+
copyFileSync(crashcartPath, join(resourcesDir, cartName));
|
|
438
|
+
// Build CrashPlayer arguments
|
|
439
|
+
const playerArgs = playerOptions ? buildCrashPlayerArgs(playerOptions) : '';
|
|
440
|
+
// Create launcher script
|
|
441
|
+
const launcherScript = `#!/bin/bash
|
|
442
|
+
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
443
|
+
"$DIR/CrashPlayer" ${playerArgs ? playerArgs + ' ' : ''}"$DIR/../Resources/${cartName}"
|
|
444
|
+
`;
|
|
445
|
+
writeFileSync(join(macosDir, 'launch.sh'), launcherScript);
|
|
446
|
+
chmodSync(join(macosDir, 'launch.sh'), 0o755);
|
|
447
|
+
// Create Info.plist
|
|
448
|
+
const bundleId = `com.crashbasic.${sanitizeName(gameName)}`;
|
|
449
|
+
const infoPlist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
450
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
451
|
+
<plist version="1.0">
|
|
452
|
+
<dict>
|
|
453
|
+
<key>CFBundleName</key>
|
|
454
|
+
<string>${gameName}</string>
|
|
455
|
+
<key>CFBundleDisplayName</key>
|
|
456
|
+
<string>${gameName}</string>
|
|
457
|
+
<key>CFBundleIdentifier</key>
|
|
458
|
+
<string>${bundleId}</string>
|
|
459
|
+
<key>CFBundleVersion</key>
|
|
460
|
+
<string>1.0</string>
|
|
461
|
+
<key>CFBundleShortVersionString</key>
|
|
462
|
+
<string>1.0</string>
|
|
463
|
+
<key>CFBundleExecutable</key>
|
|
464
|
+
<string>launch.sh</string>
|
|
465
|
+
<key>CFBundleIconFile</key>
|
|
466
|
+
<string>AppIcon</string>
|
|
467
|
+
<key>CFBundlePackageType</key>
|
|
468
|
+
<string>APPL</string>
|
|
469
|
+
<key>NSHighResolutionCapable</key>
|
|
470
|
+
<true/>
|
|
471
|
+
</dict>
|
|
472
|
+
</plist>
|
|
473
|
+
`;
|
|
474
|
+
writeFileSync(join(contentsDir, 'Info.plist'), infoPlist);
|
|
475
|
+
// Convert icon if provided and on macOS
|
|
476
|
+
if (iconPath && existsSync(iconPath) && process.platform === 'darwin') {
|
|
477
|
+
spinner.text = 'Converting icon...';
|
|
478
|
+
try {
|
|
479
|
+
const iconsetDir = join(resourcesDir, 'AppIcon.iconset');
|
|
480
|
+
mkdirSync(iconsetDir);
|
|
481
|
+
const sizes = [16, 32, 64, 128, 256, 512, 1024];
|
|
482
|
+
for (const size of sizes) {
|
|
483
|
+
execSync(`sips -z ${size} ${size} "${iconPath}" --out "${join(iconsetDir, `icon_${size}x${size}.png`)}"`, {
|
|
484
|
+
stdio: 'pipe',
|
|
485
|
+
});
|
|
486
|
+
if (size <= 512) {
|
|
487
|
+
execSync(`sips -z ${size * 2} ${size * 2} "${iconPath}" --out "${join(iconsetDir, `icon_${size}x${size}@2x.png`)}"`, { stdio: 'pipe' });
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
execSync(`iconutil -c icns "${iconsetDir}" -o "${join(resourcesDir, 'AppIcon.icns')}"`, { stdio: 'pipe' });
|
|
491
|
+
rmSync(iconsetDir, { recursive: true });
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
console.warn(chalk.yellow('\nWarning: Could not convert icon. Skipping.'));
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
else if (iconPath) {
|
|
498
|
+
console.warn(chalk.yellow('\nWarning: Icon conversion only works on macOS. Skipping.'));
|
|
499
|
+
}
|
|
500
|
+
spinner.succeed('macOS app bundle created');
|
|
501
|
+
// Sign and notarize if credentials provided and on macOS
|
|
502
|
+
let signed = false;
|
|
503
|
+
if (appleCredentials && process.platform === 'darwin') {
|
|
504
|
+
signed = await signAndNotarizeMacApp(appDir, bundleId, appleCredentials);
|
|
505
|
+
}
|
|
506
|
+
return { appPath: appDir, signed };
|
|
507
|
+
}
|
|
508
|
+
catch (err) {
|
|
509
|
+
spinner.fail('Failed to create macOS package');
|
|
510
|
+
throw err;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async function packageLinux(crashcartPath, gameName, outputDir, downloadUrl, iconPath, playerOptions) {
|
|
514
|
+
const spinner = ora('Downloading CrashPlayer for Linux...').start();
|
|
515
|
+
try {
|
|
516
|
+
const tempTar = join(outputDir, 'crashplayer.tar.gz');
|
|
517
|
+
await downloadFile(downloadUrl, tempTar);
|
|
518
|
+
spinner.text = 'Extracting CrashPlayer...';
|
|
519
|
+
await extractTarGz(tempTar, outputDir);
|
|
520
|
+
rmSync(tempTar);
|
|
521
|
+
// Copy crashcart
|
|
522
|
+
const cartName = basename(crashcartPath);
|
|
523
|
+
copyFileSync(crashcartPath, join(outputDir, cartName));
|
|
524
|
+
// Build CrashPlayer arguments
|
|
525
|
+
const playerArgs = playerOptions ? buildCrashPlayerArgs(playerOptions) : '';
|
|
526
|
+
// Create launcher script
|
|
527
|
+
const safeName = sanitizeName(gameName);
|
|
528
|
+
const launcherScript = `#!/bin/bash
|
|
529
|
+
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
530
|
+
"$DIR/CrashPlayer" ${playerArgs ? playerArgs + ' ' : ''}"$DIR/${cartName}"
|
|
531
|
+
`;
|
|
532
|
+
const launcherPath = join(outputDir, `${safeName}.sh`);
|
|
533
|
+
writeFileSync(launcherPath, launcherScript);
|
|
534
|
+
chmodSync(launcherPath, 0o755);
|
|
535
|
+
// Make CrashPlayer executable
|
|
536
|
+
const crashPlayerPath = join(outputDir, 'CrashPlayer');
|
|
537
|
+
if (existsSync(crashPlayerPath)) {
|
|
538
|
+
chmodSync(crashPlayerPath, 0o755);
|
|
539
|
+
}
|
|
540
|
+
// Copy icon if provided
|
|
541
|
+
if (iconPath && existsSync(iconPath)) {
|
|
542
|
+
copyFileSync(iconPath, join(outputDir, 'icon.png'));
|
|
543
|
+
}
|
|
544
|
+
// Create .desktop file for Linux desktop integration
|
|
545
|
+
const desktopEntry = `[Desktop Entry]
|
|
546
|
+
Type=Application
|
|
547
|
+
Name=${gameName}
|
|
548
|
+
Exec=${launcherPath}
|
|
549
|
+
Icon=${iconPath ? join(outputDir, 'icon.png') : ''}
|
|
550
|
+
Terminal=false
|
|
551
|
+
Categories=Game;
|
|
552
|
+
`;
|
|
553
|
+
writeFileSync(join(outputDir, `${safeName}.desktop`), desktopEntry);
|
|
554
|
+
spinner.succeed('Linux package created');
|
|
555
|
+
return outputDir;
|
|
556
|
+
}
|
|
557
|
+
catch (err) {
|
|
558
|
+
spinner.fail('Failed to create Linux package');
|
|
559
|
+
throw err;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function printUsage() {
|
|
563
|
+
console.log(chalk.bold.cyan('\n Crash BASIC Game Packager'));
|
|
564
|
+
console.log(chalk.gray(' Package your games for Windows, macOS, and Linux\n'));
|
|
565
|
+
console.log(chalk.bold(' Usage:'));
|
|
566
|
+
console.log(chalk.white(' npx crashcart-packager <crashcart> <game-name> [options]\n'));
|
|
567
|
+
console.log(chalk.bold(' Arguments:'));
|
|
568
|
+
console.log(chalk.cyan(' crashcart') + chalk.gray(' Path to the .crashcart file'));
|
|
569
|
+
console.log(chalk.cyan(' game-name') + chalk.gray(' Name of your game (used for output folder)\n'));
|
|
570
|
+
console.log(chalk.bold(' Packaging Options:'));
|
|
571
|
+
console.log(chalk.cyan(' -p, --platform') + chalk.gray(' Target platform (windows, macos, linux)'));
|
|
572
|
+
console.log(chalk.cyan(' -i, --icon') + chalk.gray(' Path to icon file (PNG recommended)'));
|
|
573
|
+
console.log(chalk.cyan(' -o, --output') + chalk.gray(' Output directory (default: ".")'));
|
|
574
|
+
console.log(chalk.cyan(' --all') + chalk.gray(' Build for all platforms'));
|
|
575
|
+
console.log(chalk.cyan(' --skip-stamp') + chalk.gray(' Skip license stamping\n'));
|
|
576
|
+
console.log(chalk.bold(' CrashPlayer Options:') + chalk.gray(' (baked into launcher)'));
|
|
577
|
+
console.log(chalk.cyan(' --window-size') + chalk.gray(' Force window size (e.g., 1280x720)'));
|
|
578
|
+
console.log(chalk.cyan(' -f, --fullscreen') + chalk.gray(' Start in fullscreen mode'));
|
|
579
|
+
console.log(chalk.cyan(' -d, --debug') + chalk.gray(' Enable debug mode'));
|
|
580
|
+
console.log(chalk.cyan(' --no-escape-exit') + chalk.gray(' Disable Escape key exit prompt\n'));
|
|
581
|
+
console.log(chalk.bold(' macOS Code Signing:'));
|
|
582
|
+
console.log(chalk.cyan(' --apple-team-id') + chalk.gray(' Apple Developer Team ID'));
|
|
583
|
+
console.log(chalk.cyan(' --signing-identity') + chalk.gray(' Code signing identity'));
|
|
584
|
+
console.log(chalk.cyan(' --apple-id') + chalk.gray(' Apple ID for notarization'));
|
|
585
|
+
console.log(chalk.cyan(' --apple-password') + chalk.gray(' App-specific password\n'));
|
|
586
|
+
console.log(chalk.bold(' Examples:'));
|
|
587
|
+
console.log(chalk.gray(' # Package for current platform'));
|
|
588
|
+
console.log(chalk.white(' npx crashcart-packager mygame.crashcart "My Awesome Game"\n'));
|
|
589
|
+
console.log(chalk.gray(' # Package fullscreen for all platforms'));
|
|
590
|
+
console.log(chalk.white(' npx crashcart-packager mygame.crashcart "My Game" --all --fullscreen\n'));
|
|
591
|
+
console.log(chalk.gray(' # Package with fixed window size'));
|
|
592
|
+
console.log(chalk.white(' npx crashcart-packager mygame.crashcart "My Game" --window-size 1280x720\n'));
|
|
593
|
+
// Show Crash-tan
|
|
594
|
+
printCrashTan();
|
|
595
|
+
console.log(chalk.gray(' Crash BASIC, Crash-tan, and Crash BASIC Arcade are'));
|
|
596
|
+
console.log(chalk.gray(' © 2026 Crash Continuum LLC. All rights reserved.\n'));
|
|
597
|
+
}
|
|
598
|
+
async function main() {
|
|
599
|
+
// Check if no arguments provided or help requested
|
|
600
|
+
const args = process.argv.slice(2);
|
|
601
|
+
if (args.length === 0 || args.includes('-h') || args.includes('--help')) {
|
|
602
|
+
printUsage();
|
|
603
|
+
process.exit(0);
|
|
604
|
+
}
|
|
605
|
+
program
|
|
606
|
+
.name('crashcart-packager')
|
|
607
|
+
.description('Package Crash BASIC games for distribution')
|
|
608
|
+
.version('1.0.0')
|
|
609
|
+
.argument('<crashcart>', 'Path to the .crashcart file')
|
|
610
|
+
.argument('<game-name>', 'Name of your game')
|
|
611
|
+
.option('-p, --platform <platform>', 'Target platform (windows, macos, linux)', detectPlatform())
|
|
612
|
+
.option('-i, --icon <path>', 'Path to icon file (PNG for best results)')
|
|
613
|
+
.option('-o, --output <dir>', 'Output directory', '.')
|
|
614
|
+
.option('--skip-stamp', 'Skip the stamping step (if already stamped)')
|
|
615
|
+
.option('--all', 'Build for all platforms')
|
|
616
|
+
.option('--arcade-url <url>', 'Arcade URL for downloads (default: https://arcade.crashbasic.com)')
|
|
617
|
+
// CrashPlayer options (passed through to launcher)
|
|
618
|
+
.option('--window-size <WxH>', 'Force window size (e.g., 1280x720)')
|
|
619
|
+
.option('-f, --fullscreen', 'Start in fullscreen mode')
|
|
620
|
+
.option('-d, --debug', 'Enable debug mode')
|
|
621
|
+
.option('--no-escape-exit', 'Disable Escape key exit prompt')
|
|
622
|
+
// macOS signing options
|
|
623
|
+
.option('--apple-team-id <id>', 'Apple Developer Team ID (or set APPLE_TEAM_ID env var)')
|
|
624
|
+
.option('--signing-identity <identity>', 'Code signing identity (or set APPLE_SIGNING_IDENTITY env var)')
|
|
625
|
+
.option('--apple-id <email>', 'Apple ID email for notarization (or set APPLE_ID env var)')
|
|
626
|
+
.option('--apple-password <password>', 'App-specific password for notarization (or set APPLE_PASSWORD env var)')
|
|
627
|
+
.configureOutput({
|
|
628
|
+
outputError: (str, write) => {
|
|
629
|
+
// Show custom help on error
|
|
630
|
+
printUsage();
|
|
631
|
+
console.log(chalk.red(` Error: ${str.replace('error: ', '')}`));
|
|
632
|
+
process.exit(1);
|
|
633
|
+
}
|
|
634
|
+
})
|
|
635
|
+
.parse();
|
|
636
|
+
const [crashcartPath, gameName] = program.args;
|
|
637
|
+
const options = program.opts();
|
|
638
|
+
const arcadeUrl = options.arcadeUrl || process.env.ARCADE_URL || DEFAULT_ARCADE_URL;
|
|
639
|
+
const downloads = getDownloadUrls(arcadeUrl);
|
|
640
|
+
// Show header
|
|
641
|
+
console.log();
|
|
642
|
+
printCrashTan();
|
|
643
|
+
console.log(chalk.bold.cyan(' Crash BASIC Game Packager'));
|
|
644
|
+
console.log(chalk.gray(' © 2026 Crash Continuum LLC\n'));
|
|
645
|
+
if (arcadeUrl !== DEFAULT_ARCADE_URL) {
|
|
646
|
+
console.log(chalk.yellow(` Using custom Arcade URL: ${arcadeUrl}\n`));
|
|
647
|
+
}
|
|
648
|
+
// Validate crashcart exists
|
|
649
|
+
if (!existsSync(crashcartPath)) {
|
|
650
|
+
console.error(chalk.red(`Error: CrashCart file not found: ${crashcartPath}`));
|
|
651
|
+
process.exit(1);
|
|
652
|
+
}
|
|
653
|
+
// Validate icon if provided
|
|
654
|
+
if (options.icon && !existsSync(options.icon)) {
|
|
655
|
+
console.error(chalk.red(`Error: Icon file not found: ${options.icon}`));
|
|
656
|
+
process.exit(1);
|
|
657
|
+
}
|
|
658
|
+
// Check for Apple credentials if building for macOS
|
|
659
|
+
const platforms = options.all
|
|
660
|
+
? ['windows', 'macos', 'linux']
|
|
661
|
+
: [options.platform];
|
|
662
|
+
const buildingForMacOS = platforms.includes('macos');
|
|
663
|
+
const appleCredentials = buildingForMacOS ? getAppleCredentials(options) : null;
|
|
664
|
+
// Stamp the crashcart first (unless skipped or already stamped)
|
|
665
|
+
if (!options.skipStamp) {
|
|
666
|
+
if (isAlreadyStamped(crashcartPath)) {
|
|
667
|
+
console.log(chalk.green('CrashCart already has a license stamp. Skipping stamping.\n'));
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
console.log(chalk.cyan('Stamping CrashCart with your license...\n'));
|
|
671
|
+
try {
|
|
672
|
+
await runCommand('npx', ['@crashcontinuum/crashcart-stamp', crashcartPath]);
|
|
673
|
+
console.log();
|
|
674
|
+
}
|
|
675
|
+
catch (err) {
|
|
676
|
+
console.error(chalk.red('Failed to stamp CrashCart. Make sure you are logged in.'));
|
|
677
|
+
process.exit(1);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
let macOSWasUnsigned = false;
|
|
682
|
+
// Build CrashPlayer options
|
|
683
|
+
const playerOptions = {
|
|
684
|
+
windowSize: options.windowSize,
|
|
685
|
+
fullscreen: options.fullscreen,
|
|
686
|
+
debug: options.debug,
|
|
687
|
+
escapeExit: options.escapeExit,
|
|
688
|
+
};
|
|
689
|
+
for (const platform of platforms) {
|
|
690
|
+
const safeName = sanitizeName(gameName);
|
|
691
|
+
const outputDir = join(options.output, `${safeName}-${platform}`);
|
|
692
|
+
// Clean and create output directory
|
|
693
|
+
if (existsSync(outputDir)) {
|
|
694
|
+
rmSync(outputDir, { recursive: true });
|
|
695
|
+
}
|
|
696
|
+
mkdirSync(outputDir, { recursive: true });
|
|
697
|
+
console.log(chalk.cyan(`\nPackaging for ${platform}...`));
|
|
698
|
+
try {
|
|
699
|
+
let result;
|
|
700
|
+
switch (platform) {
|
|
701
|
+
case 'windows':
|
|
702
|
+
result = await packageWindows(crashcartPath, gameName, outputDir, downloads.windows, options.icon, playerOptions);
|
|
703
|
+
console.log(chalk.green(` Output: ${result}`));
|
|
704
|
+
break;
|
|
705
|
+
case 'macos':
|
|
706
|
+
const macResult = await packageMacOS(crashcartPath, gameName, outputDir, downloads.macos, options.icon, appleCredentials, playerOptions);
|
|
707
|
+
result = macResult.appPath;
|
|
708
|
+
console.log(chalk.green(` Output: ${result}`));
|
|
709
|
+
if (!macResult.signed) {
|
|
710
|
+
macOSWasUnsigned = true;
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
console.log(chalk.green(' ✓ Signed and notarized'));
|
|
714
|
+
}
|
|
715
|
+
break;
|
|
716
|
+
case 'linux':
|
|
717
|
+
result = await packageLinux(crashcartPath, gameName, outputDir, downloads.linux, options.icon, playerOptions);
|
|
718
|
+
console.log(chalk.green(` Output: ${result}`));
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
catch (err) {
|
|
723
|
+
console.error(chalk.red(`Failed to package for ${platform}:`), err);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
console.log(chalk.bold.green('\nDone! Your game is ready for distribution.\n'));
|
|
727
|
+
// Show macOS signing instructions if the app wasn't signed
|
|
728
|
+
if (macOSWasUnsigned && process.platform === 'darwin') {
|
|
729
|
+
printMacOSSigningInstructions();
|
|
730
|
+
}
|
|
731
|
+
else if (macOSWasUnsigned) {
|
|
732
|
+
console.log(chalk.yellow('Note: macOS code signing requires building on a Mac with'));
|
|
733
|
+
console.log(chalk.yellow('Apple Developer credentials. See documentation for details.\n'));
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
main().catch((err) => {
|
|
737
|
+
console.error(chalk.red('Fatal error:'), err);
|
|
738
|
+
process.exit(1);
|
|
739
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@crashcontinuum/crashcart-packager",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Package Crash BASIC games for distribution on Windows, macOS, and Linux",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"crashcart-packager": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"crash-basic",
|
|
16
|
+
"crashbasic",
|
|
17
|
+
"crashcart",
|
|
18
|
+
"game",
|
|
19
|
+
"packager",
|
|
20
|
+
"game-distribution",
|
|
21
|
+
"electron-alternative",
|
|
22
|
+
"desktop-games",
|
|
23
|
+
"windows",
|
|
24
|
+
"macos",
|
|
25
|
+
"linux"
|
|
26
|
+
],
|
|
27
|
+
"author": {
|
|
28
|
+
"name": "Crash Continuum LLC",
|
|
29
|
+
"url": "https://crashcontinuum.com"
|
|
30
|
+
},
|
|
31
|
+
"license": "UNLICENSED",
|
|
32
|
+
"homepage": "https://arcade.crashbasic.com",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"chalk": "^5.3.0",
|
|
35
|
+
"commander": "^12.1.0",
|
|
36
|
+
"ora": "^8.0.1",
|
|
37
|
+
"png-to-ico": "^2.1.8",
|
|
38
|
+
"tar-stream": "^3.1.7"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.0.0",
|
|
42
|
+
"@types/tar-stream": "^3.1.3",
|
|
43
|
+
"typescript": "^5.0.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist"
|
|
50
|
+
],
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
}
|
|
54
|
+
}
|