Skip to content

Commit 6a6eb87

Browse files
committed
full support for MapMacros.asm
1 parent 3c956bd commit 6a6eb87

File tree

11 files changed

+213
-160
lines changed

11 files changed

+213
-160
lines changed

CHANGELOG

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# Changelog
22

33
## [unreleased]
4-
- Change DPLC limit to 255
4+
- Assembler now uses AS Macro Assembler for full macro support
5+
- MapMacros.asm definitions added
56
- Make (de)compression threaded
7+
- Change DPLC limit to 255
68
- Fix issue where image data would be cached incorrectly
7-
- Electron updated to v26
89

910
## [1.2.2]
1011
- Added sprite rotation algorithm: 3 shears

TODO

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,22 @@
1212

1313

1414
macros for mappings
15+
specify mapmacros in definition
1516
format hooks for defining asm output
1617
define macro files in script??
18+
fix prelude
19+
perf
1720

18-
return rotsprite
21+
redo tile rendering
22+
remove rotsprite
1923

2024
ROADMAP
21-
look at issues related to crashes
2225
drawing mode bugs
2326
disable mapping editing
2427
improve undo/redo when drawing pixels / moving things
2528
animation editor
2629
export to gif
2730

28-
29-
3031
==
3132
BUGS
3233

app/components/file/file-object.js

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
import React, { useState, useRef } from 'react';
22
import { observer } from 'mobx-react';
33
import { Item, Input, File as FileInput, Select, Checkbox, Button } from '#ui';
4-
import {
5-
scripts,
6-
runScript,
7-
parseASM as parseASMInternal,
8-
writeBIN,
9-
writeASM,
10-
} from '#formats/scripts';
4+
import { scripts, runScript, writeBIN, writeASM, parseASMBasic } from '#formats/scripts';
5+
import { assemble } from '#formats/asm';
116

127
import { decompress, compress, compressionFormats } from '#formats/compression';
138
import { bufferToTiles, tilesToBuffer } from '#formats/art';
@@ -17,28 +12,26 @@ import { workspace } from '#store/workspace';
1712
import ErrorMsg from './error';
1813
import SaveLoad from './save-load';
1914
import { promises } from 'fs';
20-
import { extname } from 'path';
15+
import { extname, basename } from 'path';
2116
import { uuid } from '#util/uuid';
2217

2318
const fs = promises;
2419
const compressionList = Object.keys(compressionFormats);
2520

