Implemented CRC32-IEEE for PNG files
Update crc32.mjs, data-utils.mjs, and png-utils.mjs
This commit is contained in:
33
static/resources/NibblePoker/libs/crc32.mjs
Normal file
33
static/resources/NibblePoker/libs/crc32.mjs
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
/**
|
||||
* @param data {Uint8Array}
|
||||
* @param crc32 {number}
|
||||
* @return {number}
|
||||
*/
|
||||
export function stepCrc32IEEE(data, crc32 = 0xffffffff) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
crc32 ^= data[i];
|
||||
for (let j = 0; j < 8; j++) {
|
||||
crc32 = (crc32 >>> 1) ^ (crc32 & 1 ? 0xedb88320 : 0);
|
||||
}
|
||||
}
|
||||
return crc32;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param crc32 {number}
|
||||
* @return {number}
|
||||
*/
|
||||
export function finishCrc32IEEE(crc32) {
|
||||
return (crc32 ^ 0xffffffff) >>> 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the CRC32-IEEE for the given data
|
||||
* @param data {Uint8Array}
|
||||
* @param crc32 {number}
|
||||
* @return {number} The resulting CRC32
|
||||
*/
|
||||
export function crc32IEEE(data, crc32 = 0xffffffff) {
|
||||
return finishCrc32IEEE(stepCrc32IEEE(data, crc32));
|
||||
}
|
@@ -50,3 +50,36 @@ export function peekUInt32LE(data, offset = 0) {
|
||||
}
|
||||
return new DataView(data.buffer, offset, 4).getUint32(0, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given `Uint8Array` to a hexadecimal representation.
|
||||
* @param data The data to be transformed into a string.
|
||||
* @return {string} The resulting hexadecimal string.
|
||||
*/
|
||||
export function Uint8ArrayToHex(data) {
|
||||
return Array.prototype.map.call(data, x => ('00' + x.toString(16)).slice(-2)).join('');
|
||||
}
|
||||
|
||||
/*export function decimalToHexString(number) {
|
||||
if (number < 0) {
|
||||
number = 0xFFFFFFFF + number + 1;
|
||||
}
|
||||
return number.toString(16).toUpperCase();
|
||||
}*/
|
||||
|
||||
export function AsciiStringToUint8Array(asciiText) {
|
||||
return Uint8Array.from(Array.from(asciiText).map(asciiLetter => asciiLetter.charCodeAt(0)));
|
||||
}
|
||||
|
||||
export function Int32ToUint8Array(number) {
|
||||
/*let arr = new ArrayBuffer(4);
|
||||
new DataView(arr).setUint32(0, number);
|
||||
return new Uint8Array(arr);*/
|
||||
|
||||
return new Uint8Array([
|
||||
(number >>> 24) & 0xFF,
|
||||
(number >>> 16) & 0xFF,
|
||||
(number >>> 8) & 0xFF,
|
||||
number & 0xFF
|
||||
]);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
|
||||
//import {__crc32, crc32b, _crc32b, decimalToHexString} from "./crc32.mjs";
|
||||
import {areUintArraysEqual, peekUInt32BE, peekUInt32LE} from "./data-utils.mjs"
|
||||
import {stepCrc32IEEE, finishCrc32IEEE} from "./crc32.mjs";
|
||||
import {areUintArraysEqual, peekUInt32BE, AsciiStringToUint8Array, Int32ToUint8Array, Uint8ArrayToHex} from "./data-utils.mjs"
|
||||
import {loadFileAsUint8Array} from "./file-utils.mjs";
|
||||
|
||||
/**
|
||||
@@ -14,8 +14,12 @@ export class PngInvalidStructureError extends PngError {}
|
||||
|
||||
export class PngInvalidChunkNameError extends PngError {}
|
||||
|
||||
export class PngInvalidChunkChecksumError extends PngError {}
|
||||
|
||||
export class PngInvalidImageHeaderError extends PngError {}
|
||||
|
||||
export class PngMissingCriticalChunksError extends PngError {}
|
||||
|
||||
export const PngFileHeader = new Uint8Array([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
|
||||
|
||||
export class PngChunk {
|
||||
@@ -34,10 +38,29 @@ export class PngChunk {
|
||||
constructor(type, data, expectedChecksum) {
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
|
||||
if(expectedChecksum !== null) {
|
||||
if(!areUintArraysEqual(expectedChecksum, this.getChecksumUint8Array())) {
|
||||
throw new PngInvalidChunkChecksumError(
|
||||
`Invalid checksum was given ! (Expected ${
|
||||
Uint8ArrayToHex(expectedChecksum)}, got ${
|
||||
Uint8ArrayToHex(this.getChecksumUint8Array())})`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getChecksum() {
|
||||
throw new Error("This function isn't implemented yet !");
|
||||
getChecksumNumber() {
|
||||
return finishCrc32IEEE(
|
||||
stepCrc32IEEE(
|
||||
this.data,
|
||||
stepCrc32IEEE(AsciiStringToUint8Array(this.type))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
getChecksumUint8Array() {
|
||||
return Int32ToUint8Array(this.getChecksumNumber());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,17 +134,22 @@ export class PngFile {
|
||||
/**
|
||||
* @param file {File|null}
|
||||
* @param fileData {Uint8Array|null}
|
||||
* @throws PngInvalidFileHeaderError If the `fileData` is provided and doesn't contain a valid PNG file header.
|
||||
* @throws PngInvalidFileHeaderError If `fileData` is provided and doesn't contain a valid PNG file header.
|
||||
* @throws PngMissingCriticalChunksError If `fileData` is provided and is missing some critical chunks.
|
||||
*/
|
||||
constructor(file = null, fileData = null) {
|
||||
this.originalFile = file;
|
||||
|
||||
this.chunks = [];
|
||||
|
||||
// Parsing the data
|
||||
// Parsing and validating the data if given
|
||||
if(fileData !== null) {
|
||||
this.#validateFileHeader(fileData);
|
||||
this.#parseChunks(fileData);
|
||||
|
||||
if(!this.hasEndChunk() || this.getChunkByType("IHDR") === null) {
|
||||
throw new PngMissingCriticalChunksError("One or more critical chunk is missing from the PNG file !");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user