esp32tool 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/css/dark.css +156 -0
- package/css/light.css +156 -0
- package/css/style.css +870 -0
- package/dist/const.d.ts +277 -0
- package/dist/const.js +511 -0
- package/dist/esp_loader.d.ts +222 -0
- package/dist/esp_loader.js +1466 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +15 -0
- package/dist/lib/spiffs/index.d.ts +15 -0
- package/dist/lib/spiffs/index.js +16 -0
- package/dist/lib/spiffs/spiffs.d.ts +26 -0
- package/dist/lib/spiffs/spiffs.js +132 -0
- package/dist/lib/spiffs/spiffsBlock.d.ts +36 -0
- package/dist/lib/spiffs/spiffsBlock.js +140 -0
- package/dist/lib/spiffs/spiffsConfig.d.ts +63 -0
- package/dist/lib/spiffs/spiffsConfig.js +79 -0
- package/dist/lib/spiffs/spiffsPage.d.ts +45 -0
- package/dist/lib/spiffs/spiffsPage.js +260 -0
- package/dist/lib/spiffs/spiffsReader.d.ts +19 -0
- package/dist/lib/spiffs/spiffsReader.js +192 -0
- package/dist/partition.d.ts +26 -0
- package/dist/partition.js +129 -0
- package/dist/struct.d.ts +2 -0
- package/dist/struct.js +91 -0
- package/dist/stubs/esp32.json +8 -0
- package/dist/stubs/esp32c2.json +8 -0
- package/dist/stubs/esp32c3.json +8 -0
- package/dist/stubs/esp32c5.json +8 -0
- package/dist/stubs/esp32c6.json +8 -0
- package/dist/stubs/esp32c61.json +8 -0
- package/dist/stubs/esp32h2.json +8 -0
- package/dist/stubs/esp32p4.json +8 -0
- package/dist/stubs/esp32p4r3.json +8 -0
- package/dist/stubs/esp32s2.json +8 -0
- package/dist/stubs/esp32s3.json +8 -0
- package/dist/stubs/esp8266.json +8 -0
- package/dist/stubs/index.d.ts +10 -0
- package/dist/stubs/index.js +56 -0
- package/dist/util.d.ts +14 -0
- package/dist/util.js +46 -0
- package/dist/wasm/filesystems.d.ts +33 -0
- package/dist/wasm/filesystems.js +114 -0
- package/dist/web/esp32-D955RjN9.js +16 -0
- package/dist/web/esp32c2-CJkxHDQi.js +16 -0
- package/dist/web/esp32c3-BhUHzH0o.js +16 -0
- package/dist/web/esp32c5-Chs0HtmA.js +16 -0
- package/dist/web/esp32c6-D6mPN6ut.js +16 -0
- package/dist/web/esp32c61-CQiYCWAs.js +16 -0
- package/dist/web/esp32h2-LsKJE9AS.js +16 -0
- package/dist/web/esp32p4-7nWC-HiD.js +16 -0
- package/dist/web/esp32p4r3-CwiPecZW.js +16 -0
- package/dist/web/esp32s2-CtqVheSJ.js +16 -0
- package/dist/web/esp32s3-CRbtB0QR.js +16 -0
- package/dist/web/esp8266-nEkNAo8K.js +16 -0
- package/dist/web/index.js +7265 -0
- package/electron/main.js +333 -0
- package/electron/preload.js +37 -0
- package/eslint.config.js +22 -0
- package/index.html +408 -0
- package/js/modules/esp32-D955RjN9.js +16 -0
- package/js/modules/esp32c2-CJkxHDQi.js +16 -0
- package/js/modules/esp32c3-BhUHzH0o.js +16 -0
- package/js/modules/esp32c5-Chs0HtmA.js +16 -0
- package/js/modules/esp32c6-D6mPN6ut.js +16 -0
- package/js/modules/esp32c61-CQiYCWAs.js +16 -0
- package/js/modules/esp32h2-LsKJE9AS.js +16 -0
- package/js/modules/esp32p4-7nWC-HiD.js +16 -0
- package/js/modules/esp32p4r3-CwiPecZW.js +16 -0
- package/js/modules/esp32s2-CtqVheSJ.js +16 -0
- package/js/modules/esp32s3-CRbtB0QR.js +16 -0
- package/js/modules/esp8266-nEkNAo8K.js +16 -0
- package/js/modules/esptool.js +7265 -0
- package/js/script.js +2237 -0
- package/js/utilities.js +182 -0
- package/license.md +11 -0
- package/package.json +61 -0
- package/script/build +12 -0
- package/script/develop +17 -0
- package/src/const.ts +599 -0
- package/src/esp_loader.ts +1907 -0
- package/src/index.ts +63 -0
- package/src/lib/spiffs/index.ts +22 -0
- package/src/lib/spiffs/spiffs.ts +175 -0
- package/src/lib/spiffs/spiffsBlock.ts +204 -0
- package/src/lib/spiffs/spiffsConfig.ts +140 -0
- package/src/lib/spiffs/spiffsPage.ts +357 -0
- package/src/lib/spiffs/spiffsReader.ts +280 -0
- package/src/partition.ts +155 -0
- package/src/struct.ts +108 -0
- package/src/stubs/README.md +3 -0
- package/src/stubs/esp32.json +8 -0
- package/src/stubs/esp32c2.json +8 -0
- package/src/stubs/esp32c3.json +8 -0
- package/src/stubs/esp32c5.json +8 -0
- package/src/stubs/esp32c6.json +8 -0
- package/src/stubs/esp32c61.json +8 -0
- package/src/stubs/esp32h2.json +8 -0
- package/src/stubs/esp32p4.json +8 -0
- package/src/stubs/esp32p4r3.json +8 -0
- package/src/stubs/esp32s2.json +8 -0
- package/src/stubs/esp32s3.json +8 -0
- package/src/stubs/esp8266.json +8 -0
- package/src/stubs/index.ts +86 -0
- package/src/util.ts +49 -0
- package/src/wasm/fatfs/fatfs.wasm +0 -0
- package/src/wasm/fatfs/index.d.ts +26 -0
- package/src/wasm/fatfs/index.js +343 -0
- package/src/wasm/filesystems.ts +156 -0
- package/src/wasm/littlefs/index.d.ts +83 -0
- package/src/wasm/littlefs/index.js +529 -0
- package/src/wasm/littlefs/littlefs.js +2 -0
- package/src/wasm/littlefs/littlefs.wasm +0 -0
- package/src/wasm/shared/types.ts +13 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/// <reference types="@types/w3c-web-serial" />
|
|
2
|
+
|
|
3
|
+
import { ESP_ROM_BAUD, Logger } from "./const";
|
|
4
|
+
import { ESPLoader } from "./esp_loader";
|
|
5
|
+
|
|
6
|
+
export type { Logger } from "./const";
|
|
7
|
+
export { ESPLoader } from "./esp_loader";
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
CHIP_FAMILY_ESP32,
|
|
11
|
+
CHIP_FAMILY_ESP32S2,
|
|
12
|
+
CHIP_FAMILY_ESP32S3,
|
|
13
|
+
CHIP_FAMILY_ESP8266,
|
|
14
|
+
CHIP_FAMILY_ESP32C2,
|
|
15
|
+
CHIP_FAMILY_ESP32C3,
|
|
16
|
+
CHIP_FAMILY_ESP32C5,
|
|
17
|
+
CHIP_FAMILY_ESP32C6,
|
|
18
|
+
CHIP_FAMILY_ESP32C61,
|
|
19
|
+
CHIP_FAMILY_ESP32H2,
|
|
20
|
+
CHIP_FAMILY_ESP32H4,
|
|
21
|
+
CHIP_FAMILY_ESP32H21,
|
|
22
|
+
CHIP_FAMILY_ESP32P4,
|
|
23
|
+
CHIP_FAMILY_ESP32S31,
|
|
24
|
+
} from "./const";
|
|
25
|
+
|
|
26
|
+
export const connect = async (logger: Logger) => {
|
|
27
|
+
// - Request a port and open a connection.
|
|
28
|
+
const port = await navigator.serial.requestPort();
|
|
29
|
+
|
|
30
|
+
await port.open({ baudRate: ESP_ROM_BAUD });
|
|
31
|
+
|
|
32
|
+
logger.log("Connected successfully.");
|
|
33
|
+
|
|
34
|
+
return new ESPLoader(port, logger);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
parsePartitionTable,
|
|
39
|
+
getPartitionTableOffset,
|
|
40
|
+
formatSize,
|
|
41
|
+
} from "./partition";
|
|
42
|
+
export type { Partition } from "./partition";
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
FilesystemType,
|
|
46
|
+
detectFilesystemType,
|
|
47
|
+
detectFilesystemFromImage,
|
|
48
|
+
getDefaultBlockSize,
|
|
49
|
+
getBlockSizeCandidates,
|
|
50
|
+
LITTLEFS_DEFAULT_BLOCK_SIZE,
|
|
51
|
+
LITTLEFS_BLOCK_SIZE_CANDIDATES,
|
|
52
|
+
FATFS_DEFAULT_BLOCK_SIZE,
|
|
53
|
+
FATFS_BLOCK_SIZE_CANDIDATES,
|
|
54
|
+
} from "./wasm/filesystems";
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
SpiffsFS,
|
|
58
|
+
SpiffsBuildConfig,
|
|
59
|
+
SpiffsReader,
|
|
60
|
+
DEFAULT_SPIFFS_CONFIG,
|
|
61
|
+
type SpiffsFile,
|
|
62
|
+
type SpiffsBuildConfigOptions,
|
|
63
|
+
} from "./lib/spiffs/index";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPIFFS Module Entry Point
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { SpiffsFS, type SpiffsFile } from "./spiffs";
|
|
6
|
+
export {
|
|
7
|
+
SpiffsBuildConfig,
|
|
8
|
+
type SpiffsBuildConfigOptions,
|
|
9
|
+
SpiffsFullError,
|
|
10
|
+
} from "./spiffsConfig";
|
|
11
|
+
export { SpiffsReader } from "./spiffsReader";
|
|
12
|
+
|
|
13
|
+
// Default ESP32 SPIFFS configuration
|
|
14
|
+
export const DEFAULT_SPIFFS_CONFIG = {
|
|
15
|
+
pageSize: 256,
|
|
16
|
+
blockSize: 4096,
|
|
17
|
+
objNameLen: 32,
|
|
18
|
+
metaLen: 4,
|
|
19
|
+
useMagic: true,
|
|
20
|
+
useMagicLen: true,
|
|
21
|
+
alignedObjIxTables: false,
|
|
22
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPIFFS Filesystem Implementation
|
|
3
|
+
* Based on ESP-IDF spiffsgen.py
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { SpiffsBuildConfig, SpiffsFullError } from "./spiffsConfig";
|
|
7
|
+
import { SpiffsBlock } from "./spiffsBlock";
|
|
8
|
+
|
|
9
|
+
export interface SpiffsFile {
|
|
10
|
+
name: string;
|
|
11
|
+
size: number;
|
|
12
|
+
data: Uint8Array;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class SpiffsFS {
|
|
16
|
+
private imgSize: number;
|
|
17
|
+
private buildConfig: SpiffsBuildConfig;
|
|
18
|
+
private blocks: SpiffsBlock[];
|
|
19
|
+
private blocksLim: number;
|
|
20
|
+
private remainingBlocks: number;
|
|
21
|
+
private curObjId: number;
|
|
22
|
+
|
|
23
|
+
constructor(imgSize: number, buildConfig: SpiffsBuildConfig) {
|
|
24
|
+
if (imgSize % buildConfig.blockSize !== 0) {
|
|
25
|
+
throw new Error("image size should be a multiple of block size");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this.imgSize = imgSize;
|
|
29
|
+
this.buildConfig = buildConfig;
|
|
30
|
+
this.blocks = [];
|
|
31
|
+
this.blocksLim = Math.floor(this.imgSize / this.buildConfig.blockSize);
|
|
32
|
+
this.remainingBlocks = this.blocksLim;
|
|
33
|
+
this.curObjId = 1; // starting object id
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private createBlock(): SpiffsBlock {
|
|
37
|
+
if (this.isFull()) {
|
|
38
|
+
throw new SpiffsFullError("the image size has been exceeded");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const block = new SpiffsBlock(this.blocks.length, this.buildConfig);
|
|
42
|
+
this.blocks.push(block);
|
|
43
|
+
this.remainingBlocks--;
|
|
44
|
+
return block;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
isFull(): boolean {
|
|
48
|
+
return this.remainingBlocks <= 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
createFile(imgPath: string, contents: Uint8Array): void {
|
|
52
|
+
if (imgPath.length > this.buildConfig.objNameLen) {
|
|
53
|
+
throw new Error(`object name '${imgPath}' too long`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const name = imgPath;
|
|
57
|
+
let offset = 0;
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const block = this.blocks[this.blocks.length - 1];
|
|
61
|
+
block.beginObj(this.curObjId, contents.length, name);
|
|
62
|
+
} catch {
|
|
63
|
+
const block = this.createBlock();
|
|
64
|
+
block.beginObj(this.curObjId, contents.length, name);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
while (offset < contents.length) {
|
|
68
|
+
const chunkSize = Math.min(
|
|
69
|
+
this.buildConfig.OBJ_DATA_PAGE_CONTENT_LEN,
|
|
70
|
+
contents.length - offset,
|
|
71
|
+
);
|
|
72
|
+
const contentsChunk = contents.slice(offset, offset + chunkSize);
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const block = this.blocks[this.blocks.length - 1];
|
|
76
|
+
try {
|
|
77
|
+
block.updateObj(contentsChunk);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
if (e instanceof SpiffsFullError) {
|
|
80
|
+
if (block.isFull()) {
|
|
81
|
+
throw e;
|
|
82
|
+
}
|
|
83
|
+
// Object index exhausted, write another object index page
|
|
84
|
+
block.beginObj(
|
|
85
|
+
this.curObjId,
|
|
86
|
+
contents.length,
|
|
87
|
+
name,
|
|
88
|
+
block.currentObjIndexSpanIx,
|
|
89
|
+
block.currentObjDataSpanIx,
|
|
90
|
+
);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
throw e;
|
|
94
|
+
}
|
|
95
|
+
} catch (e) {
|
|
96
|
+
if (e instanceof SpiffsFullError) {
|
|
97
|
+
// All pages in block exhausted, create new block
|
|
98
|
+
const prevBlock = this.blocks[this.blocks.length - 1];
|
|
99
|
+
const block = this.createBlock();
|
|
100
|
+
block.currentObjId = prevBlock.currentObjId;
|
|
101
|
+
block.currentObjIdxPage = prevBlock.currentObjIdxPage;
|
|
102
|
+
block.currentObjDataSpanIx = prevBlock.currentObjDataSpanIx;
|
|
103
|
+
block.currentObjIndexSpanIx = prevBlock.currentObjIndexSpanIx;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
throw e;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
offset += chunkSize;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const block = this.blocks[this.blocks.length - 1];
|
|
113
|
+
block.endObj();
|
|
114
|
+
this.curObjId++;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
toBinary(): Uint8Array {
|
|
118
|
+
const allBlocks: Uint8Array[] = [];
|
|
119
|
+
|
|
120
|
+
for (const block of this.blocks) {
|
|
121
|
+
allBlocks.push(block.toBinary(this.blocksLim));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let bix = this.blocks.length;
|
|
125
|
+
let remaining = this.remainingBlocks;
|
|
126
|
+
|
|
127
|
+
if (this.buildConfig.useMagic) {
|
|
128
|
+
// Create empty blocks with magic numbers
|
|
129
|
+
while (remaining > 0) {
|
|
130
|
+
const block = new SpiffsBlock(bix, this.buildConfig);
|
|
131
|
+
allBlocks.push(block.toBinary(this.blocksLim));
|
|
132
|
+
remaining--;
|
|
133
|
+
bix++;
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
// Fill remaining space with 0xFF
|
|
137
|
+
const remainingSize =
|
|
138
|
+
this.imgSize - allBlocks.length * this.buildConfig.blockSize;
|
|
139
|
+
if (remainingSize > 0) {
|
|
140
|
+
const padding = new Uint8Array(remainingSize);
|
|
141
|
+
padding.fill(0xff);
|
|
142
|
+
allBlocks.push(padding);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Concatenate all blocks
|
|
147
|
+
const totalSize = allBlocks.reduce((sum, block) => sum + block.length, 0);
|
|
148
|
+
const img = new Uint8Array(totalSize);
|
|
149
|
+
let offset = 0;
|
|
150
|
+
for (const block of allBlocks) {
|
|
151
|
+
img.set(block, offset);
|
|
152
|
+
offset += block.length;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return img;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
listFiles(): SpiffsFile[] {
|
|
159
|
+
// This would require parsing the blocks - implement in fromBinary
|
|
160
|
+
throw new Error("listFiles requires fromBinary to be called first");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
readFile(): Uint8Array {
|
|
164
|
+
// This would require parsing the blocks - implement in fromBinary
|
|
165
|
+
throw new Error("readFile requires fromBinary to be called first");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
deleteFile(): void {
|
|
169
|
+
// SPIFFS doesn't support in-place deletion
|
|
170
|
+
// Need to recreate filesystem without the file
|
|
171
|
+
throw new Error(
|
|
172
|
+
"deleteFile not yet implemented - requires filesystem recreation",
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPIFFS Block Class
|
|
3
|
+
* Based on ESP-IDF spiffsgen.py
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { SpiffsBuildConfig, SpiffsFullError } from "./spiffsConfig";
|
|
7
|
+
import {
|
|
8
|
+
SpiffsPage,
|
|
9
|
+
SpiffsObjLuPage,
|
|
10
|
+
SpiffsObjIndexPage,
|
|
11
|
+
SpiffsObjDataPage,
|
|
12
|
+
SpiffsObjPageWithIdx,
|
|
13
|
+
} from "./spiffsPage";
|
|
14
|
+
|
|
15
|
+
export class SpiffsBlock {
|
|
16
|
+
private buildConfig: SpiffsBuildConfig;
|
|
17
|
+
private offset: number;
|
|
18
|
+
private remainingPages: number;
|
|
19
|
+
private pages: SpiffsPage[];
|
|
20
|
+
private bix: number;
|
|
21
|
+
private luPages: SpiffsObjLuPage[];
|
|
22
|
+
private luPageIter: Iterator<SpiffsObjLuPage>;
|
|
23
|
+
private luPage: SpiffsObjLuPage | null;
|
|
24
|
+
|
|
25
|
+
private curObjIndexSpanIx: number;
|
|
26
|
+
private curObjDataSpanIx: number;
|
|
27
|
+
private curObjId: number;
|
|
28
|
+
private curObjIdxPage: SpiffsObjIndexPage | null;
|
|
29
|
+
|
|
30
|
+
constructor(bix: number, buildConfig: SpiffsBuildConfig) {
|
|
31
|
+
this.buildConfig = buildConfig;
|
|
32
|
+
this.offset = bix * this.buildConfig.blockSize;
|
|
33
|
+
this.remainingPages = this.buildConfig.OBJ_USABLE_PAGES_PER_BLOCK;
|
|
34
|
+
this.pages = [];
|
|
35
|
+
this.bix = bix;
|
|
36
|
+
|
|
37
|
+
this.luPages = [];
|
|
38
|
+
for (let i = 0; i < this.buildConfig.OBJ_LU_PAGES_PER_BLOCK; i++) {
|
|
39
|
+
const page = new SpiffsObjLuPage(this.bix, this.buildConfig);
|
|
40
|
+
this.luPages.push(page);
|
|
41
|
+
}
|
|
42
|
+
this.pages.push(...this.luPages);
|
|
43
|
+
|
|
44
|
+
this.luPageIter = this.luPages[Symbol.iterator]();
|
|
45
|
+
this.luPage = this.luPageIter.next().value || null;
|
|
46
|
+
|
|
47
|
+
this.curObjIndexSpanIx = 0;
|
|
48
|
+
this.curObjDataSpanIx = 0;
|
|
49
|
+
this.curObjId = 0;
|
|
50
|
+
this.curObjIdxPage = null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private reset(): void {
|
|
54
|
+
this.curObjIndexSpanIx = 0;
|
|
55
|
+
this.curObjDataSpanIx = 0;
|
|
56
|
+
this.curObjId = 0;
|
|
57
|
+
this.curObjIdxPage = null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private registerPage(page: SpiffsObjPageWithIdx): void {
|
|
61
|
+
if (page instanceof SpiffsObjDataPage) {
|
|
62
|
+
if (!this.curObjIdxPage) {
|
|
63
|
+
throw new Error("No current object index page");
|
|
64
|
+
}
|
|
65
|
+
this.curObjIdxPage.registerPage(page);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
if (!this.luPage) {
|
|
70
|
+
throw new SpiffsFullError();
|
|
71
|
+
}
|
|
72
|
+
this.luPage.registerPage(page);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
if (e instanceof SpiffsFullError) {
|
|
75
|
+
const next = this.luPageIter.next();
|
|
76
|
+
if (next.done) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
"Invalid attempt to add page to a block when there is no more space in lookup",
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
this.luPage = next.value;
|
|
82
|
+
this.luPage.registerPage(page);
|
|
83
|
+
} else {
|
|
84
|
+
throw e;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.pages.push(page);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
beginObj(
|
|
92
|
+
objId: number,
|
|
93
|
+
size: number,
|
|
94
|
+
name: string,
|
|
95
|
+
objIndexSpanIx = 0,
|
|
96
|
+
objDataSpanIx = 0,
|
|
97
|
+
): void {
|
|
98
|
+
if (this.remainingPages <= 0) {
|
|
99
|
+
throw new SpiffsFullError();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
this.reset();
|
|
103
|
+
this.curObjId = objId;
|
|
104
|
+
this.curObjIndexSpanIx = objIndexSpanIx;
|
|
105
|
+
this.curObjDataSpanIx = objDataSpanIx;
|
|
106
|
+
|
|
107
|
+
const page = new SpiffsObjIndexPage(
|
|
108
|
+
objId,
|
|
109
|
+
this.curObjIndexSpanIx,
|
|
110
|
+
size,
|
|
111
|
+
name,
|
|
112
|
+
this.buildConfig,
|
|
113
|
+
);
|
|
114
|
+
this.registerPage(page);
|
|
115
|
+
this.curObjIdxPage = page;
|
|
116
|
+
this.remainingPages--;
|
|
117
|
+
this.curObjIndexSpanIx++;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
updateObj(contents: Uint8Array): void {
|
|
121
|
+
if (this.remainingPages <= 0) {
|
|
122
|
+
throw new SpiffsFullError();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const page = new SpiffsObjDataPage(
|
|
126
|
+
this.offset + this.pages.length * this.buildConfig.pageSize,
|
|
127
|
+
this.curObjId,
|
|
128
|
+
this.curObjDataSpanIx,
|
|
129
|
+
contents,
|
|
130
|
+
this.buildConfig,
|
|
131
|
+
);
|
|
132
|
+
this.registerPage(page);
|
|
133
|
+
this.curObjDataSpanIx++;
|
|
134
|
+
this.remainingPages--;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
endObj(): void {
|
|
138
|
+
this.reset();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
isFull(): boolean {
|
|
142
|
+
return this.remainingPages <= 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
toBinary(blocksLim: number): Uint8Array {
|
|
146
|
+
const img = new Uint8Array(this.buildConfig.blockSize);
|
|
147
|
+
img.fill(0xff);
|
|
148
|
+
|
|
149
|
+
let offset = 0;
|
|
150
|
+
if (this.buildConfig.useMagic) {
|
|
151
|
+
for (let idx = 0; idx < this.pages.length; idx++) {
|
|
152
|
+
const page = this.pages[idx];
|
|
153
|
+
if (idx === this.buildConfig.OBJ_LU_PAGES_PER_BLOCK - 1) {
|
|
154
|
+
if (page instanceof SpiffsObjLuPage) {
|
|
155
|
+
page.magicfy(blocksLim);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const pageBinary = page.toBinary();
|
|
159
|
+
img.set(pageBinary, offset);
|
|
160
|
+
offset += pageBinary.length;
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
for (const page of this.pages) {
|
|
164
|
+
const pageBinary = page.toBinary();
|
|
165
|
+
img.set(pageBinary, offset);
|
|
166
|
+
offset += pageBinary.length;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return img;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
get currentObjIndexSpanIx(): number {
|
|
174
|
+
return this.curObjIndexSpanIx;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
get currentObjDataSpanIx(): number {
|
|
178
|
+
return this.curObjDataSpanIx;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
get currentObjId(): number {
|
|
182
|
+
return this.curObjId;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
get currentObjIdxPage(): SpiffsObjIndexPage | null {
|
|
186
|
+
return this.curObjIdxPage;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
set currentObjId(value: number) {
|
|
190
|
+
this.curObjId = value;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
set currentObjIdxPage(value: SpiffsObjIndexPage | null) {
|
|
194
|
+
this.curObjIdxPage = value;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
set currentObjDataSpanIx(value: number) {
|
|
198
|
+
this.curObjDataSpanIx = value;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
set currentObjIndexSpanIx(value: number) {
|
|
202
|
+
this.curObjIndexSpanIx = value;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPIFFS Build Configuration
|
|
3
|
+
* Based on ESP-IDF spiffsgen.py
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const SPIFFS_PH_FLAG_USED_FINAL_INDEX = 0xf8;
|
|
7
|
+
export const SPIFFS_PH_FLAG_USED_FINAL = 0xfc;
|
|
8
|
+
export const SPIFFS_PH_FLAG_LEN = 1;
|
|
9
|
+
export const SPIFFS_PH_IX_SIZE_LEN = 4;
|
|
10
|
+
export const SPIFFS_PH_IX_OBJ_TYPE_LEN = 1;
|
|
11
|
+
export const SPIFFS_TYPE_FILE = 1;
|
|
12
|
+
|
|
13
|
+
// Based on typedefs under spiffs_config.h
|
|
14
|
+
export const SPIFFS_OBJ_ID_LEN = 2; // spiffs_obj_id
|
|
15
|
+
export const SPIFFS_SPAN_IX_LEN = 2; // spiffs_span_ix
|
|
16
|
+
export const SPIFFS_PAGE_IX_LEN = 2; // spiffs_page_ix
|
|
17
|
+
export const SPIFFS_BLOCK_IX_LEN = 2; // spiffs_block_ix
|
|
18
|
+
|
|
19
|
+
export interface SpiffsBuildConfigOptions {
|
|
20
|
+
pageSize: number;
|
|
21
|
+
blockSize: number;
|
|
22
|
+
objNameLen?: number;
|
|
23
|
+
metaLen?: number;
|
|
24
|
+
pageIxLen?: number;
|
|
25
|
+
blockIxLen?: number;
|
|
26
|
+
objIdLen?: number;
|
|
27
|
+
spanIxLen?: number;
|
|
28
|
+
packed?: boolean;
|
|
29
|
+
aligned?: boolean;
|
|
30
|
+
endianness?: "little" | "big";
|
|
31
|
+
useMagic?: boolean;
|
|
32
|
+
useMagicLen?: boolean;
|
|
33
|
+
alignedObjIxTables?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class SpiffsBuildConfig {
|
|
37
|
+
pageSize: number;
|
|
38
|
+
blockSize: number;
|
|
39
|
+
objIdLen: number;
|
|
40
|
+
spanIxLen: number;
|
|
41
|
+
packed: boolean;
|
|
42
|
+
aligned: boolean;
|
|
43
|
+
objNameLen: number;
|
|
44
|
+
metaLen: number;
|
|
45
|
+
pageIxLen: number;
|
|
46
|
+
blockIxLen: number;
|
|
47
|
+
endianness: "little" | "big";
|
|
48
|
+
useMagic: boolean;
|
|
49
|
+
useMagicLen: boolean;
|
|
50
|
+
alignedObjIxTables: boolean;
|
|
51
|
+
|
|
52
|
+
PAGES_PER_BLOCK: number;
|
|
53
|
+
OBJ_LU_PAGES_PER_BLOCK: number;
|
|
54
|
+
OBJ_USABLE_PAGES_PER_BLOCK: number;
|
|
55
|
+
OBJ_LU_PAGES_OBJ_IDS_LIM: number;
|
|
56
|
+
OBJ_DATA_PAGE_HEADER_LEN: number;
|
|
57
|
+
OBJ_DATA_PAGE_HEADER_LEN_ALIGNED: number;
|
|
58
|
+
OBJ_DATA_PAGE_HEADER_LEN_ALIGNED_PAD: number;
|
|
59
|
+
OBJ_DATA_PAGE_CONTENT_LEN: number;
|
|
60
|
+
OBJ_INDEX_PAGES_HEADER_LEN: number;
|
|
61
|
+
OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED: number;
|
|
62
|
+
OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED_PAD: number;
|
|
63
|
+
OBJ_INDEX_PAGES_OBJ_IDS_HEAD_LIM: number;
|
|
64
|
+
OBJ_INDEX_PAGES_OBJ_IDS_LIM: number;
|
|
65
|
+
|
|
66
|
+
constructor(options: SpiffsBuildConfigOptions) {
|
|
67
|
+
if (options.blockSize % options.pageSize !== 0) {
|
|
68
|
+
throw new Error("block size should be a multiple of page size");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.pageSize = options.pageSize;
|
|
72
|
+
this.blockSize = options.blockSize;
|
|
73
|
+
this.objIdLen = options.objIdLen ?? SPIFFS_OBJ_ID_LEN;
|
|
74
|
+
this.spanIxLen = options.spanIxLen ?? SPIFFS_SPAN_IX_LEN;
|
|
75
|
+
this.packed = options.packed ?? true;
|
|
76
|
+
this.aligned = options.aligned ?? true;
|
|
77
|
+
this.objNameLen = options.objNameLen ?? 32;
|
|
78
|
+
this.metaLen = options.metaLen ?? 4;
|
|
79
|
+
this.pageIxLen = options.pageIxLen ?? SPIFFS_PAGE_IX_LEN;
|
|
80
|
+
this.blockIxLen = options.blockIxLen ?? SPIFFS_BLOCK_IX_LEN;
|
|
81
|
+
this.endianness = options.endianness ?? "little";
|
|
82
|
+
this.useMagic = options.useMagic ?? true;
|
|
83
|
+
this.useMagicLen = options.useMagicLen ?? true;
|
|
84
|
+
this.alignedObjIxTables = options.alignedObjIxTables ?? false;
|
|
85
|
+
|
|
86
|
+
this.PAGES_PER_BLOCK = Math.floor(this.blockSize / this.pageSize);
|
|
87
|
+
this.OBJ_LU_PAGES_PER_BLOCK = Math.ceil(
|
|
88
|
+
((this.blockSize / this.pageSize) * this.objIdLen) / this.pageSize,
|
|
89
|
+
);
|
|
90
|
+
this.OBJ_USABLE_PAGES_PER_BLOCK =
|
|
91
|
+
this.PAGES_PER_BLOCK - this.OBJ_LU_PAGES_PER_BLOCK;
|
|
92
|
+
this.OBJ_LU_PAGES_OBJ_IDS_LIM = Math.floor(this.pageSize / this.objIdLen);
|
|
93
|
+
|
|
94
|
+
this.OBJ_DATA_PAGE_HEADER_LEN =
|
|
95
|
+
this.objIdLen + this.spanIxLen + SPIFFS_PH_FLAG_LEN;
|
|
96
|
+
const pad =
|
|
97
|
+
4 -
|
|
98
|
+
(this.OBJ_DATA_PAGE_HEADER_LEN % 4 === 0
|
|
99
|
+
? 4
|
|
100
|
+
: this.OBJ_DATA_PAGE_HEADER_LEN % 4);
|
|
101
|
+
this.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED = this.OBJ_DATA_PAGE_HEADER_LEN + pad;
|
|
102
|
+
this.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED_PAD = pad;
|
|
103
|
+
this.OBJ_DATA_PAGE_CONTENT_LEN =
|
|
104
|
+
this.pageSize - this.OBJ_DATA_PAGE_HEADER_LEN;
|
|
105
|
+
|
|
106
|
+
this.OBJ_INDEX_PAGES_HEADER_LEN =
|
|
107
|
+
this.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED +
|
|
108
|
+
SPIFFS_PH_IX_SIZE_LEN +
|
|
109
|
+
SPIFFS_PH_IX_OBJ_TYPE_LEN +
|
|
110
|
+
this.objNameLen +
|
|
111
|
+
this.metaLen;
|
|
112
|
+
|
|
113
|
+
if (this.alignedObjIxTables) {
|
|
114
|
+
this.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED =
|
|
115
|
+
(this.OBJ_INDEX_PAGES_HEADER_LEN + SPIFFS_PAGE_IX_LEN - 1) &
|
|
116
|
+
~(SPIFFS_PAGE_IX_LEN - 1);
|
|
117
|
+
this.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED_PAD =
|
|
118
|
+
this.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED -
|
|
119
|
+
this.OBJ_INDEX_PAGES_HEADER_LEN;
|
|
120
|
+
} else {
|
|
121
|
+
this.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED = this.OBJ_INDEX_PAGES_HEADER_LEN;
|
|
122
|
+
this.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED_PAD = 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.OBJ_INDEX_PAGES_OBJ_IDS_HEAD_LIM = Math.floor(
|
|
126
|
+
(this.pageSize - this.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED) /
|
|
127
|
+
this.blockIxLen,
|
|
128
|
+
);
|
|
129
|
+
this.OBJ_INDEX_PAGES_OBJ_IDS_LIM = Math.floor(
|
|
130
|
+
(this.pageSize - this.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED) / this.blockIxLen,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export class SpiffsFullError extends Error {
|
|
136
|
+
constructor(message = "SPIFFS is full") {
|
|
137
|
+
super(message);
|
|
138
|
+
this.name = "SpiffsFullError";
|
|
139
|
+
}
|
|
140
|
+
}
|