|
@@ -10,16 +10,14 @@ import { ReaderResult as Result } from '../result'
|
|
import { FileHandle } from '../../common/file-handle';
|
|
import { FileHandle } from '../../common/file-handle';
|
|
import { SimpleBuffer } from 'mol-io/common/simple-buffer';
|
|
import { SimpleBuffer } from 'mol-io/common/simple-buffer';
|
|
|
|
|
|
-export async function readCcp4Header(file: FileHandle) {
|
|
|
|
|
|
+export async function readCcp4Header(file: FileHandle): Promise<{ header: Ccp4Header, littleEndian: boolean }> {
|
|
const headerSize = 1024;
|
|
const headerSize = 1024;
|
|
const { buffer } = await file.readBuffer(0, headerSize)
|
|
const { buffer } = await file.readBuffer(0, headerSize)
|
|
- const bin = buffer.buffer
|
|
|
|
- const dv = new DataView(bin)
|
|
|
|
|
|
|
|
// 53 MAP Character string 'MAP ' to identify file type
|
|
// 53 MAP Character string 'MAP ' to identify file type
|
|
const MAP = String.fromCharCode(
|
|
const MAP = String.fromCharCode(
|
|
- dv.getUint8(52 * 4), dv.getUint8(52 * 4 + 1),
|
|
|
|
- dv.getUint8(52 * 4 + 2), dv.getUint8(52 * 4 + 3)
|
|
|
|
|
|
+ buffer.readUInt8(52 * 4), buffer.readUInt8(52 * 4 + 1),
|
|
|
|
+ buffer.readUInt8(52 * 4 + 2), buffer.readUInt8(52 * 4 + 3)
|
|
)
|
|
)
|
|
if (MAP !== 'MAP ') {
|
|
if (MAP !== 'MAP ') {
|
|
throw new Error('ccp4 format error, missing "MAP " string');
|
|
throw new Error('ccp4 format error, missing "MAP " string');
|
|
@@ -27,15 +25,15 @@ export async function readCcp4Header(file: FileHandle) {
|
|
|
|
|
|
// 54 MACHST Machine stamp indicating machine type which wrote file
|
|
// 54 MACHST Machine stamp indicating machine type which wrote file
|
|
// 17 and 17 for big-endian or 68 and 65 for little-endian
|
|
// 17 and 17 for big-endian or 68 and 65 for little-endian
|
|
- const MACHST = [ dv.getUint8(53 * 4), dv.getUint8(53 * 4 + 1) ]
|
|
|
|
|
|
+ const MACHST = [ buffer.readUInt8(53 * 4), buffer.readUInt8(53 * 4 + 1) ]
|
|
let littleEndian = true
|
|
let littleEndian = true
|
|
// found MRC files that don't have the MACHST stamp set and are big-endian
|
|
// found MRC files that don't have the MACHST stamp set and are big-endian
|
|
if (MACHST[0] !== 68 && MACHST[1] !== 65) {
|
|
if (MACHST[0] !== 68 && MACHST[1] !== 65) {
|
|
littleEndian = false;
|
|
littleEndian = false;
|
|
}
|
|
}
|
|
|
|
|
|
- const readInt = (o: number) => dv.getInt32(o * 4, littleEndian)
|
|
|
|
- const readFloat = (o: number) => dv.getFloat32(o * 4, littleEndian)
|
|
|
|
|
|
+ const readInt = littleEndian ? (o: number) => buffer.readInt32LE(o * 4) : (o: number) => buffer.readInt32BE(o * 4)
|
|
|
|
+ const readFloat = littleEndian ? (o: number) => buffer.readFloatLE(o * 4) : (o: number) => buffer.readFloatBE(o * 4)
|
|
|
|
|
|
const header: Ccp4Header = {
|
|
const header: Ccp4Header = {
|
|
NC: readInt(0),
|
|
NC: readInt(0),
|
|
@@ -98,15 +96,12 @@ export async function readCcp4Header(file: FileHandle) {
|
|
}
|
|
}
|
|
|
|
|
|
function getElementByteSize(mode: number) {
|
|
function getElementByteSize(mode: number) {
|
|
- if (mode === 2) {
|
|
|
|
- return 4
|
|
|
|
- } else if (mode === 1) {
|
|
|
|
- return 2
|
|
|
|
- } else if (mode === 0) {
|
|
|
|
- return 1
|
|
|
|
- } else {
|
|
|
|
- throw new Error(`ccp4 mode '${mode}' unsupported`);
|
|
|
|
|
|
+ switch (mode) {
|
|
|
|
+ case 2: return 4
|
|
|
|
+ case 1: return 2
|
|
|
|
+ case 0: return 1
|
|
}
|
|
}
|
|
|
|
+ throw new Error(`ccp4 mode '${mode}' unsupported`);
|
|
}
|
|
}
|
|
|
|
|
|
async function parseInternal(file: FileHandle, size: number, ctx: RuntimeContext): Promise<Ccp4File> {
|
|
async function parseInternal(file: FileHandle, size: number, ctx: RuntimeContext): Promise<Ccp4File> {
|
|
@@ -115,17 +110,23 @@ async function parseInternal(file: FileHandle, size: number, ctx: RuntimeContext
|
|
const { header, littleEndian } = await readCcp4Header(file)
|
|
const { header, littleEndian } = await readCcp4Header(file)
|
|
|
|
|
|
const offset = 256 * 4 + header.NSYMBT
|
|
const offset = 256 * 4 + header.NSYMBT
|
|
|
|
+ const { buffer, bytesRead } = await file.readBuffer(offset, size - offset)
|
|
|
|
+
|
|
const count = header.NC * header.NR * header.NS
|
|
const count = header.NC * header.NR * header.NS
|
|
const elementByteSize = getElementByteSize(header.MODE)
|
|
const elementByteSize = getElementByteSize(header.MODE)
|
|
const byteCount = count * elementByteSize
|
|
const byteCount = count * elementByteSize
|
|
|
|
|
|
- const { buffer } = await file.readBuffer(offset, size)
|
|
|
|
|
|
+ if (byteCount !== bytesRead) {
|
|
|
|
+ console.warn(`byteCount ${byteCount} and bytesRead ${bytesRead} differ`)
|
|
|
|
+ }
|
|
|
|
|
|
let values
|
|
let values
|
|
if (header.MODE === 2) {
|
|
if (header.MODE === 2) {
|
|
- values = new Float32Array(buffer, offset, count)
|
|
|
|
|
|
+ values = new Float32Array(buffer.buffer, offset, count)
|
|
|
|
+ } else if (header.MODE === 1) {
|
|
|
|
+ values = new Int16Array(buffer.buffer, offset, count)
|
|
} else if (header.MODE === 0) {
|
|
} else if (header.MODE === 0) {
|
|
- values = new Int8Array(buffer, offset, count)
|
|
|
|
|
|
+ values = new Int8Array(buffer.buffer, offset, count)
|
|
} else {
|
|
} else {
|
|
throw new Error(`ccp4 mode '${header.MODE}' unsupported`);
|
|
throw new Error(`ccp4 mode '${header.MODE}' unsupported`);
|
|
}
|
|
}
|
|
@@ -136,7 +137,7 @@ async function parseInternal(file: FileHandle, size: number, ctx: RuntimeContext
|
|
|
|
|
|
// if the file was converted by mapmode2to0 - scale the data
|
|
// if the file was converted by mapmode2to0 - scale the data
|
|
// based on uglymol (https://github.com/uglymol/uglymol) by Marcin Wojdyr (wojdyr)
|
|
// based on uglymol (https://github.com/uglymol/uglymol) by Marcin Wojdyr (wojdyr)
|
|
- if (header.userFlag1 -128 && header.userFlag2 === 127) {
|
|
|
|
|
|
+ if (header.userFlag1 === -128 && header.userFlag2 === 127) {
|
|
values = new Float32Array(values)
|
|
values = new Float32Array(values)
|
|
// scaling f(x)=b1*x+b0 such that f(-128)=min and f(127)=max
|
|
// scaling f(x)=b1*x+b0 such that f(-128)=min and f(127)=max
|
|
const b1 = (header.AMAX - header.AMIN) / 255.0
|
|
const b1 = (header.AMAX - header.AMIN) / 255.0
|