using System; using System.Collections.Generic; using System.Text; using System.IO; namespace NBHTool { public class NBH { /* some small byte helpers i used while reving it */ public class ByteHelpers { public static string ByteArrayToString(byte[] b_in) { string temp = ""; foreach (byte b in b_in) temp += (char)b; return temp; } public static string ByteArrayToHexString(byte[] b_in) { string temp = ""; foreach (byte b in b_in) temp += string.Format("{0:x2} ", b); return temp; } public static int LongIntToDecimal(byte[] b_in) { return b_in[0] + b_in[1] * 256 + b_in[2] * 65536 + b_in[3] * 16777216; } } /* nbh rom class used to extract RNB rom from NBH rom * to create a NBH rom from a RNB rom */ public class NBHRom { /* NBH file identifier */ const string s_nbhident = "R000FF\n"; string s_file; public delegate void debugPrint(string function, params object[] arg); debugPrint DebugPrint; /* NBHBlock: reads and writes signed blocks */ public class NBHBlock { public byte[] b_blocksize; public byte[] b_signaturesize; public int b_flag; public byte[] b_block; public byte[] b_signature; private FileStream fs_nbhstream; public NBHBlock(FileStream fs_nbh) { fs_nbhstream = fs_nbh; b_blocksize = new byte[4]; b_signaturesize = new byte[4]; b_flag = new byte(); } public void ReadBlock() { /* * block: * * [ 4byte blocksize ][4byte signaturesize][1byte flag][ x-bytes block][ x-bytes signature] * the last block has flag == 2, and only contains a signature, probably for the whole file. */ fs_nbhstream.Read(b_blocksize, 0, 4); fs_nbhstream.Read(b_signaturesize, 0, 4); b_flag = fs_nbhstream.ReadByte(); int blocksize = ByteHelpers.LongIntToDecimal(b_blocksize); int signaturesize = ByteHelpers.LongIntToDecimal(b_signaturesize); /* Console.WriteLine("blocksize: {0} signaturesize: {1} flag: {2}", blocksize, signaturesize, b_flag); */ b_block = new byte[blocksize]; fs_nbhstream.Read(b_block, 0, blocksize); b_signature = new byte[signaturesize]; fs_nbhstream.Read(b_signature, 0, signaturesize); } /* returns true if there is more to write, false if this was the last byte */ public bool WriteBlock(FileStream fs_source, string signature) { Int32 i_blocksize = 65536; const Int32 i_sigsize = 128; byte b_flag = 1; if (fs_source.Length == fs_source.Position) { i_blocksize = 0; b_flag = 2; } else if (fs_source.Length - fs_source.Position < i_blocksize) { i_blocksize = (Int32)(fs_source.Length - fs_source.Position); } /* * block: * * [ 4byte blocksize ][4byte signaturesize][1byte flag][ x-bytes block][ x-bytes signature] * the last block has flag == 2, and only contains a signature, probably for the whole file. */ byte[] b_block = new byte[i_blocksize]; int b_read = 0; if ((b_read = fs_source.Read(b_block, 0, b_block.Length)) != i_blocksize) throw new Exception("critical error reading from rnb file"); byte[] b_signature = new byte[i_sigsize]; Encoding.ASCII.GetBytes(signature, 0, signature.Length, b_signature, 0); byte[] b_bytes = new byte[4]; /* used for block headers */ b_bytes = BitConverter.GetBytes(i_blocksize); /* blocksize */ fs_nbhstream.Write(b_bytes, 0, b_bytes.Length); b_bytes = BitConverter.GetBytes(i_sigsize); /* signaturesize */ fs_nbhstream.Write(b_bytes, 0, b_bytes.Length); fs_nbhstream.WriteByte(b_flag); /* flag */ fs_nbhstream.Write(b_block, 0, i_blocksize); fs_nbhstream.Write(b_signature, 0, i_sigsize); if (b_flag == 2) return false; return true; } } public NBHRom(string s_filein, debugPrint debugprint) { s_file = s_filein; DebugPrint = debugprint; } /* convert a rom to the rbh format, the rbh format is the nbh format without the signatures and the block headers */ public void ConvertToRBH(string s_fileout) { FileStream nbh = new FileStream(s_file, FileMode.Open, FileAccess.Read); byte[] b_headercode = new byte[23]; /* according to xda-dev wiki 7bytes filetype+16 bytes garbage */ nbh.Read(b_headercode, 0, b_headercode.Length); DebugPrint("headercode: {0}", ByteHelpers.ByteArrayToString(b_headercode)); FileStream rnbout = new FileStream(s_fileout, FileMode.Create, FileAccess.Write); /* write the raw RNB format */ NBHBlock nbhblock = new NBHBlock(nbh); do { nbhblock.ReadBlock(); rnbout.Write(nbhblock.b_block, 0, nbhblock.b_block.Length); } while (nbhblock.b_flag != 2); /* flag 2 = last block */ rnbout.Close(); nbh.Close(); } /* convert a rbh rom to the nbh rom */ public void ConvertToNBH(string s_filein, string signature) { DebugPrint("Converting {0} into {1}", s_filein, s_file); if (signature.Length > 128) throw new Exception("signature exceeds limit"); FileStream fs_nbh = new FileStream(s_file, FileMode.Create, FileAccess.Write); byte[] b_bytes = new byte[23]; Encoding.ASCII.GetBytes(s_nbhident, 0, s_nbhident.Length, b_bytes, 0); fs_nbh.Write(b_bytes, 0, b_bytes.Length); FileStream fs_in = new FileStream(s_filein, FileMode.Open, FileAccess.Read); NBHBlock nbh_block = new NBHBlock(fs_nbh); while (nbh_block.WriteBlock(fs_in, signature) == true) { #if false if (fs_in.Position % 0xB3800 == 0) Console.Write("\r\r\r\r\r\r\r\r\r\r\r\r\r\r{0:f2}MB of {1:f2}MB written", (double)fs_in.Position / 1024 / 1024, (double)fs_in.Length / 1024 / 1024); #endif } #if false Console.WriteLine("\r\r\r\r\r\r\r\r\r\r\r\r\r\r{0:f2}MB of {1:f2}MB written", (double)fs_in.Position / 1024 / 1024, (double)fs_in.Length / 1024 / 1024); #else DebugPrint("{0:f2}MB of {1:f2}MB written", (double)fs_in.Position / 1024 / 1024, (double)fs_in.Length / 1024 / 1024); #endif fs_nbh.Close(); fs_in.Close(); } } /* rnb rom class used to extract NB rom * or to construct rnb rom from several NB roms */ public class RNBRom { public class NBRom { /* image types, also used for filenames */ public enum NBImageType { IPL = 0x100, G3IPL = 0x101, G4IPL = 0x102, ATHENA_IPL = 0x103, SPL = 0x200, Radio = 0x300, Windows = 0x400, Diagnostics = 0x500, PrimarySplash = 0x600, SecondarySplash = 0x601, Herm_ExtendedRom = 0x700, SIRFBootLoader = 0x810, ExtendedRom = 0x900 } public NBImageType ImageType; public long offset; public long length; /* only used when writing images */ public string s_fullpath; public NBRom(int i_type) { ImageType = (NBImageType)i_type; } } /* rnb header information * * 32bytes imagetype in UTF32 format * 32bytes device type in ASCII format * 128bytes * 32 * 4 bytes nb image type * 128bytes * 32 * 4 bytes nb offset * 128bytes * 32 * 4 bytes nb length * * followed by raw nb images */ public string s_cid; public string s_language; public string s_version; public string s_device; public string s_imagetype; private FileStream fs_rbh; private List nbr_list; public delegate void debugPrint(string function, params object[] arg); debugPrint DebugPrint; public RNBRom(debugPrint myprint) { DebugPrint = myprint; nbr_list = new List(); } public int RomCount() { if (nbr_list == null) return 0; return nbr_list.Count; } /* extraction */ public void LoadRom(string s_filename) { if (fs_rbh != null) throw new IOException("please close first romhandle prior to opening another with the same object"); fs_rbh = new FileStream(s_filename, FileMode.Open, FileAccess.Read); /* read rom type */ byte[] b_imagetype = new byte[32]; fs_rbh.Read(b_imagetype, 0, b_imagetype.Length); s_imagetype = System.Text.Encoding.UTF32.GetString(b_imagetype); DebugPrint("imagetype: {0}", s_imagetype); /* read device type */ fs_rbh.Read(b_imagetype, 0, b_imagetype.Length); s_device = System.Text.Encoding.ASCII.GetString(b_imagetype); DebugPrint("devicetype: {0}", s_device); /* read rom types */ byte[] b_romtype = new byte[4]; for (int i = 0; i < 128; i += 4) { fs_rbh.Read(b_romtype, 0, b_romtype.Length); int i_romtype = ByteHelpers.LongIntToDecimal(b_romtype); if (i_romtype != 0) { NBRom nbr_rom = new NBRom(i_romtype); nbr_list.Add(nbr_rom); DebugPrint("found rom: {0} (0x{1:x2})", nbr_rom.ImageType, (int)nbr_rom.ImageType); } } DebugPrint("numer of roms in nbh: {0}", nbr_list.Count); /* read rom offests */ byte[] b_romoffset = new byte[4]; for (int i = 0; i < 128; i += 4) { fs_rbh.Read(b_romoffset, 0, b_romoffset.Length); int i_offset = ByteHelpers.LongIntToDecimal(b_romoffset); int rom_no = i / 4; if (i_offset != 0 && rom_no < nbr_list.Count) { nbr_list[rom_no].offset = i_offset; DebugPrint("found offset: {0} (0x{1:x2})", nbr_list[rom_no].ImageType, (int)nbr_list[rom_no].offset); } } /* read rom lengths */ byte[] b_romlength = new byte[4]; for (int i = 0; i < 128; i += 4) { fs_rbh.Read(b_romlength, 0, b_romlength.Length); int i_length = ByteHelpers.LongIntToDecimal(b_romlength); int rom_no = i / 4; if (i_length != 0 && rom_no < nbr_list.Count) { nbr_list[rom_no].length = i_length; DebugPrint("found length: {0} (0x{1:x2})", nbr_list[rom_no].ImageType, (int)nbr_list[rom_no].length); } } } public void CloseRom() { nbr_list = new List(); fs_rbh.Close(); fs_rbh = null; } public void ExtractNBH(string s_outdir) { if (fs_rbh == null) throw new Exception("no filehandle to rbh rom"); if (nbr_list.Count == 0) throw new Exception("please load the rom prior to extracting it"); if (s_outdir.EndsWith("\\") == false) s_outdir += "\\"; for (int i = 0; i < nbr_list.Count; i++) { FileStream fs_out = new FileStream(s_outdir + nbr_list[i].ImageType + ".nb", FileMode.Create, FileAccess.Write); DebugPrint("Creating: {0}\n", fs_out.Name); fs_rbh.Seek(nbr_list[i].offset, SeekOrigin.Begin); int peeksize = 65536; for (long bytesleft = nbr_list[i].length; bytesleft >= 0; bytesleft -= peeksize) { byte[] bytes; if (bytesleft < peeksize) { bytes = new byte[bytesleft]; fs_rbh.Read(bytes, 0, bytes.Length); fs_out.Write(bytes, 0, bytes.Length); break; } bytes = new byte[peeksize]; fs_rbh.Read(bytes, 0, bytes.Length); fs_out.Write(bytes, 0, bytes.Length); } fs_out.Close(); } } /* creation */ private long GetTotalRomSize() { long size = 0; foreach (NBRom nbr in nbr_list) size += nbr.length; return size; } public void AddRom(string s_fullpath, NBRom.NBImageType nbi_type) { NBRom nbr_add = new NBRom((int)nbi_type); try { FileStream fs_rom = new FileStream(s_fullpath, FileMode.Open, FileAccess.Read); nbr_add.length = fs_rom.Length; nbr_add.offset = 0x200 + GetTotalRomSize(); fs_rom.Close(); } catch (Exception e) { throw new Exception(string.Format("Couldn't open {0}: {1}", s_fullpath, e.Message)); } nbr_add.s_fullpath = s_fullpath; this.nbr_list.Add(nbr_add); DebugPrint("added rom: {0} type: {1}", s_fullpath, nbi_type); } public void SetHeaderInfo(string cid, string language, string version, string device) { s_cid = cid; s_language = language; s_version = version; s_device = device; DebugPrint(this.ToString()); } public void CreateRom(string s_outfile) { fs_rbh = new FileStream(s_outfile, FileMode.Create, FileAccess.Write); DebugPrint("rom: {0}", s_outfile); byte[] b_bytes = new byte[32]; string s_imagetype = "HTCIMAGE"; System.Text.UnicodeEncoding.UTF32.GetBytes(s_imagetype, 0, s_imagetype.Length, b_bytes, 0); DebugPrint("writing imagetype: {0}", ByteHelpers.ByteArrayToHexString(b_bytes)); fs_rbh.Write(b_bytes, 0, 32); b_bytes = new byte[32]; System.Text.Encoding.ASCII.GetBytes(this.s_device, 0, s_device.Length, b_bytes, 0); DebugPrint("writing devicetype: {0}", ByteHelpers.ByteArrayToHexString(b_bytes)); fs_rbh.Write(b_bytes, 0, 32); /* write rom types */ b_bytes = new byte[4]; for (int i = 0; i < 32; i++) { if (i >= this.nbr_list.Count) { b_bytes = new byte[4]; fs_rbh.Write(b_bytes, 0, 4); continue; } Int32 l_type = (int)this.nbr_list[i].ImageType; b_bytes = BitConverter.GetBytes(l_type); fs_rbh.Write(b_bytes, 0, b_bytes.Length); DebugPrint("writing rom type: {0} (0x{1:x2})", this.nbr_list[i].ImageType, ByteHelpers.ByteArrayToHexString(b_bytes)); } /* write rom offsets */ b_bytes = new byte[4]; for (int i = 0; i < 32; i++) { if (i >= this.nbr_list.Count) { b_bytes = new byte[4]; fs_rbh.Write(b_bytes, 0, 4); continue; } Int32 l_type = (int)this.nbr_list[i].offset; b_bytes = BitConverter.GetBytes(l_type); fs_rbh.Write(b_bytes, 0, b_bytes.Length); DebugPrint("writing rom offset: {0} (0x{1:x2})", this.nbr_list[i].ImageType, ByteHelpers.ByteArrayToHexString(b_bytes)); } /* write rom lengths */ b_bytes = new byte[4]; for (int i = 0; i < 32; i++) { if (i >= this.nbr_list.Count) { b_bytes = new byte[4]; fs_rbh.Write(b_bytes, 0, 4); continue; } Int32 l_type = (int)this.nbr_list[i].length; b_bytes = BitConverter.GetBytes(l_type); fs_rbh.Write(b_bytes, 0, b_bytes.Length); DebugPrint("writing rom length: {0} (0x{1:x2})", this.nbr_list[i].ImageType, ByteHelpers.ByteArrayToHexString(b_bytes)); } /* write additional rom headers */ /* cid type */ b_bytes = new byte[32]; System.Text.Encoding.ASCII.GetBytes(this.s_cid, 0, this.s_cid.Length, b_bytes, 0); DebugPrint("writing cid: {0}", ByteHelpers.ByteArrayToHexString(b_bytes)); fs_rbh.Write(b_bytes, 0, 32); /* version */ b_bytes = new byte[16]; System.Text.Encoding.ASCII.GetBytes(this.s_version, 0, this.s_version.Length, b_bytes, 0); DebugPrint("writing version: {0}", ByteHelpers.ByteArrayToHexString(b_bytes)); fs_rbh.Write(b_bytes, 0, 16); /* language */ b_bytes = new byte[16]; System.Text.Encoding.ASCII.GetBytes(this.s_language, 0, this.s_language.Length, b_bytes, 0); DebugPrint("writing language: {0}", ByteHelpers.ByteArrayToHexString(b_bytes)); fs_rbh.Write(b_bytes, 0, 16); foreach (NBRom nbr in nbr_list) { int peeksize = 65536; int i_bytesread = 0; b_bytes = new byte[peeksize]; FileStream fs_nbr = new FileStream(nbr.s_fullpath, FileMode.Open, FileAccess.Read); DebugPrint("Writing {0} into {1}", fs_nbr.Name, fs_rbh.Name); while ((i_bytesread = fs_nbr.Read(b_bytes, 0, b_bytes.Length)) != 0) { fs_rbh.Write(b_bytes, 0, i_bytesread); } fs_nbr.Close(); } fs_rbh.Close(); } public override string ToString() { return string.Format("{0} rom: {1} {2} {3}", s_device, s_language, s_cid, s_version); } } } }