Initial commit
commit
652b8d47d5
@ -0,0 +1,3 @@
|
||||
log.txt
|
||||
test
|
||||
node_modules
|
@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const streamToPromise = require("stream-to-promise");
|
||||
const fs = require("fs");
|
||||
const memoizee = require("memoizee");
|
||||
const archy = require("archy");
|
||||
|
||||
const httpSeekable = require("./lib/seekable/http");
|
||||
const fsSeekable = require("./lib/seekable/fs");
|
||||
const iso9660 = require("./lib/iso9660");
|
||||
|
||||
|
||||
let imagePath = process.argv[2];
|
||||
|
||||
Promise.try(() => {
|
||||
if (imagePath.match(/^https?:\/\//)) {
|
||||
return httpSeekable(imagePath);
|
||||
} else {
|
||||
return fsSeekable(imagePath);
|
||||
}
|
||||
}).then((seekable) => {
|
||||
let image = iso9660(seekable);
|
||||
|
||||
return Promise.try(() => {
|
||||
return image.getRootDirectory();
|
||||
}).then((rootDirectory) => {
|
||||
return rootDirectory.getTree();
|
||||
}).then((tree) => {
|
||||
function makeArchy(node) {
|
||||
if (node.type === "directory") {
|
||||
return {
|
||||
label: node.filename,
|
||||
nodes: node.children.map(makeArchy)
|
||||
}
|
||||
} else {
|
||||
return node.filename
|
||||
}
|
||||
}
|
||||
|
||||
let archyNodes = {
|
||||
label: "(root)",
|
||||
nodes: tree.map(makeArchy)
|
||||
}
|
||||
|
||||
console.log(archy(archyNodes));
|
||||
});
|
||||
|
||||
/*
|
||||
let image = iso9660(seekable);
|
||||
|
||||
return Promise.try(() => {
|
||||
return image.getPathTable();
|
||||
}).then((paths) => {
|
||||
//console.log(paths);
|
||||
return image.getPrimaryVolumeDescriptor();
|
||||
}).then((primaryVolumeDescriptor) => {
|
||||
console.log(JSON.stringify(primaryVolumeDescriptor, (key, value) => {
|
||||
if (value != null && value.type === "Buffer") {
|
||||
return require("util").inspect(Buffer.from(value));
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}, "\t"));
|
||||
})
|
||||
*/
|
||||
});
|
@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
const types = require("./types");
|
||||
|
||||
module.exports = function createBufferReader(buffer) {
|
||||
let typeSpecs = {
|
||||
strA: {},
|
||||
strD: {},
|
||||
strFilename: {},
|
||||
int8: { length: 1 },
|
||||
sint8: { length: 1 },
|
||||
int16_LSB: { length: 2 },
|
||||
int16_MSB: { length: 2 },
|
||||
int16_LSB_MSB: { length: 4 },
|
||||
sint16_LSB: { length: 2 },
|
||||
sint16_MSB: { length: 2 },
|
||||
sint16_LSB_MSB: { length: 4 },
|
||||
int32_LSB: { length: 4 },
|
||||
int32_MSB: { length: 4 },
|
||||
int32_LSB_MSB: { length: 8 },
|
||||
sint32_LSB: { length: 4 },
|
||||
sint32_MSB: { length: 4 },
|
||||
sint32_LSB_MSB: { length: 8 },
|
||||
decDatetime: { length: 17 },
|
||||
directoryDatetime: { length: 7 }
|
||||
}
|
||||
|
||||
return Object.keys(typeSpecs).reduce((methods, type) => {
|
||||
let options = typeSpecs[type];
|
||||
|
||||
if (options.length == null) {
|
||||
methods[type] = function(offset, length) {
|
||||
return types[type](buffer.slice(offset, offset + length));
|
||||
}
|
||||
} else {
|
||||
methods[type] = function(offset) {
|
||||
return types[type](buffer.slice(offset, offset + options.length));
|
||||
}
|
||||
}
|
||||
|
||||
return methods;
|
||||
}, {});
|
||||
};
|
@ -0,0 +1,72 @@
|
||||
'use strict';
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const streamToPromise = require("stream-to-promise");
|
||||
const memoizee = require("memoizee");
|
||||
|
||||
const parseVolumeDescriptor = require("./parse/volume-descriptor");
|
||||
const parsePathTable = require("./parse/path-table");
|
||||
|
||||
let imageOffset = 32 * 1024; // first 32KB are system area, ref http://wiki.osdev.org/ISO_9660#System_Area
|
||||
|
||||
module.exports = function createImage(seekable) {
|
||||
let image = {
|
||||
createReadStream: function createReadStream(start, end) {
|
||||
return Promise.try(() => {
|
||||
return seekable.createReadStream(start, end);
|
||||
});
|
||||
},
|
||||
readRange: function readRange(start, end) {
|
||||
return Promise.try(() => {
|
||||
return this.createReadStream(start, end);
|
||||
}).then((stream) => {
|
||||
return streamToPromise(stream);
|
||||
});
|
||||
},
|
||||
|
||||
getPrimaryVolumeDescriptorBuffer: memoizee(function getPrimaryVolumeDescriptorBuffer() {
|
||||
return this.readRange(imageOffset, imageOffset + 2047);
|
||||
}),
|
||||
getPrimaryVolumeDescriptor: function getPrimaryVolumeDescriptor() {
|
||||
return Promise.try(() => {
|
||||
return this.getPrimaryVolumeDescriptorBuffer();
|
||||
}).then((buffer) => {
|
||||
return parseVolumeDescriptor(buffer);
|
||||
});
|
||||
},
|
||||
|
||||
getSectorOffset: function getSectorOffset(sector) {
|
||||
return Promise.try(() => {
|
||||
return this.getPrimaryVolumeDescriptor();
|
||||
}).then((primaryVolumeDescriptor) => {
|
||||
return primaryVolumeDescriptor.data.sectorSize * sector;
|
||||
});
|
||||
},
|
||||
|
||||
getPathTable: function getPathTable() {
|
||||
return Promise.try(() => {
|
||||
return this.getPrimaryVolumeDescriptor();
|
||||
}).then((primaryVolumeDescriptor) => {
|
||||
return Promise.try(() => {
|
||||
let start = primaryVolumeDescriptor.data.pathTableLocationL * primaryVolumeDescriptor.data.sectorSize;
|
||||
let end = start + primaryVolumeDescriptor.data.pathTableSize - 1;
|
||||
|
||||
return this.readRange(start, end);
|
||||
}).then((buffer) => {
|
||||
return parsePathTable(buffer, primaryVolumeDescriptor.data.sectorSize);
|
||||
});
|
||||
});
|
||||
},
|
||||
getRootDirectory: function getRootDirectory() {
|
||||
return Promise.try(() => {
|
||||
return this.getPrimaryVolumeDescriptor();
|
||||
}).then((primaryVolumeDescriptor) => {
|
||||
return createDirectory(primaryVolumeDescriptor.data.rootDirectory);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const createDirectory = require("./object/directory")(image);
|
||||
|
||||
return image;
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const memoizee = require("memoizee");
|
||||
const debug = require("debug")("iso9660:object:directory");
|
||||
|
||||
const parseDirectoryRecord = require("../parse/directory-record");
|
||||
|
||||
module.exports = function(image) {
|
||||
const createFile = require("./file")(image);
|
||||
|
||||
return function createDirectory(directoryRecord, parent) {
|
||||
debug(`Creating new directory object for ${directoryRecord.identifier.filename}`);
|
||||
|
||||
return {
|
||||
type: "directory",
|
||||
filename: directoryRecord.identifier.filename,
|
||||
version: directoryRecord.identifier.version,
|
||||
parent: parent,
|
||||
recordingDate: directoryRecord.recordingDate,
|
||||
|
||||
getChildren: memoizee(function getChildren() {
|
||||
return Promise.try(() => {
|
||||
function getDirectoryContents() {
|
||||
return Promise.try(() => {
|
||||
return image.getSectorOffset(directoryRecord.extentLocation);
|
||||
}).then((sectorOffset) => {
|
||||
return image.readRange(sectorOffset, sectorOffset + directoryRecord.extentSize - 1);
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
image.getPrimaryVolumeDescriptor(),
|
||||
getDirectoryContents()
|
||||
]);
|
||||
}).spread((primaryVolumeDescriptor, directoryContents) => {
|
||||
const roundToNextSector = require("../round-to-next-sector")(primaryVolumeDescriptor.data.sectorSize);
|
||||
|
||||
let pos = 0;
|
||||
let records = [];
|
||||
|
||||
while (pos < directoryContents.length) {
|
||||
let recordLength = directoryContents.readUInt8(pos);
|
||||
|
||||
if (recordLength === 0) {
|
||||
/* We ran out of records for this sector, skip to the next. */
|
||||
pos = roundToNextSector(pos);
|
||||
} else {
|
||||
let directoryRecord = parseDirectoryRecord(directoryContents.slice(pos, pos + recordLength));
|
||||
records.push(directoryRecord);
|
||||
pos += recordLength;
|
||||
}
|
||||
}
|
||||
|
||||
return records.slice(2).map((record) => {
|
||||
if (record.fileFlags.directory) {
|
||||
debug(`Found directory: ${record.identifier.filename}`);
|
||||
|
||||
return createDirectory(record, this);
|
||||
} else {
|
||||
debug(`Found file: ${record.identifier.filename}`);
|
||||
|
||||
return createFile(record, this);
|
||||
}
|
||||
});
|
||||
});
|
||||
}),
|
||||
getTree: function getTree() {
|
||||
return Promise.try(() => {
|
||||
return this.getChildren();
|
||||
}).map((child) => {
|
||||
if (child.type === "directory") {
|
||||
return Promise.try(() => {
|
||||
return child.getTree();
|
||||
}).then((tree) => {
|
||||
return Object.assign({
|
||||
children: tree
|
||||
}, child);
|
||||
});
|
||||
} else {
|
||||
return child;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(image) {
|
||||
return function createFile(record, parent) {
|
||||
return {
|
||||
type: "file",
|
||||
filename: record.identifier.filename,
|
||||
version: record.identifier.version,
|
||||
parent: parent,
|
||||
recordingDate: record.recordingDate
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const createBufferReader = require("../../buffer-reader");
|
||||
|
||||
module.exports = function parseBootRecord(data) {
|
||||
let bufferReader = createBufferReader(data);
|
||||
|
||||
return {
|
||||
bootSystemIdentifier: bufferReader.strA(0, 32),
|
||||
bootIdentifier: bufferReader.strA(32, 32),
|
||||
bootData: data.slice(64, 2041) // FIXME: El Torito
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function parseVolumeDescriptorSetTerminator(data) {
|
||||
return {};
|
||||
};
|
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
const createBufferReader = require("../../buffer-reader");
|
||||
const parseDirectoryRecord = require("../directory-record");
|
||||
|
||||
module.exports = function parsePrimaryVolumeDescriptor(data) {
|
||||
let bufferReader = createBufferReader(data);
|
||||
|
||||
return {
|
||||
systemIdentifier: bufferReader.strA(1, 32),
|
||||
volumeIdentifier: bufferReader.strD(33, 32),
|
||||
sectorCount: bufferReader.int32_LSB_MSB(73),
|
||||
setSize: bufferReader.int16_LSB_MSB(113),
|
||||
sequenceNumber: bufferReader.int16_LSB_MSB(117),
|
||||
sectorSize: bufferReader.int16_LSB_MSB(121),
|
||||
pathTableSize: bufferReader.int32_LSB_MSB(125),
|
||||
pathTableLocationL: bufferReader.int32_LSB(133), // FIXME: Pointer? (location is expressed in 'sectors')
|
||||
optionalPathTableLocationL: bufferReader.int32_LSB(137), // FIXME: Pointer? (location is expressed in 'sectors')
|
||||
pathTableLocationM: bufferReader.int32_MSB(141), // FIXME: Pointer? (location is expressed in 'sectors')
|
||||
optionalPathTableLocationM: bufferReader.int32_MSB(145), // FIXME: Pointer? (location is expressed in 'sectors')
|
||||
rootDirectory: parseDirectoryRecord(data.slice(149, 149 + 34)),
|
||||
publisherIdentifier: bufferReader.strA(311, 128), // FIXME: null for unspecified & extended publisher information
|
||||
dataPreparerIdentifier: bufferReader.strA(439, 128), // FIXME: null for unspecified & extended publisher information
|
||||
applicationIdentifier: bufferReader.strA(567, 128), // FIXME: null for unspecified & extended publisher information
|
||||
copyrightFile: bufferReader.strD(695, 38), // FIXME: seek for file, optionally?
|
||||
abstractFile: bufferReader.strD(733, 36), // FIXME: seek for file, optionally?
|
||||
bibliographicFile: bufferReader.strD(769, 37), // FIXME: seek for file, optionally?
|
||||
creationDate: bufferReader.decDatetime(806),
|
||||
modificationDate: bufferReader.decDatetime(823),
|
||||
expirationDate: bufferReader.decDatetime(840),
|
||||
effectiveDate: bufferReader.decDatetime(857),
|
||||
fileStructureVersion: bufferReader.int8(874),
|
||||
applicationData: data.slice(876, 876 + 512)
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function parseSupplementaryVolumeDescriptor(data) {
|
||||
throw new Error("Not implemented yet");
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function parseVolumePartitionDescriptor(data) {
|
||||
throw new Error("Not implemented yet");
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
|
||||
const createBufferReader = require("../buffer-reader");
|
||||
const roundEven = require("../../round-even");
|
||||
|
||||
function parseFileFlags(value) {
|
||||
/* File flags: (ref http://wiki.osdev.org/ISO_9660#Directories)
|
||||
* Bit Description
|
||||
* 0 If set, the existence of this file need not be made known to the user (basically a 'hidden' flag.
|
||||
* 1 If set, this record describes a directory (in other words, it is a subdirectory extent).
|
||||
* 2 If set, this file is an "Associated File".
|
||||
* 3 If set, the extended attribute record contains information about the format of this file.
|
||||
* 4 If set, owner and group permissions are set in the extended attribute record.
|
||||
* 5 & 6 Reserved
|
||||
* 7 If set, this is not the final directory record for this file (for files spanning several extents, for example files over 4GiB long.
|
||||
*/
|
||||
|
||||
return {
|
||||
hidden: !!(value & 1),
|
||||
directory: !!(value & 2),
|
||||
associated: !!(value & 4),
|
||||
inEAR: !!(value & 8),
|
||||
permissionsInEAR: !!(value & 16),
|
||||
moreRecordsFollowing: !!(value & 128)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function parseDirectoryRecord(data) {
|
||||
let bufferReader = createBufferReader(data);
|
||||
|
||||
let identifierLength = bufferReader.int8(32);
|
||||
let identifierEnd = roundEven(32 + identifierLength);
|
||||
|
||||
return {
|
||||
recordLength: bufferReader.int8(0),
|
||||
extendedAttributeRecordLength: bufferReader.int8(1),
|
||||
extentLocation: bufferReader.int32_LSB_MSB(2),
|
||||
extentSize: bufferReader.int32_LSB_MSB(10),
|
||||
recordingDate: bufferReader.directoryDatetime(18),
|
||||
fileFlags: parseFileFlags(bufferReader.int8(25)),
|
||||
interleavedUnitSize: bufferReader.int8(26),
|
||||
interleavedGapSize: bufferReader.int8(27),
|
||||
sequenceNumber: bufferReader.int16_LSB_MSB(28),
|
||||
identifierLength: identifierLength,
|
||||
identifier: bufferReader.strFilename(33, identifierLength),
|
||||
systemUse: data.slice(identifierEnd, 254) // FIXME: Extensions!
|
||||
}
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
const createBufferReader = require("../buffer-reader");
|
||||
|
||||
module.exports = function parsePathTableRecord(data, tableType) {
|
||||
let bufferReader = createBufferReader(data);
|
||||
|
||||
let extentLocation, parentDirectoryIndex;
|
||||
let identifierLength = bufferReader.int8(0);
|
||||
|
||||
if (tableType === "L") {
|
||||
extentLocation = bufferReader.int32_LSB(2);
|
||||
parentDirectoryIndex = bufferReader.int16_LSB(6);
|
||||
} else if (tableType === "M") {
|
||||
extentLocation = bufferReader.int32_MSB(2);
|
||||
parentDirectoryIndex = bufferReader.int16_MSB(6);
|
||||
} else {
|
||||
throw new Error("Unknown path table type");
|
||||
}
|
||||
|
||||
return {
|
||||
identifierLength: identifierLength,
|
||||
earLength: bufferReader.int8(1),
|
||||
extentLocation: extentLocation,
|
||||
parentDirectoryIndex: parentDirectoryIndex,
|
||||
identifier: bufferReader.strFilename(8, identifierLength)
|
||||
}
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const debug = require("debug")("iso9660:parse:path-table");
|
||||
|
||||
const roundEven = require("../../round-even");
|
||||
const parsePathTableRecord = require("./path-table-record");
|
||||
|
||||
module.exports = function parsePathTable(buffer, sectorSize) {
|
||||
function roundToNextSector(position) {
|
||||
return Math.ceil(position / sectorSize) * sectorSize;
|
||||
}
|
||||
|
||||
let records = [];
|
||||
let pos = 0;
|
||||
|
||||
while (pos < buffer.length) {
|
||||
let recordIdentifierLength = buffer.readUInt8(pos);
|
||||
let recordLength = roundEven(recordIdentifierLength + 8);
|
||||
|
||||
if (recordIdentifierLength === 0) {
|
||||
/* Reached the end of records for this section, throw away the remainder. */
|
||||
debug("Ran out of records, skipping to next sector...");
|
||||
pos = roundToNextSector(pos);
|
||||
} else {
|
||||
let parsedRecord = parsePathTableRecord(buffer.slice(pos, pos + recordLength), "L");
|
||||
debug(`Found: ${parsedRecord.identifier.filename}`);
|
||||
records.push(parsedRecord);
|
||||
|
||||
pos += recordLength;
|
||||
}
|
||||
}
|
||||
|
||||
return records;
|
||||
};
|
@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
const Buffer = require("safe-buffer").Buffer;
|
||||
|
||||
const createBufferReader = require("../buffer-reader");
|
||||
const parseBootRecord = require("./descriptors/boot-record");
|
||||
const parsePrimaryVolumeDescriptor = require("./descriptors/primary-volume");
|
||||
const parseSupplementaryVolumeDescriptor = require("./descriptors/supplementary-volume");
|
||||
const parseVolumePartitionDescriptor = require("./descriptors/volume-partition");
|
||||
const parseVolumeDescriptorSetTerminator = require("./descriptors/descriptor-set-terminator");
|
||||
|
||||
let identifierCD001 = Buffer.from("CD001");
|
||||
|
||||
function getVolumeDescriptorType(typeNumber) {
|
||||
if (typeNumber === 0) {
|
||||
return "boot";
|
||||
} else if (typeNumber === 1) {
|
||||
return "primary";
|
||||
} else if (typeNumber === 2) {
|
||||
return "supplementary";
|
||||
} else if (typeNumber === 3) {
|
||||
return "partition";
|
||||
} else if (typeNumber >= 4 && typeNumber <= 254) {
|
||||
return "reserved";
|
||||
} else if (typeNumber === 255) {
|
||||
return "terminator";
|
||||
} else {
|
||||
throw new Error("Unknown volume descriptor type");
|
||||
}
|
||||
}
|
||||
|
||||
function parseVolumeDescriptorData(type, dataBuffer) {
|
||||
if (type === "boot") {
|
||||
return parseBootRecord(dataBuffer);
|
||||
} else if (type === "primary") {
|
||||
return parsePrimaryVolumeDescriptor(dataBuffer);
|
||||
} else if (type === "supplementary") {
|
||||
return parseSupplementaryVolumeDescriptor(dataBuffer);
|
||||
} else if (type === "partition") {
|
||||
return parseVolumePartitionDescriptor(dataBuffer);
|
||||
} else if (type === "terminator") {
|
||||
return parseVolumeDescriptorSetTerminator(dataBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function parseVolumeDescriptor(data) {
|
||||
if (!data.slice(1, 6).equals(identifierCD001)) {
|
||||
throw new Error("Not a valid volume descriptor - CD001 identifier is missing");
|
||||
}
|
||||
|
||||
let buffer = createBufferReader(data);
|
||||
|
||||
let descriptorType = getVolumeDescriptorType(buffer.int8(0));
|
||||
|
||||
return {
|
||||
type: descriptorType,
|
||||
version: buffer.int8(6),
|
||||
data: parseVolumeDescriptorData(descriptorType, data.slice(7, 2048))
|
||||
}
|
||||
};
|
@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(sectorSize) {
|
||||
return function roundToNextSector(position) {
|
||||
return Math.ceil(position / sectorSize) * sectorSize;
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
'use strict';
|
||||
|
||||
const moment = require("moment");
|
||||
const removeRightPadding = require("../remove-right-padding");
|
||||
|
||||
/* Basic ISO 9660 types: (ref http://wiki.osdev.org/ISO_9660#Numerical_formats)
|
||||
* int8 Unsigned 8-bit integer.
|
||||
* sint8 Signed 8-bit integer.
|
||||
* int16_LSB Little-endian encoded unsigned 16-bit integer.
|
||||
* int16_MSB Big-endian encoded unsigned 16-bit integer.
|
||||
* int16_LSB-MSB Little-endian followed by big-endian encoded unsigned 16-bit integer.
|
||||
* sint16_LSB Little-endian encoded signed 16-bit integer.
|
||||
* sint16_MSB Big-endian encoded signed 16-bit integer.
|
||||
* sint16_LSB-MSB Little-endian followed by big-endian encoded signed 16-bit integer.
|
||||
* int32_LSB Little-endian encoded unsigned 32-bit integer.
|
||||
* int32_MSB Big-endian encoded unsigned 32-bit integer.
|
||||
* int32_LSB-MSB Little-endian followed by big-endian encoded unsigned 32-bit integer.
|
||||
* sint32_LSB Little-endian encoded signed 32-bit integer.
|
||||
* sint32_MSB Big-endian encoded signed 32-bit integer.
|
||||
* sint32_LSB-MSB Little-endian followed by big-endian encoded signed 32-bit integer.
|
||||
*
|
||||
* "Where a both-endian format is present, the x86 architecture makes use of the first little-endian sequence and ignores the big-endian sequence."
|
||||
*/
|
||||
|
||||
function offsetInMinutes(value) {
|
||||
/* "Time zone offset from GMT in 15 minute intervals, starting at interval -48 (west) and running up to interval 52 (east).
|
||||
* So value 0 indicates interval -48 which equals GMT-12 hours, and value 100 indicates interval 52 which equals GMT+13 hours."
|
||||
*
|
||||
* ref: http://wiki.osdev.org/ISO_9660#Date.2Ftime_format
|
||||
*/
|
||||
|
||||
return (value - 48) * 15;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
/* String types */
|
||||
strA: function parseStrA(buffer) {
|
||||
return removeRightPadding(buffer.toString("ascii"));
|
||||
},
|
||||
strD: function parseStrD(buffer) {
|
||||
return this.strA(buffer);
|
||||
},
|
||||
strFilename: function parseStrFilename(buffer) {
|
||||
let str = this.strA(buffer);
|
||||
let [filename, version] = str.split(";");
|
||||
|
||||
return {filename, version};
|
||||
},
|
||||
|
||||
/* 8-bit integers */
|
||||
int8: function parseInt8(buffer) {
|
||||
return buffer.readUInt8(0);
|
||||
},
|
||||
sint8: function parseSInt8(buffer) {
|
||||
return buffer.readInt8(0);
|
||||
},
|
||||
|
||||
/* 16-bit integers */
|
||||
int16_LSB: function parseInt16_LSB(buffer) {
|
||||
return buffer.readUInt16LE(0);
|
||||
},
|
||||
int16_MSB: function parseInt16_LSB(buffer) {
|
||||
return buffer.readUInt16BE(0);
|
||||
},
|
||||
int16_LSB_MSB: function parseInt16_LSB(buffer) {
|
||||
return this.int16_LSB(buffer.slice(0, 2));
|
||||
},
|
||||
sint16_LSB: function parseSInt16_LSB(buffer) {
|
||||
return buffer.readInt16LE(0);
|
||||
},
|
||||
sint16_MSB: function parseSInt16_LSB(buffer) {
|
||||
return buffer.readInt16BE(0);
|
||||
},
|
||||
sint16_LSB_MSB: function parseSInt16_LSB(buffer) {
|
||||
return this.sint16_LSB(buffer.slice(0, 2));
|
||||
},
|
||||
|
||||
/* 32-bit integers */
|
||||
int32_LSB: function parseInt32_LSB(buffer) {
|
||||
return buffer.readUInt32LE(0);
|
||||
},
|
||||
int32_MSB: function parseInt32_LSB(buffer) {
|
||||
return buffer.readUInt32BE(0);
|
||||
},
|
||||
int32_LSB_MSB: function parseInt32_LSB(buffer) {
|
||||
return this.int32_LSB(buffer.slice(0, 4));
|
||||
},
|
||||
sint32_LSB: function parseSInt32_LSB(buffer) {
|
||||
return buffer.readInt32LE(0);
|
||||
},
|
||||
sint32_MSB: function parseSInt32_LSB(buffer) {
|
||||
return buffer.readInt32BE(0);
|
||||
},
|
||||
sint32_LSB_MSB: function parseSInt32_LSB(buffer) {
|
||||
return this.sint32_LSB(buffer.slice(0, 4));
|
||||
},
|
||||
|
||||
/* Date/time */
|
||||
decDatetime: function parseDecDatetime(buffer) {
|
||||
let year = parseInt(this.strD(buffer.slice(0, 4)));
|
||||
let month = parseInt(this.strD(buffer.slice(4, 6))) - 1; // "Note that like moment(Array) and new Date(year, month, date), months are 0 indexed."
|
||||
let day = parseInt(this.strD(buffer.slice(6, 8)));
|
||||
|
||||
let hour = parseInt(this.strD(buffer.slice(8, 10)));
|
||||
let minute = parseInt(this.strD(buffer.slice(10, 12)));
|
||||
let second = parseInt(this.strD(buffer.slice(12, 14)));
|
||||
let centisecond = parseInt(this.strD(buffer.slice(14, 16)));
|
||||
|
||||
let timezoneOffset = this.int8(buffer.slice(16, 17));
|
||||
|
||||
if (year === 0 && month === 0 && day === 0 && hour === 0 && minute === 0 && second === 0 && centisecond === 0 && timezoneOffset === 0) {
|
||||
return null;
|
||||
} else {
|
||||
return moment({
|
||||
year, month, day, hour, minute, second,
|
||||
millisecond: centisecond * 10,
|
||||
}).utcOffset(offsetInMinutes(timezoneOffset));
|
||||
}
|
||||
},
|
||||
directoryDatetime: function parseDirectoryDatetime(buffer) {
|
||||
return moment({
|
||||
year: 1900 + this.int8(buffer.slice(0, 0 + 1)),
|
||||
month: this.int8(buffer.slice(1, 1 + 1)) - 1,
|
||||
day: this.int8(buffer.slice(2, 2 + 1)),
|
||||
hour: this.int8(buffer.slice(3, 3 + 1)),
|
||||
minute: this.int8(buffer.slice(4, 4 + 1)),
|
||||
second: this.int8(buffer.slice(5, 5 + 1)),
|
||||
}).utcOffset(offsetInMinutes(this.int8(buffer.slice(6, 6 + 1))));
|
||||
}
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function removeRightPadding(string) {
|
||||
return string.replace(/[ ]+$/, "");
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function roundEven(number) {
|
||||
return Math.ceil((number) / 2) * 2;
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require("fs");
|
||||
const debug = require("debug")("seekable:fs");
|
||||
|
||||
module.exports = function getHttpSeekable(targetPath) {
|
||||
return {
|
||||
createReadStream: function createReadStream(start, end) {
|
||||
debug(`${start}-${end} -- ${targetPath}`);
|
||||
|
||||
return fs.createReadStream(targetPath, {
|
||||
start: start,
|
||||
end: end
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const bhttp = require("bhttp");
|
||||
const debug = require("debug")("seekable:http");
|
||||
|
||||
function generateRangeHeader(start, end) {
|
||||
if (start != null && end != null) {
|
||||
return `bytes=${start}-${end}`;
|
||||
} else if (start != null && end == null) {
|
||||
return `bytes=${start}-`;
|
||||
} else if (start == null && end != null) {
|
||||
return `bytes=-${end}`;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function getHttpSeekable(targetUrl) {
|
||||
return {
|
||||
createReadStream: function createReadStream(start, end) {
|
||||
return Promise.try(() => {
|
||||
debug(`${start}-${end} -- ${targetUrl}`);
|
||||
|
||||
return bhttp.get(targetUrl, {
|
||||
stream: true,
|
||||
headers: {
|
||||
"range": generateRangeHeader(start, end) // FIXME: What if there is no range specified at all? Is `null` valid then?
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "iso-spider",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@git.cryto.net:joepie91/iso-spider.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"archy": "^1.0.0",
|
||||
"bhttp": "^1.2.4",
|
||||
"bluebird": "^3.4.6",
|
||||
"debug": "^2.2.0",
|
||||
"memoizee": "^0.4.1",
|
||||
"moment-timezone": "^0.5.6",
|
||||
"promise-while-loop": "^1.0.1",
|
||||
"safe-buffer": "^5.0.1",
|
||||
"stream-to-promise": "^2.2.0"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue