Initial commit

This commit is contained in:
Rypi Development 2023-07-31 16:32:33 +00:00
commit e71a1939ba
14 changed files with 4859 additions and 0 deletions

10
.editorconfig Executable file
View File

@ -0,0 +1,10 @@
# editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

20
.github/ISSUE_TEMPLATE/BUG.md vendored Normal file
View File

@ -0,0 +1,20 @@
---
name: '🐛 Bug Report'
about: 'Report an unexpected problem or unintended behavior.'
title: '[Bug]'
labels: 'bug'
---
<!--
Please provide a clear and concise description of what the bug is. Include
screenshots if needed. Please make sure your issue has not already been fixed.
-->
## Steps To Reproduce
1. Step 1
2. Step 2
## The current behavior
## The expected behavior

20
.github/ISSUE_TEMPLATE/IMPROVEMENT.md vendored Normal file
View File

@ -0,0 +1,20 @@
---
name: '🔧 Improvement'
about: 'Improve structure/format/performance/refactor/tests of the code.'
title: '[Improvement]'
labels: 'improvement'
---
<!-- Please make sure your issue has not already been fixed. -->
## Type of Improvement
<!-- Please uncomment the type of improvements this issue address -->
<!-- Files and Folders Structure -->
<!-- Performance -->
<!-- Refactoring code -->
<!-- Tests -->
<!-- Not Sure? -->
## Proposal

29
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: 'Linting code'
on:
push:
branches: [develop]
pull_request:
branches: [develop]
jobs:
linting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.0.0
- uses: actions/setup-node@v3.0.0
with:
node-version: 18.x
- name: Install dependencies
run: yarn --frozen-lockfile
- name: Linting prettier
run: yarn lint:prettier
- name: Linting typescript
run: yarn lint:typescript
- name: Checking editorconfig
run: yarn lint:editorconfig

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.log
.DS_Store
node_modules
dist

10
README.md Normal file
View File

@ -0,0 +1,10 @@
# Good to know
## 🌌 Magic numbers (5.2 PNG Signature)
Identifying file formats in binary can be a bit tricky. In fact, they are magic numbers when reading the first bytes:
- `PNG`: 0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A
- `GIF`: 0x47 0x49 0x46 0x38 0x39 0x61
- `JPEG`: 0xFF 0xD8
- `ERRONEOUS_JPEG`: 0xff, 0xd9, 0xff, 0xd8, 0xff, 0xd8

38
package.json Normal file
View File

@ -0,0 +1,38 @@
{
"name": "@rypidev/shockwave-bundler",
"version": "0.0.0",
"type": "module",
"license": "MIT",
"author": "Walidoux",
"scripts": {
"dev": "ts-node-dev --respawn src/index.ts",
"build": "rm -rf dist && tsc -p tsconfig.json",
"start": "yarn build && ts-node dist/index.js",
"lint:typescript": "eslint \"**/*.ts\" --ignore-path \".gitignore\" && tsc --noemit",
"lint:prettier": "prettier \".\" --check --ignore-path \".gitignore\"",
"lint:editorconfig": "editorconfig-checker"
},
"prettier": "@walidoux/prettier-config",
"dependencies": {
"swf-parser": "0.14.1",
"swf-types": "0.14.0",
"ts-node": "10.9.1",
"ts-node-dev": "1.1.8",
"free-tex-packer-core": "^0.3.4"
},
"devDependencies": {
"@tsconfig/recommended": "^1.0.2",
"@types/node": "^18.15.11",
"@walidoux/eslint-config": "1.0.3",
"@walidoux/prettier-config": "1.0.3",
"editorconfig-checker": "^5.0.1",
"eslint": "8.38.0",
"prettier": "2.8.7",
"typescript": "^5.0.4"
},
"eslintConfig": {
"extends": [
"@walidoux/eslint-config"
]
}
}

305
src/Gist.ts Normal file
View File

