├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ └── main.yaml
├── .gitignore
├── .npmignore
├── .nvmrc
├── .prettierignore
├── .prettierrc.yaml
├── LICENSE.txt
├── README.md
├── babel.config.mjs
├── eslint.config.mjs
├── launchers
├── download.mjs
└── manifest.json
├── package-lock.json
├── package.json
├── spec
├── asm
│ ├── linux
│ │ ├── menu-i386
│ │ │ ├── 10.0-menu-1-a.asm
│ │ │ ├── 10.0-menu-1-b.asm
│ │ │ ├── 10.0-menu-2-a.asm
│ │ │ ├── 10.0-menu-2-b.asm
│ │ │ ├── 10.0-widget-a.asm
│ │ │ ├── 10.0-widget-b.asm
│ │ │ ├── 10.1-menu-a.asm
│ │ │ ├── 10.1-menu-b.asm
│ │ │ ├── 10.1-widget-a.asm
│ │ │ ├── 10.1-widget-b.asm
│ │ │ ├── 11.0-menu-a.asm
│ │ │ ├── 11.0-menu-b.asm
│ │ │ ├── 11.0-widget-a.asm
│ │ │ ├── 11.0-widget-b.asm
│ │ │ ├── 11.2-menu-a.asm
│ │ │ ├── 11.2-menu-b.asm
│ │ │ ├── 11.2-widget-a.asm
│ │ │ ├── 11.2-widget-b.asm
│ │ │ ├── 6-widget-a.asm
│ │ │ ├── 6-widget-b.asm
│ │ │ ├── 9-menu-a.asm
│ │ │ ├── 9-menu-b.asm
│ │ │ ├── 9-widget-a.asm
│ │ │ └── 9-widget-b.asm
│ │ ├── menu-x86_64
│ │ │ ├── 24-menu-a.asm
│ │ │ ├── 24-menu-b.asm
│ │ │ ├── 24-widget-a.asm
│ │ │ ├── 24-widget-b.asm
│ │ │ ├── 32-menu-a.asm
│ │ │ ├── 32-menu-b.asm
│ │ │ ├── 32-widget-a.asm
│ │ │ └── 32-widget-b.asm
│ │ ├── offset-x86_64
│ │ │ ├── 24-a.asm
│ │ │ ├── 24-b.asm
│ │ │ ├── 25-a.asm
│ │ │ ├── 25-b.asm
│ │ │ ├── 32-a.asm
│ │ │ └── 32-b.asm
│ │ ├── patch-i386
│ │ │ └── ebx.asm
│ │ ├── path-i386
│ │ │ ├── 10.0.asm
│ │ │ ├── 10.1.asm
│ │ │ ├── 11.0.asm
│ │ │ ├── 11.2.asm
│ │ │ ├── 6.asm
│ │ │ └── 9.asm
│ │ ├── path-x86_64
│ │ │ └── 24.asm
│ │ ├── title-i386
│ │ │ ├── 10.1.asm
│ │ │ ├── 11.2.asm
│ │ │ ├── 6.asm
│ │ │ └── 9.asm
│ │ └── title-x86_64
│ │ │ └── 24.asm
│ ├── mac
│ │ ├── title-arm64
│ │ │ ├── 35-debug.asm
│ │ │ └── 35-release.asm
│ │ ├── title-i386
│ │ │ ├── 11.asm
│ │ │ ├── 13.asm
│ │ │ └── 23.asm
│ │ └── title-x86_64
│ │ │ ├── 11-1.asm
│ │ │ ├── 11-2.asm
│ │ │ ├── 13.asm
│ │ │ ├── 23.asm
│ │ │ └── 35.asm
│ └── windows
│ │ ├── ood-i386
│ │ ├── 30.asm
│ │ ├── 31.asm
│ │ └── ret4.asm
│ │ └── ood-x86_64
│ │ ├── 26.asm
│ │ ├── 30.asm
│ │ └── ret.asm
└── fixtures
│ ├── Info.plist
│ ├── PkgInfo
│ ├── dummy
│ ├── dummy.app
│ └── Contents
│ │ ├── Info.plist
│ │ ├── MacOS
│ │ └── main
│ │ ├── PkgInfo
│ │ └── Resources
│ │ └── icon.icns
│ ├── dummy.exe
│ ├── dummy.exe.zip
│ ├── icon-to-icns.sh
│ ├── icon-to-ico.sh
│ ├── icon.icns
│ ├── icon.ico
│ ├── icon.png
│ ├── image.jpg
│ ├── image.png
│ ├── image.swf
│ ├── swf14-lzma.swf
│ ├── swf14-zlib.swf
│ ├── swf14.swf
│ ├── swf3.swf
│ ├── swf4-loadmovie.as
│ ├── swf4-loadmovie.sh
│ ├── swf4-loadmovie.swf
│ ├── swf6-loadmovie.as
│ ├── swf6-loadmovie.sh
│ ├── swf6-loadmovie.swf
│ ├── swf6-showmenu-false.as
│ ├── swf6-showmenu-false.sh
│ ├── swf6-showmenu-false.swf
│ ├── swf6-zlib.swf
│ └── swf6.swf
├── src
├── bundle.ts
├── bundle
│ ├── html.test.ts
│ ├── html.ts
│ ├── index.ts
│ ├── sa.spec.ts
│ ├── sa.test.ts
│ ├── sa.ts
│ └── sa
│ │ ├── index.ts
│ │ ├── linux.test.ts
│ │ ├── linux.ts
│ │ ├── mac.test.ts
│ │ ├── mac.ts
│ │ ├── windows.test.ts
│ │ └── windows.ts
├── index.ts
├── launchers.ts
├── loader.test.ts
├── loader.ts
├── meta.test.ts
├── meta.ts
├── projector.ts
├── projector
│ ├── html.test.ts
│ ├── html.ts
│ ├── index.ts
│ ├── sa.spec.ts
│ ├── sa.test.ts
│ ├── sa.ts
│ └── sa
│ │ ├── index.ts
│ │ ├── linux.spec.ts
│ │ ├── linux.test.ts
│ │ ├── linux.ts
│ │ ├── mac.spec.ts
│ │ ├── mac.test.ts
│ │ ├── mac.ts
│ │ ├── windows.spec.ts
│ │ ├── windows.test.ts
│ │ └── windows.ts
├── queue.test.ts
├── queue.ts
├── swf
│ ├── data.ts
│ ├── fixed8.ts
│ ├── rect.ts
│ ├── swf.ts
│ ├── tag.ts
│ └── util.ts
├── util.spec.ts
├── util.test.ts
├── util.ts
└── util
│ ├── index.ts
│ ├── internal
│ ├── data.ts
│ ├── linux
│ │ ├── asm.ts
│ │ ├── elf.ts
│ │ ├── menu.ts
│ │ ├── menu32.ts
│ │ ├── menu64.ts
│ │ ├── offset64.ts
│ │ ├── patch.ts
│ │ ├── path.ts
│ │ ├── path32.ts
│ │ ├── path64.ts
│ │ ├── title.ts
│ │ ├── title32.ts
│ │ └── title64.ts
│ ├── mac
│ │ ├── asm.ts
│ │ ├── constants.ts
│ │ └── title.ts
│ ├── patch.ts
│ └── windows
│ │ ├── asm.ts
│ │ ├── constants.ts
│ │ ├── exe.ts
│ │ ├── ood32.ts
│ │ ├── ood64.ts
│ │ ├── rsrc.ts
│ │ └── title.ts
│ ├── linux.ts
│ ├── mac.test.ts
│ ├── mac.ts
│ ├── windows.test.ts
│ └── windows.ts
├── tsconfig.eslint.json
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | trim_trailing_whitespace = true
7 | indent_style = tab
8 | indent_size = 4
9 | charset = utf8
10 |
11 | [*.{yml,yaml}]
12 | indent_style = space
13 | indent_size = 2
14 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 | *.bin binary
3 | spec/fixtures/dummy binary
4 | spec/fixtures/dummy.exe binary
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS files
2 | .DS_Store
3 | .DS_Store?
4 | ._*
5 | .Spotlight-V100
6 | .Trashes
7 | Icon?
8 | ehthumbs.db
9 | Thumbs.db
10 |
11 | # Node files
12 | node_modules
13 | npm-debug.log*
14 | yarn-debug.log*
15 | yarn-error.log*
16 | report.*.json
17 | *.tgz
18 | yarn.lock
19 |
20 | # IDE config
21 | .vscode
22 |
23 | # Launcher binaries
24 | /launchers/data
25 |
26 | # Build files
27 | /dts
28 | /esm
29 | /cjs
30 |
31 | # Test files
32 | /shockpkg
33 | /spec/projectors
34 | /spec/bundles
35 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # OS files
2 | .DS_Store
3 | .DS_Store?
4 | ._*
5 | .Spotlight-V100
6 | .Trashes
7 | Icon?
8 | ehthumbs.db
9 | Thumbs.db
10 |
11 | # Node files
12 | node_modules
13 | npm-debug.log*
14 | yarn-debug.log*
15 | yarn-error.log*
16 | report.*.json
17 | *.tgz
18 | yarn.lock
19 | .nvmrc
20 |
21 | # IDE config
22 | .vscode
23 |
24 | /package-lock.json
25 | /.gitattributes
26 | /.gitignore
27 | /.github
28 | /.editorconfig
29 | /.prettierrc.yaml
30 | /.prettierignore
31 | /babel.config.mjs
32 | /eslint.config.mjs
33 | /tsconfig.json
34 | /tsconfig.eslint.json
35 | /spec
36 | /src
37 |
38 | /dts/**/*.test.d.ts
39 | /esm/**/*.test.mjs
40 | /esm/**/*.test.mjs.map
41 | /cjs/**/*.test.js
42 | /cjs/**/*.test.js.map
43 |
44 | /dts/**/*.spec.d.ts
45 | /esm/**/*.spec.mjs
46 | /esm/**/*.spec.mjs.map
47 | /cjs/**/*.spec.js
48 | /cjs/**/*.spec.js.map
49 |
50 | /launchers
51 | /shockpkg
52 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 20.16.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | /package-lock.json
2 | /dts
3 | /esm
4 | /cjs
5 | /spec/fixtures
6 | /spec/projectors
7 | /spec/bundles
8 | /shockpkg
9 |
--------------------------------------------------------------------------------
/.prettierrc.yaml:
--------------------------------------------------------------------------------
1 | tabWidth: 4
2 | printWidth: 80
3 | useTabs: true
4 | semi: true
5 | singleQuote: true
6 | quoteProps: as-needed
7 | trailingComma: none
8 | bracketSpacing: false
9 | arrowParens: avoid
10 | overrides:
11 | - files:
12 | - '*.yaml'
13 | - '*.yml'
14 | options:
15 | tabWidth: 2
16 | useTabs: false
17 |
--------------------------------------------------------------------------------
/babel.config.mjs:
--------------------------------------------------------------------------------
1 | import {readFileSync, readdirSync} from 'node:fs';
2 | import {deflateRawSync} from 'node:zlib';
3 |
4 | const {name, version, engines} = JSON.parse(readFileSync('./package.json'));
5 |
6 | const node = engines.node
7 | .split(/[^\d.]+/)
8 | .filter(s => s.length)
9 | .map(s => [...s.split('.').map(s => +s || 0), 0, 0].slice(0, 3))
10 | .sort((a, b) => a[2] - b[2])
11 | .sort((a, b) => a[1] - b[1])
12 | .sort((a, b) => a[0] - b[0])[0]
13 | .join('.');
14 |
15 | const launchers = (manifest => {
16 | const r = {};
17 | for (const {name, file} of manifest) {
18 | r[name] = deflateRawSync(readFileSync(`launchers/data/${file}`), {
19 | level: 9
20 | }).toString('base64');
21 | }
22 | return r;
23 | })(JSON.parse(readFileSync('./launchers/manifest.json')));
24 |
25 | const asms = (dirs => {
26 | const parse = s => {
27 | const b = [];
28 | for (const line of s.split(/[\n\r]+/)) {
29 | const [s] = line.split(/[#;]/)[0].trim().split(' ');
30 | if (!s) {
31 | continue;
32 | }
33 | for (const h of s.split(' ')) {
34 | // eslint-disable-next-line unicorn/prefer-number-properties
35 | b.push(/^[\da-f]{2}$/i.test(h) ? parseInt(h, 16) : -1);
36 | }
37 | }
38 | return b;
39 | };
40 | const r = [];
41 | for (const dir of dirs) {
42 | const d = {};
43 | for (const f of readdirSync(dir).sort()) {
44 | const m = f.match(/^([^.].*)\.asm$/);
45 | if (m) {
46 | d[m[1]] = parse(readFileSync(`${dir}/${f}`, 'utf8'));
47 | }
48 | }
49 | r.push({
50 | search: `#{${dir}}`,
51 | replace: d
52 | });
53 | }
54 | return r;
55 | })([
56 | 'spec/asm/linux/menu-i386',
57 | 'spec/asm/linux/menu-x86_64',
58 | 'spec/asm/linux/offset-x86_64',
59 | 'spec/asm/linux/patch-i386',
60 | 'spec/asm/linux/path-i386',
61 | 'spec/asm/linux/path-x86_64',
62 | 'spec/asm/linux/title-i386',
63 | 'spec/asm/linux/title-x86_64',
64 | 'spec/asm/mac/title-i386',
65 | 'spec/asm/mac/title-x86_64',
66 | 'spec/asm/mac/title-arm64',
67 | 'spec/asm/windows/ood-i386',
68 | 'spec/asm/windows/ood-x86_64'
69 | ]);
70 |
71 | export default api => {
72 | const env = api.env();
73 | api.cache(() => env);
74 | const modules = env === 'esm' ? false : 'commonjs';
75 | const ext = modules ? '.js' : '.mjs';
76 | const presets = [];
77 | const plugins = [];
78 | presets.push(
79 | [
80 | '@babel/preset-env',
81 | {
82 | modules,
83 | exclude: ['proposal-dynamic-import'],
84 | targets: {
85 | node
86 | }
87 | }
88 | ],
89 | ['@babel/preset-typescript']
90 | );
91 | plugins.push(
92 | [
93 | 'module-replace',
94 | {
95 | replace: [[/^(\.\.?\/.+)\.(m|c)?tsx?$/i, `$1${ext}`]]
96 | }
97 | ],
98 | [
99 | 'search-and-replace',
100 | {
101 | rules: [
102 | {
103 | search: '#{NAME}',
104 | replace: name
105 | },
106 | {
107 | search: '#{VERSION}',
108 | replace: version
109 | },
110 | {
111 | search: '#{LAUNCHERS}',
112 | replace: launchers
113 | },
114 | ...asms
115 | ]
116 | }
117 | ]
118 | );
119 | if (modules === 'commonjs') {
120 | plugins.push([
121 | '@babel/plugin-transform-modules-commonjs',
122 | {
123 | importInterop: 'node'
124 | }
125 | ]);
126 | }
127 | return {
128 | presets,
129 | plugins
130 | };
131 | };
132 |
--------------------------------------------------------------------------------
/launchers/download.mjs:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsdoc/require-jsdoc */
2 | import {fileURLToPath} from 'node:url';
3 | import {join as pathJoin} from 'node:path';
4 | import {mkdir, readFile, rm, writeFile} from 'node:fs/promises';
5 | import {createHash} from 'node:crypto';
6 |
7 | const DIR = fileURLToPath(new URL('.', import.meta.url));
8 | const OUT = pathJoin(DIR, 'data');
9 | const MANIFEST = pathJoin(DIR, 'manifest.json');
10 |
11 | function sha256(data) {
12 | return createHash('sha256').update(data).digest('hex');
13 | }
14 |
15 | async function ensure({file, url, hash}) {
16 | const out = pathJoin(OUT, file);
17 | {
18 | const existing = await readFile(out).catch(() => null);
19 | if (existing) {
20 | if (sha256(await readFile(out)) === hash) {
21 | return;
22 | }
23 | await rm(out, {force: true});
24 | }
25 | }
26 | const response = await fetch(url);
27 | if (response.status !== 200) {
28 | throw new Error(`Status ${response.status}: ${url}`);
29 | }
30 | const body = new Uint8Array(await response.arrayBuffer());
31 | {
32 | const hashed = sha256(body);
33 | if (hashed !== hash) {
34 | throw new Error(`Invalid hash: ${hashed}`);
35 | }
36 | }
37 | await writeFile(out, body);
38 | }
39 |
40 | async function main() {
41 | const manifest = JSON.parse(await readFile(MANIFEST, 'utf8'));
42 | await mkdir(OUT, {recursive: true});
43 | await Promise.all(manifest.map(ensure));
44 | }
45 | main().catch(err => {
46 | // eslint-disable-next-line no-console
47 | console.error(err);
48 | process.exitCode = 1;
49 | });
50 |
--------------------------------------------------------------------------------
/launchers/manifest.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "windows-i686",
4 | "url": "https://github.com/shockpkg/projector-launcher-windows/releases/download/1.0.0/main.i686.exe",
5 | "file": "windows-i686.exe",
6 | "hash": "166e5cb9228842e98e59d0cae1578fd0d97c9754944dae6533678716f7fd1c1c"
7 | },
8 | {
9 | "name": "windows-x86_64",
10 | "url": "https://github.com/shockpkg/projector-launcher-windows/releases/download/1.0.0/main.x86_64.exe",
11 | "file": "windows-x86_64.exe",
12 | "hash": "6a8e15452b1049ed9727eee65e1f8c81a6ff496f7e452c75268e2c3193dd61b1"
13 | },
14 | {
15 | "name": "mac-app-ppc",
16 | "url": "https://github.com/shockpkg/projector-launcher-mac-app/releases/download/1.1.0/main.ppc",
17 | "file": "mac-app-ppc",
18 | "hash": "17414c123fe82ac74a89fad9c80e36d8b612ded5a520e35f3c33eabe75a023a7"
19 | },
20 | {
21 | "name": "mac-app-ppc64",
22 | "url": "https://github.com/shockpkg/projector-launcher-mac-app/releases/download/1.1.0/main.ppc64",
23 | "file": "mac-app-ppc64",
24 | "hash": "9e159161fc21b72de6fddb5fb9c60c0e34e649e4660248778219e58198adfb3d"
25 | },
26 | {
27 | "name": "mac-app-i386",
28 | "url": "https://github.com/shockpkg/projector-launcher-mac-app/releases/download/1.1.0/main.i386",
29 | "file": "mac-app-i386",
30 | "hash": "e52e19fce336130824dcfd4731bf85db7e8e96628ef8c6a49769dc5247ef6ed0"
31 | },
32 | {
33 | "name": "mac-app-x86_64",
34 | "url": "https://github.com/shockpkg/projector-launcher-mac-app/releases/download/1.1.0/main.x86_64",
35 | "file": "mac-app-x86_64",
36 | "hash": "f5b7625da819324f442cea1f3af83ea4b2bf0af1d185a7747d81b698a6168562"
37 | },
38 | {
39 | "name": "mac-app-arm64",
40 | "url": "https://github.com/shockpkg/projector-launcher-mac-app/releases/download/1.1.0/main.arm64",
41 | "file": "mac-app-arm64",
42 | "hash": "a20cbe0ffd3c3ad6b5ee2a58af81ea33145903847785382e7ef3424f08556312"
43 | },
44 | {
45 | "name": "linux-i386",
46 | "url": "https://github.com/shockpkg/projector-launcher-linux/releases/download/2.0.0/main.i386",
47 | "file": "linux-i386",
48 | "hash": "5bc49257a1bbe5f86a0068de0a783c669f10f88d44650e9451d3a1926277ab4c"
49 | },
50 | {
51 | "name": "linux-x86_64",
52 | "url": "https://github.com/shockpkg/projector-launcher-linux/releases/download/2.0.0/main.x86_64",
53 | "file": "linux-x86_64",
54 | "hash": "4d5ae1aca3cee75732be68eec6136d5fe64c4648973123c98bb0f65492825199"
55 | }
56 | ]
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@shockpkg/swf-projector",
3 | "description": "Package for creating Flash Player projectors",
4 | "version": "6.0.9",
5 | "keywords": [
6 | "shockpkg",
7 | "flash",
8 | "animate",
9 | "swf",
10 | "projector"
11 | ],
12 | "main": "cjs/index",
13 | "exports": {
14 | ".": [
15 | {
16 | "types": "./dts/index.d.ts",
17 | "import": "./esm/index.mjs",
18 | "require": "./cjs/index.js",
19 | "default": "./cjs/index.js"
20 | },
21 | "./cjs/index.js"
22 | ]
23 | },
24 | "types": "dts/index.d.ts",
25 | "module": "esm/index.mjs",
26 | "sideEffects": false,
27 | "engines": {
28 | "node": ">=18.12.0"
29 | },
30 | "scripts": {
31 | "shockpkg": "shockpkg",
32 | "launchers": "node launchers/download.mjs",
33 | "clean": "rimraf dts esm cjs spec/bundles spec/projectors",
34 | "lint": "eslint .",
35 | "format": "prettier -w .",
36 | "formatted": "prettier -c .",
37 | "build:dts": "tsc",
38 | "build:esm": "babel --env-name esm -x .ts -s true -d esm --out-file-extension .mjs src",
39 | "build:cjs": "babel --env-name cjs -x .ts -s true -d cjs --out-file-extension .js src",
40 | "build": "npm run build:dts && npm run build:esm && npm run build:cjs",
41 | "test-legacy:esm": "node -r source-map-support/register --test esm",
42 | "test-legacy:cjs": "node -r source-map-support/register --test cjs",
43 | "test-legacy": "npm run test-legacy:esm && npm run test-legacy:cjs",
44 | "test:esm": "node -r source-map-support/register --test 'esm/**/*.test.mjs'",
45 | "test:cjs": "node -r source-map-support/register --test 'cjs/**/*.test.js'",
46 | "test": "npm run test:esm && npm run test:cjs",
47 | "all-legacy:esm": "npm run clean && npm run build:esm && npm run test-legacy:esm && npm run lint && npm run formatted",
48 | "all-legacy:cjs": "npm run clean && npm run build:cjs && npm run test-legacy:cjs && npm run lint && npm run formatted",
49 | "all-legacy": "npm run clean && npm run build && npm run test-legacy && npm run lint && npm run formatted",
50 | "all:esm": "npm run clean && npm run build:esm && npm run test:esm && npm run lint && npm run formatted",
51 | "all:cjs": "npm run clean && npm run build:cjs && npm run test:cjs && npm run lint && npm run formatted",
52 | "all": "npm run clean && npm run build && npm run test && npm run lint && npm run formatted",
53 | "prepack": "npm run launchers && npm run clean && npm run build"
54 | },
55 | "repository": "https://github.com/shockpkg/swf-projector.git",
56 | "bugs": "https://github.com/shockpkg/swf-projector/issues",
57 | "author": "JrMasterModelBuilder",
58 | "copyright": "Copyright (c) 2019-2024 JrMasterModelBuilder",
59 | "license": "MPL-2.0",
60 | "dependencies": {
61 | "@shockpkg/archive-files": "^3.2.5",
62 | "@shockpkg/plist-dom": "^4.0.7",
63 | "@shockpkg/resedit": "^2.0.2",
64 | "macho-unsign": "^2.0.6",
65 | "portable-executable-signature": "^2.0.6"
66 | },
67 | "devDependencies": {
68 | "@babel/cli": "^7.24.8",
69 | "@babel/core": "^7.25.2",
70 | "@babel/preset-env": "^7.25.3",
71 | "@babel/preset-typescript": "^7.24.7",
72 | "@eslint/js": "^9.8.0",
73 | "@shockpkg/cli": "^3.0.9",
74 | "@shockpkg/core": "^3.0.8",
75 | "@stylistic/eslint-plugin": "^2.6.1",
76 | "@types/node": "^22.1.0",
77 | "babel-plugin-module-replace": "^1.0.1",
78 | "babel-plugin-search-and-replace": "^1.1.1",
79 | "eslint": "^9.8.0",
80 | "eslint-config-prettier": "^9.1.0",
81 | "eslint-plugin-jsdoc": "^48.11.0",
82 | "eslint-plugin-unicorn": "^55.0.0",
83 | "prettier": "^3.3.3",
84 | "rimraf": "^6.0.1",
85 | "source-map-support": "^0.5.21",
86 | "typescript": "^5.5.4",
87 | "typescript-eslint": "^8.0.1"
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/10.0-menu-1-a.asm:
--------------------------------------------------------------------------------
1 | 89 04 24 mov DWORD PTR [esp], eax
2 | E8 -- -- -- -- call ...
3 | C7 44 24 08 03 00 00 00 mov DWORD PTR [esp+0x8], 0x3
4 | 89 74 24 04 mov DWORD PTR [esp+0x4], esi
5 | 89 04 24 mov DWORD PTR [esp], eax
6 | E8 -- -- -- -- call _gtk_menu_shell_insert
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/10.0-menu-1-b.asm:
--------------------------------------------------------------------------------
1 | 89 04 24 mov DWORD PTR [esp], eax
2 | E8 -- -- -- -- call ...
3 | C7 44 24 08 03 00 00 00 mov DWORD PTR [esp+0x8], 0x3
4 | 89 74 24 04 mov DWORD PTR [esp+0x4], esi
5 | 89 04 24 mov DWORD PTR [esp], eax
6 | 90 90 90 90 90 nop 5
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/10.0-menu-2-a.asm:
--------------------------------------------------------------------------------
1 | 8B 45 0C mov eax, DWORD PTR [ebp+0xC]
2 | 89 04 24 mov DWORD PTR [esp], eax
3 | E8 -- -- -- -- call ...
4 | C7 44 24 08 03 00 00 00 mov DWORD PTR [esp+0x8], 0x3
5 | 89 5C 24 04 mov DWORD PTR [esp+0x4], ebx
6 | 89 04 24 mov DWORD PTR [esp], eax
7 | E8 -- -- -- -- call _gtk_menu_shell_insert
8 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/10.0-menu-2-b.asm:
--------------------------------------------------------------------------------
1 | 8B 45 0C mov eax, DWORD PTR [ebp+0xC]
2 | 89 04 24 mov DWORD PTR [esp], eax
3 | E8 -- -- -- -- call ...
4 | C7 44 24 08 03 00 00 00 mov DWORD PTR [esp+0x8], 0x3
5 | 89 5C 24 04 mov DWORD PTR [esp+0x4], ebx
6 | 89 04 24 mov DWORD PTR [esp], eax
7 | 90 90 90 90 90 nop 5
8 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/10.0-widget-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 89 86 -- 02 00 00 mov DWORD PTR [esi+...], eax
3 | 89 04 24 mov DWORD PTR [esp], eax
4 | E8 -- -- -- -- call _gtk_widget_show
5 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/10.0-widget-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 89 86 -- 02 00 00 mov DWORD PTR [esi+...], eax
3 | 89 04 24 mov DWORD PTR [esp], eax
4 | 90 90 90 90 90 nop 5
5 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/10.1-menu-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 10 mov edx, DWORD PTR [ebp+0x10]
3 | 8B 4D -- mov ecx, DWORD PTR [ebp-...]
4 | 89 54 24 08 mov DWORD PTR [esp+0x8], edx
5 | 89 4C 24 04 mov DWORD PTR [esp+0x4], ecx
6 | 89 04 24 mov DWORD PTR [esp], eax
7 | E8 -- -- -- -- call _gtk_menu_shell_insert
8 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/10.1-menu-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 10 mov edx, DWORD PTR [ebp+0x10]
3 | 8B 4D -- mov ecx, DWORD PTR [ebp-...]
4 | 89 54 24 08 mov DWORD PTR [esp+0x8], edx
5 | 89 4C 24 04 mov DWORD PTR [esp+0x4], ecx
6 | 89 04 24 mov DWORD PTR [esp], eax
7 | 90 90 90 90 90 nop 5
8 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/10.1-widget-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 DC mov edx, DWORD PTR [ebp-0x24]
3 | 8B 42 60 mov eax, DWORD PTR [edx+0x60]
4 | 89 04 24 mov DWORD PTR [esp], eax
5 | E8 -- -- -- -- call _gtk_widget_show
6 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/10.1-widget-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 DC mov edx, DWORD PTR [ebp-0x24]
3 | 8B 42 60 mov eax, DWORD PTR [edx+0x60]
4 | 89 04 24 mov DWORD PTR [esp], eax
5 | 90 90 90 90 90 nop 5
6 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/11.0-menu-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 14 mov edx, DWORD PTR [ebp+0x14]
3 | 89 74 24 04 mov DWORD PTR [esp+0x4], esi
4 | 89 54 24 08 mov DWORD PTR [esp+0x8], edx
5 | 89 04 24 mov DWORD PTR [esp], eax
6 | E8 -- -- -- -- call _gtk_menu_shell_insert
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/11.0-menu-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 14 mov edx, DWORD PTR [ebp+0x14]
3 | 89 74 24 04 mov DWORD PTR [esp+0x4], esi
4 | 89 54 24 08 mov DWORD PTR [esp+0x8], edx
5 | 89 04 24 mov DWORD PTR [esp], eax
6 | 90 90 90 90 90 nop 5
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/11.0-widget-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 D4 mov edx, DWORD PTR [ebp-0x2C]
3 | 8B 42 60 mov eax, DWORD PTR [edx+0x60]
4 | 89 04 24 mov DWORD PTR [esp], eax
5 | E8 -- -- -- -- call _gtk_widget_show
6 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/11.0-widget-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 D4 mov edx, DWORD PTR [ebp-0x2C]
3 | 8B 42 60 mov eax, DWORD PTR [edx+0x60]
4 | 89 04 24 mov DWORD PTR [esp], eax
5 | 90 90 90 90 90 nop 5
6 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/11.2-menu-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 14 mov edx, DWORD PTR [ebp+0x14]
3 | 89 7C 24 04 mov DWORD PTR [esp+0x4], edi
4 | 89 54 24 08 mov DWORD PTR [esp+0x8], edx
5 | 89 04 24 mov DWORD PTR [esp], eax
6 | E8 -- -- -- -- call _gtk_menu_shell_insert
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/11.2-menu-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 14 mov edx, DWORD PTR [ebp+0x14]
3 | 89 7C 24 04 mov DWORD PTR [esp+0x4], edi
4 | 89 54 24 08 mov DWORD PTR [esp+0x8], edx
5 | 89 04 24 mov DWORD PTR [esp], eax
6 | 90 90 90 90 90 nop 5
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/11.2-widget-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 08 mov edx, DWORD PTR [ebp+0x8]
3 | 8B 42 60 mov eax, DWORD PTR [edx+0x60]
4 | 89 04 24 mov DWORD PTR [esp], eax
5 | E8 -- -- -- -- call _gtk_widget_show
6 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/11.2-widget-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 8B 55 08 mov edx, DWORD PTR [ebp+0x8]
3 | 8B 42 60 mov eax, DWORD PTR [edx+0x60]
4 | 89 04 24 mov DWORD PTR [esp], eax
5 | 90 90 90 90 90 nop 5
6 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/6-widget-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 89 83 08 01 00 00 mov DWORD PTR [ebx+0x108], eax
3 | 89 04 24 mov DWORD PTR [esp], eax
4 | E8 -- -- -- -- call _gtk_widget_show
5 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/6-widget-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 89 83 08 01 00 00 mov DWORD PTR [ebx+0x108], eax
3 | 89 04 24 mov DWORD PTR [esp], eax
4 | 90 90 90 90 90 nop 5
5 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/9-menu-a.asm:
--------------------------------------------------------------------------------
1 | 89 04 24 mov DWORD PTR [esp], eax
2 | E8 -- -- -- -- call ...
3 | BA 03 00 00 00 mov edx, 0x3
4 | 89 54 24 08 mov DWORD PTR [esp+0x8], edx
5 | 89 -- 24 04 mov DWORD PTR [esp+0x4], ...
6 | 89 04 24 mov DWORD PTR [esp], eax
7 | E8 -- -- -- -- call _gtk_menu_shell_insert
8 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/9-menu-b.asm:
--------------------------------------------------------------------------------
1 | 89 04 24 mov DWORD PTR [esp], eax
2 | E8 -- -- -- -- call ...
3 | BA 03 00 00 00 mov edx, 0x3
4 | 89 54 24 08 mov DWORD PTR [esp+0x8], edx
5 | 89 -- 24 04 mov DWORD PTR [esp+0x4], ...
6 | 89 04 24 mov DWORD PTR [esp], eax
7 | 90 90 90 90 90 nop 5
8 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/9-widget-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 89 87 -- 02 00 00 mov DWORD PTR [edi+...], eax
3 | 89 04 24 mov DWORD PTR [esp], eax
4 | E8 -- -- -- -- call _gtk_widget_show
5 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-i386/9-widget-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 89 87 -- 02 00 00 mov DWORD PTR [edi+...], eax
3 | 89 04 24 mov DWORD PTR [esp], eax
4 | 90 90 90 90 90 nop 5
5 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-x86_64/24-menu-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 44 89 EA mov edx, r13d
3 | 48 89 DE mov rsi, rbx
4 | 48 89 C7 mov rdi, rax
5 | E8 -- -- -- -- call _gtk_menu_shell_insert
6 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-x86_64/24-menu-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 44 89 EA mov edx, r13d
3 | 48 89 DE mov rsi, rbx
4 | 48 89 C7 mov rdi, rax
5 | 90 90 90 90 90 nop 5
6 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-x86_64/24-widget-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 49 8B BC 24 90 00 00 00 mov rdi, QWORD PTR [r12+0x90]
3 | E8 -- -- -- -- call _gtk_widget_show
4 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-x86_64/24-widget-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 49 8B BC 24 90 00 00 00 mov rdi, QWORD PTR [r12+0x90]
3 | 90 90 90 90 90 nop 5
4 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-x86_64/32-menu-a.asm:
--------------------------------------------------------------------------------
1 | 48 89 C7 mov rdi, rax
2 | E8 -- -- -- -- call ...
3 | 44 89 EA mov edx, r13d
4 | 48 89 EE mov rsi, rbp
5 | 48 89 C7 mov rdi, rax
6 | E8 -- -- -- -- call _gtk_menu_shell_insert
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-x86_64/32-menu-b.asm:
--------------------------------------------------------------------------------
1 | 48 89 C7 mov rdi, rax
2 | E8 -- -- -- -- call ...
3 | 44 89 EA mov edx, r13d
4 | 48 89 EE mov rsi, rbp
5 | 48 89 C7 mov rdi, rax
6 | 90 90 90 90 90 nop 5
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-x86_64/32-widget-a.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 49 8B BC 24 90 00 00 00 mov rdi, QWORD PTR [r12+0x90]
3 | E8 -- -- -- -- call _gtk_widget_show
4 |
--------------------------------------------------------------------------------
/spec/asm/linux/menu-x86_64/32-widget-b.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 49 8B BC 24 90 00 00 00 mov rdi, QWORD PTR [r12+0x90]
3 | 90 90 90 90 90 nop 5
4 |
--------------------------------------------------------------------------------
/spec/asm/linux/offset-x86_64/24-a.asm:
--------------------------------------------------------------------------------
1 | 48 8D B4 24 80 00 00 00 lea rsi, [rsp+0x80]
2 | BA 34 00 00 00 mov edx, 0x34
3 | 4C 89 FF mov rdi, r15
4 | 4C 89 E1 mov rcx, r12
5 | FF 50 28 call QWORD PTR [rax+0x28]
6 | 84 C0 test al, al
7 | 75 45 jne 0x5D
8 | 49 8B 07 mov rax, QWORD PTR [r15]
9 | 4C 89 FF mov rdi, r15
10 | FF 50 08 call QWORD PTR [rax+0x8]
11 | 41 0F B6 5D 00 movzx ebx, BYTE PTR [r13+0x0]
12 | 48 89 EF mov rdi, rbp
13 | E8 -- -- -- -- call ...
14 | 48 8B 8C 24 B8 00 00 00 mov rcx, QWORD PTR [rsp+0xB8]
15 | 64 48 33 0C 25 28 00 00 00 xor rcx, QWORD PTR fs:0x28
16 | 89 D8 mov eax, ebx
17 | 0F 85 -- -- -- -- jne ...
18 | 48 81 C4 C8 00 00 00 add rsp, 0xC8
19 | 5B pop rbx
20 | 5D pop rbp
21 | 41 5C pop r12
22 | 41 5D pop r13
23 | 41 5E pop r14
24 | 41 5F pop r15
25 | C3 ret
26 | -- -- -- -- ...
27 | 48 83 7C 24 30 34 cmp QWORD PTR [rsp+0x30], 0x34
28 | 75 -- jne ...
29 | 8B B4 24 A0 00 00 00 mov esi, DWORD PTR [rsp+0xA0]
30 | BA 01 00 00 00 mov edx, 0x1
31 | 4C 89 FF mov rdi, r15
32 | E8 -- -- -- -- call ...
33 | 84 C0 test al, al
34 | 74 -- je ...
35 | 45 31 F6 xor r14d, r14d
36 | 66 83 BC 24 B0 00 00 00 00 cmp WORD PTR [rsp+0xB0], 0x0
37 | C7 44 24 0C 00 00 00 00 mov DWORD PTR [rsp+0xC], 0x0
38 | 74 -- je ...
39 |
--------------------------------------------------------------------------------
/spec/asm/linux/offset-x86_64/24-b.asm:
--------------------------------------------------------------------------------
1 | 48 8D B4 24 78 00 00 00 lea rsi, [rsp+0x78]
2 | BA 40 00 00 00 mov edx, 0x40
3 | 4C 89 FF mov rdi, r15
4 | 4C 89 E1 mov rcx, r12
5 | FF 50 28 call QWORD PTR [rax+0x28]
6 | 84 C0 test al, al
7 | 75 45 jne 0x5B
8 | 49 8B 07 mov rax, QWORD PTR [r15]
9 | 4C 89 FF mov rdi, r15
10 | FF 50 08 call QWORD PTR [rax+0x8]
11 | 41 0F B6 5D 00 movzx ebx, BYTE PTR [r13+0x0]
12 | 48 89 EF mov rdi, rbp
13 | E8 -- -- -- -- call ...
14 | 48 8B 8C 24 B8 00 00 00 mov rcx, QWORD PTR [rsp+0xB8]
15 | 64 48 33 0C 25 28 00 00 00 xor rcx, QWORD PTR fs:0x28
16 | 89 D8 mov eax, ebx
17 | 0F 85 -- -- -- -- jne ...
18 | 48 81 C4 C8 00 00 00 add rsp, 0xC8
19 | 5B pop rbx
20 | 5D pop rbp
21 | 41 5C pop r12
22 | 41 5D pop r13
23 | 41 5E pop r14
24 | 41 5F pop r15
25 | C3 ret
26 | -- -- -- -- ...
27 | 48 83 7C 24 30 40 cmp QWORD PTR [rsp+0x30], 0x40
28 | 75 -- jne ...
29 | 8B B4 24 A0 00 00 00 mov esi, DWORD PTR [rsp+0xA0]
30 | 41 89 F6 mov r14d, esi
31 | 0F B7 84 24 B4 00 00 00 movzx eax, WORD PTR [rsp+0xB4]
32 | C1 E0 06 shl eax, 0x6
33 | 41 01 C6 add r14d, eax
34 | 90 90 90 90 nop 4
35 | 90 90 90 90 nop 4
36 | 90 90 90 90 nop 4
37 | 90 90 90 90 nop 4
38 | 90 90 90 90 nop 4
39 | EB -- jmp ...
40 |
--------------------------------------------------------------------------------
/spec/asm/linux/offset-x86_64/25-a.asm:
--------------------------------------------------------------------------------
1 | 48 8D 74 24 70 lea rsi, [rsp+0x70]
2 | BA 34 00 00 00 mov edx, 0x34
3 | 4C 89 FF mov rdi, r15
4 | 4C 89 E1 mov rcx, r12
5 | FF 50 28 call QWORD PTR [rax+0x28]
6 | 84 C0 test al, al
7 | 75 48 jne 0x5F
8 | 49 8B 07 mov rax, QWORD PTR [r15]
9 | 4C 89 FF mov rdi, r15
10 | FF 50 08 call QWORD PTR [rax+0x8]
11 | 41 0F B6 5D 00 movzx ebx, BYTE PTR [r13+0x0]
12 | 48 89 EF mov rdi, rbp
13 | E8 -- -- -- -- call ...
14 | 48 8B 8C 24 A8 00 00 00 mov rcx, QWORD PTR [rsp+0xA8]
15 | 64 48 33 0C 25 28 00 00 00 xor rcx, QWORD PTR fs:0x28
16 | 89 D8 mov eax, ebx
17 | 0F 85 -- -- -- -- jne ...
18 | 48 81 C4 B8 00 00 00 add rsp, 0xB8
19 | 5B pop rbx
20 | 5D pop rbp
21 | 41 5C pop r12
22 | 41 5D pop r13
23 | 41 5E pop r14
24 | 41 5F pop r15
25 | C3 ret
26 | -- -- -- -- -- -- -- ...
27 | 48 83 7C 24 30 34 cmp QWORD PTR [rsp+0x30], 0x34
28 | 75 -- jne ...
29 | 8B B4 24 90 00 00 00 mov esi, DWORD PTR [rsp+0x90]
30 | BA 01 00 00 00 mov edx, 0x1
31 | 4C 89 FF mov rdi, r15
32 | E8 -- -- -- -- call ...
33 | 84 C0 test al, al
34 | 74 -- je ...
35 | 45 31 F6 xor r14d, r14d
36 | 66 83 BC 24 A0 00 00 00 00 cmp WORD PTR [rsp+0xA0], 0x0
37 | C7 44 24 0C 00 00 00 00 mov DWORD PTR [rsp+0xC], 0x0
38 | 74 -- je ...
39 |
--------------------------------------------------------------------------------
/spec/asm/linux/offset-x86_64/25-b.asm:
--------------------------------------------------------------------------------
1 | 48 8D 74 24 68 lea rsi, [rsp+0x68]
2 | BA 40 00 00 00 mov edx, 0x40
3 | 4C 89 FF mov rdi, r15
4 | 4C 89 E1 mov rcx, r12
5 | FF 50 28 call QWORD PTR [rax+0x28]
6 | 84 C0 test al, al
7 | 75 48 jne 0x5F
8 | 49 8B 07 mov rax, QWORD PTR [r15]
9 | 4C 89 FF mov rdi, r15
10 | FF 50 08 call QWORD PTR [rax+0x8]
11 | 41 0F B6 5D 00 movzx ebx, BYTE PTR [r13+0x0]
12 | 48 89 EF mov rdi, rbp
13 | E8 -- -- -- -- call ...
14 | 48 8B 8C 24 A8 00 00 00 mov rcx, QWORD PTR [rsp+0xA8]
15 | 64 48 33 0C 25 28 00 00 00 xor rcx, QWORD PTR fs:0x28
16 | 89 D8 mov eax, ebx
17 | 0F 85 -- -- -- -- jne ...
18 | 48 81 C4 B8 00 00 00 add rsp, 0xB8
19 | 5B pop rbx
20 | 5D pop rbp
21 | 41 5C pop r12
22 | 41 5D pop r13
23 | 41 5E pop r14
24 | 41 5F pop r15
25 | C3 ret
26 | -- -- -- -- -- -- -- ...
27 | 48 83 7C 24 30 40 cmp QWORD PTR [rsp+0x30], 0x40
28 | 75 -- jne ...
29 | 8B B4 24 90 00 00 00 mov esi, DWORD PTR [rsp+0x90]
30 | 41 89 F6 mov r14d, esi
31 | 0F B7 84 24 A4 00 00 00 movzx eax, WORD PTR [rsp+0xA4]
32 | C1 E0 06 shl eax, 0x6
33 | 41 01 C6 add r14d, eax
34 | 90 90 90 90 nop 4
35 | 90 90 90 90 nop 4
36 | 90 90 90 90 nop 4
37 | 90 90 90 90 nop 4
38 | 90 90 90 90 nop 4
39 | EB -- jmp ...
40 |
--------------------------------------------------------------------------------
/spec/asm/linux/offset-x86_64/32-a.asm:
--------------------------------------------------------------------------------
1 | 48 8D 74 24 70 lea rsi, [rsp+0x70]
2 | BA 34 00 00 00 mov edx, 0x34
3 | 48 89 DF mov rdi, rbx
4 | 4C 89 E9 mov rcx, r13
5 | FF 50 28 call QWORD PTR [rax+0x28]
6 | 84 C0 test al, al
7 | 75 4E jne 0x50
8 | 48 8B 03 mov rax, QWORD PTR [rbx]
9 | 48 89 DF mov rdi, rbx
10 | FF 50 08 call QWORD PTR [rax+0x8]
11 | 48 8B 44 24 08 mov rax, QWORD PTR [rsp+0x8]
12 | 4C 89 E7 mov rdi, r12
13 | 0F B6 18 movzx ebx, BYTE PTR [rax]
14 | E8 -- -- -- -- call ...
15 | 48 8B 8C 24 A8 00 00 00 mov rcx, QWORD PTR [rsp+0xA8]
16 | 64 48 33 0C 25 28 00 00 00 xor rcx, QWORD PTR fs:0x28
17 | 89 D8 mov eax, ebx
18 | 0F 85 -- -- -- -- jne ...
19 | 48 81 C4 B8 00 00 00 add rsp, 0xB8
20 | 5B pop rbx
21 | 5D pop rbp
22 | 41 5C pop r12
23 | 41 5D pop r13
24 | 41 5E pop r14
25 | 41 5F pop r15
26 | C3 ret
27 | -- -- -- -- -- -- -- -- -- -- ...
28 | 48 83 7C 24 30 34 cmp QWORD PTR [rsp+0x30], 0x34
29 | 75 -- jne ...
30 | 8B B4 24 90 00 00 00 mov esi, DWORD PTR [rsp+0x90]
31 | BA 01 00 00 00 mov edx, 0x1
32 | 48 89 DF mov rdi, rbx
33 | E8 -- -- -- -- call ...
34 | 84 C0 test al, al
35 | 74 92
36 | 66 83 BC 24 A0 00 00 00 00 cmp WORD PTR [rsp+0xA0], 0x0
37 | 0F 84 -- -- -- -- je ...
38 | 45 31 F6 xor r14d, r14d
39 | 45 31 FF xor r15d, r15d
40 | 0F 1F 00 nop DWORD PTR [rax]
41 | 48 8B 03 mov rax, QWORD PTR [rbx]
42 | 4C 89 E9 mov rcx, r13
43 | BA 28 00 00 00 mov edx, 0x28
44 | 48 89 EE mov rsi, rbp
45 | 48 89 DF mov rdi, rbx
46 | FF 50 28 call QWORD PTR [rax+0x28]
47 | 84 C0 test al, al
48 | 0F 85 -- -- -- -- jne ...
49 |
--------------------------------------------------------------------------------
/spec/asm/linux/offset-x86_64/32-b.asm:
--------------------------------------------------------------------------------
1 | 48 8D 74 24 68 lea rsi, [rsp+0x68]
2 | BA 40 00 00 00 mov edx, 0x40
3 | 48 89 DF mov rdi, rbx
4 | 4C 89 E9 mov rcx, r13
5 | FF 50 28 call QWORD PTR [rax+0x28]
6 | 84 C0 test al, al
7 | 75 4E jne 0x50
8 | 48 8B 03 mov rax, QWORD PTR [rbx]
9 | 48 89 DF mov rdi, rbx
10 | FF 50 08 call QWORD PTR [rax+0x8]
11 | 48 8B 44 24 08 mov rax, QWORD PTR [rsp+0x8]
12 | 4C 89 E7 mov rdi, r12
13 | 0F B6 18 movzx ebx, BYTE PTR [rax]
14 | E8 -- -- -- -- call ...
15 | 48 8B 8C 24 A8 00 00 00 mov rcx, QWORD PTR [rsp+0xA8]
16 | 64 48 33 0C 25 28 00 00 00 xor rcx, QWORD PTR fs:0x28
17 | 89 D8 mov eax, ebx
18 | 0F 85 -- -- -- -- jne ...
19 | 48 81 C4 B8 00 00 00 add rsp, 0xB8
20 | 5B pop rbx
21 | 5D pop rbp
22 | 41 5C pop r12
23 | 41 5D pop r13
24 | 41 5E pop r14
25 | 41 5F pop r15
26 | C3 ret
27 | -- -- -- -- -- -- -- -- -- -- ...
28 | 48 83 7C 24 30 40 cmp QWORD PTR [rsp+0x30], 0x40
29 | 75 -- jne ...
30 | 8B B4 24 90 00 00 00 mov esi, DWORD PTR [rsp+0x90]
31 | 41 89 F7 mov r15d, esi
32 | 0F B7 84 24 A4 00 00 00 movzx eax, WORD PTR [rsp+0xA4]
33 | C1 E0 06 shl eax, 0x6
34 | 41 01 C7 add r15d, eax
35 | 90 90 90 90 nop 4
36 | 90 90 90 90 nop 4
37 | 90 90 90 90 nop 4
38 | 90 90 90 90 nop 4
39 | 90 90 90 90 nop 4
40 | 90 90 90 90 nop 4
41 | 90 90 90 90 nop 4
42 | 90 90 90 90 nop 4
43 | 90 90 90 90 nop 4
44 | 90 90 90 90 nop 4
45 | 90 90 90 90 nop 4
46 | 90 90 90 90 nop 4
47 | 90 90 90 90 nop 4
48 |
--------------------------------------------------------------------------------
/spec/asm/linux/patch-i386/ebx.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call ...
2 | 81 C3 -- -- -- -- add ebx, ...
3 |
--------------------------------------------------------------------------------
/spec/asm/linux/path-i386/10.0.asm:
--------------------------------------------------------------------------------
1 | 0F 84 -- -- -- -- je ...
2 | 8D 9D E0 EF FF FF lea ebx, [ebp-0x1020]
3 | C7 44 24 04 -- -- -- -- mov DWORD PTR [esp+0x4], ...
4 | 89 1C 24 mov DWORD PTR [esp], ebx
5 | E8 -- -- -- -- call ...
6 |
--------------------------------------------------------------------------------
/spec/asm/linux/path-i386/10.1.asm:
--------------------------------------------------------------------------------
1 | 0F 84 -- -- -- -- je ...
2 | 8D 45 E4 lea eax, [ebp-0x1C]
3 | 89 04 24 mov DWORD PTR [esp], eax
4 | C7 44 24 04 -- -- -- -- mov DWORD PTR [esp+0x4], ...
5 | E8 -- -- -- -- call ...
6 | 8B 55 08 mov edx, DWORD PTR [ebp+0x8]
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/path-i386/11.0.asm:
--------------------------------------------------------------------------------
1 | 0F 84 -- -- -- -- je ...
2 | 8D 45 E4 lea eax, [ebp-0x1C]
3 | 31 DB xor ebx, ebx
4 | 89 04 24 mov DWORD PTR [esp], eax
5 | C7 44 24 04 -- -- -- -- mov DWORD PTR [esp+0x4], ...
6 | E8 -- -- -- -- call ...
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/path-i386/11.2.asm:
--------------------------------------------------------------------------------
1 | 8D 83 -- -- -- -- lea eax, [ebx+...]
2 | 31 F6 xor esi, esi
3 | 89 44 24 04 mov DWORD PTR [esp+0x4], eax
4 |
--------------------------------------------------------------------------------
/spec/asm/linux/path-i386/6.asm:
--------------------------------------------------------------------------------
1 | 55 push ebp
2 | 57 push edi
3 | 56 push esi
4 | 53 push ebx
5 | 83 EC 24 sub esp, 0x24
6 | BB 00 10 00 00 mov ebx, 0x1000
7 | 8B 6C 24 40 mov ebp, DWORD PTR [esp+0x40]
8 | 53 push ebx
9 | 68 -- -- -- -- push ...
10 | C6 05 -- -- -- -- 00 mov BYTE PTR ds:..., 0x0
11 | E8 -- -- -- -- call ...
12 | 89 C7 mov edi, eax
13 | 83 C4 10 add esp, 0x10
14 | 85 FF test edi, edi
15 | 0F 84 -- -- -- -- je ...
16 | 85 ED test ebp, ebp
17 | 0F 84 -- -- -- -- je ...
18 | 83 EC 08 sub esp, 0x8
19 | 53 push ebx
20 | 57 push edi
21 | E8 -- -- -- -- call ...
22 | 83 C4 10 add esp, 0x10
23 | 84 C0 test al, al
24 | 0F 84 -- -- -- -- je ...
25 | 83 EC 08 sub esp, 0x8
26 | 68 -- -- -- -- push ...
27 | 8D 5C 24 0C lea ebx, [esp+0xC]
28 | 53 push ebx
29 | E8 -- -- -- -- call ...
30 | 5A pop edx
31 | 59 pop ecx
32 | 57 push edi
33 | 53 push ebx
34 | E8 -- -- -- -- call ...
35 | 89 2C 24 mov DWORD PTR [esp], ebp
36 | E8 -- -- -- -- call ...
37 | 83 C4 10 add esp, 0x10
38 | 83 F8 05 cmp eax, 0x5
39 | 7E -- jle ...
40 | 83 EC 0C sub esp, 0xC
41 | 57 push edi
42 |
--------------------------------------------------------------------------------
/spec/asm/linux/path-i386/9.asm:
--------------------------------------------------------------------------------
1 | 0F 84 -- -- -- -- je ...
2 | 8D 5D E8 lea ebx, [ebp-0x18]
3 | BE -- -- -- -- mov esi, ...
4 | 89 74 24 04 mov DWORD PTR [esp+0x4], esi
5 | 89 1C 24 mov DWORD PTR [esp], ebx
6 | E8 -- -- -- -- call ...
7 |
--------------------------------------------------------------------------------
/spec/asm/linux/path-x86_64/24.asm:
--------------------------------------------------------------------------------
1 | 49 89 F4 mov r12, rsi
2 | 48 8D 35 -- -- -- -- lea rsi, [rip + -- -- -- --]
3 |
--------------------------------------------------------------------------------
/spec/asm/linux/title-i386/10.1.asm:
--------------------------------------------------------------------------------
1 | C7 44 24 08 -- -- -- -- mov DWORD PTR [esp+0x8], ...
2 | C7 44 24 04 -- -- -- -- mov DWORD PTR [esp+0x4], ...
3 | 89 -- 24 mov DWORD PTR [esp], ...
4 | E8 -- -- -- -- call ...
5 |
--------------------------------------------------------------------------------
/spec/asm/linux/title-i386/11.2.asm:
--------------------------------------------------------------------------------
1 | 8D 83 -- -- -- -- lea eax, [ebx+...]
2 | 89 44 24 04 mov DWORD PTR [esp+0x4], eax
3 | C7 44 24 08 -- -- -- -- mov DWORD PTR [esp+0x8], ...
4 | 89 34 24 mov DWORD PTR [esp], esi
5 | E8 -- -- -- -- call ...
6 |
--------------------------------------------------------------------------------
/spec/asm/linux/title-i386/6.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call _gtk_widget_show
2 | 5F pop edi
3 | 58 pop eax
4 | FF 35 -- -- -- -- push DWORD PTR ds:...
5 | -- -- -- ...
6 | -- -- -- ...
7 | E8 -- -- -- -- call _gdk_window_set_title
8 |
--------------------------------------------------------------------------------
/spec/asm/linux/title-i386/9.asm:
--------------------------------------------------------------------------------
1 | E8 -- -- -- -- call _gtk_widget_show
2 | A1 -- -- -- -- mov eax, DWORD PTR ds:....
3 | 89 44 24 04 mov DWORD PTR [esp+0x4], eax
4 | -- -- -- -- -- -- ...
5 | -- -- -- ...
6 | 89 -- 24 mov DWORD PTR [esp], eax
7 | E8 -- -- -- -- call _gtk_window_set_title
8 |
--------------------------------------------------------------------------------
/spec/asm/linux/title-x86_64/24.asm:
--------------------------------------------------------------------------------
1 | 48 8D 35 -- -- -- -- lea rsi, [rip+...]
2 | BA -- -- -- -- mov edx, ...
3 |
--------------------------------------------------------------------------------
/spec/asm/mac/title-arm64/35-debug.asm:
--------------------------------------------------------------------------------
1 | FF 43 01 D1 sub sp, sp, #0x50
2 | FD 7B 04 A9 stp x29, x30, [sp, #0x40]
3 | FD 03 01 91 add x29, sp, #0x40
4 | A0 83 1F F8 stur x0, [x29, #-8]
5 | A1 03 1F F8 stur x1, [x29, #-0x10]
6 | A8 83 5F F8 ldur x8, [x29, #-8]
7 | -- -- -- -- adrp x9, #_kCFAllocatorDefault_ptr@PAGE
8 | 29 -- -- F9 ldr x9, [x9, #_kCFAllocatorDefault_ptr@PAGEOFF]
9 | 20 01 40 F9 ldr x0, [x9]
10 | A9 03 5F F8 ldur x9, [x29, #-0x10]
11 | E0 13 00 F9 str x0, [sp, #0x20]
12 | E0 03 09 AA mov x0, x9
13 | E8 0F 00 F9 str x8, [sp, #0x18]
14 | -- -- -- -- bl ...
15 | A8 03 5F F8 ldur x8, [x29, #-0x10]
16 | E0 0B 00 F9 str x0, [sp, #0x10]
17 | E0 03 08 AA mov x0, x8
18 | -- -- -- -- bl ...
19 | E8 13 40 F9 ldr x8, [sp, #0x20]
20 | E0 07 00 F9 str x0, [sp, #8]
21 | E0 03 08 AA mov x0, x8
22 | E1 0B 40 F9 ldr x1, [sp, #0x10]
23 | E2 07 40 F9 ldr x2, [sp, #8]
24 | -- -- -- -- bl _CFStringCreateWithCharacters
25 |
--------------------------------------------------------------------------------
/spec/asm/mac/title-arm64/35-release.asm:
--------------------------------------------------------------------------------
1 | F4 4F BE A9 stp x20, x19, [sp, #-0x20]!
2 | FD 7B 01 A9 stp x29, x30, [sp, #0x10]
3 | FD 43 00 91 add x29, sp, #0x10
4 | F3 03 00 AA mov x19, x0
5 | -- -- -- -- adrp x8, #_kCFAllocatorDefault_ptr@PAGE
6 | 08 -- -- F9 ldr x8, [x8, #_kCFAllocatorDefault_ptr@PAGEOFF]
7 | 00 01 40 F9 ldr x0, [x8]
8 | 22 20 40 A9 ldp x2, x8, [x1]
9 | -- -- -- -- adrp x9, ...
10 | 29 -- -- 91 add x9, x9, ...
11 | 1F 01 00 F1 cmp x8, #0
12 | 21 01 88 9A csel x1, x9, x8, eq
13 | -- -- -- -- bl _CFStringCreateWithCharacters
14 |
--------------------------------------------------------------------------------
/spec/asm/mac/title-i386/11.asm:
--------------------------------------------------------------------------------
1 | 55 push ebp
2 | 89 E5 mov ebp, esp
3 | 56 push esi
4 | 53 push ebx
5 | 83 EC 10 sub esp, 0x10
6 | 8B 75 08 mov esi, DWORD PTR [ebp+0x8]
7 | 8B 45 0C mov eax, DWORD PTR [ebp+0xc]
8 | 8B 18 mov ebx, DWORD PTR [eax]
9 | 8B 48 04 mov ecx, DWORD PTR [eax+0x4]
10 | 85 C9 test ecx, ecx
11 | 0F 44 0D -- -- -- -- cmove ecx, DWORD PTR ds:...
12 | 89 5C 24 08 mov DWORD PTR [esp+0x8], ebx
13 | 89 4C 24 04 mov DWORD PTR [esp+0x4], ecx
14 | 8B 15 -- -- -- -- mov edx, DWORD PTR ds:...
15 | 8B 02 mov eax, DWORD PTR [edx]
16 | 89 04 24 mov DWORD PTR [esp], eax
17 | E8 -- -- -- -- call _CFStringCreateWithCharacters
18 |
--------------------------------------------------------------------------------
/spec/asm/mac/title-i386/13.asm:
--------------------------------------------------------------------------------
1 | 55 push ebp
2 | 89 E5 mov ebp, esp
3 | 57 push edi
4 | 56 push esi
5 | 83 EC 10 sub esp, 0x10
6 | E8 00 00 00 00 call 0xd
7 | 5F pop edi
8 | 8B 45 0C mov eax, DWORD PTR [ebp+0xc]
9 | 8B 08 mov ecx, DWORD PTR [eax]
10 | 8B 40 04 mov eax, DWORD PTR [eax+0x4]
11 | 89 4C 24 08 mov DWORD PTR [esp+0x8], ecx
12 | 85 C0 test eax, eax
13 | 0F 44 87 -- -- -- -- cmove eax, DWORD PTR [edi+...]
14 | 89 44 24 04 mov DWORD PTR [esp+0x4], eax
15 | 8B 87 -- -- -- -- mov eax, DWORD PTR [edi+...]
16 | 8B 00 mov eax, DWORD PTR [eax]
17 | 89 04 24 mov DWORD PTR [esp], eax
18 | E8 -- -- -- -- call _CFStringCreateWithCharacters
19 |
--------------------------------------------------------------------------------
/spec/asm/mac/title-i386/23.asm:
--------------------------------------------------------------------------------
1 | 55 push ebp
2 | 89 E5 mov ebp, esp
3 | 57 push edi
4 | 56 push esi
5 | 83 EC 10 sub esp, 0x10
6 | E8 00 00 00 00 call 0xd
7 | 5F pop edi
8 | 8B 45 0C mov eax, DWORD PTR [ebp+0xc]
9 | 8B 8F -- -- -- -- mov ecx, DWORD PTR [edi+...]
10 | 8B 09 mov ecx, DWORD PTR [ecx]
11 | 8B 10 mov edx, DWORD PTR [eax]
12 | 8B 40 04 mov eax, DWORD PTR [eax+0x4]
13 | 85 C0 test eax, eax
14 | 0F 44 87 -- -- -- -- cmove eax, DWORD PTR [edi+...]
15 | 89 54 24 08 mov DWORD PTR [esp+0x8], edx
16 | 89 44 24 04 mov DWORD PTR [esp+0x4], eax
17 | 89 0C 24 mov DWORD PTR [esp], ecx
18 | E8 -- -- -- -- call _CFStringCreateWithCharacters
19 |
--------------------------------------------------------------------------------
/spec/asm/mac/title-x86_64/11-1.asm:
--------------------------------------------------------------------------------
1 | 55 push rbp
2 | 48 89 E5 mov rbp, rsp
3 | 41 54 push r12
4 | 53 push rbx
5 | 49 89 FC mov r12, rdi
6 | 48 8B 16 mov rdx, QWORD PTR [rsi]
7 | 48 8B 76 08 mov rsi, QWORD PTR [rsi+0x8]
8 | 48 85 F6 test rsi, rsi
9 | 74 -- je --
10 | 48 8B 05 -- -- -- -- mov rax, QWORD PTR [rip+...]
11 | 48 8B 38 mov rdi, QWORD PTR [rax]
12 | E8 -- -- -- -- call _CFStringCreateWithCharacters
13 |
--------------------------------------------------------------------------------
/spec/asm/mac/title-x86_64/11-2.asm:
--------------------------------------------------------------------------------
1 | 48 8D 35 -- -- -- -- lea rsi, [rip+...]
2 |
--------------------------------------------------------------------------------
/spec/asm/mac/title-x86_64/13.asm:
--------------------------------------------------------------------------------
1 | 55 push rbp
2 | 48 89 E5 mov rbp, rsp
3 | 41 56 push r14
4 | 53 push rbx
5 | 49 89 FE mov r14, rdi
6 | 48 8B 16 mov rdx, QWORD PTR [rsi]
7 | 48 8B 76 08 mov rsi, QWORD PTR [rsi+0x8]
8 | 48 85 F6 test rsi, rsi
9 | 48 0F 44 35 -- -- -- -- cmove rsi, QWORD PTR [rip+...]
10 | 48 8B 05 -- -- -- -- mov rax, QWORD PTR [rip+...]
11 | 48 8B 38 mov rdi, QWORD PTR [rax]
12 | E8 -- -- -- -- call _CFStringCreateWithCharacters
13 |
--------------------------------------------------------------------------------
/spec/asm/mac/title-x86_64/23.asm:
--------------------------------------------------------------------------------
1 | 55 push rbp
2 | 48 89 E5 mov rbp, rsp
3 | 41 56 push r14
4 | 53 push rbx
5 | 49 89 FE mov r14, rdi
6 | 48 8B 05 -- -- -- -- mov rax, QWORD PTR [rip+...]
7 | 48 8B 38 mov rdi, QWORD PTR [rax]
8 | 48 8B 16 mov rdx, QWORD PTR [rsi]
9 | 48 8B 76 08 mov rsi, QWORD PTR [rsi+0x8]
10 | 48 85 F6 test rsi, rsi
11 | 48 0F 44 35 -- -- -- -- cmove rsi, QWORD PTR [rip+...]
12 | E8 -- -- -- -- call _CFStringCreateWithCharacters
13 |
--------------------------------------------------------------------------------
/spec/asm/mac/title-x86_64/35.asm:
--------------------------------------------------------------------------------
1 | 55 push rbp
2 | 48 89 E5 mov rbp, rsp
3 | 41 56 push r14
4 | 53 push rbx
5 | 49 89 FE mov r14, rdi
6 | 48 8B 05 -- -- -- -- mov rax, QWORD PTR [rip+...]
7 | 48 8B 38 mov rdi, QWORD PTR [rax]
8 | 48 8B 16 mov rdx, QWORD PTR [rsi]
9 | 48 8B 76 08 mov rsi, QWORD PTR [rsi+0x8]
10 | 48 85 F6 test rsi, rsi
11 | 75 07 jne 0x1d
12 | 48 8D 35 -- -- -- -- lea rsi, [rip+...]
13 | E8 -- -- -- -- call _CFStringCreateWithCharacters
14 |
--------------------------------------------------------------------------------
/spec/asm/windows/ood-i386/30.asm:
--------------------------------------------------------------------------------
1 | 55 push ebp
2 | 8B EC mov ebp, esp
3 | 56 push esi
4 | 8B F1 mov esi, ecx
5 | 57 push edi
6 | 8B 7D 08 mov edi, DWORD PTR [ebp+0x8]
7 | 57 push edi
8 | 8D 4E 04 lea ecx, [esi+0x4]
9 | E8 -- -- -- -- call ...
10 | 8B 06 mov eax, DWORD PTR [esi]
11 | 83 F8 0E cmp eax, 0xE
12 | 77 55 ja 0x6F
13 | FF 24 85 -- -- -- -- jmp DWORD PTR [eax*4+...]
14 | 57 push edi
15 | 8B CE mov ecx, esi
16 | E8 -- -- -- -- call ...
17 | EB 44 jmp 0x6F
18 | 57 push edi
19 | 8B CE mov ecx, esi
20 | E8 -- -- -- -- call ...
21 | EB 3A jmp 0x6F
22 | 57 push edi
23 | 8B CE mov ecx, esi
24 | E8 -- -- -- -- call ...
25 | EB 30 jmp 0x6F
26 | 57 push edi
27 | 8B CE mov ecx, esi
28 | E8 -- -- -- -- call ...
29 | EB 26 jmp 0x6F
30 | 57 push edi
31 | 8B CE mov ecx, esi
32 | E8 -- -- -- -- call ...
33 | EB 1C jmp 0x6F
34 | 57 push edi
35 | 8B CE mov ecx, esi
36 | E8 -- -- -- -- call ...
37 | EB 12 jmp 0x6F
38 | 57 push edi
39 | 8B CE mov ecx, esi
40 | E8 -- -- -- -- call ...
41 | EB 08 jmp 0x6F
42 | 57 push edi
43 | 8B CE mov ecx, esi
44 | E8 -- -- -- -- call ...
45 | 5F pop edi
46 | 5E pop esi
47 | 5D pop ebp
48 | C2 04 00 ret 0x4
49 |
--------------------------------------------------------------------------------
/spec/asm/windows/ood-i386/31.asm:
--------------------------------------------------------------------------------
1 | 55 push ebp
2 | 8B EC mov ebp, esp
3 | 56 push esi
4 | 8B F1 mov esi, ecx
5 | 57 push edi
6 | 8B 7D 08 mov edi, DWORD PTR [ebp+0x8]
7 | 57 push edi
8 | 8D 4E 04 lea ecx, [esi+0x4]
9 | E8 -- -- -- -- call ...
10 | 8B 06 mov eax, DWORD PTR [esi]
11 | 48 dec eax
12 | 83 F8 07 cmp eax, 0x7
13 | 77 55 ja 0x70
14 | FF 24 85 -- -- -- -- jmp DWORD PTR [eax*4+...]
15 | 57 push edi
16 | 8B CE mov ecx, esi
17 | E8 -- -- -- -- call ...
18 | EB 44 jmp 0x70
19 | 57 push edi
20 | 8B CE mov ecx, esi
21 | E8 -- -- -- -- call ...
22 | EB 3A jmp 0x70
23 | 57 push edi
24 | 8B CE mov ecx, esi
25 | E8 -- -- -- -- call ...
26 | EB 30 jmp 0x70
27 | 57 push edi
28 | 8B CE mov ecx, esi
29 | E8 -- -- -- -- call ...
30 | EB 26 jmp 0x70
31 | 57 push edi
32 | 8B CE mov ecx, esi
33 | E8 -- -- -- -- call ...
34 | EB 1C jmp 0x70
35 | 57 push edi
36 | 8B CE mov ecx, esi
37 | E8 -- -- -- -- call ...
38 | EB 12 jmp 0x70
39 | 57 push edi
40 | 8B CE mov ecx, esi
41 | E8 -- -- -- -- call ...
42 | EB 08 jmp 0x70
43 | 57 push edi
44 | 8B CE mov ecx, esi
45 | E8 -- -- -- -- call ...
46 | 5F pop edi
47 | 5E pop esi
48 | 5D pop ebp
49 | C2 04 00 ret 0x4
50 |
--------------------------------------------------------------------------------
/spec/asm/windows/ood-i386/ret4.asm:
--------------------------------------------------------------------------------
1 | C2 04 00 ret 0x4
2 |
--------------------------------------------------------------------------------
/spec/asm/windows/ood-x86_64/26.asm:
--------------------------------------------------------------------------------
1 | 48 89 5C 24 08 mov QWORD PTR [rsp+0x8], rbx
2 | 57 push rdi
3 | 48 83 EC 20 sub rsp, 0x20
4 | 48 8B D9 mov rbx, rcx
5 | 48 8B FA mov rdi, rdx
6 | 48 83 C1 08 add rcx, 0x8
7 | E8 -- -- -- -- call ...
8 | 44 8B 03 mov r8d, DWORD PTR [rbx]
9 | 41 83 E8 01 sub r8d, 0x1
10 | 0F 84 85 00 00 00 je 0xAB
11 | 41 83 E8 01 sub r8d, 0x1
12 | 74 72 je 0x9E
13 | 41 83 E8 01 sub r8d, 0x1
14 | 74 5F je 0x91
15 | 41 83 E8 01 sub r8d, 0x1
16 | 74 4C je 0x84
17 | 41 83 E8 01 sub r8d, 0x1
18 | 74 39 je 0x77
19 | 41 83 E8 01 sub r8d, 0x1
20 | 74 26 je 0x6A
21 | 41 83 E8 01 sub r8d, 0x1
22 | 74 13 je 0x5D
23 | 41 83 F8 01 cmp r8d, 0x1
24 | 75 66 jne 0xB6
25 | 48 8B D7 mov rdx, rdi
26 | 48 8B CB mov rcx, rbx
27 | E8 -- -- -- -- call ...
28 | EB 59 jmp 0xB6
29 | 48 8B D7 mov rdx, rdi
30 | 48 8B CB mov rcx, rbx
31 | E8 -- -- -- -- call ...
32 | EB 4C jmp 0xB6
33 | 48 8B D7 mov rdx, rdi
34 | 48 8B CB mov rcx, rbx
35 | E8 -- -- -- -- call ...
36 | EB 3F jmp 0xB6
37 | 48 8B D7 mov rdx, rdi
38 | 48 8B CB mov rcx, rbx
39 | E8 -- -- -- -- call ...
40 | EB 32 jmp 0xB6
41 | 48 8B D7 mov rdx, rdi
42 | 48 8B CB mov rcx, rbx
43 | E8 -- -- -- -- call ...
44 | EB 25 jmp 0xB6
45 | 48 8B D7 mov rdx, rdi
46 | 48 8B CB mov rcx, rbx
47 | E8 -- -- -- -- call ...
48 | EB 18 jmp 0xB6
49 | 48 8B D7 mov rdx, rdi
50 | 48 8B CB mov rcx, rbx
51 | E8 -- -- -- -- call ...
52 | EB 0B jmp 0xB6
53 | 48 8B D7 mov rdx, rdi
54 | 48 8B CB mov rcx, rbx
55 | E8 -- -- -- -- call ...
56 | 48 8B 5C 24 30 mov rbx, QWORD PTR [rsp+0x30]
57 | 48 83 C4 20 add rsp, 0x20
58 | 5F pop rdi
59 | C3 ret
60 |
--------------------------------------------------------------------------------
/spec/asm/windows/ood-x86_64/30.asm:
--------------------------------------------------------------------------------
1 | 48 89 5C 24 08 mov QWORD PTR [rsp+0x8], rbx
2 | 57 push rdi
3 | 48 83 EC 20 sub rsp, 0x20
4 | 48 8B D9 mov rbx, rcx
5 | 48 8B FA mov rdi, rdx
6 | 48 83 C1 08 add rcx, 0x8
7 | E8 -- -- -- -- call ...
8 | 44 8B 03 mov r8d, DWORD PTR [rbx]
9 | 41 83 F8 05 cmp r8d, 0x5
10 | 7F 64 jg 0x81
11 | 74 55 je 0x74
12 | 45 85 C0 test r8d, r8d
13 | 0F 84 90 00 00 00 je 0xB8
14 | 41 83 E8 01 sub r8d, 0x1
15 | 74 39 je 0x67
16 | 41 83 E8 01 sub r8d, 0x1
17 | 74 26 je 0x5A
18 | 41 83 E8 01 sub r8d, 0x1
19 | 74 13 je 0x4D
20 | 41 83 F8 01 cmp r8d, 0x1
21 | 75 78 jne 0xB8
22 | 48 8B D7 mov rdx, rdi
23 | 48 8B CB mov rcx, rbx
24 | E8 -- -- -- -- call ...
25 | EB 6B jmp 0xB3
26 | 48 8B D7 mov rdx, rdi
27 | 48 8B CB mov rcx, rbx
28 | E8 -- -- -- -- call ...
29 | EB 5E jmp 0xAE
30 | 48 8B D7 mov rdx, rdi
31 | 48 8B CB mov rcx, rbx
32 | E8 -- -- -- -- call ...
33 | EB 51 jmp 0xA9
34 | 48 8B D7 mov rdx, rdi
35 | 48 8B CB mov rcx, rbx
36 | E8 -- -- -- -- call ...
37 | EB 44 jmp 0xA4
38 | 48 8B D7 mov rdx, rdi
39 | 48 8B CB mov rcx, rbx
40 | E8 -- -- -- -- call ...
41 | EB 37 jmp 0x9F
42 | 41 83 F8 06 cmp r8d, 0x6
43 | 74 26 je 0x94
44 | 41 83 F8 07 cmp r8d, 0x7
45 | 74 13 je 0x87
46 | 41 83 F8 08 cmp r8d, 0x8
47 | 75 25 jne 0x9F
48 | 48 8B D7 mov rdx, rdi
49 | 48 8B CB mov rcx, rbx
50 | E8 -- -- -- -- call ...
51 | EB 18 jmp 0x9A
52 | 48 8B D7 mov rdx, rdi
53 | 48 8B CB mov rcx, rbx
54 | E8 -- -- -- -- call ...
55 | EB 0B jmp 0x95
56 | 48 8B D7 mov rdx, rdi
57 | 48 8B CB mov rcx, rbx
58 | E8 -- -- -- -- call ...
59 | 48 8B 5C 24 30 mov rbx, QWORD PTR [rsp+0x30]
60 | 48 83 C4 20 add rsp, 0x20
61 | 5F pop rdi
62 | C3 ret
63 |
--------------------------------------------------------------------------------
/spec/asm/windows/ood-x86_64/ret.asm:
--------------------------------------------------------------------------------
1 | C3 ret
2 |
--------------------------------------------------------------------------------
/spec/fixtures/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en-US
7 | CFBundleExecutable
8 | application
9 | CFBundleGetInfoString
10 | Custom Info
11 | CFBundleIconFile
12 | application.icns
13 | CFBundleIdentifier
14 | com.example.id
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleLongVersionString
18 | Version 3.14.15.92
19 | CFBundleName
20 | application
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | 3.14.15.92
25 | CFBundleSignature
26 | ????
27 | CFBundleSupportedPlatforms
28 |
29 | MacOSX
30 |
31 | CFBundleVersion
32 | 3.14.15.92
33 | CSResourcesFileMapped
34 |
35 | LSPrefersCarbon
36 | YES
37 | NSAppTransportSecurity
38 |
39 | NSAllowsArbitraryLoads
40 |
41 |
42 | NSAppleScriptEnabled
43 | YES
44 | NSHighResolutionCapable
45 |
46 | NSHumanReadableCopyright
47 | Custom Copyright
48 | NSMainNibFile
49 | MainMenu
50 | NSPrincipalClass
51 | NSApplication
52 |
53 |
54 |
--------------------------------------------------------------------------------
/spec/fixtures/PkgInfo:
--------------------------------------------------------------------------------
1 | APPL????
--------------------------------------------------------------------------------
/spec/fixtures/dummy:
--------------------------------------------------------------------------------
1 | ELF____________DUMMY_ELF_BINARY
--------------------------------------------------------------------------------
/spec/fixtures/dummy.app/Contents/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en-US
7 | CFBundleExecutable
8 | main
9 | CFBundleGetInfoString
10 | Custom Info
11 | CFBundleIconFile
12 | icon.icns
13 | CFBundleIdentifier
14 | com.example.id
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleLongVersionString
18 | Version 3.14.15.92
19 | CFBundleName
20 | dummy
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | 3.14.15.92
25 | CFBundleSignature
26 | ????
27 | CFBundleSupportedPlatforms
28 |
29 | MacOSX
30 |
31 | CFBundleVersion
32 | 3.14.15.92
33 | CSResourcesFileMapped
34 |
35 | LSPrefersCarbon
36 | YES
37 | NSAppTransportSecurity
38 |
39 | NSAllowsArbitraryLoads
40 |
41 |
42 | NSAppleScriptEnabled
43 | YES
44 | NSHighResolutionCapable
45 |
46 | NSHumanReadableCopyright
47 | Custom Copyright
48 | NSMainNibFile
49 | MainMenu
50 | NSPrincipalClass
51 | NSApplication
52 |
53 |
54 |
--------------------------------------------------------------------------------
/spec/fixtures/dummy.app/Contents/MacOS/main:
--------------------------------------------------------------------------------
1 | DUMMY_MACHO
2 |
--------------------------------------------------------------------------------
/spec/fixtures/dummy.app/Contents/PkgInfo:
--------------------------------------------------------------------------------
1 | APPL????
--------------------------------------------------------------------------------
/spec/fixtures/dummy.app/Contents/Resources/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/dummy.app/Contents/Resources/icon.icns
--------------------------------------------------------------------------------
/spec/fixtures/dummy.exe:
--------------------------------------------------------------------------------
1 | DUMMY_PE_EXE
2 |
--------------------------------------------------------------------------------
/spec/fixtures/dummy.exe.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/dummy.exe.zip
--------------------------------------------------------------------------------
/spec/fixtures/icon-to-icns.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | rm -rf icon.iconset
4 | mkdir icon.iconset
5 |
6 | convert -strip -resize 1024x1024 icon.png icon.iconset/icon_512x512@2x.png
7 | convert -strip -resize 512x512 icon.png icon.iconset/icon_512x512.png
8 | convert -strip -resize 512x512 icon.png icon.iconset/icon_256x256@2x.png
9 | convert -strip -resize 256x256 icon.png icon.iconset/icon_256x256.png
10 | convert -strip -resize 256x256 icon.png icon.iconset/icon_128x128@2x.png
11 | convert -strip -resize 128x128 icon.png icon.iconset/icon_128x128.png
12 | convert -strip -resize 64x64 icon.png icon.iconset/icon_32x32@2x.png
13 | convert -strip -resize 32x32 icon.png icon.iconset/icon_32x32.png
14 | convert -strip -resize 32x32 icon.png icon.iconset/icon_16x16@2x.png
15 | convert -strip -resize 16x16 icon.png icon.iconset/icon_16x16.png
16 |
17 | iconutil -c icns icon.iconset
18 | rm -rf icon.iconset
19 |
--------------------------------------------------------------------------------
/spec/fixtures/icon-to-ico.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | rm -rf icon.icoset
4 | mkdir icon.icoset
5 |
6 | convert icon.png -strip -resize 256x256 icon.icoset/icon_256x256.png
7 | convert icon.png -strip -resize 128x128 icon.icoset/icon_128x128.bmp
8 | convert icon.png -strip -resize 64x64 icon.icoset/icon_64x64.bmp
9 | convert icon.png -strip -resize 48x48 icon.icoset/icon_48x38.bmp
10 | convert icon.png -strip -resize 48x48 -flatten -colors 256 -type palette bmp3:icon.icoset/icon_48x38i.bmp
11 | convert icon.png -strip -resize 48x48 -flatten -colors 16 -type palette -depth 4 bmp3:icon.icoset/icon_48x38d4.bmp
12 | convert icon.png -strip -resize 32x32 icon.icoset/icon_32x32.bmp
13 | convert icon.png -strip -resize 32x32 -flatten -colors 256 -type palette bmp3:icon.icoset/icon_32x32i.bmp
14 | convert icon.png -strip -resize 32x32 -flatten -colors 16 -type palette -depth 4 bmp3:icon.icoset/icon_32x32d4.bmp
15 | convert icon.png -strip -resize 24x24 icon.icoset/icon_24x24.bmp
16 | convert icon.png -strip -resize 24x24 -flatten -colors 256 -type palette bmp3:icon.icoset/icon_24x24i.bmp
17 | convert icon.png -strip -resize 24x24 -flatten -colors 16 -type palette -depth 4 bmp3:icon.icoset/icon_24x24d4.bmp
18 | convert icon.png -strip -resize 16x16 icon.icoset/icon_16x16.bmp
19 | convert icon.png -strip -resize 16x16 -flatten -colors 256 -type palette bmp3:icon.icoset/icon_16x16i.bmp
20 | convert icon.png -strip -resize 16x16 -flatten -colors 16 -type palette -depth 4 bmp3:icon.icoset/icon_16x16d4.bmp
21 |
22 | convert icon.icoset/*.png icon.icoset/*.bmp icon.ico
23 | rm -rf icon.icoset
24 |
--------------------------------------------------------------------------------
/spec/fixtures/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/icon.icns
--------------------------------------------------------------------------------
/spec/fixtures/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/icon.ico
--------------------------------------------------------------------------------
/spec/fixtures/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/icon.png
--------------------------------------------------------------------------------
/spec/fixtures/image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/image.jpg
--------------------------------------------------------------------------------
/spec/fixtures/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/image.png
--------------------------------------------------------------------------------
/spec/fixtures/image.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/image.swf
--------------------------------------------------------------------------------
/spec/fixtures/swf14-lzma.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/swf14-lzma.swf
--------------------------------------------------------------------------------
/spec/fixtures/swf14-zlib.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/swf14-zlib.swf
--------------------------------------------------------------------------------
/spec/fixtures/swf14.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/swf14.swf
--------------------------------------------------------------------------------
/spec/fixtures/swf3.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/swf3.swf
--------------------------------------------------------------------------------
/spec/fixtures/swf4-loadmovie.as:
--------------------------------------------------------------------------------
1 | loadMovie("image.swf", "/");
2 |
--------------------------------------------------------------------------------
/spec/fixtures/swf4-loadmovie.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -o errexit
3 | set -o nounset
4 | set -o pipefail
5 |
6 | makeswf \
7 | -c -1 \
8 | -v 4 \
9 | -s 600x400 \
10 | -r 30 \
11 | -b ffffff \
12 | -o swf4-loadmovie.swf \
13 | swf4-loadmovie.as
14 | rm swf4-loadmovie.swf.*.pp
15 |
--------------------------------------------------------------------------------
/spec/fixtures/swf4-loadmovie.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/swf4-loadmovie.swf
--------------------------------------------------------------------------------
/spec/fixtures/swf6-loadmovie.as:
--------------------------------------------------------------------------------
1 | createEmptyMovieClip("img", 1);
2 | img.loadMovie("image.jpg");
3 |
4 | createTextField("txt", 2, 0, 0, 600, 400);
5 | txt.text = _root._url;
6 |
--------------------------------------------------------------------------------
/spec/fixtures/swf6-loadmovie.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -o errexit
3 | set -o nounset
4 | set -o pipefail
5 |
6 | makeswf \
7 | -c -1 \
8 | -v 6 \
9 | -s 600x400 \
10 | -r 30 \
11 | -b ffffff \
12 | -o swf6-loadmovie.swf \
13 | swf6-loadmovie.as
14 | rm swf6-loadmovie.swf.*.pp
15 |
--------------------------------------------------------------------------------
/spec/fixtures/swf6-loadmovie.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/swf6-loadmovie.swf
--------------------------------------------------------------------------------
/spec/fixtures/swf6-showmenu-false.as:
--------------------------------------------------------------------------------
1 | createEmptyMovieClip("mc", 1);
2 | mc.lineStyle(2, 0x00FF00, 100);
3 | mc.beginFill(0x0000FF, 100);
4 | mc.moveTo(0, 0);
5 | mc.lineTo(600, 0);
6 | mc.lineTo(600, 400);
7 | mc.lineTo(0, 400);
8 | mc.lineTo(0, 0);
9 | getURL("FSCommand:showmenu", "false");
10 |
--------------------------------------------------------------------------------
/spec/fixtures/swf6-showmenu-false.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -o errexit
3 | set -o nounset
4 | set -o pipefail
5 |
6 | makeswf \
7 | -c -1 \
8 | -v 6 \
9 | -s 600x400 \
10 | -r 30 \
11 | -b ffffff \
12 | -o swf6-showmenu-false.swf \
13 | swf6-showmenu-false.as
14 | rm swf6-showmenu-false.swf.*.pp
15 |
--------------------------------------------------------------------------------
/spec/fixtures/swf6-showmenu-false.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/swf6-showmenu-false.swf
--------------------------------------------------------------------------------
/spec/fixtures/swf6-zlib.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/swf6-zlib.swf
--------------------------------------------------------------------------------
/spec/fixtures/swf6.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shockpkg/swf-projector/b499f081159e34753472ce28e75e17d7991939e6/spec/fixtures/swf6.swf
--------------------------------------------------------------------------------
/src/bundle/html.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {ok} from 'node:assert';
3 | import {join as pathJoin} from 'node:path';
4 |
5 | import {cleanBundlesDir, fixtureFile} from '../util.spec.ts';
6 | import {Bundle} from '../bundle.ts';
7 |
8 | import {BundleHtml} from './html.ts';
9 |
10 | const getDir = async (d: string) => cleanBundlesDir('html', d);
11 |
12 | void describe('bundle/html', () => {
13 | void describe('BundleHtml', () => {
14 | void it('instanceof', () => {
15 | ok(BundleHtml.prototype instanceof Bundle);
16 | });
17 |
18 | void it('simple', async () => {
19 | const dir = await getDir('simple');
20 | const dest = pathJoin(dir, 'application.html');
21 |
22 | const b = new BundleHtml(dest);
23 | const p = b.projector;
24 | p.src = 'movie.swf';
25 | p.width = 600;
26 | p.height = 400;
27 | await b.write(async b => {
28 | await b.copyResource('movie.swf', fixtureFile('swf3.swf'));
29 | });
30 | });
31 |
32 | void it('complex', async () => {
33 | const dir = await getDir('complex');
34 | const dest = pathJoin(dir, 'application.html');
35 |
36 | const b = new BundleHtml(dest);
37 | const p = b.projector;
38 | p.src = 'movie.swf';
39 | p.width = 600;
40 | p.height = 400;
41 | p.lang = 'en-US';
42 | p.title = 'A "special" title with characters';
43 | p.background = '#000000';
44 | p.color = '#999999';
45 | p.bgcolor = '#000000';
46 | p.id = 'element-id';
47 | p.name = 'element-name';
48 | p.codebase =
49 | 'https://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=32,0,0,0';
50 | p.pluginspage = 'https://www.adobe.com/go/getflashplayer';
51 | p.play = true;
52 | p.loop = true;
53 | p.menu = true;
54 | p.quality = 'high';
55 | p.scale = 'default';
56 | p.align = 'l';
57 | p.salign = 'l';
58 | p.wmode = 'opaque';
59 | p.base = '.';
60 | p.allowFullScreen = true;
61 | p.allowFullScreenInteractive = true;
62 | p.allowScriptAccess = 'always';
63 | p.allowNetworking = 'all';
64 | p.fullScreenAspectRatio = 'landscape';
65 | p.flashvars = 'param1=value1¶m2=value2';
66 | p.browserzoom = 'scale';
67 | p.devicefont = false;
68 | p.swliveconnect = true;
69 | p.expressinstall = 'expressinstall.swf';
70 | p.swfversion = 32;
71 | await b.write(async b => {
72 | await b.copyResource('movie.swf', fixtureFile('swf3.swf'));
73 | });
74 | });
75 |
76 | void it('flat', async () => {
77 | const dir = await getDir('flat');
78 | const dest = pathJoin(dir, 'application.html');
79 |
80 | const b = new BundleHtml(dest, true);
81 | const p = b.projector;
82 | p.src = 'movie.swf';
83 | p.width = 600;
84 | p.height = 400;
85 | await b.write(async b => {
86 | await b.copyResource('movie.swf', fixtureFile('swf3.swf'));
87 | });
88 | });
89 | });
90 | });
91 |
--------------------------------------------------------------------------------
/src/bundle/html.ts:
--------------------------------------------------------------------------------
1 | import {basename, dirname, join as pathJoin} from 'node:path';
2 | import {mkdir, writeFile} from 'node:fs/promises';
3 |
4 | import {Bundle} from '../bundle.ts';
5 | import {ProjectorHtml} from '../projector/html.ts';
6 | import {htmlEncode, trimExtension} from '../util.ts';
7 |
8 | /**
9 | * BundleHtml object.
10 | */
11 | export class BundleHtml extends Bundle {
12 | /**
13 | * ProjectorHtml instance.
14 | */
15 | public readonly projector: ProjectorHtml;
16 |
17 | /**
18 | * BundleHtml constructor.
19 | *
20 | * @param path Output path.
21 | * @param flat Flat bundle.
22 | */
23 | constructor(path: string, flat = false) {
24 | super(path, flat);
25 |
26 | this.projector = this._createProjector();
27 | }
28 |
29 | /**
30 | * Main application file extension.
31 | *
32 | * @returns File extension.
33 | */
34 | public get extension() {
35 | const a = basename(this.path).split('.');
36 | const ext = a.length > 1 ? a.pop() : '';
37 | if (!ext) {
38 | throw new Error('Failed to extract extension');
39 | }
40 | return `.${ext}`;
41 | }
42 |
43 | /**
44 | * Get the nested subdirectory.
45 | *
46 | * @returns Directory name.
47 | */
48 | public get subdir() {
49 | return trimExtension(basename(this.path), this.extension);
50 | }
51 |
52 | /**
53 | * Get the nested index.
54 | *
55 | * @returns File name.
56 | */
57 | public get index() {
58 | return `index${this.extension}`;
59 | }
60 |
61 | /**
62 | * Get launcher HTML code.
63 | *
64 | * @returns HTML code.
65 | */
66 | public getLauncher() {
67 | const {projector, subdir, index} = this;
68 | const {lang, title} = projector;
69 | const path = `${subdir}/${index}`;
70 | const url = path.split(/[/\\]/).map(encodeURIComponent).join('/');
71 | const hAttr = lang === null ? '' : ` lang="${htmlEncode(lang, true)}"`;
72 |
73 | return [
74 | '',
75 | ``,
76 | '
',
77 | ' ',
78 | ' ',
79 | ` `,
83 | ...(title === null
84 | ? []
85 | : [` ${htmlEncode(title)}`]),
86 | ' ',
87 | ' ',
88 | '',
89 | ''
90 | ]
91 | .map(s => s.replace(/^\s+/, s => '\t'.repeat(s.length)))
92 | .join('\n');
93 | }
94 |
95 | /**
96 | * @inheritdoc
97 | */
98 | protected async _close(): Promise {
99 | if (!this.flat) {
100 | await this._writeLauncher();
101 | }
102 | await super._close();
103 | }
104 |
105 | /**
106 | * @inheritdoc
107 | */
108 | protected _getProjectorPathNested(): string {
109 | return pathJoin(dirname(this.path), this.subdir, this.index);
110 | }
111 |
112 | /**
113 | * Create projector instance for the bundle.
114 | *
115 | * @returns Projector instance.
116 | */
117 | protected _createProjector() {
118 | return new ProjectorHtml(this._getProjectorPath());
119 | }
120 |
121 | /**
122 | * Write the launcher file.
123 | */
124 | protected async _writeLauncher() {
125 | const {path} = this;
126 | await mkdir(dirname(path), {recursive: true});
127 | await writeFile(path, this.getLauncher());
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/bundle/index.ts:
--------------------------------------------------------------------------------
1 | export * from './html.ts';
2 | export * from './sa.ts';
3 | export * from './sa/index.ts';
4 |
--------------------------------------------------------------------------------
/src/bundle/sa.spec.ts:
--------------------------------------------------------------------------------
1 | import {mkdir, writeFile} from 'node:fs/promises';
2 | import {join as pathJoin, basename, dirname} from 'node:path';
3 |
4 | import {trimExtension} from '../util.ts';
5 | import {ProjectorSaDummy} from '../projector/sa.spec.ts';
6 |
7 | import {BundleSa} from './sa.ts';
8 |
9 | export class BundleSaDummy extends BundleSa {
10 | public readonly projector: ProjectorSaDummy;
11 |
12 | constructor(path: string, flat = false) {
13 | super(path, flat);
14 |
15 | this.projector = this._createProjector();
16 | }
17 |
18 | public get extension() {
19 | return '.exe';
20 | }
21 |
22 | protected _getProjectorPathNested(): string {
23 | const {path, extension} = this;
24 | const directory = trimExtension(path, extension, true);
25 | if (directory === path) {
26 | throw new Error(`Output path must end with: ${extension}`);
27 | }
28 | return pathJoin(directory, basename(path));
29 | }
30 |
31 | protected _createProjector() {
32 | return new ProjectorSaDummy(this._getProjectorPath());
33 | }
34 |
35 | protected async _writeLauncher() {
36 | await mkdir(dirname(this.path), {recursive: true});
37 | await writeFile(this.path, 'DUMMY_PE_LAUNCHER_EXE\n', 'utf8');
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/bundle/sa.ts:
--------------------------------------------------------------------------------
1 | import {ProjectorSa} from '../projector/sa.ts';
2 | import {Bundle} from '../bundle.ts';
3 |
4 | /**
5 | * BundleSa object.
6 | */
7 | export abstract class BundleSa extends Bundle {
8 | /**
9 | * ProjectorSa instance.
10 | */
11 | public abstract readonly projector: ProjectorSa;
12 |
13 | /**
14 | * BundleSa constructor.
15 | *
16 | * @param path Output path.
17 | * @param flat Flat bundle.
18 | */
19 | constructor(path: string, flat = false) {
20 | super(path, flat);
21 | }
22 |
23 | /**
24 | * @inheritdoc
25 | */
26 | protected async _close(): Promise {
27 | if (!this.flat) {
28 | await this._writeLauncher();
29 | }
30 | await super._close();
31 | }
32 |
33 | /**
34 | * Main application file extension.
35 | *
36 | * @returns File extension.
37 | */
38 | public abstract get extension(): string;
39 |
40 | /**
41 | * Create projector instance for the bundle.
42 | *
43 | * @returns Projector instance.
44 | */
45 | protected abstract _createProjector(): ProjectorSa;
46 |
47 | /**
48 | * Write the launcher file.
49 | */
50 | protected abstract _writeLauncher(): Promise;
51 | }
52 |
--------------------------------------------------------------------------------
/src/bundle/sa/index.ts:
--------------------------------------------------------------------------------
1 | export * from './windows.ts';
2 | export * from './mac.ts';
3 | export * from './linux.ts';
4 |
--------------------------------------------------------------------------------
/src/bundle/sa/linux.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {ok} from 'node:assert';
3 | import {join as pathJoin} from 'node:path';
4 |
5 | import {listSamples} from '../../projector/sa/linux.spec.ts';
6 | import {
7 | cleanBundlesDir,
8 | fixtureFile,
9 | getPackageFile,
10 | simpleSwf
11 | } from '../../util.spec.ts';
12 | import {loader} from '../../loader.ts';
13 | import {BundleSa} from '../sa.ts';
14 |
15 | import {BundleSaLinux} from './linux.ts';
16 |
17 | void describe('bundle/sa/linux', () => {
18 | void describe('BundleSaLinux', () => {
19 | void it('instanceof', () => {
20 | ok(BundleSaLinux.prototype instanceof BundleSa);
21 | });
22 |
23 | for (const pkg of listSamples()) {
24 | const getDir = async (d: string) =>
25 | cleanBundlesDir('sa', 'linux', pkg.type, pkg.name, d);
26 | const getPlayer = async () => getPackageFile(pkg.name);
27 | const simple = fixtureFile(simpleSwf(pkg.zlib, pkg.lzma));
28 |
29 | void describe(pkg.name, () => {
30 | void it('simple', async () => {
31 | const dir = await getDir('simple');
32 | const dest = pathJoin(dir, 'application');
33 |
34 | const b = new BundleSaLinux(dest);
35 | b.projector.patchProjectorOffset = pkg.patchProjectorOffset;
36 | b.projector.player = await getPlayer();
37 | b.projector.movieFile = simple;
38 | await b.write();
39 | });
40 |
41 | if (pkg.version[0] < 6) {
42 | return;
43 | }
44 |
45 | void it('complex', async () => {
46 | const dir = await getDir('complex');
47 | const dest = pathJoin(dir, 'application');
48 |
49 | const b = new BundleSaLinux(dest);
50 | b.projector.patchProjectorOffset = pkg.patchProjectorOffset;
51 | b.projector.patchProjectorPath = true;
52 | b.projector.player = await getPlayer();
53 | b.projector.movieData = loader(
54 | 6,
55 | 600,
56 | 400,
57 | 30,
58 | 0xffffff,
59 | 'main.swf',
60 | 30 / 2
61 | );
62 | await b.write(async b => {
63 | await b.copyResource(
64 | 'main.swf',
65 | fixtureFile('swf6-loadmovie.swf')
66 | );
67 | await b.copyResource(
68 | 'image.jpg',
69 | fixtureFile('image.jpg')
70 | );
71 | });
72 | });
73 |
74 | void it('flat', async () => {
75 | const dir = await getDir('flat');
76 | const dest = pathJoin(dir, 'application');
77 |
78 | const b = new BundleSaLinux(dest, true);
79 | b.projector.patchProjectorOffset = pkg.patchProjectorOffset;
80 | b.projector.patchProjectorPath = true;
81 | b.projector.player = await getPlayer();
82 | b.projector.movieData = loader(
83 | 6,
84 | 600,
85 | 400,
86 | 30,
87 | 0xffffff,
88 | 'main.swf',
89 | 30 / 2
90 | );
91 | await b.write(async b => {
92 | await b.copyResource(
93 | 'main.swf',
94 | fixtureFile('swf6-loadmovie.swf')
95 | );
96 | await b.copyResource(
97 | 'image.jpg',
98 | fixtureFile('image.jpg')
99 | );
100 | });
101 | });
102 | });
103 | }
104 | });
105 | });
106 |
--------------------------------------------------------------------------------
/src/bundle/sa/linux.ts:
--------------------------------------------------------------------------------
1 | import {mkdir, open, writeFile} from 'node:fs/promises';
2 | import {join as pathJoin, basename, dirname} from 'node:path';
3 |
4 | import {linuxLauncher} from '../../util/linux.ts';
5 | import {EM_386, EM_X86_64} from '../../util/internal/linux/elf.ts';
6 | import {ProjectorSaLinux} from '../../projector/sa/linux.ts';
7 | import {BundleSa} from '../sa.ts';
8 |
9 | /**
10 | * BundleSaLinux object.
11 | */
12 | export class BundleSaLinux extends BundleSa {
13 | /**
14 | * ProjectorSaLinux instance.
15 | */
16 | public readonly projector: ProjectorSaLinux;
17 |
18 | /**
19 | * BundleSaLinux constructor.
20 | *
21 | * @param path Output path for the main application.
22 | * @param flat Flat bundle.
23 | */
24 | constructor(path: string, flat = false) {
25 | super(path, flat);
26 |
27 | this.projector = this._createProjector();
28 | }
29 |
30 | /**
31 | * @inheritdoc
32 | */
33 | public get extension() {
34 | return '';
35 | }
36 |
37 | /**
38 | * @inheritdoc
39 | */
40 | protected _getProjectorPathNested(): string {
41 | const {path} = this;
42 | return pathJoin(`${path}.data`, basename(path));
43 | }
44 |
45 | /**
46 | * @inheritdoc
47 | */
48 | protected _createProjector() {
49 | return new ProjectorSaLinux(this._getProjectorPath());
50 | }
51 |
52 | /**
53 | * @inheritdoc
54 | */
55 | protected async _writeLauncher() {
56 | const {path, projector} = this;
57 |
58 | let stat = null;
59 | const d = new Uint8Array(2);
60 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
61 | const f = await open(projector.path, 'r');
62 | try {
63 | stat = await f.stat();
64 | const r = await f.read(d, 0, 2, 18);
65 | if (r.bytesRead < 2) {
66 | throw new Error('Unknown format');
67 | }
68 | } finally {
69 | await f.close();
70 | }
71 |
72 | const machine = v.getUint16(0, true);
73 | let launcher = null;
74 | switch (machine) {
75 | case EM_386: {
76 | launcher = await linuxLauncher('i386');
77 | break;
78 | }
79 | case EM_X86_64: {
80 | launcher = await linuxLauncher('x86_64');
81 | break;
82 | }
83 | default: {
84 | throw new Error(`Unknown machine type: ${machine}`);
85 | }
86 | }
87 |
88 | await mkdir(dirname(path), {recursive: true});
89 | await writeFile(path, launcher, {
90 | mode: stat.mode
91 | });
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/bundle/sa/mac.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {ok} from 'node:assert';
3 | import {join as pathJoin} from 'node:path';
4 |
5 | import {listSamples, customWindowTitle} from '../../projector/sa/mac.spec.ts';
6 | import {
7 | cleanBundlesDir,
8 | fixtureFile,
9 | getPackageFile,
10 | simpleSwf
11 | } from '../../util.spec.ts';
12 | import {loader} from '../../loader.ts';
13 | import {BundleSa} from '../sa.ts';
14 |
15 | import {BundleSaMac} from './mac.ts';
16 |
17 | void describe('bundle/sa/mac', () => {
18 | void describe('BundleSaMac', () => {
19 | void it('instanceof', () => {
20 | ok(BundleSaMac.prototype instanceof BundleSa);
21 | });
22 |
23 | for (const pkg of listSamples()) {
24 | const getDir = async (d: string) =>
25 | cleanBundlesDir('sa', 'mac', pkg.name, d);
26 | const getPlayer = async () => getPackageFile(pkg.name);
27 | const simple = fixtureFile(simpleSwf(pkg.zlib, pkg.lzma));
28 |
29 | void describe(pkg.name, () => {
30 | void it('simple', async () => {
31 | const dir = await getDir('simple');
32 | const dest = pathJoin(dir, 'application.app');
33 |
34 | const b = new BundleSaMac(dest);
35 | b.projector.removeCodeSignature = true;
36 | b.projector.player = await getPlayer();
37 | b.projector.movieFile = simple;
38 | await b.write();
39 | });
40 |
41 | if (pkg.version[0] < 6) {
42 | return;
43 | }
44 |
45 | void it('complex', async () => {
46 | const dir = await getDir('complex');
47 | const dest = pathJoin(dir, 'application.app');
48 |
49 | const b = new BundleSaMac(dest);
50 | b.projector.removeCodeSignature = true;
51 | b.projector.iconFile = fixtureFile('icon.icns');
52 | b.projector.infoPlistFile = fixtureFile('Info.plist');
53 | b.projector.pkgInfoFile = fixtureFile('PkgInfo');
54 | b.projector.binaryName = 'application';
55 | b.projector.bundleName = 'App Bundle Name';
56 | b.projector.removeInfoPlistStrings = true;
57 | if (pkg.version[0] >= 11) {
58 | b.projector.patchWindowTitle = customWindowTitle;
59 | }
60 | b.projector.player = await getPlayer();
61 | b.projector.movieData = loader(
62 | 6,
63 | 600,
64 | 400,
65 | 30,
66 | 0xffffff,
67 | 'main.swf'
68 | );
69 | await b.write(async b => {
70 | await b.copyResource(
71 | 'main.swf',
72 | fixtureFile('swf6-loadmovie.swf')
73 | );
74 | await b.copyResource(
75 | 'image.jpg',
76 | fixtureFile('image.jpg')
77 | );
78 | });
79 | });
80 |
81 | void it('flat', async () => {
82 | const dir = await getDir('flat');
83 | const dest = pathJoin(dir, 'application.app');
84 |
85 | const b = new BundleSaMac(dest, true);
86 | b.projector.removeCodeSignature = true;
87 | b.projector.player = await getPlayer();
88 | b.projector.movieData = loader(
89 | 6,
90 | 600,
91 | 400,
92 | 30,
93 | 0xffffff,
94 | 'main.swf'
95 | );
96 | await b.write(async b => {
97 | await b.copyResource(
98 | 'main.swf',
99 | fixtureFile('swf6-loadmovie.swf')
100 | );
101 | await b.copyResource(
102 | 'image.jpg',
103 | fixtureFile('image.jpg')
104 | );
105 | });
106 | });
107 | });
108 | }
109 | });
110 | });
111 |
--------------------------------------------------------------------------------
/src/bundle/sa/mac.ts:
--------------------------------------------------------------------------------
1 | import {copyFile, mkdir, readFile, stat, writeFile} from 'node:fs/promises';
2 | import {join as pathJoin, basename, dirname} from 'node:path';
3 |
4 | import {fsLstatExists} from '@shockpkg/archive-files';
5 | import {Plist, ValueDict, ValueString} from '@shockpkg/plist-dom';
6 |
7 | import {trimExtension} from '../../util.ts';
8 | import {machoTypesFile, machoAppLauncher} from '../../util/mac.ts';
9 | import {ProjectorSaMac} from '../../projector/sa/mac.ts';
10 | import {BundleSa} from '../sa.ts';
11 |
12 | /**
13 | * BundleSaMac object.
14 | */
15 | export class BundleSaMac extends BundleSa {
16 | /**
17 | * ProjectorSaMac instance.
18 | */
19 | public readonly projector: ProjectorSaMac;
20 |
21 | /**
22 | * BundleSaMac constructor.
23 | *
24 | * @param path Output path for the main application.
25 | * @param flat Flat bundle.
26 | */
27 | constructor(path: string, flat = false) {
28 | super(path, flat);
29 |
30 | this.projector = this._createProjector();
31 | }
32 |
33 | /**
34 | * @inheritdoc
35 | */
36 | public get extension() {
37 | return '.app';
38 | }
39 |
40 | /**
41 | * Get the launcher name.
42 | *
43 | * @returns Launcher name.
44 | */
45 | protected _getLauncherName() {
46 | return trimExtension(basename(this.path), this.extension, true);
47 | }
48 |
49 | /**
50 | * @inheritdoc
51 | */
52 | protected _getProjectorPathNested(): string {
53 | const projName = `${this._getLauncherName()}${this.extension}`;
54 | return pathJoin(this.path, 'Contents', 'Resources', projName);
55 | }
56 |
57 | /**
58 | * @inheritdoc
59 | */
60 | protected _createProjector() {
61 | return new ProjectorSaMac(this._getProjectorPath());
62 | }
63 |
64 | /**
65 | * @inheritdoc
66 | */
67 | protected async _writeLauncher() {
68 | const {path, projector} = this;
69 |
70 | // Create paths to things to create.
71 | const appContents = pathJoin(path, 'Contents');
72 | const appMacOS = pathJoin(appContents, 'MacOS');
73 | const appResources = pathJoin(appContents, 'Resources');
74 | const appInfoPlist = pathJoin(appContents, 'Info.plist');
75 | const appPkgInfo = pathJoin(appContents, 'PkgInfo');
76 |
77 | // Read the projector Info.plist.
78 | const plist = new Plist();
79 | plist.fromXml(await readFile(projector.infoPlistPath, 'utf8'));
80 | const dict = plist.getValue().castAs(ValueDict);
81 |
82 | // Get the binary path and read the types.
83 | const projBinaryName = dict
84 | .getValue('CFBundleExecutable')
85 | .castAs(ValueString).value;
86 | const projBinaryPath = projector.getBinaryPath(projBinaryName);
87 | const projBinaryTypes = await machoTypesFile(projBinaryPath);
88 |
89 | // Get the icon path.
90 | const projIconName = dict
91 | .getValue('CFBundleIconFile')
92 | .castAs(ValueString).value;
93 | const projIconPath = projector.getIconPath(projIconName);
94 |
95 | // Get the PkgInfo path.
96 | const projPkgInfoPath = projector.pkgInfoPath;
97 |
98 | // Create the launcher binary with the same types and mode.
99 | const launcherName = this._getLauncherName();
100 | const launcherPath = pathJoin(appMacOS, launcherName);
101 | await mkdir(dirname(launcherPath), {recursive: true});
102 | await writeFile(launcherPath, await machoAppLauncher(projBinaryTypes), {
103 | mode: (await stat(projBinaryPath)).mode
104 | });
105 |
106 | // Copy the projector icon if present.
107 | const pathIcon = pathJoin(appResources, projIconName);
108 | if (await fsLstatExists(projIconPath)) {
109 | await copyFile(projIconPath, pathIcon);
110 | }
111 |
112 | // Copy PkgInfo if present.
113 | if (await fsLstatExists(projPkgInfoPath)) {
114 | await copyFile(projPkgInfoPath, appPkgInfo);
115 | }
116 |
117 | // Update the executable name in the plist for the launcher.
118 | dict.set('CFBundleExecutable', new ValueString(launcherName));
119 |
120 | // Write the updated Info.plist.
121 | await mkdir(dirname(appInfoPlist), {recursive: true});
122 | await writeFile(appInfoPlist, plist.toXml(), 'utf8');
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/bundle/sa/windows.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {ok} from 'node:assert';
3 | import {join as pathJoin} from 'node:path';
4 |
5 | import {
6 | listSamples,
7 | customWindowTitle,
8 | versionStrings
9 | } from '../../projector/sa/windows.spec.ts';
10 | import {
11 | cleanBundlesDir,
12 | fixtureFile,
13 | getPackageFile,
14 | simpleSwf
15 | } from '../../util.spec.ts';
16 | import {loader} from '../../loader.ts';
17 | import {BundleSa} from '../sa.ts';
18 |
19 | import {BundleSaWindows} from './windows.ts';
20 |
21 | void describe('bundle/sa/windows', () => {
22 | void describe('BundleSaWindows', () => {
23 | void it('instanceof', () => {
24 | ok(BundleSaWindows.prototype instanceof BundleSa);
25 | });
26 |
27 | for (const pkg of listSamples()) {
28 | const getDir = async (d: string) =>
29 | cleanBundlesDir('sa', 'windows', pkg.type, pkg.name, d);
30 | const getPlayer = async () => getPackageFile(pkg.name);
31 | const simple = fixtureFile(simpleSwf(pkg.zlib, pkg.lzma));
32 |
33 | void describe(pkg.name, () => {
34 | void it('simple', async () => {
35 | const dir = await getDir('simple');
36 | const dest = pathJoin(dir, 'application.exe');
37 |
38 | const b = new BundleSaWindows(dest);
39 | b.projector.removeCodeSignature = true;
40 | b.projector.patchOutOfDateDisable =
41 | pkg.patchOutOfDateDisable;
42 | b.projector.player = await getPlayer();
43 | b.projector.movieFile = simple;
44 | await b.write();
45 | });
46 |
47 | if (pkg.version[0] < 4) {
48 | return;
49 | }
50 |
51 | const swfv = pkg.version[0] < 5 ? 4 : 5;
52 | const movies =
53 | pkg.version[0] < 6
54 | ? ['swf4-loadmovie.swf', 'image.swf']
55 | : ['swf6-loadmovie.swf', 'image.jpg'];
56 |
57 | void it('complex', async () => {
58 | const dir = await getDir('complex');
59 | const dest = pathJoin(dir, 'application.exe');
60 |
61 | const b = new BundleSaWindows(dest);
62 | b.projector.iconFile = fixtureFile('icon.ico');
63 | b.projector.versionStrings = versionStrings;
64 | b.projector.patchWindowTitle = customWindowTitle;
65 | b.projector.removeCodeSignature = true;
66 | b.projector.patchOutOfDateDisable =
67 | pkg.patchOutOfDateDisable;
68 | b.projector.player = await getPlayer();
69 | b.projector.movieData = loader(
70 | swfv,
71 | 600,
72 | 400,
73 | 30,
74 | 0xffffff,
75 | 'main.swf'
76 | );
77 | await b.write(async b => {
78 | await b.copyResource(
79 | 'main.swf',
80 | fixtureFile(movies[0])
81 | );
82 | await b.copyResource(movies[1], fixtureFile(movies[1]));
83 | });
84 | });
85 |
86 | void it('flat', async () => {
87 | const dir = await getDir('flat');
88 | const dest = pathJoin(dir, 'application.exe');
89 |
90 | const b = new BundleSaWindows(dest, true);
91 | b.projector.removeCodeSignature = true;
92 | b.projector.patchOutOfDateDisable =
93 | pkg.patchOutOfDateDisable;
94 | b.projector.player = await getPlayer();
95 | b.projector.movieData = loader(
96 | swfv,
97 | 600,
98 | 400,
99 | 30,
100 | 0xffffff,
101 | 'main.swf'
102 | );
103 | await b.write(async b => {
104 | await b.copyResource(
105 | 'main.swf',
106 | fixtureFile(movies[0])
107 | );
108 | await b.copyResource(movies[1], fixtureFile(movies[1]));
109 | });
110 | });
111 | });
112 | }
113 | });
114 | });
115 |
--------------------------------------------------------------------------------
/src/bundle/sa/windows.ts:
--------------------------------------------------------------------------------
1 | import {mkdir, open, readFile, writeFile} from 'node:fs/promises';
2 | import {join as pathJoin, basename, dirname} from 'node:path';
3 |
4 | import {trimExtension} from '../../util.ts';
5 | import {windowsLauncher} from '../../util/windows.ts';
6 | import {ProjectorSaWindows} from '../../projector/sa/windows.ts';
7 | import {BundleSa} from '../sa.ts';
8 |
9 | /**
10 | * BundleSaWindows object.
11 | */
12 | export class BundleSaWindows extends BundleSa {
13 | /**
14 | * ProjectorSaWindows instance.
15 | */
16 | public readonly projector: ProjectorSaWindows;
17 |
18 | /**
19 | * BundleSaWindows constructor.
20 | *
21 | * @param path Output path for the main application.
22 | * @param flat Flat bundle.
23 | */
24 | constructor(path: string, flat = false) {
25 | super(path, flat);
26 |
27 | this.projector = this._createProjector();
28 | }
29 |
30 | /**
31 | * @inheritdoc
32 | */
33 | public get extension() {
34 | return '.exe';
35 | }
36 |
37 | /**
38 | * @inheritdoc
39 | */
40 | protected _getProjectorPathNested(): string {
41 | const {path, extension} = this;
42 | const directory = trimExtension(path, extension, true);
43 | if (directory === path) {
44 | throw new Error(`Output path must end with: ${extension}`);
45 | }
46 | return pathJoin(directory, basename(path));
47 | }
48 |
49 | /**
50 | * @inheritdoc
51 | */
52 | protected _createProjector() {
53 | return new ProjectorSaWindows(this._getProjectorPath());
54 | }
55 |
56 | /**
57 | * @inheritdoc
58 | */
59 | protected async _writeLauncher() {
60 | const {path, projector} = this;
61 |
62 | const d = new Uint8Array(4);
63 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
64 | const f = await open(projector.path, 'r');
65 | try {
66 | let r = await f.read(d, 0, 4, 60);
67 | if (r.bytesRead < 4) {
68 | throw new Error('Unknown format');
69 | }
70 | r = await f.read(d, 0, 2, v.getUint32(0, true) + 4);
71 | if (r.bytesRead < 2) {
72 | throw new Error('Unknown format');
73 | }
74 | } finally {
75 | await f.close();
76 | }
77 |
78 | const machine = v.getUint16(0, true);
79 | // eslint-disable-next-line jsdoc/require-jsdoc
80 | const res = async () => readFile(projector.path);
81 | let launcher = null;
82 | switch (machine) {
83 | case 0x14c: {
84 | launcher = await windowsLauncher('i686', res);
85 | break;
86 | }
87 | case 0x8664: {
88 | launcher = await windowsLauncher('x86_64', res);
89 | break;
90 | }
91 | default: {
92 | throw new Error(`Unknown machine type: ${machine}`);
93 | }
94 | }
95 |
96 | await mkdir(dirname(path), {recursive: true});
97 | await writeFile(path, launcher);
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './meta.ts';
2 | export * from './launchers.ts';
3 | export * from './util.ts';
4 | export * from './util/index.ts';
5 | export * from './queue.ts';
6 | export * from './loader.ts';
7 | export * from './projector.ts';
8 | export * from './projector/index.ts';
9 | export * from './bundle.ts';
10 | export * from './bundle/index.ts';
11 |
--------------------------------------------------------------------------------
/src/launchers.ts:
--------------------------------------------------------------------------------
1 | // This file contains compile-time defined variables.
2 |
3 | export const LAUNCHERS = '#{LAUNCHERS}' as unknown as Readonly<{
4 | [key: string]: string;
5 | }>;
6 |
--------------------------------------------------------------------------------
/src/loader.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {strictEqual} from 'node:assert';
3 |
4 | import {loader} from './loader.ts';
5 |
6 | function hex(buffer: Uint8Array) {
7 | return [...buffer]
8 | .map(b => b.toString(16).padStart(2, '0').toUpperCase())
9 | .join(' ');
10 | }
11 |
12 | const swf4 = [
13 | // Magic 'SWF'
14 | '46 57 53',
15 | // Version
16 | '04',
17 | // Size
18 | '39 00 00 00',
19 | // Frame size
20 | '78 00 05 DC 00 00 0F A0 00',
21 | // Frame rate
22 | '00 1E',
23 | // Frame count
24 | '01 00',
25 | // SetBackgroundColor
26 | '43 02 33 66 99',
27 | // DoAction
28 | '19 03',
29 | // > push 'other.swf'
30 | '96 0B 00 00 6F 74 68 65 72 2E 73 77 66 00',
31 | // > push '/'
32 | '96 03 00 00 2F 00',
33 | // > loadMovie
34 | '9A 01 00 40',
35 | // > end
36 | '00',
37 | // ShowFrame
38 | '40 00',
39 | // End
40 | '00 00'
41 | ].join(' ');
42 |
43 | const swf5 = [
44 | // Magic 'SWF'
45 | '46 57 53',
46 | // Version
47 | '05',
48 | // Size
49 | '43 00 00 00',
50 | // Frame size
51 | '78 00 05 DC 00 00 0F A0 00',
52 | // Frame rate
53 | '00 1E',
54 | // Frame count
55 | '01 00',
56 | // SetBackgroundColor
57 | '43 02 33 66 99',
58 | // DoAction
59 | '23 03',
60 | // > constants 'other.swf', '_level0'
61 | '88 14 00 02 00 6F 74 68 65 72 2E 73 77 66 00 5F 6C 65 76 65 6C 30 00',
62 | // > push c:0, c:1
63 | '96 04 00 08 00 08 01',
64 | // > loadMovie
65 | '9A 01 00 40',
66 | // > end
67 | '00',
68 | // ShowFrame
69 | '40 00',
70 | // End
71 | '00 00'
72 | ].join(' ');
73 |
74 | const swf5Complex = [
75 | // Magic 'SWF'
76 | '46 57 53',
77 | // Version
78 | '05',
79 | // Size
80 | '47 00 00 00',
81 | // Frame size
82 | '78 00 05 DD 40 00 0F A5 00',
83 | // Frame rate
84 | '80 1E',
85 | // Frame count
86 | '03 00',
87 | // SetBackgroundColor
88 | '43 02 33 66 99',
89 | // ShowFrame
90 | '40 00',
91 | // ShowFrame
92 | '40 00',
93 | // DoAction
94 | '23 03',
95 | // > constants 'other.swf', '_level0'
96 | '88 14 00 02 00 6F 74 68 65 72 2E 73 77 66 00 5F 6C 65 76 65 6C 30 00',
97 | // > push c:0, c:1
98 | '96 04 00 08 00 08 01',
99 | // > loadMovie
100 | '9A 01 00 40',
101 | // > end
102 | '00',
103 | // ShowFrame
104 | '40 00',
105 | // End
106 | '00 00'
107 | ].join(' ');
108 |
109 | void describe('loader', () => {
110 | void describe('loader', () => {
111 | void it('swfv: 4', () => {
112 | strictEqual(
113 | hex(loader(4, 600, 400, 30, 0x336699, 'other.swf')),
114 | swf4
115 | );
116 | });
117 |
118 | void it('swfv: 5', () => {
119 | strictEqual(
120 | hex(loader(5, 600, 400, 30, 0x336699, 'other.swf')),
121 | swf5
122 | );
123 | });
124 |
125 | void it('swfv: 5 complex', () => {
126 | strictEqual(
127 | hex(loader(5, 600.5, 400.5, 30.5, 0x336699, 'other.swf', 2)),
128 | swf5Complex
129 | );
130 | });
131 | });
132 | });
133 |
--------------------------------------------------------------------------------
/src/loader.ts:
--------------------------------------------------------------------------------
1 | import {stringEncode} from './swf/util.ts';
2 | import {Tag} from './swf/tag.ts';
3 | import {Swf} from './swf/swf.ts';
4 | import {concat} from './util/internal/data.ts';
5 |
6 | /**
7 | * Type string.
8 | *
9 | * @param str The string.
10 | * @returns Bytecode data.
11 | */
12 | function asvm1TypeString(str: string) {
13 | return concat([new Uint8Array(1), stringEncode(str)]);
14 | }
15 |
16 | /**
17 | * Action: ConstantPoolEnd.
18 | *
19 | * @param strs Constant strings.
20 | * @returns Bytecode data.
21 | */
22 | function asmv1ActionConstantPool(strs: string[]) {
23 | const data = concat([new Uint8Array(5), ...strs.map(stringEncode)]);
24 | const v = new DataView(data.buffer, data.byteOffset, data.byteLength);
25 | data[0] = 0x88;
26 | v.setUint16(1, data.length - 3, true);
27 | v.setUint16(3, strs.length, true);
28 | return data;
29 | }
30 |
31 | /**
32 | * Type constant 8-bit.
33 | *
34 | * @param i Constant index.
35 | * @returns Bytecode data.
36 | */
37 | function asvm1TypeConstant8(i: number) {
38 | const data = new Uint8Array(2);
39 | data[0] = 0x08;
40 | data[1] = i;
41 | return data;
42 | }
43 |
44 | /**
45 | * Action: Push.
46 | *
47 | * @param pushed Pushed data.
48 | * @returns Bytecode data.
49 | */
50 | function asmv1ActionPush(pushed: Uint8Array[]) {
51 | const data = concat([new Uint8Array(3), ...pushed]);
52 | const v = new DataView(data.buffer, data.byteOffset, data.byteLength);
53 | data[0] = 0x96;
54 | v.setUint16(1, data.length - 3, true);
55 | return data;
56 | }
57 |
58 | /**
59 | * Action: LoadMovie.
60 | *
61 | * @returns Bytecode data.
62 | */
63 | function asvm1ActionLoadMovie() {
64 | const data = new Uint8Array(4);
65 | const v = new DataView(data.buffer, data.byteOffset, data.byteLength);
66 | data[0] = 0x9a;
67 | v.setUint16(1, data.length - 3, true);
68 | v.setUint8(3, 0x40);
69 | return data;
70 | }
71 |
72 | /**
73 | * Action: End.
74 | *
75 | * @returns Bytecode data.
76 | */
77 | function asvm1ActionEnd() {
78 | return new Uint8Array(1);
79 | }
80 |
81 | /**
82 | * Bytecode in SWF4 format.
83 | *
84 | * @param url The URL to load.
85 | * @returns Bytecode data.
86 | */
87 | function bytecodeLoadMovieSwf4(url: string) {
88 | return concat([
89 | asmv1ActionPush([asvm1TypeString(url)]),
90 | asmv1ActionPush([asvm1TypeString('/')]),
91 | asvm1ActionLoadMovie(),
92 | asvm1ActionEnd()
93 | ]);
94 | }
95 |
96 | /**
97 | * Bytecode in SWF5 format.
98 | *
99 | * @param url The URL to load.
100 | * @returns Bytecode data.
101 | */
102 | function bytecodeLoadMovieSwf5(url: string) {
103 | return concat([
104 | asmv1ActionConstantPool([url, '_level0']),
105 | asmv1ActionPush([asvm1TypeConstant8(0), asvm1TypeConstant8(1)]),
106 | asvm1ActionLoadMovie(),
107 | asvm1ActionEnd()
108 | ]);
109 | }
110 |
111 | /**
112 | * Generate a loader stub SWF movie.
113 | * Optionally include a delay to give player a change to initialize first.
114 | *
115 | * @param swfv SWF format version number.
116 | * @param width Frame width.
117 | * @param height Frame height.
118 | * @param fps Frames-per-second.
119 | * @param color Background color.
120 | * @param url The URL to load.
121 | * @param delay Number of frame to delay loading.
122 | * @returns Movie data.
123 | */
124 | export function loader(
125 | swfv: number,
126 | width: number,
127 | height: number,
128 | fps: number,
129 | color: number,
130 | url: string,
131 | delay = 0
132 | ) {
133 | if (swfv < 4) {
134 | throw new Error('SWF format version must be 4+');
135 | }
136 | delay = delay < 0 ? 0 : delay;
137 |
138 | const swf = new Swf();
139 | swf.version = swfv;
140 | swf.frameSize.xMax = Math.round(width * 20);
141 | swf.frameSize.yMax = Math.round(height * 20);
142 | swf.frameRate.value = fps;
143 | swf.frameCount = delay + 1;
144 |
145 | const setBackgroundColor = new Tag();
146 | setBackgroundColor.code = 9;
147 | setBackgroundColor.data = new Uint8Array(3);
148 | // eslint-disable-next-line no-bitwise
149 | setBackgroundColor.data[2] = color & 0xff;
150 | // eslint-disable-next-line no-bitwise
151 | setBackgroundColor.data[1] = (color >> 8) & 0xff;
152 | // eslint-disable-next-line no-bitwise
153 | setBackgroundColor.data[0] = (color >> 16) & 0xff;
154 | swf.tags.push(setBackgroundColor);
155 |
156 | const showFrame = new Tag();
157 | showFrame.code = 1;
158 | for (let i = 0; i < delay; i++) {
159 | swf.tags.push(showFrame);
160 | }
161 |
162 | const doAction = new Tag();
163 | doAction.code = 12;
164 | doAction.data =
165 | swfv < 5 ? bytecodeLoadMovieSwf4(url) : bytecodeLoadMovieSwf5(url);
166 | swf.tags.push(doAction, showFrame);
167 |
168 | const end = new Tag();
169 | end.code = 0;
170 | swf.tags.push(end);
171 |
172 | return swf.encode();
173 | }
174 |
--------------------------------------------------------------------------------
/src/meta.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {match} from 'node:assert';
3 |
4 | import {NAME, VERSION} from './meta.ts';
5 |
6 | void describe('meta', () => {
7 | void it('NAME', () => {
8 | match(NAME, /^(@[\d._a-z-]+\/)?[\d._a-z-]+/);
9 | });
10 |
11 | void it('VERSION', () => {
12 | match(VERSION, /^\d+\.\d+\.\d+/);
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/meta.ts:
--------------------------------------------------------------------------------
1 | // This file contains compile-time defined variables.
2 |
3 | export const NAME: string = '#{NAME}';
4 |
5 | export const VERSION: string = '#{VERSION}';
6 |
--------------------------------------------------------------------------------
/src/projector.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Projector object.
3 | */
4 | export abstract class Projector {
5 | /**
6 | * Set the nobrowse option on mounted disk images.
7 | */
8 | public nobrowse = false;
9 |
10 | /**
11 | * Output path.
12 | */
13 | public readonly path: string;
14 |
15 | /**
16 | * Projector constructor.
17 | *
18 | * @param path Output path.
19 | */
20 | constructor(path: string) {
21 | this.path = path;
22 | }
23 |
24 | /**
25 | * Write out the projector.
26 | */
27 | public abstract write(): Promise;
28 | }
29 |
--------------------------------------------------------------------------------
/src/projector/html.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {ok} from 'node:assert';
3 | import {copyFile} from 'node:fs/promises';
4 | import {join as pathJoin} from 'node:path';
5 |
6 | import {cleanProjectorDir, fixtureFile} from '../util.spec.ts';
7 | import {Projector} from '../projector.ts';
8 |
9 | import {ProjectorHtml} from './html.ts';
10 |
11 | const getDir = async (d: string) => cleanProjectorDir('html', d);
12 |
13 | void describe('projector/html', () => {
14 | void describe('ProjectorHtml', () => {
15 | void it('instanceof', () => {
16 | ok(ProjectorHtml.prototype instanceof Projector);
17 | });
18 |
19 | void it('simple', async () => {
20 | const dir = await getDir('simple');
21 | const dest = pathJoin(dir, 'page.html');
22 |
23 | const p = new ProjectorHtml(dest);
24 | p.src = 'movie.swf';
25 | p.width = 600;
26 | p.height = 400;
27 | await p.write();
28 |
29 | await copyFile(fixtureFile('swf3.swf'), pathJoin(dir, 'movie.swf'));
30 | });
31 |
32 | void it('complex', async () => {
33 | const dir = await getDir('complex');
34 | const dest = pathJoin(dir, 'page.html');
35 |
36 | const p = new ProjectorHtml(dest);
37 | p.src = 'movie.swf';
38 | p.width = 600;
39 | p.height = 400;
40 | p.lang = 'en-US';
41 | p.title = 'A "special" title with characters';
42 | p.background = '#000000';
43 | p.color = '#999999';
44 | p.bgcolor = '#000000';
45 | p.id = 'element-id';
46 | p.name = 'element-name';
47 | p.codebase =
48 | 'https://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=32,0,0,0';
49 | p.pluginspage = 'https://www.adobe.com/go/getflashplayer';
50 | p.play = true;
51 | p.loop = true;
52 | p.menu = true;
53 | p.quality = 'high';
54 | p.scale = 'default';
55 | p.align = 'l';
56 | p.salign = 'l';
57 | p.wmode = 'opaque';
58 | p.base = '.';
59 | p.allowFullScreen = true;
60 | p.allowFullScreenInteractive = true;
61 | p.allowScriptAccess = 'always';
62 | p.allowNetworking = 'all';
63 | p.fullScreenAspectRatio = 'landscape';
64 | p.flashvars = 'param1=value1¶m2=value2';
65 | p.browserzoom = 'scale';
66 | p.devicefont = false;
67 | p.swliveconnect = true;
68 | p.expressinstall = 'expressinstall.swf';
69 | p.swfversion = 32;
70 | await p.write();
71 |
72 | await copyFile(fixtureFile('swf3.swf'), pathJoin(dir, 'movie.swf'));
73 | });
74 | });
75 | });
76 |
--------------------------------------------------------------------------------
/src/projector/index.ts:
--------------------------------------------------------------------------------
1 | export * from './html.ts';
2 | export * from './sa.ts';
3 | export * from './sa/index.ts';
4 |
--------------------------------------------------------------------------------
/src/projector/sa.spec.ts:
--------------------------------------------------------------------------------
1 | import {mkdir, readFile, writeFile} from 'node:fs/promises';
2 | import {dirname} from 'node:path';
3 |
4 | import {concat} from '../util/internal/data.ts';
5 |
6 | import {ProjectorSa} from './sa.ts';
7 |
8 | export class ProjectorSaDummy extends ProjectorSa {
9 | constructor(path: string) {
10 | super(path);
11 | }
12 |
13 | public get extension(): string {
14 | return '.exe';
15 | }
16 |
17 | protected async _writePlayer(player: string) {
18 | let data: Uint8Array = await readFile(player);
19 | const movieData = await this.getMovieData();
20 | if (movieData) {
21 | data = concat([data, this._encodeMovieData(movieData, 'dms')]);
22 | }
23 | await mkdir(dirname(this.path), {recursive: true});
24 | await writeFile(this.path, data);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/projector/sa.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {join as pathJoin} from 'node:path';
3 |
4 | import {cleanProjectorDir, fixtureFile} from '../util.spec.ts';
5 |
6 | import {ProjectorSaDummy} from './sa.spec.ts';
7 |
8 | const getDir = async (d: string) => cleanProjectorDir('sa', 'dummy', d);
9 |
10 | void describe('projector/sa', () => {
11 | void describe('ProjectorSaDummy', () => {
12 | void it('simple', async () => {
13 | const dir = await getDir('simple');
14 | const dest = pathJoin(dir, 'application.exe');
15 |
16 | const p = new ProjectorSaDummy(dest);
17 | p.player = fixtureFile('dummy.exe');
18 | p.movieFile = fixtureFile('swf3.swf');
19 | await p.write();
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/projector/sa/index.ts:
--------------------------------------------------------------------------------
1 | export * from './windows.ts';
2 | export * from './mac.ts';
3 | export * from './linux.ts';
4 |
--------------------------------------------------------------------------------
/src/projector/sa/linux.spec.ts:
--------------------------------------------------------------------------------
1 | import {shouldTest, getInstalledPackagesInfoSync} from '../../util.spec.ts';
2 |
3 | export function listSamples() {
4 | const platforms = new Set();
5 | if (shouldTest('linux-i386')) {
6 | platforms.add('linux');
7 | platforms.add('linux-i386');
8 | }
9 | if (shouldTest('linux-x86_64')) {
10 | platforms.add('linux-x86_64');
11 | }
12 | return getInstalledPackagesInfoSync()
13 | .filter(o => platforms.has(o.platform))
14 | .map(o => ({
15 | ...o,
16 | type: o.platform === 'linux-x86_64' ? 'x86_64' : 'i386',
17 | patchProjectorOffset: o.platform === 'linux-x86_64'
18 | }));
19 | }
20 |
21 | export const customWindowTitle =
22 | 'Custom Window Title (Longer Than The Original Window Title Was)';
23 |
--------------------------------------------------------------------------------
/src/projector/sa/linux.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {ok} from 'node:assert';
3 | import {copyFile} from 'node:fs/promises';
4 | import {join as pathJoin} from 'node:path';
5 |
6 | import {
7 | cleanProjectorDir,
8 | fixtureFile,
9 | getPackageFile,
10 | simpleSwf,
11 | testShowMenu
12 | } from '../../util.spec.ts';
13 | import {ProjectorSa} from '../sa.ts';
14 |
15 | import {ProjectorSaLinux} from './linux.ts';
16 | import {customWindowTitle, listSamples} from './linux.spec.ts';
17 |
18 | void describe('projector/sa/linux', () => {
19 | void describe('ProjectorSaLinux', () => {
20 | void it('instanceof', () => {
21 | ok(ProjectorSaLinux.prototype instanceof ProjectorSa);
22 | });
23 |
24 | void describe('dummy', () => {
25 | // eslint-disable-next-line unicorn/consistent-function-scoping
26 | const getDir = async (d: string) =>
27 | cleanProjectorDir('sa', 'linux', 'dummy', d);
28 |
29 | void it('simple', async () => {
30 | const dir = await getDir('simple');
31 | const dest = pathJoin(dir, 'application');
32 |
33 | const p = new ProjectorSaLinux(dest);
34 | p.player = fixtureFile('dummy');
35 | p.movieFile = fixtureFile('swf3.swf');
36 | await p.write();
37 | });
38 | });
39 |
40 | for (const pkg of listSamples()) {
41 | const getDir = async (d: string) =>
42 | cleanProjectorDir('sa', 'linux', pkg.type, pkg.name, d);
43 | const getPlayer = async () => getPackageFile(pkg.name);
44 | const simple = fixtureFile(simpleSwf(pkg.zlib, pkg.lzma));
45 |
46 | void describe(pkg.name, () => {
47 | void it('simple', async () => {
48 | const dir = await getDir('simple');
49 | const dest = pathJoin(dir, 'application');
50 |
51 | const p = new ProjectorSaLinux(dest);
52 | p.patchProjectorOffset = pkg.patchProjectorOffset;
53 | p.player = await getPlayer();
54 | p.movieFile = simple;
55 | await p.write();
56 | });
57 |
58 | void it('complex', async () => {
59 | const dir = await getDir('complex');
60 | const dest = pathJoin(dir, 'application');
61 |
62 | const p = new ProjectorSaLinux(dest);
63 | p.patchProjectorOffset = pkg.patchProjectorOffset;
64 | p.patchProjectorPath = true;
65 | p.patchWindowTitle = customWindowTitle;
66 | p.patchMenuRemove = true;
67 | p.player = await getPlayer();
68 |
69 | if (pkg.version[0] < 6) {
70 | p.movieFile = fixtureFile('swf3.swf');
71 | await p.write();
72 | } else {
73 | p.movieFile = fixtureFile('swf6-loadmovie.swf');
74 | await p.write();
75 | await copyFile(
76 | fixtureFile('image.jpg'),
77 | pathJoin(dir, 'image.jpg')
78 | );
79 | }
80 | });
81 |
82 | if (pkg.version[0] >= 6 && testShowMenu) {
83 | void it('showmenu-false', async () => {
84 | const dir = await getDir('showmenu-false');
85 | const dest = pathJoin(dir, 'application');
86 |
87 | const p = new ProjectorSaLinux(dest);
88 | p.patchProjectorOffset = pkg.patchProjectorOffset;
89 | p.player = await getPlayer();
90 | p.movieFile = fixtureFile('swf6-showmenu-false.swf');
91 | await p.write();
92 | });
93 | }
94 | });
95 | }
96 | });
97 | });
98 |
--------------------------------------------------------------------------------
/src/projector/sa/mac.spec.ts:
--------------------------------------------------------------------------------
1 | import {shouldTest, getInstalledPackagesInfoSync} from '../../util.spec.ts';
2 |
3 | export function listSamples() {
4 | if (!shouldTest('mac')) {
5 | return [];
6 | }
7 | return getInstalledPackagesInfoSync().filter(o =>
8 | o.platform.startsWith('mac')
9 | );
10 | }
11 |
12 | export const customWindowTitle =
13 | 'Custom Window Title (Longer Than Possible Existing Unused Strings)';
14 |
--------------------------------------------------------------------------------
/src/projector/sa/mac.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {ok} from 'node:assert';
3 | import {copyFile} from 'node:fs/promises';
4 | import {join as pathJoin} from 'node:path';
5 |
6 | import {
7 | cleanProjectorDir,
8 | fixtureFile,
9 | getPackageFile,
10 | simpleSwf,
11 | testShowMenu
12 | } from '../../util.spec.ts';
13 | import {ProjectorSa} from '../sa.ts';
14 |
15 | import {ProjectorSaMac} from './mac.ts';
16 | import {customWindowTitle, listSamples} from './mac.spec.ts';
17 |
18 | void describe('projector/sa/mac', () => {
19 | void describe('ProjectorSaMac', () => {
20 | void it('instanceof', () => {
21 | ok(ProjectorSaMac.prototype instanceof ProjectorSa);
22 | });
23 |
24 | void describe('dummy', () => {
25 | // eslint-disable-next-line unicorn/consistent-function-scoping
26 | const getDir = async (d: string) =>
27 | cleanProjectorDir('sa', 'mac', 'dummy', d);
28 |
29 | void it('simple', async () => {
30 | const dir = await getDir('simple');
31 | const dest = pathJoin(dir, 'application.app');
32 |
33 | const p = new ProjectorSaMac(dest);
34 | p.player = fixtureFile('dummy.app');
35 | p.movieFile = fixtureFile('swf3.swf');
36 | await p.write();
37 | });
38 | });
39 |
40 | for (const pkg of listSamples()) {
41 | const getDir = async (d: string) =>
42 | cleanProjectorDir('sa', 'mac', pkg.name, d);
43 | const getPlayer = async () => getPackageFile(pkg.name);
44 | const simple = fixtureFile(simpleSwf(pkg.zlib, pkg.lzma));
45 |
46 | void describe(pkg.name, () => {
47 | void it('simple', async () => {
48 | const dir = await getDir('simple');
49 | const dest = pathJoin(dir, 'application.app');
50 |
51 | const p = new ProjectorSaMac(dest);
52 | p.removeCodeSignature = true;
53 | p.player = await getPlayer();
54 | p.movieFile = simple;
55 | await p.write();
56 | });
57 |
58 | void it('removeFileAssociations', async () => {
59 | const dir = await getDir('removeFileAssociations');
60 | const dest = pathJoin(dir, 'application.app');
61 |
62 | const p = new ProjectorSaMac(dest);
63 | p.removeFileAssociations = true;
64 | p.removeCodeSignature = true;
65 | p.player = await getPlayer();
66 | p.movieFile = fixtureFile('swf3.swf');
67 | await p.write();
68 | });
69 |
70 | void it('complex', async () => {
71 | const dir = await getDir('complex');
72 | const dest = pathJoin(dir, 'application.app');
73 |
74 | const p = new ProjectorSaMac(dest);
75 | p.iconFile = fixtureFile('icon.icns');
76 | p.infoPlistFile = fixtureFile('Info.plist');
77 | p.pkgInfoFile = fixtureFile('PkgInfo');
78 | p.binaryName = 'application';
79 | p.bundleName = 'App Bundle Name';
80 | p.removeInfoPlistStrings = true;
81 | p.removeCodeSignature = true;
82 | if (pkg.version[0] >= 11) {
83 | p.patchWindowTitle = customWindowTitle;
84 | }
85 | p.player = await getPlayer();
86 | if (pkg.version[0] < 6) {
87 | p.movieFile = fixtureFile('swf3.swf');
88 | await p.write();
89 | } else {
90 | p.movieFile = fixtureFile('swf6-loadmovie.swf');
91 | await p.write();
92 | await copyFile(
93 | fixtureFile('image.jpg'),
94 | pathJoin(dir, 'image.jpg')
95 | );
96 | }
97 | });
98 |
99 | if (pkg.version[0] >= 6 && testShowMenu) {
100 | void it('showmenu-false', async () => {
101 | const dir = await getDir('showmenu-false');
102 | const dest = pathJoin(dir, 'application.app');
103 |
104 | const p = new ProjectorSaMac(dest);
105 | p.player = await getPlayer();
106 | p.movieFile = fixtureFile('swf6-showmenu-false.swf');
107 | await p.write();
108 | });
109 | }
110 | });
111 | }
112 | });
113 | });
114 |
--------------------------------------------------------------------------------
/src/projector/sa/windows.spec.ts:
--------------------------------------------------------------------------------
1 | import {shouldTest, getInstalledPackagesInfoSync} from '../../util.spec.ts';
2 |
3 | export function listSamples() {
4 | const platforms = new Set();
5 | if (shouldTest('windows-i386')) {
6 | platforms.add('windows');
7 | platforms.add('windows-32bit');
8 | platforms.add('windows-i386');
9 | }
10 | if (shouldTest('windows-x86_64')) {
11 | platforms.add('windows-x86_64');
12 | }
13 | return getInstalledPackagesInfoSync()
14 | .filter(o => platforms.has(o.platform))
15 | .map(o => ({
16 | ...o,
17 | type: o.platform === 'windows-x86_64' ? 'x86_64' : 'i386',
18 | patchOutOfDateDisable: o.version[0] >= 30
19 | }));
20 | }
21 |
22 | export const customWindowTitle =
23 | 'Custom Window Title (Longer Than The Original Window Title Was)';
24 |
25 | export const versionStrings = {
26 | FileVersion: '3.14.15.92',
27 | ProductVersion: '3.1.4.1',
28 | CompanyName: 'Custom Company Name',
29 | FileDescription: 'Custom File Description',
30 | LegalCopyright: 'Custom Legal Copyright',
31 | ProductName: 'Custom Product Name',
32 | LegalTrademarks: 'Custom Legal Trademarks',
33 | OriginalFilename: 'CustomOriginalFilename.exe',
34 | InternalName: 'CustomInternalName',
35 | Comments: 'Custom Comments'
36 | };
37 |
--------------------------------------------------------------------------------
/src/projector/sa/windows.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {ok} from 'node:assert';
3 | import {copyFile} from 'node:fs/promises';
4 | import {join as pathJoin} from 'node:path';
5 |
6 | import {
7 | cleanProjectorDir,
8 | fixtureFile,
9 | getPackageFile,
10 | simpleSwf,
11 | testShowMenu
12 | } from '../../util.spec.ts';
13 | import {ProjectorSa} from '../sa.ts';
14 |
15 | import {ProjectorSaWindows} from './windows.ts';
16 | import {
17 | customWindowTitle,
18 | listSamples,
19 | versionStrings
20 | } from './windows.spec.ts';
21 |
22 | void describe('projector/sa/windows', () => {
23 | void describe('ProjectorSaWindows', () => {
24 | void it('instanceof', () => {
25 | ok(ProjectorSaWindows.prototype instanceof ProjectorSa);
26 | });
27 |
28 | void describe('dummy', () => {
29 | // eslint-disable-next-line unicorn/consistent-function-scoping
30 | const getDir = async (d: string) =>
31 | cleanProjectorDir('sa', 'windows', 'dummy', d);
32 |
33 | void it('simple', async () => {
34 | const dir = await getDir('simple');
35 | const dest = pathJoin(dir, 'application.exe');
36 |
37 | const p = new ProjectorSaWindows(dest);
38 | p.player = fixtureFile('dummy.exe');
39 | p.movieFile = fixtureFile('swf3.swf');
40 | await p.write();
41 | });
42 |
43 | void it('archived', async () => {
44 | const dir = await getDir('archived');
45 | const dest = pathJoin(dir, 'application.exe');
46 |
47 | const p = new ProjectorSaWindows(dest);
48 | p.player = fixtureFile('dummy.exe.zip');
49 | p.movieFile = fixtureFile('swf3.swf');
50 | await p.write();
51 | });
52 | });
53 |
54 | for (const pkg of listSamples()) {
55 | const getDir = async (d: string) =>
56 | cleanProjectorDir('sa', 'windows', pkg.type, pkg.name, d);
57 | const getPlayer = async () => getPackageFile(pkg.name);
58 | const simple = fixtureFile(simpleSwf(pkg.zlib, pkg.lzma));
59 |
60 | void describe(pkg.name, () => {
61 | void it('simple', async () => {
62 | const dir = await getDir('simple');
63 | const dest = pathJoin(dir, 'application.exe');
64 |
65 | const p = new ProjectorSaWindows(dest);
66 | p.removeCodeSignature = true;
67 | p.patchOutOfDateDisable = pkg.patchOutOfDateDisable;
68 | p.player = await getPlayer();
69 | p.movieFile = simple;
70 | await p.write();
71 | });
72 |
73 | void it('complex', async () => {
74 | const dir = await getDir('complex');
75 | const dest = pathJoin(dir, 'application.exe');
76 |
77 | const p = new ProjectorSaWindows(dest);
78 | p.iconFile = fixtureFile('icon.ico');
79 | p.versionStrings = versionStrings;
80 | p.removeCodeSignature = true;
81 | p.patchWindowTitle = customWindowTitle;
82 | p.patchOutOfDateDisable = pkg.patchOutOfDateDisable;
83 | p.player = await getPlayer();
84 |
85 | if (pkg.version[0] < 6) {
86 | p.movieFile = fixtureFile('swf3.swf');
87 | await p.write();
88 | } else {
89 | p.movieFile = fixtureFile('swf6-loadmovie.swf');
90 | await p.write();
91 | await copyFile(
92 | fixtureFile('image.jpg'),
93 | pathJoin(dir, 'image.jpg')
94 | );
95 | }
96 | });
97 |
98 | if (pkg.version[0] >= 6 && testShowMenu) {
99 | void it('showmenu-false', async () => {
100 | const dir = await getDir('showmenu-false');
101 | const dest = pathJoin(dir, 'application.exe');
102 |
103 | const p = new ProjectorSaWindows(dest);
104 | p.removeCodeSignature = true;
105 | p.patchOutOfDateDisable = pkg.patchOutOfDateDisable;
106 | p.player = await getPlayer();
107 | p.movieFile = fixtureFile('swf6-showmenu-false.swf');
108 | await p.write();
109 | });
110 | }
111 | });
112 | }
113 | });
114 | });
115 |
--------------------------------------------------------------------------------
/src/queue.test.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable unicorn/no-array-push-push */
2 | import {describe, it} from 'node:test';
3 | import {strictEqual} from 'node:assert';
4 |
5 | import {Queue} from './queue.ts';
6 |
7 | void describe('queue', () => {
8 | void describe('Queue', () => {
9 | void it('priority: 0', async () => {
10 | const order: string[] = [];
11 | const q = new Queue();
12 |
13 | q.push(async () => {
14 | order.push('a');
15 | });
16 | q.push(async () => {
17 | order.push('b');
18 | });
19 | q.push(async () => {
20 | order.push('c');
21 | });
22 | q.push(async () => {
23 | order.push('d');
24 | });
25 | q.push(async () => {
26 | order.push('e');
27 | });
28 | q.push(async () => {
29 | order.push('f');
30 | });
31 |
32 | await q.run();
33 |
34 | strictEqual(order.join(','), 'a,b,c,d,e,f');
35 | });
36 |
37 | void it('priority: incremental', async () => {
38 | const order: string[] = [];
39 | const q = new Queue();
40 |
41 | q.push(async () => {
42 | order.push('f');
43 | }, 1);
44 | q.push(async () => {
45 | order.push('e');
46 | }, 2);
47 | q.push(async () => {
48 | order.push('d');
49 | }, 3);
50 | q.push(async () => {
51 | order.push('c');
52 | }, 4);
53 | q.push(async () => {
54 | order.push('b');
55 | }, 5);
56 | q.push(async () => {
57 | order.push('a');
58 | }, 6);
59 |
60 | await q.run();
61 |
62 | strictEqual(order.join(','), 'a,b,c,d,e,f');
63 | });
64 |
65 | void it('priority: decremental', async () => {
66 | const order: string[] = [];
67 | const q = new Queue();
68 |
69 | q.push(async () => {
70 | order.push('a');
71 | }, 6);
72 | q.push(async () => {
73 | order.push('b');
74 | }, 5);
75 | q.push(async () => {
76 | order.push('c');
77 | }, 4);
78 | q.push(async () => {
79 | order.push('d');
80 | }, 3);
81 | q.push(async () => {
82 | order.push('e');
83 | }, 2);
84 | q.push(async () => {
85 | order.push('f');
86 | }, 1);
87 |
88 | await q.run();
89 |
90 | strictEqual(order.join(','), 'a,b,c,d,e,f');
91 | });
92 |
93 | void it('priority: mixed', async () => {
94 | const order: string[] = [];
95 | const q = new Queue();
96 |
97 | q.push(async () => {
98 | order.push('d');
99 | }, 0);
100 | q.push(async () => {
101 | order.push('a');
102 | }, 2);
103 | q.push(async () => {
104 | order.push('f');
105 | }, -1);
106 | q.push(async () => {
107 | order.push('b');
108 | }, 2);
109 | q.push(async () => {
110 | order.push('c');
111 | }, 1);
112 | q.push(async () => {
113 | order.push('e');
114 | }, 0);
115 |
116 | await q.run();
117 |
118 | strictEqual(order.join(','), 'a,b,c,d,e,f');
119 | });
120 | });
121 | });
122 |
--------------------------------------------------------------------------------
/src/queue.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A simple queue with optional priority.
3 | */
4 | export class Queue {
5 | /**
6 | * Queue data.
7 | */
8 | private _queue_: {
9 | priority: number;
10 | handler: () => Promise;
11 | }[] = [];
12 |
13 | /**
14 | * Queue constructor.
15 | */
16 | constructor() {}
17 |
18 | /**
19 | * Get size of queue.
20 | *
21 | * @returns Total callbacks in queue.
22 | */
23 | public get size() {
24 | return this._queue_.length;
25 | }
26 |
27 | /**
28 | * Clear queue.
29 | */
30 | public clear() {
31 | this._queue_ = [];
32 | }
33 |
34 | /**
35 | * Enqueue callback.
36 | *
37 | * @param handler Callback function.
38 | * @param priority Callback priority.
39 | */
40 | public push(handler: () => Promise, priority = 0) {
41 | const queue = this._queue_;
42 | let index = 0;
43 | for (let i = queue.length; i--; ) {
44 | if (queue[i].priority < priority) {
45 | index = i + 1;
46 | break;
47 | }
48 | }
49 | queue.splice(index, 0, {
50 | priority,
51 | handler
52 | });
53 | }
54 |
55 | /**
56 | * Pop callback off queue.
57 | *
58 | * @returns Callback function or null if empty.
59 | */
60 | public pop() {
61 | const entry = this._queue_.pop();
62 | return entry ? entry.handler : null;
63 | }
64 |
65 | /**
66 | * Shift callback off queue.
67 | *
68 | * @returns Callback function or null if empty.
69 | */
70 | public shift() {
71 | const entry = this._queue_.shift();
72 | return entry ? entry.handler : null;
73 | }
74 |
75 | /**
76 | * Run queue.
77 | */
78 | public async run() {
79 | for (;;) {
80 | const entry = this.pop();
81 | if (!entry) {
82 | break;
83 | }
84 |
85 | // eslint-disable-next-line no-await-in-loop
86 | await entry();
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/swf/data.ts:
--------------------------------------------------------------------------------
1 | import {subview} from './util.ts';
2 |
3 | /**
4 | * Data object.
5 | */
6 | export abstract class Data {
7 | /**
8 | * Data constructor.
9 | */
10 | constructor() {}
11 |
12 | /**
13 | * Encode size.
14 | *
15 | * @returns The size.
16 | */
17 | public abstract get size(): number;
18 |
19 | /**
20 | * Encode into data.
21 | *
22 | * @param data Data encode target.
23 | */
24 | public abstract encoder(data: Uint8Array): void;
25 |
26 | /**
27 | * Encode into data at offset, or new data.
28 | *
29 | * @param data The buffer to use.
30 | * @param offset Offset in the buffer.
31 | * @returns Encoded data.
32 | */
33 | public encode(data: Uint8Array | null = null, offset = 0) {
34 | const {size} = this;
35 | data = data ? subview(data, offset, size) : new Uint8Array(size);
36 | this.encoder(data);
37 | return data;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/swf/fixed8.ts:
--------------------------------------------------------------------------------
1 | import {Data} from './data.ts';
2 |
3 | /**
4 | * Fixed8 object.
5 | */
6 | export class Fixed8 extends Data {
7 | /**
8 | * Float numerator.
9 | */
10 | public numerator: number = 0;
11 |
12 | /**
13 | * Float denominator.
14 | */
15 | public denominator: number = 0;
16 |
17 | /**
18 | * Fixed8 constructor.
19 | */
20 | constructor() {
21 | super();
22 | }
23 |
24 | /**
25 | * @inheritdoc
26 | */
27 | public get size() {
28 | return 2;
29 | }
30 |
31 | /**
32 | * Value.
33 | *
34 | * @param value The value.
35 | */
36 | public set value(value) {
37 | const i16 = Math.floor(value * 256);
38 | // eslint-disable-next-line no-bitwise
39 | this.numerator = (i16 >> 8) & 0xff;
40 | // eslint-disable-next-line no-bitwise
41 | this.denominator = i16 & 0xff;
42 | }
43 |
44 | /**
45 | * Value.
46 | *
47 | * @returns The value.
48 | */
49 | public get value() {
50 | // eslint-disable-next-line no-bitwise
51 | const i16 = (this.numerator << 8) | this.denominator;
52 | return i16 / 256;
53 | }
54 |
55 | /**
56 | * @inheritdoc
57 | */
58 | public encoder(data: Uint8Array) {
59 | data[0] = this.denominator;
60 | data[1] = this.numerator;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/swf/rect.ts:
--------------------------------------------------------------------------------
1 | import {Data} from './data.ts';
2 | import {bitCountS, bitCountToBytes, bitWriter} from './util.ts';
3 |
4 | /**
5 | * Rect object.
6 | */
7 | export class Rect extends Data {
8 | /**
9 | * Minimum X.
10 | */
11 | public xMin: number = 0;
12 |
13 | /**
14 | * Maximum X.
15 | */
16 | public xMax: number = 0;
17 |
18 | /**
19 | * Minimum Y.
20 | */
21 | public yMin: number = 0;
22 |
23 | /**
24 | * Maximum Y.
25 | */
26 | public yMax: number = 0;
27 |
28 | /**
29 | * Rect constructor.
30 | */
31 | constructor() {
32 | super();
33 | }
34 |
35 | /**
36 | * Number of bits need to encode the values.
37 | *
38 | * @returns Bit count.
39 | */
40 | public get nBits() {
41 | return Math.max(
42 | bitCountS(this.xMin),
43 | bitCountS(this.xMax),
44 | bitCountS(this.yMin),
45 | bitCountS(this.yMax)
46 | );
47 | }
48 |
49 | /**
50 | * @inheritdoc
51 | */
52 | public get size() {
53 | return bitCountToBytes(5 + this.nBits * 4);
54 | }
55 |
56 | /**
57 | * @inheritdoc
58 | */
59 | public encoder(data: Uint8Array) {
60 | const {nBits} = this;
61 | const bW = bitWriter(data, 0);
62 | let b = 0;
63 | bW(nBits, 5, b);
64 | b += 5;
65 | for (const value of [this.xMin, this.xMax, this.yMin, this.yMax]) {
66 | bW(value, nBits, b);
67 | b += nBits;
68 | }
69 | const over = b % 8;
70 | if (over) {
71 | bW(0, 8 - over, b);
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/swf/swf.ts:
--------------------------------------------------------------------------------
1 | import {Data} from './data.ts';
2 | import {Rect} from './rect.ts';
3 | import {Fixed8} from './fixed8.ts';
4 | import {Tag} from './tag.ts';
5 |
6 | /**
7 | * Swf object.
8 | */
9 | export class Swf extends Data {
10 | /**
11 | * Format version.
12 | */
13 | public version: number = 0;
14 |
15 | /**
16 | * Frame size.
17 | */
18 | public frameSize: Rect = new Rect();
19 |
20 | /**
21 | * Frame rate.
22 | */
23 | public frameRate: Fixed8 = new Fixed8();
24 |
25 | /**
26 | * Frame count.
27 | */
28 | public frameCount: number = 0;
29 |
30 | /**
31 | * Movie tags.
32 | */
33 | public tags: Tag[] = [];
34 |
35 | /**
36 | * Swf constructor.
37 | */
38 | constructor() {
39 | super();
40 | }
41 |
42 | /**
43 | * @inheritdoc
44 | */
45 | public get size() {
46 | return this.tags.reduce(
47 | (v, t) => t.size + v,
48 | 3 + 1 + 4 + this.frameSize.size + this.frameRate.size + 2
49 | );
50 | }
51 |
52 | /**
53 | * @inheritdoc
54 | */
55 | public encoder(data: Uint8Array) {
56 | let i = 0;
57 | data[i++] = 0x46;
58 | data[i++] = 0x57;
59 | data[i++] = 0x53;
60 | data[i++] = this.version;
61 |
62 | const v = new DataView(data.buffer, data.byteOffset, data.byteLength);
63 | v.setUint32(i, this.size, true);
64 | i += 4;
65 |
66 | i += this.frameSize.encode(data, i).length;
67 |
68 | i += this.frameRate.encode(data, i).length;
69 |
70 | v.setUint16(i, this.frameCount, true);
71 | i += 2;
72 |
73 | for (const tag of this.tags) {
74 | i += tag.encode(data, i).length;
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/swf/tag.ts:
--------------------------------------------------------------------------------
1 | import {Data} from './data.ts';
2 |
3 | /**
4 | * Tag object.
5 | */
6 | export class Tag extends Data {
7 | /**
8 | * Tag code.
9 | */
10 | public code: number = 0;
11 |
12 | /**
13 | * Tag data.
14 | */
15 | public data: Uint8Array = new Uint8Array(0);
16 |
17 | /**
18 | * Tag constructor.
19 | */
20 | constructor() {
21 | super();
22 | }
23 |
24 | /**
25 | * Is the tag a long tag.
26 | *
27 | * @returns True if long tag.
28 | */
29 | public get long() {
30 | return this.data.length >= 0b111111;
31 | }
32 |
33 | /**
34 | * @inheritdoc
35 | */
36 | public get size() {
37 | return 2 + (this.long ? 4 : 0) + this.data.length;
38 | }
39 |
40 | /**
41 | * @inheritdoc
42 | */
43 | public encoder(data: Uint8Array) {
44 | let i = 0;
45 | const {code, data: d, long} = this;
46 | // eslint-disable-next-line no-bitwise
47 | const head = (code << 6) | (long ? 0b111111 : d.length);
48 | const v = new DataView(data.buffer, data.byteOffset, data.byteLength);
49 | v.setUint16(i, head, true);
50 | i += 2;
51 | if (long) {
52 | v.setUint32(i, d.length, true);
53 | i += 4;
54 | }
55 | data.set(d, i);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/swf/util.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Set subview of an existing buffer.
3 | *
4 | * @param data Data to get a subview of.
5 | * @param start Start offset.
6 | * @param size Subview size.
7 | * @returns Data subview.
8 | */
9 | export function subview(data: Uint8Array, start: number, size = -1) {
10 | size = size < 0 ? data.length - start : size;
11 | const r = data.subarray(start, start + size);
12 | if (r.length < size) {
13 | throw new Error(`Data is too small: ${r.length} < ${size}`);
14 | }
15 | return r;
16 | }
17 |
18 | /**
19 | * Encode a string as a C-String.
20 | *
21 | * @param str String to be encoded.
22 | * @returns Encoded string.
23 | */
24 | export function stringEncode(str: string) {
25 | return new TextEncoder().encode(`${str}\0`);
26 | }
27 |
28 | /**
29 | * Get number of bits needed to encode an unsigned integer.
30 | *
31 | * @param i Integer, unsigned.
32 | * @returns The number of bits.
33 | */
34 | export function bitCountU(i: number) {
35 | let n = 0;
36 | // eslint-disable-next-line no-bitwise
37 | for (; i; i >>= 1) {
38 | n++;
39 | }
40 | return n;
41 | }
42 |
43 | /**
44 | * Get number of bits needed to encode a signed integer.
45 | *
46 | * @param i Integer, signed.
47 | * @returns The number of bits.
48 | */
49 | export function bitCountS(i: number) {
50 | // eslint-disable-next-line no-bitwise
51 | return bitCountU(i < 0 ? i ^ -1 : i) + 1;
52 | }
53 |
54 | /**
55 | * Get the number of bytes needed to store N bit.
56 | *
57 | * @param bits The number of bits.
58 | * @returns The number of bytes.
59 | */
60 | export function bitCountToBytes(bits: number) {
61 | const over = bits % 8;
62 | return (bits - over) / 8 + (over ? 1 : 0);
63 | }
64 |
65 | /**
66 | * Create a bit writer function for a buffer at offset.
67 | *
68 | * @param data The buffer to write bits into.
69 | * @param start Start offset.
70 | * @returns Writter function (value, count, offset).
71 | */
72 | export function bitWriter(data: Uint8Array, start = 0) {
73 | const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
74 | return (v: number, c: number, b: number) => {
75 | for (let i = 0; i < c; i++) {
76 | const bI = b + i;
77 | const bitI = bI % 8;
78 | const byteI = (bI - bitI) / 8;
79 | let byteV = view.getUint8(start + byteI);
80 | // eslint-disable-next-line no-bitwise
81 | const flag = 1 << (7 - bitI);
82 | // eslint-disable-next-line no-bitwise
83 | if ((v >> (c - 1 - i)) & 1) {
84 | // eslint-disable-next-line no-bitwise
85 | byteV |= flag;
86 | } else {
87 | // eslint-disable-next-line no-bitwise
88 | byteV &= ~flag;
89 | }
90 | view.setUint8(start + byteI, byteV);
91 | }
92 | };
93 | }
94 |
--------------------------------------------------------------------------------
/src/util.spec.ts:
--------------------------------------------------------------------------------
1 | import {join as pathJoin} from 'node:path';
2 | import {mkdir, rm} from 'node:fs/promises';
3 | import {readdirSync, statSync} from 'node:fs';
4 |
5 | import {Manager} from '@shockpkg/core';
6 |
7 | // eslint-disable-next-line no-process-env
8 | const envTest = process.env.SWF_PROJECTOR_TEST || null;
9 |
10 | export function shouldTest(name: string) {
11 | return (
12 | !envTest ||
13 | envTest.toLowerCase().split(',').includes(name.toLowerCase())
14 | );
15 | }
16 |
17 | // eslint-disable-next-line no-process-env
18 | export const testShowMenu = process.env.SWF_PROJECTOR_SHOWMENU === '1';
19 |
20 | export const specFixturesPath = pathJoin('spec', 'fixtures');
21 | export const specProjectorsPath = pathJoin('spec', 'projectors');
22 | export const specBundlesPath = pathJoin('spec', 'bundles');
23 |
24 | export function fixtureFile(name: string) {
25 | return pathJoin(specFixturesPath, name);
26 | }
27 |
28 | export async function getPackageFile(pkg: string) {
29 | return new Manager().file(pkg);
30 | }
31 |
32 | export async function cleanProjectorDir(...path: string[]) {
33 | const dir = pathJoin(specProjectorsPath, ...path);
34 | await rm(dir, {recursive: true, force: true});
35 | await mkdir(dir, {recursive: true});
36 | return dir;
37 | }
38 |
39 | export async function cleanBundlesDir(...path: string[]) {
40 | const dir = pathJoin(specBundlesPath, ...path);
41 | await rm(dir, {recursive: true, force: true});
42 | await mkdir(dir, {recursive: true});
43 | return dir;
44 | }
45 |
46 | let getInstalledPackagesCache: string[] | null = null;
47 | export function getInstalledPackagesSync() {
48 | if (!getInstalledPackagesCache) {
49 | getInstalledPackagesCache = [];
50 | try {
51 | const dir = 'shockpkg';
52 | for (const d of readdirSync(dir, {withFileTypes: true})) {
53 | if (d.name.startsWith('.') || !d.isDirectory()) {
54 | continue;
55 | }
56 | try {
57 | const st = statSync(
58 | `${dir}/${d.name}/.shockpkg/package.json`
59 | );
60 | if (st.isFile()) {
61 | getInstalledPackagesCache.push(d.name);
62 | }
63 | } catch (err) {
64 | if (!(err && (err as {code: string}).code === 'ENOENT')) {
65 | throw err;
66 | }
67 | }
68 | }
69 | } catch (err) {
70 | if (!(err && (err as {code: string}).code === 'ENOENT')) {
71 | throw err;
72 | }
73 | }
74 | }
75 | return getInstalledPackagesCache;
76 | }
77 |
78 | export function versionZlib(version: number[]) {
79 | return version[0] >= 6;
80 | }
81 |
82 | export function versionLzma(version: number[]) {
83 | return version[0] > 11 || (version[0] === 11 && version[1] >= 1);
84 | }
85 |
86 | export function simpleSwf(zlib: boolean, lzma: boolean) {
87 | if (lzma) {
88 | return 'swf14-lzma.swf';
89 | }
90 | if (zlib) {
91 | return 'swf6-zlib.swf';
92 | }
93 | return 'swf3.swf';
94 | }
95 |
96 | export function packageInfo(name: string) {
97 | const m = name.match(/^flash-player-([\d.]+)-(.*)-sa(-debug)?(-.*)?$/);
98 | if (!m) {
99 | return null;
100 | }
101 |
102 | const version = m[1].split('.').map(Number);
103 | const zlib = versionZlib(version);
104 | const lzma = versionLzma(version);
105 | return {
106 | name,
107 | version,
108 | platform: m[2],
109 | debug: !!m[3],
110 | zlib,
111 | lzma
112 | };
113 | }
114 |
115 | export function getInstalledPackagesInfoSync() {
116 | const r = [];
117 | for (const name of getInstalledPackagesSync()) {
118 | const info = packageInfo(name);
119 | if (info) {
120 | r.push(info);
121 | }
122 | }
123 |
124 | r.sort((a, b) => +a.debug - +b.debug);
125 | for (let i = 4; i--; ) {
126 | r.sort((a, b) => (a.version[i] || 0) - (b.version[i] || 0));
127 | }
128 |
129 | return r;
130 | }
131 |
--------------------------------------------------------------------------------
/src/util.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {strictEqual} from 'node:assert';
3 |
4 | import {pathRelativeBase, trimExtension} from './util.ts';
5 |
6 | void describe('util', () => {
7 | void describe('pathRelativeBase', () => {
8 | void it('file', () => {
9 | strictEqual(pathRelativeBase('test', 'test'), '');
10 | strictEqual(pathRelativeBase('test/', 'test'), '');
11 | strictEqual(pathRelativeBase('test', 'Test'), null);
12 | });
13 |
14 | void it('file nocase', () => {
15 | strictEqual(pathRelativeBase('test', 'Test', true), '');
16 | });
17 |
18 | void it('dir', () => {
19 | strictEqual(pathRelativeBase('test/123', 'test'), '123');
20 | strictEqual(pathRelativeBase('test/123', 'Test'), null);
21 | });
22 |
23 | void it('dir nocase', () => {
24 | strictEqual(pathRelativeBase('test/123', 'Test', true), '123');
25 | });
26 | });
27 |
28 | void describe('trimExtension', () => {
29 | void it('case', () => {
30 | strictEqual(trimExtension('test.txt', '.txt'), 'test');
31 | strictEqual(trimExtension('test.bin', '.txt'), 'test.bin');
32 | strictEqual(trimExtension('test.TXT', '.txt'), 'test.TXT');
33 | strictEqual(trimExtension('test.txt', '.TXT'), 'test.txt');
34 | });
35 |
36 | void it('nocase', () => {
37 | strictEqual(trimExtension('test.txt', '.TXT', true), 'test');
38 | strictEqual(trimExtension('test.TXT', '.txt', true), 'test');
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/src/util.ts:
--------------------------------------------------------------------------------
1 | import {inflateRaw} from 'node:zlib';
2 |
3 | import {LAUNCHERS} from './launchers.ts';
4 |
5 | /**
6 | * HTML encode.
7 | *
8 | * @param s Raw strings.
9 | * @param dq Double quotes.
10 | * @param sq Single quotes.
11 | * @returns Encoded strings.
12 | */
13 | export function htmlEncode(s: string, dq = false, sq = false) {
14 | s = s.replace(/&/g, '&').replace(//g, '>');
15 | if (dq) {
16 | s = s.replace(/"/g, '"');
17 | }
18 | if (sq) {
19 | s = s.replace(/'/g, ''');
20 | }
21 | return s;
22 | }
23 |
24 | /**
25 | * Trim dot slash from head of path.
26 | *
27 | * @param path Path string.
28 | * @returns Trimmed path.
29 | */
30 | export function trimDotSlash(path: string) {
31 | return path.replace(/^(\.\/)+/, '');
32 | }
33 |
34 | /**
35 | * Find path relative from base, if base matches.
36 | *
37 | * @param path Path to match against.
38 | * @param start Search start.
39 | * @param nocase Match case-insensitive.
40 | * @returns Returns path, or null.
41 | */
42 | export function pathRelativeBase(path: string, start: string, nocase = false) {
43 | const p = trimDotSlash(nocase ? path.toLowerCase() : path);
44 | const s = trimDotSlash(nocase ? start.toLowerCase() : start);
45 | if (p === s) {
46 | return '';
47 | }
48 | if (p.startsWith(`${s}/`)) {
49 | return path.slice(s.length + 1);
50 | }
51 | return null;
52 | }
53 |
54 | /**
55 | * Same as pathRelativeBase, but retuns true on a match, else false.
56 | *
57 | * @param path Path to match against.
58 | * @param start Search start.
59 | * @param nocase Match case-insensitive.
60 | * @returns Returns true on match, else false.
61 | */
62 | export function pathRelativeBaseMatch(
63 | path: string,
64 | start: string,
65 | nocase = false
66 | ) {
67 | return pathRelativeBase(path, start, nocase) !== null;
68 | }
69 |
70 | /**
71 | * Trim a file extenion.
72 | *
73 | * @param path File path.
74 | * @param ext File extension.
75 | * @param nocase Match case-insensitive.
76 | * @returns Path without file extension.
77 | */
78 | export function trimExtension(path: string, ext: string, nocase = false) {
79 | const p = nocase ? path.toLowerCase() : path;
80 | const e = nocase ? ext.toLowerCase() : ext;
81 | return p.endsWith(e) ? path.slice(0, p.length - e.length) : path;
82 | }
83 |
84 | /**
85 | * Get launcher data for an ID.
86 | *
87 | * @param id Laucher ID.
88 | * @returns Launcher data.
89 | */
90 | export async function launcher(id: string) {
91 | const b64 = LAUNCHERS[id];
92 | if (typeof b64 !== 'string') {
93 | // eslint-disable-next-line unicorn/prefer-type-error
94 | throw new Error(`Invalid launcher id: ${id}`);
95 | }
96 |
97 | return new Promise((resolve, reject) => {
98 | inflateRaw(Buffer.from(b64, 'base64'), (err, data) => {
99 | if (err) {
100 | reject(err);
101 | return;
102 | }
103 | resolve(
104 | new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
105 | );
106 | });
107 | });
108 | }
109 |
--------------------------------------------------------------------------------
/src/util/index.ts:
--------------------------------------------------------------------------------
1 | export * from './windows.ts';
2 | export * from './mac.ts';
3 | export * from './linux.ts';
4 |
--------------------------------------------------------------------------------
/src/util/internal/data.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Encode integer as 4 byte hex.
3 | *
4 | * @param i The integer to encode.
5 | * @returns Hex string.
6 | */
7 | export function hex4(i: number) {
8 | return i.toString(16).toUpperCase().padStart(8, '0');
9 | }
10 |
11 | /**
12 | * Align integer.
13 | *
14 | * @param i Integer value.
15 | * @param align Alignment amount.
16 | * @returns Aligned integer.
17 | */
18 | export function align(i: number, align: number) {
19 | const o = i % align;
20 | return o ? align - o + i : i;
21 | }
22 |
23 | /**
24 | * Concat data chunks together.
25 | *
26 | * @param pieces The pieces to merge.
27 | * @returns Merged data.
28 | */
29 | export function concat(pieces: readonly Readonly[]) {
30 | let l = 0;
31 | for (const piece of pieces) {
32 | l += piece.length;
33 | }
34 | const r = new Uint8Array(l);
35 | l = 0;
36 | for (const piece of pieces) {
37 | r.set(piece, l);
38 | l += piece.length;
39 | }
40 | return r;
41 | }
42 |
43 | /**
44 | * Get buffer.
45 | *
46 | * @param data Data view.
47 | * @param offset The offset.
48 | * @param size The size.
49 | * @returns ArrayBuffer slice.
50 | */
51 | export function getBuffer(data: Readonly, offset: number, size = -1) {
52 | const {byteOffset, byteLength} = data;
53 | const o = byteOffset + offset;
54 | const l = byteLength - byteOffset;
55 | if (size > l) {
56 | throw new Error(`Size out of bounds`);
57 | }
58 | return data.buffer.slice(o, o + (size < 0 ? l : size));
59 | }
60 |
61 | /**
62 | * Set buffer.
63 | *
64 | * @param data Data view.
65 | * @param offset The offset.
66 | * @param buffer The ArrayBuffer.
67 | * @param size The size.
68 | */
69 | export function setBuffer(
70 | data: DataView,
71 | offset: number,
72 | buffer: Readonly,
73 | size = -1
74 | ) {
75 | const {byteOffset, byteLength} = data;
76 | const o = byteOffset + offset;
77 | new Uint8Array(data.buffer, o, byteLength - o).set(
78 | new Uint8Array(buffer, 0, size < 0 ? buffer.byteLength : size)
79 | );
80 | }
81 |
82 | /**
83 | * Find exact match in data.
84 | *
85 | * @param data Data to search.
86 | * @param find Search for.
87 | * @param from Search from.
88 | * @returns Index.
89 | */
90 | export function findIndex(
91 | data: Readonly,
92 | find: Readonly,
93 | from = 0
94 | ) {
95 | const l = find.length;
96 | if (l) {
97 | const e = data.length - l;
98 | const [f] = find;
99 | for (let i = from; ; i++) {
100 | i = data.indexOf(f, i);
101 | if (i < 0 || i > e) {
102 | break;
103 | }
104 | let m = true;
105 | for (let j = 1; j < l; j++) {
106 | if (data[i + j] !== find[j]) {
107 | m = false;
108 | break;
109 | }
110 | }
111 | if (m) {
112 | return i;
113 | }
114 | }
115 | }
116 | return -1;
117 | }
118 |
119 | /**
120 | * Get C-String with a max length.
121 | *
122 | * @param data Data buffer.
123 | * @param i Integer offset.
124 | * @param l Max length.
125 | * @returns ASCII string.
126 | */
127 | export function getCstrN(data: Readonly, i: number, l: number) {
128 | let c = 0;
129 | for (; c < l; c++) {
130 | if (!data[i + c]) {
131 | break;
132 | }
133 | }
134 | // eslint-disable-next-line unicorn/prefer-code-point
135 | return String.fromCharCode(...data.subarray(i, i + c));
136 | }
137 |
138 | /**
139 | * Encode string as UTF-16.
140 | *
141 | * @param str The string to encode.
142 | * @param le Little endian.
143 | * @returns Encoded data.
144 | */
145 | export function encodeUtf16(str: string, le = false) {
146 | const l = str.length;
147 | const d = new ArrayBuffer(l * 2);
148 | const v = new DataView(d);
149 | for (let i = 0; i < l; i++) {
150 | // eslint-disable-next-line unicorn/prefer-code-point
151 | v.setUint16(i * 2, str.charCodeAt(i), le);
152 | }
153 | return new Uint8Array(d);
154 | }
155 |
156 | /**
157 | * Get UTF16 string from data buffer.
158 | *
159 | * @param data Data buffer.
160 | * @param i Start index.
161 | * @param le Little endian.
162 | * @returns Decoded string or null if never null terminated.
163 | */
164 | export function getUtf16(data: Readonly, i: number, le = false) {
165 | const v = new DataView(data.buffer, data.byteOffset, data.byteLength);
166 | const e = v.byteLength - 1;
167 | for (const a = []; i < e; i += 2) {
168 | const c = v.getUint16(i, le);
169 | if (!c) {
170 | // eslint-disable-next-line unicorn/prefer-code-point
171 | return String.fromCharCode(...a);
172 | }
173 | a.push(c);
174 | }
175 | return null;
176 | }
177 |
--------------------------------------------------------------------------------
/src/util/internal/linux/asm.ts:
--------------------------------------------------------------------------------
1 | // This file contains compile-time defined variables.
2 |
3 | export const MENU_I386 = '#{spec/asm/linux/menu-i386}' as unknown as {
4 | [p: string]: number[];
5 | };
6 |
7 | export const MENU_X8664 = '#{spec/asm/linux/menu-x86_64}' as unknown as {
8 | [p: string]: number[];
9 | };
10 |
11 | export const OFFSET_X8664 = '#{spec/asm/linux/offset-x86_64}' as unknown as {
12 | [p: string]: number[];
13 | };
14 |
15 | export const PATCH_I386 = '#{spec/asm/linux/patch-i386}' as unknown as {
16 | [p: string]: number[];
17 | };
18 |
19 | export const PATH_I386 = '#{spec/asm/linux/path-i386}' as unknown as {
20 | [p: string]: number[];
21 | };
22 |
23 | export const PATH_X8664 = '#{spec/asm/linux/path-x86_64}' as unknown as {
24 | [p: string]: number[];
25 | };
26 |
27 | export const TITLE_I386 = '#{spec/asm/linux/title-i386}' as unknown as {
28 | [p: string]: number[];
29 | };
30 |
31 | export const TITLE_X8664 = '#{spec/asm/linux/title-x86_64}' as unknown as {
32 | [p: string]: number[];
33 | };
34 |
--------------------------------------------------------------------------------
/src/util/internal/linux/menu.ts:
--------------------------------------------------------------------------------
1 | import {writeFuzzy} from '../patch.ts';
2 |
3 | import {Elf32, Elf64} from './elf.ts';
4 | import {Patch} from './patch.ts';
5 |
6 | /**
7 | * Patch menu spec.
8 | */
9 | export interface IPatchMenuSpec {
10 | /**
11 | * Expected number of replacements.
12 | */
13 | count: number;
14 |
15 | /**
16 | * Fuzzy find.
17 | */
18 | find: number[];
19 |
20 | /**
21 | * Fuzzy replace.
22 | */
23 | replace: number[];
24 | }
25 |
26 | /**
27 | * PatchMenu object.
28 | */
29 | export abstract class PatchMenu extends Patch {
30 | /**
31 | * Patch spec.
32 | */
33 | protected abstract _spec: IPatchMenuSpec[];
34 |
35 | private _replace_ = [] as [Uint8Array, number, number[]][];
36 |
37 | /**
38 | * @inheritDoc
39 | */
40 | public check() {
41 | this._replace_ = [];
42 | const rep = [] as [Uint8Array, number, number[]][];
43 | for (const {count, find, replace} of this._spec) {
44 | let found = 0;
45 | for (const [, i, d] of this._findFuzzyCode(find)) {
46 | found++;
47 | rep.push([d, i, replace]);
48 | if (found > count) {
49 | return false;
50 | }
51 | }
52 | if (found !== count) {
53 | return false;
54 | }
55 | }
56 | this._replace_ = rep;
57 | return !!rep.length;
58 | }
59 |
60 | /**
61 | * @inheritDoc
62 | */
63 | public patch() {
64 | for (const [d, i, f] of this._replace_) {
65 | writeFuzzy(d, i, f);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/util/internal/linux/menu32.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-classes-per-file */
2 |
3 | import {MENU_I386} from './asm.ts';
4 | import {Elf32} from './elf.ts';
5 | import {PatchMenu} from './menu.ts';
6 |
7 | /**
8 | * PatchMenu32 object.
9 | */
10 | export abstract class PatchMenu32 extends PatchMenu {}
11 |
12 | /**
13 | * Patch objects.
14 | */
15 | export const menu32 = [
16 | /**
17 | * 6.0.79.0 i386.
18 | */
19 | class extends PatchMenu32 {
20 | /**
21 | * @inheritDoc
22 | */
23 | protected _spec = [
24 | {
25 | count: 1,
26 | find: MENU_I386['6-widget-a'],
27 | replace: MENU_I386['6-widget-b']
28 | }
29 | ];
30 | },
31 |
32 | /**
33 | * 9.0.115.0 i386.
34 | */
35 | class extends PatchMenu32 {
36 | /**
37 | * @inheritDoc
38 | */
39 | protected _spec = [
40 | {
41 | count: 2,
42 | find: MENU_I386['9-widget-a'],
43 | replace: MENU_I386['9-widget-b']
44 | },
45 | {
46 | count: 2,
47 | find: MENU_I386['9-menu-a'],
48 | replace: MENU_I386['9-menu-b']
49 | }
50 | ];
51 | },
52 |
53 | /**
54 | * 10.0.12.36 i386.
55 | */
56 | class extends PatchMenu32 {
57 | /**
58 | * @inheritDoc
59 | */
60 | protected _spec = [
61 | {
62 | count: 2,
63 | find: MENU_I386['10.0-widget-a'],
64 | replace: MENU_I386['10.0-widget-b']
65 | },
66 | {
67 | count: 1,
68 | find: MENU_I386['10.0-menu-1-a'],
69 | replace: MENU_I386['10.0-menu-1-b']
70 | },
71 | {
72 | count: 1,
73 | find: MENU_I386['10.0-menu-2-a'],
74 | replace: MENU_I386['10.0-menu-2-b']
75 | }
76 | ];
77 | },
78 |
79 | /**
80 | * 10.1.53.64 i386.
81 | */
82 | class extends PatchMenu32 {
83 | /**
84 | * @inheritDoc
85 | */
86 | protected _spec = [
87 | {
88 | count: 1,
89 | find: MENU_I386['10.1-widget-a'],
90 | replace: MENU_I386['10.1-widget-b']
91 | },
92 | {
93 | count: 1,
94 | find: MENU_I386['10.1-menu-a'],
95 | replace: MENU_I386['10.1-menu-b']
96 | }
97 | ];
98 | },
99 |
100 | /**
101 | * 11.0.1.152 i386.
102 | */
103 | class extends PatchMenu32 {
104 | /**
105 | * @inheritDoc
106 | */
107 | protected _spec = [
108 | {
109 | count: 1,
110 | find: MENU_I386['11.0-widget-a'],
111 | replace: MENU_I386['11.0-widget-b']
112 | },
113 | {
114 | count: 1,
115 | find: MENU_I386['11.0-menu-a'],
116 | replace: MENU_I386['11.0-menu-b']
117 | }
118 | ];
119 | },
120 |
121 | /**
122 | * 11.2.202.228 i386.
123 | */
124 | class extends PatchMenu32 {
125 | /**
126 | * @inheritDoc
127 | */
128 | protected _spec = [
129 | {
130 | count: 1,
131 | find: MENU_I386['11.2-widget-a'],
132 | replace: MENU_I386['11.2-widget-b']
133 | },
134 | {
135 | count: 1,
136 | find: MENU_I386['11.2-menu-a'],
137 | replace: MENU_I386['11.2-menu-b']
138 | }
139 | ];
140 | }
141 | ] as (new (elf: Elf32) => PatchMenu32)[];
142 |
--------------------------------------------------------------------------------
/src/util/internal/linux/menu64.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-classes-per-file */
2 |
3 | import {MENU_X8664} from './asm.ts';
4 | import {Elf64} from './elf.ts';
5 | import {PatchMenu} from './menu.ts';
6 |
7 | /**
8 | * PatchMenu64 object.
9 | */
10 | export abstract class PatchMenu64 extends PatchMenu {}
11 |
12 | /**
13 | * Patch objects.
14 | */
15 | export const menu64 = [
16 | /**
17 | * 24.0.0.186 x86_64.
18 | */
19 | class extends PatchMenu64 {
20 | /**
21 | * @inheritDoc
22 | */
23 | protected _spec = [
24 | {
25 | count: 1,
26 | find: MENU_X8664['24-widget-a'],
27 | replace: MENU_X8664['24-widget-b']
28 | },
29 | {
30 | count: 1,
31 | find: MENU_X8664['24-menu-a'],
32 | replace: MENU_X8664['24-menu-b']
33 | }
34 | ];
35 | },
36 |
37 | /**
38 | * 32.0.0.293 x86_64.
39 | */
40 | class extends PatchMenu64 {
41 | /**
42 | * @inheritDoc
43 | */
44 | protected _spec = [
45 | {
46 | count: 1,
47 | find: MENU_X8664['32-widget-a'],
48 | replace: MENU_X8664['32-widget-b']
49 | },
50 | {
51 | count: 1,
52 | find: MENU_X8664['32-menu-a'],
53 | replace: MENU_X8664['32-menu-b']
54 | }
55 | ];
56 | }
57 | ] as (new (elf: Elf64) => PatchMenu64)[];
58 |
--------------------------------------------------------------------------------
/src/util/internal/linux/offset64.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-classes-per-file */
2 |
3 | import {writeFuzzy} from '../patch.ts';
4 |
5 | import {OFFSET_X8664} from './asm.ts';
6 | import {Elf64} from './elf.ts';
7 | import {Patch} from './patch.ts';
8 |
9 | /**
10 | * Patch offset 64-bit spec.
11 | */
12 | export interface IPatchOffset64Spec {
13 | /**
14 | * Fuzzy find.
15 | */
16 | find: number[];
17 |
18 | /**
19 | * Fuzzy replace.
20 | */
21 | replace: number[];
22 | }
23 |
24 | /**
25 | * PatchOffset64 object.
26 | * Replace the bad ELF header reading logic with new logic.
27 | * The code was never updated from the old 32-bit code and is not accurate.
28 | */
29 | export abstract class PatchOffset64 extends Patch {
30 | /**
31 | * Patch spec.
32 | */
33 | protected abstract _spec: IPatchOffset64Spec;
34 |
35 | private _replace_ = [] as [Uint8Array, number, number[]][];
36 |
37 | /**
38 | * @inheritDoc
39 | */
40 | public check() {
41 | this._replace_ = [];
42 | const {find, replace} = this._spec;
43 | const rep = [] as [Uint8Array, number, number[]][];
44 | for (const [, i, d] of this._findFuzzyCode(find)) {
45 | if (rep.length) {
46 | return false;
47 | }
48 | rep.push([d, i, replace]);
49 | }
50 | if (rep.length !== 1) {
51 | return false;
52 | }
53 | this._replace_ = rep;
54 | return true;
55 | }
56 |
57 | /**
58 | * @inheritDoc
59 | */
60 | public patch() {
61 | for (const [d, i, f] of this._replace_) {
62 | writeFuzzy(d, i, f);
63 | }
64 | }
65 | }
66 |
67 | /**
68 | * Patch objects.
69 | */
70 | export const offset64 = [
71 | /**
72 | * 24.0.0.186 x86_64.
73 | */
74 | class extends PatchOffset64 {
75 | /**
76 | * @inheritDoc
77 | */
78 | protected _spec = {
79 | find: OFFSET_X8664['24-a'],
80 | replace: OFFSET_X8664['24-b']
81 | };
82 | },
83 |
84 | /**
85 | * 25.0.0.127 x86_64.
86 | */
87 | class extends PatchOffset64 {
88 | /**
89 | * @inheritDoc
90 | */
91 | protected _spec = {
92 | find: OFFSET_X8664['25-a'],
93 | replace: OFFSET_X8664['25-b']
94 | };
95 | },
96 |
97 | /**
98 | * 32.0.0.293 x86_64.
99 | */
100 | class extends PatchOffset64 {
101 | /**
102 | * @inheritDoc
103 | */
104 | protected _spec = {
105 | find: OFFSET_X8664['32-a'],
106 | replace: OFFSET_X8664['32-b']
107 | };
108 | }
109 | ] as (new (elf: Elf64) => PatchOffset64)[];
110 |
--------------------------------------------------------------------------------
/src/util/internal/linux/patch.ts:
--------------------------------------------------------------------------------
1 | import {findFuzzy} from '../patch.ts';
2 |
3 | import {PATCH_I386} from './asm.ts';
4 | import {Elf32, Elf32Shdr, Elf64, EM_386} from './elf.ts';
5 |
6 | type Unpacked = T extends (infer U)[] ? U : T;
7 |
8 | /**
9 | * Patch object.
10 | */
11 | export abstract class Patch {
12 | /**
13 | * ELF object.
14 | */
15 | protected _elf: T;
16 |
17 | /**
18 | * Patch constructor.
19 | *
20 | * @param elf ELF object.
21 | */
22 | constructor(elf: T) {
23 | this._elf = elf;
24 | }
25 |
26 | /**
27 | * Get shdr for address.
28 | *
29 | * @param addr The address.
30 | * @returns The shdr or null.
31 | */
32 | protected _getShdrForAddress(addr: number | bigint) {
33 | for (const shdr of this._elf.sectionHeaders) {
34 | const {shAddr} = shdr;
35 | if (
36 | addr >= shAddr &&
37 | addr < (shAddr as number) + (shdr.shSize as number)
38 | ) {
39 | return shdr as Unpacked;
40 | }
41 | }
42 | return null;
43 | }
44 |
45 | /**
46 | * The shdr for address or throw.
47 | *
48 | * @param addr The address.
49 | * @returns The shdr.
50 | */
51 | protected _theShdrForAddress(addr: number | bigint) {
52 | const shdr = this._getShdrForAddress(addr);
53 | if (!shdr) {
54 | throw new Error(`No section at address: ${addr.toString()}`);
55 | }
56 | return shdr;
57 | }
58 |
59 | /**
60 | * Fuzzy find in code.
61 | *
62 | * @param find Fuzzy find.
63 | * @yields The shdr and index in shdr.
64 | */
65 | protected *_findFuzzyCode(find: number[]) {
66 | const shdr = this._getShdrForAddress(this._elf.elfHeader.eEntry);
67 | if (!shdr) {
68 | return;
69 | }
70 | const d = new Uint8Array(shdr.data);
71 | for (const i of findFuzzy(d, find)) {
72 | yield [shdr, i, d] as [
73 | Unpacked,
74 | number,
75 | Uint8Array
76 | ];
77 | }
78 | }
79 |
80 | /**
81 | * Get ebx for code at address.
82 | *
83 | * @param addr The address.
84 | * @returns The value of ebd or null if value not found.
85 | */
86 | protected _findEbx(addr: number) {
87 | if (this._elf.bits !== 32 || this._elf.elfHeader.eMachine !== EM_386) {
88 | throw new Error('Unsupported architecture');
89 | }
90 | const shdr = this._theShdrForAddress(addr) as Elf32Shdr;
91 | const d = new Uint8Array(shdr.data);
92 | const v = new DataView(shdr.data);
93 | const before = addr - shdr.shAddr;
94 | for (const i of findFuzzy(d, PATCH_I386['ebx'], 0, before, true)) {
95 | return shdr.shAddr + i + 5 + v.getUint32(i + 7, true);
96 | }
97 | return null;
98 | }
99 |
100 | /**
101 | * Read C-String from address.
102 | *
103 | * @param addr String address.
104 | * @returns The C-String or null if invalid.
105 | */
106 | protected _readCstr(addr: number | bigint) {
107 | const shdr = this._getShdrForAddress(addr);
108 | if (!shdr) {
109 | return null;
110 | }
111 | const d = new Uint8Array(shdr.data);
112 | const s = Number(addr) - Number(shdr.shAddr);
113 | const e = d.length;
114 | for (let i = s; i < e; i++) {
115 | if (!d[i]) {
116 | // eslint-disable-next-line unicorn/prefer-code-point
117 | return String.fromCharCode(...d.subarray(s, i));
118 | }
119 | }
120 | return null;
121 | }
122 |
123 | /**
124 | * Check patch.
125 | *
126 | * @returns True if valid patch, else false.
127 | */
128 | public abstract check(): boolean;
129 |
130 | /**
131 | * Apply patch.
132 | */
133 | public abstract patch(): void;
134 | }
135 |
--------------------------------------------------------------------------------
/src/util/internal/linux/path.ts:
--------------------------------------------------------------------------------
1 | import {findIndex} from '../data.ts';
2 |
3 | import {Elf32, Elf64} from './elf.ts';
4 | import {Patch} from './patch.ts';
5 |
6 | /**
7 | * PatchPath object.
8 | * Patch broken Linux projector path code.
9 | * Replaces bad "file:" prefix with "file://" for projector self URL.
10 | */
11 | export abstract class PatchPath extends Patch {
12 | /**
13 | * Get a remapped pointer, if pointer needs remapping.
14 | *
15 | * @param ptr Current pointer.
16 | * @returns Remapped pointer or null.
17 | */
18 | protected _getRemap(ptr: number | bigint) {
19 | const str = this._readCstr(ptr);
20 | if (str !== 'file:') {
21 | return null;
22 | }
23 | const shdr = this._theShdrForAddress(ptr);
24 | const data = new Uint8Array(shdr.data);
25 | const strd = new TextEncoder().encode('\0file://\0');
26 | const fileI = findIndex(data, strd) + 1;
27 | if (!fileI) {
28 | return null;
29 | }
30 | return (
31 | this._elf.bits === 64
32 | ? (shdr.shAddr as bigint) + BigInt(fileI)
33 | : (shdr.shAddr as number) + fileI
34 | ) as T['elfHeader']['eEntry'];
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/util/internal/linux/path32.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-classes-per-file */
2 |
3 | import {PATH_I386} from './asm.ts';
4 | import {Elf32} from './elf.ts';
5 | import {PatchPath} from './path.ts';
6 |
7 | /**
8 | * PatchPath32 object.
9 | */
10 | export abstract class PatchPath32 extends PatchPath {}
11 |
12 | /**
13 | * PatchPath32Dir object.
14 | */
15 | abstract class PatchPath32Dir extends PatchPath32 {}
16 |
17 | /**
18 | * PatchPath32File object.
19 | */
20 | abstract class PatchPath32File extends PatchPath32 {
21 | /**
22 | * Relative offset.
23 | */
24 | protected abstract readonly _relative: boolean;
25 |
26 | /**
27 | * Fuzzy find.
28 | */
29 | protected abstract readonly _find: number[];
30 |
31 | /**
32 | * Address offset.
33 | */
34 | protected abstract readonly _offset: number;
35 |
36 | private _addr_ = 0;
37 |
38 | private _remap_ = 0;
39 |
40 | private _ebx_ = 0;
41 |
42 | /**
43 | * @inheritDoc
44 | */
45 | public check() {
46 | const {_relative: rel, _find: find, _offset: o} = this;
47 | for (const [shdr, i, d] of this._findFuzzyCode(find)) {
48 | const addr = shdr.shAddr + i;
49 | const ebx = rel ? this._findEbx(addr) : null;
50 | if (rel && ebx === null) {
51 | continue;
52 | }
53 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
54 | const ptr = rel
55 | ? (ebx as number) + v.getInt32(i + o, true)
56 | : v.getUint32(i + o, true);
57 | const remap = this._getRemap(ptr);
58 | if (!remap) {
59 | continue;
60 | }
61 | if (this._addr_) {
62 | return false;
63 | }
64 | this._addr_ = addr;
65 | this._remap_ = remap;
66 | if (rel) {
67 | this._ebx_ = ebx as number;
68 | }
69 | }
70 | return !!this._addr_;
71 | }
72 |
73 | /**
74 | * @inheritDoc
75 | */
76 | public patch() {
77 | const {_relative: rel, _offset: o} = this;
78 | const addr = this._addr_;
79 | const shdr = this._theShdrForAddress(addr);
80 | const v = new DataView(shdr.data);
81 | const i = addr - shdr.shAddr;
82 | if (rel) {
83 | v.setInt32(i + o, this._remap_ - this._ebx_, true);
84 | } else {
85 | v.setUint32(i + o, this._remap_, true);
86 | }
87 | }
88 | }
89 |
90 | /**
91 | * PatchPath32FileAbs object.
92 | */
93 | abstract class PatchPath32FileAbs extends PatchPath32File {
94 | /**
95 | * @inheritDoc
96 | */
97 | protected readonly _relative = false;
98 | }
99 |
100 | /**
101 | * PatchPath32FileRel object.
102 | */
103 | abstract class PatchPath32FileRel extends PatchPath32File {
104 | /**
105 | * @inheritDoc
106 | */
107 | protected readonly _relative = true;
108 | }
109 |
110 | /**
111 | * Patch objects.
112 | */
113 | export const path32 = [
114 | /**
115 | * 6.0.79.0 i386.
116 | */
117 | class extends PatchPath32Dir {
118 | private _addr_ = 0;
119 |
120 | /**
121 | * @inheritDoc
122 | */
123 | public check() {
124 | for (const [shdr, i] of this._findFuzzyCode(PATH_I386['6'])) {
125 | if (this._addr_) {
126 | return false;
127 | }
128 | this._addr_ = shdr.shAddr + i;
129 | }
130 | return !!this._addr_;
131 | }
132 |
133 | /**
134 | * @inheritDoc
135 | */
136 | public patch() {
137 | const {_addr_: addr} = this;
138 | const shdr = this._theShdrForAddress(addr);
139 | const v = new DataView(shdr.data);
140 | const i = addr - shdr.shAddr;
141 |
142 | // sIsProjector -> sExecutableName
143 | const ptr = v.getUint32(i + 24, true) + 4;
144 |
145 | // nop 2x
146 | v.setUint8(i + 68, 0x90);
147 | v.setUint8(i + 69, 0x90);
148 |
149 | // mov esi, DWORD PTR ds:...
150 | v.setUint8(i + 70, 0x8b);
151 | v.setUint8(i + 71, 0x35);
152 | v.setUint32(i + 72, ptr, true);
153 |
154 | // push esi
155 | v.setUint8(i + 96, 0x56);
156 |
157 | // push esi
158 | v.setUint8(i + 122, 0x56);
159 | }
160 | },
161 |
162 | /**
163 | * 9.0.115.0 i386.
164 | */
165 | class extends PatchPath32FileAbs {
166 | /**
167 | * @inheritDoc
168 | */
169 | protected readonly _find = PATH_I386['9'];
170 |
171 | /**
172 | * @inheritDoc
173 | */
174 | protected readonly _offset = 10;
175 | },
176 |
177 | /**
178 | * 10.0.12.36 i386.
179 | */
180 | class extends PatchPath32FileAbs {
181 | /**
182 | * @inheritDoc
183 | */
184 | protected readonly _find = PATH_I386['10.0'];
185 |
186 | /**
187 | * @inheritDoc
188 | */
189 | protected readonly _offset = 16;
190 | },
191 |
192 | /**
193 | * 10.1.53.64 i386.
194 | */
195 | class extends PatchPath32FileAbs {
196 | /**
197 | * @inheritDoc
198 | */
199 | protected readonly _find = PATH_I386['10.1'];
200 |
201 | /**
202 | * @inheritDoc
203 | */
204 | protected readonly _offset = 16;
205 | },
206 |
207 | /**
208 | * 11.0.1.152 i386.
209 | */
210 | class extends PatchPath32FileAbs {
211 | /**
212 | * @inheritDoc
213 | */
214 | protected readonly _find = PATH_I386['11.0'];
215 |
216 | /**
217 | * @inheritDoc
218 | */
219 | protected readonly _offset = 18;
220 | },
221 |
222 | /**
223 | * 11.2.202.228 i386.
224 | */
225 | class extends PatchPath32FileRel {
226 | /**
227 | * @inheritDoc
228 | */
229 | protected readonly _find = PATH_I386['11.2'];
230 |
231 | /**
232 | * @inheritDoc
233 | */
234 | protected readonly _offset = 2;
235 | }
236 | ] as (new (elf: Elf32) => PatchPath32File)[];
237 |
--------------------------------------------------------------------------------
/src/util/internal/linux/path64.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-classes-per-file */
2 |
3 | import {PATH_X8664} from './asm.ts';
4 | import {Elf64} from './elf.ts';
5 | import {PatchPath} from './path.ts';
6 |
7 | /**
8 | * PatchPath64 object.
9 | */
10 | export abstract class PatchPath64 extends PatchPath {}
11 |
12 | /**
13 | * PatchPath64File object.
14 | */
15 | abstract class PatchPath64File extends PatchPath64 {
16 | /**
17 | * Fuzzy find.
18 | */
19 | protected abstract readonly _find: number[];
20 |
21 | /**
22 | * Address offset.
23 | */
24 | protected abstract readonly _offset: number;
25 |
26 | private _addr_ = 0n;
27 |
28 | private _remap_ = 0n;
29 |
30 | /**
31 | * @inheritDoc
32 | */
33 | public check() {
34 | const {_find: find, _offset: o} = this;
35 | for (const [shdr, i, d] of this._findFuzzyCode(find)) {
36 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
37 | const addr = shdr.shAddr + BigInt(i);
38 | const rip = addr + 10n;
39 | const ptr = rip + BigInt(v.getUint32(i + o, true));
40 | const remap = this._getRemap(ptr);
41 | if (!remap) {
42 | continue;
43 | }
44 | if (this._addr_) {
45 | return false;
46 | }
47 | this._addr_ = addr;
48 | this._remap_ = remap;
49 | }
50 | return !!this._addr_;
51 | }
52 |
53 | /**
54 | * @inheritDoc
55 | */
56 | public patch() {
57 | const {_offset: o} = this;
58 | const addr = this._addr_;
59 | const shdr = this._theShdrForAddress(addr);
60 | const v = new DataView(shdr.data);
61 | const i = Number(addr - shdr.shAddr);
62 | const rip = shdr.shAddr + BigInt(i + 10);
63 | v.setUint32(i + o, Number(this._remap_ - rip), true);
64 | }
65 | }
66 |
67 | /**
68 | * Patch objects.
69 | */
70 | export const path64 = [
71 | /**
72 | * 24.0.0.186 x86_64.
73 | */
74 | class extends PatchPath64File {
75 | /**
76 | * @inheritDoc
77 | */
78 | protected readonly _find = PATH_X8664[24];
79 |
80 | /**
81 | * @inheritDoc
82 | */
83 | protected readonly _offset = 6;
84 | }
85 | ] as (new (elf: Elf64) => PatchPath64)[];
86 |
--------------------------------------------------------------------------------
/src/util/internal/linux/title.ts:
--------------------------------------------------------------------------------
1 | import {Elf32, Elf64} from './elf.ts';
2 | import {Patch} from './patch.ts';
3 |
4 | // Match all known titles.
5 | export const titleMatchM = /^Macromedia Flash Player \d$/;
6 | export const titleMatchA = /^Adobe Flash Player \d+((?:,\d+){3})?$/;
7 |
8 | /**
9 | * PatchTitle object.
10 | */
11 | export abstract class PatchTitle extends Patch {
12 | /**
13 | * New title address.
14 | */
15 | protected readonly _titleA: T['elfHeader']['eEntry'];
16 |
17 | /**
18 | * New title length, without null termination.
19 | */
20 | protected readonly _titleL: number;
21 |
22 | /**
23 | * PatchTitle constructor.
24 | *
25 | * @param elf ELF object.
26 | * @param titleA New title address.
27 | * @param titleL New title length, without null termination.
28 | */
29 | constructor(elf: T, titleA: T['elfHeader']['eEntry'], titleL: number) {
30 | super(elf);
31 |
32 | this._titleA = titleA;
33 | this._titleL = titleL;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/util/internal/linux/title32.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-classes-per-file */
2 |
3 | import {TITLE_I386} from './asm.ts';
4 | import {Elf32} from './elf.ts';
5 | import {PatchTitle, titleMatchM, titleMatchA} from './title.ts';
6 |
7 | /**
8 | * PatchTitle32 object.
9 | */
10 | export abstract class PatchTitle32 extends PatchTitle {}
11 |
12 | /**
13 | * Patch objects.
14 | */
15 | export const title32 = [
16 | /**
17 | * 6.0.79.0 i386.
18 | */
19 | class extends PatchTitle32 {
20 | private _addr_ = 0;
21 |
22 | /**
23 | * @inheritDoc
24 | */
25 | public check() {
26 | for (const [shdr, i, d] of this._findFuzzyCode(TITLE_I386['6'])) {
27 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
28 | const ptr = v.getUint32(i + 9, true);
29 | const shdr2 = this._getShdrForAddress(ptr);
30 | if (!shdr2) {
31 | continue;
32 | }
33 | const v2 = new DataView(shdr2.data);
34 | const i2 = ptr - shdr2.shAddr;
35 | const ptr2 = v2.getUint32(i2, true);
36 | const str = this._readCstr(ptr2);
37 | if (!str || !titleMatchM.test(str)) {
38 | continue;
39 | }
40 | this._addr_ = shdr.shAddr + i;
41 | }
42 | return !!this._addr_;
43 | }
44 |
45 | /**
46 | * @inheritDoc
47 | */
48 | public patch() {
49 | const addr = this._addr_;
50 | const shdr = this._theShdrForAddress(addr);
51 | const v = new DataView(shdr.data);
52 | let i = addr - shdr.shAddr + 7;
53 |
54 | // nop
55 | v.setUint8(i++, 0x90);
56 |
57 | // push ...
58 | v.setUint8(i++, 0x68);
59 | v.setUint32(i, this._titleA, true);
60 | }
61 | },
62 |
63 | /**
64 | * 9.0.115.0 i386.
65 | */
66 | class extends PatchTitle32 {
67 | private _addrs_ = [] as number[];
68 |
69 | /**
70 | * @inheritDoc
71 | */
72 | public check() {
73 | this._addrs_ = [];
74 | for (const [shdr, i, d] of this._findFuzzyCode(TITLE_I386['9'])) {
75 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
76 | const ptr = v.getUint32(i + 6, true);
77 | const shdr2 = this._getShdrForAddress(ptr);
78 | if (!shdr2) {
79 | continue;
80 | }
81 | const v2 = new DataView(shdr2.data);
82 | const i2 = ptr - shdr2.shAddr;
83 | const ptr2 = v2.getUint32(i2, true);
84 | const str = this._readCstr(ptr2);
85 | if (!str || !titleMatchA.test(str)) {
86 | continue;
87 | }
88 | this._addrs_.push(shdr.shAddr + i);
89 | }
90 | return this._addrs_.length === 2;
91 | }
92 |
93 | /**
94 | * @inheritDoc
95 | */
96 | public patch() {
97 | for (const addr of this._addrs_) {
98 | const shdr = this._theShdrForAddress(addr);
99 | const v = new DataView(shdr.data);
100 | let i = addr - shdr.shAddr + 5;
101 |
102 | // mov eax, ...
103 | v.setUint8(i++, 0xb8);
104 | v.setUint32(i, this._titleA, true);
105 | }
106 | }
107 | },
108 |
109 | /**
110 | * 10.1.53.64 i386.
111 | */
112 | class extends PatchTitle32 {
113 | private _addr_ = 0;
114 |
115 | /**
116 | * @inheritDoc
117 | */
118 | public check() {
119 | for (const [shdr, i, d] of this._findFuzzyCode(
120 | TITLE_I386['10.1']
121 | )) {
122 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
123 | const addr = shdr.shAddr + i;
124 | const ptr = v.getUint32(i + 12, true);
125 | const len = v.getUint32(i + 4, true);
126 | const str = this._readCstr(ptr);
127 | if (!str || str.length !== len || !titleMatchA.test(str)) {
128 | continue;
129 | }
130 | if (this._addr_) {
131 | return false;
132 | }
133 | this._addr_ = addr;
134 | }
135 | return !!this._addr_;
136 | }
137 |
138 | /**
139 | * @inheritDoc
140 | */
141 | public patch() {
142 | const addr = this._addr_;
143 | const shdr = this._theShdrForAddress(addr);
144 | const v = new DataView(shdr.data);
145 | const i = addr - shdr.shAddr;
146 | v.setUint32(i + 12, this._titleA, true);
147 | v.setUint32(i + 4, this._titleL, true);
148 | }
149 | },
150 |
151 | /**
152 | * 11.2.202.228 i386.
153 | */
154 | class extends PatchTitle32 {
155 | private _addr_ = 0;
156 |
157 | private _ebx_ = 0;
158 |
159 | /**
160 | * @inheritDoc
161 | */
162 | public check() {
163 | for (const [shdr, i, d] of this._findFuzzyCode(
164 | TITLE_I386['11.2']
165 | )) {
166 | const addr = shdr.shAddr + i;
167 | const ebx = this._findEbx(addr);
168 | if (ebx === null) {
169 | continue;
170 | }
171 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
172 | const ptr = ebx + v.getInt32(i + 2, true);
173 | const len = v.getUint32(i + 14, true);
174 | const str = this._readCstr(ptr);
175 | if (!str || str.length !== len || !titleMatchA.test(str)) {
176 | continue;
177 | }
178 | if (this._addr_) {
179 | return false;
180 | }
181 | this._addr_ = addr;
182 | this._ebx_ = ebx;
183 | }
184 | return !!this._addr_;
185 | }
186 |
187 | /**
188 | * @inheritDoc
189 | */
190 | public patch() {
191 | const addr = this._addr_;
192 | const ebx = this._ebx_;
193 | const shdr = this._theShdrForAddress(addr);
194 | const v = new DataView(shdr.data);
195 | const i = addr - shdr.shAddr;
196 | v.setInt32(i + 2, this._titleA - ebx, true);
197 | v.setUint32(i + 14, this._titleL, true);
198 | }
199 | }
200 | ] as (new (elf: Elf32, titleA: number, titleL: number) => PatchTitle32)[];
201 |
--------------------------------------------------------------------------------
/src/util/internal/linux/title64.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable max-classes-per-file */
2 |
3 | import {TITLE_X8664} from './asm.ts';
4 | import {Elf64} from './elf.ts';
5 | import {PatchTitle, titleMatchA} from './title.ts';
6 |
7 | /**
8 | * PatchTitle64 object.
9 | */
10 | export abstract class PatchTitle64 extends PatchTitle {}
11 |
12 | /**
13 | * Patch objects.
14 | */
15 | export const title64 = [
16 | /**
17 | * 24.0.0.186 x86_64.
18 | */
19 | class extends PatchTitle64 {
20 | private _addr_ = 0n;
21 |
22 | /**
23 | * @inheritDoc
24 | */
25 | public check() {
26 | for (const [shdr, i, d] of this._findFuzzyCode(TITLE_X8664['24'])) {
27 | const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
28 | const addr = shdr.shAddr + BigInt(i);
29 | const rip = addr + 7n;
30 | const ptr = rip + BigInt(v.getUint32(i + 3, true));
31 | const len = v.getUint32(i + 8, true);
32 | const str = this._readCstr(ptr);
33 | if (!str || str.length !== len || !titleMatchA.test(str)) {
34 | continue;
35 | }
36 | if (this._addr_) {
37 | return false;
38 | }
39 | this._addr_ = addr;
40 | }
41 | return !!this._addr_;
42 | }
43 |
44 | /**
45 | * @inheritDoc
46 | */
47 | public patch() {
48 | const addr = this._addr_;
49 | const shdr = this._theShdrForAddress(addr);
50 | const v = new DataView(shdr.data);
51 | const i = Number(addr - shdr.shAddr);
52 | const rip = shdr.shAddr + BigInt(i + 7);
53 | v.setUint32(i + 3, Number(this._titleA - rip), true);
54 | v.setUint32(i + 8, this._titleL, true);
55 | }
56 | }
57 | ] as (new (elf: Elf64, titleA: bigint, titleL: number) => PatchTitle64)[];
58 |
--------------------------------------------------------------------------------
/src/util/internal/mac/asm.ts:
--------------------------------------------------------------------------------
1 | // This file contains compile-time defined variables.
2 |
3 | export const TITLE_I386 = '#{spec/asm/mac/title-i386}' as unknown as {
4 | [p: string]: number[];
5 | };
6 |
7 | export const TITLE_X8664 = '#{spec/asm/mac/title-x86_64}' as unknown as {
8 | [p: string]: number[];
9 | };
10 |
11 | export const TITLE_ARM64 = '#{spec/asm/mac/title-arm64}' as unknown as {
12 | [p: string]: number[];
13 | };
14 |
--------------------------------------------------------------------------------
/src/util/internal/mac/constants.ts:
--------------------------------------------------------------------------------
1 | export const VM_PROT_READ = 0x1;
2 |
3 | export const FAT_MAGIC = 0xcafebabe;
4 | export const MH_MAGIC = 0xfeedface;
5 | export const MH_CIGAM = 0xcefaedfe;
6 | export const MH_MAGIC_64 = 0xfeedfacf;
7 | export const MH_CIGAM_64 = 0xcffaedfe;
8 |
9 | export const LC_REQ_DYLD = 0x80000000;
10 |
11 | export const LC_SEGMENT = 0x1;
12 | export const LC_SYMTAB = 0x2;
13 | export const LC_DYSYMTAB = 0xb;
14 | export const LC_SEGMENT_64 = 0x19;
15 | export const LC_CODE_SIGNATURE = 0x1d;
16 | export const LC_SEGMENT_SPLIT_INFO = 0x1e;
17 | export const LC_DYLD_INFO = 0x22;
18 | // eslint-disable-next-line no-bitwise
19 | export const LC_DYLD_INFO_ONLY = (0x22 | LC_REQ_DYLD) >>> 0;
20 | export const LC_FUNCTION_STARTS = 0x26;
21 | export const LC_DATA_IN_CODE = 0x29;
22 | export const LC_DYLIB_CODE_SIGN_DRS = 0x2b;
23 | export const LC_LINKER_OPTIMIZATION_HINT = 0x2e;
24 | // eslint-disable-next-line no-bitwise
25 | export const LC_DYLD_EXPORTS_TRIE = (0x33 | LC_REQ_DYLD) >>> 0;
26 | // eslint-disable-next-line no-bitwise
27 | export const LC_DYLD_CHAINED_FIXUPS = (0x34 | LC_REQ_DYLD) >>> 0;
28 |
29 | export const SEG_TEXT = '__TEXT';
30 | export const SECT_TEXT = '__text';
31 | export const SEG_LINKEDIT = '__LINKEDIT';
32 |
33 | export const CPU_TYPE_POWERPC = 0x00000012;
34 | export const CPU_TYPE_POWERPC64 = 0x01000012;
35 | export const CPU_TYPE_I386 = 0x00000007;
36 | export const CPU_TYPE_X86_64 = 0x01000007;
37 | export const CPU_TYPE_ARM64 = 0x0100000c;
38 |
--------------------------------------------------------------------------------
/src/util/internal/patch.ts:
--------------------------------------------------------------------------------
1 | import {findIndex} from './data.ts';
2 |
3 | /**
4 | * Find exact matches in data.
5 | *
6 | * @param data Data to search.
7 | * @param find Search for.
8 | * @param from Search from.
9 | * @yields Index.
10 | */
11 | export function* findExact(
12 | data: Readonly,
13 | find: Readonly,
14 | from = 0
15 | ) {
16 | for (let index = from - 1; ; ) {
17 | index = findIndex(data, find, index + 1);
18 | if (index < 0) {
19 | break;
20 | }
21 | yield index;
22 | }
23 | }
24 |
25 | /**
26 | * Find similar matches in data.
27 | *
28 | * @param data Data to search.
29 | * @param find Search for.
30 | * @param from Search from.
31 | * @param until Search until.
32 | * @param backward Search backwards.
33 | * @yields Index.
34 | */
35 | export function* findFuzzy(
36 | data: Readonly,
37 | find: readonly number[],
38 | from = 0,
39 | until = -1,
40 | backward = false
41 | ) {
42 | const end = (until < 0 ? data.length : until) - find.length;
43 | const add = backward ? -1 : 1;
44 | const stop = backward ? -1 : end + 1;
45 | for (let i = backward ? end : from; i !== stop; i += add) {
46 | let found = true;
47 | const {length} = find;
48 | for (let j = 0; j < length; j++) {
49 | const b = find[j];
50 | if (!(b < 0) && data[i + j] !== b) {
51 | found = false;
52 | break;
53 | }
54 | }
55 | if (found) {
56 | yield i;
57 | }
58 | }
59 | }
60 |
61 | /**
62 | * Fuzzy find once, null if multiple.
63 | *
64 | * @param data Data.
65 | * @param fuzzy Fuzzy data.
66 | * @returns Index or null.
67 | */
68 | export function findFuzzyOnce(
69 | data: Readonly,
70 | fuzzy: readonly number[]
71 | ) {
72 | let r = null;
73 | for (const found of findFuzzy(data, fuzzy)) {
74 | if (r !== null) {
75 | return null;
76 | }
77 | r = found;
78 | }
79 | return r;
80 | }
81 |
82 | /**
83 | * Write similar match in data.
84 | *
85 | * @param data Data to write into.
86 | * @param offset Offset to write at.
87 | * @param fuzzy The similar data.
88 | */
89 | export function writeFuzzy(
90 | data: Uint8Array,
91 | offset: number,
92 | fuzzy: readonly number[]
93 | ) {
94 | const {length} = fuzzy;
95 | for (let i = 0; i < length; i++) {
96 | const b = fuzzy[i];
97 | if (!(b < 0)) {
98 | data[offset + i] = b;
99 | }
100 | }
101 | }
102 |
103 | /**
104 | * Find the offsets for the patches in a group.
105 | *
106 | * @param data Data buffer.
107 | * @param patches Patches group.
108 | * @returns The offsets or null.
109 | */
110 | export function patchGroupOffsets(
111 | data: Readonly,
112 | patches: {
113 | count: number;
114 | find: readonly number[];
115 | replace: readonly number[];
116 | }[]
117 | ) {
118 | const offsets = [];
119 | for (const {find, count} of patches) {
120 | const found = [...findFuzzy(data, find)];
121 | if (found.length !== count) {
122 | return null;
123 | }
124 | offsets.push(found);
125 | }
126 | return offsets;
127 | }
128 |
129 | /**
130 | * Patch one group and only from list of patch groups.
131 | *
132 | * @param data Data to be patched.
133 | * @param patches Patches list.
134 | * @param type Patch type.
135 | */
136 | export function patchOnce(
137 | data: Uint8Array,
138 | patches: {
139 | count: number;
140 | find: readonly number[];
141 | replace: readonly number[];
142 | }[][],
143 | type: string
144 | ) {
145 | // Search the buffer for patch candidates.
146 | let foundOffsets = null;
147 | let foundGroup = null;
148 | for (const group of patches) {
149 | const offsets = patchGroupOffsets(data, group);
150 | if (!offsets) {
151 | continue;
152 | }
153 | if (foundOffsets) {
154 | throw new Error(`Multiple patch candidates for: ${type}`);
155 | }
156 | foundOffsets = offsets;
157 | foundGroup = group;
158 | }
159 | if (!foundGroup || !foundOffsets) {
160 | throw new Error(`No patch candidates for: ${type}`);
161 | }
162 |
163 | // Apply the patches to the buffer.
164 | const {length} = foundGroup;
165 | for (let i = 0; i < length; i++) {
166 | for (const offset of foundOffsets[i]) {
167 | writeFuzzy(data, offset, foundGroup[i].replace);
168 | }
169 | }
170 | }
171 |
172 | /**
173 | * A utility to slide values within a window.
174 | *
175 | * @param amount The amount to slide.
176 | * @param offset Window offset.
177 | * @param size Window size.
178 | * @returns Sliding functions.
179 | */
180 | export function slider(amount: number, offset: number, size: number) {
181 | const end = offset + size;
182 | return {
183 | /**
184 | * For UINT32.
185 | *
186 | * @param data Data view.
187 | * @param i Integer offset.
188 | * @param le Little endian if true.
189 | */
190 | u32: (data: DataView, i: number, le: boolean) => {
191 | const v = data.getUint32(i, le);
192 | if (v >= offset && v <= end) {
193 | data.setUint32(i, v + amount, le);
194 | }
195 | }
196 | };
197 | }
198 |
--------------------------------------------------------------------------------
/src/util/internal/windows/asm.ts:
--------------------------------------------------------------------------------
1 | // This file contains compile-time defined variables.
2 |
3 | export const OOD_I386 = '#{spec/asm/windows/ood-i386}' as unknown as {
4 | [p: string]: number[];
5 | };
6 |
7 | export const OOD_X8664 = '#{spec/asm/windows/ood-x86_64}' as unknown as {
8 | [p: string]: number[];
9 | };
10 |
--------------------------------------------------------------------------------
/src/util/internal/windows/constants.ts:
--------------------------------------------------------------------------------
1 | // IMAGE_DATA_DIRECTORY indexes.
2 | export const IDD_RESOURCE = 2;
3 | export const IDD_EXCEPTION = 3;
4 | export const IDD_BASE_RELOCATION = 5;
5 | export const IDD_RESERVED = 15;
6 |
7 | // IMAGE_SECTION_HEADER characteristics.
8 | export const IMAGE_SCN_CNT_CODE = 0x00000020;
9 | export const IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040;
10 | export const IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080;
11 | export const IMAGE_SCN_MEM_SHARED = 0x10000000;
12 | export const IMAGE_SCN_MEM_EXECUTE = 0x20000000;
13 | export const IMAGE_SCN_MEM_READ = 0x40000000;
14 | export const IMAGE_SCN_MEM_WRITE = 0x80000000;
15 |
--------------------------------------------------------------------------------
/src/util/internal/windows/exe.ts:
--------------------------------------------------------------------------------
1 | import {NtExecutable} from '@shockpkg/resedit';
2 |
3 | import {align} from '../data.ts';
4 |
5 | import {
6 | IDD_BASE_RELOCATION,
7 | IMAGE_SCN_CNT_CODE,
8 | IMAGE_SCN_CNT_INITIALIZED_DATA,
9 | IMAGE_SCN_CNT_UNINITIALIZED_DATA
10 | } from './constants.ts';
11 |
12 | /**
13 | * Get the EXE section that includes an address.
14 | *
15 | * @param exe NtExecutable instance.
16 | * @param address The address.
17 | * @returns The section or null if section not found.
18 | */
19 | export function exeSectionByAddress(exe: NtExecutable, address: number) {
20 | for (const {info, data} of exe.getAllSections()) {
21 | const {virtualAddress, virtualSize} = info;
22 | if (
23 | address >= virtualAddress &&
24 | address < virtualAddress + virtualSize
25 | ) {
26 | return {
27 | info: info as {virtualAddress: number},
28 | data
29 | };
30 | }
31 | }
32 | return null;
33 | }
34 |
35 | /**
36 | * Get the EXE code section.
37 | *
38 | * @param exe NtExecutable instance.
39 | * @returns The section.
40 | */
41 | export function exeCodeSection(exe: NtExecutable) {
42 | const s = exeSectionByAddress(exe, exe.newHeader.optionalHeader.baseOfCode);
43 | if (!s || !s.data) {
44 | throw new Error(`Invalid PE code section`);
45 | }
46 | return {
47 | info: s.info,
48 | data: s.data
49 | };
50 | }
51 |
52 | /**
53 | * Assert the given section is last section.
54 | *
55 | * @param exe NtExecutable instance.
56 | * @param index ImageDirectory index.
57 | * @param name Friendly name for messages.
58 | */
59 | export function exeAssertLastSection(
60 | exe: NtExecutable,
61 | index: number,
62 | name: string
63 | ) {
64 | const section = exe.getSectionByEntry(index);
65 | if (!section) {
66 | throw new Error(`Missing section: ${index}:${name}`);
67 | }
68 | const allSections = exe.getAllSections();
69 | let last = allSections[0].info;
70 | for (const {info} of allSections) {
71 | if (info.pointerToRawData > last.pointerToRawData) {
72 | last = info;
73 | }
74 | }
75 | const {info} = section;
76 | if (info.pointerToRawData < last.pointerToRawData) {
77 | throw new Error(`Not the last section: ${index}:${name}`);
78 | }
79 | }
80 |
81 | /**
82 | * Removes the reloc section if exists, fails if not the last section.
83 | *
84 | * @param exe NtExecutable instance.
85 | * @returns Restore function.
86 | */
87 | export function exeRemoveReloc(exe: NtExecutable) {
88 | const section = exe.getSectionByEntry(IDD_BASE_RELOCATION);
89 | if (!section) {
90 | return () => {};
91 | }
92 | const {size} =
93 | exe.newHeader.optionalHeaderDataDirectory.get(IDD_BASE_RELOCATION);
94 | exeAssertLastSection(exe, IDD_BASE_RELOCATION, '.reloc');
95 | exe.setSectionByEntry(IDD_BASE_RELOCATION, null);
96 | return () => {
97 | exe.setSectionByEntry(IDD_BASE_RELOCATION, section);
98 | const {virtualAddress} =
99 | exe.newHeader.optionalHeaderDataDirectory.get(IDD_BASE_RELOCATION);
100 | exe.newHeader.optionalHeaderDataDirectory.set(IDD_BASE_RELOCATION, {
101 | virtualAddress,
102 | size
103 | });
104 | };
105 | }
106 |
107 | /**
108 | * Update the sizes in EXE headers.
109 | *
110 | * @param exe NtExecutable instance.
111 | */
112 | export function exeUpdateSizes(exe: NtExecutable) {
113 | const {optionalHeader} = exe.newHeader;
114 | const {fileAlignment} = optionalHeader;
115 | let sizeOfCode = 0;
116 | let sizeOfInitializedData = 0;
117 | let sizeOfUninitializedData = 0;
118 | for (const {
119 | info: {characteristics, sizeOfRawData, virtualSize}
120 | } of exe.getAllSections()) {
121 | // eslint-disable-next-line no-bitwise
122 | if (characteristics & IMAGE_SCN_CNT_CODE) {
123 | sizeOfCode += sizeOfRawData;
124 | }
125 | // eslint-disable-next-line no-bitwise
126 | if (characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
127 | sizeOfInitializedData += Math.max(
128 | sizeOfRawData,
129 | align(virtualSize, fileAlignment)
130 | );
131 | }
132 | // eslint-disable-next-line no-bitwise
133 | if (characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
134 | sizeOfUninitializedData += align(virtualSize, fileAlignment);
135 | }
136 | }
137 | optionalHeader.sizeOfCode = sizeOfCode;
138 | optionalHeader.sizeOfInitializedData = sizeOfInitializedData;
139 | optionalHeader.sizeOfUninitializedData = sizeOfUninitializedData;
140 | }
141 |
--------------------------------------------------------------------------------
/src/util/internal/windows/ood32.ts:
--------------------------------------------------------------------------------
1 | import {OOD_I386} from './asm.ts';
2 |
3 | export const ood32 = [
4 | // 30.0.0.113
5 | [
6 | {
7 | count: 1,
8 | find: OOD_I386['30'],
9 | replace: OOD_I386['ret4']
10 | }
11 | ],
12 | // 31.0.0.108
13 | [
14 | {
15 | count: 1,
16 | find: OOD_I386['31'],
17 | replace: OOD_I386['ret4']
18 | }
19 | ]
20 | ];
21 |
--------------------------------------------------------------------------------
/src/util/internal/windows/ood64.ts:
--------------------------------------------------------------------------------
1 | import {OOD_X8664} from './asm.ts';
2 |
3 | export const ood64 = [
4 | // 26.0.0.137, 32.0.0.270
5 | [
6 | {
7 | count: 1,
8 | find: OOD_X8664['26'],
9 | replace: OOD_X8664['ret']
10 | }
11 | ],
12 | // 30.0.0.134
13 | [
14 | {
15 | count: 1,
16 | find: OOD_X8664['30'],
17 | replace: OOD_X8664['ret']
18 | }
19 | ]
20 | ];
21 |
--------------------------------------------------------------------------------
/src/util/internal/windows/rsrc.ts:
--------------------------------------------------------------------------------
1 | import {NtExecutableResource, Resource, Data} from '@shockpkg/resedit';
2 |
3 | /**
4 | * Parse PE version string to integers (MS then LS bits) or null.
5 | *
6 | * @param version Version string.
7 | * @returns Version integers ([MS, LS]) or null.
8 | */
9 | function peVersionInts(version: string): [number, number] | null {
10 | const parts = version.split(/[,.]/);
11 | const numbers = [];
12 | for (const part of parts) {
13 | const n = /^\d+$/.test(part) ? +part : -1;
14 | if (n < 0 || n > 0xffff) {
15 | return null;
16 | }
17 | numbers.push(n);
18 | }
19 | return numbers.length
20 | ? [
21 | // eslint-disable-next-line no-bitwise
22 | (((numbers[0] || 0) << 16) | (numbers[1] || 0)) >>> 0,
23 | // eslint-disable-next-line no-bitwise
24 | (((numbers[2] || 0) << 16) | (numbers[3] || 0)) >>> 0
25 | ]
26 | : null;
27 | }
28 |
29 | /**
30 | * Replace all the icons in all icon groups.
31 | *
32 | * @param rsrc NtExecutableResource instance.
33 | * @param iconData Icon data.
34 | */
35 | export function rsrcPatchIcon(
36 | rsrc: NtExecutableResource,
37 | iconData: Readonly
38 | ) {
39 | const ico = Data.IconFile.from(
40 | iconData.buffer.slice(iconData.byteOffset, iconData.byteLength)
41 | );
42 | for (const iconGroup of Resource.IconGroupEntry.fromEntries(rsrc.entries)) {
43 | Resource.IconGroupEntry.replaceIconsForResource(
44 | rsrc.entries,
45 | iconGroup.id,
46 | iconGroup.lang,
47 | ico.icons.map(icon => icon.data)
48 | );
49 | }
50 | }
51 |
52 | /**
53 | * Update strings if present for all the languages.
54 | *
55 | * @param rsrc NtExecutableResource instance.
56 | * @param versionStrings Version strings.
57 | */
58 | export function rsrcPatchVersion(
59 | rsrc: NtExecutableResource,
60 | versionStrings: Readonly<{[key: string]: string}>
61 | ) {
62 | for (const versionInfo of Resource.VersionInfo.fromEntries(rsrc.entries)) {
63 | // Get all the languages, not just available languages.
64 | const languages = versionInfo.getAllLanguagesForStringValues();
65 | for (const language of languages) {
66 | versionInfo.setStringValues(language, versionStrings);
67 | }
68 |
69 | // Update integer values from parsed strings if possible.
70 | const {FileVersion, ProductVersion} = versionStrings;
71 | if (FileVersion) {
72 | const uints = peVersionInts(FileVersion);
73 | if (uints) {
74 | const [ms, ls] = uints;
75 | versionInfo.fixedInfo.fileVersionMS = ms;
76 | versionInfo.fixedInfo.fileVersionLS = ls;
77 | }
78 | }
79 | if (ProductVersion) {
80 | const uints = peVersionInts(ProductVersion);
81 | if (uints) {
82 | const [ms, ls] = uints;
83 | versionInfo.fixedInfo.productVersionMS = ms;
84 | versionInfo.fixedInfo.productVersionLS = ls;
85 | }
86 | }
87 |
88 | versionInfo.outputToResourceEntries(rsrc.entries);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/util/mac.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {deepStrictEqual, strictEqual} from 'node:assert';
3 | import {createHash} from 'node:crypto';
4 |
5 | import {machoAppLauncher, machoTypesData} from './mac.ts';
6 |
7 | const unhex = (hex: string) =>
8 | new Uint8Array(
9 | [...(hex.replace(/\s/g, '').match(/../g) || [])].map(h =>
10 | // eslint-disable-next-line unicorn/prefer-number-properties
11 | parseInt(h, 16)
12 | )
13 | );
14 |
15 | function sha256(data: Uint8Array) {
16 | return createHash('sha256').update(data).digest('hex');
17 | }
18 |
19 | const machoTypes = [
20 | {
21 | name: 'slim: ppc',
22 | data: unhex('FE ED FA CE 00 00 00 12 00 00 00 0A'),
23 | format: {
24 | cpuType: 0x00000012,
25 | cpuSubtype: 10
26 | },
27 | launcher:
28 | '17414c123fe82ac74a89fad9c80e36d8b612ded5a520e35f3c33eabe75a023a7'
29 | },
30 | {
31 | name: 'slim: ppc64',
32 | data: unhex('FE ED FA CF 01 00 00 12 80 00 00 00'),
33 | format: {
34 | cpuType: 0x01000012,
35 | cpuSubtype: 0x80000000
36 | },
37 | launcher:
38 | '9e159161fc21b72de6fddb5fb9c60c0e34e649e4660248778219e58198adfb3d'
39 | },
40 | {
41 | name: 'slim: i386',
42 | data: unhex('CE FA ED FE 07 00 00 00 03 00 00 00'),
43 | format: {
44 | cpuType: 0x00000007,
45 | cpuSubtype: 3
46 | },
47 | launcher:
48 | 'e52e19fce336130824dcfd4731bf85db7e8e96628ef8c6a49769dc5247ef6ed0'
49 | },
50 | {
51 | name: 'slim: x86_64',
52 | data: unhex('CF FA ED FE 07 00 00 01 03 00 00 80'),
53 | format: {
54 | cpuType: 0x01000007,
55 | cpuSubtype: 0x80000003
56 | },
57 | launcher:
58 | 'f5b7625da819324f442cea1f3af83ea4b2bf0af1d185a7747d81b698a6168562'
59 | },
60 | {
61 | name: 'fat: ppc, ppc64, i386, x86_64',
62 | data: unhex(
63 | [
64 | 'CA FE BA BE 00 00 00 04',
65 | '00 00 00 12 00 00 00 0A 00 00 00 00 00 00 00 00 00 00 00 00',
66 | '01 00 00 12 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00',
67 | '00 00 00 07 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00',
68 | '01 00 00 07 80 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00'
69 | ].join('')
70 | ),
71 | format: [
72 | {
73 | cpuType: 0x00000012,
74 | cpuSubtype: 10
75 | },
76 | {
77 | cpuType: 0x01000012,
78 | cpuSubtype: 0x80000000
79 | },
80 | {
81 | cpuType: 0x00000007,
82 | cpuSubtype: 3
83 | },
84 | {
85 | cpuType: 0x01000007,
86 | cpuSubtype: 0x80000003
87 | }
88 | ],
89 | launcher:
90 | '4646bb12e944d4cc2e1b2649b5b33112237a69e01d1aa30d64994135b7969b1d'
91 | }
92 | ];
93 |
94 | void describe('util/mac', () => {
95 | void describe('machoTypesData', () => {
96 | for (const {name, data, format} of machoTypes) {
97 | void it(name, () => {
98 | deepStrictEqual(machoTypesData(data), format);
99 | });
100 | }
101 | });
102 |
103 | void describe('machoAppLauncher', () => {
104 | for (const {name, format, launcher} of machoTypes) {
105 | void it(name, async () => {
106 | const data = await machoAppLauncher(format);
107 | deepStrictEqual(machoTypesData(data), format);
108 | strictEqual(sha256(data), launcher);
109 | });
110 | }
111 | });
112 | });
113 |
--------------------------------------------------------------------------------
/src/util/windows.test.ts:
--------------------------------------------------------------------------------
1 | import {describe, it} from 'node:test';
2 | import {strictEqual} from 'node:assert';
3 | import {createHash} from 'node:crypto';
4 |
5 | import {windowsLauncher} from './windows.ts';
6 |
7 | function sha256(data: Uint8Array) {
8 | return createHash('sha256').update(data).digest('hex');
9 | }
10 |
11 | const launcherTypes: ['i686' | 'x86_64', string][] = [
12 | [
13 | 'i686',
14 | '166e5cb9228842e98e59d0cae1578fd0d97c9754944dae6533678716f7fd1c1c'
15 | ],
16 | [
17 | 'x86_64',
18 | '6a8e15452b1049ed9727eee65e1f8c81a6ff496f7e452c75268e2c3193dd61b1'
19 | ]
20 | ];
21 |
22 | void describe('util/windows', () => {
23 | void describe('windowsLauncher', () => {
24 | for (const [type, hash] of launcherTypes) {
25 | void it(type, async () => {
26 | const data = await windowsLauncher(type);
27 | strictEqual(sha256(data), hash);
28 | });
29 | }
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/tsconfig.eslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [
4 | "**/*.js",
5 | "**/*.jsx",
6 | "**/*.mjs",
7 | "**/*.mjsx",
8 | "**/*.ts",
9 | "**/*.tsx"
10 | ],
11 | "compilerOptions": {
12 | "allowJs": true,
13 | "checkJs": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "target": "ESNext",
5 | "module": "commonjs",
6 | "moduleResolution": "Node",
7 | "allowSyntheticDefaultImports": true,
8 | "allowImportingTsExtensions": true,
9 | "lib": ["ESNext"],
10 | "newLine": "lf",
11 |
12 | "alwaysStrict": true,
13 | "emitDecoratorMetadata": true,
14 | "experimentalDecorators": true,
15 | "forceConsistentCasingInFileNames": true,
16 |
17 | "strictFunctionTypes": true,
18 | "strictNullChecks": true,
19 | "strictPropertyInitialization": true,
20 | "strictBindCallApply": true,
21 |
22 | "noImplicitAny": true,
23 | "noImplicitReturns": true,
24 | "noImplicitThis": true,
25 |
26 | "declaration": true,
27 | "emitDeclarationOnly": true,
28 |
29 | "types": ["node"],
30 | "outDir": "dts"
31 | },
32 | "include": ["src/**/*.ts", "src/**/*.tsx"]
33 | }
34 |
--------------------------------------------------------------------------------