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.
Files changed (115) hide show
  1. package/README.md +31 -0
  2. package/css/dark.css +156 -0
  3. package/css/light.css +156 -0
  4. package/css/style.css +870 -0
  5. package/dist/const.d.ts +277 -0
  6. package/dist/const.js +511 -0
  7. package/dist/esp_loader.d.ts +222 -0
  8. package/dist/esp_loader.js +1466 -0
  9. package/dist/index.d.ts +10 -0
  10. package/dist/index.js +15 -0
  11. package/dist/lib/spiffs/index.d.ts +15 -0
  12. package/dist/lib/spiffs/index.js +16 -0
  13. package/dist/lib/spiffs/spiffs.d.ts +26 -0
  14. package/dist/lib/spiffs/spiffs.js +132 -0
  15. package/dist/lib/spiffs/spiffsBlock.d.ts +36 -0
  16. package/dist/lib/spiffs/spiffsBlock.js +140 -0
  17. package/dist/lib/spiffs/spiffsConfig.d.ts +63 -0
  18. package/dist/lib/spiffs/spiffsConfig.js +79 -0
  19. package/dist/lib/spiffs/spiffsPage.d.ts +45 -0
  20. package/dist/lib/spiffs/spiffsPage.js +260 -0
  21. package/dist/lib/spiffs/spiffsReader.d.ts +19 -0
  22. package/dist/lib/spiffs/spiffsReader.js +192 -0
  23. package/dist/partition.d.ts +26 -0
  24. package/dist/partition.js +129 -0
  25. package/dist/struct.d.ts +2 -0
  26. package/dist/struct.js +91 -0
  27. package/dist/stubs/esp32.json +8 -0
  28. package/dist/stubs/esp32c2.json +8 -0
  29. package/dist/stubs/esp32c3.json +8 -0
  30. package/dist/stubs/esp32c5.json +8 -0
  31. package/dist/stubs/esp32c6.json +8 -0
  32. package/dist/stubs/esp32c61.json +8 -0
  33. package/dist/stubs/esp32h2.json +8 -0
  34. package/dist/stubs/esp32p4.json +8 -0
  35. package/dist/stubs/esp32p4r3.json +8 -0
  36. package/dist/stubs/esp32s2.json +8 -0
  37. package/dist/stubs/esp32s3.json +8 -0
  38. package/dist/stubs/esp8266.json +8 -0
  39. package/dist/stubs/index.d.ts +10 -0
  40. package/dist/stubs/index.js +56 -0
  41. package/dist/util.d.ts +14 -0
  42. package/dist/util.js +46 -0
  43. package/dist/wasm/filesystems.d.ts +33 -0
  44. package/dist/wasm/filesystems.js +114 -0
  45. package/dist/web/esp32-D955RjN9.js +16 -0
  46. package/dist/web/esp32c2-CJkxHDQi.js +16 -0
  47. package/dist/web/esp32c3-BhUHzH0o.js +16 -0
  48. package/dist/web/esp32c5-Chs0HtmA.js +16 -0
  49. package/dist/web/esp32c6-D6mPN6ut.js +16 -0
  50. package/dist/web/esp32c61-CQiYCWAs.js +16 -0
  51. package/dist/web/esp32h2-LsKJE9AS.js +16 -0
  52. package/dist/web/esp32p4-7nWC-HiD.js +16 -0
  53. package/dist/web/esp32p4r3-CwiPecZW.js +16 -0
  54. package/dist/web/esp32s2-CtqVheSJ.js +16 -0
  55. package/dist/web/esp32s3-CRbtB0QR.js +16 -0
  56. package/dist/web/esp8266-nEkNAo8K.js +16 -0
  57. package/dist/web/index.js +7265 -0
  58. package/electron/main.js +333 -0
  59. package/electron/preload.js +37 -0
  60. package/eslint.config.js +22 -0
  61. package/index.html +408 -0
  62. package/js/modules/esp32-D955RjN9.js +16 -0
  63. package/js/modules/esp32c2-CJkxHDQi.js +16 -0
  64. package/js/modules/esp32c3-BhUHzH0o.js +16 -0
  65. package/js/modules/esp32c5-Chs0HtmA.js +16 -0
  66. package/js/modules/esp32c6-D6mPN6ut.js +16 -0
  67. package/js/modules/esp32c61-CQiYCWAs.js +16 -0
  68. package/js/modules/esp32h2-LsKJE9AS.js +16 -0
  69. package/js/modules/esp32p4-7nWC-HiD.js +16 -0
  70. package/js/modules/esp32p4r3-CwiPecZW.js +16 -0
  71. package/js/modules/esp32s2-CtqVheSJ.js +16 -0
  72. package/js/modules/esp32s3-CRbtB0QR.js +16 -0
  73. package/js/modules/esp8266-nEkNAo8K.js +16 -0
  74. package/js/modules/esptool.js +7265 -0
  75. package/js/script.js +2237 -0
  76. package/js/utilities.js +182 -0
  77. package/license.md +11 -0
  78. package/package.json +61 -0
  79. package/script/build +12 -0
  80. package/script/develop +17 -0
  81. package/src/const.ts +599 -0
  82. package/src/esp_loader.ts +1907 -0
  83. package/src/index.ts +63 -0
  84. package/src/lib/spiffs/index.ts +22 -0
  85. package/src/lib/spiffs/spiffs.ts +175 -0
  86. package/src/lib/spiffs/spiffsBlock.ts +204 -0
  87. package/src/lib/spiffs/spiffsConfig.ts +140 -0
  88. package/src/lib/spiffs/spiffsPage.ts +357 -0
  89. package/src/lib/spiffs/spiffsReader.ts +280 -0
  90. package/src/partition.ts +155 -0
  91. package/src/struct.ts +108 -0
  92. package/src/stubs/README.md +3 -0
  93. package/src/stubs/esp32.json +8 -0
  94. package/src/stubs/esp32c2.json +8 -0
  95. package/src/stubs/esp32c3.json +8 -0
  96. package/src/stubs/esp32c5.json +8 -0
  97. package/src/stubs/esp32c6.json +8 -0
  98. package/src/stubs/esp32c61.json +8 -0
  99. package/src/stubs/esp32h2.json +8 -0
  100. package/src/stubs/esp32p4.json +8 -0
  101. package/src/stubs/esp32p4r3.json +8 -0
  102. package/src/stubs/esp32s2.json +8 -0
  103. package/src/stubs/esp32s3.json +8 -0
  104. package/src/stubs/esp8266.json +8 -0
  105. package/src/stubs/index.ts +86 -0
  106. package/src/util.ts +49 -0
  107. package/src/wasm/fatfs/fatfs.wasm +0 -0
  108. package/src/wasm/fatfs/index.d.ts +26 -0
  109. package/src/wasm/fatfs/index.js +343 -0
  110. package/src/wasm/filesystems.ts +156 -0
  111. package/src/wasm/littlefs/index.d.ts +83 -0
  112. package/src/wasm/littlefs/index.js +529 -0
  113. package/src/wasm/littlefs/littlefs.js +2 -0
  114. package/src/wasm/littlefs/littlefs.wasm +0 -0
  115. package/src/wasm/shared/types.ts +13 -0