@ -0,0 +1,305 @@
import { readFile } from "fs";
import { tags, Tag } from "swf-types";
import streamToArray from "stream-to-array";
import encoder from "png-stream/encoder";
import { unzip } from "zlib";
import { promisify } from "util";
export class SWFBuffer {
public static RECORDHEADER_LENTH_FULL: number = 0x3f;
public static EOS: number = 0x00;
public static STYLE_COUNT_EXT: number = 0xff;
public buffer: Buffer = null;
public pointer: number = 0;
public position: number = 1;
public current: number = 0;
public length: number = 0;
constructor(buffer: Buffer) {
if (!Buffer.isBuffer(buffer)) throw new Error("invalid_buffer");
this.buffer = buffer;
this.length = buffer.length;
}
public readUIntLE(bits: number): number {
let value = 0;
switch (bits) {
case 16:
value = this.buffer.readUInt16LE(this.pointer);
break;
case 32:
value = this.buffer.readUInt32LE(this.pointer);
break;
}
this.pointer += bits / 8;
return value;
}
public readUInt8(): number {
return this.buffer.readUInt8(this.pointer++);
}
public readEncodedU32(): number {
let i = 5;
let result = 0;
let nb = 0;
do {
result += nb = this.nextByte();
} while (nb & 128 && --i);
return result;
}
public readRGB(): [number, number, number] {
return [this.readUInt8(), this.readUInt8(), this.readUInt8()];
}
public readRGBA(): [number, number, number, number] {
return [...this.readRGB(), this.readUInt8()];
}
public readString(encoding: BufferEncoding = "utf8"): string {
const init = this.pointer;
while (this.readUInt8() !== SWFBuffer.EOS);
return this.buffer.toString(encoding || "utf8", init, this.pointer - 1);
}
public readStyleArray(buffer: SWFBuffer, next) {
let styleArrayCount = buffer.readUInt8();
const styles = [];
if (styleArrayCount === SWFBuffer.STYLE_COUNT_EXT)
styleArrayCount = buffer.readUIntLE(16);
for (let i = 0; i < styleArrayCount; i++) styles.push(next(buffer));
return styles;
}
public readFillStyle(buffer: SWFBuffer): {
fillStyleType: number;
color?: [number, number, number, number];
bitmapId?: number;
} {
const type = buffer.readUInt8();
const fillStyle: {
fillStyleType: number;
color?: [number, number, number, number];
bitmapId?: number;
} = {
fillStyleType: type,
};
switch (type) {
case 0x00:
fillStyle.color = buffer.readRGBA();
break;
case 0x10:
case 0x12:
case 0x13:
console.log("Gradient");
break;
case 0x40:
case 0x41:
case 0x42:
case 0x43:
fillStyle.bitmapId = buffer.readUIntLE(16);
break;
}
return fillStyle;
}
public readLineStyle(buffer: SWFBuffer): {
width: number;
color: [number, number, number, number];
} {
return {
width: buffer.readUIntLE(16) / 20,
color: buffer.readRGBA(),
};
}
public readShapeRecords(buffer: SWFBuffer) {
let shapeRecords = null;
const typeFlag = buffer.readBits(1);
let eos = 0;
while ((eos = buffer.readBits(5))) {
if (0 === typeFlag) {
shapeRecords = {
type: "STYLECHANGERECORD",
};
}
}
return shapeRecords;
}
public readShapeWithStyle() {
return {
fillStyles: this.readStyleArray(this, this.readFillStyle),
lineStyles: this.readStyleArray(this, this.readLineStyle),
numFillBits: this.readBits(4),
numLineBits: this.readBits(4),
shapeRecords: this.readShapeRecords(this),
};
}
public readTagCodeAndLength(): { code: number; length: number } {
if (this.pointer === this.length) return null;
const n = this.readUIntLE(16);
const tagType = n >> 6;
let tagLength = n & SWFBuffer.RECORDHEADER_LENTH_FULL;
if (n === 0) return null;
if (tagLength === SWFBuffer.RECORDHEADER_LENTH_FULL)
tagLength = this.readUIntLE(32);
return { code: tagType, length: tagLength };
}
public readRect(): { x: number; y: number; width: number; height: number } {
this.start();
const NBits = this.readBits(5);
const Xmin = this.readBits(NBits, true) / 20;
const Xmax = this.readBits(NBits, true) / 20;
const Ymin = this.readBits(NBits, true) / 20;
const Ymax = this.readBits(NBits, true) / 20;
return {
x: Xmin,
y: Ymin,
width: Xmax > Xmin ? Xmax - Xmin : Xmin - Xmax,
height: Ymax > Ymin ? Ymax - Ymin : Ymin - Ymax,
};
}
public seek(pos: number): void {
this.pointer = pos % this.buffer.length;
}
public start(): void {
this.current = this.nextByte();
this.position = 1;
}
public nextByte(): number {
return this.pointer > this.buffer.length
? null
: this.buffer[this.pointer++];
}
public readBits(b: number, signed: boolean = false): number {
let n = 0;
let r = 0;
const sign =
signed && ++n && (this.current >> (8 - this.position++)) & 1 ? -1 : 1;
while (n++ < b) {
if (this.position > 8) this.start();
r = (r << 1) + ((this.current >> (8 - this.position++)) & 1);
}
return sign * r;
}
}
export const ReadImage = async (bitmap: tags.DefineBitmap) => {
const buffer = new SWFBuffer(Buffer.from(bitmap.data));
// const bitmapFormat = buffer[0];
const bitmapFormat = buffer.readUInt8();
const bitmapWidth = buffer.readUIntLE(16);
const bitmapHeight = buffer.readUIntLE(16);
console.log(bitmapFormat);
console.log(bitmapWidth);
console.log(bitmapHeight);
const zlibBitmapData = buffer.buffer.slice(buffer.pointer);
const dataBuf = await promisify(unzip)(zlibBitmapData);
const pngEncoder = new encoder(bitmap.width, bitmap.height, {
colorSpace: "rgba",
});
if (bitmapFormat !== 5) {
// you'll have to figure this out yourself.
throw Error("Unsupported bitmap format");
}
let index = 0;
let ptr = 0;
const output = Buffer.alloc(bitmap.width * bitmap.height * 4);
for (let x = 0; x < bitmap.height; x++) {
for (let y = 0; y < bitmap.width; y++) {
const alpha = dataBuf[ptr];
output[index] = dataBuf[ptr + 1] * (255 / alpha);
output[index + 1] = dataBuf[ptr + 2] * (255 / alpha);
output[index + 2] = dataBuf[ptr + 3] * (255 / alpha);
output[index + 3] = alpha;
index += 4;
ptr += 4;
}
}
pngEncoder.end(output);
const parts = await streamToArray(pngEncoder);
const buffers = parts.map((part) =>
Buffer.isBuffer(part) ? part : Buffer.from(part)
);
const base64String = Buffer.concat(buffers).toString("base64");
const imgSrc = `data:image/png;base64,${base64String}`;
console.log(imgSrc);
};
readFile(process.cwd() + "/swfs/bunny.swf", async (_, data) => {
// Define type compression from reading the three first bytes of the file
// defineTypeCompression(data)
const SWF = (await import("swf-parser")).parseSwf(data);
const images = SWF.tags.filter((tag) => {
const mediaType = (tag as tags.DefineBitmap).mediaType;
if (String(mediaType).startsWith("image")) {
return true;
}
}) as Extract<Tag, tags.DefineBitmap>[];
const className = (
SWF.tags.filter(
(tag) => (tag as tags.PlaceObject).name
)[0] as tags.PlaceObject
).name;
const current = images.map(async (img) => ({
path: className as string,
// Convertion from Uint8Array to Buffer: Buffer.from(img.data)
// we need to find a way for reading this buffer, can it be read in base64 ?
contents: await ReadImage(img),
}));
// unhandled error: "free-tex-packer-core: Error reading, mine must be a string" ?
// const packerAtlas = await packAsync(current)
});