21+
const isASM = (path) => ['.asm', '.s'].includes(extname(path));
22+
2623
export const FileObject = observer(({ obj }) => {
2724
scripts.length; // react to script updates
2825
const script = obj.format && runScript(obj.format);
2926
const scriptSafe = script && !script.error;
3027

3128
const { isAbsolute } = obj; // set in store/workspace
3229

33-
const mappingsASM = extname(obj.mappings.path) === '.asm';
34-
const dplcsASM = extname(obj.dplcs.path) === '.asm';
30+
const mappingsASM = isASM(obj.mappings.path);
31+
const dplcsASM = isASM(obj.dplcs.path);
3532
const linesLeft = obj.palettes.reduce((a, c) => a - c.length, 4);
3633

37-
const scriptDPLCs = scriptSafe && script.DPLCs;
38-
const scriptArt = scriptSafe && script.art;
39-
const scriptPalettes = scriptSafe && script.palettes;
4034
const toggleDPLCs = () => (obj.dplcs.enabled = !obj.dplcs.enabled);
41-
const parseASM = (scriptSafe && script.parseASM) || parseASMInternal;
4235

4336
function ioWrap(filePath, setError, e, cb) {
4437
setError();
@@ -59,9 +52,28 @@ export const FileObject = observer(({ obj }) => {
5952
}
6053
}
6154

55+
async function getBuffer(path, isASM) {
56+
if (isASM) {
57+
const contents = await fs.readFile(path, 'utf8');
58+
59+
console.time(path);
60+
if (script.asm.basic) return await parseASMBasic(contents);
61+
62+
const buffer = await assemble(script.asm.prelude + contents, {
63+
filename: basename(path),
64+
});
65+
console.timeEnd(path);
66+
67+
return buffer;
68+
}
69+
70+
return await fs.readFile(path);
71+
}
72+
6273
const loadRef = useRef();
6374

6475
function loadObject() {
76+
loadRef.current.childNodes.forEach(n => { n.textContent = ''; });
6577
loadArt({ target: loadRef.current.childNodes[0] });
6678
loadMappings({ target: loadRef.current.childNodes[1] });
6779
if (obj.dplcs.enabled) {
@@ -71,6 +83,7 @@ export const FileObject = observer(({ obj }) => {
7183
}
7284

7385
function saveObject() {
86+
loadRef.current.childNodes.forEach(n => { n.textContent = ''; });
7487
saveArt({ target: loadRef.current.childNodes[0] });
7588
saveMappings({ target: loadRef.current.childNodes[1] });
7689
if (obj.dplcs.enabled) {
@@ -85,7 +98,7 @@ export const FileObject = observer(({ obj }) => {
8598
ioWrap(obj.art.path, setArtError, e, async (path) => {
8699
const buffer = (await fs.readFile(path)).slice(obj.art.offset || 0);
87100

88-
if (scriptArt) {
101+
if (script.art) {
89102
environment.tiles.replace(script.readArt(buffer));
90103
} else {
91104
const decompBuffer = await decompress(
@@ -102,12 +115,12 @@ export const FileObject = observer(({ obj }) => {
102115
if (obj.art.offset) {
103116
throw new Error('Can only save art at offset 0');
104117
}
105-
const tiles = scriptArt
118+
const tiles = script.art
106119
? script.writeArt(tiles)
107120
: tilesToBuffer(environment.tiles, obj.art.compression);
108121
await fs.writeFile(path, tiles);
109122

110-
if (scriptArt) {
123+
if (script.art) {
111124
await fs.writeFile(path, script.writeArt(tiles));
112125
} else {
113126
const buffer = tilesToBuffer(environment.tiles);
@@ -124,10 +137,7 @@ export const FileObject = observer(({ obj }) => {
124137
function loadMappings(e) {
125138
ioWrap(obj.mappings.path, setMappingError, e, async (path) => {
126139
if (!obj.dplcs.enabled) environment.config.dplcsEnabled = false;
127-
128-
const buffer = mappingsASM
129-
? parseASM(await fs.readFile(path, 'utf8'))
130-
: await fs.readFile(path);
140+
const buffer = await getBuffer(path, mappingsASM);
131141

132142
const mappings = script.readMappings(buffer);
133143
if (mappings.error) throw mappings.error;
@@ -168,9 +178,7 @@ export const FileObject = observer(({ obj }) => {
168178
function loadDPLCs(e) {
169179
ioWrap(obj.dplcs.path, setDPLCError, e, async (path) => {
170180
environment.config.dplcsEnabled = true;
171-
const buffer = dplcsASM
172-
? parseASM(await fs.readFile(path, 'utf8'))
173-
: await fs.readFile(path);
181+
const buffer = await getBuffer(path, dplcsASM);
174182

175183
const dplcs = script.readDPLCs(buffer);
176184
if (dplcs.error) throw dplcs.error;
@@ -206,7 +214,7 @@ export const FileObject = observer(({ obj }) => {
206214
? palPath
207215
: workspace.absolutePath(palPath);
208216

209-
(scriptPalettes ? script.readPalettes : buffersToColors)({
217+
(script.palettes ? script.readPalettes : buffersToColors)({
210218
buffer: await fs.readFile(path),
211219
length,
212220
}).forEach((line) => {
@@ -233,7 +241,7 @@ export const FileObject = observer(({ obj }) => {
233241
: workspace.absolutePath(palPath);
234242

235243
const chunk = (
236-
scriptPalettes ? script.writePalettes : colorsToBuffers
244+
script.palettes ? script.writePalettes : colorsToBuffers
237245
)(environment.palettes, cursor, cursor + length);
238246
await fs.writeFile(path, chunk);
239247
cursor += length;
@@ -263,7 +271,7 @@ export const FileObject = observer(({ obj }) => {
263271
<Item color="green">Art</Item>
264272
<SaveLoad load={loadArt} save={saveArt} />
265273
</div>
266-
{!scriptArt && (
274+
{!script.art && (
267275
<>
268276
<div className="menu-item">
269277
<Item>Compression</Item>
@@ -305,7 +313,7 @@ export const FileObject = observer(({ obj }) => {
305313
</div>
306314
)}
307315

308-
{scriptDPLCs && (
316+
{script.PLCs && (
309317
<>
310318
<div className="menu-item" onClick={toggleDPLCs}>
311319
<Item>DPLCs Enabled</Item>

app/formats/asm/index.js

Lines changed: 1 addition & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export async function assemble(
2828
const aslWorker = new Worker('bundles/asl-worker.js');
2929
const asl = Comlink.wrap(aslWorker);
3030

31-
const pFile = await asl.assemble(prelude + code, {
31+
const pFile = await asl.assemble(code, {
3232
messages: asMessages,
3333
filename,
3434
});
@@ -43,98 +43,3 @@ export async function assemble(
4343

4444
return bin;
4545
}
46-
47-
const prelude = `SonicMappingsVer := 2
48-
SonicDplcVer = 2
49-
; macro to declare a mappings table (taken from Sonic 2 Hg disassembly)
50-
mappingsTable macro {INTLABEL}
51-
__LABEL__ label *
52-
.current_mappings_table := __LABEL__
53-
endm
54-
55-
; macro to declare an entry in a mappings table (taken from Sonic 2 Hg disassembly)
56-
mappingsTableEntry macro ptr
57-
dc.ATTRIBUTE ptr-.current_mappings_table
58-
endm
59-
60-
spriteHeader macro {INTLABEL}
61-
__LABEL__ label *
62-
if SonicMappingsVer=1
63-
dc.b ((__LABEL___End - __LABEL___Begin) / 5)
64-
elseif SonicMappingsVer=2
65-
dc.w ((__LABEL___End - __LABEL___Begin) / 8)
66-
else
67-
dc.w ((__LABEL___End - __LABEL___Begin) / 6)
68-
endif
69-
__LABEL___Begin label *
70-
endm
71-
72-
spritePiece macro xpos,ypos,width,height,tile,xflip,yflip,pal,pri
73-
if SonicMappingsVer=1
74-
dc.b ypos
75-
dc.b (((width-1)&3)<<2)|((height-1)&3)
76-
dc.b ((pri&1)<<7)|((pal&3)<<5)|((yflip&1)<<4)|((xflip&1)<<3)|((tile&$700)>>8)
77-
dc.b tile&$FF
78-
dc.b xpos
79-
elseif SonicMappingsVer=2
80-
dc.w ((ypos&$FF)<<8)|(((width-1)&3)<<2)|((height-1)&3)
81-
dc.w ((pri&1)<<15)|((pal&3)<<13)|((yflip&1)<<12)|((xflip&1)<<11)|(tile&$7FF)
82-
dc.w ((pri&1)<<15)|((pal&3)<<13)|((yflip&1)<<12)|((xflip&1)<<11)|((tile>>1)&$7FF)
83-
dc.w xpos
84-
else
85-
dc.w ((ypos&$FF)<<8)|(((width-1)&3)<<2)|((height-1)&3)
86-
dc.w ((pri&1)<<15)|((pal&3)<<13)|((yflip&1)<<12)|((xflip&1)<<11)|(tile&$7FF)
87-
dc.w xpos
88-
endif
89-
endm
90-
91-
spritePiece2P macro xpos,ypos,width,height,tile,xflip,yflip,pal,pri,tile2,xflip2,yflip2,pal2,pri2
92-
if SonicMappingsVer=1
93-
dc.b ypos
94-
dc.b (((width-1)&3)<<2)|((height-1)&3)
95-
dc.b ((pri&1)<<7)|((pal&3)<<5)|((yflip&1)<<4)|((xflip&1)<<3)|((tile&$700)>>8)
96-
dc.b tile&$FF
97-
dc.b xpos
98-
elseif SonicMappingsVer=2
99-
dc.w ((ypos&$FF)<<8)|(((width-1)&3)<<2)|((height-1)&3)
100-
dc.w ((pri&1)<<15)|((pal&3)<<13)|((yflip&1)<<12)|((xflip&1)<<11)|(tile&$7FF)
101-
dc.w ((pri2&1)<<15)|((pal2&3)<<13)|((yflip2&1)<<12)|((xflip2&1)<<11)|(tile2&$7FF)
102-
dc.w xpos
103-
else
104-
dc.w ((ypos&$FF)<<8)|(((width-1)&3)<<2)|((height-1)&3)
105-
dc.w ((pri&1)<<15)|((pal&3)<<13)|((yflip&1)<<12)|((xflip&1)<<11)|(tile&$7FF)
106-
dc.w xpos
107-
endif
108-
endm
109-
110-
dplcHeader macro {INTLABEL}
111-
__LABEL__ label *
112-
if SonicDplcVer=1
113-
dc.b ((__LABEL___End - __LABEL___Begin) / 2)
114-
elseif SonicDplcVer=3
115-
dc.w (((__LABEL___End - __LABEL___Begin) / 2)-1)
116-
else
117-
dc.w ((__LABEL___End - __LABEL___Begin) / 2)
118-
endif
119-
__LABEL___Begin label *
120-
endm
121-
122-
dplcEntry macro tiles,offset
123-
if SonicDplcVer=3
124-
dc.w ((offset&$FFF)<<4)|((tiles-1)&$F)
125-
elseif SonicDplcVer=4
126-
dc.w (((tiles-1)&$F)<<12)|((offset&$FFF)<<4)
127-
else
128-
dc.w (((tiles-1)&$F)<<12)|(offset&$FFF)
129-
endif
130-
endm
131-
132-
even macro
133-
if (*)&1
134-
paddingSoFar set paddingSoFar+1
135-
dc.b 0 ;ds.b 1
136-
endif
137-
endm
138-
139-
cpu 68000
140-
`;

app/formats/scripts/file.js

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,40 @@ import { errorMsg } from '#util/dialog';
44
import { uniq, debounce } from 'lodash';
55
import { observable } from 'mobx';
66

7-
const scriptPaths = uniq([
8-
dirname(process.execPath),
9-
process.cwd(),
10-
]).map(path => join(path, 'scripts'));
7+
const scriptPaths = uniq([dirname(process.execPath), process.cwd()]).map(
8+
(path) => join(path, 'scripts'),
9+
);
1110

12-
const scriptDir = scriptPaths.find(path => (
13-
fs.existsSync(path) && fs.lstatSync(path).isDirectory()
14-
));
11+
const scriptDir = scriptPaths.find(
12+
(path) => fs.existsSync(path) && fs.lstatSync(path).isDirectory(),
13+
);
1514

1615
const scripts = observable([]);
1716

1817
const { readdir } = fs.promises;
1918

2019
async function loadScripts() {
2120
try {
22-
const list = await readdir(scriptDir);
23-
scripts.replace(list.map(filename => ({
24-
value: filename,
25-
label: parse(filename).name,
26-
})));
21+
const list = (await readdir(scriptDir)).filter((filename) =>
22+
filename.endsWith('.js'),
23+
);
24+
scripts.replace(
25+
list.map((filename) => ({
26+
value: filename,
27+
label: parse(filename).name,
28+
})),
29+
);
2730
} catch (e) {
2831
console.error(e);
2932
}
3033
}
3134

3235
if (!scriptDir) {
33-
errorMsg(`Script Error`, `Could not find 'scripts' directory for mapping definition files
34-
Searched in;\n\t${scriptPaths.join('\n\t')}`);
36+
errorMsg(
37+
`Script Error`,
38+
`Could not find 'scripts' directory for mapping definition files
39+
Searched in;\n\t${scriptPaths.join('\n\t')}`,
40+
);
3541
} else {
3642
loadScripts();
3743
fs.watch(scriptDir, debounce(loadScripts, 100));
@@ -41,7 +47,4 @@ function loadScript(file) {
4147
return fs.readFileSync(join(scriptDir, file), 'utf8');
4248
}
4349

44-
export {
45-
scripts,
46-
loadScript,
47-
};
50+
export { scripts, loadScript, scriptDir };

app/formats/scripts/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export { scripts } from './file';
2-
export { parseASM } from './parse-asm';
2+
export { parseASMBasic } from './parse-asm';
33
export { writeBIN } from './write-bin';
44
export { writeASM } from './write-asm';
55
export { default as runScript } from './run-script';

app/formats/scripts/parse-asm.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ const sizes = {
2020
l: 4,
2121
};
2222

23-
export function parseASM(text) {
23+
// use with asm(({ basic }) => basic())
24+
export function parseASMBasic(text) {
2425
const comment = regex(/^;.*$/m).map(() => [ignore]);
2526
const even = str('even').map(() => [ignore]);
2627
const label = regex(/^[A-Z0-9_@$.]+(\s+)?:/i).map(t => [lbl, t.replace(':', '')]);

0 commit comments

Comments
 (0)