Created
June 1, 2016 20:45
-
-
Save espresso3389/a4a7ffcc28cbd15dce61a5f45a94c3bf to your computer and use it in GitHub Desktop.
JPEG 2000 Parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//---------------------------------------------------------------------------- | |
// JPEG 2000 Parser | |
//---------------------------------------------------------------------------- | |
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Text; | |
namespace JPXDump | |
{ | |
class JPXDump | |
{ | |
public static void Main(string[] args) | |
{ | |
for (int i = 0; i < args.Length; i++) | |
process(args[i]); | |
} | |
static void process(string fileName) | |
{ | |
using (FileStream fs = File.OpenRead(fileName)) | |
{ | |
var jpxd = new JPXDump(); | |
jpxd.process(fs, 0, 0); | |
} | |
} | |
int jpmVerMajor; | |
int jpmVerMinor; | |
void process(Stream stream, int level, long baseOffset) | |
{ | |
for (;;) | |
{ | |
long pos = stream.Position; | |
if(pos >= stream.Length) | |
break; | |
int hdrSize = 8; | |
int boxLen = IO.ReadU32(stream, "BOXLEN"); | |
string boxType = IO.ReadString(stream, 4, "BOXTYPE"); | |
if (boxLen == 0) | |
{ | |
boxLen = hdrSize + (int)(stream.Length - stream.Position); | |
} | |
else if (boxLen == 1) | |
{ | |
IO.SkipBytes(stream, 4); | |
boxLen = IO.ReadU32(stream, "BOXLEN"); | |
hdrSize = 16; | |
} | |
Console.Write("{1:X8} {3:X8} {0}{2} ", | |
new string(' ', level), baseOffset, boxType, boxLen); | |
int size = boxLen - hdrSize; | |
baseOffset += hdrSize; | |
process(new PartialStream(stream, size), level, boxType, baseOffset); | |
baseOffset += size; | |
} | |
} | |
void process(Stream stream, int level, string boxType, long baseOffset) | |
{ | |
string spc = ""; | |
int minlevel = 6; | |
if (level < minlevel) | |
{ | |
Console.Write(new String (' ', minlevel - level)); | |
spc = new String(' ', minlevel + 23); | |
} | |
else | |
spc = new String(' ', level + 23); | |
if (isSuperBox(boxType)) | |
{ | |
Console.WriteLine(""); | |
process(stream, level + 1, baseOffset + stream.Position); | |
} | |
else if (boxType == "jP ") | |
{ | |
Console.WriteLine(""); | |
if(IO.ReadU32(stream, "JP2 sig") != 0x0d0a870a) | |
throw new Exception("Invalid JP2 signature."); | |
} | |
else if (boxType == "ftyp") | |
{ | |
var brand = IO.ReadString(stream, 4, "ftyp BR"); | |
var minV = IO.ReadU32(stream, "ftyp MinV"); | |
Console.Write("Brand={0} MinV={1} CL", brand, minV); | |
for (int i = 0; stream.Position < stream.Length; i++) | |
{ | |
var cl = IO.ReadString(stream, 4, string.Format("ftyp CL{0}", i)); | |
Console.Write("{0}\"{1}\"", i == 0 ? "={" : ",", cl); | |
} | |
Console.WriteLine("}"); | |
} | |
else if (boxType == "ihdr") | |
{ | |
string[] compStr = { | |
"Uncompressed", "ModHuff", "ModREAD", "MMREAD", | |
"JBIG", "JPEG", "JPEG-LS", "JP2K", "JBIG2"}; | |
int h = IO.ReadU32(stream, "ihdr height"); | |
int w = IO.ReadU32(stream, "ihdr width"); | |
int comps = IO.ReadU16(stream, "ihdr comps"); | |
int bpc = IO.ReadU8(stream, "ihdr bpc"); | |
int compression = IO.ReadU8(stream, "ihdr compression"); | |
bool unkC = IO.ReadU8(stream, "ihdr unkC") != 0; | |
bool ipr = IO.ReadU8(stream, "ihdr ipr") != 0; | |
Console.WriteLine( | |
"{0}x{1} N={2} BPC={3} C.={4} UnkC={5} IPR={6}", | |
w, h, comps, bpc, | |
compression >= 0 && compression < compStr.Length ? (object)compStr[compression] : (object)compression, | |
unkC, ipr); | |
} | |
else if (boxType == "colr") | |
{ | |
int meth = IO.ReadU8(stream, "colr meth"); | |
int precedence = IO.ReadU8(stream, "colr precedence"); | |
int approx = IO.ReadU8(stream, "colr approx"); | |
if (meth == 1) | |
{ | |
int enumCs = IO.ReadU32(stream, "colr enumCs"); | |
string cs; | |
if (enumCs == 16) cs = "sRGB (IEC 61966-2-1)"; | |
else if (enumCs == 17) cs = "Grayscale"; | |
else cs = string.Format("CS={0}", enumCs); | |
Console.WriteLine("meth={0} precedence={1} approx={2} {3}", meth, precedence, approx, cs); | |
} | |
else if (meth == 2) | |
{ | |
Console.WriteLine("meth={0} precedence={1} approx={2} ICC profile", meth, precedence, approx); | |
} | |
} | |
else if (boxType == "resd" || boxType == "resc") | |
{ | |
// resolution in samples/meter | |
double vn = IO.ReadU16(stream, "resd/resc vn") * 2.54f; | |
double vd = IO.ReadU16(stream, "resd/resc vd") * 100f; | |
double hn = IO.ReadU16(stream, "resd/resc hn") * 2.54f; | |
double hd = IO.ReadU16(stream, "resd/resc hd") * 100f; | |
double ve = IO.ReadU8(stream, "resd/resc ve"); | |
double he = IO.ReadU8(stream, "resd/resc he"); | |
double vres = vn * Math.Pow(10f, ve) / vd; | |
double hres = hn * Math.Pow(10f, he) / hd; | |
Console.WriteLine("Resolution H={0} V={1}", hres, vres); | |
} | |
else if (boxType == "jp2c") | |
{ | |
Console.WriteLine(""); | |
process_jp2c(stream, level); | |
} | |
else if (boxType == "mhdr") | |
{ | |
// Part 6 | |
jpmVerMajor = IO.ReadU8(stream, "mhdr VERS[0]"); | |
jpmVerMinor = IO.ReadU8(stream, "mhdr VERS[1]"); | |
var numberOfPages = IO.ReadU16(stream, "mhdr NP"); | |
var profileID = IO.ReadU8(stream, "mhdr P"); | |
var mc = IO.ReadMSB7ExtendedBytes(stream, "mhdr MC"); | |
var ic = IO.ReadMSB7ExtendedBytes(stream, "mhdr IC"); | |
var ipr = IO.ReadU8(stream, "mhdr IPR"); | |
Console.WriteLine("Version={0:X2}.{1:X2} NumOfPages={2} Profile={3} IPR={4}", | |
jpmVerMajor, jpmVerMinor, numberOfPages, getPart6ProfileName(profileID), ipr); | |
showPart6MaskCoders(mc, spc); | |
showPart6ImageCoders(ic, spc); | |
} | |
else if (boxType == "lbl ") | |
{ | |
// Part 6 | |
var len = (int)(stream.Length - stream.Position); | |
var bytes = new byte[len]; | |
stream.Read(bytes, 0, len); | |
Console.WriteLine("Label=\"{0}\"", Encoding.UTF8.GetString(bytes)); | |
} | |
else if (boxType == "pagt") | |
{ | |
// Part 6 | |
var npc = IO.ReadU16(stream, "pagt NPC"); | |
var ne = IO.ReadU16(stream, "pagt NE"); | |
Console.WriteLine("NumOfPagesInCollection={0}, NumOfEntries={1}", npc, ne); | |
for (int i = 0; i < ne; i++) | |
{ | |
var offset = IO.ReadU64(stream, string.Format("pagt OFF{0}", i)); | |
var length = IO.ReadU32(stream, string.Format("pagt LEN{0}", i)); | |
var dataRef = IO.ReadU16(stream, string.Format("pagt DR{0}", i)); | |
var flags = IO.ReadU8(stream, string.Format("pagt FL{0}", i)); | |
var meaning = ""; | |
if ((flags & 7) == 1) | |
meaning = "Offset to Page box"; | |
else if ((flags & 7) == 5) | |
meaning = "Offset to Page box containing thumbnail"; | |
else if ((flags & 7) == 4) | |
meaning = "Offset to Page Collection box"; | |
else if ((flags & 8) == 8) | |
meaning = "Offset to Page or Page Collection box containing metadata"; | |
else | |
meaning = "Reserved for ISO use"; | |
Console.WriteLine( | |
"{0}{1}: OFF={2:X8}h LEN={3:X8}h, DataRef={4}, Flags={5:X2}h: {6}", | |
spc, i, offset, length, dataRef, flags, meaning); | |
} | |
} | |
else if (boxType == "phdr") | |
{ | |
// Part 6 | |
int pageID, nlObj; | |
if (jpmVerMajor == 0) | |
{ | |
pageID = IO.ReadU8(stream, "phdr PageID"); | |
nlObj = IO.ReadU8(stream, "phdr NLobj"); | |
} | |
else | |
{ | |
pageID = IO.ReadU16(stream, "phdr PageID"); | |
nlObj = IO.ReadU16(stream, "phdr NLobj"); | |
} | |
var pHeight = IO.ReadU32(stream, "phdr PHeight"); | |
var pWidth = IO.ReadU32(stream, "phdr PWidth"); | |
var or = (Part6OrientationClockWise) IO.ReadU16(stream, "phdr OR"); | |
var pColor = (Part6PageColor) IO.ReadU16(stream, "phdr PColor"); | |
Console.WriteLine( | |
"PageID={0} NLobj={1} PHeight={2} PWidth={3} OR={4} PColor={5}", | |
pageID, nlObj, pHeight, pWidth, or, pColor); | |
for (int i = 0; stream.Position < stream.Length; i++) | |
{ | |
var offset = IO.ReadU64(stream, string.Format("phdr OFF{0}", i)); | |
var length = IO.ReadU32(stream, string.Format("phdr LEN{0}", i)); | |
var dataRef = IO.ReadU16(stream, string.Format("phdr DR{0}", i)); | |
Console.WriteLine( | |
"{0}{1}: OFF={2:X8}h LEN={3:X8}h, DataRef={4}", | |
spc, i, offset, length, dataRef); | |
} | |
} | |
else if (boxType == "ppcl") | |
{ | |
var ppcOff = IO.ReadU64(stream, "ppcl PPCOff"); | |
var ppcLen = IO.ReadU32(stream, "ppcl PPCLen"); | |
var ppcDataRef = IO.ReadU16(stream, "ppcl PPCDR"); | |
var pIx = IO.ReadU32(stream, "ppcl PIx"); | |
Console.WriteLine("PPCOff={0:X8}h PPCLen={1:X8} PPCDR={2} PIx={3}", | |
ppcOff, ppcLen, ppcDataRef, pIx); | |
} | |
else if (boxType == "lhdr") | |
{ | |
var lObjID = IO.ReadU16(stream, "lhdr LObjID"); | |
var lHeight = IO.ReadU32(stream, "lhdr LHeight"); | |
var lWidth = IO.ReadU32(stream, "lhdr LWidth"); | |
var lvOffset = IO.ReadU32(stream, "lhdr LVoff"); | |
var lhOffset = IO.ReadU32(stream, "lhdr LHoff"); | |
Console.Write( | |
"LObjID={0} LHeight={1} LWidth={2} LVoff={3} LHoff={4}", | |
lObjID, lHeight, lWidth, lvOffset, lhOffset); | |
if (stream.Position + 2 <= stream.Length) | |
{ | |
var style = (Part6LayoutObjectStyle) IO.ReadU16(stream, "lhdr Style"); | |
Console.WriteLine(" Style={0}", style); | |
} | |
else if (stream.Position + 1 <= stream.Length) | |
{ | |
// Not compliant to ISO/IEC WD15444-6: 2001 (FCD, 16 November 2001) | |
// but some jpm have 1-byte Style. | |
var style = (Part6LayoutObjectStyle) IO.ReadU8(stream, "lhdr Style"); | |
Console.WriteLine(" Style={0}", style); | |
} | |
else | |
{ | |
// Not compliant to ISO/IEC WD15444-6: 2001 (FCD, 16 November 2001) | |
// but some jpm do not have Style. | |
Console.WriteLine(""); | |
} | |
} | |
else if (boxType == "ohdr") | |
{ | |
Part6ObjectType objType; | |
bool noCodestream; | |
if (jpmVerMajor == 0) | |
{ | |
objType = (Part6ObjectType) IO.ReadU8(stream, "ohdr ObjType"); | |
noCodestream = IO.ReadU8(stream, "ohdr NoCodestream") != 0 ? true : false; | |
} | |
else | |
{ | |
objType = (Part6ObjectType) IO.ReadU16(stream, "ohdr ObjType"); | |
noCodestream = IO.ReadU16(stream, "ohdr NoCodestream") != 0 ? true : false; | |
} | |
var oVoff = IO.ReadU32(stream, "ohdr OVoff"); | |
var oHoff = IO.ReadU32(stream, "ohdr OHoff"); | |
var offset = IO.ReadU64(stream, "ohdr OFF"); | |
var length = IO.ReadU32(stream, "ohdr LEN"); | |
var dataRef = 0;//IO.ReadU16(stream, "ohdr DR"); | |
Console.Write( | |
"Type={0} NoCodestream={1}", objType, noCodestream); | |
if (!noCodestream) | |
{ | |
Console.WriteLine(" OVoff={0} OHoff={1} OFF={2:X8}h LEN={3:X8}h DR={4}", | |
oVoff, oHoff, offset, length, dataRef); | |
} | |
else | |
{ | |
Console.WriteLine(""); | |
} | |
} | |
else if (boxType == "scal") | |
{ | |
var vrn = IO.ReadU16(stream, "scal VRN"); | |
var vrd = IO.ReadU16(stream, "scal VRD"); | |
var hrn = IO.ReadU16(stream, "scal HRN"); | |
var hrd = IO.ReadU16(stream, "scal HRD"); | |
Console.WriteLine("VScale={0}/{1} HScale={2}/{3}", vrn, vrd, hrn, hrd); | |
} | |
else | |
{ | |
Console.WriteLine(""); | |
} | |
// skip the trailing bytes | |
IO.SkipBytes(stream, (int)(stream.Length - stream.Position)); | |
} | |
enum Part6OrientationClockWise | |
{ | |
NotSpecified = 0, | |
Rotate0 = 1, | |
Rotate90 = 2, | |
Rotate180 = 3, | |
Rotate270 = 4 | |
} | |
enum Part6PageColor | |
{ | |
Transparent = 0, | |
White = 1, | |
Black = 2, | |
SpecifiedInBaseColourBox = 255 | |
} | |
enum Part6LayoutObjectStyle | |
{ | |
SepObjsForImageAndMask = 0, | |
SingleObjForImageAndMask = 1, | |
SingleObjForImageOnly = 2, | |
SingleObjForMaskOnly = 3, | |
VendorSpecific = 255, | |
} | |
enum Part6ObjectType | |
{ | |
MaskOfLayoutObj = 0, | |
ImageOfLayoutObj = 1, | |
ImageAndMaskOfLayoutObj = 2, | |
} | |
static bool isSuperBox(string boxType) | |
{ | |
if (boxType == "jp2h") return true; | |
else if (boxType == "res ") return true; // resc/resd | |
else if (boxType == "pcol") return true; // pcol: ISO/IEC WD15444-6: 2001 | |
else if (boxType == "page") return true; // page: ISO/IEC WD15444-6: 2001 | |
else if (boxType == "lobj") return true; // lobj: ISO/IEC WD15444-6: 2001 | |
else if (boxType == "objc") return true; // objc: ISO/IEC WD15444-6: 2001 | |
return false; | |
} | |
static void process_jp2c(Stream stream, int level) | |
{ | |
long basePos = 0; | |
if(stream is PartialStream) | |
basePos = ((PartialStream)stream).BasePosition; | |
for (;;) | |
{ | |
long pos = basePos + stream.Position; | |
int marker = IO.ReadU16(stream, "marker"); | |
if ((marker & 0xff00) != 0xff00) | |
throw new Exception(string.Format("Not a marker: {0:X2}", marker)); | |
marker &= 0xff; | |
if (marker == 0xd9) // EOC | |
{ | |
Console.WriteLine("{0:X8} 00000002 EOC(D9)", pos); | |
break; | |
} | |
if (marker == 0x93) // SOD | |
{ | |
int len = (int)(stream.Length - stream.Position) - 2; | |
Console.WriteLine("{0:X8} 00000002 SOD(93)", pos); | |
Console.WriteLine("{0:X8} {1:X8} Coded Data", pos + 2, len); | |
IO.SkipBytes(stream, len); | |
continue; | |
} | |
int size; | |
if (marker == 0x4f || (marker >= 0xd0 && marker <= 0xd8)) | |
{ | |
// SOC, SOI, RSTx | |
size = 0; | |
} | |
else | |
{ | |
size = IO.ReadU16(stream, "size"); | |
size -= 2; | |
} | |
string m = markerNames[marker]; | |
if (m == null) | |
m = string.Format("{0:X2}", marker); | |
else | |
m = string.Format("{0}({1:X2})", m, marker); | |
Console.WriteLine("{0:X8} {1:X8} {2}", pos, size, m); | |
IO.SkipBytes(stream, size); | |
} | |
} | |
static string getPart6ProfileName(int profileID) | |
{ | |
switch (profileID) | |
{ | |
case 0: return "None"; | |
case 1: return "Web"; | |
case 2: return "T.44-compatible"; | |
default: return "Reserved"; | |
} | |
} | |
static void showBits(byte[] b, string title, string[] names, string spc) | |
{ | |
if (b[0] != 0) | |
Console.WriteLine("{0}{1}: {2:X2}h", spc, title, b[0]); | |
for (int i = 0; i < 7; i++) | |
{ | |
if ((b[0] & (1 << i)) != 0) | |
Console.WriteLine("{0} {1}", spc, names[i]); | |
} | |
} | |
static string[] Part6MaskCoders = { | |
"One dimensional T.4 (MH) coding", | |
"Two dimensional T.4 (MR) coding", | |
"T.6 (MMR) coding", | |
"T.82 (JBIG) coding applying Recommendation T.85", | |
"T.88 (JBIG2) coding applying ITU-T Rec, T.89", | |
"T.800 (JPEG 2000) coding", | |
"Uncompressed" | |
}; | |
static void showPart6MaskCoders(byte[] mc, string spc) | |
{ | |
showBits(mc, "Mask Coders", Part6MaskCoders, spc); | |
} | |
static string[] Part6ImageCoders = { | |
"T.81 (JPEG) coding", | |
"T.82 (JBIG) coding applying Recommendation T.43", | |
"T.800 (JPEG 2000) coding", | |
"T.87 (JPEG-LS) coding", | |
"T.45 (Run-Length Colour Encoding)", | |
"Uncompressed", | |
"Reserved" | |
}; | |
static void showPart6ImageCoders(byte[] ic, string spc) | |
{ | |
showBits(ic, "Image Coders", Part6ImageCoders, spc); | |
} | |
static string[] markerNames = { | |
null, "TEM", null, null, null, null, null, null, // 00- | |
null, null, null, null, null, null, null, null, // 08- | |
null, null, null, null, null, null, null, null, // 10- | |
null, null, null, null, null, null, null, null, // 18- | |
null, null, null, null, null, null, null, null, // 20- | |
null, null, null, null, null, null, null, null, // 28- | |
null, null, null, null, null, null, null, null, // 30- | |
null, null, null, null, null, null, null, null, // 38- | |
null, null, null, null, null, null, null, null, // 40- | |
null, null, null, null, null, null, null, "SOC", // 48- | |
null, "SIZ", "COD", "COC", null, "TLM", null, "PLM", // 50- | |
"PLT", null, null, null, "QCD", "QCC", "RGN", "POC", // 58- | |
"PPM", "PPT", null, "CRG", "COM", null, null, null, // 60- | |
null, null, null, null, null, null, null, null, // 68- | |
null, null, null, null, null, null, null, null, // 70- | |
null, null, null, null, null, null, null, null, // 78- | |
null, null, null, null, null, null, null, null, // 80- | |
null, null, null, null, null, null, null, null, // 88- | |
"SOT", "SOP", "EPH", "SOD", null, null, null, null, // 90- | |
null, null, null, null, null, null, null, null, // 98- | |
null, null, null, null, null, null, null, null, // A0- | |
null, null, null, null, null, null, null, null, // A8- | |
null, null, null, null, null, null, null, null, // B0- | |
null, null, null, null, null, null, null, null, // B8- | |
"SOF0", "SOF1", "SOF2", "SOF3", "DHT", "SOF5", "SOF6", "SOF7", // C0- | |
"JPG", "SOF9", "SOF10", "SOF11", "DAC", "SOF13", "SOF14", "SOF15", // C8- | |
"RST0", "RST1", "RST2", "RST3", "RST4", "RST5", "RST6", "RST7", // D0- | |
"SOI", "EOC", "SOS", "DQT", "DNL", "DRI", "DHP", "EXP", // D8- | |
"APP0", "APP1", "APP2", "APP3", "APP4", "APP5", "APP6", "APP7", // E0- | |
"APP8", "APP9", "APP10", "APP11", "APP12", "APP13", "APP14", "APP15", // E8- | |
"JPG0", "JPG1", "JPG2", "JPG3", "JPG4", "JPG5", "JPG6", "JPG7", // F0- | |
"JPG8", "JPG9", "JPG10", "JPG11", "JPG12", "JPG13", "COM", null // F8- | |
}; | |
} | |
//------------------------------------------------------------------------ | |
/// <summary> | |
/// This class provides easy stream I/O functions. | |
/// </summary> | |
public abstract class IO | |
{ | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read specified number of bytes from a stream. | |
/// If stream reachs EOF before reading all of the specified bytes, | |
/// this function throws an exception. | |
/// </summary> | |
/// <param name="stream">Stream to read.</param> | |
/// <param name="n">Number of bytes to read.</param> | |
/// <returns></returns> | |
public static byte[] ReadBytes(Stream stream, int n, String tag) | |
{ | |
var pos = stream.Position; | |
var buf = new byte[n]; | |
var bytes = stream.Read(buf, 0, n); | |
if (bytes < n) | |
throw new Exception(string.Format("EOF during reading {0}: {1} bytes of {2} bytes from {3:X8}", tag, bytes, n, pos)); | |
return buf; | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a byte value from a stream. | |
/// </summary> | |
/// <param name="stream">Stream to read.</param> | |
/// <returns>The byte read.</returns> | |
public static int ReadU8(Stream stream, String tag) | |
{ | |
return (int)ReadBytes(stream, 1, tag)[0]; | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a byte value from binary data. | |
/// </summary> | |
/// <param name="data">Binary data array.</param> | |
/// <param name="index">Location of the data to read.</param> | |
/// <returns>The byte read.</returns> | |
public static int ReadU8(byte[] data, int index) | |
{ | |
return (int)data[index]; | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a big-endian 16-bit value from a stream. | |
/// </summary> | |
/// <param name="stream">Stream to read.</param> | |
/// <returns>The value read.</returns> | |
public static int ReadU16(Stream stream, String tag) | |
{ | |
return ReadU16(ReadBytes(stream, 2, tag), 0); | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a big-endian 16-bit value from binary data. | |
/// </summary> | |
/// <param name="data">Binary data array.</param> | |
/// <param name="index">Location of the data to read.</param> | |
/// <returns>The value read.</returns> | |
public static int ReadU16(byte[] buf, int index) | |
{ | |
unchecked | |
{ | |
return ((int)buf[index] << 8) | buf[index + 1]; | |
} | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a big-endian 24-bit value from a stream. | |
/// </summary> | |
/// <param name="stream">Stream to read.</param> | |
/// <returns>The value read.</returns> | |
public static int ReadU24(Stream stream, String tag) | |
{ | |
return ReadU24(ReadBytes(stream, 3, tag), 0); | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a big-endian 24-bit value from binary data. | |
/// </summary> | |
/// <param name="data">Binary data array.</param> | |
/// <param name="index">Location of the data to read.</param> | |
/// <returns>The value read.</returns> | |
public static int ReadU24(byte[] buf, int index) | |
{ | |
unchecked | |
{ | |
return ((int)buf[index] << 16) | (buf[index + 1] << 8) | buf[index + 2]; | |
} | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a big-endian 32-bit value from a stream. | |
/// </summary> | |
/// <param name="stream">Stream to read.</param> | |
/// <returns>The value read.</returns> | |
public static int ReadU32(Stream stream, String tag) | |
{ | |
return ReadU32(ReadBytes(stream, 4, tag), 0); | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a big-endian 32-bit value from binary data. | |
/// </summary> | |
/// <param name="data">Binary data array.</param> | |
/// <param name="index">Location of the data to read.</param> | |
/// <returns>The value read.</returns> | |
public static int ReadU32(byte[] buf, int index) | |
{ | |
unchecked | |
{ | |
return ((int)buf[index] << 24) | | |
(buf[index + 1] << 16) | | |
(buf[index + 2] << 8) | | |
buf[index + 3]; | |
} | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a big-endian 64-bit value from a stream. | |
/// </summary> | |
/// <param name="stream">Stream to read.</param> | |
/// <returns>The value read.</returns> | |
public static long ReadU64(Stream stream, String tag) | |
{ | |
return ReadU64(ReadBytes(stream, 8, tag), 0); | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a big-endian 64-bit value from binary data. | |
/// </summary> | |
/// <param name="data">Binary data array.</param> | |
/// <param name="index">Location of the data to read.</param> | |
/// <returns>The value read.</returns> | |
public static long ReadU64(byte[] buf, int index) | |
{ | |
unchecked | |
{ | |
return | |
((long)buf[index] << 56) | | |
((long)buf[index + 1] << 48) | | |
((long)buf[index + 2] << 40) | | |
((long)buf[index + 3] << 32) | | |
((long)buf[index + 4] << 24) | | |
((long)buf[index + 5] << 16) | | |
((long)buf[index + 6] << 8) | | |
(long)buf[index + 7]; | |
} | |
} | |
//-------------------------------------------------------------------- | |
public static byte[] ReadMSB7ExtendedBytes(Stream stream, String tag) | |
{ | |
var bytes = new List<byte>(); | |
for (;;) | |
{ | |
var b = (byte)ReadU8(stream, tag); | |
bytes.Add(b); | |
if ((b & 0x80) == 0) | |
return bytes.ToArray(); | |
} | |
} | |
public delegate int ReadStreamDelegate(Stream stream); | |
public delegate int ReadMemoryDelegate(byte[] buf, int index); | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Read a UTF-8 string from a stream. | |
/// </summary> | |
/// <param name="stream">Stream to read.</param> | |
/// <param name="sizeInBytes">Number of bytes.</param> | |
/// <returns></returns> | |
public static string ReadString(Stream stream, int sizeInBytes, String tag) | |
{ | |
byte[] bytes = ReadBytes(stream, sizeInBytes, tag); | |
return Encoding.UTF8.GetString(bytes, 0, bytes.Length); | |
} | |
//-------------------------------------------------------------------- | |
/// <summary> | |
/// Skip (just read) the specified bytes. | |
/// </summary> | |
/// <param name="stream">Stream to read.</param> | |
/// <param name="count">Number of bytes to skip.</param> | |
public static void SkipBytes(Stream stream, int count) | |
{ | |
if (count == 0) | |
return; | |
if (stream.CanSeek) | |
{ | |
stream.Seek(count, SeekOrigin.Current); | |
return; | |
} | |
const int BUFSIZE = 1024 * 10; | |
byte[] tmp = new byte[count < BUFSIZE ? count : BUFSIZE]; | |
for (; ;) | |
{ | |
int byte2Read = count < BUFSIZE ? count : BUFSIZE; | |
int bytesRead = stream.Read(tmp, 0, byte2Read); | |
if(bytesRead == 0) | |
return; | |
count -= bytesRead; | |
if(count == 0) | |
return; | |
} | |
} | |
} | |
/// <summary> | |
/// This class enables create a new short stream on an existing | |
/// stream. | |
/// </summary> | |
public class PartialStream : Stream | |
{ | |
/// <summary> | |
/// Initializes a new instance. | |
/// </summary> | |
/// <param name="stream">Stream.</param> | |
/// <param name="length">Length of the new stream.</param> | |
public PartialStream(Stream stream, int length) | |
{ | |
m_stream = stream; | |
m_length = length; | |
m_firstPos = m_stream.CanSeek ? m_stream.Position : 0L; | |
m_pos = 0; | |
} | |
public long BasePosition { get { return m_firstPos; } } | |
public override bool CanRead { get { return true; } } | |
public override bool CanSeek { get { return m_stream.CanSeek; } } | |
public override bool CanWrite { get { return false; } } | |
public override void Flush() { } | |
public override long Length { get { return m_length; } } | |
public override long Position | |
{ | |
get { return m_pos; } | |
set { m_pos = m_stream.Position = value; } | |
} | |
public override int Read(byte[] buffer, int offset, int count) | |
{ | |
if (m_pos >= m_length) | |
return 0; | |
if (m_pos + count > m_length) | |
count = (int)(m_length - m_pos); | |
int bytes = m_stream.Read(buffer, offset, count); | |
m_pos += bytes; | |
return bytes; | |
} | |
public override long Seek(long offset, SeekOrigin origin) | |
{ | |
if (m_stream.CanSeek) | |
{ | |
switch (origin) | |
{ | |
case SeekOrigin.Begin: | |
offset += m_firstPos; | |
break; | |
case SeekOrigin.End: | |
offset += m_firstPos + m_length; | |
origin = SeekOrigin.Begin; | |
break; | |
} | |
m_pos = m_stream.Seek(offset, origin) - m_firstPos; | |
return m_pos; | |
} | |
else | |
{ | |
throw new NotSupportedException(); | |
} | |
} | |
public override void SetLength(long value) | |
{ | |
throw new NotSupportedException(); | |
} | |
public override void Write(byte[] buffer, int offset, int count) | |
{ | |
throw new NotSupportedException(); | |
} | |
Stream m_stream; | |
long m_length; | |
long m_firstPos; | |
long m_pos; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment