parser.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Task, RuntimeContext } from 'mol-task';
  7. import * as Schema from './schema'
  8. import Result from '../result'
  9. import { FileHandle } from '../../common/file-handle';
  10. async function parseInternal(file: FileHandle, ctx: RuntimeContext): Promise<Result<Schema.Ccp4File>> {
  11. await ctx.update({ message: 'Parsing CCP4 file...' });
  12. const { buffer } = await file.readBuffer(0, file.length)
  13. const bin = buffer.buffer
  14. const intView = new Int32Array(bin, 0, 56)
  15. const floatView = new Float32Array(bin, 0, 56)
  16. const dv = new DataView(bin)
  17. // 53 MAP Character string 'MAP ' to identify file type
  18. const MAP = String.fromCharCode(
  19. dv.getUint8(52 * 4), dv.getUint8(52 * 4 + 1),
  20. dv.getUint8(52 * 4 + 2), dv.getUint8(52 * 4 + 3)
  21. )
  22. if (MAP !== 'MAP ') {
  23. return Result.error('ccp4 format error, missing "MAP " string');
  24. }
  25. // 54 MACHST Machine stamp indicating machine type which wrote file
  26. // 17 and 17 for big-endian or 68 and 65 for little-endian
  27. const MACHST = [ dv.getUint8(53 * 4), dv.getUint8(53 * 4 + 1) ]
  28. // found MRC files that don't have the MACHST stamp set and are big-endian
  29. if (MACHST[0] !== 68 && MACHST[1] !== 65) {
  30. // flip byte order in-place
  31. for (let i = 0, il = bin.byteLength; i < il; i += 4) {
  32. dv.setFloat32(i, dv.getFloat32(i), true)
  33. }
  34. }
  35. const header: Schema.Ccp4Header = {
  36. NC: intView[0],
  37. NR: intView[1],
  38. NS: intView[2],
  39. MODE: intView[3],
  40. NCSTART: intView[4],
  41. NRSTART: intView[5],
  42. NSSTART: intView[6],
  43. NX: intView[7],
  44. NY: intView[8],
  45. NZ: intView[9],
  46. xLength: floatView[10],
  47. yLength: floatView[11],
  48. zLength: floatView[12],
  49. alpha: floatView[13],
  50. beta: floatView[14],
  51. gamma: floatView[15],
  52. MAPC: intView[16],
  53. MAPR: intView[17],
  54. MAPS: intView[18],
  55. AMIN: floatView[19],
  56. AMAX: floatView[20],
  57. AMEAN: floatView[21],
  58. ISPG: intView[22],
  59. NSYMBT: intView[23],
  60. LSKFLG: intView[24],
  61. SKWMAT: [], // TODO bytes 26-34
  62. SKWTRN: [], // TODO bytes 35-37
  63. // bytes 50-52 origin in X,Y,Z used for transforms
  64. originX: floatView[49],
  65. originY: floatView[50],
  66. originZ: floatView[51],
  67. MAP, // bytes 53 MAP
  68. MACHST, // bytes 54 MACHST
  69. ARMS: floatView[54],
  70. // TODO bytes 56 NLABL
  71. // TODO bytes 57-256 LABEL
  72. }
  73. const offset = 256 * 4 + header.NSYMBT
  74. const count = header.NC * header.NR * header.NS
  75. let values
  76. if (header.MODE === 2) {
  77. values = new Float32Array(bin, offset, count)
  78. } else if (header.MODE === 0) {
  79. values = new Int8Array(bin, offset, count)
  80. } else {
  81. return Result.error(`ccp4 mode '${header.MODE}' unsupported`);
  82. }
  83. const result: Schema.Ccp4File = { header, values };
  84. return Result.success(result);
  85. }
  86. export function parse(file: FileHandle) {
  87. return Task.create<Result<Schema.Ccp4File>>('Parse CCP4', async ctx => {
  88. return await parseInternal(file, ctx);
  89. });
  90. }
  91. export default parse;