Skip to content

Commit

Permalink
dist: build
Browse files Browse the repository at this point in the history
  • Loading branch information
kdrag0n committed May 9, 2023
1 parent 071c8ef commit d30a5a2
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 76 deletions.
82 changes: 47 additions & 35 deletions dist/fastboot.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ var ChunkType;
ChunkType[ChunkType["Skip"] = 51907] = "Skip";
ChunkType[ChunkType["Crc32"] = 51908] = "Crc32";
})(ChunkType || (ChunkType = {}));
class BlobBuilder {
constructor(type = "") {
this.type = type;
this.blob = new Blob([], { type: this.type });
}
append(blob) {
this.blob = new Blob([this.blob, blob], { type: this.type });
}
getBlob() {
return this.blob;
}
}
/**
* Returns a parsed version of the sparse image file header from the given buffer.
*
Expand Down Expand Up @@ -184,16 +196,17 @@ function calcChunksBlockSize(chunks) {
}
function calcChunksDataSize(chunks) {
return chunks
.map((chunk) => chunk.data.byteLength)
.map((chunk) => chunk.data.size)
.reduce((total, c) => total + c, 0);
}
function calcChunksSize(chunks) {
// 28-byte file header, 12-byte chunk headers
let overhead = FILE_HEADER_SIZE + CHUNK_HEADER_SIZE * chunks.length;
return overhead + calcChunksDataSize(chunks);
}
function createImage(header, chunks) {
let buffer = new ArrayBuffer(calcChunksSize(chunks));
async function createImage(header, chunks) {
let blobBuilder = new BlobBuilder();
let buffer = new ArrayBuffer(FILE_HEADER_SIZE);
let dataView = new DataView(buffer);
let arrayView = new Uint8Array(buffer);
dataView.setUint32(0, FILE_MAGIC, true);
Expand All @@ -210,41 +223,43 @@ function createImage(header, chunks) {
// but AOSP libsparse always sets 0 and puts the CRC in a final undocumented
// 0xCAC4 chunk instead.
dataView.setUint32(24, 0, true);
let chunkOff = FILE_HEADER_SIZE;
blobBuilder.append(new Blob([buffer]));
for (let chunk of chunks) {
dataView.setUint16(chunkOff, chunk.type, true);
dataView.setUint16(chunkOff + 2, 0, true); // reserved
dataView.setUint32(chunkOff + 4, chunk.blocks, true);
dataView.setUint32(chunkOff + 8, CHUNK_HEADER_SIZE + chunk.data.byteLength, true);
chunkOff += CHUNK_HEADER_SIZE;
let chunkArrayView = new Uint8Array(chunk.data);
arrayView.set(chunkArrayView, chunkOff);
chunkOff += chunk.data.byteLength;
buffer = new ArrayBuffer(CHUNK_HEADER_SIZE + chunk.data.size);
dataView = new DataView(buffer);
arrayView = new Uint8Array(buffer);
dataView.setUint16(0, chunk.type, true);
dataView.setUint16(2, 0, true); // reserved
dataView.setUint32(4, chunk.blocks, true);
dataView.setUint32(8, CHUNK_HEADER_SIZE + chunk.data.size, true);
let chunkArrayView = new Uint8Array(await readBlobAsBuffer(chunk.data));
arrayView.set(chunkArrayView, CHUNK_HEADER_SIZE);
blobBuilder.append(new Blob([buffer]));
}
return buffer;
return blobBuilder.getBlob();
}
/**
* Creates a sparse image from buffer containing raw image data.
*
* @param {ArrayBuffer} rawBuffer - Buffer containing the raw image data.
* @returns {ArrayBuffer} Buffer containing the new sparse image.
* @param {Blob} blob - Blob containing the raw image data.
* @returns {Promise<Blob>} Promise that resolves the blob containing the new sparse image.
*/
function fromRaw(rawBuffer) {
async function fromRaw(blob) {
let header = {
blockSize: 4096,
blocks: rawBuffer.byteLength / 4096,
blocks: blob.size / 4096,
chunks: 1,
crc32: 0,
};
let chunks = [];
while (rawBuffer.byteLength > 0) {
let chunkSize = Math.min(rawBuffer.byteLength, RAW_CHUNK_SIZE);
while (blob.size > 0) {
let chunkSize = Math.min(blob.size, RAW_CHUNK_SIZE);
chunks.push({
type: ChunkType.Raw,
blocks: chunkSize / header.blockSize,
data: rawBuffer.slice(0, chunkSize),
data: blob.slice(0, chunkSize),
});
rawBuffer = rawBuffer.slice(chunkSize);
blob = blob.slice(chunkSize);
}
return createImage(header, chunks);
}
Expand Down Expand Up @@ -281,7 +296,7 @@ async function* splitBlob(blob, splitSize) {
for (let i = 0; i < header.chunks; i++) {
let chunkHeaderData = await readBlobAsBuffer(blob.slice(0, CHUNK_HEADER_SIZE));
let chunk = parseChunkHeader(chunkHeaderData);
chunk.data = await readBlobAsBuffer(blob.slice(CHUNK_HEADER_SIZE, CHUNK_HEADER_SIZE + chunk.dataBytes));
chunk.data = blob.slice(CHUNK_HEADER_SIZE, CHUNK_HEADER_SIZE + chunk.dataBytes);
blob = blob.slice(CHUNK_HEADER_SIZE + chunk.dataBytes);
let bytesRemaining = splitSize - calcChunksSize(splitChunks);
logVerbose(` Chunk ${i}: type ${chunk.type}, ${chunk.dataBytes} bytes / ${chunk.blocks} blocks, ${bytesRemaining} bytes remaining`);
Expand All @@ -300,14 +315,14 @@ async function* splitBlob(blob, splitSize) {
splitChunks.push({
type: ChunkType.Skip,
blocks: header.blocks - splitBlocks,
data: new ArrayBuffer(0),
data: new Blob([]),
dataBytes: 0,
});
logVerbose(`Partition is ${header.blocks} blocks, used ${splitBlocks}, padded with ${header.blocks - splitBlocks}, finishing split with ${calcChunksBlockSize(splitChunks)} blocks`);
let splitImage = createImage(header, splitChunks);
logDebug(`Finished ${splitImage.byteLength}-byte split with ${splitChunks.length} chunks`);
let splitImage = await createImage(header, splitChunks);
logDebug(`Finished ${splitImage.size}-byte split with ${splitChunks.length} chunks`);
yield {
data: splitImage,
data: await readBlobAsBuffer(splitImage),
bytes: splitDataBytes,
};
// Start a new split. Every split is considered a full image by the
Expand All @@ -317,7 +332,7 @@ async function* splitBlob(blob, splitSize) {
{
type: ChunkType.Skip,
blocks: splitBlocks,
data: new ArrayBuffer(0),
data: new Blob([]),
dataBytes: 0,
},
chunk,
Expand All @@ -328,10 +343,10 @@ async function* splitBlob(blob, splitSize) {
// Finish the final split if necessary
if (splitChunks.length > 0 &&
(splitChunks.length > 1 || splitChunks[0].type !== ChunkType.Skip)) {
let splitImage = createImage(header, splitChunks);
logDebug(`Finishing final ${splitImage.byteLength}-byte split with ${splitChunks.length} chunks`);
let splitImage = await createImage(header, splitChunks);
logDebug(`Finishing final ${splitImage.size}-byte split with ${splitChunks.length} chunks`);
yield {
data: splitImage,
data: await readBlobAsBuffer(splitImage),
bytes: splitDataBytes,
};
}
Expand Down Expand Up @@ -7959,6 +7974,7 @@ const SYSTEM_IMAGES = [
"odm",
"odm_dlkm",
"product",
"system_dlkm",
"system_ext",
"system",
"vendor_dlkm",
Expand Down Expand Up @@ -8574,11 +8590,7 @@ class FastbootDevice {
// Convert image to sparse (for splitting) if it exceeds the size limit
if (blob.size > maxDlSize && !isSparse) {
logDebug(`${partition} image is raw, converting to sparse`);
// Assume that non-sparse images will always be small enough to convert in RAM.
// The buffer is converted to a Blob for compatibility with the existing flashing code.
let rawData = await readBlobAsBuffer(blob);
let sparse = fromRaw(rawData);
blob = new Blob([sparse]);
blob = await fromRaw(blob);
}
logDebug(`Flashing ${blob.size} bytes to ${partition}, ${maxDlSize} bytes per split`);
let splits = 0;
Expand Down
2 changes: 1 addition & 1 deletion dist/fastboot.cjs.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/fastboot.min.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/fastboot.min.cjs.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/fastboot.min.mjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/fastboot.min.mjs.map

Large diffs are not rendered by default.

82 changes: 47 additions & 35 deletions dist/fastboot.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,18 @@ var ChunkType;
ChunkType[ChunkType["Skip"] = 51907] = "Skip";
ChunkType[ChunkType["Crc32"] = 51908] = "Crc32";
})(ChunkType || (ChunkType = {}));
class BlobBuilder {
constructor(type = "") {
this.type = type;
this.blob = new Blob([], { type: this.type });
}
append(blob) {
this.blob = new Blob([this.blob, blob], { type: this.type });
}
getBlob() {
return this.blob;
}
}
/**
* Returns a parsed version of the sparse image file header from the given buffer.
*
Expand Down Expand Up @@ -180,16 +192,17 @@ function calcChunksBlockSize(chunks) {
}
function calcChunksDataSize(chunks) {
return chunks
.map((chunk) => chunk.data.byteLength)
.map((chunk) => chunk.data.size)
.reduce((total, c) => total + c, 0);
}
function calcChunksSize(chunks) {
// 28-byte file header, 12-byte chunk headers
let overhead = FILE_HEADER_SIZE + CHUNK_HEADER_SIZE * chunks.length;
return overhead + calcChunksDataSize(chunks);
}
function createImage(header, chunks) {
let buffer = new ArrayBuffer(calcChunksSize(chunks));
async function createImage(header, chunks) {
let blobBuilder = new BlobBuilder();
let buffer = new ArrayBuffer(FILE_HEADER_SIZE);
let dataView = new DataView(buffer);
let arrayView = new Uint8Array(buffer);
dataView.setUint32(0, FILE_MAGIC, true);
Expand All @@ -206,41 +219,43 @@ function createImage(header, chunks) {
// but AOSP libsparse always sets 0 and puts the CRC in a final undocumented
// 0xCAC4 chunk instead.
dataView.setUint32(24, 0, true);
let chunkOff = FILE_HEADER_SIZE;
blobBuilder.append(new Blob([buffer]));
for (let chunk of chunks) {
dataView.setUint16(chunkOff, chunk.type, true);
dataView.setUint16(chunkOff + 2, 0, true); // reserved
dataView.setUint32(chunkOff + 4, chunk.blocks, true);
dataView.setUint32(chunkOff + 8, CHUNK_HEADER_SIZE + chunk.data.byteLength, true);
chunkOff += CHUNK_HEADER_SIZE;
let chunkArrayView = new Uint8Array(chunk.data);
arrayView.set(chunkArrayView, chunkOff);
chunkOff += chunk.data.byteLength;
buffer = new ArrayBuffer(CHUNK_HEADER_SIZE + chunk.data.size);
dataView = new DataView(buffer);
arrayView = new Uint8Array(buffer);
dataView.setUint16(0, chunk.type, true);
dataView.setUint16(2, 0, true); // reserved
dataView.setUint32(4, chunk.blocks, true);
dataView.setUint32(8, CHUNK_HEADER_SIZE + chunk.data.size, true);
let chunkArrayView = new Uint8Array(await readBlobAsBuffer(chunk.data));
arrayView.set(chunkArrayView, CHUNK_HEADER_SIZE);
blobBuilder.append(new Blob([buffer]));
}
return buffer;
return blobBuilder.getBlob();
}
/**
* Creates a sparse image from buffer containing raw image data.
*
* @param {ArrayBuffer} rawBuffer - Buffer containing the raw image data.
* @returns {ArrayBuffer} Buffer containing the new sparse image.
* @param {Blob} blob - Blob containing the raw image data.
* @returns {Promise<Blob>} Promise that resolves the blob containing the new sparse image.
*/
function fromRaw(rawBuffer) {
async function fromRaw(blob) {
let header = {
blockSize: 4096,
blocks: rawBuffer.byteLength / 4096,
blocks: blob.size / 4096,
chunks: 1,
crc32: 0,
};
let chunks = [];
while (rawBuffer.byteLength > 0) {
let chunkSize = Math.min(rawBuffer.byteLength, RAW_CHUNK_SIZE);
while (blob.size > 0) {
let chunkSize = Math.min(blob.size, RAW_CHUNK_SIZE);
chunks.push({
type: ChunkType.Raw,
blocks: chunkSize / header.blockSize,
data: rawBuffer.slice(0, chunkSize),
data: blob.slice(0, chunkSize),
});
rawBuffer = rawBuffer.slice(chunkSize);
blob = blob.slice(chunkSize);
}
return createImage(header, chunks);
}
Expand Down Expand Up @@ -277,7 +292,7 @@ async function* splitBlob(blob, splitSize) {
for (let i = 0; i < header.chunks; i++) {
let chunkHeaderData = await readBlobAsBuffer(blob.slice(0, CHUNK_HEADER_SIZE));
let chunk = parseChunkHeader(chunkHeaderData);
chunk.data = await readBlobAsBuffer(blob.slice(CHUNK_HEADER_SIZE, CHUNK_HEADER_SIZE + chunk.dataBytes));
chunk.data = blob.slice(CHUNK_HEADER_SIZE, CHUNK_HEADER_SIZE + chunk.dataBytes);
blob = blob.slice(CHUNK_HEADER_SIZE + chunk.dataBytes);
let bytesRemaining = splitSize - calcChunksSize(splitChunks);
logVerbose(` Chunk ${i}: type ${chunk.type}, ${chunk.dataBytes} bytes / ${chunk.blocks} blocks, ${bytesRemaining} bytes remaining`);
Expand All @@ -296,14 +311,14 @@ async function* splitBlob(blob, splitSize) {
splitChunks.push({
type: ChunkType.Skip,
blocks: header.blocks - splitBlocks,
data: new ArrayBuffer(0),
data: new Blob([]),
dataBytes: 0,
});
logVerbose(`Partition is ${header.blocks} blocks, used ${splitBlocks}, padded with ${header.blocks - splitBlocks}, finishing split with ${calcChunksBlockSize(splitChunks)} blocks`);
let splitImage = createImage(header, splitChunks);
logDebug(`Finished ${splitImage.byteLength}-byte split with ${splitChunks.length} chunks`);
let splitImage = await createImage(header, splitChunks);
logDebug(`Finished ${splitImage.size}-byte split with ${splitChunks.length} chunks`);
yield {
data: splitImage,
data: await readBlobAsBuffer(splitImage),
bytes: splitDataBytes,
};
// Start a new split. Every split is considered a full image by the
Expand All @@ -313,7 +328,7 @@ async function* splitBlob(blob, splitSize) {
{
type: ChunkType.Skip,
blocks: splitBlocks,
data: new ArrayBuffer(0),
data: new Blob([]),
dataBytes: 0,
},
chunk,
Expand All @@ -324,10 +339,10 @@ async function* splitBlob(blob, splitSize) {
// Finish the final split if necessary
if (splitChunks.length > 0 &&
(splitChunks.length > 1 || splitChunks[0].type !== ChunkType.Skip)) {
let splitImage = createImage(header, splitChunks);
logDebug(`Finishing final ${splitImage.byteLength}-byte split with ${splitChunks.length} chunks`);
let splitImage = await createImage(header, splitChunks);
logDebug(`Finishing final ${splitImage.size}-byte split with ${splitChunks.length} chunks`);
yield {
data: splitImage,
data: await readBlobAsBuffer(splitImage),
bytes: splitDataBytes,
};
}
Expand Down Expand Up @@ -7955,6 +7970,7 @@ const SYSTEM_IMAGES = [
"odm",
"odm_dlkm",
"product",
"system_dlkm",
"system_ext",
"system",
"vendor_dlkm",
Expand Down Expand Up @@ -8570,11 +8586,7 @@ class FastbootDevice {
// Convert image to sparse (for splitting) if it exceeds the size limit
if (blob.size > maxDlSize && !isSparse) {
logDebug(`${partition} image is raw, converting to sparse`);
// Assume that non-sparse images will always be small enough to convert in RAM.
// The buffer is converted to a Blob for compatibility with the existing flashing code.
let rawData = await readBlobAsBuffer(blob);
let sparse = fromRaw(rawData);
blob = new Blob([sparse]);
blob = await fromRaw(blob);
}
logDebug(`Flashing ${blob.size} bytes to ${partition}, ${maxDlSize} bytes per split`);
let splits = 0;
Expand Down
2 changes: 1 addition & 1 deletion dist/fastboot.mjs.map

Large diffs are not rendered by default.

0 comments on commit d30a5a2

Please sign in to comment.