@@ -0,0 +1,260 @@
1
+ /**
2
+ * SPIFFS Page Classes
3
+ * Based on ESP-IDF spiffsgen.py
4
+ */
5
+ import { SpiffsFullError, SPIFFS_PH_FLAG_USED_FINAL_INDEX, SPIFFS_PH_FLAG_USED_FINAL, SPIFFS_TYPE_FILE, } from "./spiffsConfig";
6
+ export class SpiffsPage {
7
+ constructor(bix, buildConfig) {
8
+ this.buildConfig = buildConfig;
9
+ this.bix = bix;
10
+ }
11
+ pack(format, ...values) {
12
+ const buffer = new ArrayBuffer(this.calcSize(format));
13
+ const view = new DataView(buffer);
14
+ let offset = 0;
15
+ for (let i = 0; i < format.length; i++) {
16
+ const type = format[i];
17
+ const value = values[i];
18
+ switch (type) {
19
+ case "B": // unsigned char (1 byte)
20
+ view.setUint8(offset, value);
21
+ offset += 1;
22
+ break;
23
+ case "H": // unsigned short (2 bytes)
24
+ if (this.buildConfig.endianness === "little") {
25
+ view.setUint16(offset, value, true);
26
+ }
27
+ else {
28
+ view.setUint16(offset, value, false);
29
+ }
30
+ offset += 2;
31
+ break;
32
+ case "I": // unsigned int (4 bytes)
33
+ if (this.buildConfig.endianness === "little") {
34
+ view.setUint32(offset, value, true);
35
+ }
36
+ else {
37
+ view.setUint32(offset, value, false);
38
+ }
39
+ offset += 4;
40
+ break;
41
+ }
42
+ }
43
+ return new Uint8Array(buffer);
44
+ }
45
+ unpack(format, data, offset = 0) {
46
+ const view = new DataView(data.buffer, data.byteOffset + offset);
47
+ const results = [];
48
+ let pos = 0;
49
+ for (const type of format) {
50
+ switch (type) {
51
+ case "B":
52
+ results.push(view.getUint8(pos));
53
+ pos += 1;
54
+ break;
55
+ case "H":
56
+ results.push(this.buildConfig.endianness === "little"
57
+ ? view.getUint16(pos, true)
58
+ : view.getUint16(pos, false));
59
+ pos += 2;
60
+ break;
61
+ case "I":
62
+ results.push(this.buildConfig.endianness === "little"
63
+ ? view.getUint32(pos, true)
64
+ : view.getUint32(pos, false));
65
+ pos += 4;
66
+ break;
67
+ }
68
+ }
69
+ return results;
70
+ }
71
+ calcSize(format) {
72
+ let size = 0;
73
+ for (const type of format) {
74
+ switch (type) {
75
+ case "B":
76
+ size += 1;
77
+ break;
78
+ case "H":
79
+ size += 2;
80
+ break;
81
+ case "I":
82
+ size += 4;
83
+ break;
84
+ }
85
+ }
86
+ return size;
87
+ }
88
+ }
89
+ export class SpiffsObjPageWithIdx extends SpiffsPage {
90
+ constructor(objId, buildConfig) {
91
+ super(0, buildConfig);
92
+ this.objId = objId;
93
+ }
94
+ getObjId() {
95
+ return this.objId;
96
+ }
97
+ }
98
+ export class SpiffsObjLuPage extends SpiffsPage {
99
+ constructor(bix, buildConfig) {
100
+ super(bix, buildConfig);
101
+ this.objIdsLimit = this.buildConfig.OBJ_LU_PAGES_OBJ_IDS_LIM;
102
+ this.objIds = [];
103
+ }
104
+ calcMagic(blocksLim) {
105
+ let magic = 0x20140529 ^ this.buildConfig.pageSize;
106
+ if (this.buildConfig.useMagicLen) {
107
+ magic = magic ^ (blocksLim - this.bix);
108
+ }
109
+ const mask = (1 << (8 * this.buildConfig.objIdLen)) - 1;
110
+ return magic & mask;
111
+ }
112
+ registerPage(page) {
113
+ if (this.objIdsLimit <= 0) {
114
+ throw new SpiffsFullError();
115
+ }
116
+ const pageType = page instanceof SpiffsObjIndexPage ? "index" : "data";
117
+ this.objIds.push([page.getObjId(), pageType]);
118
+ this.objIdsLimit--;
119
+ }
120
+ toBinary() {
121
+ const img = new Uint8Array(this.buildConfig.pageSize);
122
+ img.fill(0xff);
123
+ let offset = 0;
124
+ for (const [objId, pageType] of this.objIds) {
125
+ let id = objId;
126
+ if (pageType === "index") {
127
+ id ^= 1 << (this.buildConfig.objIdLen * 8 - 1);
128
+ }
129
+ const packed = this.pack(this.buildConfig.objIdLen === 1
130
+ ? "B"
131
+ : this.buildConfig.objIdLen === 2
132
+ ? "H"
133
+ : "I", id);
134
+ img.set(packed, offset);
135
+ offset += packed.length;
136
+ }
137
+ return img;
138
+ }
139
+ magicfy(blocksLim) {
140
+ const remaining = this.objIdsLimit;
141
+ const emptyObjId = (1 << (this.buildConfig.objIdLen * 8)) - 1;
142
+ if (remaining >= 2) {
143
+ for (let i = 0; i < remaining; i++) {
144
+ if (i === remaining - 2) {
145
+ this.objIds.push([this.calcMagic(blocksLim), "data"]);
146
+ break;
147
+ }
148
+ else {
149
+ this.objIds.push([emptyObjId, "data"]);
150
+ }
151
+ this.objIdsLimit--;
152
+ }
153
+ }
154
+ }
155
+ }
156
+ export class SpiffsObjIndexPage extends SpiffsObjPageWithIdx {
157
+ constructor(objId, spanIx, size, name, buildConfig) {
158
+ super(objId, buildConfig);
159
+ this.spanIx = spanIx;
160
+ this.name = name;
161
+ this.size = size;
162
+ if (this.spanIx === 0) {
163
+ this.pagesLim = this.buildConfig.OBJ_INDEX_PAGES_OBJ_IDS_HEAD_LIM;
164
+ }
165
+ else {
166
+ this.pagesLim = this.buildConfig.OBJ_INDEX_PAGES_OBJ_IDS_LIM;
167
+ }
168
+ this.pages = [];
169
+ }
170
+ registerPage(page) {
171
+ if (this.pagesLim <= 0) {
172
+ throw new SpiffsFullError();
173
+ }
174
+ this.pages.push(page.offset);
175
+ this.pagesLim--;
176
+ }
177
+ toBinary() {
178
+ const img = new Uint8Array(this.buildConfig.pageSize);
179
+ img.fill(0xff);
180
+ const objId = this.objId ^ (1 << (this.buildConfig.objIdLen * 8 - 1));
181
+ const format = (this.buildConfig.objIdLen === 1
182
+ ? "B"
183
+ : this.buildConfig.objIdLen === 2
184
+ ? "H"
185
+ : "I") +
186
+ (this.buildConfig.spanIxLen === 1
187
+ ? "B"
188
+ : this.buildConfig.spanIxLen === 2
189
+ ? "H"
190
+ : "I") +
191
+ "B";
192
+ let offset = 0;
193
+ const header = this.pack(format, objId, this.spanIx, SPIFFS_PH_FLAG_USED_FINAL_INDEX);
194
+ img.set(header, offset);
195
+ offset += header.length;
196
+ // Add padding
197
+ offset += this.buildConfig.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED_PAD;
198
+ // If first index page, add filename, type and size
199
+ if (this.spanIx === 0) {
200
+ const sizeType = this.pack("IB", this.size, SPIFFS_TYPE_FILE);
201
+ img.set(sizeType, offset);
202
+ offset += sizeType.length;
203
+ // Write filename with proper null-termination
204
+ const nameBytes = new TextEncoder().encode(this.name);
205
+ // Ensure we don't exceed objNameLen
206
+ const bytesToWrite = Math.min(nameBytes.length, this.buildConfig.objNameLen);
207
+ img.set(nameBytes.slice(0, bytesToWrite), offset);
208
+ // The rest is already 0xFF from img.fill(0xff), but SPIFFS expects 0x00 for unused name bytes
209
+ // Fill remaining name bytes with 0x00
210
+ for (let i = bytesToWrite; i < this.buildConfig.objNameLen; i++) {
211
+ img[offset + i] = 0x00;
212
+ }
213
+ offset +=
214
+ this.buildConfig.objNameLen +
215
+ this.buildConfig.metaLen +
216
+ this.buildConfig.OBJ_INDEX_PAGES_HEADER_LEN_ALIGNED_PAD;
217
+ }
218
+ // Add page indices
219
+ for (const page of this.pages) {
220
+ // Calculate page index by dividing page offset by page size
221
+ // pageSize is always a power of 2, so integer division is safe
222
+ const pageIx = Math.floor(page / this.buildConfig.pageSize);
223
+ const pageIxPacked = this.pack(this.buildConfig.pageIxLen === 1
224
+ ? "B"
225
+ : this.buildConfig.pageIxLen === 2
226
+ ? "H"
227
+ : "I", pageIx);
228
+ img.set(pageIxPacked, offset);
229
+ offset += pageIxPacked.length;
230
+ }
231
+ return img;
232
+ }
233
+ }
234
+ export class SpiffsObjDataPage extends SpiffsObjPageWithIdx {
235
+ constructor(offset, objId, spanIx, contents, buildConfig) {
236
+ super(objId, buildConfig);
237
+ this.offset = offset;
238
+ this.spanIx = spanIx;
239
+ this.contents = contents;
240
+ }
241
+ toBinary() {
242
+ const img = new Uint8Array(this.buildConfig.pageSize);
243
+ img.fill(0xff);
244
+ const format = (this.buildConfig.objIdLen === 1
245
+ ? "B"
246
+ : this.buildConfig.objIdLen === 2
247
+ ? "H"
248
+ : "I") +
249
+ (this.buildConfig.spanIxLen === 1
250
+ ? "B"
251
+ : this.buildConfig.spanIxLen === 2
252
+ ? "H"
253
+ : "I") +
254
+ "B";
255
+ const header = this.pack(format, this.objId, this.spanIx, SPIFFS_PH_FLAG_USED_FINAL);
256
+ img.set(header, 0);
257
+ img.set(this.contents, header.length);
258
+ return img;
259
+ }
260
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * SPIFFS Reader - Parse and extract files from SPIFFS images
3
+ * Based on ESP-IDF spiffsgen.py extract_files() method
4
+ */
5
+ import { SpiffsBuildConfig } from "./spiffsConfig";
6
+ import type { SpiffsFile } from "./spiffs";
7
+ export declare class SpiffsReader {
8
+ private buildConfig;
9
+ private imageData;
10
+ private filesMap;
11
+ constructor(imageData: Uint8Array, buildConfig: SpiffsBuildConfig);
12
+ private unpack;
13
+ parse(): void;
14
+ private parseBlock;
15
+ private parsePage;
16
+ private parseIndexPage;
17
+ listFiles(): SpiffsFile[];
18
+ readFile(path: string): Uint8Array | null;
19
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * SPIFFS Reader - Parse and extract files from SPIFFS images
3
+ * Based on ESP-IDF spiffsgen.py extract_files() method
4
+ */
5
+ import { SPIFFS_PH_FLAG_USED_FINAL_INDEX, SPIFFS_PH_FLAG_USED_FINAL, SPIFFS_PH_FLAG_LEN, SPIFFS_PH_IX_SIZE_LEN, SPIFFS_PH_IX_OBJ_TYPE_LEN, } from "./spiffsConfig";
6
+ export class SpiffsReader {
7
+ constructor(imageData, buildConfig) {
8
+ this.imageData = imageData;
9
+ this.buildConfig = buildConfig;
10
+ this.filesMap = new Map();
11
+ }
12
+ unpack(format, data, offset = 0) {
13
+ const view = new DataView(data.buffer, data.byteOffset + offset);
14
+ const results = [];
15
+ let pos = 0;
16
+ for (const type of format) {
17
+ switch (type) {
18
+ case "B":
19
+ results.push(view.getUint8(pos));
20
+ pos += 1;
21
+ break;
22
+ case "H":
23
+ results.push(this.buildConfig.endianness === "little"
24
+ ? view.getUint16(pos, true)
25
+ : view.getUint16(pos, false));
26
+ pos += 2;
27
+ break;
28
+ case "I":
29
+ results.push(this.buildConfig.endianness === "little"
30
+ ? view.getUint32(pos, true)
31
+ : view.getUint32(pos, false));
32
+ pos += 4;
33
+ break;
34
+ }
35
+ }
36
+ return results;
37
+ }
38
+ parse() {
39
+ const blocksCount = Math.floor(this.imageData.length / this.buildConfig.blockSize);
40
+ for (let bix = 0; bix < blocksCount; bix++) {
41
+ const blockOffset = bix * this.buildConfig.blockSize;
42
+ const blockData = this.imageData.slice(blockOffset, blockOffset + this.buildConfig.blockSize);
43
+ this.parseBlock(blockData);
44
+ }
45
+ }
46
+ parseBlock(blockData) {
47
+ // Parse lookup pages to find valid objects
48
+ for (let pageIdx = 0; pageIdx < this.buildConfig.OBJ_LU_PAGES_PER_BLOCK; pageIdx++) {
49
+ const luPageOffset = pageIdx * this.buildConfig.pageSize;
50
+ const luPageData = blockData.slice(luPageOffset, luPageOffset + this.buildConfig.pageSize);
51
+ // Parse object IDs from lookup page
52
+ for (let i = 0; i < luPageData.length; i += this.buildConfig.objIdLen) {
53
+ if (i + this.buildConfig.objIdLen > luPageData.length)
54
+ break;
55
+ const objIdBytes = luPageData.slice(i, i + this.buildConfig.objIdLen);
56
+ const [objId] = this.unpack(this.buildConfig.objIdLen === 1
57
+ ? "B"
58
+ : this.buildConfig.objIdLen === 2
59
+ ? "H"
60
+ : "I", objIdBytes);
61
+ // Check if it's a valid object (not erased/empty)
62
+ const emptyValue = (1 << (this.buildConfig.objIdLen * 8)) - 1;
63
+ if (objId === emptyValue)
64
+ continue;
65
+ // Check if it's an index page (MSB set)
66
+ const isIndex = (objId & (1 << (this.buildConfig.objIdLen * 8 - 1))) !== 0;
67
+ const realObjId = objId & ~(1 << (this.buildConfig.objIdLen * 8 - 1));
68
+ if (isIndex && !this.filesMap.has(realObjId)) {
69
+ this.filesMap.set(realObjId, {
70
+ name: null,
71
+ size: 0,
72
+ dataPages: [],
73
+ });
74
+ }
75
+ }
76
+ }
77
+ // Parse actual pages to get file metadata and content
78
+ for (let pageIdx = this.buildConfig.OBJ_LU_PAGES_PER_BLOCK; pageIdx < this.buildConfig.PAGES_PER_BLOCK; pageIdx++) {
79
+ const pageOffset = pageIdx * this.buildConfig.pageSize;
80
+ const pageData = blockData.slice(pageOffset, pageOffset + this.buildConfig.pageSize);
81
+ this.parsePage(pageData);
82
+ }
83
+ }
84
+ parsePage(pageData) {
85
+ // Parse page header
86
+ const headerFormat = (this.buildConfig.objIdLen === 1
87
+ ? "B"
88
+ : this.buildConfig.objIdLen === 2
89
+ ? "H"
90
+ : "I") +
91
+ (this.buildConfig.spanIxLen === 1
92
+ ? "B"
93
+ : this.buildConfig.spanIxLen === 2
94
+ ? "H"
95
+ : "I") +
96
+ "B";
97
+ const headerSize = this.buildConfig.objIdLen +
98
+ this.buildConfig.spanIxLen +
99
+ SPIFFS_PH_FLAG_LEN;
100
+ if (pageData.length < headerSize)
101
+ return;
102
+ const [objId, spanIx, flags] = this.unpack(headerFormat, pageData);
103
+ // Check for valid page
104
+ const emptyId = (1 << (this.buildConfig.objIdLen * 8)) - 1;
105
+ if (objId === emptyId)
106
+ return;
107
+ const isIndex = (objId & (1 << (this.buildConfig.objIdLen * 8 - 1))) !== 0;
108
+ const realObjId = objId & ~(1 << (this.buildConfig.objIdLen * 8 - 1));
109
+ if (isIndex && flags === SPIFFS_PH_FLAG_USED_FINAL_INDEX) {
110
+ // Index page - contains file metadata
111
+ if (!this.filesMap.has(realObjId)) {
112
+ this.filesMap.set(realObjId, {
113
+ name: null,
114
+ size: 0,
115
+ dataPages: [],
116
+ });
117
+ }
118
+ // Only first index page (span_ix == 0) has filename and size
119
+ if (spanIx === 0) {
120
+ this.parseIndexPage(pageData, headerSize, realObjId);
121
+ }
122
+ }
123
+ else if (!isIndex && flags === SPIFFS_PH_FLAG_USED_FINAL) {
124
+ // Data page - contains file content
125
+ if (this.filesMap.has(realObjId)) {
126
+ const contentStart = headerSize;
127
+ const content = pageData.slice(contentStart, contentStart + this.buildConfig.OBJ_DATA_PAGE_CONTENT_LEN);
128
+ this.filesMap.get(realObjId).dataPages.push([spanIx, content]);
129
+ }
130
+ }
131
+ }
132
+ parseIndexPage(pageData, headerSize, objId) {
133
+ // Skip to size and type fields
134
+ let offset = headerSize + this.buildConfig.OBJ_DATA_PAGE_HEADER_LEN_ALIGNED_PAD;
135
+ const sizeTypeFormat = "IB";
136
+ const sizeTypeSize = SPIFFS_PH_IX_SIZE_LEN + SPIFFS_PH_IX_OBJ_TYPE_LEN;
137
+ if (offset + sizeTypeSize <= pageData.length) {
138
+ const [fileSize] = this.unpack(sizeTypeFormat, pageData, offset);
139
+ offset += sizeTypeSize;
140
+ // Read filename
141
+ const nameEnd = offset + this.buildConfig.objNameLen;
142
+ if (nameEnd <= pageData.length) {
143
+ const nameBytes = pageData.slice(offset, nameEnd);
144
+ // Find null terminator
145
+ const nullPos = nameBytes.indexOf(0);
146
+ const actualNameBytes = nullPos !== -1 ? nameBytes.slice(0, nullPos) : nameBytes;
147
+ const filename = new TextDecoder().decode(actualNameBytes);
148
+ const fileInfo = this.filesMap.get(objId);
149
+ fileInfo.name = filename;
150
+ fileInfo.size = fileSize;
151
+ }
152
+ }
153
+ }
154
+ listFiles() {
155
+ const files = [];
156
+ for (const [, fileInfo] of this.filesMap) {
157
+ if (fileInfo.name === null)
158
+ continue;
159
+ // Sort data pages by span index
160
+ fileInfo.dataPages.sort((a, b) => a[0] - b[0]);
161
+ // Reconstruct file content
162
+ const chunks = [];
163
+ let totalWritten = 0;
164
+ for (const [, content] of fileInfo.dataPages) {
165
+ const remaining = fileInfo.size - totalWritten;
166
+ if (remaining <= 0)
167
+ break;
168
+ const toWrite = Math.min(content.length, remaining);
169
+ chunks.push(content.slice(0, toWrite));
170
+ totalWritten += toWrite;
171
+ }
172
+ // Concatenate chunks
173
+ const data = new Uint8Array(totalWritten);
174
+ let offset = 0;
175
+ for (const chunk of chunks) {
176
+ data.set(chunk, offset);
177
+ offset += chunk.length;
178
+ }
179
+ files.push({
180
+ name: fileInfo.name,
181
+ size: fileInfo.size,
182
+ data,
183
+ });
184
+ }
185
+ return files;
186
+ }
187
+ readFile(path) {
188
+ const files = this.listFiles();
189
+ const file = files.find((f) => f.name === path || f.name === "/" + path);
190
+ return file ? file.data : null;
191
+ }
192
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * ESP32 Partition Table Parser
3
+ * Based on ESP-IDF partition table format
4
+ */
5
+ export interface Partition {
6
+ name: string;
7
+ type: number;
8
+ subtype: number;
9
+ offset: number;
10
+ size: number;
11
+ flags: number;
12
+ typeName: string;
13
+ subtypeName: string;
14
+ }
15
+ /**
16
+ * Parse the entire partition table
17
+ */
18
+ export declare function parsePartitionTable(data: Uint8Array): Partition[];
19
+ /**
20
+ * Get the default partition table offset
21
+ */
22
+ export declare function getPartitionTableOffset(): number;
23
+ /**
24
+ * Format size in human-readable format
25
+ */
26
+ export declare function formatSize(bytes: number): string;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * ESP32 Partition Table Parser
3
+ * Based on ESP-IDF partition table format
4
+ */
5
+ // Partition types
6
+ const PARTITION_TYPES = {
7
+ 0x00: "app",
8
+ 0x01: "data",
9
+ };
10
+ // App subtypes
11
+ const APP_SUBTYPES = {
12
+ 0x00: "factory",
13
+ 0x10: "ota_0",
14
+ 0x11: "ota_1",
15
+ 0x12: "ota_2",
16
+ 0x13: "ota_3",
17
+ 0x14: "ota_4",
18
+ 0x15: "ota_5",
19
+ 0x16: "ota_6",
20
+ 0x17: "ota_7",
21
+ 0x18: "ota_8",
22
+ 0x19: "ota_9",
23
+ 0x1a: "ota_10",
24
+ 0x1b: "ota_11",
25
+ 0x1c: "ota_12",
26
+ 0x1d: "ota_13",
27
+ 0x1e: "ota_14",
28
+ 0x1f: "ota_15",
29
+ 0x20: "test",
30
+ };
31
+ // Data subtypes
32
+ const DATA_SUBTYPES = {
33
+ 0x00: "ota",
34
+ 0x01: "phy",
35
+ 0x02: "nvs",
36
+ 0x03: "coredump",
37
+ 0x04: "nvs_keys",
38
+ 0x05: "efuse",
39
+ 0x80: "esphttpd",
40
+ 0x81: "fat",
41
+ 0x82: "spiffs",
42
+ };
43
+ const PARTITION_TABLE_OFFSET = 0x8000; // Default partition table offset
44
+ const PARTITION_ENTRY_SIZE = 32;
45
+ const PARTITION_MAGIC = 0x50aa;
46
+ /**
47
+ * Parse a single partition entry from binary data
48
+ */
49
+ function parsePartitionEntry(data) {
50
+ if (data.length < PARTITION_ENTRY_SIZE) {
51
+ return null;
52
+ }
53
+ // Check magic bytes
54
+ const magic = (data[0] | (data[1] << 8)) & 0xffff;
55
+ if (magic !== PARTITION_MAGIC) {
56
+ return null;
57
+ }
58
+ const type = data[2];
59
+ const subtype = data[3];
60
+ const offset = data[4] | (data[5] << 8) | (data[6] << 16) | (data[7] << 24);
61
+ const size = data[8] | (data[9] << 8) | (data[10] << 16) | (data[11] << 24);
62
+ // Name is at offset 12, max 16 bytes, null-terminated
63
+ let name = "";
64
+ for (let i = 12; i < 28; i++) {
65
+ if (data[i] === 0)
66
+ break;
67
+ name += String.fromCharCode(data[i]);
68
+ }
69
+ const flags = data[28] | (data[29] << 8) | (data[30] << 16) | (data[31] << 24);
70
+ // Get type and subtype names
71
+ const typeName = PARTITION_TYPES[type] || `unknown(0x${type.toString(16)})`;
72
+ let subtypeName = "";
73
+ if (type === 0x00) {
74
+ subtypeName = APP_SUBTYPES[subtype] || `unknown(0x${subtype.toString(16)})`;
75
+ }
76
+ else if (type === 0x01) {
77
+ subtypeName =
78
+ DATA_SUBTYPES[subtype] || `unknown(0x${subtype.toString(16)})`;
79
+ }
80
+ else {
81
+ subtypeName = `0x${subtype.toString(16)}`;
82
+ }
83
+ return {
84
+ name,
85
+ type,
86
+ subtype,
87
+ offset,
88
+ size,
89
+ flags,
90
+ typeName,
91
+ subtypeName,
92
+ };
93
+ }
94
+ /**
95
+ * Parse the entire partition table
96
+ */
97
+ export function parsePartitionTable(data) {
98
+ const partitions = [];
99
+ for (let i = 0; i < data.length; i += PARTITION_ENTRY_SIZE) {
100
+ const entryData = data.slice(i, i + PARTITION_ENTRY_SIZE);
101
+ const partition = parsePartitionEntry(entryData);
102
+ if (partition === null) {
103
+ // End of partition table or invalid entry
104
+ break;
105
+ }
106
+ partitions.push(partition);
107
+ }
108
+ return partitions;
109
+ }
110
+ /**
111
+ * Get the default partition table offset
112
+ */
113
+ export function getPartitionTableOffset() {
114
+ return PARTITION_TABLE_OFFSET;
115
+ }
116
+ /**
117
+ * Format size in human-readable format
118
+ */
119
+ export function formatSize(bytes) {
120
+ if (bytes < 1024) {
121
+ return `${bytes} B`;
122
+ }
123
+ else if (bytes < 1024 * 1024) {
124
+ return `${(bytes / 1024).toFixed(2)} KB`;
125
+ }
126
+ else {
127
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
128
+ }
129
+ }
@@ -0,0 +1,2 @@
1
+ export declare const pack: (format: string, ...data: number[]) => number[];
2
+ export declare const unpack: (format: string, bytes: number[]) => number[];