42 lines
4.5 KiB
JavaScript
42 lines
4.5 KiB
JavaScript
/* Dependency-free binary utilities for offline testing and authorized repair work.
|
|
Verify Multi-PROG host compatibility and API signatures in Local Script > Help. */
|
|
export function requireBytes(data) {
|
|
if (!(data instanceof Uint8Array)) throw new TypeError("Uint8Array required");
|
|
return data;
|
|
}
|
|
export function assertRange(data, offset, length) {
|
|
requireBytes(data);
|
|
if (!Number.isInteger(offset)||!Number.isInteger(length)||offset<0||length<0||offset+length>data.length) throw new RangeError("Range outside buffer");
|
|
}
|
|
export function cloneBytes(data){ requireBytes(data); return new Uint8Array(data); }
|
|
export function sliceBytes(data,offset,length){ assertRange(data,offset,length); return data.slice(offset,offset+length); }
|
|
export function bytesToHex(data,separator=" "){ requireBytes(data); return Array.from(data,b=>b.toString(16).padStart(2,"0")).join(separator).toUpperCase(); }
|
|
export function hexToBytes(text){
|
|
const s=String(text).replace(/[^0-9a-f]/gi,"");
|
|
if(s.length%2) throw new Error("Hex text must contain complete bytes");
|
|
const out=new Uint8Array(s.length/2); for(let i=0;i<out.length;i++) out[i]=parseInt(s.slice(i*2,i*2+2),16); return out;
|
|
}
|
|
export function asciiPreview(data,replacement="."){ requireBytes(data); return Array.from(data,b=>b>=32&&b<=126?String.fromCharCode(b):replacement).join(""); }
|
|
export function readU16LE(d,o){assertRange(d,o,2);return d[o]|(d[o+1]<<8);}
|
|
export function readU16BE(d,o){assertRange(d,o,2);return (d[o]<<8)|d[o+1];}
|
|
export function readU32LE(d,o){assertRange(d,o,4);return (d[o]|d[o+1]<<8|d[o+2]<<16|d[o+3]<<24)>>>0;}
|
|
export function readU32BE(d,o){assertRange(d,o,4);return ((d[o]<<24)|(d[o+1]<<16)|(d[o+2]<<8)|d[o+3])>>>0;}
|
|
export function writeU16LE(d,o,v){assertRange(d,o,2);d[o]=v&255;d[o+1]=(v>>>8)&255;}
|
|
export function writeU16BE(d,o,v){assertRange(d,o,2);d[o]=(v>>>8)&255;d[o+1]=v&255;}
|
|
export function writeU32LE(d,o,v){assertRange(d,o,4);for(let i=0;i<4;i++)d[o+i]=(v>>>(8*i))&255;}
|
|
export function writeU32BE(d,o,v){assertRange(d,o,4);for(let i=0;i<4;i++)d[o+i]=(v>>>(8*(3-i)))&255;}
|
|
export function findPattern(data,pattern,start=0){requireBytes(data);requireBytes(pattern);const hits=[];for(let i=start;i<=data.length-pattern.length;i++){let ok=true;for(let j=0;j<pattern.length;j++)if(data[i+j]!==pattern[j]){ok=false;break;}if(ok)hits.push(i);}return hits;}
|
|
export function countByte(data,value){requireBytes(data);let n=0;for(const b of data)if(b===(value&255))n++;return n;}
|
|
export function isBlank(data,offset=0,length=data.length-offset,blank=0xFF){assertRange(data,offset,length);for(let i=offset;i<offset+length;i++)if(data[i]!==blank)return false;return true;}
|
|
export function diffRanges(a,b){requireBytes(a);requireBytes(b);if(a.length!==b.length)throw new Error("Length mismatch");const out=[];let s=-1;for(let i=0;i<a.length;i++){if(a[i]!==b[i]&&s<0)s=i;if((a[i]===b[i]||i===a.length-1)&&s>=0){const e=a[i]===b[i]?i-1:i;out.push({start:s,end:e,length:e-s+1});s=-1;}}return out;}
|
|
export function changedByteCount(a,b){return diffRanges(a,b).reduce((n,r)=>n+r.length,0);}
|
|
export function sum8(data){requireBytes(data);let s=0;for(const b of data)s=(s+b)&255;return s;}
|
|
export function xor8(data){requireBytes(data);let x=0;for(const b of data)x^=b;return x;}
|
|
export function crc16CcittFalse(data){requireBytes(data);let crc=0xFFFF;for(const b of data){crc^=b<<8;for(let i=0;i<8;i++)crc=((crc&0x8000)?(crc<<1)^0x1021:crc<<1)&0xFFFF;}return crc;}
|
|
export function crc32(data){requireBytes(data);let crc=0xFFFFFFFF;for(const b of data){crc^=b;for(let i=0;i<8;i++)crc=(crc>>>1)^((crc&1)?0xEDB88320:0);}return (crc^0xFFFFFFFF)>>>0;}
|
|
export function adler32(data){requireBytes(data);let a=1,b=0;for(const v of data){a=(a+v)%65521;b=(b+a)%65521;}return ((b<<16)|a)>>>0;}
|
|
export function bcdToInt(byte){const hi=(byte>>>4)&15,lo=byte&15;if(hi>9||lo>9)throw new Error("Invalid BCD");return hi*10+lo;}
|
|
export function intToBcd(value){if(!Number.isInteger(value)||value<0||value>99)throw new RangeError("BCD value must be 0..99");return ((Math.floor(value/10)<<4)|(value%10));}
|
|
export function constantTimeEqual(a,b){requireBytes(a);requireBytes(b);if(a.length!==b.length)return false;let d=0;for(let i=0;i<a.length;i++)d|=a[i]^b[i];return d===0;}
|
|
export function allowListedWrite(copy,offset,newBytes,allowedRanges){requireBytes(copy);requireBytes(newBytes);assertRange(copy,offset,newBytes.length);const ok=allowedRanges.some(r=>offset>=r.start&&offset+newBytes.length-1<=r.end);if(!ok)throw new Error("Write is outside allow-list");copy.set(newBytes,offset);return copy;}
|