45
src/index.ts Normal file
View File

@ -0,0 +1,45 @@
import { readFile } from 'node:fs'
import type { tags } from 'swf-types'
const defineTypeCompression = (data: Buffer): string => {
// Define type compression from reading the three first bytes of the file
// defineTypeCompression(data)
return Array.from({ length: 3 })
.map((_, index) => {
return String.fromCharCode(data[index])
})
.join('')
}
readFile(process.cwd() + '/swfs/bunny.swf', async (error, data) => {
const typeCompression = defineTypeCompression(data)
if (error != null) throw new Error(String(error))
if (typeCompression !== 'CWS') throw new Error(`New Type Compression detected: ${typeCompression}`)
const SWF = (await import('swf-parser')).parseSwf(data)
const images = SWF.tags.filter((tag) => {
const mediaType = (tag as tags.DefineBitmap).mediaType
return String(mediaType).startsWith('image')
}) as tags.DefineBitmap[]
const className = (
SWF.tags.filter((tag) => {
return (tag as tags.PlaceObject).name
})[0] as tags.PlaceObject
).name
const current = images.map((img) => {
return {
path: className,
contents: img.data
}
})
console.log(current)
// const packerAtlas = await packAsync(current)
})

BIN
swfs/Ghost.swf Normal file

Binary file not shown.

BIN
swfs/art_c20_clock.swf Normal file

Binary file not shown.

BIN
swfs/bunny.swf Normal file

Binary file not shown.

28
tsconfig.json Normal file
View File

@ -0,0 +1,28 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Default config",
"compilerOptions": {
"target": "ESNext",
"module": "CommonJS",
"lib": ["ESNext"],
"composite": false,
"declaration": true,
"sourceMap": true,
"useDefineForClassFields": true,
"declarationMap": true,
"esModuleInterop": true,
"noImplicitAny": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "nodenext",
"resolveJsonModule": true,
"removeComments": true,
"preserveWatchOutput": true,
"allowJs": false,
"strict": true,
"noEmitOnError": true,
"importHelpers": true,
"outDir": "dist"
}
}

4350
yarn.lock Normal file

File diff suppressed because it is too large Load Diff