Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

bps

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bps - npm Package Compare versions

Comparing version 1.0.0 to 2.0.0

2

bps.cli.js
#!/usr/bin/env node
import{Command as e}from"commander";import{resolve as t}from"path";import o from"fs-extra";import r from"chalk-template";import a from"crc-32";async function n(e){try{return o.readFile(e,null)}catch{const o=t(e);throw new Error(`Could not read file ${e} (${o}), it may not exist or you do not have permissions to read it.`)}}function c(e){console.log(e)}function i(e){const t=e instanceof Error?e.message:e;console.log(r`{bold.red ✖} ${t}`)}function s(e){console.log(r`{bold.green ✔} ${e}`)}function u(e){return a.buf(e)>>>0}const f=Object.freeze({LE:827543618,BE:1112560433}),h=Object.freeze({SourceRead:0,TargetRead:1,SourceCopy:2,TargetCopy:3});function l(e){return{type:h.SourceRead,length:e}}function p(e){return{type:h.TargetRead,bytes:e}}function g(e,t){return{type:h.SourceCopy,offset:e,length:t}}function d(e,t){return{type:h.TargetCopy,offset:e,length:t}}function y(e,t,o=!0){let r=0,a=1,n=0,c=t;for(;;){if(n=e.getUint8(c++,o),r+=(127&n)*a,128&n)return[r,c];a<<=7,r+=a}}function m(e){const t=new DataView(e.buffer);let o=!0,r=4,a=0,n=0,c=0,i=[],s=0,m=0,w=0;const b=t.getUint32(0,!0);if(b===f.BE)o=!1;else if(b!==f.LE)throw new Error("Patch is not valid, it does not start with a valid `BPS1` header.");for([a,r]=y(t,r,o),[n,r]=y(t,r,o),[c,r]=y(t,r,o),r+=c;r<t.byteLength-12;){let e=0,a=0,n=0;switch([e,r]=y(t,r,o),a=1+(e>>2),3&e){case h.SourceRead:n=l(a);break;case h.TargetRead:{let e=new Array(a);for(let n=0;n<a;++n)e[n]=t.getUint8(r++,o);n=p(e)}break;case h.SourceCopy:{let e=0;[e,r]=y(t,r,o),e=(1&e?-1:1)*(e>>1),n=g(e,a)}break;case h.TargetCopy:{let e=0;[e,r]=y(t,r,o),e=(1&e?-1:1)*(e>>1),n=d(e,a)}}i.push(n)}if(s=t.getUint32(r+0,o),m=t.getUint32(r+4,o),w=t.getUint32(r+8,o),u(e.subarray(0,-4))!==w)throw new Error(`Patch is invalid, it does not have the expected checksum ${w}.`);return{sourceSize:a,targetSize:n,actions:i,sourceChecksum:s,targetChecksum:m,patchChecksum:w}}const w=(new e).name("bps").version("1.0.0").description("A tool for creating and applying BPS patches.");w.command("verify").description("verifies a patch file").argument("<patch>","the patch file to read").action((async function(e){try{const{sourceSize:t,targetSize:o,sourceChecksum:r,targetChecksum:a,patchChecksum:i}=m(await n(e));c(`Expects source file of size ${t} bytes with checksum ${r}.`),c(`Expects target file of size ${o} bytes with checksum ${a}.`),s(`Patch has checksum ${i} and is valid.`)}catch(e){i(e)}})),w.command("apply").description("applies a patch to a file").argument("<patch>","the patch file to apply").argument("<source>","the source file to patch").argument("<output>","the location to write the patched file to").action((async function(e,r,a){try{const c=function(e,t){if(u(t)!==e.sourceChecksum)throw new Error("Source file is not compatible with the patch, it does not have the expected checksum.");const o=new DataView(t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)),r=new DataView(new ArrayBuffer(e.targetSize));let a=0,n=0,c=0;for(const t of e.actions)switch(t.type){case h.SourceRead:for(let e=0;e<t.length;++e)r.setUint8(a+e,o.getUint8(a+e));a+=t.length;break;case h.TargetRead:for(let e=0,o=t.bytes.length;e<o;++e)r.setUint8(a++,t.bytes[e]);break;case h.SourceCopy:n+=t.offset;for(let e=0;e<t.length;++e)r.setUint8(a++,o.getUint8(n++));break;case h.TargetCopy:c+=t.offset;for(let e=0;e<t.length;++e)r.setUint8(a++,r.getUint8(c++));break;default:throw new Error("Patch is invalid, it contains an invalid action type.")}const i=new Uint8Array(r.buffer);if(u(i)!==e.targetChecksum)throw new Error("Resulting file is not valid, it does not have the expected checksum.");return i}(m(await n(e)),await n(r));await async function(e,r){try{await o.outputFile(e,r,null)}catch{const o=t(e);throw new Error(`Could not write to file ${e} (${o}), make sure you have the relevant permissions.`)}}(a,c),s("Patch was applied successfully.")}catch(e){i(e)}})),w.parse(process.argv);
import{Command as t}from"commander";import{resolve as e}from"path";import o from"fs-extra";import r from"chalk-template";import n from"crc-32";async function a(t){try{return o.readFile(t,null)}catch{const o=e(t);throw new Error(`Could not read file ${t} (${o}), it may not exist or you do not have permissions to read it.`)}}async function s(t,r){try{await o.outputFile(t,r,null)}catch{const o=e(t);throw new Error(`Could not write to file ${t} (${o}), make sure you have the relevant permissions.`)}}function c(t){const e=t instanceof Error?t.message:t;console.log(r`{bold.red ✖} ${e}`)}function i(t){console.log(r`{bold.green ✔} ${t}`)}const f=Object.freeze({LE:827543618,BE:1112560433}),u=Object.freeze({SourceRead:0,TargetRead:1,SourceCopy:2,TargetCopy:3});function h(t){return{type:u.SourceRead,length:t}}function l(t){return{type:u.TargetRead,bytes:t,length:t.length}}function g(t,e){return{type:u.SourceCopy,offset:t,length:e}}function p(t,e){return{type:u.TargetCopy,offset:t,length:e}}function d(t){return n.buf(t)>>>0}function y(t,e){let o=0,r=1,n=0,a=e;for(;;){if(n=t.getUint8(a++),o+=(127&n)*r,128&n)return[o,a];r<<=7,o+=r}}function w(t){const e=new DataView(t.buffer);let o=!0,r=0,n=0,a=0,s=0,c=[],i=0,w=0,b=0;const m=e.getUint32(r,!0);if(m===f.BE)o=!1;else if(m!==f.LE)throw new Error("Patch is not valid, it does not start with a valid `BPS1` header.");for(r+=4,[n,r]=y(e,r),[a,r]=y(e,r),[s,r]=y(e,r),r+=s;r<e.byteLength-12;){let t=0,n=0,a=0;switch([t,r]=y(e,r),n=1+(t>>2),3&t){case u.SourceRead:a=h(n);break;case u.TargetRead:{let t=new Array(n);for(let a=0;a<n;++a)t[a]=e.getUint8(r++,o);a=l(t)}break;case u.SourceCopy:{let t=0;[t,r]=y(e,r),t=(1&t?-1:1)*(t>>1),a=g(t,n)}break;case u.TargetCopy:{let t=0;[t,r]=y(e,r),t=(1&t?-1:1)*(t>>1),a=p(t,n)}}c.push(a)}if(i=e.getUint32(r+0,o),w=e.getUint32(r+4,o),b=e.getUint32(r+8,o),d(t.subarray(0,-4))!==b)throw new Error(`Patch is invalid, it does not have the expected checksum ${b}.`);return{instructions:{sourceSize:n,sourceChecksum:i,targetSize:a,targetChecksum:w,actions:c},checksum:b}}function b(t,e){let o=t.getUint8(e);return e<t.byteLength-1&&(o|=t.getUint8(e+1)<<8),o}class m{#t=new Array(65536);constructor(t=null){if(t)for(let e=0,o=t.byteLength;e<o;++e)this.addWordLocation(b(t,e),e)}addWordLocation(t,e){const o=this.#t[t];o?o.push(e):this.#t[t]=[e]}getWordLocations(t){return this.#t[t]??[]}}function U(t,e,{outputOffset:o}){let r=0,n=o;for(;n<t.byteLength&&n<e.byteLength&&t.getUint8(n)===e.getUint8(n);)r++,n++;return h(r)}function L(t,{outputOffset:e,targetReadLength:o}){const r=[],n=e-o;for(let e=0;e<o;++e)r.push(t.getUint8(n+e));return l(r)}function k(t,e,{word:o,outputOffset:r,sourceRelativeOffset:n,sourceWordTable:a}){let s=0,c=0;const i=a.getWordLocations(o);for(const o of i){let n=0,a=o,i=r;for(;a<t.byteLength&&i<e.byteLength&&t.getUint8(a++)===e.getUint8(i++);)n++;n>s&&(s=n,c=o)}return g(c-n,s)}function O(t,{word:e,outputOffset:o,targetRelativeOffset:r,targetWordTable:n}){let a=0,s=0;const c=n.getWordLocations(e);for(const e of c){let r=0,n=e,c=o;for(;c<t.byteLength&&t.getUint8(n++)===t.getUint8(c++);)r++;r>a&&(a=r,s=e)}return p(s-r,a)}function v(...t){let e=t[0];for(let o=1,r=t.length;o<r;++o)t[o].length>e.length&&(e=t[o]);return e}function R(t){let e=0,o=t;for(;;){if(e++,o>>=7,0===o)return e;o--}}function C(t,e,o){let r=o,n=e;for(;;){let e=127&r;if(r>>=7,0===r){t.setUint8(n++,128|e);break}t.setUint8(n++,e),r--}return n}function S(t,e,o){let r=C(t,e,(o.length-1<<2)+o.type);return o.type===u.TargetRead?o.bytes.forEach((e=>t.setUint8(r++,e))):o.type!==u.SourceCopy&&o.type!==u.TargetCopy||(r=C(t,r,(Math.abs(o.offset)<<1)+(o.offset<0?1:0))),r}function T({sourceSize:t,sourceChecksum:e,targetSize:o,targetChecksum:r,actions:n}){const a=4+R(t)+R(o)+R(0)+n.reduce(((t,e)=>t+function(t){let e=R((t.length-1<<2)+t.type);return t.type===u.TargetRead?e+=t.length:t.type!==u.SourceCopy&&t.type!==u.TargetCopy||(e+=R((Math.abs(t.offset)<<1)+(t.offset<0?1:0))),e}(e)),0)+4+4+4,s=new DataView(new ArrayBuffer(a)),c=new Uint8Array(s.buffer);let i=0;s.setUint32(i,f.LE,!0),i=4,i=C(s,i,t),i=C(s,i,o),i=C(s,i,0);for(const t of n)i=S(s,i,t);s.setUint32(i,e,!0),s.setUint32(i+4,r,!0);const h=d(c.subarray(0,-4));return s.setUint32(i+8,h,!0),{buffer:c,checksum:h}}const E=(new t).name("bps").version("1.0.0").description("A tool for creating and applying BPS patches.");E.command("verify").description("verifies a patch file").argument("<patch>","the patch file to read").action((async function(t){try{const{checksum:e}=w(await a(t));i(`Patch has checksum ${e} and is valid.`)}catch(t){c(t)}})),E.command("apply").description("applies a patch to a file").argument("<patch>","the patch file to apply").argument("<source>","the source file to patch").argument("<output>","the location to write the patched file to").action((async function(t,e,o){try{const{instructions:r}=w(await a(t)),n=function({sourceChecksum:t,targetSize:e,targetChecksum:o,actions:r},n){if(d(n)!==t)throw new Error("Source is not compatible with the patch, it does not have the expected checksum.");const a=new DataView(n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength)),s=new DataView(new ArrayBuffer(e));let c=0,i=0,f=0;for(const t of r)switch(t.type){case u.SourceRead:for(let e=0;e<t.length;++e)s.setUint8(c+e,a.getUint8(c+e));c+=t.length;break;case u.TargetRead:for(let e=0,o=t.bytes.length;e<o;++e)s.setUint8(c++,t.bytes[e]);break;case u.SourceCopy:i+=t.offset;for(let e=0;e<t.length;++e)s.setUint8(c++,a.getUint8(i++));break;case u.TargetCopy:f+=t.offset;for(let e=0;e<t.length;++e)s.setUint8(c++,s.getUint8(f++));break;default:throw new Error("Patch is invalid, it contains an invalid action type.")}const h=new Uint8Array(s.buffer);if(d(h)!==o)throw new Error("Resulting target is not valid, it does not have the expected checksum.");return h}(r,await a(e));await s(o,n),i("Patch was applied successfully.")}catch(t){c(t)}})),E.command("create").description("creates a patch from a source and a desired target.").argument("<source>","the source file").argument("<target>","the target file").argument("<output>","the location to write the patch to").action((async function(t,e,o){try{const r=T(function(t,e){const o=t.byteLength,r=d(t),n=e.byteLength,a=d(e),s=[],c=new DataView(t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)),i=new DataView(e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)),f={outputOffset:0,word:0,sourceRelativeOffset:0,sourceWordTable:new m(c),targetRelativeOffset:0,targetWordTable:new m,targetReadLength:0};for(;f.outputOffset<n;){f.word=b(i,f.outputOffset);const t=U(c,i,f),e=k(c,i,f),o=O(i,f);f.targetWordTable.addWordLocation(f.word,f.outputOffset);const r=v(t,e,o);if(r.length<4){const t=Math.min(1,n-f.outputOffset);f.targetReadLength+=t,f.outputOffset+=t}else f.targetReadLength&&(s.push(L(i,f)),f.targetReadLength=0),f.outputOffset+=r.length,r.type===u.SourceCopy&&(f.sourceRelativeOffset=r.offset+f.sourceRelativeOffset+r.length),r.type===u.TargetCopy&&(f.targetRelativeOffset=r.offset+f.targetRelativeOffset+r.length),s.push(r)}return f.targetReadLength&&s.push(L(i,f)),{sourceSize:o,sourceChecksum:r,targetSize:n,targetChecksum:a,actions:s}}(await a(t),await a(e)));await s(o,r.buffer),i(`Patch was generated successfully with checksum ${r.checksum}.`)}catch(t){c(t)}})),E.parse(process.argv);

@@ -28,3 +28,3 @@ /**

/**
* Represents an action that copies bytes from the source to the target.
* Represents a BPS action that copies bytes from the source to the target.
*/

@@ -45,3 +45,3 @@ export interface SourceReadAction

/**
* Represents an action that writes data stored in the patch to the target.
* Represents a BPS action that writes data stored in the patch to the target.
*/

@@ -56,2 +56,7 @@ export interface TargetReadAction

/**
* The number of bytes to write to the target.
*/
length : number;
/**
* The bytes to write to the target.

@@ -63,3 +68,3 @@ */

/**
* Represents an action that seeks to a location of the source and copies data from that location to the target.
* Represents a BPS action that seeks to a location of the source and copies data from that location to the target.
*/

@@ -85,3 +90,3 @@ export interface SourceCopyAction

/**
* Represents an action that seeks to a location of the target and copies data from that location to the target.
* Represents a BPS action that seeks to a location of the target and copies data from that location to the target.
*/

@@ -107,5 +112,5 @@ export interface TargetCopyAction

/**
* Represents a BPS patch.
* Represents BPS instruction set.
*/
export interface Patch
export interface PatchInstructions
{

@@ -120,3 +125,3 @@ /**

*/
sourceChecksum : number,
sourceChecksum : number;

@@ -131,3 +136,3 @@ /**

*/
targetChecksum : number,
targetChecksum : number;

@@ -138,15 +143,42 @@ /**

actions : (SourceReadAction | TargetReadAction | SourceCopyAction | TargetCopyAction)[];
}
/**
* Represents a BPS patch.
*/
export interface Patch
{
/**
* A CRC32 checksum used to verify the patch itself.
* The BPS patch.
*/
patchChecksum : number;
instructions : PatchInstructions;
/**
* A CRC32 checksum used to verify the patch.
*/
checksum : number;
}
/**
* Represents a binary BPS patch.
*/
export interface BinaryPatch
{
/**
* The buffer containing the complete patch, including the BPS header and the CRC32 patch checksum.
*/
buffer : Uint8Array;
/**
* The CRC32 checksum used to verify the patch.
*/
checksum : number;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
* Parses and validates a binary patch.
* Parses a BPS patch from a binary format.
*
* @returns The parsed patch file.
* @returns The parsed patch.
*

@@ -161,12 +193,35 @@ * @param patch The patch to be parsed.

/**
* Applies a patch to a binary source.
* Applies a BPS instruction set to a binary source.
*
* @returns The resulting target.
*
* @param patch The patch to be applied. This needs to be an already parsed patch, see {@link parse}.
* @param source The binary source to be patched.
* @param instructions The instruction set to be applied.
* @param source The binary source to be patched.
*
* @throws {@link Error} When the source does not match the checksum stated in the patch.
* @throws {@link Error} When the resulting target does not match the checksum states in the patch.
* @throws {@link Error} When the source does not match the expected checksum.
* @throws {@link Error} When the resulting target does not match the expected checksum.
*/
export function apply(patch : Patch, source : Uint8Array) : Uint8Array;
export function apply(instructions : PatchInstructions, source : Uint8Array) : Uint8Array;
/**
* Builds a BPS instruction set from a binary source and target.
*
* This prioritizes to produce a small set of instructions over being a fast operation.
*
* @returns The resulting patch.
*
* @param source The binary source.
* @param target The binary target to be created after the source is patched.
*/
export function build(source : Uint8Array, target : Uint8Array) : PatchInstructions;
/**
* Serializes a BPS instruction set into a binary BPS buffer.
*
* The binary result includes the BPS header and the CRC32 checksum of the patch.
*
* @returns The serialized binary patch.
*
* @param patch The instruction set to be serialized.
*/
export function serialize(instructions : PatchInstructions) : BinaryPatch;

@@ -1,1 +0,1 @@

import e from"crc-32";function t(t){return e.buf(t)>>>0}const r=Object.freeze({LE:827543618,BE:1112560433}),n=Object.freeze({SourceRead:0,TargetRead:1,SourceCopy:2,TargetCopy:3});function o(e){return{type:n.SourceRead,length:e}}function a(e){return{type:n.TargetRead,bytes:e}}function i(e,t){return{type:n.SourceCopy,offset:e,length:t}}function c(e,t){return{type:n.TargetCopy,offset:e,length:t}}function s(e,t,r=!0){let n=0,o=1,a=0,i=t;for(;;){if(a=e.getUint8(i++,r),n+=(127&a)*o,128&a)return[n,i];o<<=7,n+=o}}function f(e){const f=new DataView(e.buffer);let u=!0,h=4,l=0,g=0,y=0,d=[],p=0,w=0,b=0;const k=f.getUint32(0,!0);if(k===r.BE)u=!1;else if(k!==r.LE)throw new Error("Patch is not valid, it does not start with a valid `BPS1` header.");for([l,h]=s(f,h,u),[g,h]=s(f,h,u),[y,h]=s(f,h,u),h+=y;h<f.byteLength-12;){let e=0,t=0,r=0;switch([e,h]=s(f,h,u),t=1+(e>>2),3&e){case n.SourceRead:r=o(t);break;case n.TargetRead:{let e=new Array(t);for(let r=0;r<t;++r)e[r]=f.getUint8(h++,u);r=a(e)}break;case n.SourceCopy:{let e=0;[e,h]=s(f,h,u),e=(1&e?-1:1)*(e>>1),r=i(e,t)}break;case n.TargetCopy:{let e=0;[e,h]=s(f,h,u),e=(1&e?-1:1)*(e>>1),r=c(e,t)}}d.push(r)}if(p=f.getUint32(h+0,u),w=f.getUint32(h+4,u),b=f.getUint32(h+8,u),t(e.subarray(0,-4))!==b)throw new Error(`Patch is invalid, it does not have the expected checksum ${b}.`);return{sourceSize:l,targetSize:g,actions:d,sourceChecksum:p,targetChecksum:w,patchChecksum:b}}function u(e,r){if(t(r)!==e.sourceChecksum)throw new Error("Source file is not compatible with the patch, it does not have the expected checksum.");const o=new DataView(r.buffer.slice(r.byteOffset,r.byteOffset+r.byteLength)),a=new DataView(new ArrayBuffer(e.targetSize));let i=0,c=0,s=0;for(const t of e.actions)switch(t.type){case n.SourceRead:for(let e=0;e<t.length;++e)a.setUint8(i+e,o.getUint8(i+e));i+=t.length;break;case n.TargetRead:for(let e=0,r=t.bytes.length;e<r;++e)a.setUint8(i++,t.bytes[e]);break;case n.SourceCopy:c+=t.offset;for(let e=0;e<t.length;++e)a.setUint8(i++,o.getUint8(c++));break;case n.TargetCopy:s+=t.offset;for(let e=0;e<t.length;++e)a.setUint8(i++,a.getUint8(s++));break;default:throw new Error("Patch is invalid, it contains an invalid action type.")}const f=new Uint8Array(a.buffer);if(t(f)!==e.targetChecksum)throw new Error("Resulting file is not valid, it does not have the expected checksum.");return f}export{n as ActionType,u as apply,f as parse};
import t from"crc-32";const e=Object.freeze({LE:827543618,BE:1112560433}),r=Object.freeze({SourceRead:0,TargetRead:1,SourceCopy:2,TargetCopy:3});function n(t){return{type:r.SourceRead,length:t}}function o(t){return{type:r.TargetRead,bytes:t,length:t.length}}function s(t,e){return{type:r.SourceCopy,offset:t,length:e}}function f(t,e){return{type:r.TargetCopy,offset:t,length:e}}function a(e){return t.buf(e)>>>0}function i(t,e){let r=0,n=1,o=0,s=e;for(;;){if(o=t.getUint8(s++),r+=(127&o)*n,128&o)return[r,s];n<<=7,r+=n}}function c(t){const c=new DataView(t.buffer);let u=!0,g=0,h=0,l=0,y=0,d=[],p=0,b=0,w=0;const U=c.getUint32(g,!0);if(U===e.BE)u=!1;else if(U!==e.LE)throw new Error("Patch is not valid, it does not start with a valid `BPS1` header.");for(g+=4,[h,g]=i(c,g),[l,g]=i(c,g),[y,g]=i(c,g),g+=y;g<c.byteLength-12;){let t=0,e=0,a=0;switch([t,g]=i(c,g),e=1+(t>>2),3&t){case r.SourceRead:a=n(e);break;case r.TargetRead:{let t=new Array(e);for(let r=0;r<e;++r)t[r]=c.getUint8(g++,u);a=o(t)}break;case r.SourceCopy:{let t=0;[t,g]=i(c,g),t=(1&t?-1:1)*(t>>1),a=s(t,e)}break;case r.TargetCopy:{let t=0;[t,g]=i(c,g),t=(1&t?-1:1)*(t>>1),a=f(t,e)}}d.push(a)}if(p=c.getUint32(g+0,u),b=c.getUint32(g+4,u),w=c.getUint32(g+8,u),a(t.subarray(0,-4))!==w)throw new Error(`Patch is invalid, it does not have the expected checksum ${w}.`);return{instructions:{sourceSize:h,sourceChecksum:p,targetSize:l,targetChecksum:b,actions:d},checksum:w}}function u({sourceChecksum:t,targetSize:e,targetChecksum:n,actions:o},s){if(a(s)!==t)throw new Error("Source is not compatible with the patch, it does not have the expected checksum.");const f=new DataView(s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength)),i=new DataView(new ArrayBuffer(e));let c=0,u=0,g=0;for(const t of o)switch(t.type){case r.SourceRead:for(let e=0;e<t.length;++e)i.setUint8(c+e,f.getUint8(c+e));c+=t.length;break;case r.TargetRead:for(let e=0,r=t.bytes.length;e<r;++e)i.setUint8(c++,t.bytes[e]);break;case r.SourceCopy:u+=t.offset;for(let e=0;e<t.length;++e)i.setUint8(c++,f.getUint8(u++));break;case r.TargetCopy:g+=t.offset;for(let e=0;e<t.length;++e)i.setUint8(c++,i.getUint8(g++));break;default:throw new Error("Patch is invalid, it contains an invalid action type.")}const h=new Uint8Array(i.buffer);if(a(h)!==n)throw new Error("Resulting target is not valid, it does not have the expected checksum.");return h}function g(t,e){let r=t.getUint8(e);return e<t.byteLength-1&&(r|=t.getUint8(e+1)<<8),r}class h{#t=new Array(65536);constructor(t=null){if(t)for(let e=0,r=t.byteLength;e<r;++e)this.addWordLocation(g(t,e),e)}addWordLocation(t,e){const r=this.#t[t];r?r.push(e):this.#t[t]=[e]}getWordLocations(t){return this.#t[t]??[]}}function l(t,e,{outputOffset:r}){let o=0,s=r;for(;s<t.byteLength&&s<e.byteLength&&t.getUint8(s)===e.getUint8(s);)o++,s++;return n(o)}function y(t,{outputOffset:e,targetReadLength:r}){const n=[],s=e-r;for(let e=0;e<r;++e)n.push(t.getUint8(s+e));return o(n)}function d(t,e,{word:r,outputOffset:n,sourceRelativeOffset:o,sourceWordTable:f}){let a=0,i=0;const c=f.getWordLocations(r);for(const r of c){let o=0,s=r,f=n;for(;s<t.byteLength&&f<e.byteLength&&t.getUint8(s++)===e.getUint8(f++);)o++;o>a&&(a=o,i=r)}return s(i-o,a)}function p(t,{word:e,outputOffset:r,targetRelativeOffset:n,targetWordTable:o}){let s=0,a=0;const i=o.getWordLocations(e);for(const e of i){let n=0,o=e,f=r;for(;f<t.byteLength&&t.getUint8(o++)===t.getUint8(f++);)n++;n>s&&(s=n,a=e)}return f(a-n,s)}function b(...t){let e=t[0];for(let r=1,n=t.length;r<n;++r)t[r].length>e.length&&(e=t[r]);return e}function w(t,e){const n=t.byteLength,o=a(t),s=e.byteLength,f=a(e),i=[],c=new DataView(t.buffer.slice(t.byteOffset,t.byteOffset+t.byteLength)),u=new DataView(e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength)),w={outputOffset:0,word:0,sourceRelativeOffset:0,sourceWordTable:new h(c),targetRelativeOffset:0,targetWordTable:new h,targetReadLength:0};for(;w.outputOffset<s;){w.word=g(u,w.outputOffset);const t=l(c,u,w),e=d(c,u,w),n=p(u,w);w.targetWordTable.addWordLocation(w.word,w.outputOffset);const o=b(t,e,n);if(o.length<4){const t=Math.min(1,s-w.outputOffset);w.targetReadLength+=t,w.outputOffset+=t}else w.targetReadLength&&(i.push(y(u,w)),w.targetReadLength=0),w.outputOffset+=o.length,o.type===r.SourceCopy&&(w.sourceRelativeOffset=o.offset+w.sourceRelativeOffset+o.length),o.type===r.TargetCopy&&(w.targetRelativeOffset=o.offset+w.targetRelativeOffset+o.length),i.push(o)}return w.targetReadLength&&i.push(y(u,w)),{sourceSize:n,sourceChecksum:o,targetSize:s,targetChecksum:f,actions:i}}function U(t){let e=0,r=t;for(;;){if(e++,r>>=7,0===r)return e;r--}}function L(t,e,r){let n=r,o=e;for(;;){let e=127&n;if(n>>=7,0===n){t.setUint8(o++,128|e);break}t.setUint8(o++,e),n--}return o}function O(t,e,n){let o=L(t,e,(n.length-1<<2)+n.type);return n.type===r.TargetRead?n.bytes.forEach((e=>t.setUint8(o++,e))):n.type!==r.SourceCopy&&n.type!==r.TargetCopy||(o=L(t,o,(Math.abs(n.offset)<<1)+(n.offset<0?1:0))),o}function R({sourceSize:t,sourceChecksum:n,targetSize:o,targetChecksum:s,actions:f}){const i=4+U(t)+U(o)+U(0)+f.reduce(((t,e)=>t+function(t){let e=U((t.length-1<<2)+t.type);return t.type===r.TargetRead?e+=t.length:t.type!==r.SourceCopy&&t.type!==r.TargetCopy||(e+=U((Math.abs(t.offset)<<1)+(t.offset<0?1:0))),e}(e)),0)+4+4+4,c=new DataView(new ArrayBuffer(i)),u=new Uint8Array(c.buffer);let g=0;c.setUint32(g,e.LE,!0),g=4,g=L(c,g,t),g=L(c,g,o),g=L(c,g,0);for(const t of f)g=O(c,g,t);c.setUint32(g,n,!0),c.setUint32(g+4,s,!0);const h=a(u.subarray(0,-4));return c.setUint32(g+8,h,!0),{buffer:u,checksum:h}}export{r as ActionType,u as apply,w as build,c as parse,R as serialize};

@@ -5,4 +5,40 @@ # Changelog

## 2.0.0 - 2024-02-24
### Added
- Introduced the `bps.build()` function that enables you to build an instruction set from a source and a desired target.
- Introduced the `bps.serialize()` function that enables you to serialize an instruction set into a binary BPS buffer.
- Introduced the `create` command in the CLI tool that enables you to create a BPS patch file from a source file and a desired target file.
### Changed
- The result of `bps.parse()` has been changed, it now has a different shape. Instead of this object structure:
```
{
sourceSize : 0,
sourceChecksum : 0,
targetSize : 0,
targetChecksum : 0,
actions : [],
patchChecksum : 0
}
```
You will instead recieve an object with this structure:
```
{
instructions : {
sourceSize : 0,
sourceChecksum : 0,
targetSize : 0,
targetChecksum : 0,
actions : []
},
checksum : 0
}
```
- The `bps.patch()` function no longer takes the entire patch previously returned by `bps.parse()` but instead just the instruction set.
## 1.0.0 - 2024-02-02
The initial public release.
{
"name" : "bps",
"version" : "1.0.0",
"version" : "2.0.0",

@@ -35,3 +35,3 @@ "type" : "module",

"crc-32" : "1.2.2",
"commander" : "11.1.0",
"commander" : "12.0.0",
"chalk-template" : "1.1.0",

@@ -47,8 +47,8 @@ "fs-extra" : "11.2.0"

"eslint-plugin-node" : "11.1.0",
"@stylistic/eslint-plugin-js" : "1.5.4",
"@stylistic/eslint-plugin-js" : "1.6.2",
"eslint-config-protect-me-from-my-stupidity" : "10.0.0",
"rollup" : "4.9.6",
"rollup" : "4.12.0",
"@rollup/plugin-terser" : "0.4.4",
"mocha" : "10.2.0",
"chai" : "5.0.3"
"mocha" : "10.3.0",
"chai" : "5.1.0"
},

@@ -55,0 +55,0 @@

@@ -15,3 +15,3 @@ # `bps`

// or
import { parse, apply, ActionType } from 'bps';
import { parse, apply, build, serialize, ActionType } from 'bps';
```

@@ -24,3 +24,3 @@

// or
const { parse, apply, ActionType } = require('bps');
const { parse, apply, build, serialize, ActionType } = require('bps');
```

@@ -30,3 +30,3 @@

You can a parse and validate a binary BPS patch:
You can parse a BPS binary patch into an instruction set:

@@ -38,7 +38,10 @@ ``` js

{
const patch = parse(file);
const {
instructions,
checksum
} = bps.parse(file);
}
catch (error)
{
// Throws an error when the patch is invalud, e.g. when
// Throws an error when the patch is invalid, e.g. when
// the patch doesn't have a valid BPS header.

@@ -48,33 +51,5 @@ }

A patch object will have the following fields:
| Property | Type | Description |
| ---------------- | :--------: | ------------------------------------------------------------------------------- |
| `sourceSize` | `number` | The expected size (in bytes) that the source should be. |
| `sourceChecksum` | `number` | A CRC32 checksum used to verify the source. |
| `targetSize` | `number` | The expected size (in bytes) that the target should be. |
| `targetChecksum` | `number` | A CRC32 checksum used to verify the target. |
| `actions` | `Object[]` | The actions describing how to sequentially create a new target from the source. |
| `patchChecksum` | `number` | A CRC32 checksum used to verify the patch itself. |
There are four types of action objects, each with a `type` discriminator property. The four action types are:
- `ActionType.SourceRead` \
Represents an action that copies bytes from the source to the target. These action objects will have the following properties:
- `length` - The number of bytes to copy from the source.
- `ActionType.TargetRead` \
Represents an action that writes data stored in the patch to the target. These action objects will have the following properties:
- `bytes` - The bytes to write to the target.
- `ActionType.SourceCopy` \
Represents an action that seeks to a location of the source and copies data from that location to the target. These action objects will have the following properties:
- `offset` - The amount to move the source relative offset by (can be negative to move backwards).
- `length` - The number of bytes to copy from the source.
- `ActionType.TargetCopy` \
Represents an action that seeks to a location of the target and copies data from that location to the target. These action objects will have the following properties:
- `offset` - The amount to move the target relative offset by (can be negative to move backwards).
- `length` - The number of bytes to copy from the target.
### Applying a BPS patch
Once you have parsed a BPS patch, you can then apply it to a binary source:
You can apply an instruction set to a binary source:

@@ -86,3 +61,3 @@ ``` js

{
const target = apply(patch, source);
const target = bps.apply(instructions, source);
}

@@ -92,8 +67,60 @@ catch (error)

// Throws an error when the provided source does not
// match the checksum stated by the patch.
// match the checksum stated in the patch instructions.
}
```
**Important Note:** The source is not modified! The result is a new binary buffer.
### Building a BPS patch
You can build an instruction set from a source and a desired target:
``` js
const instructions = bps.build(
await fs.readFile('source.txt', null),
await fs.readFile('target.txt', null)
);
```
### Serializing a BPS patch
You can serialize an instruction set into a binary BPS buffer:
``` js
const {
buffer,
checksum
} = bps.serialize(instructions);
await fs.writeFile('patch.bps', buffer, null);
```
### Instruction sets
An instruction set will have the following fields:
| Property | Type | Description |
| ---------------- | :--------: | ------------------------------------------------------------------------------- |
| `sourceSize` | `number` | The expected size (in bytes) that the source should be. |
| `sourceChecksum` | `number` | A CRC32 checksum used to verify the source. |
| `targetSize` | `number` | The expected size (in bytes) that the target should be. |
| `targetChecksum` | `number` | A CRC32 checksum used to verify the target. |
| `actions` | `Object[]` | The actions describing how to sequentially create a new target from the source. |
An instruction set compromises of actions, an action results in bytes being appended to the target. Each action has the following properties:
- `type`\
A discriminator property stating what type of action it is.
- `length`\
The number of bytes that the action will write to the target.
The four action types are:
- `ActionType.SourceRead`\
Represents an action that copies a number of bytes from the source to the target.
- `ActionType.TargetRead`\
Represents an action that writes specified bytes to the target. These actions will have an additional property called `bytes` which will be an array of bytes to write to the target.
- `ActionType.SourceCopy`\
Represents an action that seeks to a location (a.k.a. relative offset) in the source and copies a number of bytes from that location to the target. These actions will have an additional property called `offset` which describes the amount to move the source relative offset by, this can be negative to move backwards.
- `ActionType.TargetCopy`\
Represents an action that seeks to a location (a.k.a. relative offset) in the target that has been produced up to this point and copies a number of bytes from that location to the target. These actions will have an additional property called `offset` which describes the amount to move the target relative offset by, this can be negative to move backwards.
## Getting started

@@ -119,9 +146,10 @@

Options:
-V, --version output the version number
-h, --help display help for command
-V, --version output the version number
-h, --help display help for command
Commands:
verify <patch> verifies a patch file
apply <patch> <source> <output> applies a patch to a file
help [command] display help for command
verify <patch> verifies a patch file
apply <patch> <source> <output> applies a patch to a file
create <source> <target> <output> creates a patch from a source and a desired target.
help [command] display help for command
```

@@ -128,0 +156,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc