refactor: translate error messages from Chinese to English
Standardize all user-facing error/validation messages across partition-table, nvs, app-image, and ota-data parsers to English.
This commit is contained in:
parent
d32674617d
commit
4959ff74c1
|
|
@ -15,13 +15,13 @@ import {
|
||||||
*/
|
*/
|
||||||
export function parseAppImage(data: Uint8Array): AppImageInfo {
|
export function parseAppImage(data: Uint8Array): AppImageInfo {
|
||||||
if (data.length < IMAGE_HEADER_SIZE + EXTENDED_HEADER_SIZE) {
|
if (data.length < IMAGE_HEADER_SIZE + EXTENDED_HEADER_SIZE) {
|
||||||
throw new Error(`数据太短: ${data.length} 字节 (最少需要 ${IMAGE_HEADER_SIZE + EXTENDED_HEADER_SIZE} 字节)`);
|
throw new Error(`Image too short: ${data.length}B`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Image Header (8 bytes: magic + segments + spi_mode + spi_speed_size + entry_addr) ──
|
// ── Image Header (8 bytes: magic + segments + spi_mode + spi_speed_size + entry_addr) ──
|
||||||
const magic = readU8(data, 0);
|
const magic = readU8(data, 0);
|
||||||
if (magic !== IMAGE_MAGIC) {
|
if (magic !== IMAGE_MAGIC) {
|
||||||
throw new Error(`无效的魔数: 0x${magic.toString(16)} (应为 0xE9)`);
|
throw new Error(`Invalid image magic: 0x${magic.toString(16)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const segmentCount = readU8(data, 1);
|
const segmentCount = readU8(data, 1);
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,10 @@ interface ParsedPage {
|
||||||
*/
|
*/
|
||||||
export function parseBinary(data: Uint8Array): NvsPartition {
|
export function parseBinary(data: Uint8Array): NvsPartition {
|
||||||
if (data.length % PAGE_SIZE !== 0) {
|
if (data.length % PAGE_SIZE !== 0) {
|
||||||
throw new Error(`二进制数据大小 (${data.length}) 不是页大小 (${PAGE_SIZE}) 的倍数`);
|
throw new Error(`Invalid NVS size: ${data.length}B`);
|
||||||
}
|
}
|
||||||
if (data.length === 0) {
|
if (data.length === 0) {
|
||||||
throw new Error('二进制数据为空');
|
throw new Error('NVS data is empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
const pageCount = data.length / PAGE_SIZE;
|
const pageCount = data.length / PAGE_SIZE;
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,10 @@ interface PlannedEntry {
|
||||||
*/
|
*/
|
||||||
export function serializeBinary(partition: NvsPartition, targetSize: number): Uint8Array {
|
export function serializeBinary(partition: NvsPartition, targetSize: number): Uint8Array {
|
||||||
if (targetSize % PAGE_SIZE !== 0) {
|
if (targetSize % PAGE_SIZE !== 0) {
|
||||||
throw new Error(`目标大小 (${targetSize}) 不是页大小 (${PAGE_SIZE}) 的倍数`);
|
throw new Error(`Target size (${targetSize}) is not a multiple of page size (${PAGE_SIZE})`);
|
||||||
}
|
}
|
||||||
if (targetSize < MIN_PARTITION_SIZE) {
|
if (targetSize < MIN_PARTITION_SIZE) {
|
||||||
throw new Error(`目标大小 (${targetSize}) 小于最小分区大小 (${MIN_PARTITION_SIZE})`);
|
throw new Error(`Target size (${targetSize}) is less than minimum partition size (${MIN_PARTITION_SIZE})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate buffer filled with 0xFF (erased flash state)
|
// Allocate buffer filled with 0xFF (erased flash state)
|
||||||
|
|
@ -328,8 +328,8 @@ export function serializeBinary(partition: NvsPartition, targetSize: number): Ui
|
||||||
|
|
||||||
if (plannedIdx < planned.length) {
|
if (plannedIdx < planned.length) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`分区空间不足: 还有 ${planned.length - plannedIdx} 个条目无法写入。` +
|
`Partition space exhausted: ${planned.length - plannedIdx} entries could not be written. ` +
|
||||||
`请增大分区大小。`
|
`Increase partition size.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,17 +40,17 @@ function parseIntValue(str: string): number {
|
||||||
let val: number;
|
let val: number;
|
||||||
if (str.startsWith('0x') || str.startsWith('0X')) {
|
if (str.startsWith('0x') || str.startsWith('0X')) {
|
||||||
if (!/^-?0[xX][0-9a-fA-F]+$/.test(str)) {
|
if (!/^-?0[xX][0-9a-fA-F]+$/.test(str)) {
|
||||||
throw new Error(`无效的整数值: "${str}"`);
|
throw new Error(`Invalid integer value: "${str}"`);
|
||||||
}
|
}
|
||||||
val = parseInt(str, 16);
|
val = parseInt(str, 16);
|
||||||
} else {
|
} else {
|
||||||
if (!/^-?\d+$/.test(str)) {
|
if (!/^-?\d+$/.test(str)) {
|
||||||
throw new Error(`无效的整数值: "${str}"`);
|
throw new Error(`Invalid integer value: "${str}"`);
|
||||||
}
|
}
|
||||||
val = parseInt(str, 10);
|
val = parseInt(str, 10);
|
||||||
}
|
}
|
||||||
if (Number.isNaN(val)) {
|
if (Number.isNaN(val)) {
|
||||||
throw new Error(`无效的整数值: "${str}"`);
|
throw new Error(`Invalid integer value: "${str}"`);
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
@ -62,14 +62,14 @@ function parseBigIntValue(str: string): bigint {
|
||||||
// Handle negative hex explicitly.
|
// Handle negative hex explicitly.
|
||||||
if (str.startsWith('-0x') || str.startsWith('-0X')) {
|
if (str.startsWith('-0x') || str.startsWith('-0X')) {
|
||||||
if (!/^-0[xX][0-9a-fA-F]+$/.test(str)) {
|
if (!/^-0[xX][0-9a-fA-F]+$/.test(str)) {
|
||||||
throw new Error(`无效的整数值: "${str}"`);
|
throw new Error(`Invalid integer value: "${str}"`);
|
||||||
}
|
}
|
||||||
return -BigInt(str.slice(1));
|
return -BigInt(str.slice(1));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return BigInt(str);
|
return BigInt(str);
|
||||||
} catch {
|
} catch {
|
||||||
throw new Error(`无效的整数值: "${str}"`);
|
throw new Error(`Invalid integer value: "${str}"`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,20 +169,20 @@ export function parseCsv(text: string): NvsPartition {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentNamespace) {
|
if (!currentNamespace) {
|
||||||
throw new Error(`行 ${i + 1}: 数据条目 "${key}" 出现在任何命名空间之前`);
|
throw new Error(`Line ${i + 1}: data entry "${key}" appears before any namespace`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type !== 'data' && type !== 'file') {
|
if (type !== 'data' && type !== 'file') {
|
||||||
throw new Error(`行 ${i + 1}: 未知类型 "${type}"`);
|
throw new Error(`Line ${i + 1}: unknown type "${type}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!encoding) {
|
if (!encoding) {
|
||||||
throw new Error(`行 ${i + 1}: 键 "${key}" 缺少编码类型`);
|
throw new Error(`Line ${i + 1}: key "${key}" missing encoding`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nvsType = ENCODING_TO_TYPE[encoding as NvsEncoding];
|
const nvsType = ENCODING_TO_TYPE[encoding as NvsEncoding];
|
||||||
if (nvsType === undefined) {
|
if (nvsType === undefined) {
|
||||||
throw new Error(`行 ${i + 1}: 未知编码 "${encoding}"`);
|
throw new Error(`Line ${i + 1}: unknown encoding "${encoding}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsedValue: number | bigint | string | Uint8Array;
|
let parsedValue: number | bigint | string | Uint8Array;
|
||||||
|
|
|
||||||
|
|
@ -182,27 +182,27 @@ export function validatePartition(partition: NvsPartition): string[] {
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
|
|
||||||
if (partition.namespaces.length > MAX_NAMESPACES) {
|
if (partition.namespaces.length > MAX_NAMESPACES) {
|
||||||
errors.push(`命名空间数量超过上限 ${MAX_NAMESPACES}`);
|
errors.push(`Namespace count exceeds limit ${MAX_NAMESPACES}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ns of partition.namespaces) {
|
for (const ns of partition.namespaces) {
|
||||||
if (ns.length === 0) {
|
if (ns.length === 0) {
|
||||||
errors.push('命名空间名称不能为空');
|
errors.push('Namespace name cannot be empty');
|
||||||
}
|
}
|
||||||
if (ns.length > MAX_KEY_LENGTH) {
|
if (ns.length > MAX_KEY_LENGTH) {
|
||||||
errors.push(`命名空间 "${ns}" 名称超过 ${MAX_KEY_LENGTH} 字符`);
|
errors.push(`Namespace "${ns}" exceeds ${MAX_KEY_LENGTH} characters`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const entry of partition.entries) {
|
for (const entry of partition.entries) {
|
||||||
if (entry.key.length === 0) {
|
if (entry.key.length === 0) {
|
||||||
errors.push(`在命名空间 "${entry.namespace}" 中存在空键名`);
|
errors.push(`Empty key in namespace "${entry.namespace}"`);
|
||||||
}
|
}
|
||||||
if (entry.key.length > MAX_KEY_LENGTH) {
|
if (entry.key.length > MAX_KEY_LENGTH) {
|
||||||
errors.push(`键 "${entry.key}" 名称超过 ${MAX_KEY_LENGTH} 字符`);
|
errors.push(`Key "${entry.key}" exceeds ${MAX_KEY_LENGTH} characters`);
|
||||||
}
|
}
|
||||||
if (!partition.namespaces.includes(entry.namespace)) {
|
if (!partition.namespaces.includes(entry.namespace)) {
|
||||||
errors.push(`键 "${entry.key}" 的命名空间 "${entry.namespace}" 未注册`);
|
errors.push(`Key "${entry.key}" references unregistered namespace "${entry.namespace}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate value ranges for primitive types
|
// Validate value ranges for primitive types
|
||||||
|
|
@ -210,21 +210,21 @@ export function validatePartition(partition: NvsPartition): string[] {
|
||||||
if (typeof entry.value === 'number') {
|
if (typeof entry.value === 'number') {
|
||||||
const v = entry.value;
|
const v = entry.value;
|
||||||
switch (entry.type) {
|
switch (entry.type) {
|
||||||
case NvsType.U8: if (v < 0 || v > 0xFF) errors.push(`"${entry.key}" U8 值超出范围`); break;
|
case NvsType.U8: if (v < 0 || v > 0xFF) errors.push(`"${entry.key}" U8 value out of range`); break;
|
||||||
case NvsType.I8: if (v < -128 || v > 127) errors.push(`"${entry.key}" I8 值超出范围`); break;
|
case NvsType.I8: if (v < -128 || v > 127) errors.push(`"${entry.key}" I8 value out of range`); break;
|
||||||
case NvsType.U16: if (v < 0 || v > 0xFFFF) errors.push(`"${entry.key}" U16 值超出范围`); break;
|
case NvsType.U16: if (v < 0 || v > 0xFFFF) errors.push(`"${entry.key}" U16 value out of range`); break;
|
||||||
case NvsType.I16: if (v < -32768 || v > 32767) errors.push(`"${entry.key}" I16 值超出范围`); break;
|
case NvsType.I16: if (v < -32768 || v > 32767) errors.push(`"${entry.key}" I16 value out of range`); break;
|
||||||
case NvsType.U32: if (v < 0 || v > 0xFFFFFFFF) errors.push(`"${entry.key}" U32 值超出范围`); break;
|
case NvsType.U32: if (v < 0 || v > 0xFFFFFFFF) errors.push(`"${entry.key}" U32 value out of range`); break;
|
||||||
case NvsType.I32: if (v < -2147483648 || v > 2147483647) errors.push(`"${entry.key}" I32 值超出范围`); break;
|
case NvsType.I32: if (v < -2147483648 || v > 2147483647) errors.push(`"${entry.key}" I32 value out of range`); break;
|
||||||
}
|
}
|
||||||
} else if (typeof entry.value === 'bigint') {
|
} else if (typeof entry.value === 'bigint') {
|
||||||
const v = entry.value;
|
const v = entry.value;
|
||||||
switch (entry.type) {
|
switch (entry.type) {
|
||||||
case NvsType.U64:
|
case NvsType.U64:
|
||||||
if (v < 0n || v > 0xFFFFFFFFFFFFFFFFn) errors.push(`"${entry.key}" U64 值超出范围`);
|
if (v < 0n || v > 0xFFFFFFFFFFFFFFFFn) errors.push(`"${entry.key}" U64 value out of range`);
|
||||||
break;
|
break;
|
||||||
case NvsType.I64:
|
case NvsType.I64:
|
||||||
if (v < -9223372036854775808n || v > 9223372036854775807n) errors.push(`"${entry.key}" I64 值超出范围`);
|
if (v < -9223372036854775808n || v > 9223372036854775807n) errors.push(`"${entry.key}" I64 value out of range`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -234,7 +234,7 @@ export function validatePartition(partition: NvsPartition): string[] {
|
||||||
if (entry.type === NvsType.SZ && typeof entry.value === 'string') {
|
if (entry.type === NvsType.SZ && typeof entry.value === 'string') {
|
||||||
const byteLen = new TextEncoder().encode(entry.value).length;
|
const byteLen = new TextEncoder().encode(entry.value).length;
|
||||||
if (byteLen >= MAX_STRING_LENGTH) {
|
if (byteLen >= MAX_STRING_LENGTH) {
|
||||||
errors.push(`"${entry.key}" 字符串长度 ${byteLen} 字节超过上限 ${MAX_STRING_LENGTH - 1}`);
|
errors.push(`"${entry.key}" string length ${byteLen} bytes exceeds limit ${MAX_STRING_LENGTH - 1}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,11 +244,11 @@ export function validatePartition(partition: NvsPartition): string[] {
|
||||||
// NvsType.BLOB_DATA uses the V2 chunked format and is capped at MAX_BLOB_SIZE_V2.
|
// NvsType.BLOB_DATA uses the V2 chunked format and is capped at MAX_BLOB_SIZE_V2.
|
||||||
if (entry.type === NvsType.BLOB && entry.value instanceof Uint8Array) {
|
if (entry.type === NvsType.BLOB && entry.value instanceof Uint8Array) {
|
||||||
if (entry.value.length > MAX_BLOB_SIZE_V1) {
|
if (entry.value.length > MAX_BLOB_SIZE_V1) {
|
||||||
errors.push(`"${entry.key}" BLOB ${entry.value.length} 字节超过上限 ${MAX_BLOB_SIZE_V1}`);
|
errors.push(`"${entry.key}" BLOB ${entry.value.length} bytes exceeds limit ${MAX_BLOB_SIZE_V1}`);
|
||||||
}
|
}
|
||||||
} else if (entry.type === NvsType.BLOB_DATA && entry.value instanceof Uint8Array) {
|
} else if (entry.type === NvsType.BLOB_DATA && entry.value instanceof Uint8Array) {
|
||||||
if (entry.value.length > MAX_BLOB_SIZE_V2) {
|
if (entry.value.length > MAX_BLOB_SIZE_V2) {
|
||||||
errors.push(`"${entry.key}" BLOB ${entry.value.length} 字节超过 V2 上限 ${MAX_BLOB_SIZE_V2}`);
|
errors.push(`"${entry.key}" BLOB ${entry.value.length} bytes exceeds V2 limit ${MAX_BLOB_SIZE_V2}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -258,7 +258,7 @@ export function validatePartition(partition: NvsPartition): string[] {
|
||||||
for (const entry of partition.entries) {
|
for (const entry of partition.entries) {
|
||||||
const k = `${entry.namespace}::${entry.key}`;
|
const k = `${entry.namespace}::${entry.key}`;
|
||||||
if (seen.has(k)) {
|
if (seen.has(k)) {
|
||||||
errors.push(`重复键: ${entry.namespace}/${entry.key}`);
|
errors.push(`Duplicate key: ${entry.namespace}/${entry.key}`);
|
||||||
}
|
}
|
||||||
seen.add(k);
|
seen.add(k);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ function isEntryValid(entry: OtaSelectEntry): boolean {
|
||||||
*/
|
*/
|
||||||
export function parseOtaData(data: Uint8Array, numOtaPartitions = DEFAULT_NUM_OTA_PARTITIONS): OtaData {
|
export function parseOtaData(data: Uint8Array, numOtaPartitions = DEFAULT_NUM_OTA_PARTITIONS): OtaData {
|
||||||
if (data.length < OTA_DATA_MIN_SIZE) {
|
if (data.length < OTA_DATA_MIN_SIZE) {
|
||||||
throw new Error(`OTA data too short: ${data.length} bytes, need at least ${OTA_DATA_MIN_SIZE}`);
|
throw new Error(`OTA data too short: ${data.length}B`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry0 = parseEntry(data, 0);
|
const entry0 = parseEntry(data, 0);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ function parseFlags(str: string): number {
|
||||||
for (const part of normalized.split(/[\s|,]+/).filter(Boolean)) {
|
for (const part of normalized.split(/[\s|,]+/).filter(Boolean)) {
|
||||||
if (part === 'encrypted') result |= PartitionFlags.ENCRYPTED;
|
if (part === 'encrypted') result |= PartitionFlags.ENCRYPTED;
|
||||||
else if (part === 'readonly') result |= PartitionFlags.READONLY;
|
else if (part === 'readonly') result |= PartitionFlags.READONLY;
|
||||||
else throw new Error(`未知标志: "${part}"`);
|
else throw new Error(`Unknown flag: "${part}"`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -26,9 +26,9 @@ function parseSize(str: string): number {
|
||||||
str = str.trim();
|
str = str.trim();
|
||||||
if (!str) return 0;
|
if (!str) return 0;
|
||||||
if (str.startsWith('0x') || str.startsWith('0X')) {
|
if (str.startsWith('0x') || str.startsWith('0X')) {
|
||||||
if (!/^0x[0-9a-f]+$/i.test(str)) throw new Error(`无效的大小/偏移值: "${str}"`);
|
if (!/^0x[0-9a-f]+$/i.test(str)) throw new Error(`Invalid size/offset value: "${str}"`);
|
||||||
const v = parseInt(str, 16);
|
const v = parseInt(str, 16);
|
||||||
if (isNaN(v) || v < 0 || v > U32_MAX) throw new Error(`无效的大小/偏移值: "${str}"`);
|
if (isNaN(v) || v < 0 || v > U32_MAX) throw new Error(`Invalid size/offset value: "${str}"`);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,11 +40,11 @@ function parseSize(str: string): number {
|
||||||
if (unit === 'K') result = Math.floor(num * 1024);
|
if (unit === 'K') result = Math.floor(num * 1024);
|
||||||
else if (unit === 'M') result = Math.floor(num * 1024 * 1024);
|
else if (unit === 'M') result = Math.floor(num * 1024 * 1024);
|
||||||
else result = Math.floor(num);
|
else result = Math.floor(num);
|
||||||
if (result > U32_MAX) throw new Error(`无效的大小/偏移值: "${str}" (超出 32 位范围)`);
|
if (result > U32_MAX) throw new Error(`Invalid size/offset value: "${str}" (exceeds 32-bit range)`);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(`无效的大小/偏移值: "${str}"`);
|
throw new Error(`Invalid size/offset value: "${str}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -86,7 +86,7 @@ function splitCsvLine(line: string): string[] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (inQuotes) throw new Error('引号未闭合');
|
if (inQuotes) throw new Error('Unclosed quote');
|
||||||
fields.push(current.trim());
|
fields.push(current.trim());
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +111,7 @@ export function parseCsv(text: string, onWarning?: (line: number, message: strin
|
||||||
try {
|
try {
|
||||||
const fields = splitCsvLine(line);
|
const fields = splitCsvLine(line);
|
||||||
if (fields.length < 5) {
|
if (fields.length < 5) {
|
||||||
onWarning?.(lineIdx + 1, `字段数量不足 (需要 5,实际 ${fields.length}): "${line}"`);
|
onWarning?.(lineIdx + 1, `Not enough fields (need 5, got ${fields.length}): "${line}"`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +127,7 @@ export function parseCsv(text: string, onWarning?: (line: number, message: strin
|
||||||
|
|
||||||
const type = NAME_TO_TYPE[typeName];
|
const type = NAME_TO_TYPE[typeName];
|
||||||
if (type === undefined) {
|
if (type === undefined) {
|
||||||
onWarning?.(lineIdx + 1, `未知分区类型: "${typeName}"`);
|
onWarning?.(lineIdx + 1, `Unknown partition type: "${typeName}"`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,7 +137,7 @@ export function parseCsv(text: string, onWarning?: (line: number, message: strin
|
||||||
const flags = parseFlags(flagsStr);
|
const flags = parseFlags(flagsStr);
|
||||||
entries.push({ name, type, subtype, offset, size, flags });
|
entries.push({ name, type, subtype, offset, size, flags });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
onWarning?.(lineIdx + 1, `解析失败: ${(e as Error).message}`);
|
onWarning?.(lineIdx + 1, `Parse failed: ${(e as Error).message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@ const U32_MAX = 0xFFFF_FFFF;
|
||||||
|
|
||||||
function assertU8(val: number, field: string): void {
|
function assertU8(val: number, field: string): void {
|
||||||
if (!Number.isInteger(val) || val < 0 || val > 0xFF)
|
if (!Number.isInteger(val) || val < 0 || val > 0xFF)
|
||||||
throw new Error(`"${field}" 不是有效的字节值 (0–255): ${val}`);
|
throw new Error(`"${field}" is not a valid byte value (0–255): ${val}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertU32(val: number, field: string): void {
|
function assertU32(val: number, field: string): void {
|
||||||
if (!Number.isInteger(val) || val < 0 || val > U32_MAX)
|
if (!Number.isInteger(val) || val < 0 || val > U32_MAX)
|
||||||
throw new Error(`"${field}" 不是有效的 32 位无符号整数 (0–0xFFFFFFFF): ${val}`);
|
throw new Error(`"${field}" is not a valid uint32 (0–0xFFFFFFFF): ${val}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -26,14 +26,14 @@ export function serializeBinary(table: PartitionTable): Uint8Array {
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
for (const entry of table.entries) {
|
for (const entry of table.entries) {
|
||||||
if (offset + ENTRY_SIZE > TABLE_MAX_SIZE - ENTRY_SIZE) {
|
if (offset + ENTRY_SIZE > TABLE_MAX_SIZE - ENTRY_SIZE) {
|
||||||
throw new Error(`分区表条目过多,超过最大容量 (最多 ${Math.floor((TABLE_MAX_SIZE - ENTRY_SIZE) / ENTRY_SIZE)} 条)`);
|
throw new Error(`Too many partition table entries (max ${Math.floor((TABLE_MAX_SIZE - ENTRY_SIZE) / ENTRY_SIZE)})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertU8(entry.type, '类型');
|
assertU8(entry.type, 'type');
|
||||||
assertU8(entry.subtype, '子类型');
|
assertU8(entry.subtype, 'subtype');
|
||||||
assertU32(entry.offset, '偏移量');
|
assertU32(entry.offset, 'offset');
|
||||||
assertU32(entry.size, '大小');
|
assertU32(entry.size, 'size');
|
||||||
assertU32(entry.flags, '标志');
|
assertU32(entry.flags, 'flags');
|
||||||
|
|
||||||
writeU16(buf, offset, ENTRY_MAGIC);
|
writeU16(buf, offset, ENTRY_MAGIC);
|
||||||
buf[offset + 2] = entry.type;
|
buf[offset + 2] = entry.type;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export function validateTable(table: PartitionTable): PartitionValidationError[]
|
||||||
if (names.has(entry.name)) {
|
if (names.has(entry.name)) {
|
||||||
errors.push({
|
errors.push({
|
||||||
type: 'duplicate_name',
|
type: 'duplicate_name',
|
||||||
message: `分区名称重复: "${entry.name}"`,
|
message: `Duplicate partition name: "${entry.name}"`,
|
||||||
entryA: names.get(entry.name),
|
entryA: names.get(entry.name),
|
||||||
entryB: entry,
|
entryB: entry,
|
||||||
});
|
});
|
||||||
|
|
@ -33,14 +33,14 @@ export function validateTable(table: PartitionTable): PartitionValidationError[]
|
||||||
if (entry.offset !== 0 && entry.offset % SECTOR_SIZE !== 0) {
|
if (entry.offset !== 0 && entry.offset % SECTOR_SIZE !== 0) {
|
||||||
errors.push({
|
errors.push({
|
||||||
type: 'alignment',
|
type: 'alignment',
|
||||||
message: `"${entry.name}" 偏移 0x${entry.offset.toString(16)} 未对齐到 4KB 边界`,
|
message: `"${entry.name}" offset 0x${entry.offset.toString(16)} not aligned to 4KB boundary`,
|
||||||
entryA: entry,
|
entryA: entry,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (entry.size !== 0 && entry.size % SECTOR_SIZE !== 0) {
|
if (entry.size !== 0 && entry.size % SECTOR_SIZE !== 0) {
|
||||||
errors.push({
|
errors.push({
|
||||||
type: 'alignment',
|
type: 'alignment',
|
||||||
message: `"${entry.name}" 大小 0x${entry.size.toString(16)} 未对齐到 4KB 边界`,
|
message: `"${entry.name}" size 0x${entry.size.toString(16)} not aligned to 4KB boundary`,
|
||||||
entryA: entry,
|
entryA: entry,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ export function validateTable(table: PartitionTable): PartitionValidationError[]
|
||||||
if (a.offset < bEnd && b.offset < aEnd) {
|
if (a.offset < bEnd && b.offset < aEnd) {
|
||||||
errors.push({
|
errors.push({
|
||||||
type: 'overlap',
|
type: 'overlap',
|
||||||
message: `"${a.name}" [0x${a.offset.toString(16)}..0x${aEnd.toString(16)}] 与 "${b.name}" [0x${b.offset.toString(16)}..0x${bEnd.toString(16)}] 重叠`,
|
message: `"${a.name}" [0x${a.offset.toString(16)}..0x${aEnd.toString(16)}] overlaps "${b.name}" [0x${b.offset.toString(16)}..0x${bEnd.toString(16)}]`,
|
||||||
entryA: a,
|
entryA: a,
|
||||||
entryB: b,
|
entryB: b,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue