@zwave-js/nvmedit 13.3.0 → 13.4.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/build/cli.js +5 -5
- package/build/cli.js.map +1 -1
- package/build/convert.d.ts +15 -14
- package/build/convert.d.ts.map +1 -1
- package/build/convert.js +647 -389
- package/build/convert.js.map +1 -1
- package/build/index.d.ts +12 -4
- package/build/index.d.ts.map +1 -1
- package/build/index.js +20 -2
- package/build/index.js.map +1 -1
- package/build/index_safe.d.ts +7 -4
- package/build/index_safe.d.ts.map +1 -1
- package/build/index_safe.js +4 -2
- package/build/index_safe.js.map +1 -1
- package/build/lib/NVM3.d.ts +53 -0
- package/build/lib/NVM3.d.ts.map +1 -0
- package/build/lib/NVM3.js +650 -0
- package/build/lib/NVM3.js.map +1 -0
- package/build/lib/NVM500.d.ts +46 -0
- package/build/lib/NVM500.d.ts.map +1 -0
- package/build/lib/NVM500.js +413 -0
- package/build/lib/NVM500.js.map +1 -0
- package/build/lib/common/definitions.d.ts +138 -0
- package/build/lib/common/definitions.d.ts.map +1 -0
- package/build/lib/common/definitions.js +11 -0
- package/build/lib/common/definitions.js.map +1 -0
- package/build/lib/common/routeCache.d.ts +18 -0
- package/build/lib/common/routeCache.d.ts.map +1 -0
- package/build/lib/common/routeCache.js +56 -0
- package/build/lib/common/routeCache.js.map +1 -0
- package/build/lib/common/sucUpdateEntry.d.ts +10 -0
- package/build/lib/common/sucUpdateEntry.d.ts.map +1 -0
- package/build/lib/common/sucUpdateEntry.js +35 -0
- package/build/lib/common/sucUpdateEntry.js.map +1 -0
- package/build/lib/common/utils.d.ts +9 -0
- package/build/lib/common/utils.d.ts.map +1 -0
- package/build/lib/common/utils.js +52 -0
- package/build/lib/common/utils.js.map +1 -0
- package/build/lib/io/BufferedNVMReader.d.ts +21 -0
- package/build/lib/io/BufferedNVMReader.d.ts.map +1 -0
- package/build/lib/io/BufferedNVMReader.js +79 -0
- package/build/lib/io/BufferedNVMReader.js.map +1 -0
- package/build/lib/io/NVMFileIO.d.ts +24 -0
- package/build/lib/io/NVMFileIO.d.ts.map +1 -0
- package/build/lib/io/NVMFileIO.js +86 -0
- package/build/lib/io/NVMFileIO.js.map +1 -0
- package/build/lib/io/NVMMemoryIO.d.ts +20 -0
- package/build/lib/io/NVMMemoryIO.d.ts.map +1 -0
- package/build/lib/io/NVMMemoryIO.js +48 -0
- package/build/lib/io/NVMMemoryIO.js.map +1 -0
- package/build/lib/nvm3/adapter.d.ts +33 -0
- package/build/lib/nvm3/adapter.d.ts.map +1 -0
- package/build/lib/nvm3/adapter.js +903 -0
- package/build/lib/nvm3/adapter.js.map +1 -0
- package/build/lib/nvm3/consts.d.ts.map +1 -0
- package/build/lib/nvm3/consts.js.map +1 -0
- package/build/{files → lib/nvm3/files}/ApplicationCCsFile.d.ts +5 -3
- package/build/lib/nvm3/files/ApplicationCCsFile.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/ApplicationCCsFile.js +4 -3
- package/build/lib/nvm3/files/ApplicationCCsFile.js.map +1 -0
- package/build/{files → lib/nvm3/files}/ApplicationDataFile.d.ts +4 -4
- package/build/lib/nvm3/files/ApplicationDataFile.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/ApplicationDataFile.js +7 -6
- package/build/lib/nvm3/files/ApplicationDataFile.js.map +1 -0
- package/build/{files → lib/nvm3/files}/ApplicationNameFile.d.ts +5 -3
- package/build/lib/nvm3/files/ApplicationNameFile.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/ApplicationNameFile.js +4 -3
- package/build/lib/nvm3/files/ApplicationNameFile.js.map +1 -0
- package/build/{files → lib/nvm3/files}/ApplicationRFConfigFile.d.ts +5 -3
- package/build/lib/nvm3/files/ApplicationRFConfigFile.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/ApplicationRFConfigFile.js +4 -3
- package/build/lib/nvm3/files/ApplicationRFConfigFile.js.map +1 -0
- package/build/{files → lib/nvm3/files}/ApplicationTypeFile.d.ts +5 -3
- package/build/lib/nvm3/files/ApplicationTypeFile.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/ApplicationTypeFile.js +4 -3
- package/build/lib/nvm3/files/ApplicationTypeFile.js.map +1 -0
- package/build/{files → lib/nvm3/files}/ControllerInfoFile.d.ts +5 -3
- package/build/lib/nvm3/files/ControllerInfoFile.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/ControllerInfoFile.js +4 -3
- package/build/lib/nvm3/files/ControllerInfoFile.js.map +1 -0
- package/build/{files → lib/nvm3/files}/NVMFile.d.ts +18 -7
- package/build/lib/nvm3/files/NVMFile.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/NVMFile.js +44 -30
- package/build/lib/nvm3/files/NVMFile.js.map +1 -0
- package/build/{files → lib/nvm3/files}/NodeInfoFiles.d.ts +10 -4
- package/build/lib/nvm3/files/NodeInfoFiles.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/NodeInfoFiles.js +6 -3
- package/build/lib/nvm3/files/NodeInfoFiles.js.map +1 -0
- package/build/{files → lib/nvm3/files}/ProtocolNodeMaskFiles.d.ts +22 -15
- package/build/lib/nvm3/files/ProtocolNodeMaskFiles.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/ProtocolNodeMaskFiles.js +50 -30
- package/build/lib/nvm3/files/ProtocolNodeMaskFiles.js.map +1 -0
- package/build/{files → lib/nvm3/files}/RouteCacheFiles.d.ts +8 -17
- package/build/lib/nvm3/files/RouteCacheFiles.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/RouteCacheFiles.js +16 -64
- package/build/lib/nvm3/files/RouteCacheFiles.js.map +1 -0
- package/build/{files → lib/nvm3/files}/SUCUpdateEntriesFile.d.ts +10 -13
- package/build/lib/nvm3/files/SUCUpdateEntriesFile.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/SUCUpdateEntriesFile.js +15 -42
- package/build/lib/nvm3/files/SUCUpdateEntriesFile.js.map +1 -0
- package/build/{files → lib/nvm3/files}/VersionFiles.d.ts +7 -5
- package/build/lib/nvm3/files/VersionFiles.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/VersionFiles.js +10 -7
- package/build/lib/nvm3/files/VersionFiles.js.map +1 -0
- package/build/{files → lib/nvm3/files}/index.d.ts +1 -0
- package/build/lib/nvm3/files/index.d.ts.map +1 -0
- package/build/{files → lib/nvm3/files}/index.js +1 -0
- package/build/lib/nvm3/files/index.js.map +1 -0
- package/build/lib/nvm3/object.d.ts +29 -0
- package/build/lib/nvm3/object.d.ts.map +1 -0
- package/build/lib/nvm3/object.js +118 -0
- package/build/lib/nvm3/object.js.map +1 -0
- package/build/{nvm3 → lib/nvm3}/page.d.ts +1 -5
- package/build/lib/nvm3/page.d.ts.map +1 -0
- package/build/lib/nvm3/page.js +37 -0
- package/build/lib/nvm3/page.js.map +1 -0
- package/build/{nvm3 → lib/nvm3}/utils.d.ts +2 -4
- package/build/lib/nvm3/utils.d.ts.map +1 -0
- package/build/lib/nvm3/utils.js +143 -0
- package/build/lib/nvm3/utils.js.map +1 -0
- package/build/lib/nvm500/EntryParsers.d.ts.map +1 -0
- package/build/lib/nvm500/EntryParsers.js.map +1 -0
- package/build/lib/nvm500/adapter.d.ts +22 -0
- package/build/lib/nvm500/adapter.d.ts.map +1 -0
- package/build/lib/nvm500/adapter.js +371 -0
- package/build/lib/nvm500/adapter.js.map +1 -0
- package/build/lib/nvm500/impls/Bridge_6_6x.d.ts +3 -0
- package/build/lib/nvm500/impls/Bridge_6_6x.d.ts.map +1 -0
- package/build/{nvm500/parsers → lib/nvm500/impls}/Bridge_6_6x.js +1 -1
- package/build/lib/nvm500/impls/Bridge_6_6x.js.map +1 -0
- package/build/lib/nvm500/impls/Bridge_6_7x.d.ts +3 -0
- package/build/lib/nvm500/impls/Bridge_6_7x.d.ts.map +1 -0
- package/build/{nvm500/parsers → lib/nvm500/impls}/Bridge_6_7x.js +1 -1
- package/build/lib/nvm500/impls/Bridge_6_7x.js.map +1 -0
- package/build/lib/nvm500/impls/Bridge_6_8x.d.ts +3 -0
- package/build/lib/nvm500/impls/Bridge_6_8x.d.ts.map +1 -0
- package/build/{nvm500/parsers → lib/nvm500/impls}/Bridge_6_8x.js +1 -1
- package/build/lib/nvm500/impls/Bridge_6_8x.js.map +1 -0
- package/build/lib/nvm500/impls/Static_6_6x.d.ts +3 -0
- package/build/lib/nvm500/impls/Static_6_6x.d.ts.map +1 -0
- package/build/{nvm500/parsers → lib/nvm500/impls}/Static_6_6x.js +1 -1
- package/build/lib/nvm500/impls/Static_6_6x.js.map +1 -0
- package/build/lib/nvm500/impls/Static_6_7x.d.ts +3 -0
- package/build/lib/nvm500/impls/Static_6_7x.d.ts.map +1 -0
- package/build/{nvm500/parsers → lib/nvm500/impls}/Static_6_7x.js +1 -1
- package/build/lib/nvm500/impls/Static_6_7x.js.map +1 -0
- package/build/lib/nvm500/impls/Static_6_8x.d.ts +3 -0
- package/build/lib/nvm500/impls/Static_6_8x.d.ts.map +1 -0
- package/build/{nvm500/parsers → lib/nvm500/impls}/Static_6_8x.js +1 -1
- package/build/lib/nvm500/impls/Static_6_8x.js.map +1 -0
- package/build/lib/nvm500/impls/index.d.ts +2 -0
- package/build/lib/nvm500/impls/index.d.ts.map +1 -0
- package/build/lib/nvm500/impls/index.js +18 -0
- package/build/lib/nvm500/impls/index.js.map +1 -0
- package/build/{nvm500 → lib/nvm500}/shared.d.ts +19 -2
- package/build/lib/nvm500/shared.d.ts.map +1 -0
- package/build/{nvm500 → lib/nvm500}/shared.js +19 -1
- package/build/lib/nvm500/shared.js.map +1 -0
- package/build/nvm500/NVMParser.d.ts +5 -36
- package/build/nvm500/NVMParser.d.ts.map +1 -1
- package/build/nvm500/NVMParser.js +0 -524
- package/build/nvm500/NVMParser.js.map +1 -1
- package/package.json +2 -2
- package/build/files/ApplicationCCsFile.d.ts.map +0 -1
- package/build/files/ApplicationCCsFile.js.map +0 -1
- package/build/files/ApplicationDataFile.d.ts.map +0 -1
- package/build/files/ApplicationDataFile.js.map +0 -1
- package/build/files/ApplicationNameFile.d.ts.map +0 -1
- package/build/files/ApplicationNameFile.js.map +0 -1
- package/build/files/ApplicationRFConfigFile.d.ts.map +0 -1
- package/build/files/ApplicationRFConfigFile.js.map +0 -1
- package/build/files/ApplicationTypeFile.d.ts.map +0 -1
- package/build/files/ApplicationTypeFile.js.map +0 -1
- package/build/files/ControllerInfoFile.d.ts.map +0 -1
- package/build/files/ControllerInfoFile.js.map +0 -1
- package/build/files/NVMFile.d.ts.map +0 -1
- package/build/files/NVMFile.js.map +0 -1
- package/build/files/NodeInfoFiles.d.ts.map +0 -1
- package/build/files/NodeInfoFiles.js.map +0 -1
- package/build/files/ProtocolNodeMaskFiles.d.ts.map +0 -1
- package/build/files/ProtocolNodeMaskFiles.js.map +0 -1
- package/build/files/RouteCacheFiles.d.ts.map +0 -1
- package/build/files/RouteCacheFiles.js.map +0 -1
- package/build/files/SUCUpdateEntriesFile.d.ts.map +0 -1
- package/build/files/SUCUpdateEntriesFile.js.map +0 -1
- package/build/files/VersionFiles.d.ts.map +0 -1
- package/build/files/VersionFiles.js.map +0 -1
- package/build/files/index.d.ts.map +0 -1
- package/build/files/index.js.map +0 -1
- package/build/nvm3/consts.d.ts.map +0 -1
- package/build/nvm3/consts.js.map +0 -1
- package/build/nvm3/nvm.d.ts +0 -31
- package/build/nvm3/nvm.d.ts.map +0 -1
- package/build/nvm3/nvm.js +0 -184
- package/build/nvm3/nvm.js.map +0 -1
- package/build/nvm3/object.d.ts +0 -25
- package/build/nvm3/object.d.ts.map +0 -1
- package/build/nvm3/object.js +0 -197
- package/build/nvm3/object.js.map +0 -1
- package/build/nvm3/page.d.ts.map +0 -1
- package/build/nvm3/page.js +0 -129
- package/build/nvm3/page.js.map +0 -1
- package/build/nvm3/utils.d.ts.map +0 -1
- package/build/nvm3/utils.js +0 -103
- package/build/nvm3/utils.js.map +0 -1
- package/build/nvm500/EntryParsers.d.ts.map +0 -1
- package/build/nvm500/EntryParsers.js.map +0 -1
- package/build/nvm500/parsers/Bridge_6_6x.d.ts +0 -3
- package/build/nvm500/parsers/Bridge_6_6x.d.ts.map +0 -1
- package/build/nvm500/parsers/Bridge_6_6x.js.map +0 -1
- package/build/nvm500/parsers/Bridge_6_7x.d.ts +0 -3
- package/build/nvm500/parsers/Bridge_6_7x.d.ts.map +0 -1
- package/build/nvm500/parsers/Bridge_6_7x.js.map +0 -1
- package/build/nvm500/parsers/Bridge_6_8x.d.ts +0 -3
- package/build/nvm500/parsers/Bridge_6_8x.d.ts.map +0 -1
- package/build/nvm500/parsers/Bridge_6_8x.js.map +0 -1
- package/build/nvm500/parsers/Static_6_6x.d.ts +0 -3
- package/build/nvm500/parsers/Static_6_6x.d.ts.map +0 -1
- package/build/nvm500/parsers/Static_6_6x.js.map +0 -1
- package/build/nvm500/parsers/Static_6_7x.d.ts +0 -3
- package/build/nvm500/parsers/Static_6_7x.d.ts.map +0 -1
- package/build/nvm500/parsers/Static_6_7x.js.map +0 -1
- package/build/nvm500/parsers/Static_6_8x.d.ts +0 -3
- package/build/nvm500/parsers/Static_6_8x.d.ts.map +0 -1
- package/build/nvm500/parsers/Static_6_8x.js.map +0 -1
- package/build/nvm500/shared.d.ts.map +0 -1
- package/build/nvm500/shared.js.map +0 -1
- package/build/shared.d.ts +0 -2
- package/build/shared.d.ts.map +0 -1
- package/build/shared.js +0 -3
- package/build/shared.js.map +0 -1
- /package/build/{nvm3 → lib/nvm3}/consts.d.ts +0 -0
- /package/build/{nvm3 → lib/nvm3}/consts.js +0 -0
- /package/build/{nvm500 → lib/nvm500}/EntryParsers.d.ts +0 -0
- /package/build/{nvm500 → lib/nvm500}/EntryParsers.js +0 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NVM3 = void 0;
|
|
4
|
+
const core_1 = require("@zwave-js/core");
|
|
5
|
+
const shared_1 = require("@zwave-js/shared");
|
|
6
|
+
const definitions_1 = require("./common/definitions");
|
|
7
|
+
const utils_1 = require("./common/utils");
|
|
8
|
+
const consts_1 = require("./nvm3/consts");
|
|
9
|
+
const files_1 = require("./nvm3/files");
|
|
10
|
+
const object_1 = require("./nvm3/object");
|
|
11
|
+
const page_1 = require("./nvm3/page");
|
|
12
|
+
const utils_2 = require("./nvm3/utils");
|
|
13
|
+
class NVM3 {
|
|
14
|
+
constructor(io) {
|
|
15
|
+
this._io = io;
|
|
16
|
+
}
|
|
17
|
+
_io;
|
|
18
|
+
_access = definitions_1.NVMAccess.None;
|
|
19
|
+
_info;
|
|
20
|
+
get info() {
|
|
21
|
+
return this._info;
|
|
22
|
+
}
|
|
23
|
+
async ensureReadable() {
|
|
24
|
+
if (this._access === definitions_1.NVMAccess.Read
|
|
25
|
+
|| this._access === definitions_1.NVMAccess.ReadWrite) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (this._access === definitions_1.NVMAccess.Write) {
|
|
29
|
+
await this._io.close();
|
|
30
|
+
}
|
|
31
|
+
this._access = await this._io.open(definitions_1.NVMAccess.Read);
|
|
32
|
+
}
|
|
33
|
+
async ensureWritable() {
|
|
34
|
+
if (this._access === definitions_1.NVMAccess.Write
|
|
35
|
+
|| this._access === definitions_1.NVMAccess.ReadWrite) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (this._access === definitions_1.NVMAccess.Read) {
|
|
39
|
+
await this._io.close();
|
|
40
|
+
}
|
|
41
|
+
this._access = await this._io.open(definitions_1.NVMAccess.Write);
|
|
42
|
+
}
|
|
43
|
+
async init() {
|
|
44
|
+
await this.ensureReadable();
|
|
45
|
+
let pageOffset = 0;
|
|
46
|
+
// Determine NVM size, scan pages
|
|
47
|
+
const pages = [];
|
|
48
|
+
let isSharedFileSystem = false;
|
|
49
|
+
while (pageOffset < this._io.size) {
|
|
50
|
+
// console.debug(
|
|
51
|
+
// `NVM3 init() - reading page header at offset ${
|
|
52
|
+
// num2hex(pageOffset)
|
|
53
|
+
// }`,
|
|
54
|
+
// );
|
|
55
|
+
const header = await readPageHeader(this._io, pageOffset);
|
|
56
|
+
pages.push({
|
|
57
|
+
...header,
|
|
58
|
+
objects: [],
|
|
59
|
+
});
|
|
60
|
+
pageOffset += header.pageSize;
|
|
61
|
+
}
|
|
62
|
+
// Scan each page for objects
|
|
63
|
+
for (const page of pages) {
|
|
64
|
+
// Scan objects in this page
|
|
65
|
+
let objectOffset = page.offset + consts_1.NVM3_PAGE_HEADER_SIZE;
|
|
66
|
+
const nextPageOffset = page.offset + page.pageSize;
|
|
67
|
+
while (objectOffset < nextPageOffset) {
|
|
68
|
+
// console.debug(
|
|
69
|
+
// `NVM3 init() - reading object header. page offset ${
|
|
70
|
+
// num2hex(page.offset)
|
|
71
|
+
// }, object offset ${num2hex(objectOffset)}`,
|
|
72
|
+
// );
|
|
73
|
+
const objectHeader = await readObjectHeader(this._io, objectOffset);
|
|
74
|
+
if (objectHeader) {
|
|
75
|
+
page.objects.push(objectHeader);
|
|
76
|
+
objectOffset += objectHeader.alignedSize;
|
|
77
|
+
// Detect the 800 series shared protocol & application NVM file system
|
|
78
|
+
// by looking for the 800 series application version file
|
|
79
|
+
if (objectHeader.key === files_1.ApplicationVersionFile800ID) {
|
|
80
|
+
isSharedFileSystem = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Reached the end of the data in this page
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// By convention, we only use the applicationPages in that case
|
|
90
|
+
let applicationPages;
|
|
91
|
+
let protocolPages;
|
|
92
|
+
if (isSharedFileSystem) {
|
|
93
|
+
applicationPages = pages;
|
|
94
|
+
protocolPages = [];
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
applicationPages = pages.filter((p) => p.offset < consts_1.ZWAVE_APPLICATION_NVM_SIZE);
|
|
98
|
+
protocolPages = pages.filter((p) => p.offset >= consts_1.ZWAVE_APPLICATION_NVM_SIZE);
|
|
99
|
+
}
|
|
100
|
+
// NVM3 layouts pages in a ring buffer. Pages are written from front to back, then occupied pages
|
|
101
|
+
// are erased and overwritten. Pages at the start of the memory section may have an erase count that's 1 higher
|
|
102
|
+
// than the pages at the end.
|
|
103
|
+
const pageInfoToSectionInfo = (pages) => {
|
|
104
|
+
// Find the current page, which is either:
|
|
105
|
+
// - The last page with the high erase count that contains an object
|
|
106
|
+
const maxEraseCount = Math.max(...pages.map((p) => p.eraseCount));
|
|
107
|
+
let currentPageIndex = pages.findLastIndex((p) => p.eraseCount === maxEraseCount && p.objects.length > 0);
|
|
108
|
+
// - or if there is none, the last page with the lower erase count that contains an object
|
|
109
|
+
if (currentPageIndex === -1) {
|
|
110
|
+
currentPageIndex = pages.findLastIndex((p) => p.objects.length > 0);
|
|
111
|
+
}
|
|
112
|
+
// - Or if no objects exist at all, the beginning of the section
|
|
113
|
+
if (currentPageIndex === -1)
|
|
114
|
+
currentPageIndex = 0;
|
|
115
|
+
// Find the next free byte of the current page
|
|
116
|
+
const currentPage = pages[currentPageIndex];
|
|
117
|
+
let offset = consts_1.NVM3_PAGE_HEADER_SIZE;
|
|
118
|
+
for (const object of currentPage.objects) {
|
|
119
|
+
offset += object.alignedSize;
|
|
120
|
+
}
|
|
121
|
+
const objectLocations = new Map();
|
|
122
|
+
for (let i = 0; i < pages.length; i++) {
|
|
123
|
+
const page = pages[i];
|
|
124
|
+
for (const object of page.objects) {
|
|
125
|
+
const location = objectLocations.get(object.key);
|
|
126
|
+
if (location == undefined) {
|
|
127
|
+
// Object seen for the first time, remember the page it is in
|
|
128
|
+
objectLocations.set(object.key, i);
|
|
129
|
+
}
|
|
130
|
+
else if ((object.fragmentType === consts_1.FragmentType.None
|
|
131
|
+
|| object.fragmentType === consts_1.FragmentType.First)
|
|
132
|
+
&& (page.eraseCount >= pages[location].eraseCount)) {
|
|
133
|
+
// Object was seen before. Only remember it if it is the only
|
|
134
|
+
// or first fragment and the object appears in a later location
|
|
135
|
+
// of the ring buffer
|
|
136
|
+
objectLocations.set(object.key, i);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
pages,
|
|
142
|
+
offsetInPage: offset,
|
|
143
|
+
currentPage: currentPageIndex,
|
|
144
|
+
objectLocations,
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
if (isSharedFileSystem) {
|
|
148
|
+
this._info = {
|
|
149
|
+
isSharedFileSystem: true,
|
|
150
|
+
sections: {
|
|
151
|
+
all: pageInfoToSectionInfo(applicationPages),
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
this._info = {
|
|
157
|
+
isSharedFileSystem: false,
|
|
158
|
+
sections: {
|
|
159
|
+
application: pageInfoToSectionInfo(applicationPages),
|
|
160
|
+
protocol: pageInfoToSectionInfo(protocolPages),
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return this._info;
|
|
165
|
+
}
|
|
166
|
+
getNVMSectionForFile(fileId) {
|
|
167
|
+
// Determine which ring buffer to read in
|
|
168
|
+
return this._info.isSharedFileSystem
|
|
169
|
+
? this._info.sections.all
|
|
170
|
+
: this._info.sections[(0, files_1.getNVMSectionByFileID)(fileId)];
|
|
171
|
+
}
|
|
172
|
+
async has(fileId) {
|
|
173
|
+
this._info ??= await this.init();
|
|
174
|
+
// Determine which ring buffer to read in
|
|
175
|
+
const section = this.getNVMSectionForFile(fileId);
|
|
176
|
+
return section.objectLocations.has(fileId);
|
|
177
|
+
}
|
|
178
|
+
readObjectData(object) {
|
|
179
|
+
return (0, utils_1.nvmReadBuffer)(this._io, object.offset + object.headerSize, object.fragmentSize);
|
|
180
|
+
}
|
|
181
|
+
async get(fileId) {
|
|
182
|
+
this._info ??= await this.init();
|
|
183
|
+
// Determine which ring buffer to read in
|
|
184
|
+
const section = this.getNVMSectionForFile(fileId);
|
|
185
|
+
const pages = section.pages;
|
|
186
|
+
// TODO: There should be no need for scanning, since we know the object locations after init().
|
|
187
|
+
// Start scanning backwards through the pages ring buffer, starting with the current page
|
|
188
|
+
let parts;
|
|
189
|
+
let complete = false;
|
|
190
|
+
let objType;
|
|
191
|
+
const resetFragments = () => {
|
|
192
|
+
// if (parts?.length) {
|
|
193
|
+
// console.debug("Resetting fragmented object");
|
|
194
|
+
// }
|
|
195
|
+
parts = undefined;
|
|
196
|
+
complete = false;
|
|
197
|
+
};
|
|
198
|
+
pages: for (let offset = 0; offset < pages.length; offset++) {
|
|
199
|
+
const index = (section.currentPage - offset + pages.length)
|
|
200
|
+
% pages.length;
|
|
201
|
+
const page = pages[index];
|
|
202
|
+
// console.debug(
|
|
203
|
+
// `NVM3.get(${fileId}): scanning page ${index} at offset ${
|
|
204
|
+
// num2hex(page.offset)
|
|
205
|
+
// }`,
|
|
206
|
+
// );
|
|
207
|
+
// Scan objects in this page, read backwards.
|
|
208
|
+
// The last non-deleted object wins
|
|
209
|
+
objects: for (let j = page.objects.length - 1; j >= 0; j--) {
|
|
210
|
+
const object = page.objects[j];
|
|
211
|
+
const readObject = () => this.readObjectData(object);
|
|
212
|
+
if (object.key !== fileId) {
|
|
213
|
+
// Reset any fragmented objects when encountering a different key
|
|
214
|
+
resetFragments();
|
|
215
|
+
continue objects;
|
|
216
|
+
}
|
|
217
|
+
if (object.type === consts_1.ObjectType.Deleted) {
|
|
218
|
+
// Last action for this object was a deletion. There is no data.
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
else if (object.fragmentType === consts_1.FragmentType.None) {
|
|
222
|
+
// console.debug(
|
|
223
|
+
// `NVM3.get(${fileId}): found complete object - header offset ${
|
|
224
|
+
// num2hex(object.offset)
|
|
225
|
+
// }, content offset ${
|
|
226
|
+
// num2hex(object.offset + object.headerSize)
|
|
227
|
+
// }, length ${object.fragmentSize}`,
|
|
228
|
+
// );
|
|
229
|
+
// This is a complete object
|
|
230
|
+
parts = [await readObject()];
|
|
231
|
+
objType = object.type;
|
|
232
|
+
complete = true;
|
|
233
|
+
break pages;
|
|
234
|
+
}
|
|
235
|
+
else if (object.fragmentType === consts_1.FragmentType.Last) {
|
|
236
|
+
// console.debug(
|
|
237
|
+
// `NVM3.get(${fileId}): found LAST fragment - header offset ${
|
|
238
|
+
// num2hex(object.offset)
|
|
239
|
+
// }, content offset ${
|
|
240
|
+
// num2hex(object.offset + object.headerSize)
|
|
241
|
+
// }, length ${object.fragmentSize}`,
|
|
242
|
+
// );
|
|
243
|
+
parts = [await readObject()];
|
|
244
|
+
objType = object.type;
|
|
245
|
+
complete = false;
|
|
246
|
+
}
|
|
247
|
+
else if (object.fragmentType === consts_1.FragmentType.Next) {
|
|
248
|
+
if (parts?.length && objType === object.type) {
|
|
249
|
+
// console.debug(
|
|
250
|
+
// `NVM3.get(${fileId}): found NEXT fragment - header offset ${
|
|
251
|
+
// num2hex(object.offset)
|
|
252
|
+
// }, content offset ${
|
|
253
|
+
// num2hex(object.offset + object.headerSize)
|
|
254
|
+
// }, length ${object.fragmentSize}`,
|
|
255
|
+
// );
|
|
256
|
+
parts.unshift(await readObject());
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
// This shouldn't be here
|
|
260
|
+
resetFragments();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
else if (object.fragmentType === consts_1.FragmentType.First) {
|
|
264
|
+
if (parts?.length && objType === object.type) {
|
|
265
|
+
// console.debug(
|
|
266
|
+
// `NVM3.get(${fileId}): found FIRST fragment - header offset ${
|
|
267
|
+
// num2hex(object.offset)
|
|
268
|
+
// }, content offset ${
|
|
269
|
+
// num2hex(object.offset + object.headerSize)
|
|
270
|
+
// }, length ${object.fragmentSize}`,
|
|
271
|
+
// );
|
|
272
|
+
parts.unshift(await readObject());
|
|
273
|
+
complete = true;
|
|
274
|
+
break pages;
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
// This shouldn't be here
|
|
278
|
+
resetFragments();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (!parts?.length || !complete || objType == undefined)
|
|
284
|
+
return;
|
|
285
|
+
return Buffer.concat(parts);
|
|
286
|
+
}
|
|
287
|
+
async writeObjects(objects) {
|
|
288
|
+
const section = this.getNVMSectionForFile(objects[0].key);
|
|
289
|
+
let page = section.pages[section.currentPage];
|
|
290
|
+
let remainingSpace = page.pageSize
|
|
291
|
+
- consts_1.NVM3_PAGE_HEADER_SIZE
|
|
292
|
+
- section.offsetInPage;
|
|
293
|
+
// TODO: See if we can avoid double writes on a page change
|
|
294
|
+
/** Moves to the next page and erases it if necessary */
|
|
295
|
+
const nextPage = async () => {
|
|
296
|
+
section.currentPage = (section.currentPage + 1)
|
|
297
|
+
% section.pages.length;
|
|
298
|
+
page = section.pages[section.currentPage];
|
|
299
|
+
// Find headers of objects that need to be preserved
|
|
300
|
+
const toPreserve = [...section.objectLocations].filter(([, pageIndex]) => pageIndex === section.currentPage)
|
|
301
|
+
.map(([fileID]) => page.objects.findLast((h) => h.key === fileID))
|
|
302
|
+
.filter((h) => h != undefined)
|
|
303
|
+
.filter((h) => h.type !== consts_1.ObjectType.Deleted);
|
|
304
|
+
// And add the objects to the TODO list
|
|
305
|
+
for (const header of toPreserve) {
|
|
306
|
+
const data = await this.get(header.key);
|
|
307
|
+
console.error(`Need to preserve object ${(0, shared_1.num2hex)(header.key)}
|
|
308
|
+
page index: ${section.currentPage}
|
|
309
|
+
object type: ${(0, shared_1.getEnumMemberName)(consts_1.ObjectType, header.type)}
|
|
310
|
+
data: ${data != undefined ? `${data.length} bytes` : "(no data)"}`);
|
|
311
|
+
objects.push({
|
|
312
|
+
key: header.key,
|
|
313
|
+
type: header.type,
|
|
314
|
+
fragmentType: consts_1.FragmentType.None,
|
|
315
|
+
data,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
if (page.objects.length > 0) {
|
|
319
|
+
// The page needs to be erased
|
|
320
|
+
page.eraseCount++;
|
|
321
|
+
page.objects = [];
|
|
322
|
+
const pageHeaderBuffer = (0, page_1.serializePageHeader)(page);
|
|
323
|
+
const pageBuffer = Buffer.alloc(page.pageSize, 0xff);
|
|
324
|
+
pageHeaderBuffer.copy(pageBuffer, 0);
|
|
325
|
+
await (0, utils_1.nvmWriteBuffer)(this._io, page.offset, pageBuffer);
|
|
326
|
+
}
|
|
327
|
+
section.offsetInPage = consts_1.NVM3_PAGE_HEADER_SIZE;
|
|
328
|
+
remainingSpace = page.pageSize - consts_1.NVM3_PAGE_HEADER_SIZE;
|
|
329
|
+
};
|
|
330
|
+
// Go through the list of objects and write all of them to the NVM
|
|
331
|
+
for (const object of objects) {
|
|
332
|
+
const isLargeObject = object.type === consts_1.ObjectType.DataLarge
|
|
333
|
+
|| object.type === consts_1.ObjectType.CounterLarge;
|
|
334
|
+
let fragments;
|
|
335
|
+
if (isLargeObject) {
|
|
336
|
+
// Large objects may be fragmented
|
|
337
|
+
// We need to start a new page, if the remaining space is not enough for
|
|
338
|
+
// the object header plus additional data
|
|
339
|
+
if (remainingSpace <= consts_1.NVM3_OBJ_HEADER_SIZE_LARGE) {
|
|
340
|
+
await nextPage();
|
|
341
|
+
}
|
|
342
|
+
fragments = (0, object_1.fragmentLargeObject)(object, remainingSpace, page.pageSize - consts_1.NVM3_PAGE_HEADER_SIZE);
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
// Small objects cannot be fragmented. If they don't fit,
|
|
346
|
+
// they need to go on the next page.
|
|
347
|
+
const requiredSpace = (0, object_1.getRequiredSpace)(object);
|
|
348
|
+
if (requiredSpace > remainingSpace) {
|
|
349
|
+
await nextPage();
|
|
350
|
+
}
|
|
351
|
+
fragments = [object];
|
|
352
|
+
}
|
|
353
|
+
// Write each fragment to the NVM. If there are multiple fragments,
|
|
354
|
+
// each one but the first needs to be written at the beginning of a new page
|
|
355
|
+
for (let i = 0; i < fragments.length; i++) {
|
|
356
|
+
if (i > 0)
|
|
357
|
+
await nextPage();
|
|
358
|
+
const fragment = fragments[i];
|
|
359
|
+
const objBuffer = (0, object_1.serializeObject)(fragment);
|
|
360
|
+
const objOffset = page.offset + section.offsetInPage;
|
|
361
|
+
await this._io.write(objOffset, objBuffer);
|
|
362
|
+
const requiredSpace = (0, object_1.getRequiredSpace)(fragment);
|
|
363
|
+
section.offsetInPage += requiredSpace;
|
|
364
|
+
remainingSpace -= requiredSpace;
|
|
365
|
+
// Remember which objects exist in this page
|
|
366
|
+
page.objects.push((0, object_1.getObjectHeader)(object, objOffset));
|
|
367
|
+
// And remember where this object lives
|
|
368
|
+
if (object.type === consts_1.ObjectType.Deleted) {
|
|
369
|
+
section.objectLocations.delete(object.key);
|
|
370
|
+
}
|
|
371
|
+
else if (fragment.fragmentType === consts_1.FragmentType.None
|
|
372
|
+
|| fragment.fragmentType === consts_1.FragmentType.First) {
|
|
373
|
+
section.objectLocations.set(fragment.key, section.currentPage);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
async set(property, value) {
|
|
379
|
+
if (!this._info)
|
|
380
|
+
await this.init();
|
|
381
|
+
await this.ensureWritable();
|
|
382
|
+
await this.writeObjects([{
|
|
383
|
+
key: property,
|
|
384
|
+
type: value.length <= consts_1.NVM3_MAX_OBJ_SIZE_SMALL
|
|
385
|
+
? consts_1.ObjectType.DataSmall
|
|
386
|
+
: consts_1.ObjectType.DataLarge,
|
|
387
|
+
// writeObject deals with fragmentation
|
|
388
|
+
fragmentType: consts_1.FragmentType.None,
|
|
389
|
+
data: value,
|
|
390
|
+
}]);
|
|
391
|
+
}
|
|
392
|
+
/** Writes multiple values to the NVM at once. `null` / `undefined` cause the value to be deleted */
|
|
393
|
+
async setMany(values) {
|
|
394
|
+
if (!this._info)
|
|
395
|
+
await this.init();
|
|
396
|
+
await this.ensureWritable();
|
|
397
|
+
// Group objects by their NVM section
|
|
398
|
+
const objectsBySection = new Map();
|
|
399
|
+
for (const [key, value] of values) {
|
|
400
|
+
const sectionOffset = this.getNVMSectionForFile(key).pages[0].offset;
|
|
401
|
+
if (!objectsBySection.has(sectionOffset)) {
|
|
402
|
+
objectsBySection.set(sectionOffset, []);
|
|
403
|
+
}
|
|
404
|
+
objectsBySection.get(sectionOffset).push([key, value]);
|
|
405
|
+
}
|
|
406
|
+
// And call writeObjects for each group
|
|
407
|
+
for (const objectGroups of objectsBySection.values()) {
|
|
408
|
+
await this.writeObjects(objectGroups.map(([key, value]) => (value
|
|
409
|
+
? {
|
|
410
|
+
key,
|
|
411
|
+
type: value.length <= consts_1.NVM3_MAX_OBJ_SIZE_SMALL
|
|
412
|
+
? consts_1.ObjectType.DataSmall
|
|
413
|
+
: consts_1.ObjectType.DataLarge,
|
|
414
|
+
// writeObject deals with fragmentation
|
|
415
|
+
fragmentType: consts_1.FragmentType.None,
|
|
416
|
+
data: value,
|
|
417
|
+
}
|
|
418
|
+
: {
|
|
419
|
+
key,
|
|
420
|
+
type: consts_1.ObjectType.Deleted,
|
|
421
|
+
fragmentType: consts_1.FragmentType.None,
|
|
422
|
+
})));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
async delete(property) {
|
|
426
|
+
if (!this._info)
|
|
427
|
+
await this.init();
|
|
428
|
+
await this.ensureWritable();
|
|
429
|
+
await this.writeObjects([{
|
|
430
|
+
key: property,
|
|
431
|
+
type: consts_1.ObjectType.Deleted,
|
|
432
|
+
fragmentType: consts_1.FragmentType.None,
|
|
433
|
+
}]);
|
|
434
|
+
}
|
|
435
|
+
async erase(options) {
|
|
436
|
+
const { deviceFamily = 2047, writeSize = consts_1.PageWriteSize.WRITE_SIZE_16, memoryMapped = true, sharedFileSystem = false, } = options ?? {};
|
|
437
|
+
const maxPageSize = sharedFileSystem
|
|
438
|
+
? consts_1.FLASH_MAX_PAGE_SIZE_800
|
|
439
|
+
: consts_1.FLASH_MAX_PAGE_SIZE_700;
|
|
440
|
+
const pageSize = Math.min(options?.pageSize ?? maxPageSize, maxPageSize);
|
|
441
|
+
// Make sure we won't be writing incomplete pages
|
|
442
|
+
if (this._io.size % pageSize !== 0) {
|
|
443
|
+
throw new core_1.ZWaveError(`Invalid page size. NVM size ${this._io.size} must be a multiple of the page size ${pageSize}.`, core_1.ZWaveErrorCodes.Argument_Invalid);
|
|
444
|
+
}
|
|
445
|
+
else if (!sharedFileSystem && consts_1.ZWAVE_APPLICATION_NVM_SIZE % pageSize !== 0) {
|
|
446
|
+
throw new core_1.ZWaveError(`Invalid page size. The application NVM size ${consts_1.ZWAVE_APPLICATION_NVM_SIZE} must be a multiple of the page size ${pageSize}.`, core_1.ZWaveErrorCodes.Argument_Invalid);
|
|
447
|
+
}
|
|
448
|
+
else if (!sharedFileSystem
|
|
449
|
+
&& (this._io.size - consts_1.ZWAVE_APPLICATION_NVM_SIZE) % pageSize !== 0) {
|
|
450
|
+
throw new core_1.ZWaveError(`Invalid page size. The protocol NVM size ${this._io.size
|
|
451
|
+
- consts_1.ZWAVE_APPLICATION_NVM_SIZE} must be a multiple of the page size ${pageSize}.`, core_1.ZWaveErrorCodes.Argument_Invalid);
|
|
452
|
+
}
|
|
453
|
+
await this.ensureWritable();
|
|
454
|
+
// Create empty pages, write them to the NVM
|
|
455
|
+
const applicationPages = [];
|
|
456
|
+
const protocolPages = [];
|
|
457
|
+
const numPages = this._io.size / pageSize;
|
|
458
|
+
for (let i = 0; i < numPages; i++) {
|
|
459
|
+
const offset = i * pageSize;
|
|
460
|
+
const pageBuffer = Buffer.alloc(pageSize, 0xff);
|
|
461
|
+
const pageHeader = {
|
|
462
|
+
offset,
|
|
463
|
+
version: 0x01,
|
|
464
|
+
eraseCount: 0,
|
|
465
|
+
encrypted: false,
|
|
466
|
+
deviceFamily,
|
|
467
|
+
memoryMapped,
|
|
468
|
+
pageSize,
|
|
469
|
+
status: consts_1.PageStatus.OK,
|
|
470
|
+
writeSize,
|
|
471
|
+
};
|
|
472
|
+
(0, page_1.serializePageHeader)(pageHeader).copy(pageBuffer, 0);
|
|
473
|
+
await (0, utils_1.nvmWriteBuffer)(this._io, offset, pageBuffer);
|
|
474
|
+
if (sharedFileSystem || offset < consts_1.ZWAVE_APPLICATION_NVM_SIZE) {
|
|
475
|
+
applicationPages.push({ ...pageHeader, objects: [] });
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
protocolPages.push({ ...pageHeader, objects: [] });
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
// Remember the pages we just created for further use
|
|
482
|
+
this._info = sharedFileSystem
|
|
483
|
+
? {
|
|
484
|
+
isSharedFileSystem: true,
|
|
485
|
+
sections: {
|
|
486
|
+
all: {
|
|
487
|
+
currentPage: 0,
|
|
488
|
+
objectLocations: new Map(),
|
|
489
|
+
offsetInPage: consts_1.NVM3_PAGE_HEADER_SIZE,
|
|
490
|
+
pages: applicationPages,
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
}
|
|
494
|
+
: {
|
|
495
|
+
isSharedFileSystem: false,
|
|
496
|
+
sections: {
|
|
497
|
+
application: {
|
|
498
|
+
currentPage: 0,
|
|
499
|
+
objectLocations: new Map(),
|
|
500
|
+
offsetInPage: consts_1.NVM3_PAGE_HEADER_SIZE,
|
|
501
|
+
pages: applicationPages,
|
|
502
|
+
},
|
|
503
|
+
protocol: {
|
|
504
|
+
currentPage: 0,
|
|
505
|
+
objectLocations: new Map(),
|
|
506
|
+
offsetInPage: consts_1.NVM3_PAGE_HEADER_SIZE,
|
|
507
|
+
pages: protocolPages,
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
exports.NVM3 = NVM3;
|
|
514
|
+
async function readPageHeader(io, offset) {
|
|
515
|
+
if (offset > io.size - consts_1.NVM3_PAGE_HEADER_SIZE) {
|
|
516
|
+
throw new core_1.ZWaveError("Incomplete page in buffer!", core_1.ZWaveErrorCodes.NVM_InvalidFormat);
|
|
517
|
+
}
|
|
518
|
+
const buffer = (await io.read(offset, consts_1.NVM3_PAGE_HEADER_SIZE)).buffer;
|
|
519
|
+
const { version, eraseCount } = tryGetVersionAndEraseCount(buffer);
|
|
520
|
+
// Page status
|
|
521
|
+
const status = buffer.readUInt32LE(12);
|
|
522
|
+
const devInfo = buffer.readUInt16LE(16);
|
|
523
|
+
const deviceFamily = devInfo & 0x7ff;
|
|
524
|
+
const writeSize = (devInfo >> 11) & 0b1;
|
|
525
|
+
const memoryMapped = !!((devInfo >> 12) & 0b1);
|
|
526
|
+
let pageSize = (0, page_1.pageSizeFromBits)((devInfo >> 13) & 0b111);
|
|
527
|
+
if (pageSize > 0xffff) {
|
|
528
|
+
// Some controllers have no valid info in the page size bits, resulting
|
|
529
|
+
// in an impossibly large page size. To try and figure out the actual page
|
|
530
|
+
// size without knowing the hardware, we scan the buffer for the next valid
|
|
531
|
+
// page start.
|
|
532
|
+
for (let exponent = 0; exponent < 0b111; exponent++) {
|
|
533
|
+
const testPageSize = (0, page_1.pageSizeFromBits)(exponent);
|
|
534
|
+
const nextOffset = offset + testPageSize;
|
|
535
|
+
if (
|
|
536
|
+
// exactly end of NVM OR
|
|
537
|
+
io.size === nextOffset
|
|
538
|
+
// next page
|
|
539
|
+
|| await isValidPageHeaderAtOffset(io, nextOffset)) {
|
|
540
|
+
pageSize = testPageSize;
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (pageSize > 0xffff) {
|
|
546
|
+
throw new core_1.ZWaveError("Could not determine page size!", core_1.ZWaveErrorCodes.NVM_InvalidFormat);
|
|
547
|
+
}
|
|
548
|
+
if (io.size < offset + pageSize) {
|
|
549
|
+
throw new core_1.ZWaveError(`NVM contains incomplete page at offset ${(0, shared_1.num2hex)(offset)}!`, core_1.ZWaveErrorCodes.NVM_InvalidFormat);
|
|
550
|
+
}
|
|
551
|
+
const formatInfo = buffer.readUInt16LE(18);
|
|
552
|
+
const encrypted = !(formatInfo & 0b1);
|
|
553
|
+
return {
|
|
554
|
+
offset,
|
|
555
|
+
version,
|
|
556
|
+
eraseCount,
|
|
557
|
+
status,
|
|
558
|
+
encrypted,
|
|
559
|
+
pageSize,
|
|
560
|
+
writeSize,
|
|
561
|
+
memoryMapped,
|
|
562
|
+
deviceFamily,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
function tryGetVersionAndEraseCount(header) {
|
|
566
|
+
const version = header.readUInt16LE(0);
|
|
567
|
+
const magic = header.readUInt16LE(2);
|
|
568
|
+
if (magic !== consts_1.NVM3_PAGE_MAGIC) {
|
|
569
|
+
throw new core_1.ZWaveError("Not a valid NVM3 page!", core_1.ZWaveErrorCodes.NVM_InvalidFormat);
|
|
570
|
+
}
|
|
571
|
+
if (version !== 0x01) {
|
|
572
|
+
throw new core_1.ZWaveError(`Unsupported NVM3 page version: ${version}`, core_1.ZWaveErrorCodes.NVM_NotSupported);
|
|
573
|
+
}
|
|
574
|
+
// The erase counter is saved twice, once normally, once inverted
|
|
575
|
+
let eraseCount = header.readUInt32LE(4);
|
|
576
|
+
const eraseCountCode = eraseCount >>> consts_1.NVM3_PAGE_COUNTER_SIZE;
|
|
577
|
+
eraseCount &= consts_1.NVM3_PAGE_COUNTER_MASK;
|
|
578
|
+
(0, utils_2.validateBergerCode)(eraseCount, eraseCountCode, consts_1.NVM3_PAGE_COUNTER_SIZE);
|
|
579
|
+
let eraseCountInv = header.readUInt32LE(8);
|
|
580
|
+
const eraseCountInvCode = eraseCountInv >>> consts_1.NVM3_PAGE_COUNTER_SIZE;
|
|
581
|
+
eraseCountInv &= consts_1.NVM3_PAGE_COUNTER_MASK;
|
|
582
|
+
(0, utils_2.validateBergerCode)(eraseCountInv, eraseCountInvCode, consts_1.NVM3_PAGE_COUNTER_SIZE);
|
|
583
|
+
if (eraseCount !== (~eraseCountInv & consts_1.NVM3_PAGE_COUNTER_MASK)) {
|
|
584
|
+
throw new core_1.ZWaveError("Invalid erase count!", core_1.ZWaveErrorCodes.NVM_InvalidFormat);
|
|
585
|
+
}
|
|
586
|
+
return { version, eraseCount };
|
|
587
|
+
}
|
|
588
|
+
async function isValidPageHeaderAtOffset(io, offset) {
|
|
589
|
+
if (offset > io.size - consts_1.NVM3_PAGE_HEADER_SIZE) {
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
const { buffer } = await io.read(offset, consts_1.NVM3_PAGE_HEADER_SIZE);
|
|
593
|
+
try {
|
|
594
|
+
tryGetVersionAndEraseCount(buffer);
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
async function readObjectHeader(io, offset) {
|
|
602
|
+
let headerSize = 4;
|
|
603
|
+
const hdr1 = await (0, utils_1.nvmReadUInt32LE)(io, offset);
|
|
604
|
+
// Skip over blank page areas
|
|
605
|
+
if (hdr1 === 0xffffffff)
|
|
606
|
+
return;
|
|
607
|
+
const key = (hdr1 >> consts_1.NVM3_OBJ_KEY_SHIFT) & consts_1.NVM3_OBJ_KEY_MASK;
|
|
608
|
+
let objType = hdr1 & consts_1.NVM3_OBJ_TYPE_MASK;
|
|
609
|
+
let fragmentSize = 0;
|
|
610
|
+
let hdr2;
|
|
611
|
+
const isLarge = objType === consts_1.ObjectType.DataLarge
|
|
612
|
+
|| objType === consts_1.ObjectType.CounterLarge;
|
|
613
|
+
if (isLarge) {
|
|
614
|
+
hdr2 = await (0, utils_1.nvmReadUInt32LE)(io, offset + 4);
|
|
615
|
+
headerSize += 4;
|
|
616
|
+
fragmentSize = hdr2 & consts_1.NVM3_OBJ_LARGE_LEN_MASK;
|
|
617
|
+
}
|
|
618
|
+
else if (objType > consts_1.ObjectType.DataSmall) {
|
|
619
|
+
// In small objects with data, the length and object type are stored in the same value
|
|
620
|
+
fragmentSize = objType - consts_1.ObjectType.DataSmall;
|
|
621
|
+
objType = consts_1.ObjectType.DataSmall;
|
|
622
|
+
}
|
|
623
|
+
else if (objType === consts_1.ObjectType.CounterSmall) {
|
|
624
|
+
fragmentSize = consts_1.NVM3_COUNTER_SIZE;
|
|
625
|
+
}
|
|
626
|
+
const fragmentType = isLarge
|
|
627
|
+
? (hdr1 >>> consts_1.NVM3_OBJ_FRAGTYPE_SHIFT) & consts_1.NVM3_OBJ_FRAGTYPE_MASK
|
|
628
|
+
: consts_1.FragmentType.None;
|
|
629
|
+
if (isLarge) {
|
|
630
|
+
(0, utils_2.validateBergerCodeMulti)([hdr1, hdr2], 32 + consts_1.NVM3_CODE_LARGE_SHIFT);
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
(0, utils_2.validateBergerCodeMulti)([hdr1], consts_1.NVM3_CODE_SMALL_SHIFT);
|
|
634
|
+
}
|
|
635
|
+
if (io.size < offset + headerSize + fragmentSize) {
|
|
636
|
+
throw new core_1.ZWaveError(`NVM contains incomplete object at offset ${(0, shared_1.num2hex)(offset)}!`, core_1.ZWaveErrorCodes.NVM_InvalidFormat);
|
|
637
|
+
}
|
|
638
|
+
const alignedFragmentSize = (0, object_1.getAlignedSize)(fragmentSize);
|
|
639
|
+
const alignedSize = headerSize + alignedFragmentSize;
|
|
640
|
+
return {
|
|
641
|
+
key,
|
|
642
|
+
offset,
|
|
643
|
+
type: objType,
|
|
644
|
+
fragmentType,
|
|
645
|
+
headerSize,
|
|
646
|
+
fragmentSize,
|
|
647
|
+
alignedSize,
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
//# sourceMappingURL=NVM3.js.map
|