diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader.sln" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader.sln" new file mode 100644 index 0000000..f22741a --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader.sln" @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BackupReader", "BackupReader\BackupReader.csproj", "{CAAE6D2F-69A2-46E0-A310-BF1DA11B19B9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CAAE6D2F-69A2-46E0-A310-BF1DA11B19B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CAAE6D2F-69A2-46E0-A310-BF1DA11B19B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAAE6D2F-69A2-46E0-A310-BF1DA11B19B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CAAE6D2F-69A2-46E0-A310-BF1DA11B19B9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader.suo" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader.suo" new file mode 100644 index 0000000..b7ab293 Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader.suo" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/BackupReader.csproj" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/BackupReader.csproj" new file mode 100644 index 0000000..d273095 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/BackupReader.csproj" @@ -0,0 +1,102 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {CAAE6D2F-69A2-46E0-A310-BF1DA11B19B9} + WinExe + Properties + BackupReader + BackupReader + backup.ico + false + LocalIntranet + false + + + + + 2.0 + 1 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + + + + + Form + + + frmMain.cs + + + + + Designer + frmMain.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/BackupReader.csproj.user" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/BackupReader.csproj.user" new file mode 100644 index 0000000..e50fd5e --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/BackupReader.csproj.user" @@ -0,0 +1,15 @@ + + + publish\ + + + + + + + + + en-US + false + + \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/BackupReader_TemporaryKey.pfx" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/BackupReader_TemporaryKey.pfx" new file mode 100644 index 0000000..f565c98 Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/BackupReader_TemporaryKey.pfx" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CBackupReader.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CBackupReader.cs" new file mode 100644 index 0000000..58f10a8 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CBackupReader.cs" @@ -0,0 +1,197 @@ + +namespace BackupReader +{ + /// + /// Represents a backup file reader. + /// + class CBackupReader + { + private long mLastPos; + private long mIncrement; + private bool mCancel; + private CBackupStream mStream; + + /// + /// Provides an event handler for the OnProgressChange event. + /// Progress is an integer between 1-100, representing the progress of + /// the catalog read operation. + /// + public delegate void ProgressChange(int Progress); + /// + /// Occurs when the catalog read progress changes by 1%. + /// + public event ProgressChange OnProgressChange; + + /// + /// Returns the underlying stream. + /// + public CBackupStream Stream + { + get { return mStream; } + } + + /// + /// Reads the entire backup file and returns a root catalog node. + /// The root node contains backup sets/volumes/directories/files + /// as child nodes. + /// + public CCatalogNode ReadCatalog() + { + // Set to true to cancel reading + mCancel = false; + + // Read the media header + CTapeHeaderDescriptorBlock tape = (CTapeHeaderDescriptorBlock)mStream.ReadDBLK(); + // Read soft file mark + CSoftFilemarkDescriptorBlock file = (CSoftFilemarkDescriptorBlock)mStream.ReadDBLK(); + + // Create the root catalog node + CCatalogNode node = new CCatalogNode(tape.MediaName, ENodeType.Root, 0); + CCatalogNode nLastSet = null; + CCatalogNode nLastVolume = null; + CCatalogNode nLastDir = null; + + // Get next block type + EBlockType bt = mStream.PeekNextBlockType(); + while ((bt != EBlockType.MTF_EOTM) && (bt != 0) && (mCancel == false)) + { + // Read next block + CDescriptorBlock block = mStream.ReadDBLK(); + + // Add to catalog + if (bt == EBlockType.MTF_SSET) + { + CStartOfDataSetDescriptorBlock sset = (CStartOfDataSetDescriptorBlock)block; + CCatalogNode cnode = node.AddSet("Set: " + sset.DataSetNumber + " - " + sset.DataSetName, block.StartPosition); + nLastSet = cnode; + } + else if (bt == EBlockType.MTF_VOLB) + { + CVolumeDescriptorBlock vol = (CVolumeDescriptorBlock)block; + CCatalogNode cnode = nLastSet.AddVolume(vol.DeviceName, block.StartPosition); + nLastVolume = cnode; + } + else if (bt == EBlockType.MTF_DIRB) + { + CDirectoryDescriptorBlock dir = (CDirectoryDescriptorBlock)block; + // Check if the directory name is contained in a data stream + CCatalogNode cnode = null; + if ((dir.DIRBAttributes & EDIRBAttributes.DIRB_PATH_IN_STREAM_BIT) != 0) + { + foreach (CDataStream data in dir.Streams) + { + if (data.Header.StreamID == "PNAM") + { + if (dir.StringType == EStringType.ANSI) + { + System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); + string str = encoding.GetString(data.Data); + str = str.Substring(0, str.Length - 1); + cnode = nLastVolume.AddFolder(str, block.StartPosition); + } + else if (dir.StringType == EStringType.Unicode) + { + System.Text.UnicodeEncoding encoding = new System.Text.UnicodeEncoding(); + string str = encoding.GetString(data.Data); + str = str.Substring(0, str.Length - 1); + cnode = nLastVolume.AddFolder(str, block.StartPosition); + } + + } + } + } + else + cnode = nLastVolume.AddFolder(dir.DirectoryName.Substring(0, dir.DirectoryName.Length - 1), block.StartPosition); + + if (cnode != null) nLastDir = cnode; + } + else if (bt == EBlockType.MTF_FILE) + { + CFileDescriptorBlock fil = (CFileDescriptorBlock)block; + // Check if the file name is contained in a data stream + CCatalogNode cnode = null; + if ((fil.FileAttributes & EFileAttributes.FILE_NAME_IN_STREAM_BIT) != 0) + { + foreach (CDataStream data in fil.Streams) + { + if (data.Header.StreamID == "FNAM") + { + if (fil.StringType == EStringType.ANSI) + { + System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); + string str = encoding.GetString(data.Data); + cnode = nLastDir.AddFile(str, block.StartPosition); + } + else if (fil.StringType == EStringType.Unicode) + { + System.Text.UnicodeEncoding encoding = new System.Text.UnicodeEncoding(); + string str = encoding.GetString(data.Data); + cnode = nLastDir.AddFile(str, block.StartPosition); + } + + } + } + } + else + cnode = nLastDir.AddFile(fil.FileName, block.StartPosition); + } + + + // Get next block type + bt = mStream.PeekNextBlockType(); + + // Check progress + if (mStream.BaseStream.Position > mLastPos + mIncrement) + { + mLastPos = mStream.BaseStream.Position; + OnProgressChange((int)((float)mLastPos / (float)mStream.BaseStream.Length * 100.0f)); + } + } + + return node; + } + + /// + /// Stops reading the catalog. The nodes that has already been read will still be available. + /// + public void CancelRead() + { + mCancel = true; + } + + /// + /// Opens a backup file. + /// + public void Open(string Filename) + { + mStream = new CBackupStream(Filename); + mIncrement = mStream.BaseStream.Length / 100; + mLastPos = 0; + mCancel = false; + } + + /// + /// Closes the backup file. + /// + public void Close() + { + mStream.Close(); + } + + public CBackupReader() + { + + } + + public CBackupReader(string Filename) + { + Open(Filename); + } + + ~CBackupReader() + { + Close(); + } + } + +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CBackupStream.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CBackupStream.cs" new file mode 100644 index 0000000..10a710f --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CBackupStream.cs" @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BackupReader +{ + /// + /// Represents a binary stream for reading a backup file. + /// This class is derived from System.IO.BinaryReader. + /// + class CBackupStream : System.IO.BinaryReader + { + /// + /// Reads a descriptor block from the stream. + /// + public CDescriptorBlock ReadDBLK() + { + // Read block type + EBlockType bt = PeekNextBlockType(); + + switch (bt) + { + case EBlockType.MTF_TAPE: // TAPE descriptor block + return new CTapeHeaderDescriptorBlock(this); + case EBlockType.MTF_SSET: // Start of data SET descriptor block + return new CStartOfDataSetDescriptorBlock(this); + case EBlockType.MTF_VOLB: // VOLume descriptor Block + return new CVolumeDescriptorBlock(this); + case EBlockType.MTF_DIRB: // DIRectory descriptor Block + return new CDirectoryDescriptorBlock(this); + case EBlockType.MTF_FILE: // FILE descriptor block + return new CFileDescriptorBlock(this); + case EBlockType.MTF_CFIL: // Corrupt object descriptor block + return new CCorruptObjectDescriptorBlock(this); + case EBlockType.MTF_ESPB: // End of Set Pad descriptor Block + return new CEndOfPadSetDescriptorBlock(this); + case EBlockType.MTF_ESET: // End of SET descriptor block + return new CEndOfDataSetDescriptorBlock(this); + case EBlockType.MTF_EOTM: // End Of Tape Marker descriptor block + return new CEndOfTapeMarkerDescriptorBlock(this); + case EBlockType.MTF_SFMB: // Soft FileMark descriptor Block + return new CSoftFilemarkDescriptorBlock(this); + } + + return null; + } + + /// + /// Reads a stream header from the stream. + /// + public CDataStream.CStreamHeader ReadStreamHeader() + { + return new CDataStream.CStreamHeader(this); + } + + /// + /// Reads the type of the next desciptor block. Does not advance stream position. + /// + public EBlockType PeekNextBlockType() + { + // Check for EOF + if (BaseStream.Position + 4 >= BaseStream.Length) + return 0; + + EBlockType et = (EBlockType)ReadUInt32(); + BaseStream.Seek(-4, System.IO.SeekOrigin.Current); + return et; + } + + /// + /// Reads the type of the next data stream. + /// + public string GetNextDataStreamType() + { + // Check for EOF + if (BaseStream.Position + 4 >= BaseStream.Length) + return ""; + + return ReadFixedSizeString(4, EStringType.ANSI); + } + + /// + /// Reads OS specific data. It is assumed that a MTF_MEDIA_ADDRESS structure is next in the stream. + /// + public COSSpecificData ReadOsSpecificData(long StartPosition, EOSID OSID, byte OSVersion, EBlockType Type) + { + long oldpos = BaseStream.Position; + ushort sz = ReadUInt16(); + ushort off = ReadUInt16(); + BaseStream.Seek(StartPosition + off, System.IO.SeekOrigin.Begin); + + if ((OSID == EOSID.NetWare) && (OSVersion == 0)) + { + if (Type == EBlockType.MTF_DIRB) return new CNetwareDirB(this); + if (Type == EBlockType.MTF_FILE) return new CNetwareFile(this); + } + if ((OSID == EOSID.NetWare_SMS) && (OSVersion == 1)) + { + if (Type == EBlockType.MTF_DIRB) return new CNetwareSMSDirB(this); + if (Type == EBlockType.MTF_FILE) return new CNetwareSMSFile(this); + } + if ((OSID == EOSID.NetWare_SMS) && (OSVersion == 2)) + { + if (Type == EBlockType.MTF_DIRB) return new CNetwareSMEDirB(this); + if (Type == EBlockType.MTF_FILE) return new CNetwareSMEFile(this); + } + if ((OSID == EOSID.Windows_NT) && (OSVersion == 0)) + { + if (Type == EBlockType.MTF_DIRB) return new CWindowsNT0DirB(this); + if (Type == EBlockType.MTF_FILE) return new CWindowsNT0File(this); + } + if ((OSID == EOSID.Windows_NT) && (OSVersion == 1)) + { + if (Type == EBlockType.MTF_VOLB) return new CWindowsNT1VolB(this); + if (Type == EBlockType.MTF_DIRB) return new CWindowsNT1DirB(this); + if (Type == EBlockType.MTF_FILE) return new CWindowsNT1File(this); + } + if ((OSID == EOSID.OS_2) && (OSVersion == 0)) + { + if (Type == EBlockType.MTF_DIRB) return new COS2DirB(this); + if (Type == EBlockType.MTF_FILE) return new COS2File(this); + } + if ((OSID == EOSID.Windows_95) && (OSVersion == 0)) + { + if (Type == EBlockType.MTF_DIRB) return new CWindows95DirB(this); + if (Type == EBlockType.MTF_FILE) return new CWindows95File(this); + } + if ((OSID == EOSID.Macintosh) && (OSVersion == 0)) + { + if (Type == EBlockType.MTF_VOLB) return new CMacintoshVolB(this); + if (Type == EBlockType.MTF_DIRB) return new CMacintoshDirB(this); + if (Type == EBlockType.MTF_FILE) return new CMacintoshFile(this); + } + + BaseStream.Seek(oldpos + 4, System.IO.SeekOrigin.Begin); + + return new COSSpecificData(); + } + + /// + /// Reads a string. It is assumed that a MTF_MEDIA_ADDRESS structure is next in the stream. + /// + public string ReadString(long StartPosition, EStringType Type) + { + long oldpos = BaseStream.Position; + + ushort sz = ReadUInt16(); + long off = StartPosition + ReadUInt16(); + BaseStream.Seek(off, System.IO.SeekOrigin.Begin); + string str; + if (Type == EStringType.ANSI) + { + byte[] bytes = ReadBytes(sz); + System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); + str = encoding.GetString(bytes); + } + else if (Type == EStringType.Unicode) + { + byte[] bytes = ReadBytes(sz); + System.Text.UnicodeEncoding encoding = new System.Text.UnicodeEncoding(); + str = encoding.GetString(bytes); + } + else + { + str = ""; + } + + BaseStream.Seek(oldpos + 4, System.IO.SeekOrigin.Begin); + + return str; + } + + /// + /// Reads a string. It is assumed that string bytes is next in the stream. + /// + public string ReadFixedSizeString(int Size, EStringType Type) + { + string str; + if (Type == EStringType.ANSI) + { + byte[] bytes = ReadBytes(Size); + System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); + str = encoding.GetString(bytes); + } + else if (Type == EStringType.Unicode) + { + byte[] bytes = ReadBytes(Size); + System.Text.UnicodeEncoding encoding = new System.Text.UnicodeEncoding(); + str = encoding.GetString(bytes); + } + else + { + str = ""; + } + + return str; + } + + /// + /// Reads a date time. It is assumed that a 5 byte MTF_MEDIA_DATE structure is next in the stream. + /// + public System.DateTime ReadDate() + { + byte byte1 = ReadByte(); + byte byte2 = ReadByte(); + byte byte3 = ReadByte(); + byte byte4 = ReadByte(); + byte byte5 = ReadByte(); + + if ((byte1 == 0) && (byte2 == 0) && (byte3 == 0) && (byte4 == 0) && (byte5 == 0)) + return new System.DateTime(); + + int year = (byte1 << 6) + (byte2 >> 2); + int month = ((byte2 & 0x3) << 2) + (byte3 >> 6); + int day = ((byte3 & 0x3E) >> 1); + int hour = ((byte3 & 0x1) << 4) + (byte4 >> 4); + int minute = ((byte4 & 0xF) << 2) + (byte5 >> 6); + int second = (byte5 & 0x3F); + return new System.DateTime(year, month, day, hour, minute, second); + } + + public CBackupStream(string Filename) + : base (new System.IO.FileStream(Filename, System.IO.FileMode.Open, System.IO.FileAccess.Read)) + { + + } + + ~CBackupStream() + { + Close(); + } + + } +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CCatalog.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CCatalog.cs" new file mode 100644 index 0000000..0f56b75 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CCatalog.cs" @@ -0,0 +1,231 @@ + +namespace BackupReader +{ + enum ENodeType : int + { + Root = 0, + Set = 1, + Volume = 2, + Folder = 3, + File = 4, + } + + /// + /// Catalog nodes represents the blocks in the backup file. + /// + class CCatalogNode + { + private string mName; + private ENodeType mType; + private long mOffset; + private CCatalogNode mParent; + private System.Collections.Generic.List mNodes; + + public string Name + { + get { return mName; } + } + + public ENodeType Type + { + get { return mType; } + } + + public long Offset + { + get { return mOffset; } + } + + protected CCatalogNode Parent + { + get { return mParent; } + } + + public System.Collections.Generic.List Children + { + get { return mNodes; } + } + + public CCatalogNode() + { + mName = ""; + mType = ENodeType.Root; + mOffset = 0; + mParent = null; + mNodes = new System.Collections.Generic.List(); + } + + public CCatalogNode(string nName, ENodeType nType, long nOffset) + { + mName = nName; + mType = nType; + mOffset = nOffset; + mParent = null; + mNodes = new System.Collections.Generic.List(); + } + + public CCatalogNode AddSet(string nName, long nOffset) + { + CCatalogNode cnode = new CCatalogNode(nName, ENodeType.Set, nOffset); + cnode.mParent = this; + mNodes.Add(cnode); + return cnode; + } + + public CCatalogNode AddVolume(string nName, long nOffset) + { + CCatalogNode cnode = new CCatalogNode(nName, ENodeType.Volume, nOffset); + cnode.mParent = this; + mNodes.Add(cnode); + return cnode; + } + + public CCatalogNode AddFolder(string nName, long nOffset) + { + CCatalogNode cnode = new CCatalogNode(nName, ENodeType.Folder, nOffset); + cnode.mParent = this; + mNodes.Add(cnode); + return cnode; + } + + public CCatalogNode AddFile(string nName, long nOffset) + { + CCatalogNode cnode = new CCatalogNode(nName, ENodeType.File, nOffset); + cnode.mParent = this; + mNodes.Add(cnode); + return cnode; + } + + public bool ExtractTo(CBackupReader BackupFile, string TargetPath) + { + // Ensure that the target path has a trailing '\' + if (TargetPath[TargetPath.Length - 1] != '\\') + TargetPath += '\\'; + + if ((mType == ENodeType.Root) || (mType == ENodeType.Set)) + { + throw new System.Exception("Tape and set nodes can not be extracted. Only volume, folder or file nodes can be extracted."); + } + else if (mType == ENodeType.Volume) + { + System.IO.DirectoryInfo dirinfo = System.IO.Directory.CreateDirectory(TargetPath); + foreach (CCatalogNode node in mNodes) + node.ExtractTo(BackupFile, TargetPath); + } + else if (mType == ENodeType.Folder) + { + System.IO.DirectoryInfo dirinfo = System.IO.Directory.CreateDirectory(TargetPath + mName); + foreach (CCatalogNode node in mNodes) + node.ExtractTo(BackupFile, dirinfo.FullName); + } + else if (mType == ENodeType.File) + { + // Create the target directory if it does not exist + System.IO.Directory.CreateDirectory(TargetPath); + BackupFile.Stream.BaseStream.Seek(mOffset, System.IO.SeekOrigin.Begin); + System.IO.FileStream file = new System.IO.FileStream(TargetPath + mName, System.IO.FileMode.Create); + CFileDescriptorBlock fil = (CFileDescriptorBlock)BackupFile.Stream.ReadDBLK(); + foreach (CDataStream data in fil.Streams) + { + if (data.Header.StreamID == "STAN") + { + file.Write(data.Data, 0, data.Data.Length); + } + } + file.Close(); + } + + return true; + } + + /// + /// Saves the catalog to the disk. + /// + public static void SaveCatalog(string Filename, CCatalogNode Node, string BackupFilename) + { + // Open the file + System.IO.BinaryWriter file = new System.IO.BinaryWriter(new System.IO.FileStream(Filename, System.IO.FileMode.Create, System.IO.FileAccess.Write)); + + // Write full path to backup file + file.Write(BackupFilename); + + // Write nodes + Node.SaveNode(file); + + // Close the file + file.Close(); + } + + /// + /// Reads the name of the backup file used to create the catalog. + /// + public static string ReadBackupFilename(string Filename) + { + // Open the file + System.IO.BinaryReader file = new System.IO.BinaryReader(new System.IO.FileStream(Filename, System.IO.FileMode.Open, System.IO.FileAccess.Read)); + + // Read backup file name + string bkfname = file.ReadString(); + + // Close the file + file.Close(); + + return bkfname; + } + + /// + /// Reads the catalog from the disk. + /// + public static CCatalogNode ReadCatalog(string Filename) + { + // Create the root node + CCatalogNode Node = new CCatalogNode(); + + // Open the file + System.IO.BinaryReader file = new System.IO.BinaryReader(new System.IO.FileStream(Filename, System.IO.FileMode.Open, System.IO.FileAccess.Read)); + + // Read backup file name + file.ReadString(); + + // Read nodes + Node.ReadNode(file); + + // Close the file + file.Close(); + + return Node; + } + + private void SaveNode(System.IO.BinaryWriter file) + { + // Write node info + file.Write((int)mType); + file.Write(mName); + file.Write(mOffset); + file.Write(mNodes.Count); + + // Recursively write child nodes + foreach (CCatalogNode node in mNodes) + node.SaveNode(file); + } + + private void ReadNode(System.IO.BinaryReader file) + { + // Read node info + mType = (ENodeType)file.ReadInt32(); + mName = file.ReadString(); + mOffset = file.ReadInt64(); + int count = file.ReadInt32(); + + // Recursively read child nodes + for (int i = 0; i < count; i++) + { + CCatalogNode node = new CCatalogNode(); + mNodes.Add(node); + node.ReadNode(file); + } + } + + } + +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CDataStream.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CDataStream.cs" new file mode 100644 index 0000000..589b2c4 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CDataStream.cs" @@ -0,0 +1,76 @@ + +namespace BackupReader +{ + enum EStreamFileSystemAttributes : ushort + { + STREAM_MODIFIED_BY_READ = 1, // Data in stream has changed after reading, do not attempt to do a verify operation. BIT0 + STREAM_CONTAINS_SECURITY = 2, // Security information is contained in this stream. BIT1 + STREAM_IS_NON_PORTABLE = 4, // This data can only be restored to the same OS that it was saved from. BIT2 + STREAM_IS_SPARSE = 8, // The stream data is sparse (see below) BIT3 + //Reserved for future use. BIT4 - BIT15 + } + + enum EStreamMediaFormatAttributes : ushort + { + STREAM_CONTINUE = 1, // This is a continuation stream. BIT0 + STREAM_VARIABLE = 2, // Data size for this stream is variable. BIT1 + STREAM_VAR_END = 4, // Last piece of the variable length data. BIT2 + STREAM_ENCRYPTED = 8, // This stream is encrypted. BIT3 + STREAM_COMPRESSED = 16, // This stream is compressed. BIT4 + STREAM_CHECKSUMED = 32, // This stream is followed by a checksum stream. BIT5 + STREAM_EMBEDDED_LENGTH = 64, // The stream length is embedded in the data BIT6 + // Reserved for future use. BIT7 - BIT15 + } + + /// + /// Each block in the backup file is followed by one or more data streams. + /// Data streams may be used for alignment purposes, storing file data, or + /// storing long directory and file names, security information, etc. + /// + class CDataStream + { + public CStreamHeader Header; + public byte[] Data; + + public CDataStream(CBackupStream Reader) + { + Header = new CStreamHeader(Reader); + Data = Reader.ReadBytes((int)Header.StreamLength); + // Ensure that we are on the 4 byte boundary + long nullbytecount = (4 - (Reader.BaseStream.Position % 4)) % 4; + Reader.BaseStream.Seek(nullbytecount, System.IO.SeekOrigin.Current); + } + + public class CStreamHeader + { + public string StreamID; + public EStreamFileSystemAttributes StreamFileSystemAttributes; + public EStreamMediaFormatAttributes StreamMediaFormatAttributes; + public ulong StreamLength; + public ushort DataEncryptionAlgorithm; + public ushort DataCompressionAlgorithm; + public ushort Checksum; // Stream data follow immediately after the Checksum field. + + public CStreamHeader(CBackupStream Reader) + { + // Check for EOF + if (Reader.BaseStream.Position + 22 >= Reader.BaseStream.Length) + { + StreamID = ""; + return; + } + + StreamID = Reader.ReadFixedSizeString(4, EStringType.ANSI); + + StreamFileSystemAttributes = (EStreamFileSystemAttributes)Reader.ReadUInt16(); + StreamMediaFormatAttributes = (EStreamMediaFormatAttributes)Reader.ReadUInt16(); + StreamLength = Reader.ReadUInt64(); + DataEncryptionAlgorithm = Reader.ReadUInt16(); + DataCompressionAlgorithm = Reader.ReadUInt16(); + Checksum = Reader.ReadUInt16(); + } + } + + } + +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CDescriptorBlock.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CDescriptorBlock.cs" new file mode 100644 index 0000000..afdb127 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CDescriptorBlock.cs" @@ -0,0 +1,484 @@ + +namespace BackupReader +{ + + enum EBlockType: uint + { + /// + /// TAPE descriptor block + /// + MTF_TAPE = 0x45504154, + /// + /// Start of data SET descriptor block + /// + MTF_SSET = 0x54455353, + /// + /// VOLume descriptor Block + /// + MTF_VOLB = 0x424C4F56, + /// + /// DIRectory descriptor Block + /// + MTF_DIRB = 0x42524944, + /// + /// FILE descriptor block + /// + MTF_FILE = 0x454C4946, + /// + /// Corrupt object descriptor block + /// + MTF_CFIL = 0x4C494643, + /// + /// End of Set Pad descriptor Block + /// + MTF_ESPB = 0x42505345, + /// + /// End of SET descriptor block + /// + MTF_ESET = 0x54455345, + /// + /// End Of Tape Marker descriptor block + /// + MTF_EOTM = 0x4D544F45, + /// + /// Soft FileMark descriptor Block + /// + MTF_SFMB = 0x424D4653, + } + + enum EBlockAttributes : uint + { + MTF_CONTINUATION = 0x1, // Bit set if DBLK is a continuation from the previous tape. any BIT0 + MTF_COMPRESSION = 0x4, // Bit set if compression may be active. any BIT2 + MTF_EOS_AT_EOM = 0x8, // Bit set if the End Of Medium was hit during end of set processing. any BIT3 + MTF_SET_MAP_EXISTS = 0x10000, // Bit set if an Media Based Catalog Set Map can be found on the tape. MTF_TAPE BIT16 + MTF_FDD_ALLOWED = 0x20000, // Bit set if an attempt will be made to put a Media Based Catalog File/Directory Detail section on the tape. MTF_TAPE BIT17 + MTF_FDD_EXISTS = 0x10000, // Bit set if a Media Based Catalog File/Directory Detail section has been successfully put on the tape for this Data Set. MTF_SSET BIT16 + MTF_ENCRYPTION = 0x20000, // Bit set if encryption is active for the data streams within this Data Set. MTF_SSET BIT17 + MTF_FDD_ABORTED = 0x10000, // Bit set if a Media Based Catalog File/Directory Detail section was aborted for any reason during the write operation. MTF_ESET BIT16 + MTF_END_OF_FAMILY = 0x20000, // Bit set if the Media Based Catalog Set Map has been aborted. This condition means that additional Data Sets cannot be appended to the tape. MTF_ESET BIT17 + MTF_ABORTED_SET = 0x40000, // Bit set if the Data Set was aborted while being written. This can happen if a fatal error occurs while writing data, or if the user terminates the data management operation. An MTF_ESET DBLK containing this flag is put at the end of the Data Set even if it was aborted. MTF_ESET BIT18 + MTF_NO_ESET_PBA = 0x10000, // Bit set if no Data Set ends on this tape (i.e. continuation tape must follow this tape). MTF_EOTM BIT16 + MTF_INVALID_ESET_PBA = 0x20000,// Bit set if the Physical Block Address (PBA) of the MTF_ESET is invalid because the tape drive doesn't support physical block addressing. MTF_EOTM BIT17 + } + + enum EOSID : byte + { + NetWare = 1, // 0 + NetWare_SMS = 13, // 1, 2 + Windows_NT = 14, // 0 + DOS_Windows_3X = 24, // 0 + OS_2 = 25, // 0 + Windows_95 = 26, // 0 + Macintosh = 27, // 0 + UNIX = 28, // 0 + // To Be Assigned 33 - 127 + // Vendor Specific 128 - 255 + } + + enum EStringType : byte + { + None = 0, + ANSI = 1, + Unicode = 2, + } + + /// + /// Represents a descriptor block. Descriptor blocks define the types and + /// attributes of the data in the backup file. + /// + class CDescriptorBlock + { + public long StartPosition; + + public EBlockType BlockType; + public EBlockAttributes Attributes; + public ushort OffsetToFirstEvent; // Obsolete + public EOSID OSID; + public byte OSVersion; + public ulong DisplayableSize; + public ulong FormatLogicalAddress; + public ushort ReservedMBC; + public ushort Reserved1; + public ushort Reserved2; + public ushort Reserved3; + public uint ControlBlock; + public uint Reserved4; + public COSSpecificData OsSpecificData; + public EStringType StringType; + public byte Reserved5; + public ushort HeaderChecksum; + + public System.Collections.Generic.List Streams; + + /// + /// Read block header. + /// + protected void ReadData(CBackupStream Reader) + { + StartPosition = Reader.BaseStream.Position; + Streams = new System.Collections.Generic.List(); + + BlockType = (EBlockType)Reader.ReadUInt32(); + Attributes = (EBlockAttributes)Reader.ReadUInt32(); + OffsetToFirstEvent = Reader.ReadUInt16(); + OSID = (EOSID)Reader.ReadByte(); + OSVersion = Reader.ReadByte(); + DisplayableSize = Reader.ReadUInt64(); + FormatLogicalAddress = Reader.ReadUInt64(); + ReservedMBC = Reader.ReadUInt16(); + Reserved1 = Reader.ReadUInt16(); + Reserved2 = Reader.ReadUInt16(); + Reserved3 = Reader.ReadUInt16(); + ControlBlock = Reader.ReadUInt32(); + Reserved4 = Reader.ReadUInt32(); + OsSpecificData = Reader.ReadOsSpecificData(StartPosition, OSID, OSVersion, BlockType); + StringType = (EStringType)Reader.ReadByte(); + Reserved5 = Reader.ReadByte(); + HeaderChecksum = Reader.ReadUInt16(); + } + + /// + /// Read streams following this block. + /// + /// + protected void ReadStreams(CBackupStream Reader) + { + // Move to stream + long off = OffsetToFirstEvent + StartPosition; + // Make sure we are at a 4 byte boundary + long nullbytecount = (4 - (off % 4)) % 4; + + Reader.BaseStream.Seek(off + nullbytecount, System.IO.SeekOrigin.Begin); + string streamtype = ""; + + do + { + // Read next stream + CDataStream stream = new CDataStream(Reader); + streamtype = stream.Header.StreamID; + Streams.Add(stream); + } while ((streamtype != "SPAD") && (streamtype != "")); + } + + public CDescriptorBlock() + { + } + + public CDescriptorBlock(CBackupStream Reader) + { + ReadData(Reader); + } + } + + enum ETapeAttributes : uint + { + TAPE_SOFT_FILEMARK_BIT = 1, + TAPE_MEDIA_LABEL_BIT = 2, + // Reserved 2-23 + // Vendor Specific 24-31 + } + + enum EMediaBasedCatalogType : ushort + { + No_MBC = 0, + Type_1_MBC = 1, + Type_2_MBC = 2, + } + + class CTapeHeaderDescriptorBlock : CDescriptorBlock + { + public uint MediaFamilyID; + public ETapeAttributes TapeAttributes; + public ushort MediaSequenceNumber; + public ushort PasswordEncryptionAlgorithm; + public ushort SoftFilemarkBlockSize; + public EMediaBasedCatalogType MediaBasedCatalogType; + public string MediaName; + public string MediaDescription; + public string MediaPassword; + public string SoftwareName; + public ushort FormatLogicalBlockSize; + public ushort SoftwareVendorID; + public System.DateTime MediaDate; + public byte MTFMajorVersion; + + public CTapeHeaderDescriptorBlock(CBackupStream Reader) + { + base.ReadData(Reader); + MediaFamilyID = Reader.ReadUInt32(); + TapeAttributes = (ETapeAttributes)Reader.ReadUInt32(); + MediaSequenceNumber = Reader.ReadUInt16(); + PasswordEncryptionAlgorithm = Reader.ReadUInt16(); + SoftFilemarkBlockSize = Reader.ReadUInt16(); + MediaBasedCatalogType = (EMediaBasedCatalogType)Reader.ReadUInt16(); + MediaName = Reader.ReadString(StartPosition, StringType); + MediaDescription = Reader.ReadString(StartPosition, StringType); + MediaPassword = Reader.ReadString(StartPosition, StringType); + SoftwareName = Reader.ReadString(StartPosition, StringType); + FormatLogicalBlockSize = Reader.ReadUInt16(); + SoftwareVendorID = Reader.ReadUInt16(); + MediaDate = Reader.ReadDate(); + MTFMajorVersion = Reader.ReadByte(); + base.ReadStreams(Reader); + } +} + + class CEndOfTapeMarkerDescriptorBlock : CDescriptorBlock + { + public ulong LastESETPBA; + + public CEndOfTapeMarkerDescriptorBlock(CBackupStream Reader) + { + base.ReadData(Reader); + LastESETPBA = Reader.ReadUInt64(); + base.ReadStreams(Reader); + } + } + + enum ESSETAttributes : uint + { + SSET_TRANSFER_BIT = 0x1, // This bit is set if the data management operation is a transfer. It indicates that the files in this Data Set were removed from the source media after the operation was completed. BIT0 + SSET_COPY_BIT = 0x2, // This bit is set if the operation is a copy. The copy method copies all selected files from the primary storage to the media. The files modified flag IS NOT reset afterwards. BIT1 + SSET_NORMAL_BIT = 0x4, // This bit is set if the backup type is normal. The normal backup method backs up all selected files. The files modified flag IS reset afterwards. BIT2 + SSET_DIFFERENTIAL_BIT = 0x8, // This bit is set if the backup type is differential. The differential backup method only backs up selected files having their modified flag set. The files modified flag IS NOT reset afterwards. BIT3 + SSET_INCREMENTAL_BIT = 0x10, // This bit is set if the backup type is incremental. The incremental backup method only backs up selected files having their modified flag set. The files modified flag IS reset afterwards. BIT4 + SSET_DAILY_BIT = 0x20, // This bit is set if the backup type is daily. The daily backup method only backs up selected files created or modified with todays date. The files modified flag IS NOT reset afterwards. BIT5 + // Reserved (set to zero) BIT6 - BIT23 + // Vendor Specific BIT24 - BIT31 + } + + class CStartOfDataSetDescriptorBlock : CDescriptorBlock + { + public ESSETAttributes SSETAttributes; + public ushort PasswordEncryptionAlgorithm; + public ushort SoftwareCompressionAlgorithm; + public ushort SoftwareVendorID; + public ushort DataSetNumber; + public string DataSetName; + public string DataSetDescription; + public string DataSetPassword; + public string UserName; + public ulong PhysicalBlockAddress; + public System.DateTime MediaWriteDate; + public byte SoftwareMajorVersion; + public byte SoftwareMinorVersion; + public sbyte MTFTimeZone; + public byte MTFMinorVersion; + public byte MediaCatalogVersion; + + public CStartOfDataSetDescriptorBlock(CBackupStream Reader) + { + base.ReadData(Reader); + SSETAttributes = (ESSETAttributes)Reader.ReadUInt32(); + PasswordEncryptionAlgorithm = Reader.ReadUInt16(); + SoftwareCompressionAlgorithm = Reader.ReadUInt16(); + SoftwareVendorID = Reader.ReadUInt16(); + DataSetNumber = Reader.ReadUInt16(); + DataSetName = Reader.ReadString(StartPosition, StringType); + DataSetDescription = Reader.ReadString(StartPosition, StringType); + DataSetPassword = Reader.ReadString(StartPosition, StringType); + UserName = Reader.ReadString(StartPosition, StringType); + PhysicalBlockAddress = Reader.ReadUInt64(); + MediaWriteDate = Reader.ReadDate(); + SoftwareMajorVersion = Reader.ReadByte(); + SoftwareMinorVersion = Reader.ReadByte(); + MTFTimeZone = Reader.ReadSByte(); + MTFMinorVersion = Reader.ReadByte(); + MediaCatalogVersion = Reader.ReadByte(); + base.ReadStreams(Reader); + } + } + + class CEndOfDataSetDescriptorBlock : CDescriptorBlock + { + public ESSETAttributes ESETAttributes; + public uint NumberOfCorruptFiles; + public ulong ReservedforMBC1; + public ulong ReservedforMBC2; + public ushort FDDMediaSequenceNumber; + public ushort DataSetNumber; + public System.DateTime MediaWriteDate; + + public CEndOfDataSetDescriptorBlock(CBackupStream Reader) + { + base.ReadData(Reader); + ESETAttributes = (ESSETAttributes)Reader.ReadUInt32(); + NumberOfCorruptFiles = Reader.ReadUInt32(); + ReservedforMBC1 = Reader.ReadUInt64(); + ReservedforMBC2 = Reader.ReadUInt64(); + FDDMediaSequenceNumber = Reader.ReadUInt16(); + DataSetNumber = Reader.ReadUInt16(); + MediaWriteDate = Reader.ReadDate(); + base.ReadStreams(Reader); + } + } + + enum EVOLBAttributes : uint + { + VOLB_NO_REDIRECT_RESTORE_BIT = 0x1, // Objects following this DBLK can only be restored to the device from which they were backed up. BIT0 + VOLB_NON_VOLUME_BIT = 0x2, // Objects following this DBLK are not associated with a volume. BIT1 + VOLB_DEV_DRIVE_BIT = 0x4, // Device name format is, :. BIT2 + VOLB_DEV_UNC_BIT = 0x8, // Device name format is UNC. BIT3 + VOLB_DEV_OS_SPEC_BIT = 0x10, // Device name format is OS specific (refer to Appendix C for details on a given OS). BIT4 + VOLB_DEV_VEND_SPEC_BIT = 0x20 // Device name format is vendor specific. BIT5 + // Reserved (set to zero) BIT6 - BIT23 + // Vendor Specific BIT24 - BIT31 + } + + class CVolumeDescriptorBlock : CDescriptorBlock + { + public EVOLBAttributes VOLBAttributes; + public string DeviceName; + public string VolumeName; + public string MachineName; + public System.DateTime MediaWriteDate; + + public CVolumeDescriptorBlock(CBackupStream Reader) + { + base.ReadData(Reader); + VOLBAttributes = (EVOLBAttributes)Reader.ReadUInt32(); + DeviceName = Reader.ReadString(StartPosition, StringType); + VolumeName = Reader.ReadString(StartPosition, StringType); + MachineName = Reader.ReadString(StartPosition, StringType); + MediaWriteDate = Reader.ReadDate(); + base.ReadStreams(Reader); + } + } + + enum EDIRBAttributes : uint + { + DIRB_READ_ONLY_BIT = 0x100, // This bit is set if the directory is marked as read only. BIT8 + DIRB_HIDDEN_BIT = 0x200, // This bit is set if the directory is hidden from the user. BIT9 + DIRB_SYSTEM_BIT = 0x400, // This bit is set if the directory is a system directory. BIT10 + DIRB_MODIFIED_BIT = 0x800, // This bit is set if the directory has been modified. This is also referred to as an archive flag. BIT11 + DIRB_EMPTY_BIT = 0x10000, // This bit set if the directory contained no files or subdirectories. BIT16 + DIRB_PATH_IN_STREAM_BIT = 0x20000, // This bit set if the directory path is stored in a stream associated with this DBLK. BIT17 + DIRE_CORRUPT_BIT = 0x40000, // This bit set if the data associated with the directory could not be read. BIT18 + // Reserved (set to zero) BIT0 - BIT7, BIT12 - BIT15, BIT19 - BIT23 + // Vendor Specific BIT24 - BIT31 + } + + class CDirectoryDescriptorBlock : CDescriptorBlock + { + public EDIRBAttributes DIRBAttributes; + public System.DateTime LastModificationDate; + public System.DateTime CreationDate; + public System.DateTime BackupDate; + public System.DateTime LastAccessDate; + public uint DirectoryID; + public string DirectoryName; + + public CDirectoryDescriptorBlock(CBackupStream Reader) + { + base.ReadData(Reader); + DIRBAttributes = (EDIRBAttributes)Reader.ReadUInt32(); + LastModificationDate = Reader.ReadDate(); + CreationDate = Reader.ReadDate(); + BackupDate = Reader.ReadDate(); + LastAccessDate = Reader.ReadDate(); + DirectoryID = Reader.ReadUInt32(); + // MTF uses '\0' as the path seperator. Replace them with '\\' + DirectoryName = Reader.ReadString(StartPosition, StringType).Replace('\0','\\'); + base.ReadStreams(Reader); + } + } + + enum EFileAttributes : uint + { + FILE_READ_ONLY_BIT = 0x100, // This bit is set if the file is marked as read only. BIT8 + FILE_HIDDEN_BIT = 0x200, // This bit is set if the file is hidden from the user. BIT9 + FILE_SYSTEM_BIT = 0x400, // This bit is set if the file is a system file. BIT10 + FILE_MODIFIED_BIT = 0x800, // This bit is set if the file has been modified. This is also referred to as an archive flag. BIT11 + FILE_IN_USE_BIT = 0x10000, // This bit set if the file was in use at the time it was backed up. BIT16 + FILE_NAME_IN_STREAM_BIT = 0x20000, // This bit set if the file name is stored in a stream associated with this DBLK. BIT17 + FILE_CORRUPT_BIT = 0x40000, // This bit set if the data associated with the file could not be read. BIT18 + // Reserved (set to zero) BIT0 - BIT7, BIT12 - BIT15, BIT19 - BIT23 + // Vendor Specific BIT24 - BIT31 + } + + class CFileDescriptorBlock : CDescriptorBlock + { + public EFileAttributes FileAttributes; + public System.DateTime LastModificationDate; + public System.DateTime CreationDate; + public System.DateTime BackupDate; + public System.DateTime LastAccessDate; + public uint DirectoryID; + public uint FileID; + public string FileName; + + public CFileDescriptorBlock(CBackupStream Reader) + { + base.ReadData(Reader); + FileAttributes = (EFileAttributes)Reader.ReadUInt32(); + LastModificationDate = Reader.ReadDate(); + CreationDate = Reader.ReadDate(); + BackupDate = Reader.ReadDate(); + LastAccessDate = Reader.ReadDate(); + DirectoryID = Reader.ReadUInt32(); + FileID = Reader.ReadUInt32(); + FileName = Reader.ReadString(StartPosition, StringType); + base.ReadStreams(Reader); + } + } + + enum ECFilAttributes : uint + { + CFIL_LENGTH_CHANGE_BIT = 0x10000, // This bit is set if the file size has changed since the file was opened for the write operation. BIT16 + CFIL_UNREADABLE_BLK_BIT = 0x20000, // This bit is set if a hard error was encountered reading the source media (hard disk). This usually indicates that the media itself is bad (i.e. bad sector). BIT17 + CFIL_DEADLOCK_BIT = 0x40000, // This bit is set if the file was deadlocked. (i.e. On a system supporting record and file locking, it was not possible to get a region of a file unlocked within a watchdog time interval.) BIT18 + // Reserved (set to zero) BIT0 - BIT15, BIT19 - BIT23 + // Vendor Specific BIT24 - BIT31 + } + + class CCorruptObjectDescriptorBlock : CDescriptorBlock + { + public ECFilAttributes CFilAttributes; + public ulong Reserved; + public ulong StreamOffset; + public ushort CorrupStreamNumber; + + public CCorruptObjectDescriptorBlock(CBackupStream Reader) + { + base.ReadData(Reader); + CFilAttributes = (ECFilAttributes)Reader.ReadUInt32(); + Reserved = Reader.ReadUInt64(); + StreamOffset = Reader.ReadUInt64(); + CorrupStreamNumber = Reader.ReadUInt16(); + base.ReadStreams(Reader); + } + } + + class CEndOfPadSetDescriptorBlock : CDescriptorBlock + { + public CEndOfPadSetDescriptorBlock(CBackupStream Reader) + { + base.ReadData(Reader); + base.ReadStreams(Reader); + } + } + + class CSoftFilemarkDescriptorBlock : CDescriptorBlock + { + public uint NumberOfFilemarkEntries; + public uint FilemarkEntriesUsed; + public uint[] PBAofPreviousFilemarksArray; + + public CSoftFilemarkDescriptorBlock(CBackupStream Reader) + { + base.ReadData(Reader); + NumberOfFilemarkEntries = Reader.ReadUInt32(); + FilemarkEntriesUsed = Reader.ReadUInt32(); + PBAofPreviousFilemarksArray = new uint[FilemarkEntriesUsed]; + for (uint i = 0; i < NumberOfFilemarkEntries; i++) + { + uint val = Reader.ReadUInt32(); + if (i < FilemarkEntriesUsed) + PBAofPreviousFilemarksArray.SetValue(val, i); + } + //base.ReadStreams(Reader); + } + } + +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/COSSpecificData.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/COSSpecificData.cs" new file mode 100644 index 0000000..701105b --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/COSSpecificData.cs" @@ -0,0 +1,297 @@ +using System; + +namespace BackupReader +{ + /// + /// Represents data specific to an operating system. + /// + class COSSpecificData + { + } + + class CNetwareDirB : COSSpecificData + { + UInt32 OwnerID; + UInt32 DirectoryAttributes; + UInt32 MaximumSpace; + UInt16 InheritedRights; + + public CNetwareDirB(CBackupStream Reader) + { + OwnerID = Reader.ReadUInt32(); + DirectoryAttributes = Reader.ReadUInt32(); + MaximumSpace = Reader.ReadUInt32(); + InheritedRights = Reader.ReadUInt16(); + } + } + + class CNetwareFile : COSSpecificData + { + UInt32 OwnerID; + UInt32 FileAttributes; + UInt32 LastModiferID; + UInt32 ArchiverID; + UInt16 InheritedRights; + + public CNetwareFile(CBackupStream Reader) + { + OwnerID = Reader.ReadUInt32(); + FileAttributes = Reader.ReadUInt32(); + LastModiferID = Reader.ReadUInt32(); + ArchiverID = Reader.ReadUInt32(); + InheritedRights = Reader.ReadUInt16(); + } + } + + class CNetwareSMSDirB : COSSpecificData + { + UInt32 DirectoryAttributes; + Int16 Modified; + UInt32 CreatorNameSpace; + byte[] Volume; // 17 bytes + + public CNetwareSMSDirB(CBackupStream Reader) + { + DirectoryAttributes = Reader.ReadUInt32(); + Modified = Reader.ReadInt16(); + CreatorNameSpace = Reader.ReadUInt32(); + Volume = Reader.ReadBytes(17); + } + } + + class CNetwareSMSFile : COSSpecificData + { + UInt32 FileAttributes; + Int16 Modified; + UInt32 CreatorNameSpace; + byte[] Volume; // 17 bytes + + public CNetwareSMSFile(CBackupStream Reader) + { + FileAttributes = Reader.ReadUInt32(); + Modified = Reader.ReadInt16(); + CreatorNameSpace = Reader.ReadUInt32(); + Volume = Reader.ReadBytes(17); + } + } + + class CNetwareSMEDirB : COSSpecificData + { + UInt32 DirectoryAttributes; + UInt32 CreatorNameSpace; + byte[] Volume; // 18 bytes + Int16 Modified; + + public CNetwareSMEDirB(CBackupStream Reader) + { + DirectoryAttributes = Reader.ReadUInt32(); + CreatorNameSpace = Reader.ReadUInt32(); + Volume = Reader.ReadBytes(18); + Modified = Reader.ReadInt16(); + } + } + + class CNetwareSMEFile : COSSpecificData + { + UInt32 FileAttributes; + UInt32 CreatorNameSpace; + byte[] Volume; // 18 bytes + Int16 Modified; + + public CNetwareSMEFile(CBackupStream Reader) + { + FileAttributes = Reader.ReadUInt32(); + CreatorNameSpace = Reader.ReadUInt32(); + Volume = Reader.ReadBytes(18); + Modified = Reader.ReadInt16(); + } + } + + class CWindowsNT0DirB : COSSpecificData + { + UInt32 DirectoryAttributes; + + public CWindowsNT0DirB(CBackupStream Reader) + { + DirectoryAttributes = Reader.ReadUInt32(); + } + } + + class CWindowsNT0File : COSSpecificData + { + UInt32 FileAttributes; + UInt16 ShortNameOffset; + UInt16 ShortNameSize; + Int16 IsLink; + UInt16 Reserved; + + public CWindowsNT0File(CBackupStream Reader) + { + FileAttributes = Reader.ReadUInt32(); + ShortNameOffset = Reader.ReadUInt16(); + ShortNameSize = Reader.ReadUInt16(); + IsLink = Reader.ReadInt16(); + Reserved = Reader.ReadUInt16(); + } + } + + class CWindowsNT1VolB : COSSpecificData + { + UInt32 FileSystemFlags; + UInt32 NTBackupSetAttributes; + + public CWindowsNT1VolB(CBackupStream Reader) + { + FileSystemFlags = Reader.ReadUInt32(); + NTBackupSetAttributes = Reader.ReadUInt32(); + } + } + + class CWindowsNT1DirB : COSSpecificData + { + UInt32 DirectoryAttributes; + UInt16 ShortNameOffset; + UInt16 ShortNameSize; + + public CWindowsNT1DirB(CBackupStream Reader) + { + DirectoryAttributes = Reader.ReadUInt32(); + ShortNameOffset = Reader.ReadUInt16(); + ShortNameSize = Reader.ReadUInt16(); + } + } + + class CWindowsNT1File : COSSpecificData + { + UInt32 FileAttributes; + UInt16 ShortNameOffset; + UInt16 ShortNameSize; + UInt32 NTFileFlags; + + public CWindowsNT1File(CBackupStream Reader) + { + FileAttributes = Reader.ReadUInt32(); + ShortNameOffset = Reader.ReadUInt16(); + ShortNameSize = Reader.ReadUInt16(); + NTFileFlags = Reader.ReadUInt32(); + } + } + + class COS2DirB : COSSpecificData + { + UInt32 DirectoryAttributes; + + public COS2DirB(CBackupStream Reader) + { + DirectoryAttributes = Reader.ReadUInt32(); + } + } + + class COS2File : COSSpecificData + { + UInt32 FileAttributes; + + public COS2File(CBackupStream Reader) + { + FileAttributes = Reader.ReadUInt32(); + } + } + + class CWindows95DirB : COSSpecificData + { + UInt32 DirectoryAttributes; + UInt16 ShortNameOffset; + UInt16 ShortNameSize; + + public CWindows95DirB(CBackupStream Reader) + { + DirectoryAttributes = Reader.ReadUInt32(); + ShortNameOffset = Reader.ReadUInt16(); + ShortNameSize = Reader.ReadUInt16(); + } + } + + class CWindows95File : COSSpecificData + { + UInt32 FileAttributes; + UInt16 ShortNameOffset; + UInt16 ShortNameSize; + + public CWindows95File(CBackupStream Reader) + { + FileAttributes = Reader.ReadUInt32(); + ShortNameOffset = Reader.ReadUInt16(); + ShortNameSize = Reader.ReadUInt16(); + } + } + + class CMacintoshVolB : COSSpecificData + { + UInt32 VolumeParmsAttributes; + UInt16 VolumeAttributes; + UInt16 VolumeSignature; + UInt16 DriveNumber; + UInt16 DriverRefNumber; + UInt16 FileSystemID; + System.DateTime CreatorDate; // MTF_DATE_TIME 5 bytes + System.DateTime ModificationDate; // MTF_DATE_TIME 5 bytes + byte [] VolumeFinderInfo; // 32 bytes + + public CMacintoshVolB(CBackupStream Reader) + { + VolumeParmsAttributes = Reader.ReadUInt32(); + VolumeAttributes = Reader.ReadUInt16(); + VolumeSignature = Reader.ReadUInt16(); + DriveNumber = Reader.ReadUInt16(); + DriverRefNumber = Reader.ReadUInt16(); + FileSystemID = Reader.ReadUInt16(); + CreatorDate = Reader.ReadDate(); + ModificationDate = Reader.ReadDate(); + VolumeFinderInfo = Reader.ReadBytes(32); + } + } + + class CMacintoshDirB : COSSpecificData + { + byte[] FinderInfo; // 16 bytes + byte [] AdditionalFinderInfo; // 16 bytes + UInt32 DirectoryID; + UInt16 DirectoryInfo; + byte DirectoryXInfo; + byte DirectoryAttributes; + + public CMacintoshDirB(CBackupStream Reader) + { + FinderInfo = Reader.ReadBytes(16); + AdditionalFinderInfo = Reader.ReadBytes(16); + DirectoryID = Reader.ReadUInt32(); + DirectoryInfo = Reader.ReadUInt16(); + DirectoryXInfo = Reader.ReadByte(); + DirectoryAttributes = Reader.ReadByte(); + } + } + + class CMacintoshFile : COSSpecificData + { + byte[] FinderInfo; // 16 bytes + byte[] AdditionalFinderInfo; // 16 bytes + UInt32 FileID; + UInt32 FileType; + UInt32 FileCreator; + UInt16 FileInfo; + byte FileXInfo; + byte FileAttributes; + + public CMacintoshFile(CBackupStream Reader) + { + FinderInfo = Reader.ReadBytes(16); + AdditionalFinderInfo = Reader.ReadBytes(16); + FileID = Reader.ReadUInt32(); + FileType = Reader.ReadUInt32(); + FileCreator = Reader.ReadUInt32(); + FileInfo = Reader.ReadUInt16(); + FileXInfo = Reader.ReadByte(); + FileAttributes = Reader.ReadByte(); + } + } +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CSupport.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CSupport.cs" new file mode 100644 index 0000000..f2b5938 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/CSupport.cs" @@ -0,0 +1,58 @@ + +namespace BackupReader +{ + /// + /// Media adresses in the backup file are used as pointers to variable length + /// data, such as strings. + /// + class CMediaAddress + { + public ushort Size; + public ushort Offset; + + public CMediaAddress() + { + Size = 0; + Offset = 0; + } + } + + /// + /// Dates are saved as 40 bit packed values in the backup file. An undefined date + /// is specified by using 0 for all bits. Bits are ordered from most significant to least + /// significant as follows: + /// Bits 0-13: Year, 14-17: Month, 18-22: Day, 23-27: Hour, 28-33: Minute, 34-39: Second + /// + class CMediaDate + { + public byte byte1; + public byte byte2; + public byte byte3; + public byte byte4; + public byte byte5; + + public CMediaDate() + { + byte1 = 0; + byte2 = 0; + byte3 = 0; + byte4 = 0; + byte5 = 0; + } + + public System.DateTime ToDateTime() + { + if ((byte1 == 0) && (byte2 == 0) && (byte3 == 0) && (byte4 == 0) && (byte5 == 0)) + return new System.DateTime(); + + int year = (byte1 << 6) + (byte2 >> 2); + int month = ((byte2 & 0x3) << 2) + (byte3 >> 6); + int day = ((byte3 & 0x3E) >> 1); + int hour = ((byte3 & 0x1) << 4) + (byte4 >> 4); + int minute = ((byte4 & 0xF) << 2) + (byte5 >> 6); + int second = (byte5 & 0x3F); + return new System.DateTime(year, month, day, hour, minute, second); + } + } + +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Program.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Program.cs" new file mode 100644 index 0000000..13306dc --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Program.cs" @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace BackupReader +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new frmMain()); + } + } +} \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/AssemblyInfo.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/AssemblyInfo.cs" new file mode 100644 index 0000000..34eabb8 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/AssemblyInfo.cs" @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BackupReader")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BackupReader")] +[assembly: AssemblyCopyright("Copyright © 2007")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("75741ccb-ae66-42bd-b440-6aca026e075a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Resources.Designer.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Resources.Designer.cs" new file mode 100644 index 0000000..ea37f19 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Resources.Designer.cs" @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:2.0.50727.4959 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace BackupReader.Properties { + using System; + + + /// + /// 强类型资源类,用于查找本地化字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BackupReader.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// 为使用此强类型资源类的所有资源查找 + /// 重写当前线程的 CurrentUICulture 属性。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Resources.resx" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Resources.resx" new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Resources.resx" @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Settings.Designer.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Settings.Designer.cs" new file mode 100644 index 0000000..201fc68 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Settings.Designer.cs" @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:2.0.50727.4959 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace BackupReader.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Settings.settings" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Settings.settings" new file mode 100644 index 0000000..abf36c5 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/Settings.settings" @@ -0,0 +1,7 @@ + + + + + + + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/app.manifest" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/app.manifest" new file mode 100644 index 0000000..ec82d19 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/Properties/app.manifest" @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/backup.ico" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/backup.ico" new file mode 100644 index 0000000..2cd6ca1 Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/backup.ico" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.exe" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.exe" new file mode 100644 index 0000000..d595fec Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.exe" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.pdb" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.pdb" new file mode 100644 index 0000000..61c75a6 Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.pdb" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.vshost.exe" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.vshost.exe" new file mode 100644 index 0000000..69ed6c0 Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.vshost.exe" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.vshost.exe.manifest" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.vshost.exe.manifest" new file mode 100644 index 0000000..f96b1d6 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/bin/Debug/BackupReader.vshost.exe.manifest" @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.Designer.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.Designer.cs" new file mode 100644 index 0000000..9a489d1 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.Designer.cs" @@ -0,0 +1,251 @@ +namespace BackupReader +{ + partial class frmMain + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmMain)); + this.ofdBackup = new System.Windows.Forms.OpenFileDialog(); + this.toolStripContainer1 = new System.Windows.Forms.ToolStripContainer(); + this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.tsStatus = new System.Windows.Forms.ToolStripStatusLabel(); + this.tvDirs = new System.Windows.Forms.TreeView(); + this.ImageList1 = new System.Windows.Forms.ImageList(this.components); + this.toolStrip1 = new System.Windows.Forms.ToolStrip(); + this.openToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.extractToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.cancelToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.fbdBackup = new System.Windows.Forms.FolderBrowserDialog(); + this.toolStripButton1 = new System.Windows.Forms.ToolStripSeparator(); + this.opencatalogToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.savecatalogToolStripButton = new System.Windows.Forms.ToolStripButton(); + this.ofdCatalog = new System.Windows.Forms.OpenFileDialog(); + this.sfdCatalog = new System.Windows.Forms.SaveFileDialog(); + this.toolStripContainer1.BottomToolStripPanel.SuspendLayout(); + this.toolStripContainer1.ContentPanel.SuspendLayout(); + this.toolStripContainer1.TopToolStripPanel.SuspendLayout(); + this.toolStripContainer1.SuspendLayout(); + this.statusStrip1.SuspendLayout(); + this.toolStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // ofdBackup + // + this.ofdBackup.DefaultExt = "*.bkf"; + this.ofdBackup.Filter = "Backup Files (*.bkf)|*.bkf|All Files|*.*"; + // + // toolStripContainer1 + // + // + // toolStripContainer1.BottomToolStripPanel + // + this.toolStripContainer1.BottomToolStripPanel.Controls.Add(this.statusStrip1); + // + // toolStripContainer1.ContentPanel + // + this.toolStripContainer1.ContentPanel.Controls.Add(this.tvDirs); + this.toolStripContainer1.ContentPanel.Size = new System.Drawing.Size(527, 461); + this.toolStripContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.toolStripContainer1.Location = new System.Drawing.Point(0, 0); + this.toolStripContainer1.Name = "toolStripContainer1"; + this.toolStripContainer1.Size = new System.Drawing.Size(527, 508); + this.toolStripContainer1.TabIndex = 0; + this.toolStripContainer1.Text = "toolStripContainer1"; + // + // toolStripContainer1.TopToolStripPanel + // + this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.toolStrip1); + // + // statusStrip1 + // + this.statusStrip1.Dock = System.Windows.Forms.DockStyle.None; + this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsStatus}); + this.statusStrip1.Location = new System.Drawing.Point(0, 0); + this.statusStrip1.Name = "statusStrip1"; + this.statusStrip1.Size = new System.Drawing.Size(527, 22); + this.statusStrip1.TabIndex = 0; + // + // tsStatus + // + this.tsStatus.Name = "tsStatus"; + this.tsStatus.Size = new System.Drawing.Size(38, 17); + this.tsStatus.Text = "Ready"; + // + // tvDirs + // + this.tvDirs.Dock = System.Windows.Forms.DockStyle.Fill; + this.tvDirs.ImageIndex = 0; + this.tvDirs.ImageList = this.ImageList1; + this.tvDirs.Location = new System.Drawing.Point(0, 0); + this.tvDirs.Name = "tvDirs"; + this.tvDirs.SelectedImageIndex = 0; + this.tvDirs.Size = new System.Drawing.Size(527, 461); + this.tvDirs.TabIndex = 1; + this.tvDirs.NodeMouseDoubleClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.tvDirs_NodeMouseDoubleClick); + this.tvDirs.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvDirs_AfterSelect); + // + // mageList1 + // + this.ImageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("ImageList1.ImageStream"))); + //this.ImageList1.ImageSize = new System.Drawing.Size(16, 16); + this.ImageList1.TransparentColor = System.Drawing.Color.Transparent; + this.ImageList1.Images.SetKeyName(0, "Disk"); + this.ImageList1.Images.SetKeyName(1, "Tape"); + this.ImageList1.Images.SetKeyName(2, "Volume"); + this.ImageList1.Images.SetKeyName(3, "Folder"); + this.ImageList1.Images.SetKeyName(4, "File"); + // + // toolStrip1 + // + this.toolStrip1.Dock = System.Windows.Forms.DockStyle.None; + this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; + this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.openToolStripButton, + this.extractToolStripButton, + this.cancelToolStripButton, + this.toolStripButton1, + this.opencatalogToolStripButton, + this.savecatalogToolStripButton}); + this.toolStrip1.Location = new System.Drawing.Point(0, 0); + this.toolStrip1.Name = "toolStrip1"; + this.toolStrip1.Size = new System.Drawing.Size(527, 25); + this.toolStrip1.Stretch = true; + this.toolStrip1.TabIndex = 0; + // + // openToolStripButton + // + this.openToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("openToolStripButton.Image"))); + this.openToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.openToolStripButton.Name = "openToolStripButton"; + this.openToolStripButton.Size = new System.Drawing.Size(120, 22); + this.openToolStripButton.Text = "&Read Backup File..."; + this.openToolStripButton.Click += new System.EventHandler(this.openToolStripButton_Click); + // + // extractToolStripButton + // + this.extractToolStripButton.Enabled = false; + this.extractToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("extractToolStripButton.Image"))); + this.extractToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.extractToolStripButton.Name = "extractToolStripButton"; + this.extractToolStripButton.Size = new System.Drawing.Size(89, 22); + this.extractToolStripButton.Text = "&Extract To..."; + this.extractToolStripButton.Click += new System.EventHandler(this.extractToolStripButton_Click); + // + // cancelToolStripButton + // + this.cancelToolStripButton.Enabled = false; + this.cancelToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("cancelToolStripButton.Image"))); + this.cancelToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.cancelToolStripButton.Name = "cancelToolStripButton"; + this.cancelToolStripButton.Size = new System.Drawing.Size(71, 22); + this.cancelToolStripButton.Text = "&Cancel..."; + this.cancelToolStripButton.Click += new System.EventHandler(this.cancelToolStripButton_Click); + // + // toolStripButton1 + // + this.toolStripButton1.Name = "toolStripButton1"; + this.toolStripButton1.Size = new System.Drawing.Size(6, 25); + // + // opencatalogToolStripButton + // + this.opencatalogToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.opencatalogToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("opencatalogToolStripButton.Image"))); + this.opencatalogToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.opencatalogToolStripButton.Name = "opencatalogToolStripButton"; + this.opencatalogToolStripButton.Size = new System.Drawing.Size(89, 22); + this.opencatalogToolStripButton.Text = "Open Catalog..."; + this.opencatalogToolStripButton.Click += new System.EventHandler(this.opencatalogToolStripButton_Click); + // + // savecatalogToolStripButton + // + this.savecatalogToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.savecatalogToolStripButton.Enabled = false; + this.savecatalogToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("savecatalogToolStripButton.Image"))); + this.savecatalogToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta; + this.savecatalogToolStripButton.Name = "savecatalogToolStripButton"; + this.savecatalogToolStripButton.Size = new System.Drawing.Size(87, 22); + this.savecatalogToolStripButton.Text = "Save Catalog..."; + this.savecatalogToolStripButton.Click += new System.EventHandler(this.savecatalogToolStripButton_Click); + // + // ofdCatalog + // + this.ofdCatalog.DefaultExt = "*.bkf"; + this.ofdCatalog.Filter = "Catalog Files (*.cat)|*.cat|All Files|*.*"; + // + // sfdCatalog + // + this.sfdCatalog.DefaultExt = "cat"; + this.sfdCatalog.Filter = "Catalog Files (*.cat)|*.cat|All Files|*.*"; + // + // frmMain + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(527, 508); + this.Controls.Add(this.toolStripContainer1); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "frmMain"; + this.Text = "Backup Reader"; + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.frmMain_FormClosed); + this.toolStripContainer1.BottomToolStripPanel.ResumeLayout(false); + this.toolStripContainer1.BottomToolStripPanel.PerformLayout(); + this.toolStripContainer1.ContentPanel.ResumeLayout(false); + this.toolStripContainer1.TopToolStripPanel.ResumeLayout(false); + this.toolStripContainer1.TopToolStripPanel.PerformLayout(); + this.toolStripContainer1.ResumeLayout(false); + this.toolStripContainer1.PerformLayout(); + this.statusStrip1.ResumeLayout(false); + this.statusStrip1.PerformLayout(); + this.toolStrip1.ResumeLayout(false); + this.toolStrip1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.OpenFileDialog ofdBackup; + private System.Windows.Forms.ToolStripContainer toolStripContainer1; + private System.Windows.Forms.StatusStrip statusStrip1; + private System.Windows.Forms.ToolStrip toolStrip1; + private System.Windows.Forms.ToolStripButton openToolStripButton; + private System.Windows.Forms.ToolStripStatusLabel tsStatus; + private System.Windows.Forms.ImageList ImageList1; + private System.Windows.Forms.TreeView tvDirs; + private System.Windows.Forms.ToolStripButton extractToolStripButton; + private System.Windows.Forms.FolderBrowserDialog fbdBackup; + private System.Windows.Forms.ToolStripButton cancelToolStripButton; + private System.Windows.Forms.ToolStripSeparator toolStripButton1; + private System.Windows.Forms.ToolStripButton opencatalogToolStripButton; + private System.Windows.Forms.ToolStripButton savecatalogToolStripButton; + private System.Windows.Forms.OpenFileDialog ofdCatalog; + private System.Windows.Forms.SaveFileDialog sfdCatalog; + } +} + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.cs" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.cs" new file mode 100644 index 0000000..9583f09 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.cs" @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; + +namespace BackupReader +{ + public partial class frmMain : Form + { + private string mFileName; + private CBackupReader mFile; + + public frmMain() + { + InitializeComponent(); + } + + private void openToolStripButton_Click(object sender, EventArgs e) + { + if (ofdBackup.ShowDialog() == DialogResult.OK) + { + // Open the backup file + mFileName = ofdBackup.FileName; + tsStatus.Text = "Reading " + mFileName; + + // UI cues + openToolStripButton.Enabled = false; + extractToolStripButton.Enabled = false; + cancelToolStripButton.Enabled = true; + opencatalogToolStripButton.Enabled = false; + savecatalogToolStripButton.Enabled = false; + + // Open and read the catalog + if (mFile != null) mFile.Close(); + mFile = new CBackupReader(mFileName); + mFile.OnProgressChange += new CBackupReader.ProgressChange(mFile_OnProgressChange); + CCatalogNode node = mFile.ReadCatalog(); + + // Populate tree view + tvDirs.Nodes.Clear(); + tvDirs.Nodes.Add("root", node.Name, 0); + tvDirs.Nodes[0].Tag = node; + PopulateTreeView(tvDirs.Nodes[0], node); + tsStatus.Text = "Select a single volume, folder or file to extract."; + + // UI cues + openToolStripButton.Enabled = true; + extractToolStripButton.Enabled = false; + cancelToolStripButton.Enabled = false; + savecatalogToolStripButton.Enabled = true; + opencatalogToolStripButton.Enabled = true; + savecatalogToolStripButton.Enabled = true; + } + } + + private void PopulateTreeView(TreeNode TNode, CCatalogNode CNode) + { + foreach (CCatalogNode node in CNode.Children) + { + TreeNode snode = new TreeNode(node.Name); + if (node.Type == ENodeType.Set) + { + snode.ImageIndex = 1; + snode.SelectedImageIndex = 1; + snode.Tag = node; + TNode.Nodes.Add(snode); + } + else if (node.Type == ENodeType.Volume) + { + snode.ImageIndex = 2; + snode.SelectedImageIndex = 2; + snode.Tag = node; + TNode.Nodes.Add(snode); + } + else if (node.Type == ENodeType.Folder) + { + snode.ImageIndex = 3; + snode.SelectedImageIndex = 3; + snode.Tag = node; + TNode.Nodes.Add(snode); + } + else if (node.Type == ENodeType.File) + { + snode.ImageIndex = 4; + snode.SelectedImageIndex = 4; + snode.Tag = node; + TNode.Nodes.Add(snode); + } + PopulateTreeView(snode, node); + } + } + + private void extractToolStripButton_Click(object sender, EventArgs e) + { + if (tvDirs.SelectedNode == null) return; + + // Get the selected catalog node from tree node tag + CCatalogNode node = (CCatalogNode)tvDirs.SelectedNode.Tag; + if (node == null) return; + if ((node.Type == ENodeType.Root) || (node.Type == ENodeType.Set)) return; + + // Get extraction path + if (fbdBackup.ShowDialog() != DialogResult.OK) return; + string TargetPath = fbdBackup.SelectedPath; + + // Extract the selected node and child nodes + node.ExtractTo(mFile, TargetPath); + } + + private void tvDirs_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) + { + if (e.Button != MouseButtons.Left) return; + + // Get the selected catalog node from tree node tag + CCatalogNode node = (CCatalogNode)tvDirs.SelectedNode.Tag; + if (node == null) return; + if (node.Type != ENodeType.File) return; + + // Extract the selected node to a temporary folder + string TargetPath = System.IO.Path.GetTempPath(); + node.ExtractTo(mFile, TargetPath); + + // Open the file + System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(TargetPath + node.Name); + psi.UseShellExecute = true; + psi.ErrorDialog = true; + psi.ErrorDialogParentHandle = this.Handle; + try + { + System.Diagnostics.Process.Start(psi); + } + catch(Win32Exception ex) + { + MessageBox.Show(this, "Could not open the file '" + node.Name + "'." + ex.ToString(), "Backup Reader", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + } + + private void frmMain_FormClosed(object sender, FormClosedEventArgs e) + { + if (mFile != null) mFile.Close(); + } + + void mFile_OnProgressChange(int Progress) + { + tsStatus.Text = "Reading backup file. %" + Progress.ToString() + " completed."; + System.Windows.Forms.Application.DoEvents(); + } + + private void cancelToolStripButton_Click(object sender, EventArgs e) + { + if (MessageBox.Show(this, "Are you sure you want to cancel?", "Backup Reader", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) + mFile.CancelRead(); + } + + private void tvDirs_AfterSelect(object sender, TreeViewEventArgs e) + { + extractToolStripButton.Enabled = false; + + if (tvDirs.SelectedNode == null) return; + + // Get the selected catalog node from tree node tag + CCatalogNode node = (CCatalogNode)tvDirs.SelectedNode.Tag; + if (node == null) return; + if ((node.Type == ENodeType.Root) || (node.Type == ENodeType.Set)) return; + + extractToolStripButton.Enabled = true; + } + + private void opencatalogToolStripButton_Click(object sender, EventArgs e) + { + if (ofdCatalog.ShowDialog() == DialogResult.Cancel) return; + + // Read the catalog from the file + tsStatus.Text = "Reading catalog..."; + mFileName = CCatalogNode.ReadBackupFilename(ofdCatalog.FileName); + if (mFile != null) mFile.Close(); + mFile = new CBackupReader(mFileName); + CCatalogNode node = CCatalogNode.ReadCatalog(ofdCatalog.FileName); + + // Populate tree view + tvDirs.Nodes.Clear(); + tvDirs.Nodes.Add("root", node.Name, 0); + tvDirs.Nodes[0].Tag = node; + PopulateTreeView(tvDirs.Nodes[0], node); + tsStatus.Text = "Select a single volume, folder or file to extract."; + + // UI cues + extractToolStripButton.Enabled = false; + cancelToolStripButton.Enabled = false; + savecatalogToolStripButton.Enabled = true; + } + + private void savecatalogToolStripButton_Click(object sender, EventArgs e) + { + if (tvDirs.Nodes.Count == 0) return; + if (tvDirs.Nodes[0].Tag == null) return; + if (sfdCatalog.ShowDialog() == DialogResult.Cancel) return; + + // Save the catalog to the file + CCatalogNode.SaveCatalog(sfdCatalog.FileName, (CCatalogNode)tvDirs.Nodes[0].Tag, mFileName); + } + } +} \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.resx" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.resx" new file mode 100644 index 0000000..5384e59 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.resx" @@ -0,0 +1,699 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 120, 17 + + + 329, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACG + EwAAAk1TRnQBSQFMAgEBBQEAAQkBAAEEAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA + AwABIAMAAQEBAAEgBgABIP8AGwABsAGgAZAB/wFoAVABOAH/AWgBUAE4Af8BaAFQATgB/wFoAVABOAH/ + AWgBUAE4Af8BaAFQATgB/wFoAVABOAH/AWgBUAE4Af8BaAFQATgB/wFoAVABOAH/1AABsAGgAZAF/wGw + AaABkAH/AbABoAGQAf8BsAGgAZAB/wGwAaABkAH/AbABoAGQAf8BsAGgAZAB/wGwAaABkAH/AbABoAGQ + Af8BaAFQATgB/9QAAbABoAGQCv8B+AL/A/AB/wHwAegB4AH/AfAB4AHQAf8B4ALQAf8B4AHIAcAB/wGw + AaABkAH/AWgBUAE4Af/UAAGwAaABkA7/AfgB8AH/A/AB/wHwAuAB/wHwAdgB0AH/AeAB0AHAAf8BsAGg + AZAB/wFoAVABOAH/1AABsAGgAZAS/wLwAf8B8AHoAeAB/wHwAuAB/wHgAdgB0AH/AbABoAGQAf8BaAFQ + ATgB/9QAAcABqAGQEv8B+AHwAf8D8AH/AfAB6AHgAf8B8AHYAdAB/wGwAaABkAH/AWgBUAE4Af/UAAHA + AagBoBb/AfgB8AH/AfAB6AHgAf8B8ALgAf8BsAGgAZAB/wFoAVABOAH/1AABwAGwAaAW/wH4Av8D8AH/ + AfAB6AHgAf8BsAGgAZAB/wFoAVABOAH/1AAB0AGwAaAa/wH4AfAB/wPwAf8BsAGgAZAB/wFoAVABOAH/ + 1AAB0AG4AaAd/wGwAaABkAH/AbABoAGQAf8BaAFQATgB/9QAAdABuAGwGf8BsAGgAZAB/wFoAVABOAH/ + AWgBUAE4Af8BaAFQATgB/9QAAdABwAGwGf8BwAGoAZAB/wHQAcgBwAH/AWgBUAE4Af8BTQICAZDUAAHg + AcABsBn/AcABqAGgAf8BaAFQATgB/wFNAgIBkNgAAeABwAGwAf8B4AHAAbAB/wHgAcABsAH/AeABwAGw + Af8B4AHAAbAB/wHQAcABsAH/AdABuAGwAf8B0AGwAaAB/wFNAgIBkP8A5QAD3QH/A64B/wODAf8DawH/ + A2EB/wNrAf8DgwH/A64B/wPdAf/YAAPEAf8DfQH/AXsCdwH/AZQCjAH/AbMCqQH/AZQCjwH/AXUCcwH/ + A1oB/wNZAf8DgQH/A74B/9AAA7gB/wGBAn0B/wHPArQC/wLdAv8C4gL/AugC/wLuAv8C8wH/A88B/wOB + Af8DUwH/A3MB/wO+Af8IAAENAgEBEFgAA+4B/wP0Af8YAAF4AYgBkAH/AXgBgAGBAf8BaAJ4Af8BWAJo + Af8BSAJYAf8BOAFAAUgB/wEoATABOAH/ARgCKAH/AhgBKAH/AhgBKAH/AhgBKAH/AhgBKAH/AhgBKAH/ + AhgBKAH/AhgBKAH/CAADzwH/AYUCgQH/AfUCxwL/AtIC/wLYAv8C3QL/AuIC/wLoAv8C9AX/A/UB/wOd + Af8DUwH/A4EB/wPdAf8BsAGQAYEB/wGBAWgBWAH/AXgBWAFIAf8BeAFgAUgB/wF4AWABSAH/AXgBWAFI + Af8BeAFYAUgB/wFoAVABOAH/AWgBUAE4Af8BaAFQATgB/wFoAVABOAH/AWgBUAE4Af8BaAFQATgB/wFo + AVABOAH/AWgBUAE4Af8YAAPxAf8D2QH/A6sB/wOKAf8DowH/A9AB/wPtAf8QAAF4AYgBkAH/AaAB4AHw + Af8BeAHQAfAB/wFYAbgB4AH/ATgBsAHgAf8BOAGoAeAB/wEoAaAB0AH/ASgBmAHAAf8BKAGQAcAB/wEo + AYEBsAH/ASgBgQGwAf8BGAGBAbAB/wEoAYABoAH/ASgBeAGQAf8BGAIoAf8IAAGIAoMB/wH1As0C/wLP + Av8CzQL/AtIC/wLYAv8C3QL/AuIC/wL6Cf8D9QH/A4EB/wNZAf8DrgH/AbABmAGBAf8D4AH/AbABuAGw + Af8D8AH/AfAB6AHwAf8B0AHYAdAB/wGwAbgBsAH/A7AB/wGwAagBsAH/AbABqAGgAf8BkAGBAXgB/wFo + AVABOAH/AaABmAGQAf8BgQGAAXgB/wFoAVABOAH/EAAD7QH/A88B/wOTAf8DbwH/AY4ChAH/AVoCVQH/ + A1IB/wNlAf8DiQH/A8AB/wPlAf8IAAGBAYgBkAH/AbAB6AHwAf8BkAHoAv8BgQHgAv8BeAHYAv8BeAHQ + AfAB/wFoAcgB8AH/AVgBwAHwAf8BSAG4AfAB/wE4AagB8AH/ATgBqAHgAf8BKAGYAeAB/wEYAZAB0AH/ + ASgBgAGgAf8BKAEwATgB/wQAA7cB/wHGArEC/wLZAv8C1AL/As8C/wLNAv8B1AHbAv8B1wHiAv8B5AHq + Ef8B0gLMAf8DWgH/A4UB/wGwAaABkAH/AfAB+AHwAf8D4AH/A8AB/wHwAfgB8AH/AeAB6AHgAf8D4AH/ + A9AB/wPAAf8BkAGIAYEB/wFoAVABOAH/AbACoAH/AbABqAGgAf8BoAGYAZAB/wFoAVABOAH/CAAD6wH/ + A8AB/wOBAf8DfQH/A40B/wOlAf8BfQJ4Af8BRwJEAf8BUQJMAf8BYgJaAf8BVgJUAf8DXgH/A3oB/wOo + Af8D3AH/AYEBkAGgAf8BsAHoAfAB/wGgAegC/wGQAegC/wGBAeAC/wF4AdgC/wF4AdAB8AH/AWgByAHw + Af8BWAHAAfAB/wFIAbgB8AH/ATgBqAHwAf8BOAGgAeAB/wEoAZgB4AH/ARgBgQGwAf8BOAFAAUgB/wQA + AZcClgL/AuMC/wLeAv8C2QL/AtQC/wHeAecC/wHaAfwC/wHRAfMC/wHiAfwC/wH4Bv8B7wLfAf8B3AK5 + Af8BzAKZAf8BcAJpAf8DcQH/AbABoAGQAv8B+AHwAf8BwAG4AbAB/wHgAegB4AH/AfAB6AHwAf8D8AH/ + A/AB/wPwAf8D8AH/A/AB/wHwAfgB8AH/A/AB/wHgAdgB4AH/AbABqAGgAf8BeAFYAUgB/wQAA/EB/wOJ + Af8DiQH/A8kB/wOjAf8DgQH/A4IB/wOBAf8DVgH/A3kB/wOBAf8BfAJ2Af8BfAJtAf8BXgJZAf8DWgH/ + A4EB/wGBAZABoAH/AbAB8AL/AbAB8AL/AaAB6AL/AZAB4AL/AYEB4AL/AXgB2AL/AXgB0AHwAf8BaAHI + AfAB/wFYAcAB8AH/AUgBsAHwAf8BOAGoAfAB/wEoAaAB4AH/ARgBgQGwAf8CSAFYAf8EAAGcApcC/wLn + Av8C4wL/At4C/wLgAv8B7wH8Af8BswGeAaMB/wNxAf8BrwGoAawB/wH8AfAB+QH/AdYCrAH/AcwCmQH/ + AcwCmQH/AcwCmQH/AYECeQH/A2QB/wHAAagBkAL/AfgC/wOgAf8B4AHYAeAB/wPQAf8D0AH/AdAByAHA + Af8B0ALAAf8B0ALAAf8BwAG4AbAB/wHAAbgBsAH/AtABwAH/AfAB+AHwAf8DsAH/AXgBWAFIAf8EAAOm + Af8DrgH/A5gB/wOCAf8DpAH/A7kB/wPRAf8DjgH/A4EB/wN0Af8DgQH/A2oB/wM8Af8BtwKYAf8BigJ/ + Af8DcwH/AYEBmAGgAf8BwAHwAv8BsALwAf8BoAHwAv8BoAHoAv8BkAHgAv8BgQHgAv8BeAHYAv8BaAHQ + AfAB/wFoAcgB8AH/AVgBuAHwAf8BSAGwAfAB/wE4AagB4AH/ARgBiAHAAf8CWAFoAf8EAAGeAowB/wHp + Ar4B/wHvAsIB/wHzAsUB/wH5AtYC/wHpAfMB/wGCAn8B/wNTAf8DcAH/AfkB6AHsAf8B7ALKAf8B5gK6 + Af8B5gK9Af8B5gLAAf8BpgKUAf8DcQH/AcABsAGgAv8B+AL/A5AB/wHgAegB4AH/AWgBYAFYAf8DoAH/ + AdAByAHAAf8B0AHIAcAB/wHQAsAB/wFoAWABWAH/AZABmAGQAf8BwAG4AbAB/wHwAfgB8AH/AcABuAGw + Af8BeAFYAUgB/wQAA6AB/wOcAf8DtQH/A8cB/wPhAf8D5gH/A9AB/wPYAf8D0QH/A8MB/wGaApkB/wGB + AoAB/wFOAksB/wGTAoEB/wGEAn4B/wOBAf8BgQGYAaAB/wHAAfAC/wGwAfAC/wGwAfAC/wGgAegC/wGQ + AegC/wGQAeAC/wGBAeAC/wF4AdgC/wFoAdAB8AH/AWgByAHwAf8BWAG4AfAB/wE4AagB4AH/ARgBkAHA + Af8BWAFoAXgB/wQAAZwCjwH/AdsCqAH/AeMCsAH/AeoCtwH/AfMCzQL/Ae4B/AH/AcUBvAHCAf8BdwJz + Af8BswGgAaMC/wH0AfwC/wLYAv8C2AL/At0C/wLiAf8BiQKDAf8DhQH/AdABsAGgAv8B+AL/A5AB/wPw + Af8BSAFAAUgB/wNoAf8D0AH/AdAByAHAAf8B0AHIAcAB/wFIAUABSAH/A3gB/wHAAbgBsAH/AfAB+AHw + Af8DwAH/AXgBWAFIAf8EAAO6Af8D0wH/A94B/wPHAf8D4QH/A98B/wPyAf8D/AH/A+wB/wPfAf8D9wH/ + Ae4C6gH/Ad4CzAH/AY8CigH/AY0CiQH/A+cB/wGQAqAB/wHAAfAC/wGwAfAC/wGwAfAC/wGwAvAB/wGg + AfAC/wGQAegC/wGQAeAC/wGBAeAC/wF4AdAC/wFoAdAB8AH/AVgBwAHwAf8BWAG4AfAB/wEoAZgB0AH/ + AWgBeAGBAf8EAAGaApYB/wHVAqIB/wHnAsQB/wH3AucG/wH2A/8B3gP/AdIB9QL/AeIB/AL/AeAB5wL/ + As0C/wLSAv8C2AL/At0B/wFyAnAB/wOuAf8B0AGwAaAF/wKgAZAB/wHwAfgB8AH/A/AB/wPgAf8B4AHY + AdAB/wPQAf8B0AHIAcAB/wHQAsAB/wHQAcABsAH/AcABuAGwAf8D4AH/A8AB/wF4AVgBSAH/BAAD4wH/ + A+AB/wO7Af8DgQH/A3QB/wO4Af8D8gH/A/wB/wPsAf8D3wH/A/cB/wPKAf8DwQH/A8sB/wgAAZABoAGw + Af8BwAHwAv8BwAHwAv8BwAHwAv8BwAHwAv8BsAHwAv8BsAHwAv8BoAHoAv8BkAHoAv8BkAHgAv8BgQHY + Av8BeAHQAv8BeAHIAfAB/wFoAcAB8AH/AWgBeAGBAf8EAAPLAf8BwwK/Ev8B7AHxAv8B2wHnAv8B2QHe + Av8C1AL/As8C/wLNAv8C0gH/AcYCrQH/A38B/wPeAf8B0AG4AaAC/wH4Av8B4AHYAdAB/wGgAZgBkAH/ + AZABiAGBAf8BkAKBAf8BkAKBAf8BkAKBAf8BkAGIAYEB/wGQAYgBgQH/A5AB/wGgApAB/wGwAbgBsAH/ + AeAB2AHgAf8BeAFgAUgB/xAAA+wB/wPbAf8DyAH/A9UB/wPmAf8DzQH/A9QB/wPUAf8UAAGQAaABsAH/ + AZABoAGwAf8BkAGgAbAB/wGQAaABsAH/AZABoAGwAf8BkAGgAbAB/wGQAqAB/wGQAZgBoAH/AYEBmAGg + Af8BgQGYAaAB/wGBAZgBoAH/AYEBmAGgAf8BgQGYAaAB/wGBAZgBoAH/ATsCAQFgCAADlAH/A/UK/wL+ + Av8C5wL/AuMC/wLeAv8C2QL/AtQC/wLPAf8B9QLHAf8BgQJ+Af8DxgH/BAAB0AG4AaAB/wPgCv8B+AP/ + AfgD/wH4A/8B+AHwAf8B8AH4AfAB/wPwAf8D8AH/A/AB/wHwAegB4AH/AfAB6AHgAf8BoAGIAYEB/0QA + AZABqAGwAf8BsAHoAfAB/wGwAfAC/wGwAfAC/wGwAvAB/wGQAeAB8AH/AZABoAGwAf8BSAICAYAkAAPm + Af8DjAH/A/UG/wL4Av8C7AL/AucC/wLjAv8C3gL/AtkB/wH1As0B/wGFAoEB/wO6Af8IAAEiAgEBMAHQ + AbgBoAH/AdABuAGgAf8B0AG4AaAB/wHQAbABoAH/AcABsAGgAf8BwAGwAaAB/wHAAbABoAH/AcABsAGg + Af8BwAGwAaAB/wHAAagBoAH/AcABqAGgAf8BwAGoAaAB/wHAAbABoAH/AUgCAgGARAABIgIBATABkAGo + AbAB/wGQAagBsAH/AZABqAGwAf8BkAGoAbAB/wGQAagBsAH/ASsCAQFALAAD5gH/A5QB/wPGAv8C9wL/ + AvIC/wLsAv8C5wL/AuMB/wHGArEB/wGOAokB/wPRAf9AAAENAgEBEAEZAgEBIJQAA8sB/wOkAf8BpgKj + Af8BjAKJAf8BowKfAf8BoAKeAf8DwQH/1AABQgFNAT4HAAE+AwABKAMAAUADAAEgAwABAQEAAQEGAAEB + FgAD/wEAAv8GAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHA + AQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQ8GAAHAAR8GAAL/BgAB8AEHBv8B4AEDBv8BwAEBAb8C/wE/ + AQABAQGAAgABAQH4AQ8BAAEBAYACAAEBAeABAwEAAQEDAAEBAYACAAEBAwABAQMAAQEDAAEBAwABAQMA + AQEDAAEBAwABAQMAAQEDAAEBAQABAwEAAQEDAAEBAeABHwEAAQEBgAEBAQABAQL/AQAB/wGAAQMBAAEB + Av8BAQH/AcABBwH/AfkE/wHwAR8G/ws= + + + + 230, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAlpJREFUOE+tk21I + k1EYhif0oyA0sqIQCix/+GcQFFH9CCmiUBTLLEjShJofVBgL2fxoU9Pp5ubUlS5rU9f8rCyjsA+pUCRC + TR1ppmVFUSlmhq78unrnQF1KGHTg/nEOz30993PO+7qJFrmUeiv2n+Mij+XLRLLYULdF2pxlEVIDcw0p + AsyxD5fmI/rQ94pqi26eOlsfuZj+7BgSm01QdA4ih7m73Yx9qGpavwatjPebqCzOprPt8YKQgzFagqL0 + BEjyEFWVaBkdLHMxT34uYNwWR9nVTEoL0zHlp2DMSeaSRk6eKt4VWm5WM/rVPNN5SjDTLQebZEHNA1wr + UvHjk3E6tsNcV62e1r3KLGqtKm6WplNpSsVqVFJsOM8VfSKFWjkGtcyZptSYzvC7XByx3zQoqCnTMvlG + CX1prnornPUmQJcUXsbSVhGK5bIOkcmQyveeTHiv4VZ5Nk33Nc6iuSO8CIfmECYa/bE/8ON1iRipJNh5 + F0V6Bd86lfQ1JlFj1TDVq4COKCegLVIwHmGiKRB7/V6G7+5koHozymgfYRy5E1CgTWKgXcZ1i5qWp0KS + rjgBcAJawph6FszYk/2M1O1isGYLX8p9ab6wgqP+3rMvYciS01GfzA1LFvQkQ6sQ9/khxhoCGHnox1Dt + NvorxXw0b8Km8UQh2cip6GOzgNyMeKqKM7HdjqFZJ5pRk2YJ9aql3EnxoCJxNaZ4Ly6e3UDY3O6OEXRp + 59ApTpIhiyDh9GHORAZyPHQPB/ZtZ/cOMVvFPvh6e7F+3SrWrHRnraf7Xz/xf/rJ/kvxb84I3U1y+9/W + AAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAKZJREFUOE+1kt0N + gCAMhJnWUdiBbRiAZXjjrXqVQ1HwX5PmInJfr6bGfPU4Z2Rbl9kwxhhFxIm1VhWF81MILoUwaMHsvYep + nLWSVWC8iAQtAFJKCuDZXp3CcUfTLYA5PhNwjJ2GAwDN0Mo4mfQ9azcBYiHFPEI2UVeQLgCdOca6Izs/ + S7CJD8i7BF3A9KH6B43OOkILwEW6o2UP8qpyMe7q6ab/f2EES5nilRcvs5AAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAWVJREFUOE+tkkFq + w0AMRQ1dtLuZXUPBnik0xBtbs2so2JpdnEKYHCFHyBFyBB8hyy59hBzB2ywKPkJuMJXcJjROWrvQAWFj + 6T99a3QT0HGvzkweJ2/xU9zs3/cNf/vTwRmurLU+y7KDm7vlb2LnnMYC12c1iLjK87wyU+sZ5GZu04Uo + AEl1dlEsDvTcdgGakwxhQAtxrqKQXAiIMi9cm4MplECwH10SaP0NssUXNDjFshUDlOykdz4EMeSm5l9K + kuTAEcVpNUh8pFM3a4zx9hPSKDWg80n8DDqM05oBbTBknAy7XurcinVsfEq2xYMyURRVDFIxnF9fdxgw + IXEY1lprn6bpTiklhRBt0HvJs1BUc3WIACip0zYchTy0hpyYY+ERQtCaQVcBlFjKkfZsnbpsWHSxTOxw + HDaUu3Qh7sVGSunJxU7cXin4ogHtBAFO7k5NyIEJ7gK2ZwMR9C1LX7531/6v4AMif3KiZiJqOQAAAABJ + RU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAgxJREFUOE+lkvtL + U2EYx+0PEbtpFwnBKPGKiJImGP0gYhIYs1E5GF5gIxkpA00JRSmMEF0ohMh+GaRWYlqabMVcNdS2QpaI + VqiDIYhk397vA6fXhCjyhYdzeM/5fp7vczkAdeL2cwho7v/wWzT1zcN+Pwhr51uY2/y41PQaF+wzKKiZ + QvaN58g0jyLd5KEUcQbg+84P/Cm2tncQjW3j68YWIqubCC3FcOJc478BAuGoZM6zvoRnakXEruEIjhc4 + /g5gZop9c+voGAyLbQIfeBZxLL9BA1jzXvuGbWamuKh+GmmVbswE19A59FEBbmoAG7YbsLtm2mZmiml9 + cvabNDwpz6YB7LYBoMXCumkJr7LOmnnHzBQ/9X2Bo2cOibm1GsBREbAQiYmw/8lnuCeWkVzcgnZlnw1j + 3HV/wuNXK6i/9x5Hc6wawDlTXHbLJ+LZUBQPRyKwdQdxutwl1h+NLXHh5Ht1ewBHsiwawCW57HyDAfWR + dvl0uhZQ1eqX8aVc7EKLqrum651ATLf9OJx5XQM4KmY0xPzZ0hFAiQJnXB0WwME0E3IsL5B17ZlADqWb + NYDrOepdlcysmTWWOrxqbceRWtaLk0VO1XW72D5Vckd2gMBfq8zdpmUG62NJvKM4+XyziDk24xmfWoGE + s1c0gHPmbrPTpHNJKOCo2G1mZs20zcwUJ5yp1AB5+8/zEwgF5GMVDxh4AAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAgxJREFUOE+lkvtL + U2EYx+0PEbtpFwnBKPGKiJImGP0gYhIYs1E5GF5gIxkpA00JRSmMEF0ohMh+GaRWYlqabMVcNdS2QpaI + VqiDIYhk397vA6fXhCjyhYdzeM/5fp7vczkAdeL2cwho7v/wWzT1zcN+Pwhr51uY2/y41PQaF+wzKKiZ + QvaN58g0jyLd5KEUcQbg+84P/Cm2tncQjW3j68YWIqubCC3FcOJc478BAuGoZM6zvoRnakXEruEIjhc4 + /g5gZop9c+voGAyLbQIfeBZxLL9BA1jzXvuGbWamuKh+GmmVbswE19A59FEBbmoAG7YbsLtm2mZmiml9 + cvabNDwpz6YB7LYBoMXCumkJr7LOmnnHzBQ/9X2Bo2cOibm1GsBREbAQiYmw/8lnuCeWkVzcgnZlnw1j + 3HV/wuNXK6i/9x5Hc6wawDlTXHbLJ+LZUBQPRyKwdQdxutwl1h+NLXHh5Ht1ewBHsiwawCW57HyDAfWR + dvl0uhZQ1eqX8aVc7EKLqrum651ATLf9OJx5XQM4KmY0xPzZ0hFAiQJnXB0WwME0E3IsL5B17ZlADqWb + NYDrOepdlcysmTWWOrxqbceRWtaLk0VO1XW72D5Vckd2gMBfq8zdpmUG62NJvKM4+XyziDk24xmfWoGE + s1c0gHPmbrPTpHNJKOCo2G1mZs20zcwUJ5yp1AB5+8/zEwgF5GMVDxh4AAAAAElFTkSuQmCC + + + + 434, 17 + + + 537, 17 + + + 642, 17 + + + + AAABAAgAICAQAAEABADoAgAAhgAAABAQEAABAAQAKAEAAG4DAAAwMAAAAQAIAKgOAACWBAAAICAAAAEA + CACoCAAAPhMAABAQAAABAAgAaAUAAOYbAAAwMAAAAQAgAKglAABOIQAAICAAAAEAIACoEAAA9kYAABAQ + AAABACAAaAQAAJ5XAAAoAAAAIAAAAEAAAAABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////ABER + EREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREQAAAAAAAAAAAA + AAERERERf4iIiIiIiIiIiIgBEREREX+AiIiIiIiIiIgIARERERF/iIiIiIiIiIiIiAERERERf4iIiIiI + iIiIiIgBEREREX+IAAAAAAAAAACIARERERF/iA/4cACIh/9wiAERERERf4gHD4cACHcPcIgBEREREX+I + APD3AAhw8HCIARERERF/iAAH9wAIgAdwiAERERERf4gAAAAAAAAAAIhBEREREX+AiIiIiIiIiIiIARAA + AAB/iIiIiIiIiIiIiEEXd3d3f/////////////gBH4iIiHd3d3d3d3d3d3d3ER+KqoiIiIiIiIiIiId3 + AREfgiKIiIiIiIiIiIiHdwERH4//////////////h3cBER+AAAAAAAAAAAAAQId3AREfj/////////// + //+HdwERH4AAAAAAAAAAAAAAh3cBER+IiIiIiIiIiIiIiId3AREf///////////////3dwERF/iIiIiI + iIiIiIiIj3cBERF/iIiIiIiIiIiIiIj3ARERF3d3d3d3d3d3d3d3dxERERERERERERERERERERERERER + ERERERERERERERERERERERERERERERERERERERER/////////////////4AAAf8AAAH/AAAB/wAAAf8A + AAH/AAAB/wAAAf8AAAH/AAAB/wAAAf8AAAH/AAABgAAAAYAAAAGAAAADgAAAB4AAAAeAAAAHgAAAB4AA + AAeAAAAHgAAAB4AAAAeAAAAHwAAAB+AAAA////////////////8oAAAAEAAAACAAAAABAAQAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAICAAIAAAACAAIAAgIAAAMDAwACAgIAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////ABEREAAAAAABERGPd3d3d3AREY9wAAAAcBERj3B4eHBwERGPcAAA + AHARAI93d3d3cBiIj//////wh3eIiIiIgBGHp3d3d3eAEYeIiIiIiIARh3d3d3d3gBGP//////+AERh3 + d3d3d/AREYiIiIiIgRERERERERERERERERERERER+AEREfAAAADwAAAA8ACIAfAAERHAAA/4gACIhwAD + iAEAAxERAAMHDwADCHcAA4gBgAMREcAHAPD//whw//+IASgAAAAwAAAAYAAAAAEACAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AJYrKwCgNzcAoz09AKhHRwC7U1MAslFRAN1qagDMY2MAlklJAN9t + bQDOZWUA1WlpAJRJSQDMZmYAyWVlAMFhYQCpVVUAikZGAMVlZQC0XV0AqlpaAKZZWQCkWFgAm1VVAJ1d + XQCBTU0Af0xMAK9rawCIU1MA/pycAM6AgADFfX0A/6OjAMJ8fABxSEgAtnV1AMV/fwC8fHwAl2RkAM6J + iQD/rKwAsHh4AKNvbwDCiIgA/7a2AK58fACMZGQAWUBAAJdubgDJlZUAoXd3AFxERABVPz8A/7+/AMyZ + mQC2iYkAxpaWAL+SkgCshYUA3qysALqQkADMn58Aw5iYALaOjgD9x8cAz6OjAKF/fwCXd3cA5ra2AK+L + iwCmhoYA7sHBANmxsQDSrKwAw6CgALmZmQDMqakAln19AIRvbwCtkpIAm4ODAPfS0gDFp6cAqI+PAL6i + ogCymJgA2Lm5ANO2tgCPe3sAj3x8AIx5eQDmyMgAempqAN7ExADXvr4A7dTUAM65uQBpXl4A69TUAOfS + 0gDXxMQAxLOzAPTi4gCmm5sA2c7OAHt1dQBwa2sA9ezsAMvGxgCgnJwAUlBQAGFfXwDi3t4APz4+AIOB + gQBBQEAAcG9vAGxrawD6+fkA9vX1ANLR0QDFxMQAraysAKWkpACdnJwAjo2NAJ+MiwCkjIoAzbSyAH5x + cACPgoEAp5WTANS/vQCvnpwAeG9uAE46NwC9trUAuK2rAEtBPwCFdnMAh3l2AElEQwCflZMAemRfAIRz + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHx8fHx8NQ8PFwAAAAAA////////AAD///////8AAP// + +H///wAA///gH///AAD//4AD//8AAP/+AAD//wAA//gAAB//AAD/4AAAB/8AAP+AAAAA/wAA/gAAAAA/ + AAD4AAAAAA8AAOAAAAAAAwAAgAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAA + AAAAAwAAAAAAAAADAAAAAAAAAAcAAAAAAAAAAwAAAAAAAAADAACAAAAAAAMAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAMAAAAAABwAAwAAAAAAPAADAAAAAAB8AAMAAAAAADwAAwAAAAAAPAADAAAAAAA8AAMAA + AAAADwAAwAAAAAAPAADAAAAAAA8AAMAAAAAADwAAwAAAAAAPAADAAAAAAA8AAMAAAAAADwAA+AAAAAAP + AAD/gAAAAA8AAP/+AAAADwAA///wAAAPAAD///wAAA8AAP///AAADwAA///8AAAPAAD////wAA8AAP// + ///AHwAAKAAAACAAAABAAAAAAQAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AlisrAKA3 + NwCjPT0AqEdHALtTUwCyUVEA3WpqAMxjYwCWSUkA321tAM5lZQDVaWkAlElJAMxmZgDJZWUAwWFhAKlV + VQCKRkYAxWVlALRdXQCqWloApllZAKRYWACbVVUAnV1dAIFNTQB/TEwAr2trAIhTUwD+nJwAzoCAAMV9 + fQD/o6MAwnx8AHFISAC2dXUAxX9/ALx8fACXZGQAzomJAP+srACweHgAo29vAMKIiAD/trYArnx8AIxk + ZABZQEAAl25uAMmVlQChd3cAXEREAFU/PwD/v78AzJmZALaJiQDGlpYAv5KSAKyFhQDerKwAupCQAMyf + nwDDmJgAto6OAP3HxwDPo6MAoX9/AJd3dwDmtrYAr4uLAKaGhgDuwcEA2bGxANKsrADDoKAAuZmZAMyp + qQCWfX0AhG9vAK2SkgCbg4MA99LSAMWnpwCoj48AvqKiALKYmADYubkA07a2AI97ewCPfHwAjHl5AObI + yAB6amoA3sTEANe+vgDt1NQAzrm5AGleXgDr1NQA59LSANfExADEs7MA9OLiAKabmwDZzs4Ae3V1AHBr + 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 + PQCoR0cAu1NTALJRUQDdamoAzGNjAJZJSQDfbW0AzmVlANVpaQCUSUkAzGZmAMllZQDBYWEAqVVVAIpG + RgDFZWUAtF1dAKpaWgCmWVkApFhYAJtVVQCdXV0AgU1NAH9MTACva2sAiFNTAP6cnADOgIAAxX19AP+j + owDCfHwAcUhIALZ1dQDFf38AvHx8AJdkZADOiYkA/6ysALB4eACjb28AwoiIAP+2tgCufHwAjGRkAFlA + QACXbm4AyZWVAKF3dwBcREQAVT8/AP+/vwDMmZkAtomJAMaWlgC/kpIArIWFAN6srAC6kJAAzJ+fAMOY + mAC2jo4A/cfHAM+jowChf38Al3d3AOa2tgCvi4sApoaGAO7BwQDZsbEA0qysAMOgoAC5mZkAzKmpAJZ9 + fQCEb28ArZKSAJuDgwD30tIAxaenAKiPjwC+oqIAspiYANi5uQDTtrYAj3t7AI98fACMeXkA5sjIAHpq + agDexMQA176+AO3U1ADOubkAaV5eAOvU1ADn0tIA18TEAMSzswD04uIAppubANnOzgB7dXUAcGtrAPXs + 7ADLxsYAoJycAFJQUABhX18A4t7eAD8+PgCDgYEAQUBAAHBvbwBsa2sA+vn5APb19QDS0dEAxcTEAK2s + rAClpKQAnZycAI6NjQCfjIsApIyKAM20sgB+cXAAj4KBAKeVkwDUv70Ar56cAHhvbgBOOjcAvba1ALit + qwBLQT8AhXZzAId5dgBJREMAn5WTAHpkXwCEc28AloaCALinowDDv74AcWNfAKihnwBpVU8Al42KAK6M + gABhWFUAWFVUAOfk4wAtKCYAraimAEs5MQBXRT0ARDYwAGxbUwB4aWIAYE5FADcvKwCNi4oA6efmAFNA + NgBUTEcAlJOSAMvKyQDk5OMA3t7dALi4twAAAAAA3+fnADs9PQB6fX0AxcnJAFxeXgBUVVUAQkNDAHt8 + fADr7OwA6uvrAOjp6QCYmZkAJCwuACsvMAAiJSYAMD5EACozNwBlcHUAOkRJAExOTwAvOD0AUVhcAERF + RgCRlpwA1d7/AAAz/wAHOP8AFUT/ACNP/wAsV/8ATXH/AFx8/wB/mf8Am6//ALHA/wBpgPIAxM79AM3V + +wDm6v0A2t/8APX2/AB6ensA/f39APPz8wDt7e0A5+fnAODg4ADb29sA2NjYANXV1QDT09MAz8/PAMzM + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFVAAADSSQkB0QiIg8+IxodOyIZNDYjGVE3IRZ0NCIYny4p + JtwvODz8XV1d/7i4uP/X19f/V1dX/4qKiv/Nzc3/ysrK/7Kysv9nZ2f/r5aW/zMzM/8zMzP/MzMz/zMz + M/9KRET/pYuL/8Wiov9vYGD/JCos/iQgHvcwHBLeMhwQwTMcEaQzHRKCNB4TXjMfFEEyHxMpNyEWFysr + FQwzMwAFAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAABQEBABDk5HAk2KCgTOiQdIzgnGjs5IhpaNiAWfzMn + H7UvLy3oO0VJ/3V1df/Nzc3/1tbW/9PT0//Pz8//nZ2d/7CwsP+Xl5f/Tk5O/3Fxcf9sbGz/n4yM/zMz + M/8zMzP/MzMz/zMzM/8zMzP/MzMz/zc2Nv9tX1//zaio/66Rkf8+Q0X/ISEh+ykeGe0zGw/NMh0QsjMc + EZIzHhJvNB0TTzIeFDM1IxIdMCAQEEAgIAhVAAADAAAAAUBAQARGLi4LNywhFz4sHyk4Jh5EOCQaZDch + Foo1KyjHMzU18lJdYf+QkJD/1dXV/9bW1v/S0tL/zs7O/8vLy/+1tbX/a2tr/1xcXP+fn5//ubm5/7a2 + tv90dHT/koOD/zMzM/81NTX/OTk5/zs7O/88PDz/PT09/z4+Pv8/Pz//QEBA/1lTU/+zlpb/2bGx/11e + YP8kKiz+JSAd9TAcEt0zHBC/MhwRojIeEn81HxRbMSEVPjIcFSQwICAQMzMzBTwtHhE7Kh4rOiUbSzoj + Gm43JRubODEu1kBESPlnZ2f/qqqq/9jY2P/V1dX/0dHR/87Ozv/Hx8f/jo6O/19fX/+Ghob/sbGx/7i4 + uP+1tbX/sbGx/62trf9xcXH/gnh4/zMzM/9DQ0P/V1dX/2dnZ/9sbGz/cnJy/3d3d/98fHz/goKC/4eH + h/+NjY3/jo2N/6GTk//YsbH/yaWl/01OUf8hISH7Kx4X6jQcEMs0HRGvMx0SjDMeE18xIRYvMyIiDz0k + GCo4JBpkOSkhpjs4OOJNU1n9fn5+/8fHx//Y2Nj/1NTU/9HR0f/Nzc3/sbGx/3d3d/9vb2//ra2t/7u7 + u/+4uLj/tLS0/7CwsP+srKz/qamp/6ampv9ubm7/dXBw/zMzM/9DQ0P/V1dX/2dnZ/9sbGz/cnJy/3d3 + d/98fHz/goKC/4eHh/+NjY3/kZGR/5aWlv+elpb/oYqK/72bm//tv7//eHV3/zEzNP0nHxvzLxwT1DId + EJ0yIBVXMSEZHz86OnFAQ0fdZXB1/5iYmP/W1tb/1tbW/9PT0//Q0ND/ycnJ/5OTk/9vb2//mZmZ/7q6 + uv+6urr/t7e3/7Ozs/+vr6//q6ur/6ioqP+lpaX/oqKi/56env9zc3P/UlJS/0FAQP86Ojr/UFBQ/2dn + Z/9sbGz/cnJy/3d3d/98fHz/goKC/4eHh/+NjY3/kZGR/5aWlv+hmZn/spmZ/8OZmf/Lp6f/8cPD//fH + x/+5mJj/OD0//y0eFcwzHxRzLyQYK2VlZf+rq6v/pqam/9bW1v/T09P/z8/P/7Kysv94eHj/hISE/7a2 + tv+9vb3/ubm5/7a2tv+ysrL/rq6u/6urq/+np6f/pKSk/6Ghof+qqqr/2dnZ/+np6f/X19f/vb29/319 + ff9MS0v/SUhI/0VERP9ISEj/ZGRk/3d3d/98fHz/goKC/4eHh/+NjY3/kZGR/5aWlv+hmZn/spmZ/8OZ + mf++np7/47m5/+q+vv/xw8P/lHx8/ywdF9ozHxSCMiMZM3BwcP/Hx8f/dHR0/8XFxf+Tk5P/hoaG/6ur + q/+/v7//vLy8/7i4uP+0tLT/sbGx/62trf+qqqr/p6en/6Ojo/+np6f/ysrK/+fn5//g4OD/2NjY/7u7 + u/94eHj/zs7O/9PT0//S0tL/vLy8/3t7e/9OTU3/UU5O/0tJSf9NTU3/c3Nz/4eHh/+NjY3/kZGR/5aW + lv+hmZn/spmZ/8OZmf+0l5f/1K+v/9u0tP/kurr/jnh4/ywdFuAyHhSPNB4WO3BwcP+2trb/hoaG/5eX + l/+/v7//vr6+/7u7u/+4uLj/tLS0/7CwsP+srKz/qamp/6ampv+kpKT/vLy8/9/f3//n5+f/3t7e/93d + 3f/c3Nz/2tra/9ra2v/X19f/1tbW/9TU1P/T09P/0tLS/9HR0f/S0tL/srKy/2dnZ/9IRkb/VFBQ/1JP + T/9dXV3/hISE/5aWlv+hmZn/spmZ/8OZmf+mjo7/x6am/86rq//Ur6//hnNz/yYgHO0yHxWVNSAYP2pq + av+3t7f/wcHB/76+vv+6urr/t7e3/7Ozs/+vr6//rKys/6mpqf+lpaX/s7Oz/9TU1P/p6en/5eXl/+Pj + 4//i4uL/4eHh/+Dg4P/e3t7/3t7e/9zc3P/W1tX/0c/P/87My//Mysn/zczL/9DPz//S0dH/0tLS/9HR + 0f/Q0ND/rKys/2loaP9ST0//Yltb/1lUVP9nYmL/oYqK/8OZmf+bhob/tpub/8mysv/OsbH/gG9v/yYg + HO0yHxWVNSAYP29vb/+9vb3/ubm5/7a2tv+ysrL/rq6u/6urq/+np6f/rq6u/8rKyv/m5ub/6+vr/+rq + 6v/o6Oj/5ubm/+bm5v/h4eH/0dHR/7+/v/+9vb3/u7u7/7Kysv+tq6n/nJeU/5GKhv+Oh4T/k42K/5uY + lv+gnp3/o6Kh/6WkpP+amZn/raen/7+3t//Kw8P/q6mp/2loaP9RTU3/Z19f/2BXV/+QgID/p5GR/9fL + y///////xL29/yYgHeozHxSNNR8WOm9vb/+1tbX/sbGx/62trf+rq6v/qKio/8HBwf/i4uL/6+vr/+vr + 6//u7u7/7e3t/+zs7P/l5eX/1NTU/8XFxf/Dw8P/1NTU/+Pj4//n5+f/9fX1//r6+v+HenL/a1pR/31s + ZP91Zl3/g3Vu/5qPi/+poZ7/uLOx/87Kyf/f3dz/4+Hg/+Lc3P/Yzc3/0ri4/9q7u//TxcX/mpqa/1xb + W/9fV1f/inx8/6CNjf/6+Pj/1N3//6ako/EzIBV3MSEWL29vb/+tra3/qqqq/7q6uv/c3Nz/5eXl/8LC + wv+ampr/lJSU/5ycnP+rq6v/uLi4/8DAwP/ExMT/y8vL/9bW1v/j4+P/6Ojo/+zs7P/x8fH/9fX1//r6 + +v+KYWH/Tz8+/0BDQ/9dYF//UDsw/19NRP9sXFT/f3Jt/5ySjv+1ran/ysXC/+Hd3P/p5+b/5uXk/+La + 2v/oxMT/5cjI/83Nzf+3t7f/lpaW/1lXV//Y1tb/MVr///f5//+akY1+MSEZH3V1df/T09P/8vLy/3R0 + dP9DQ0P/Nzc3/zc3N/9BQUH/TExM/1JSUv9jY2P/f39//4yMjP+bm5v/p6en/7Ozs/+/v7//zMzM/93d + 3f/u7u7/9fX1//r6+v/8mZn/olhY/8JkZP+cWVn/XElJ/1hRT/9GQkD/Tzov/2FPRf9zY1r/in12/6mg + m/+8tLH/xsC+/+3s6//x7u7/4r69/8fGxf+YmJj/pqWl/3d3d//P0dH/I0///5ur9//u7eytOycUDWdu + driam5v+WVlZ/5OTk//ZsbH/vXt7/8J8fP+wcnL/p52d/6iFhf9/Zmb/SEFB/zo6Ov9VVVX/cHBw/4OD + g/+Ojo7/n5+f/6urq/+2trb/x8fH/97e3v//nJz/pllZ/81mZv/JZWX/ekpK/9a/v//g4eH/qKio/4CA + gP9RSkb/Uz4z/2dVS/93Z1//xb67//P0/v/o4+L/gXl2/15YVv9HS03/KiwtzyQiIZL7+/v0ADP//ypV + ///+/v7gVQAAAzMiIg8kJCFpRERE///////GgID/a2dn/5Vxcf/FfX3/69PT/8Ohof/MmZn/nXl5/5l4 + eP+riIj/Z1lZ/0E+Pv9GRkb/YGBg/3p6ev+Hh4f/l5eX/6Ojo///np7/pllZ/8xmZv/MZmb/gU1N/97D + w//49fX/+f///9/n5//Jy8v/tLS0/4+Pj/+XkY7/8/T+/01x///Oysj9Lh4W5DAeFsE2HhN9OiEWVtLN + y5Hm6v3/ADP//wc4////////AAAAAC4XFwsjIiFjVVVV///////FfX3/vXt7/8J8fP/FfX3/4r6+/9TD + w//MmZn/toqK/7aKiv+/kZH/c2Fh/8+np//Eo6P/jHx8/1ZQUP8+PT3/UFBQ/2tra///oKD/pllZ/8xm + Zv/MZmb/gE1N/8etrf/Wv7//1IWF/+exsf/25+f/8PDw/9jY2P/6+vr/XHz//ypV///q6Oj/dWhj9W9d + Vd13Zl3Gn5KMu/r5+fZpgPL/ADP//wAz////////AAAAADMaGgojIiFiVVVV///////FfX3/xX19/8V9 + ff/FfX3/0ZmZ/+Lb2/+9kZH/zJmZ/8yZmf/Hlpb/pX9//82env/PpaX/0ays/9O1tf/Vu7v/u6ys/4V+ + fv//oqL/pllZ/8xmZv/MZmb/gE1N/7OZmf++pKT/vFNT/8dlZf/nu7v/+Pj4//7+/v9cfP//ADP//xVE + ///U3f//1N3//9Td///U3f//zdX7/2mA8v8AM///ADP//yNP////////AAAAADMaGgojIiFiVVVV//// + ///FfX3/xX19/8V9ff/FfX3/w39//511df+FcXH/cGtr/3xxcf+qiIj/w5SU/8yZmf/NnJz/zqSk/9Cr + q//Ts7P/x66u/3dwcP//pKT/pllZ/8xmZv/MZmb/gE1N/62Tk/+njY3/uFJS/75WVv/isLD//////5uv + //8AM///ADP//wAz//8AM///ADP//wAz//8AM///ADP//wAz//8AM///ADP//2OC//////+fAAAAADMa + GgojIiFiVVVV///////FfX3/xX19/8V9ff+ldXX/knp6/8SVlf/Ak5P/uY+P/6yIiP+TfHz/hG1t/3Zs + bP+TfHz/tY+P/8uhof/Qqan/zays/6uTk///pqb/pllZ/8xmZv/MZmb/gE1N/8KoqP+pjo7/sUtL/7hS + Uv/r1dX/scD//wAz//8AM///ADP//wAz//8AM///ADP//wAz//8AM///ADP//wAz//8AM///MVr///// + //////8gAAAAADMaGgojIiFiVVVV///////FfX3/xX19/7J4eP+KeHj/xpaW/8CTk/+5j4//souL/6qI + iP+jhIT/oH9//8N9ff+9e3v/mnJy/3tra/96cXH/m4eH/8Olpf//qKj/pllZ/8xmZv/MZmb/gE1N/9rB + wf++paX/qUND/7FLS//Yubn/8/T+/yNP//8AM///ADP//wAz//8AM///ADP//wAz//8AM///ADP//wAz + //8jT///8/T+/+nm5GwAAAAAAAAAADMaGgojIiFiVVVV///////FfX3/xX19/4BwcP/FlZX/vpKS/7iP + j/+4p6f/sJmW/7Kbm/+agID/lX19/556ev/FfX3/xX19/8J8fP+jeHj/uIaG/5d9ff//q6v/pllZ/8xm + Zv/MZmb/gE1N/+7V1f/Turr/oz09/6lDQ/+wfX3/4+Pj/+bq/f8HOP//ADP//wc4//9/mf//f5n//3+Z + //9/mf//qrv//9vi///t6un1rKKchzMaDRQAAAAAAAAAADMaGgojIiFiVVVV///////FfX3/t3l5/3Vt + bf++kpL/uI6O/3ViXN9uXlWfmoR5ts2amv+dhob/jXl5/4Z2dv+/fHz/rXd3/415ef/Ilpb/v5KS/7eO + jv//ra3/pllZ/8xmZv/MZmb/gE1N/+3U1P/ix8f/lisr/6A3N/+jb2//qqqq/+rq6v/a3/z/Bzj//ypV + ///79fX/5rOz/6ygoP/ms7P/5rOz/8KNjf8zGg2vMxoNUDMaDRQAAAAAAAAAADMaGgojIiFiVVVV//// + ///GhIT/q3t7/4x5ef+4jo7/sYuL/1I9NcZAKR1qRCwhitTBu/+skZH/hnZ2/35ycv+5gID/fG9v/8eW + lv+/kpL/uZGR/7aXl///r6//pllZ/8xmZv/MZmb/gU1N/+bOzv/o1tb/uHBw/6dPT/+tfn7/oqKi/6ys + rP/x8fH/wcr6/ypV///57Oz/zGZm/1lAQP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAADMa + GgojIiFiVVVV///////IjIz/uoaG/3NsbP+xi4v/qIeH/2hXUfc5IhWxaFZM0tC3sP+pkJD/fnJy/3hv + b/+kfn7/lX19/7+Skv+3jo7/g3Rt25uGfcr/sbH/pllZ/8xmZv/MZmb/dUlJ/+a7u//39vb/+O3t//Hd + 3f/d0ND/vLy8/6Kiov+xsbH/9fX1/8bR///46+v/zGZm/1lAQP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMa + DRQAAAAAAAAAADMaGgojIiFiVVVV///////KlJT/ypSU/25qav+oh4f/oYOD/5uBgf+iioL9rox//rR3 + d/+IfX3/eG9v/3Bra/+Senr/oYOD/7aOjv+FcnD5PycbxmBNRVz/s7P/pllZ/8xmZv/MZmb/iVBQ/2lT + U/91dnb/lp2d/8LIyP/i6ur/7Ovr/8nKyv+rrKz/t7i4//z8/P/16Oj/zGZm/1lAQP/MZmb/zGZm/6ZZ + Wf8zGg2vMxoNUDMaDRQAAAAAAAAAADMaGgojIiFiVVVV///////NnZ3/zZ2d/6uLi/+NeXn/mX9//5J8 + fP+LeHj/hXV1/31xcf92bm7/cGtr/4t5ef+ZgYH/knx8/6+Kiv+Kenr9OyIX4UEoHsT/trb/pllZ/8xm + Zv/MZmb/wmNj/65aWv+pWlr/nV1d/5JlZf+TeXn/jIKC/4eJif+GiYn/iYyM/7Kzs//ap6f/0mho/1lA + QP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAADMaGgojIiFiVVVV///////PpaX/z6Wl/8+l + pf+Ke3v/jHl5/4t4eP+FdXX/fXFx/3Zubv9vamr/a2ho/8afn/+9mpr/hXZ2/6eGhv+gg4P/i317/IV0 + bvT/uLj/pllZ/8xmZv/MZmb/1mlp/9lqav/YaWn/zmVl/7xcXP+pU1P/j0lJ/4lOTv+FWVn/fF1d/31q + av+cYmL/pVlZ/1Q+Pv/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAADMaGgokIyJhVVVV//// + ///Mqan/hHBw/9Gtrf/Rra3/j4KC/4J1df98cXH/dm5u/29qav98dXX/yKen/9Gtrf/Rra3/enR0/5uA + gP+Yf3//kXt7/5OFhf//urr/pllZ/8xmZv/MZmb/ilBQ/35ERP+USUn/tVdX/89iYv/caGj/4G1t/99r + a//PZGT/tltb/5pQUP9yRkb/akZG/3NJSf/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAAEAg + IAghIyFcT09P/8/Pz//LtbX/yKys/9O2tv/Ttrb/07a2/9O2tv/Ttrb/07a2/9O2tv/Ttrb/07a2/9O2 + tv/Ttrb/vqam/3JsbP+Penr/iHd3/4Bzc///vLz/pllZ/8xmZv/MZmb/XEFB/8SXl/+omZn/lHd3/45n + Z/+IWFj/hUJC/5ZJSf+xVlb/w15e/9loaP/da2v/3Wxs/9tra//MZmb/zGZm/6ZZWf8zGg2vMxoNUDMa + DRQAAAAAAAAAADMzAAUgIiNTMzMz/7+/v/+cnJz/kYmJ/6KWlv/EsLD/0ru7/9W+vv/Vvr7/1b6+/9W+ + vv/Vvr7/1b6+/9W+vv/Vvr7/1b6+/62env9qaGj/fXFx/3dvb///vr7/pllZ/8xmZv/MZmb/WEBA/+bI + yP/o6+v/2Nra/8fKyv+1urr/qqGh/6KMjP+eenr/mWpq/65kZP+rWlr/qlVV/75hYf/MZmb/zGZm/6ZZ + Wf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAAAAIiJyEaGyMn8SkzOf9RUVH/VFRU/2VlZf9wcHD/cHBw/3Jy + cv+Bfn7/o5qa/8Cysv/Jurr/18bG/9fGxv/Xxsb/18bG/9fGxv/Xxsb/xLa2/8W3t///wMD/pllZ/8xm + Zv/MZmb/WUBA/+PKyv/Kysr/1dXV/+Pj4//09fX////////////m6Oj/xcjI/6eoqP+Uior/e2dn/3FI + SP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAAAABVAAADIiYgGx8jJWMeIySXHSMmzyAq + LvQkLzL/KTM5/y8+RP9UVFT/ZWVl/25ubv9wcHD/cHBw/4F/f/+emZn/qqOj/8e+vv/Zz8//2c/P/9nP + z///wsL/pllZ/8xmZv/MZmb/WUBA/+HHx//S0tL/0NDQ/8PDw//Hx8f/x8fH/8rLy//a2tr/6Onp//n6 + +v///////////1U/P//MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAAAAAAAAAAAAAAAisr + KwYzGhoKMyIRDzkcDhIrIh0mISIhWB8jJJceIiW2HiUp3CErL/8pMzn/LDg+/zI+Rf9lZWX/bm5u/3Bw + cP9wcHD/eHh4/4eGhv//xMT/pllZ/8xmZv/MZmb/WUBA/+DGxv/BwcH/wMDA/8DAwP/IyMj/wsLC/8PD + w//Gxsb/w8PD/8rKyv/Pz8///v7+/1lAQP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAJVAAADKysrBjMaGgo3JBIOLR4eETMaGhQhISFGICIjeR8j + JJodIybPICou9CUwNf8rNjv/Lz5E/zZDTP//xsb/pllZ/8xmZv/MZmb/WUBA/9/Fxf/X19f/y8vL/8vL + y//Ly8v/ysrK/9jY2P/U1NT/1NTU/8/Pz//Hx8f/+vr6/1lAQP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMa + DRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAgAA + AAJAQAAEQCAgCCsrFQwzIhEPNhsbEyUhHzYiISJaHyMkmB0jJcL/yMj/pllZ/8xmZv/MZmb/WUBA/93D + w//FxcX/wMDA/8bGxv/Gxsb/xcXF/8XFxf+3t7f/xMTE/76+vv/Pz8//+fn5/1lAQP/MZmb/zGZm/6ZZ + Wf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAkBAAAQrKysGMxoaCjckEg7/ysr/pVhY/81m + Zv/MZmb/WUBA/9vCwv/S0tL/xMTE/8TExP/CwsL/ysrK/9PT0//Y2Nj/4eHh/9PT0//b29v/+Pj4/1lA + QP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAA + AAH/z8//rmFh/8JgYP/MY2P/UDU1/9e7u//f39//29vb/9ra2v/R0dH/zc3N/7+/v/+1tbX/tra2/7e3 + t//IyMj/9vb2/1lAQP/MZmb/zGZm/6ZZWf8zGg2uMxoNUDMaDRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADGi4rBtHJy2s9/f/rMfX3/qZSU/8rGxv/b2dn/7u7u//Dw8P/v7+//8vLy/+7u + 7v/09PT/9PT0/+vr6//h4eH/8/T0/1lAQP/MZmb/zGZm/6ZZWf80Gw6nNRoNTTYbDRMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHi4sIu3d3E86AgCjMgIAycU5FM6Wem3ezr6/Go5+eyqSf + ntyzsrHsurm5+cXGxv3JzMz/4OHh/+vs7P/y8vL/8/Ly/1xDQ//MZ2f/zGZm/6ZZWf83HRGLOR4TQzwe + DxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABcXFwKpKSkIrGwsDDDw8NHoJqYg6+rqZymoqHBqKWkz6mlpNy1srLqycfH+KCYmPTEfHz8zGZm/6hY + WP88JBhVQCMXLEArFQwAAAAAAAAAAP//4Af//wAA//+AAf//AAD//AAAP/8AAP/wAAAP/wAA/8AAAAP/ + AAD/AAAAAH8AAPgAAAAAHwAA4AAAAAADAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAEAAAAA + AAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAAD + AAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAIAAAAAAAwAAwAAAAAADAADwAAAAAAMAAP+A + AAAAAwAA//wAAAADAAD//+AAAAMAAP///AAAAwAA///8AAADAAD////wAAMAACgAAAAgAAAAQAAAAAEA + IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAABVQAAAiQkJAY3JBILNyQSFjYhFiYzHxg0NiAWOTEhFTI0HRcjMR0UFTckEgskJCQGVQAAAgAA + AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAABQEAAAzkcHAc2KBsPNSYXGzYfFi41IBVGMyAXeDUgFqk0HhO0Mh4VojMgFm40HhVDNyAXLTUe + Fxs2GxsPORwcB0AAAAMAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAEAAAACMzMzBC4uLgk6IyMSOCUZITUiFzY0IBZVNSAYizQmH8tJSEj0W15h/TEtK/YzIRjnMxwQ0DUe + E7czIBWKNCAWVDEeFzYyHxkhNSAVEzsnFAorKysFAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAGAAAACKysrBTckJAs5JhwWOiUbJjYlGz02IhlkNSQcpTUsJ9pWWVr6kJCQ/9PT0/+biYn/hHJy/1xT + U/9ERET9MiYg8TMeEts0HhLEMx8VnzMfF2UzIBZANSIYKjEhGRk8Hg8OORwcB0BAAAMAAAABAAAAAAAA + AABVVVUCSSQkBkAwIA06KSEZOCUgLDkkG0Q4JRt3OioitTo2NOh8f4L9qKio/66urv/Q0ND/zMzM/42A + gP9JRET/MzMz/1VNTf+Cb2//Vk5P/zErKPcyIBXkNBwPzTQgFbMzIBaHNB4TSzMfFDQzIBogNyEWEisr + FQpAQAADAAAAAEArIBM+KSIeOiUdMjokG0o6Jx6IPjEsyUxISO6Wm5//zc3N/9XV1f/CwsL/n5+f/6Ki + ov+SkpL/fHNz/0ZCQv8zMzP/MzMz/zMzM/9BPj7/dWVl/3ZmZv9IRUT7MSYg8TMcD9c1HhLBMx8VnjMf + F2Q1IRc+NiYbJjEkGBEAAAAAPigfQjsmHFs8Jh2XRTs412lqbfeampr/1dXV/9TU1P/Ozs7/t7e3/5eX + l/+lpaX/t7e3/7Gxsf90bm7/Q0FB/0tLS/9cXFz/ZGRk/2tra/9zc3P/a2tr/4F4eP+Qe3v/dnR1/jEr + KPcyIBXjNB0QyzgiF6w0IRd0MSQWLgAAAABALSW6TEVE5YyOkfu8vLz/2NjY/9PT0//Jycn/oaGh/6Gh + of+3t7f/tbW1/7CwsP+rq6v/pqam/2dlZf9APj7/UFBQ/2VlZf9ubm7/d3d3/39/f/+IiIj/kJCQ/5iY + mP+Xhob/oISE/5iWlv9UTkz7MiUe6jQdEbkyIRdjdXqCn4+Umv/Q0ND/19fX/9HR0f+1tbX/np6e/66u + rv+6urr/tLS0/6+vr/+qqqr/pKSk/6ysrP/R0dH/mJiY/1lYWP9DQkL/S0tL/2ZmZv93d3f/f39//4iI + iP+QkJD/mJiY/62Zmf+tiIj/676+//bGxv+koKD/MiQc2zMgFnOTl53/kpKS/8fHx/+oqKj/p6en/76+ + vv+4uLj/s7Oz/66urv+pqan/qqqq/8fHx//q6ur/39/e/66trf+Uk5L/zs3N/8HAwP+PjY3/V1ZW/0pI + SP9cW1v/gYGB/5CQkP+YmJj/rZmZ/62IiP/Srq7/3ra2/8ynp/8yIxvfMyEVd4mOlf+lpaX/uLi4/729 + vf+3t7f/srKy/62trf+qqqr/vb29/+Dg4P/o6Oj/4ODg/93d3P/V09P/ysfG/8PAvv/Cv73/xsTD/8nH + x//Lysr/v76+/5KRkf9RT07/TEpK/2tra/+jkJD/rYiI/8asrP/Qs7P/uZqa/zIkG94zIRV2nqOr/7u7 + u/+2trb/sbGx/6ysrP+2trb/1NTU/+zs7P/q6ur/6Ojo/+Xl5f/V1dX/wcDA/7azsv9cSUD/Tzsx/1lH + P/92a2f/ioSA/5mWlP+koaD/qaam/721tP+1sbH/gH5+/11ZWf9WTk7/sJ2d/9TGxv/Jubn/MiQc1jMh + F26hpq7/sLCw/7Ozs//Jycn/3d3d/9fX1//W1tb/4+Pj/9zc3P/Nzc3/zc3N/9/f3//m5eT/m2lp/2NJ + SP9fTUr/RTIp/0o3Lf9VQTf/Z1hQ/5CGgf/Nycf/4+De/9zX1v/bxMP/3MHA/7Szs/+Tj4//KlX///// + //9lXFjHMiQYUpqepv/Ly8v/fX19/15UVP9PQ0P/TkhI/1BQUP9eXl7/hoaG/52dnf+tra3/wMDA/9LS + 0f/nior/xF9f/6RTU/+XhIT/qaqo/3BsaP9SQzv/STIo/1I+NP94amL/7Orq/9DLyf/RsrH/nZqZ/3V0 + c//b4v//Y4L//9rZ2MI2Ihseen+IUJWWl/SkpKT/vn5+/41vb//Nj4//zLKy/7aKiv97Y2P/VExM/09P + T/9ycnL/iYiI/96IiP++Xl7/sVdX/7ydnf/57e3/5tnZ/9PT0/+mpqX/aF9Z/8O8uf+bq/f/n5WP/0k2 + Lv9TRkLhop6c0Nrf/P8qVf//+vn5xDMaGggAAAAAMDI0nd3d3f/FfX3/rXd3/8aAgP/Rxsb/x5aW/7+Q + kP+mgID/zqOj/66UlP95bWz/3YmJ/71eXv+yWVn/oIaG/8ORkf/NaWn/9dvb//L09P/r6+v/m6v3/yNP + ///Vz87/o5WR+q2infT29PT7FUT//ypV///+/v7AAAAAAQAAAAAwMjSa3d3d/8V9ff/FfX3/xX19/8eh + of+JdHT/noKC/8OUlP/MmZn/zaCg/8uoqP/fjYz/vV5e/7NaWv+bgID/rHt7/7lJSf/nwcH//////5ur + 9/8HOP//I0///yNP//8jT///I0///wAz//8AM///Y4L//////58AAAAAAAAAADAyNJrd3d3/xX19/8V9 + ff+fenr/xpaW/7yRkf+wior/kXt7/41ycv+UfX3/so+P/9+Ojv+9XV3/r1ZW/7Samv+4h4f/r0FB/8+r + q/+bq/f/ADP//wAz//8AM///ADP//wAz//8AM///ADP//yNP////////////MAAAAAAAAAAAMDI0mt3d + 3f/FfX3/qHh4/8aWlv+8kZH/uZeX/6+Skv+cgID/onx8/8R9ff+tdHT/3o+P/71dXf+uVVX/ya+v/8uY + mP+dLCz/roqK/+Tn5/+bq/f/I0///yNP//9/mf//f5n//3+Z///b4v///fz88u7s61cAAAAAAAAAAAAA + AAAwMjSa3d3d/8V9ff+FcXH/vJGR/4RvavpXQzjSkm9i26GRkf+SeHj/wXx8/452dv/fkpL/vV1d/65U + VP/Jrq7/4ry8/6hHR/+lgoL/qq6u/+np6f+bq/f/ADP//9/R0f/jsrL/162t/5aJg+lzYVg1MxoNCgAA + AAAAAAAAAAAAADAyNJrd3d3/x4eH/4Rycv+yi4v/inRx/TgfE8yEbGLsopiY/4Zzc/+IdHT/vZCQ/9+V + lf++XV3/rlhY/51/f//Q1dX/49zc/+He3v+5urr/srKy//Hy8v+bq/f/v6Oj/8ZkZP+vW1v/NRwP1zYd + ECgzGg0KAAAAAAAAAAAAAAAAMDI0mt3d3f/LlJT/k3t7/6WFhf+agID/pZaR/rSYmP97cnL/f3Jy/3Zt + bf+zior/3ZWV/7tbW//JZWX/nVVV/5JeXv+cfX3/rKCg/5+goP+MkZH/p6ur//Tb2//ApKT/xmRk/69b + W/81HA/XNh0QKDMaDQoAAAAAAAAAAAAAAAAwMjSZ3d3d/86goP/Lnp7/hnd3/456ev+EdXX/eW9v/29q + av+ojIz/d29v/6aEg//dmJf/ultb/8xnZ/+8W1v/w1dX/8ZZWf+2VFT/p1ZW/5hcXP+DXl7/jFZW/2pF + Rf/JZWX/r1tb/zUcD9c2HRAoMxoNCgAAAAAAAAAAAAAAAC8yNJfb29v/z6ys/7+fn//Oq6v/n4yM/4N4 + eP+Bd3f/sJiY/9Gtrf/Bo6P/fHBv/96amv+/XFz/qFhY/4toaP+ifn7/omho/6RZWf+sUFD/vVdX/8Va + Wv+3W1v/vGFh/89nZ/+vW1v/NRwP1zYdECgzGg0KAAAAAAAAAAAAAAAALzQ2kJKSkv+blpb/w62t/8yz + s//Uurr/1Lq6/9S6uv/Uurr/1Lq6/9O5uf+/qKj/3Zyc/79cXP+eT0//sZ6e/+Pn5//P1NT/xsnJ/8C0 + tP+wlpb/qHl5/59dXf+nWFj/z2dn/69bW/81HA/XNh0QKDMaDQoAAAAAAAAAAAAAAAAvMzdaRk1T8UtT + W/9UVFT/ZWVl/25ubv+Bfn7/koyM/6mfn//GuLj/1MPD/9LAwP/fn5//v1xc/59RUf+tmpr/1dTU/9DP + z//b29v/4OHh/+Hl5f/e5OT/2Nra/4Bubv/GX1//r1tb/zUcD9c2HRAoMxoNCgAAAAAAAAAAAAAAAEBA + AAMrKxUKMDExPzAyMmUvMzaUPEJHzENKUOZGTlX/UVFR/1tbW/9nZ2f/cG9u/92hof+/XFz/oFFR/6yZ + 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 + + + \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.resx.bak" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.resx.bak" new file mode 100644 index 0000000..bf43433 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/frmMain.resx.bak" @@ -0,0 +1,699 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 120, 17 + + + 329, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACG + EwAAAk1TRnQBSQFMAgEBBQEAAQkBAAEEAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA + AwABIAMAAQEBAAEgBgABIP8AGwABsAGgAZAB/wFoAVABOAH/AWgBUAE4Af8BaAFQATgB/wFoAVABOAH/ + AWgBUAE4Af8BaAFQATgB/wFoAVABOAH/AWgBUAE4Af8BaAFQATgB/wFoAVABOAH/1AABsAGgAZAF/wGw + AaABkAH/AbABoAGQAf8BsAGgAZAB/wGwAaABkAH/AbABoAGQAf8BsAGgAZAB/wGwAaABkAH/AbABoAGQ + Af8BaAFQATgB/9QAAbABoAGQCv8B+AL/A/AB/wHwAegB4AH/AfAB4AHQAf8B4ALQAf8B4AHIAcAB/wGw + AaABkAH/AWgBUAE4Af/UAAGwAaABkA7/AfgB8AH/A/AB/wHwAuAB/wHwAdgB0AH/AeAB0AHAAf8BsAGg + AZAB/wFoAVABOAH/1AABsAGgAZAS/wLwAf8B8AHoAeAB/wHwAuAB/wHgAdgB0AH/AbABoAGQAf8BaAFQ + ATgB/9QAAcABqAGQEv8B+AHwAf8D8AH/AfAB6AHgAf8B8AHYAdAB/wGwAaABkAH/AWgBUAE4Af/UAAHA + AagBoBb/AfgB8AH/AfAB6AHgAf8B8ALgAf8BsAGgAZAB/wFoAVABOAH/1AABwAGwAaAW/wH4Av8D8AH/ + AfAB6AHgAf8BsAGgAZAB/wFoAVABOAH/1AAB0AGwAaAa/wH4AfAB/wPwAf8BsAGgAZAB/wFoAVABOAH/ + 1AAB0AG4AaAd/wGwAaABkAH/AbABoAGQAf8BaAFQATgB/9QAAdABuAGwGf8BsAGgAZAB/wFoAVABOAH/ + AWgBUAE4Af8BaAFQATgB/9QAAdABwAGwGf8BwAGoAZAB/wHQAcgBwAH/AWgBUAE4Af8BTQICAZDUAAHg + AcABsBn/AcABqAGgAf8BaAFQATgB/wFNAgIBkNgAAeABwAGwAf8B4AHAAbAB/wHgAcABsAH/AeABwAGw + Af8B4AHAAbAB/wHQAcABsAH/AdABuAGwAf8B0AGwAaAB/wFNAgIBkP8A5QAD3QH/A64B/wODAf8DawH/ + A2EB/wNrAf8DgwH/A64B/wPdAf/YAAPEAf8DfQH/AXsCdwH/AZQCjAH/AbMCqQH/AZQCjwH/AXUCcwH/ + A1oB/wNZAf8DgQH/A74B/9AAA7gB/wGBAn0B/wHPArQC/wLdAv8C4gL/AugC/wLuAv8C8wH/A88B/wOB + Af8DUwH/A3MB/wO+Af8IAAENAgEBEFgAA+4B/wP0Af8YAAF4AYgBkAH/AXgBgAGBAf8BaAJ4Af8BWAJo + Af8BSAJYAf8BOAFAAUgB/wEoATABOAH/ARgCKAH/AhgBKAH/AhgBKAH/AhgBKAH/AhgBKAH/AhgBKAH/ + AhgBKAH/AhgBKAH/CAADzwH/AYUCgQH/AfUCxwL/AtIC/wLYAv8C3QL/AuIC/wLoAv8C9AX/A/UB/wOd + Af8DUwH/A4EB/wPdAf8BsAGQAYEB/wGBAWgBWAH/AXgBWAFIAf8BeAFgAUgB/wF4AWABSAH/AXgBWAFI + Af8BeAFYAUgB/wFoAVABOAH/AWgBUAE4Af8BaAFQATgB/wFoAVABOAH/AWgBUAE4Af8BaAFQATgB/wFo + AVABOAH/AWgBUAE4Af8YAAPxAf8D2QH/A6sB/wOKAf8DowH/A9AB/wPtAf8QAAF4AYgBkAH/AaAB4AHw + Af8BeAHQAfAB/wFYAbgB4AH/ATgBsAHgAf8BOAGoAeAB/wEoAaAB0AH/ASgBmAHAAf8BKAGQAcAB/wEo + AYEBsAH/ASgBgQGwAf8BGAGBAbAB/wEoAYABoAH/ASgBeAGQAf8BGAIoAf8IAAGIAoMB/wH1As0C/wLP + Av8CzQL/AtIC/wLYAv8C3QL/AuIC/wL6Cf8D9QH/A4EB/wNZAf8DrgH/AbABmAGBAf8D4AH/AbABuAGw + Af8D8AH/AfAB6AHwAf8B0AHYAdAB/wGwAbgBsAH/A7AB/wGwAagBsAH/AbABqAGgAf8BkAGBAXgB/wFo + AVABOAH/AaABmAGQAf8BgQGAAXgB/wFoAVABOAH/EAAD7QH/A88B/wOTAf8DbwH/AY4ChAH/AVoCVQH/ + A1IB/wNlAf8DiQH/A8AB/wPlAf8IAAGBAYgBkAH/AbAB6AHwAf8BkAHoAv8BgQHgAv8BeAHYAv8BeAHQ + AfAB/wFoAcgB8AH/AVgBwAHwAf8BSAG4AfAB/wE4AagB8AH/ATgBqAHgAf8BKAGYAeAB/wEYAZAB0AH/ + ASgBgAGgAf8BKAEwATgB/wQAA7cB/wHGArEC/wLZAv8C1AL/As8C/wLNAv8B1AHbAv8B1wHiAv8B5AHq + Ef8B0gLMAf8DWgH/A4UB/wGwAaABkAH/AfAB+AHwAf8D4AH/A8AB/wHwAfgB8AH/AeAB6AHgAf8D4AH/ + A9AB/wPAAf8BkAGIAYEB/wFoAVABOAH/AbACoAH/AbABqAGgAf8BoAGYAZAB/wFoAVABOAH/CAAD6wH/ + A8AB/wOBAf8DfQH/A40B/wOlAf8BfQJ4Af8BRwJEAf8BUQJMAf8BYgJaAf8BVgJUAf8DXgH/A3oB/wOo + Af8D3AH/AYEBkAGgAf8BsAHoAfAB/wGgAegC/wGQAegC/wGBAeAC/wF4AdgC/wF4AdAB8AH/AWgByAHw + Af8BWAHAAfAB/wFIAbgB8AH/ATgBqAHwAf8BOAGgAeAB/wEoAZgB4AH/ARgBgQGwAf8BOAFAAUgB/wQA + AZcClgL/AuMC/wLeAv8C2QL/AtQC/wHeAecC/wHaAfwC/wHRAfMC/wHiAfwC/wH4Bv8B7wLfAf8B3AK5 + Af8BzAKZAf8BcAJpAf8DcQH/AbABoAGQAv8B+AHwAf8BwAG4AbAB/wHgAegB4AH/AfAB6AHwAf8D8AH/ + A/AB/wPwAf8D8AH/A/AB/wHwAfgB8AH/A/AB/wHgAdgB4AH/AbABqAGgAf8BeAFYAUgB/wQAA/EB/wOJ + Af8DiQH/A8kB/wOjAf8DgQH/A4IB/wOBAf8DVgH/A3kB/wOBAf8BfAJ2Af8BfAJtAf8BXgJZAf8DWgH/ + A4EB/wGBAZABoAH/AbAB8AL/AbAB8AL/AaAB6AL/AZAB4AL/AYEB4AL/AXgB2AL/AXgB0AHwAf8BaAHI + AfAB/wFYAcAB8AH/AUgBsAHwAf8BOAGoAfAB/wEoAaAB4AH/ARgBgQGwAf8CSAFYAf8EAAGcApcC/wLn + Av8C4wL/At4C/wLgAv8B7wH8Af8BswGeAaMB/wNxAf8BrwGoAawB/wH8AfAB+QH/AdYCrAH/AcwCmQH/ + AcwCmQH/AcwCmQH/AYECeQH/A2QB/wHAAagBkAL/AfgC/wOgAf8B4AHYAeAB/wPQAf8D0AH/AdAByAHA + Af8B0ALAAf8B0ALAAf8BwAG4AbAB/wHAAbgBsAH/AtABwAH/AfAB+AHwAf8DsAH/AXgBWAFIAf8EAAOm + Af8DrgH/A5gB/wOCAf8DpAH/A7kB/wPRAf8DjgH/A4EB/wN0Af8DgQH/A2oB/wM8Af8BtwKYAf8BigJ/ + Af8DcwH/AYEBmAGgAf8BwAHwAv8BsALwAf8BoAHwAv8BoAHoAv8BkAHgAv8BgQHgAv8BeAHYAv8BaAHQ + AfAB/wFoAcgB8AH/AVgBuAHwAf8BSAGwAfAB/wE4AagB4AH/ARgBiAHAAf8CWAFoAf8EAAGeAowB/wHp + Ar4B/wHvAsIB/wHzAsUB/wH5AtYC/wHpAfMB/wGCAn8B/wNTAf8DcAH/AfkB6AHsAf8B7ALKAf8B5gK6 + Af8B5gK9Af8B5gLAAf8BpgKUAf8DcQH/AcABsAGgAv8B+AL/A5AB/wHgAegB4AH/AWgBYAFYAf8DoAH/ + AdAByAHAAf8B0AHIAcAB/wHQAsAB/wFoAWABWAH/AZABmAGQAf8BwAG4AbAB/wHwAfgB8AH/AcABuAGw + Af8BeAFYAUgB/wQAA6AB/wOcAf8DtQH/A8cB/wPhAf8D5gH/A9AB/wPYAf8D0QH/A8MB/wGaApkB/wGB + AoAB/wFOAksB/wGTAoEB/wGEAn4B/wOBAf8BgQGYAaAB/wHAAfAC/wGwAfAC/wGwAfAC/wGgAegC/wGQ + AegC/wGQAeAC/wGBAeAC/wF4AdgC/wFoAdAB8AH/AWgByAHwAf8BWAG4AfAB/wE4AagB4AH/ARgBkAHA + Af8BWAFoAXgB/wQAAZwCjwH/AdsCqAH/AeMCsAH/AeoCtwH/AfMCzQL/Ae4B/AH/AcUBvAHCAf8BdwJz + Af8BswGgAaMC/wH0AfwC/wLYAv8C2AL/At0C/wLiAf8BiQKDAf8DhQH/AdABsAGgAv8B+AL/A5AB/wPw + Af8BSAFAAUgB/wNoAf8D0AH/AdAByAHAAf8B0AHIAcAB/wFIAUABSAH/A3gB/wHAAbgBsAH/AfAB+AHw + Af8DwAH/AXgBWAFIAf8EAAO6Af8D0wH/A94B/wPHAf8D4QH/A98B/wPyAf8D/AH/A+wB/wPfAf8D9wH/ + Ae4C6gH/Ad4CzAH/AY8CigH/AY0CiQH/A+cB/wGQAqAB/wHAAfAC/wGwAfAC/wGwAfAC/wGwAvAB/wGg + AfAC/wGQAegC/wGQAeAC/wGBAeAC/wF4AdAC/wFoAdAB8AH/AVgBwAHwAf8BWAG4AfAB/wEoAZgB0AH/ + AWgBeAGBAf8EAAGaApYB/wHVAqIB/wHnAsQB/wH3AucG/wH2A/8B3gP/AdIB9QL/AeIB/AL/AeAB5wL/ + As0C/wLSAv8C2AL/At0B/wFyAnAB/wOuAf8B0AGwAaAF/wKgAZAB/wHwAfgB8AH/A/AB/wPgAf8B4AHY + AdAB/wPQAf8B0AHIAcAB/wHQAsAB/wHQAcABsAH/AcABuAGwAf8D4AH/A8AB/wF4AVgBSAH/BAAD4wH/ + A+AB/wO7Af8DgQH/A3QB/wO4Af8D8gH/A/wB/wPsAf8D3wH/A/cB/wPKAf8DwQH/A8sB/wgAAZABoAGw + Af8BwAHwAv8BwAHwAv8BwAHwAv8BwAHwAv8BsAHwAv8BsAHwAv8BoAHoAv8BkAHoAv8BkAHgAv8BgQHY + Av8BeAHQAv8BeAHIAfAB/wFoAcAB8AH/AWgBeAGBAf8EAAPLAf8BwwK/Ev8B7AHxAv8B2wHnAv8B2QHe + Av8C1AL/As8C/wLNAv8C0gH/AcYCrQH/A38B/wPeAf8B0AG4AaAC/wH4Av8B4AHYAdAB/wGgAZgBkAH/ + AZABiAGBAf8BkAKBAf8BkAKBAf8BkAKBAf8BkAGIAYEB/wGQAYgBgQH/A5AB/wGgApAB/wGwAbgBsAH/ + AeAB2AHgAf8BeAFgAUgB/xAAA+wB/wPbAf8DyAH/A9UB/wPmAf8DzQH/A9QB/wPUAf8UAAGQAaABsAH/ + AZABoAGwAf8BkAGgAbAB/wGQAaABsAH/AZABoAGwAf8BkAGgAbAB/wGQAqAB/wGQAZgBoAH/AYEBmAGg + Af8BgQGYAaAB/wGBAZgBoAH/AYEBmAGgAf8BgQGYAaAB/wGBAZgBoAH/ATsCAQFgCAADlAH/A/UK/wL+ + Av8C5wL/AuMC/wLeAv8C2QL/AtQC/wLPAf8B9QLHAf8BgQJ+Af8DxgH/BAAB0AG4AaAB/wPgCv8B+AP/ + AfgD/wH4A/8B+AHwAf8B8AH4AfAB/wPwAf8D8AH/A/AB/wHwAegB4AH/AfAB6AHgAf8BoAGIAYEB/0QA + AZABqAGwAf8BsAHoAfAB/wGwAfAC/wGwAfAC/wGwAvAB/wGQAeAB8AH/AZABoAGwAf8BSAICAYAkAAPm + Af8DjAH/A/UG/wL4Av8C7AL/AucC/wLjAv8C3gL/AtkB/wH1As0B/wGFAoEB/wO6Af8IAAEiAgEBMAHQ + AbgBoAH/AdABuAGgAf8B0AG4AaAB/wHQAbABoAH/AcABsAGgAf8BwAGwAaAB/wHAAbABoAH/AcABsAGg + Af8BwAGwAaAB/wHAAagBoAH/AcABqAGgAf8BwAGoAaAB/wHAAbABoAH/AUgCAgGARAABIgIBATABkAGo + AbAB/wGQAagBsAH/AZABqAGwAf8BkAGoAbAB/wGQAagBsAH/ASsCAQFALAAD5gH/A5QB/wPGAv8C9wL/ + AvIC/wLsAv8C5wL/AuMB/wHGArEB/wGOAokB/wPRAf9AAAENAgEBEAEZAgEBIJQAA8sB/wOkAf8BpgKj + Af8BjAKJAf8BowKfAf8BoAKeAf8DwQH/1AABQgFNAT4HAAE+AwABKAMAAUADAAEgAwABAQEAAQEGAAEB + FgAD/wEAAv8GAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHA + AQcGAAHAAQcGAAHAAQcGAAHAAQcGAAHAAQ8GAAHAAR8GAAL/BgAB8AEHBv8B4AEDBv8BwAEBAb8C/wE/ + AQABAQGAAgABAQH4AQ8BAAEBAYACAAEBAeABAwEAAQEDAAEBAYACAAEBAwABAQMAAQEDAAEBAwABAQMA + AQEDAAEBAwABAQMAAQEDAAEBAQABAwEAAQEDAAEBAeABHwEAAQEBgAEBAQABAQL/AQAB/wGAAQMBAAEB + Av8BAQH/AcABBwH/AfkE/wHwAR8G/ws= + + + + 230, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAlpJREFUOE+tk21I + k1EYhif0oyA0sqIQCix/+GcQFFH9CCmiUBTLLEjShJofVBgL2fxoU9Pp5ubUlS5rU9f8rCyjsA+pUCRC + TR1ppmVFUSlmhq78unrnQF1KGHTg/nEOz30993PO+7qJFrmUeiv2n+Mij+XLRLLYULdF2pxlEVIDcw0p + AsyxD5fmI/rQ94pqi26eOlsfuZj+7BgSm01QdA4ih7m73Yx9qGpavwatjPebqCzOprPt8YKQgzFagqL0 + BEjyEFWVaBkdLHMxT34uYNwWR9nVTEoL0zHlp2DMSeaSRk6eKt4VWm5WM/rVPNN5SjDTLQebZEHNA1wr + UvHjk3E6tsNcV62e1r3KLGqtKm6WplNpSsVqVFJsOM8VfSKFWjkGtcyZptSYzvC7XByx3zQoqCnTMvlG + CX1prnornPUmQJcUXsbSVhGK5bIOkcmQyveeTHiv4VZ5Nk33Nc6iuSO8CIfmECYa/bE/8ON1iRipJNh5 + F0V6Bd86lfQ1JlFj1TDVq4COKCegLVIwHmGiKRB7/V6G7+5koHozymgfYRy5E1CgTWKgXcZ1i5qWp0KS + rjgBcAJawph6FszYk/2M1O1isGYLX8p9ab6wgqP+3rMvYciS01GfzA1LFvQkQ6sQ9/khxhoCGHnox1Dt + NvorxXw0b8Km8UQh2cip6GOzgNyMeKqKM7HdjqFZJ5pRk2YJ9aql3EnxoCJxNaZ4Ly6e3UDY3O6OEXRp + 59ApTpIhiyDh9GHORAZyPHQPB/ZtZ/cOMVvFPvh6e7F+3SrWrHRnraf7Xz/xf/rJ/kvxb84I3U1y+9/W + AAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAKZJREFUOE+1kt0N + gCAMhJnWUdiBbRiAZXjjrXqVQ1HwX5PmInJfr6bGfPU4Z2Rbl9kwxhhFxIm1VhWF81MILoUwaMHsvYep + nLWSVWC8iAQtAFJKCuDZXp3CcUfTLYA5PhNwjJ2GAwDN0Mo4mfQ9azcBYiHFPEI2UVeQLgCdOca6Izs/ + S7CJD8i7BF3A9KH6B43OOkILwEW6o2UP8qpyMe7q6ab/f2EES5nilRcvs5AAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAWVJREFUOE+tkkFq + w0AMRQ1dtLuZXUPBnik0xBtbs2so2JpdnEKYHCFHyBFyBB8hyy59hBzB2ywKPkJuMJXcJjROWrvQAWFj + 6T99a3QT0HGvzkweJ2/xU9zs3/cNf/vTwRmurLU+y7KDm7vlb2LnnMYC12c1iLjK87wyU+sZ5GZu04Uo + AEl1dlEsDvTcdgGakwxhQAtxrqKQXAiIMi9cm4MplECwH10SaP0NssUXNDjFshUDlOykdz4EMeSm5l9K + kuTAEcVpNUh8pFM3a4zx9hPSKDWg80n8DDqM05oBbTBknAy7XurcinVsfEq2xYMyURRVDFIxnF9fdxgw + IXEY1lprn6bpTiklhRBt0HvJs1BUc3WIACip0zYchTy0hpyYY+ERQtCaQVcBlFjKkfZsnbpsWHSxTOxw + HDaUu3Qh7sVGSunJxU7cXin4ogHtBAFO7k5NyIEJ7gK2ZwMR9C1LX7531/6v4AMif3KiZiJqOQAAAABJ + RU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAgxJREFUOE+lkvtL + U2EYx+0PEbtpFwnBKPGKiJImGP0gYhIYs1E5GF5gIxkpA00JRSmMEF0ohMh+GaRWYlqabMVcNdS2QpaI + VqiDIYhk397vA6fXhCjyhYdzeM/5fp7vczkAdeL2cwho7v/wWzT1zcN+Pwhr51uY2/y41PQaF+wzKKiZ + QvaN58g0jyLd5KEUcQbg+84P/Cm2tncQjW3j68YWIqubCC3FcOJc478BAuGoZM6zvoRnakXEruEIjhc4 + /g5gZop9c+voGAyLbQIfeBZxLL9BA1jzXvuGbWamuKh+GmmVbswE19A59FEBbmoAG7YbsLtm2mZmiml9 + cvabNDwpz6YB7LYBoMXCumkJr7LOmnnHzBQ/9X2Bo2cOibm1GsBREbAQiYmw/8lnuCeWkVzcgnZlnw1j + 3HV/wuNXK6i/9x5Hc6wawDlTXHbLJ+LZUBQPRyKwdQdxutwl1h+NLXHh5Ht1ewBHsiwawCW57HyDAfWR + dvl0uhZQ1eqX8aVc7EKLqrum651ATLf9OJx5XQM4KmY0xPzZ0hFAiQJnXB0WwME0E3IsL5B17ZlADqWb + NYDrOepdlcysmTWWOrxqbceRWtaLk0VO1XW72D5Vckd2gMBfq8zdpmUG62NJvKM4+XyziDk24xmfWoGE + s1c0gHPmbrPTpHNJKOCo2G1mZs20zcwUJ5yp1AB5+8/zEwgF5GMVDxh4AAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAgxJREFUOE+lkvtL + U2EYx+0PEbtpFwnBKPGKiJImGP0gYhIYs1E5GF5gIxkpA00JRSmMEF0ohMh+GaRWYlqabMVcNdS2QpaI + VqiDIYhk397vA6fXhCjyhYdzeM/5fp7vczkAdeL2cwho7v/wWzT1zcN+Pwhr51uY2/y41PQaF+wzKKiZ + QvaN58g0jyLd5KEUcQbg+84P/Cm2tncQjW3j68YWIqubCC3FcOJc478BAuGoZM6zvoRnakXEruEIjhc4 + /g5gZop9c+voGAyLbQIfeBZxLL9BA1jzXvuGbWamuKh+GmmVbswE19A59FEBbmoAG7YbsLtm2mZmiml9 + cvabNDwpz6YB7LYBoMXCumkJr7LOmnnHzBQ/9X2Bo2cOibm1GsBREbAQiYmw/8lnuCeWkVzcgnZlnw1j + 3HV/wuNXK6i/9x5Hc6wawDlTXHbLJ+LZUBQPRyKwdQdxutwl1h+NLXHh5Ht1ewBHsiwawCW57HyDAfWR + dvl0uhZQ1eqX8aVc7EKLqrum651ATLf9OJx5XQM4KmY0xPzZ0hFAiQJnXB0WwME0E3IsL5B17ZlADqWb + NYDrOepdlcysmTWWOrxqbceRWtaLk0VO1XW72D5Vckd2gMBfq8zdpmUG62NJvKM4+XyziDk24xmfWoGE + s1c0gHPmbrPTpHNJKOCo2G1mZs20zcwUJ5yp1AB5+8/zEwgF5GMVDxh4AAAAAElFTkSuQmCC + + + + 434, 17 + + + 537, 17 + + + 642, 17 + + + + AAABAAgAICAQAAEABADoAgAAhgAAABAQEAABAAQAKAEAAG4DAAAwMAAAAQAIAKgOAACWBAAAICAAAAEA + CACoCAAAPhMAABAQAAABAAgAaAUAAOYbAAAwMAAAAQAgAKglAABOIQAAICAAAAEAIACoEAAA9kYAABAQ + AAABACAAaAQAAJ5XAAAoAAAAIAAAAEAAAAABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////ABER + EREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREQAAAAAAAAAAAA + AAERERERf4iIiIiIiIiIiIgBEREREX+AiIiIiIiIiIgIARERERF/iIiIiIiIiIiIiAERERERf4iIiIiI + iIiIiIgBEREREX+IAAAAAAAAAACIARERERF/iA/4cACIh/9wiAERERERf4gHD4cACHcPcIgBEREREX+I + APD3AAhw8HCIARERERF/iAAH9wAIgAdwiAERERERf4gAAAAAAAAAAIhBEREREX+AiIiIiIiIiIiIARAA + AAB/iIiIiIiIiIiIiEEXd3d3f/////////////gBH4iIiHd3d3d3d3d3d3d3ER+KqoiIiIiIiIiIiId3 + AREfgiKIiIiIiIiIiIiHdwERH4//////////////h3cBER+AAAAAAAAAAAAAQId3AREfj/////////// + //+HdwERH4AAAAAAAAAAAAAAh3cBER+IiIiIiIiIiIiIiId3AREf///////////////3dwERF/iIiIiI + iIiIiIiIj3cBERF/iIiIiIiIiIiIiIj3ARERF3d3d3d3d3d3d3d3dxERERERERERERERERERERERERER + ERERERERERERERERERERERERERERERERERERERER/////////////////4AAAf8AAAH/AAAB/wAAAf8A + AAH/AAAB/wAAAf8AAAH/AAAB/wAAAf8AAAH/AAABgAAAAYAAAAGAAAADgAAAB4AAAAeAAAAHgAAAB4AA + AAeAAAAHgAAAB4AAAAeAAAAHwAAAB+AAAA////////////////8oAAAAEAAAACAAAAABAAQAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAICAAIAAAACAAIAAgIAAAMDAwACAgIAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////ABEREAAAAAABERGPd3d3d3AREY9wAAAAcBERj3B4eHBwERGPcAAA + AHARAI93d3d3cBiIj//////wh3eIiIiIgBGHp3d3d3eAEYeIiIiIiIARh3d3d3d3gBGP//////+AERh3 + d3d3d/AREYiIiIiIgRERERERERERERERERERERER+AEREfAAAADwAAAA8ACIAfAAERHAAA/4gACIhwAD + iAEAAxERAAMHDwADCHcAA4gBgAMREcAHAPD//whw//+IASgAAAAwAAAAYAAAAAEACAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AJYrKwCgNzcAoz09AKhHRwC7U1MAslFRAN1qagDMY2MAlklJAN9t + bQDOZWUA1WlpAJRJSQDMZmYAyWVlAMFhYQCpVVUAikZGAMVlZQC0XV0AqlpaAKZZWQCkWFgAm1VVAJ1d + XQCBTU0Af0xMAK9rawCIU1MA/pycAM6AgADFfX0A/6OjAMJ8fABxSEgAtnV1AMV/fwC8fHwAl2RkAM6J + iQD/rKwAsHh4AKNvbwDCiIgA/7a2AK58fACMZGQAWUBAAJdubgDJlZUAoXd3AFxERABVPz8A/7+/AMyZ + mQC2iYkAxpaWAL+SkgCshYUA3qysALqQkADMn58Aw5iYALaOjgD9x8cAz6OjAKF/fwCXd3cA5ra2AK+L + iwCmhoYA7sHBANmxsQDSrKwAw6CgALmZmQDMqakAln19AIRvbwCtkpIAm4ODAPfS0gDFp6cAqI+PAL6i + ogCymJgA2Lm5ANO2tgCPe3sAj3x8AIx5eQDmyMgAempqAN7ExADXvr4A7dTUAM65uQBpXl4A69TUAOfS + 0gDXxMQAxLOzAPTi4gCmm5sA2c7OAHt1dQBwa2sA9ezsAMvGxgCgnJwAUlBQAGFfXwDi3t4APz4+AIOB + gQBBQEAAcG9vAGxrawD6+fkA9vX1ANLR0QDFxMQAraysAKWkpACdnJwAjo2NAJ+MiwCkjIoAzbSyAH5x + cACPgoEAp5WTANS/vQCvnpwAeG9uAE46NwC9trUAuK2rAEtBPwCFdnMAh3l2AElEQwCflZMAemRfAIRz + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHx8fHx8NQ8PFwAAAAAA////////AAD///////8AAP// + +H///wAA///gH///AAD//4AD//8AAP/+AAD//wAA//gAAB//AAD/4AAAB/8AAP+AAAAA/wAA/gAAAAA/ + AAD4AAAAAA8AAOAAAAAAAwAAgAAAAAADAAAAAAAAAAMAAAAAAAAAAwAAAAAAAAADAAAAAAAAAAMAAAAA + AAAAAwAAAAAAAAADAAAAAAAAAAcAAAAAAAAAAwAAAAAAAAADAACAAAAAAAMAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAMAAAAAABwAAwAAAAAAPAADAAAAAAB8AAMAAAAAADwAAwAAAAAAPAADAAAAAAA8AAMAA + AAAADwAAwAAAAAAPAADAAAAAAA8AAMAAAAAADwAAwAAAAAAPAADAAAAAAA8AAMAAAAAADwAA+AAAAAAP + AAD/gAAAAA8AAP/+AAAADwAA///wAAAPAAD///wAAA8AAP///AAADwAA///8AAAPAAD////wAA8AAP// + ///AHwAAKAAAACAAAABAAAAAAQAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AlisrAKA3 + NwCjPT0AqEdHALtTUwCyUVEA3WpqAMxjYwCWSUkA321tAM5lZQDVaWkAlElJAMxmZgDJZWUAwWFhAKlV + VQCKRkYAxWVlALRdXQCqWloApllZAKRYWACbVVUAnV1dAIFNTQB/TEwAr2trAIhTUwD+nJwAzoCAAMV9 + fQD/o6MAwnx8AHFISAC2dXUAxX9/ALx8fACXZGQAzomJAP+srACweHgAo29vAMKIiAD/trYArnx8AIxk + ZABZQEAAl25uAMmVlQChd3cAXEREAFU/PwD/v78AzJmZALaJiQDGlpYAv5KSAKyFhQDerKwAupCQAMyf + nwDDmJgAto6OAP3HxwDPo6MAoX9/AJd3dwDmtrYAr4uLAKaGhgDuwcEA2bGxANKsrADDoKAAuZmZAMyp + qQCWfX0AhG9vAK2SkgCbg4MA99LSAMWnpwCoj48AvqKiALKYmADYubkA07a2AI97ewCPfHwAjHl5AObI + yAB6amoA3sTEANe+vgDt1NQAzrm5AGleXgDr1NQA59LSANfExADEs7MA9OLiAKabmwDZzs4Ae3V1AHBr + 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 + PQCoR0cAu1NTALJRUQDdamoAzGNjAJZJSQDfbW0AzmVlANVpaQCUSUkAzGZmAMllZQDBYWEAqVVVAIpG + RgDFZWUAtF1dAKpaWgCmWVkApFhYAJtVVQCdXV0AgU1NAH9MTACva2sAiFNTAP6cnADOgIAAxX19AP+j + owDCfHwAcUhIALZ1dQDFf38AvHx8AJdkZADOiYkA/6ysALB4eACjb28AwoiIAP+2tgCufHwAjGRkAFlA + QACXbm4AyZWVAKF3dwBcREQAVT8/AP+/vwDMmZkAtomJAMaWlgC/kpIArIWFAN6srAC6kJAAzJ+fAMOY + mAC2jo4A/cfHAM+jowChf38Al3d3AOa2tgCvi4sApoaGAO7BwQDZsbEA0qysAMOgoAC5mZkAzKmpAJZ9 + fQCEb28ArZKSAJuDgwD30tIAxaenAKiPjwC+oqIAspiYANi5uQDTtrYAj3t7AI98fACMeXkA5sjIAHpq + agDexMQA176+AO3U1ADOubkAaV5eAOvU1ADn0tIA18TEAMSzswD04uIAppubANnOzgB7dXUAcGtrAPXs + 7ADLxsYAoJycAFJQUABhX18A4t7eAD8+PgCDgYEAQUBAAHBvbwBsa2sA+vn5APb19QDS0dEAxcTEAK2s + rAClpKQAnZycAI6NjQCfjIsApIyKAM20sgB+cXAAj4KBAKeVkwDUv70Ar56cAHhvbgBOOjcAvba1ALit + qwBLQT8AhXZzAId5dgBJREMAn5WTAHpkXwCEc28AloaCALinowDDv74AcWNfAKihnwBpVU8Al42KAK6M + gABhWFUAWFVUAOfk4wAtKCYAraimAEs5MQBXRT0ARDYwAGxbUwB4aWIAYE5FADcvKwCNi4oA6efmAFNA + NgBUTEcAlJOSAMvKyQDk5OMA3t7dALi4twAAAAAA3+fnADs9PQB6fX0AxcnJAFxeXgBUVVUAQkNDAHt8 + fADr7OwA6uvrAOjp6QCYmZkAJCwuACsvMAAiJSYAMD5EACozNwBlcHUAOkRJAExOTwAvOD0AUVhcAERF + RgCRlpwA1d7/AAAz/wAHOP8AFUT/ACNP/wAsV/8ATXH/AFx8/wB/mf8Am6//ALHA/wBpgPIAxM79AM3V + +wDm6v0A2t/8APX2/AB6ensA/f39APPz8wDt7e0A5+fnAODg4ADb29sA2NjYANXV1QDT09MAz8/PAMzM + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFVAAADSSQkB0QiIg8+IxodOyIZNDYjGVE3IRZ0NCIYny4p + JtwvODz8XV1d/7i4uP/X19f/V1dX/4qKiv/Nzc3/ysrK/7Kysv9nZ2f/r5aW/zMzM/8zMzP/MzMz/zMz + M/9KRET/pYuL/8Wiov9vYGD/JCos/iQgHvcwHBLeMhwQwTMcEaQzHRKCNB4TXjMfFEEyHxMpNyEWFysr + FQwzMwAFAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAABQEBABDk5HAk2KCgTOiQdIzgnGjs5IhpaNiAWfzMn + H7UvLy3oO0VJ/3V1df/Nzc3/1tbW/9PT0//Pz8//nZ2d/7CwsP+Xl5f/Tk5O/3Fxcf9sbGz/n4yM/zMz + M/8zMzP/MzMz/zMzM/8zMzP/MzMz/zc2Nv9tX1//zaio/66Rkf8+Q0X/ISEh+ykeGe0zGw/NMh0QsjMc + EZIzHhJvNB0TTzIeFDM1IxIdMCAQEEAgIAhVAAADAAAAAUBAQARGLi4LNywhFz4sHyk4Jh5EOCQaZDch + Foo1KyjHMzU18lJdYf+QkJD/1dXV/9bW1v/S0tL/zs7O/8vLy/+1tbX/a2tr/1xcXP+fn5//ubm5/7a2 + tv90dHT/koOD/zMzM/81NTX/OTk5/zs7O/88PDz/PT09/z4+Pv8/Pz//QEBA/1lTU/+zlpb/2bGx/11e + YP8kKiz+JSAd9TAcEt0zHBC/MhwRojIeEn81HxRbMSEVPjIcFSQwICAQMzMzBTwtHhE7Kh4rOiUbSzoj + Gm43JRubODEu1kBESPlnZ2f/qqqq/9jY2P/V1dX/0dHR/87Ozv/Hx8f/jo6O/19fX/+Ghob/sbGx/7i4 + uP+1tbX/sbGx/62trf9xcXH/gnh4/zMzM/9DQ0P/V1dX/2dnZ/9sbGz/cnJy/3d3d/98fHz/goKC/4eH + h/+NjY3/jo2N/6GTk//YsbH/yaWl/01OUf8hISH7Kx4X6jQcEMs0HRGvMx0SjDMeE18xIRYvMyIiDz0k + GCo4JBpkOSkhpjs4OOJNU1n9fn5+/8fHx//Y2Nj/1NTU/9HR0f/Nzc3/sbGx/3d3d/9vb2//ra2t/7u7 + u/+4uLj/tLS0/7CwsP+srKz/qamp/6ampv9ubm7/dXBw/zMzM/9DQ0P/V1dX/2dnZ/9sbGz/cnJy/3d3 + d/98fHz/goKC/4eHh/+NjY3/kZGR/5aWlv+elpb/oYqK/72bm//tv7//eHV3/zEzNP0nHxvzLxwT1DId + EJ0yIBVXMSEZHz86OnFAQ0fdZXB1/5iYmP/W1tb/1tbW/9PT0//Q0ND/ycnJ/5OTk/9vb2//mZmZ/7q6 + uv+6urr/t7e3/7Ozs/+vr6//q6ur/6ioqP+lpaX/oqKi/56env9zc3P/UlJS/0FAQP86Ojr/UFBQ/2dn + Z/9sbGz/cnJy/3d3d/98fHz/goKC/4eHh/+NjY3/kZGR/5aWlv+hmZn/spmZ/8OZmf/Lp6f/8cPD//fH + x/+5mJj/OD0//y0eFcwzHxRzLyQYK2VlZf+rq6v/pqam/9bW1v/T09P/z8/P/7Kysv94eHj/hISE/7a2 + tv+9vb3/ubm5/7a2tv+ysrL/rq6u/6urq/+np6f/pKSk/6Ghof+qqqr/2dnZ/+np6f/X19f/vb29/319 + ff9MS0v/SUhI/0VERP9ISEj/ZGRk/3d3d/98fHz/goKC/4eHh/+NjY3/kZGR/5aWlv+hmZn/spmZ/8OZ + mf++np7/47m5/+q+vv/xw8P/lHx8/ywdF9ozHxSCMiMZM3BwcP/Hx8f/dHR0/8XFxf+Tk5P/hoaG/6ur + q/+/v7//vLy8/7i4uP+0tLT/sbGx/62trf+qqqr/p6en/6Ojo/+np6f/ysrK/+fn5//g4OD/2NjY/7u7 + u/94eHj/zs7O/9PT0//S0tL/vLy8/3t7e/9OTU3/UU5O/0tJSf9NTU3/c3Nz/4eHh/+NjY3/kZGR/5aW + lv+hmZn/spmZ/8OZmf+0l5f/1K+v/9u0tP/kurr/jnh4/ywdFuAyHhSPNB4WO3BwcP+2trb/hoaG/5eX + l/+/v7//vr6+/7u7u/+4uLj/tLS0/7CwsP+srKz/qamp/6ampv+kpKT/vLy8/9/f3//n5+f/3t7e/93d + 3f/c3Nz/2tra/9ra2v/X19f/1tbW/9TU1P/T09P/0tLS/9HR0f/S0tL/srKy/2dnZ/9IRkb/VFBQ/1JP + T/9dXV3/hISE/5aWlv+hmZn/spmZ/8OZmf+mjo7/x6am/86rq//Ur6//hnNz/yYgHO0yHxWVNSAYP2pq + av+3t7f/wcHB/76+vv+6urr/t7e3/7Ozs/+vr6//rKys/6mpqf+lpaX/s7Oz/9TU1P/p6en/5eXl/+Pj + 4//i4uL/4eHh/+Dg4P/e3t7/3t7e/9zc3P/W1tX/0c/P/87My//Mysn/zczL/9DPz//S0dH/0tLS/9HR + 0f/Q0ND/rKys/2loaP9ST0//Yltb/1lUVP9nYmL/oYqK/8OZmf+bhob/tpub/8mysv/OsbH/gG9v/yYg + HO0yHxWVNSAYP29vb/+9vb3/ubm5/7a2tv+ysrL/rq6u/6urq/+np6f/rq6u/8rKyv/m5ub/6+vr/+rq + 6v/o6Oj/5ubm/+bm5v/h4eH/0dHR/7+/v/+9vb3/u7u7/7Kysv+tq6n/nJeU/5GKhv+Oh4T/k42K/5uY + lv+gnp3/o6Kh/6WkpP+amZn/raen/7+3t//Kw8P/q6mp/2loaP9RTU3/Z19f/2BXV/+QgID/p5GR/9fL + y///////xL29/yYgHeozHxSNNR8WOm9vb/+1tbX/sbGx/62trf+rq6v/qKio/8HBwf/i4uL/6+vr/+vr + 6//u7u7/7e3t/+zs7P/l5eX/1NTU/8XFxf/Dw8P/1NTU/+Pj4//n5+f/9fX1//r6+v+HenL/a1pR/31s + ZP91Zl3/g3Vu/5qPi/+poZ7/uLOx/87Kyf/f3dz/4+Hg/+Lc3P/Yzc3/0ri4/9q7u//TxcX/mpqa/1xb + W/9fV1f/inx8/6CNjf/6+Pj/1N3//6ako/EzIBV3MSEWL29vb/+tra3/qqqq/7q6uv/c3Nz/5eXl/8LC + wv+ampr/lJSU/5ycnP+rq6v/uLi4/8DAwP/ExMT/y8vL/9bW1v/j4+P/6Ojo/+zs7P/x8fH/9fX1//r6 + +v+KYWH/Tz8+/0BDQ/9dYF//UDsw/19NRP9sXFT/f3Jt/5ySjv+1ran/ysXC/+Hd3P/p5+b/5uXk/+La + 2v/oxMT/5cjI/83Nzf+3t7f/lpaW/1lXV//Y1tb/MVr///f5//+akY1+MSEZH3V1df/T09P/8vLy/3R0 + dP9DQ0P/Nzc3/zc3N/9BQUH/TExM/1JSUv9jY2P/f39//4yMjP+bm5v/p6en/7Ozs/+/v7//zMzM/93d + 3f/u7u7/9fX1//r6+v/8mZn/olhY/8JkZP+cWVn/XElJ/1hRT/9GQkD/Tzov/2FPRf9zY1r/in12/6mg + m/+8tLH/xsC+/+3s6//x7u7/4r69/8fGxf+YmJj/pqWl/3d3d//P0dH/I0///5ur9//u7eytOycUDWdu + driam5v+WVlZ/5OTk//ZsbH/vXt7/8J8fP+wcnL/p52d/6iFhf9/Zmb/SEFB/zo6Ov9VVVX/cHBw/4OD + g/+Ojo7/n5+f/6urq/+2trb/x8fH/97e3v//nJz/pllZ/81mZv/JZWX/ekpK/9a/v//g4eH/qKio/4CA + gP9RSkb/Uz4z/2dVS/93Z1//xb67//P0/v/o4+L/gXl2/15YVv9HS03/KiwtzyQiIZL7+/v0ADP//ypV + ///+/v7gVQAAAzMiIg8kJCFpRERE///////GgID/a2dn/5Vxcf/FfX3/69PT/8Ohof/MmZn/nXl5/5l4 + eP+riIj/Z1lZ/0E+Pv9GRkb/YGBg/3p6ev+Hh4f/l5eX/6Ojo///np7/pllZ/8xmZv/MZmb/gU1N/97D + w//49fX/+f///9/n5//Jy8v/tLS0/4+Pj/+XkY7/8/T+/01x///Oysj9Lh4W5DAeFsE2HhN9OiEWVtLN + y5Hm6v3/ADP//wc4////////AAAAAC4XFwsjIiFjVVVV///////FfX3/vXt7/8J8fP/FfX3/4r6+/9TD + w//MmZn/toqK/7aKiv+/kZH/c2Fh/8+np//Eo6P/jHx8/1ZQUP8+PT3/UFBQ/2tra///oKD/pllZ/8xm + Zv/MZmb/gE1N/8etrf/Wv7//1IWF/+exsf/25+f/8PDw/9jY2P/6+vr/XHz//ypV///q6Oj/dWhj9W9d + Vd13Zl3Gn5KMu/r5+fZpgPL/ADP//wAz////////AAAAADMaGgojIiFiVVVV///////FfX3/xX19/8V9 + ff/FfX3/0ZmZ/+Lb2/+9kZH/zJmZ/8yZmf/Hlpb/pX9//82env/PpaX/0ays/9O1tf/Vu7v/u6ys/4V+ + fv//oqL/pllZ/8xmZv/MZmb/gE1N/7OZmf++pKT/vFNT/8dlZf/nu7v/+Pj4//7+/v9cfP//ADP//xVE + ///U3f//1N3//9Td///U3f//zdX7/2mA8v8AM///ADP//yNP////////AAAAADMaGgojIiFiVVVV//// + ///FfX3/xX19/8V9ff/FfX3/w39//511df+FcXH/cGtr/3xxcf+qiIj/w5SU/8yZmf/NnJz/zqSk/9Cr + q//Ts7P/x66u/3dwcP//pKT/pllZ/8xmZv/MZmb/gE1N/62Tk/+njY3/uFJS/75WVv/isLD//////5uv + //8AM///ADP//wAz//8AM///ADP//wAz//8AM///ADP//wAz//8AM///ADP//2OC//////+fAAAAADMa + GgojIiFiVVVV///////FfX3/xX19/8V9ff+ldXX/knp6/8SVlf/Ak5P/uY+P/6yIiP+TfHz/hG1t/3Zs + bP+TfHz/tY+P/8uhof/Qqan/zays/6uTk///pqb/pllZ/8xmZv/MZmb/gE1N/8KoqP+pjo7/sUtL/7hS + Uv/r1dX/scD//wAz//8AM///ADP//wAz//8AM///ADP//wAz//8AM///ADP//wAz//8AM///MVr///// + //////8gAAAAADMaGgojIiFiVVVV///////FfX3/xX19/7J4eP+KeHj/xpaW/8CTk/+5j4//souL/6qI + iP+jhIT/oH9//8N9ff+9e3v/mnJy/3tra/96cXH/m4eH/8Olpf//qKj/pllZ/8xmZv/MZmb/gE1N/9rB + wf++paX/qUND/7FLS//Yubn/8/T+/yNP//8AM///ADP//wAz//8AM///ADP//wAz//8AM///ADP//wAz + //8jT///8/T+/+nm5GwAAAAAAAAAADMaGgojIiFiVVVV///////FfX3/xX19/4BwcP/FlZX/vpKS/7iP + j/+4p6f/sJmW/7Kbm/+agID/lX19/556ev/FfX3/xX19/8J8fP+jeHj/uIaG/5d9ff//q6v/pllZ/8xm + Zv/MZmb/gE1N/+7V1f/Turr/oz09/6lDQ/+wfX3/4+Pj/+bq/f8HOP//ADP//wc4//9/mf//f5n//3+Z + //9/mf//qrv//9vi///t6un1rKKchzMaDRQAAAAAAAAAADMaGgojIiFiVVVV///////FfX3/t3l5/3Vt + bf++kpL/uI6O/3ViXN9uXlWfmoR5ts2amv+dhob/jXl5/4Z2dv+/fHz/rXd3/415ef/Ilpb/v5KS/7eO + jv//ra3/pllZ/8xmZv/MZmb/gE1N/+3U1P/ix8f/lisr/6A3N/+jb2//qqqq/+rq6v/a3/z/Bzj//ypV + ///79fX/5rOz/6ygoP/ms7P/5rOz/8KNjf8zGg2vMxoNUDMaDRQAAAAAAAAAADMaGgojIiFiVVVV//// + ///GhIT/q3t7/4x5ef+4jo7/sYuL/1I9NcZAKR1qRCwhitTBu/+skZH/hnZ2/35ycv+5gID/fG9v/8eW + lv+/kpL/uZGR/7aXl///r6//pllZ/8xmZv/MZmb/gU1N/+bOzv/o1tb/uHBw/6dPT/+tfn7/oqKi/6ys + rP/x8fH/wcr6/ypV///57Oz/zGZm/1lAQP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAADMa + GgojIiFiVVVV///////IjIz/uoaG/3NsbP+xi4v/qIeH/2hXUfc5IhWxaFZM0tC3sP+pkJD/fnJy/3hv + b/+kfn7/lX19/7+Skv+3jo7/g3Rt25uGfcr/sbH/pllZ/8xmZv/MZmb/dUlJ/+a7u//39vb/+O3t//Hd + 3f/d0ND/vLy8/6Kiov+xsbH/9fX1/8bR///46+v/zGZm/1lAQP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMa + DRQAAAAAAAAAADMaGgojIiFiVVVV///////KlJT/ypSU/25qav+oh4f/oYOD/5uBgf+iioL9rox//rR3 + d/+IfX3/eG9v/3Bra/+Senr/oYOD/7aOjv+FcnD5PycbxmBNRVz/s7P/pllZ/8xmZv/MZmb/iVBQ/2lT + U/91dnb/lp2d/8LIyP/i6ur/7Ovr/8nKyv+rrKz/t7i4//z8/P/16Oj/zGZm/1lAQP/MZmb/zGZm/6ZZ + Wf8zGg2vMxoNUDMaDRQAAAAAAAAAADMaGgojIiFiVVVV///////NnZ3/zZ2d/6uLi/+NeXn/mX9//5J8 + fP+LeHj/hXV1/31xcf92bm7/cGtr/4t5ef+ZgYH/knx8/6+Kiv+Kenr9OyIX4UEoHsT/trb/pllZ/8xm + Zv/MZmb/wmNj/65aWv+pWlr/nV1d/5JlZf+TeXn/jIKC/4eJif+GiYn/iYyM/7Kzs//ap6f/0mho/1lA + QP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAADMaGgojIiFiVVVV///////PpaX/z6Wl/8+l + pf+Ke3v/jHl5/4t4eP+FdXX/fXFx/3Zubv9vamr/a2ho/8afn/+9mpr/hXZ2/6eGhv+gg4P/i317/IV0 + bvT/uLj/pllZ/8xmZv/MZmb/1mlp/9lqav/YaWn/zmVl/7xcXP+pU1P/j0lJ/4lOTv+FWVn/fF1d/31q + av+cYmL/pVlZ/1Q+Pv/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAADMaGgokIyJhVVVV//// + ///Mqan/hHBw/9Gtrf/Rra3/j4KC/4J1df98cXH/dm5u/29qav98dXX/yKen/9Gtrf/Rra3/enR0/5uA + gP+Yf3//kXt7/5OFhf//urr/pllZ/8xmZv/MZmb/ilBQ/35ERP+USUn/tVdX/89iYv/caGj/4G1t/99r + a//PZGT/tltb/5pQUP9yRkb/akZG/3NJSf/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAAEAg + IAghIyFcT09P/8/Pz//LtbX/yKys/9O2tv/Ttrb/07a2/9O2tv/Ttrb/07a2/9O2tv/Ttrb/07a2/9O2 + tv/Ttrb/vqam/3JsbP+Penr/iHd3/4Bzc///vLz/pllZ/8xmZv/MZmb/XEFB/8SXl/+omZn/lHd3/45n + Z/+IWFj/hUJC/5ZJSf+xVlb/w15e/9loaP/da2v/3Wxs/9tra//MZmb/zGZm/6ZZWf8zGg2vMxoNUDMa + DRQAAAAAAAAAADMzAAUgIiNTMzMz/7+/v/+cnJz/kYmJ/6KWlv/EsLD/0ru7/9W+vv/Vvr7/1b6+/9W+ + vv/Vvr7/1b6+/9W+vv/Vvr7/1b6+/62env9qaGj/fXFx/3dvb///vr7/pllZ/8xmZv/MZmb/WEBA/+bI + yP/o6+v/2Nra/8fKyv+1urr/qqGh/6KMjP+eenr/mWpq/65kZP+rWlr/qlVV/75hYf/MZmb/zGZm/6ZZ + Wf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAAAAIiJyEaGyMn8SkzOf9RUVH/VFRU/2VlZf9wcHD/cHBw/3Jy + cv+Bfn7/o5qa/8Cysv/Jurr/18bG/9fGxv/Xxsb/18bG/9fGxv/Xxsb/xLa2/8W3t///wMD/pllZ/8xm + Zv/MZmb/WUBA/+PKyv/Kysr/1dXV/+Pj4//09fX////////////m6Oj/xcjI/6eoqP+Uior/e2dn/3FI + SP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAAAABVAAADIiYgGx8jJWMeIySXHSMmzyAq + LvQkLzL/KTM5/y8+RP9UVFT/ZWVl/25ubv9wcHD/cHBw/4F/f/+emZn/qqOj/8e+vv/Zz8//2c/P/9nP + z///wsL/pllZ/8xmZv/MZmb/WUBA/+HHx//S0tL/0NDQ/8PDw//Hx8f/x8fH/8rLy//a2tr/6Onp//n6 + +v///////////1U/P//MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAAAAAAAAAAAAAAAisr + KwYzGhoKMyIRDzkcDhIrIh0mISIhWB8jJJceIiW2HiUp3CErL/8pMzn/LDg+/zI+Rf9lZWX/bm5u/3Bw + cP9wcHD/eHh4/4eGhv//xMT/pllZ/8xmZv/MZmb/WUBA/+DGxv/BwcH/wMDA/8DAwP/IyMj/wsLC/8PD + w//Gxsb/w8PD/8rKyv/Pz8///v7+/1lAQP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAJVAAADKysrBjMaGgo3JBIOLR4eETMaGhQhISFGICIjeR8j + JJodIybPICou9CUwNf8rNjv/Lz5E/zZDTP//xsb/pllZ/8xmZv/MZmb/WUBA/9/Fxf/X19f/y8vL/8vL + y//Ly8v/ysrK/9jY2P/U1NT/1NTU/8/Pz//Hx8f/+vr6/1lAQP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMa + DRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAgAA + AAJAQAAEQCAgCCsrFQwzIhEPNhsbEyUhHzYiISJaHyMkmB0jJcL/yMj/pllZ/8xmZv/MZmb/WUBA/93D + w//FxcX/wMDA/8bGxv/Gxsb/xcXF/8XFxf+3t7f/xMTE/76+vv/Pz8//+fn5/1lAQP/MZmb/zGZm/6ZZ + Wf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAABAAAAAkBAAAQrKysGMxoaCjckEg7/ysr/pVhY/81m + Zv/MZmb/WUBA/9vCwv/S0tL/xMTE/8TExP/CwsL/ysrK/9PT0//Y2Nj/4eHh/9PT0//b29v/+Pj4/1lA + QP/MZmb/zGZm/6ZZWf8zGg2vMxoNUDMaDRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAA + 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 + ov+SkpL/fHNz/0ZCQv8zMzP/MzMz/zMzM/9BPj7/dWVl/3ZmZv9IRUT7MSYg8TMcD9c1HhLBMx8VnjMf + F2Q1IRc+NiYbJjEkGBEAAAAAPigfQjsmHFs8Jh2XRTs412lqbfeampr/1dXV/9TU1P/Ozs7/t7e3/5eX + l/+lpaX/t7e3/7Gxsf90bm7/Q0FB/0tLS/9cXFz/ZGRk/2tra/9zc3P/a2tr/4F4eP+Qe3v/dnR1/jEr + KPcyIBXjNB0QyzgiF6w0IRd0MSQWLgAAAABALSW6TEVE5YyOkfu8vLz/2NjY/9PT0//Jycn/oaGh/6Gh + of+3t7f/tbW1/7CwsP+rq6v/pqam/2dlZf9APj7/UFBQ/2VlZf9ubm7/d3d3/39/f/+IiIj/kJCQ/5iY + mP+Xhob/oISE/5iWlv9UTkz7MiUe6jQdEbkyIRdjdXqCn4+Umv/Q0ND/19fX/9HR0f+1tbX/np6e/66u + rv+6urr/tLS0/6+vr/+qqqr/pKSk/6ysrP/R0dH/mJiY/1lYWP9DQkL/S0tL/2ZmZv93d3f/f39//4iI + iP+QkJD/mJiY/62Zmf+tiIj/676+//bGxv+koKD/MiQc2zMgFnOTl53/kpKS/8fHx/+oqKj/p6en/76+ + vv+4uLj/s7Oz/66urv+pqan/qqqq/8fHx//q6ur/39/e/66trf+Uk5L/zs3N/8HAwP+PjY3/V1ZW/0pI + SP9cW1v/gYGB/5CQkP+YmJj/rZmZ/62IiP/Srq7/3ra2/8ynp/8yIxvfMyEVd4mOlf+lpaX/uLi4/729 + vf+3t7f/srKy/62trf+qqqr/vb29/+Dg4P/o6Oj/4ODg/93d3P/V09P/ysfG/8PAvv/Cv73/xsTD/8nH + x//Lysr/v76+/5KRkf9RT07/TEpK/2tra/+jkJD/rYiI/8asrP/Qs7P/uZqa/zIkG94zIRV2nqOr/7u7 + u/+2trb/sbGx/6ysrP+2trb/1NTU/+zs7P/q6ur/6Ojo/+Xl5f/V1dX/wcDA/7azsv9cSUD/Tzsx/1lH + P/92a2f/ioSA/5mWlP+koaD/qaam/721tP+1sbH/gH5+/11ZWf9WTk7/sJ2d/9TGxv/Jubn/MiQc1jMh + F26hpq7/sLCw/7Ozs//Jycn/3d3d/9fX1//W1tb/4+Pj/9zc3P/Nzc3/zc3N/9/f3//m5eT/m2lp/2NJ + SP9fTUr/RTIp/0o3Lf9VQTf/Z1hQ/5CGgf/Nycf/4+De/9zX1v/bxMP/3MHA/7Szs/+Tj4//KlX///// + //9lXFjHMiQYUpqepv/Ly8v/fX19/15UVP9PQ0P/TkhI/1BQUP9eXl7/hoaG/52dnf+tra3/wMDA/9LS + 0f/nior/xF9f/6RTU/+XhIT/qaqo/3BsaP9SQzv/STIo/1I+NP94amL/7Orq/9DLyf/RsrH/nZqZ/3V0 + c//b4v//Y4L//9rZ2MI2Ihseen+IUJWWl/SkpKT/vn5+/41vb//Nj4//zLKy/7aKiv97Y2P/VExM/09P + T/9ycnL/iYiI/96IiP++Xl7/sVdX/7ydnf/57e3/5tnZ/9PT0/+mpqX/aF9Z/8O8uf+bq/f/n5WP/0k2 + Lv9TRkLhop6c0Nrf/P8qVf//+vn5xDMaGggAAAAAMDI0nd3d3f/FfX3/rXd3/8aAgP/Rxsb/x5aW/7+Q + kP+mgID/zqOj/66UlP95bWz/3YmJ/71eXv+yWVn/oIaG/8ORkf/NaWn/9dvb//L09P/r6+v/m6v3/yNP + ///Vz87/o5WR+q2infT29PT7FUT//ypV///+/v7AAAAAAQAAAAAwMjSa3d3d/8V9ff/FfX3/xX19/8eh + of+JdHT/noKC/8OUlP/MmZn/zaCg/8uoqP/fjYz/vV5e/7NaWv+bgID/rHt7/7lJSf/nwcH//////5ur + 9/8HOP//I0///yNP//8jT///I0///wAz//8AM///Y4L//////58AAAAAAAAAADAyNJrd3d3/xX19/8V9 + ff+fenr/xpaW/7yRkf+wior/kXt7/41ycv+UfX3/so+P/9+Ojv+9XV3/r1ZW/7Samv+4h4f/r0FB/8+r + q/+bq/f/ADP//wAz//8AM///ADP//wAz//8AM///ADP//yNP////////////MAAAAAAAAAAAMDI0mt3d + 3f/FfX3/qHh4/8aWlv+8kZH/uZeX/6+Skv+cgID/onx8/8R9ff+tdHT/3o+P/71dXf+uVVX/ya+v/8uY + mP+dLCz/roqK/+Tn5/+bq/f/I0///yNP//9/mf//f5n//3+Z///b4v///fz88u7s61cAAAAAAAAAAAAA + AAAwMjSa3d3d/8V9ff+FcXH/vJGR/4RvavpXQzjSkm9i26GRkf+SeHj/wXx8/452dv/fkpL/vV1d/65U + VP/Jrq7/4ry8/6hHR/+lgoL/qq6u/+np6f+bq/f/ADP//9/R0f/jsrL/162t/5aJg+lzYVg1MxoNCgAA + AAAAAAAAAAAAADAyNJrd3d3/x4eH/4Rycv+yi4v/inRx/TgfE8yEbGLsopiY/4Zzc/+IdHT/vZCQ/9+V + lf++XV3/rlhY/51/f//Q1dX/49zc/+He3v+5urr/srKy//Hy8v+bq/f/v6Oj/8ZkZP+vW1v/NRwP1zYd + ECgzGg0KAAAAAAAAAAAAAAAAMDI0mt3d3f/LlJT/k3t7/6WFhf+agID/pZaR/rSYmP97cnL/f3Jy/3Zt + bf+zior/3ZWV/7tbW//JZWX/nVVV/5JeXv+cfX3/rKCg/5+goP+MkZH/p6ur//Tb2//ApKT/xmRk/69b + W/81HA/XNh0QKDMaDQoAAAAAAAAAAAAAAAAwMjSZ3d3d/86goP/Lnp7/hnd3/456ev+EdXX/eW9v/29q + av+ojIz/d29v/6aEg//dmJf/ultb/8xnZ/+8W1v/w1dX/8ZZWf+2VFT/p1ZW/5hcXP+DXl7/jFZW/2pF + Rf/JZWX/r1tb/zUcD9c2HRAoMxoNCgAAAAAAAAAAAAAAAC8yNJfb29v/z6ys/7+fn//Oq6v/n4yM/4N4 + eP+Bd3f/sJiY/9Gtrf/Bo6P/fHBv/96amv+/XFz/qFhY/4toaP+ifn7/omho/6RZWf+sUFD/vVdX/8Va + Wv+3W1v/vGFh/89nZ/+vW1v/NRwP1zYdECgzGg0KAAAAAAAAAAAAAAAALzQ2kJKSkv+blpb/w62t/8yz + s//Uurr/1Lq6/9S6uv/Uurr/1Lq6/9O5uf+/qKj/3Zyc/79cXP+eT0//sZ6e/+Pn5//P1NT/xsnJ/8C0 + tP+wlpb/qHl5/59dXf+nWFj/z2dn/69bW/81HA/XNh0QKDMaDQoAAAAAAAAAAAAAAAAvMzdaRk1T8UtT + W/9UVFT/ZWVl/25ubv+Bfn7/koyM/6mfn//GuLj/1MPD/9LAwP/fn5//v1xc/59RUf+tmpr/1dTU/9DP + z//b29v/4OHh/+Hl5f/e5OT/2Nra/4Bubv/GX1//r1tb/zUcD9c2HRAoMxoNCgAAAAAAAAAAAAAAAEBA + AAMrKxUKMDExPzAyMmUvMzaUPEJHzENKUOZGTlX/UVFR/1tbW/9nZ2f/cG9u/92hof+/XFz/oFFR/6yZ + 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 + + + \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.Properties.Resources.resources" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.Properties.Resources.resources" new file mode 100644 index 0000000..06c24d0 Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.Properties.Resources.resources" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.csproj.FileListAbsolute.txt" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.csproj.FileListAbsolute.txt" new file mode 100644 index 0000000..51ebb5d --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.csproj.FileListAbsolute.txt" @@ -0,0 +1,8 @@ +E:\work\2011-5-18\文件格式研究\MTF\BackupReader\BackupReader\bin\Debug\BackupReader.exe +E:\work\2011-5-18\文件格式研究\MTF\BackupReader\BackupReader\bin\Debug\BackupReader.pdb +E:\work\2011-5-18\文件格式研究\MTF\BackupReader\BackupReader\obj\Debug\ResolveAssemblyReference.cache +E:\work\2011-5-18\文件格式研究\MTF\BackupReader\BackupReader\obj\Debug\BackupReader.frmMain.resources +E:\work\2011-5-18\文件格式研究\MTF\BackupReader\BackupReader\obj\Debug\BackupReader.Properties.Resources.resources +E:\work\2011-5-18\文件格式研究\MTF\BackupReader\BackupReader\obj\Debug\BackupReader.csproj.GenerateResource.Cache +E:\work\2011-5-18\文件格式研究\MTF\BackupReader\BackupReader\obj\Debug\BackupReader.exe +E:\work\2011-5-18\文件格式研究\MTF\BackupReader\BackupReader\obj\Debug\BackupReader.pdb diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.csproj.GenerateResource.Cache" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.csproj.GenerateResource.Cache" new file mode 100644 index 0000000..3eac12c Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.csproj.GenerateResource.Cache" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.exe" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.exe" new file mode 100644 index 0000000..d595fec Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.exe" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.frmMain.resources" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.frmMain.resources" new file mode 100644 index 0000000..bbe5350 Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.frmMain.resources" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.pdb" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.pdb" new file mode 100644 index 0000000..61c75a6 Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/BackupReader.pdb" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/TempPE/Properties.Resources.Designer.cs.dll" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/TempPE/Properties.Resources.Designer.cs.dll" new file mode 100644 index 0000000..5093ed0 Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader/obj/Debug/TempPE/Properties.Resources.Designer.cs.dll" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader_src.zip" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader_src.zip" new file mode 100644 index 0000000..92387f3 Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/BackupReader_src.zip" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/UpgradeLog.XML" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/UpgradeLog.XML" new file mode 100644 index 0000000..0a7d284 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/UpgradeLog.XML" @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport.css" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport.css" new file mode 100644 index 0000000..3411f63 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport.css" @@ -0,0 +1,207 @@ +BODY +{ + BACKGROUND-COLOR: white; + FONT-FAMILY: "Verdana", sans-serif; + FONT-SIZE: 100%; + MARGIN-LEFT: 0px; + MARGIN-TOP: 0px +} +P +{ + FONT-FAMILY: "Verdana", sans-serif; + FONT-SIZE: 70%; + LINE-HEIGHT: 12pt; + MARGIN-BOTTOM: 0px; + MARGIN-LEFT: 10px; + MARGIN-TOP: 10px +} +.note +{ + BACKGROUND-COLOR: #ffffff; + COLOR: #336699; + FONT-FAMILY: "Verdana", sans-serif; + FONT-SIZE: 100%; + MARGIN-BOTTOM: 0px; + MARGIN-LEFT: 0px; + MARGIN-TOP: 0px; + PADDING-RIGHT: 10px +} +.infotable +{ + BACKGROUND-COLOR: #f0f0e0; + BORDER-BOTTOM: #ffffff 0px solid; + BORDER-COLLAPSE: collapse; + BORDER-LEFT: #ffffff 0px solid; + BORDER-RIGHT: #ffffff 0px solid; + BORDER-TOP: #ffffff 0px solid; + FONT-SIZE: 70%; + MARGIN-LEFT: 10px +} +.issuetable +{ + BACKGROUND-COLOR: #ffffe8; + BORDER-COLLAPSE: collapse; + COLOR: #000000; + FONT-SIZE: 100%; + MARGIN-BOTTOM: 10px; + MARGIN-LEFT: 13px; + MARGIN-TOP: 0px +} +.issuetitle +{ + BACKGROUND-COLOR: #ffffff; + BORDER-BOTTOM: #dcdcdc 1px solid; + BORDER-TOP: #dcdcdc 1px; + COLOR: #003366; + FONT-WEIGHT: normal +} +.header +{ + BACKGROUND-COLOR: #cecf9c; + BORDER-BOTTOM: #ffffff 1px solid; + BORDER-LEFT: #ffffff 1px solid; + BORDER-RIGHT: #ffffff 1px solid; + BORDER-TOP: #ffffff 1px solid; + COLOR: #000000; + FONT-WEIGHT: bold +} +.issuehdr +{ + BACKGROUND-COLOR: #E0EBF5; + BORDER-BOTTOM: #dcdcdc 1px solid; + BORDER-TOP: #dcdcdc 1px solid; + COLOR: #000000; + FONT-WEIGHT: normal +} +.issuenone +{ + BACKGROUND-COLOR: #ffffff; + BORDER-BOTTOM: 0px; + BORDER-LEFT: 0px; + BORDER-RIGHT: 0px; + BORDER-TOP: 0px; + COLOR: #000000; + FONT-WEIGHT: normal +} +.content +{ + BACKGROUND-COLOR: #e7e7ce; + BORDER-BOTTOM: #ffffff 1px solid; + BORDER-LEFT: #ffffff 1px solid; + BORDER-RIGHT: #ffffff 1px solid; + BORDER-TOP: #ffffff 1px solid; + PADDING-LEFT: 3px +} +.issuecontent +{ + BACKGROUND-COLOR: #ffffff; + BORDER-BOTTOM: #dcdcdc 1px solid; + BORDER-TOP: #dcdcdc 1px solid; + PADDING-LEFT: 3px +} +A:link +{ + COLOR: #cc6633; + TEXT-DECORATION: underline +} +A:visited +{ + COLOR: #cc6633; +} +A:active +{ + COLOR: #cc6633; +} +A:hover +{ + COLOR: #cc3300; + TEXT-DECORATION: underline +} +H1 +{ + BACKGROUND-COLOR: #003366; + BORDER-BOTTOM: #336699 6px solid; + COLOR: #ffffff; + FONT-SIZE: 130%; + FONT-WEIGHT: normal; + MARGIN: 0em 0em 0em -20px; + PADDING-BOTTOM: 8px; + PADDING-LEFT: 30px; + PADDING-TOP: 16px +} +H2 +{ + COLOR: #000000; + FONT-SIZE: 80%; + FONT-WEIGHT: bold; + MARGIN-BOTTOM: 3px; + MARGIN-LEFT: 10px; + MARGIN-TOP: 20px; + PADDING-LEFT: 0px +} +H3 +{ + COLOR: #000000; + FONT-SIZE: 80%; + FONT-WEIGHT: bold; + MARGIN-BOTTOM: -5px; + MARGIN-LEFT: 10px; + MARGIN-TOP: 20px +} +H4 +{ + COLOR: #000000; + FONT-SIZE: 70%; + FONT-WEIGHT: bold; + MARGIN-BOTTOM: 0px; + MARGIN-TOP: 15px; + PADDING-BOTTOM: 0px +} +UL +{ + COLOR: #000000; + FONT-SIZE: 70%; + LIST-STYLE: square; + MARGIN-BOTTOM: 0pt; + MARGIN-TOP: 0pt +} +OL +{ + COLOR: #000000; + FONT-SIZE: 70%; + LIST-STYLE: square; + MARGIN-BOTTOM: 0pt; + MARGIN-TOP: 0pt +} +LI +{ + LIST-STYLE: square; + MARGIN-LEFT: 0px +} +.expandable +{ + CURSOR: hand +} +.expanded +{ + color: black +} +.collapsed +{ + DISPLAY: none +} +.foot +{ +BACKGROUND-COLOR: #ffffff; +BORDER-BOTTOM: #cecf9c 1px solid; +BORDER-TOP: #cecf9c 2px solid +} +.settings +{ +MARGIN-LEFT: 25PX; +} +.help +{ +TEXT-ALIGN: right; +margin-right: 10px; +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport.xslt" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport.xslt" new file mode 100644 index 0000000..142057a --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport.xslt" @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ 解决方案: + 项目: + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + src + + + + + + + + + + + + +
文件名状态错误警告
+ javascript:document.images[''].click()src + + + + 已转换 + + + + 已转换 + +
+ + 个文件 + + + 1 个文件 + + + 已转换:
+ 未转换: +
+
+
+ + + + : + + + + + + + + + 转换报告 + <xsl:if test="Properties/Property[@Name='LogNumber']"> + <xsl:value-of select="Properties/Property[@Name='LogNumber']/@Value"/> + </xsl:if> + + + + +

转换报告 -

+ +

+ 转换时间:
+

+ + + + + + + + + + + + + + + + + + + + + + + + +

+ + + + + +
+ 转换设置 +

+ + +
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Minus.gif" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Minus.gif" new file mode 100644 index 0000000..17751cb Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Minus.gif" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Plus.gif" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Plus.gif" new file mode 100644 index 0000000..f6009ca Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/BackupReader/_UpgradeReport_Files/UpgradeReport_Plus.gif" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/MTF_100a.pdf" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/MTF_100a.pdf" new file mode 100644 index 0000000..9f1debd Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/MTF_100a.pdf" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/CONTACT.HTM" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/CONTACT.HTM" new file mode 100644 index 0000000..19fc4ec --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/CONTACT.HTM" @@ -0,0 +1,41 @@ + + +

Contact Info

+You are welcome (encouraged) to send me personal email regarding topics discussed on +my web pages. If you don't mind please include at least you country and possibly +you state/country/territory. With some of my more obscure software I get mail from +all over and its interesting tracking the source. Please send email +to the following address:
+will_kranz at SoftHome.net

+Sorry, you've got to cut and paste the address above. I've +been getting way too much junk email and have been told to +remove automatic HTML addresses such as

+someone
because +automated systems scan web pages looking for these. My user name and +server above are separated by the string " at " which must be replace by '@' +to create a valid address. Sorry to +make you do a little extra work, but maybe this will cut down +on the amount of spam I recieve.

+ +If interested, you can obtain more information about fighting spam at the +following locations:
+CAUSE
+Junkbusters
+Spam-l discussion group
+Spam Recycling
+ +

WARNING

+If you are emailing me some solicitation I have not requested, please +don't. I've registered with Junkbusters and will attempt to +charge people who do send me unsolicited solicitations. See the +registered terms and conditions +under which I will accept unsolicited email of this nature.

+You won't find my phone number or postal address on these pages, +but I feel the same way about +telephone and mail solicitations. + + + + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/GET_FOFF.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/GET_FOFF.C" new file mode 100644 index 0000000..b2b97d3 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/GET_FOFF.C" @@ -0,0 +1,87 @@ +/* get_foff.c 3/30/05 developed in ffndstr.c then moved here. + see tst_foff.c for a driver routine, plan use as generic + module for HAS_INT64 code in dumpit and ntbkup.c + Always display in hex wwith the leading 0x + Always look for leading 'h' in string for hex. + if there do as hex and check for HAS_INT64, otherwise only + treat as a long. + + The MSVC runtime library docs say printf (and presumably scanf) + accepts I64 for int64 as well as l for long in format spec + + gcc's libc C seems to have no such built in support so just + truncate quadword decimal input for these. +*/ +disp_foffset(FOFFSET pos) +{ + unsigned long *dw = (unsigned long *)&pos; + printf("0x"); +#ifdef HAS_INT64 + if(*(dw+1) > 0) + printf("%lx",*(dw+1)); // print high order portion +#endif + printf("%lx",*dw); + +} + +FOFFSET get_foffset(char *str,FOFFSET *pos) +{ + int suc=0,cnt=0,i,nul; + char *num=str, ch = toupper(*str); + *pos = 0; // clear it, may only set low bits below + if(ch == 'H') + { + str++; // skip initial q + while(*(str+cnt) != 0) + { + ch = toupper(*(str+cnt)); + if((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') ) + cnt++; + else + break; + } +// if(cnt > 16 just truncate like sscanf(%lx) does + if(cnt <= 8) + i = 0; + else + i = cnt - 8; + suc = sscanf(str+i,"%lx",(unsigned long *)pos); // set low order DWORD + + cnt = i; // chars remaining + if(cnt && suc) // more than 8 bytes in input string + { +#ifndef HAS_INT64 + printf("\nWarning, input truncated to 32 bits\n"); +#else + nul = i; + ch = *(str+nul); + *(str+nul) = 0; // temporarily terminate + if(cnt <= 8) + i = 0; + else + i = cnt -8; + suc = sscanf(str+i,"%lx",(unsigned long *)pos+1); // set high order DWORD + *(str+nul) = ch; // restore str + if(i & suc) + printf("\nWarning, input truncated to 64 bits\n"); +#endif + } + } + else + { +#if defined(HAS_INT64) && defined(_WIN32) + suc = sscanf(str,"%I64u",pos); // unsigned long long +#else // truncate to 32 bits + suc = sscanf(str,"%lu",(unsigned long *)pos);// unsigned long +#endif + } + if(suc == 0) + { + printf("\nfailed to parse string: %s",num); + exit(-1); + } + else + return(*pos); +} + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.CL" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.CL" new file mode 100644 index 0000000..a87fe8e --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.CL" @@ -0,0 +1,11 @@ +#macro defines for microsofts MSC 6.0 +#same as VC 5.0 execpt for the DELIM +EXE = exe +OBJ = obj +DEL = del +DELIM = ; +CC = cl /c /AL +LNK = link + +#for debugging /Zi recommended in CC, and /CO required in LNK + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.QCL" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.QCL" new file mode 100644 index 0000000..2ee517e --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.QCL" @@ -0,0 +1,8 @@ +#macro defines for Microsofts QC25 environment +EXE = exe +OBJ = obj +DEL = del +DELIM = ; +CC = qcl /c /AL +LNK = qlink + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.VC" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.VC" new file mode 100644 index 0000000..8428c06 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MACRO.VC" @@ -0,0 +1,8 @@ +#macro defines for Microsofts VC 5.0 command line compiler +EXE = exe +OBJ = obj +DEL = del +CC = cl /c +LNK = link + + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBACKUP.HTM" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBACKUP.HTM" new file mode 100644 index 0000000..f9eb152 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBACKUP.HTM" @@ -0,0 +1,1531 @@ + + + + + + + + +

Win9x & XP & NT MSBackUp File Format and Data Recovery

+
+Created:     10/02/03
+Last Update: 03/02/07
+Current Versions: MSQIC.exe  1.11     NTBKUP.exe  1.07c
+
+

Contents

+Short Support Rant
+Introduction & Disclaimer
+Norton's MSBackup (supplied with MSDOS 6.xx)
+Win98 *.QIC MSBackUp's File Format
+Win95 *.QIC MSBackUp File Format
+Disastor Recovery? version of *.QIC MSBackUp File Format
+Multi-Volume *.QIC MSBackup issues
+QIC Data Decompression
+Large File Issues, the 2 Giga Byte file limit
+NTBackUp's *.BKF Data File Format
+Data Recovery from a *.QIC file with MSQIC
+Data Recovery from a *.BKF file with NTBkUp
+Multi-Volume *.BKF Backup issues
+Sample Program Output
+Downloads available & Version history
+MSBackUp FAQ
+Acknowledgements
+Links to related programs and information + +

Support Rant

+I guess its not surprising that one gets a fair amount of email when +you put something like this out for free. My target audience was other programmers +or IT professionals, although I'm happy for anyone who benifits. +I did it cause I was interested in the file formats, not to make money. +However, as indicated near the end of the download section, my return on invested time is currently around $0.10 per hour. PLEASE READ THE DOCUMENTATION AVAILABLE +before asking me a question. I spent a fair amount of time writing the
Sample Output section which has syntax examples and the output they should produce. This was written in the hope of minimizing repeated questions. +Much of the mail I get would not have been sent if this section had been +read, and its getting tedious! I admit to being over 50 years old, so I'm more +comfortable with a command line interface than apparently many of today's users. +If you don't know how to use a consol application in Windows, please do some +research elsewhere before asking me. If you are really desperate and need +a holding hand, then pay me for my time (minimum rate is $50/hour). I'm currently +underemployed and need the money! Please do not compliment me on the free software +and then ask me to do your job for you.

+ +Beginning in 2004 the amount of email I recieve on this subject has decreased. +I suspect most people who might get burned by these 'great' MS applications have +resolved their problems. I am happy to give anyone a couple of free support +emails. I have also charged a few people for extracting files from, or +'fixing' corrupted backup files. In general this has worked well, and I include +some acknowledgements, however I've also been stiffed for several +days work so if you need something like this I now have a money in front policy.

+ +I also spend half my time at the end of a dirt road with a slow telephone link. +Please to not send large attachements without discussing it in advance. Recently +a well intentioned user sent me some *.jpg screen images of his output, this would +have been painful if I had been on the dirt road when I recieved them.
+End of Rant +

+ +

Introduction to MSBackUp

+This page discusses some of the internal structures I've observed/discovered +in the backup programs distributed with the Win32 versions of +Microsoft's Windows Operating systems. It also introduces two freely +available programs I wrote based on this information to recover data +from backup archive files (*.QIC and *.BKF) created with these Windows +backup programs. As of 2004 I've made the source code for these +programs available under the GNU Public License.

+ +I took a look at Win32 Backup Programs after coming across a FreeDos +project. +The project page above says there were several incompatible +MSDOS versions of BACKUP. Apparently none of these +are compatible with the Win32 programs described below, and +worse I currently know of three mutually incompatible Win32 +backup programs: NT, XP, and Win 2000 use NTBackup while Win95 and Win98 +used different versions of MSBackup. By default NTBackup produces a *.BKF +file and both versions of MSBackup produce *.QIC files, +but the *.QIC internals are sufficiently different that these files +can only be used with the program with which +they were created. Thanks again guys, that's real helpful in a +backup program!

+ +If one does a search for 'QIC Data Recovery' you will find a number of +people have backed up their Win9x data, and installed a newer OS, ie XP +or Win 98, only to find they can't restore the data without going back to the +older OS. My MSQIC attempts to solve this problem. +I found a third party +article +which discusses Win9x vs WinXP MSBackUp incompatibility.
+Note: sorry I believe the URL above is correct, but sometimes it redirects +for some reason. If above doesn' take you to 'Quarter-Inch Cartridge' +go to 'Q' in the index, then pick QIC.
+Also a
review +which may be of interest and is definately a fun read.

+ +I couldn't resist looking at the more recent NTBackup +issues and also created a program that will restore files from these archives. +It turns out both my programs are useful for data recovery. Apparently passing +one of these backup archives around a network can cause minnor corruption such +that even the originating program no longer recognizes the file, yet my +more simplistic approaches are perfectly happy recovering the data.

+ +Additional information is available from +Microsoft +which confirms the NTBackUp incompatibility with Win32 *.QIC formats. +It seems odd, but this Microsoft Knowledge Base article seems +to say the NT and XP versions of NTBackUp are different. Apparently none +of the new NTBackUp programs support QIC tapes +as the Win9x versions did. The NTBackUp version that comes with XP (but not NT) +can read an uncompressed *.QIC file image. The Microsoft article says +NTBackUp uses a different file compression algorithm. So if you compressed +your archive or are running NT, Microsoft can't help you. +Another third party article about +XP Home +edition suggests it is not so easy to find or install NTBackUp if you +don't have the Professional edition.(Search for NTBackUp on page above to +find the relevant section)

+ +The general solution for recovering data from a *.QIC archive seems to be +you should take the file to a Win9x machine running the OS the archive was +created with and use its standard MSBackUp program if you despreately need +to recover the older archive. This is not bad advise, just not very convenient.

+ +I have not been trapped by this problem. I either backup the original files to CDROM, or +use a Unix compatible TAR program whose format hasn't change significantly +in something like 20 years. However I was curious about the data +file format and I did the exploration described below. At this time +I understand the general layout of both *.QIC and *.BKF files. I only +know how to decompress *.QIC files. As of 2004 I'm making both executable +versions and source code available under the GNU Public License. +Binary distributions for +MSDOS, WIN32, and Linux are now freely available. However I still +ask that you give me feedback so I can improve these programs +(ie fix bugs!). +

+ +I don't imagine there will be a lot of interest in this, but if +you have gotten this far and are interested please send me +a note. +I'd be interested in looking at your sample archive data if these programs +do not work properly. In particular I do not have either WinXP nor NT +so I can not create my own *.BKF archives. +People have sent me several small uncompressed *.BKF archives as samples +that I've used to verify this work. Please DO NOT send large files +without checking, I don't always have a high speed connection nor +a lot of disk space. Its apparently not possible to activate +the compression option when backing up to a file, and I only deal with *.BKF +files.

+ + +

WARNING: all information presented below are GUESSES done by inspection. +Use this information and the associated programs at your own risk. +I do NOT claim they are correct nor accept liability for your use of them. +

+ + +

Win98 QIC Backup File Format

+The data structures described below were obtained by inspection of +*.qic BackUp files produced by the program below which came +with my WinME operating system. More information about this BackUp +program is supposed to be available from the vendor,
Seagate. However when I tried +the URL I was redirected to a site that doesn't seem to have much +to do with this product. Seagate Software was apparently purchased +by Veritas Software in 1999. +The 'About' box in the help menu displays the following information: +
+Microsoft Backup
+Version 4.10.1397
+Distributed by: Seagate Software, Inc.
+Copyright 1998 Seagate Software, Inc. All rights reserved
+
+ + +Note after about 10 days looking at this WinME format, I find differences +between it and my Win95 BackUp programs *.qic files. +The major and minor version numbers from the VTBL header from the WinME program +in the *.qic files discussed below are 0x5341 and 0x49. +

+ + +The Win9x version of MSBackUp clearly has some relationship to the QIC format +specifications which +are available on-line. I'd done some work on this previously as I +have some QIC80 tape drives. However I quickly found the MSBackUp +*.qic file format is significantly different. I am using the +structure definitions below to attempt to describe what I have +learned regarding the *.qic file format. See the msqic.h file in the +source code archive for the most recent information.

+ +

+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+
+
+// from pp9 of QIC113G, 
+struct qic_vtbl {
+BYTE tag[4]; // should be 'VTBL'
+DWORD nseg; // # of logical segments
+char  desc[44];
+DWORD date; // date and time created
+BYTE flag;  // bitmap
+BYTE seq;   // multi catridge sequence #
+WORD rev_major,rev_minor; // revision numberrs
+BYTE vres[14]; // reserved for vendor extensions
+DWORD start,end; // physical QFA block numbers, in WIN98 and WINME
+                 // these point to start volume data and dir segments
+BYTE passwd[8]; // if not used, start with a 0 byte
+DWORD dirSz,     // size of file set directory region in bytes
+      dataSz[2]; // total size of data region in bytes
+BYTE OSver[2];   // major and minor #
+BYTE sdrv[16];   // source drive volume lable
+BYTE ldev,       // logical dev file set originated from
+     res,        // should be 0
+     comp,       // compression bitmap, 0 if not used
+     OStype,
+     res2[2];       // more reserved stuff
+};
+
+/* If its a compressed volume there will be cseg_head
+   records ahead of each segment (in both catalog and data segments).  
+   The first immediately follows the Volume Table area
+   For the sake of argument, lets assume per QIC133 segments are
+   supposed to be < 32K, ie seg_sz high order bit isn't required.
+   Its used as a flag bit, set to indicate raw data.  IE do not 
+   decompress this segment.  Use seg_sz to jump to the
+   next segment header.
+*/
+#define SEG_SZ  0x7400  // Segment Size = blocking factor for *.QIC file
+#define RAW_SEG 0x8000  // flag for a raw data segment
+
+struct cseg_head {
+DWORD cum_sz,   // cumlative uncompressed bytes preceeding this segment
+      cum_sz_hi;// normally zero. High order DWORD of above for > 4Gb
+WORD  seg_sz;   // physical bytes in this segment, offset to next header
+                // typically 40% -50% of bytes which will be decompressed
+};
+
+
+// see section 7.1.3 of QIC 113 Spec for directory info, does not match below
+
+// DATA_SIG only if in data region 
+struct ms_dir_fixed {
+WORD rec_len;   // only valid in dir set
+DWORD ndx[2];   // thought this was quad word pointer to data? apparently not
+                // ndx[0] varies, ndx[1] = 0, was unknow[8]  
+                // in data section always seems to be 0xffffffff
+WORD path_len,  // @ 0xA  # path chars, exits in catalog and data section
+                // however path chars only present in data section
+     unknww1;   // 0xA  always?
+BYTE flag;      //  flag bytes
+WORD unknww2;   // 0x7  always?
+DWORD file_len; // @ 0x11 # bytes in original file
+BYTE unknwb1[20],  // was flags[0x18] but attrib at flags[20]
+     attrib,
+     unknwb2[3];   
+DWORD c_datetime, // created
+      unknwl1,    // always 0xFFFFFFFF?
+      a_datetime, // accessed
+      unknwl2,    // always 0xFFFFFFFF?
+      m_datetime, // modified, as shown in DOS
+      unknwl3;    // so can be expanded? always 0xFFFFFFFF?
+WORD nm_len; // length of the long variable length name
+};
+// var length name, case sensitive, unicode
+
+struct ms_dir_fixed2 {
+BYTE unkwn1[13];   // was [0x15];  this region fairly constant
+DWORD var1;        // these vars change file to file
+DWORD var2;
+WORD  nm_len; // length of 2nd, short ie DOS, variable length name
+};
+
+// var length name, always upper case => DOS, unicode
+// if in data region path follows, not in directory set
+//     var length path per ms_dir_fixed.path_len, unicode
+
+// BOTH ms_dir_fix* structures must be packed!
+
+/* Bitmap defines for flags
+   below seem to work with my current ms_dir_fixed.flag
+   don't seem to match QIC113G
+
+   Note there are a LOT of undefined bits below.  Wonder what they might be?
+*/
+#define SUBDIR   0x1  // this is a directory entry, not a file
+#define EMPTYDIR 0x2  // this marks an empty sub-directory
+#define DIRLAST  0x8  // last entry in this directory
+#define DIREND   0x30  // last entry in entire volume directory
+#define DAT_SIG  0x33CC33CCL // signature at start of Data Segment
+#define EDAT_SIG 0x66996699L // just before start of data file
+/* EDAT_SIG is immediately followed by a WORD before the
+   actual data stream starts.  No idea what this is, in my
+   sample files its been 0x7.  I ignore it
+*/
+
+#define BASEYR 1970 // uses unix base year and elapsed seconds in time
+
+ +Starting from the top, the file begins with a standard QIC113 +volume table per struct qic_vtbl. There is at least one VTBL tag entry +followed by a second Microsoft specific MDID tag and data block +to terminate the volume table. Most of the fields conform to +the QIC specification, however bit 5 of the flag byte is not set +although the directory set does seem to follow the data region. +I'm not clear if the size fields conform or not (can't tell +from my reading of the spec). dataSz looks like the number +of uncompressed bytes used for the data region. dirSz is +the number of bytes from the start of the directory to the +end of the file. The volume table header area normally +contains 256 bytes, one VTBL region and one MDID region. However if +multiple drives are contained in the archive there is one VTBL for +each drive. In a compressed volume these records are immediately followed by +10 bytes for the first struct cseg_head.

+ +Note to find the beginning of the data or directory (catalog) segements +use the qic_vtbl start and end fields. Subtracting 3 from each of +these produces the number of SEG_SZ segments before the start of +the data. Ie a value of 3 implies data starts immediately after the +MDID region. See also the discussion of how this is done for +WIN95 archives. The WIN95 logic works for +single volume WIN98 and WINME archives. + +Following this header region where entries have a 128 byte block size, +the remainder of the file is broken up into segments of 0x7400 bytes (SEG_SZ). +All Win98/ME archives I've seen do not compress the 1st segment, +nor the catalog segment(s), thus these files will always be at least +59648 bytes long. Data compression is discussed +in detail later, but is done on a segment by segment basis.

+ +The first data region segment immediately follows the VTBL header region. +In a compressed volume the sum of the bytes in the VTBL header region ++ dataSz generally takes one well past EOF, ie dataSz always +represents the uncompressed data length. Without compression, for my sample +files, the VTBL header region size + dataSz falls significantly +short of the beginning of the directory set because the last segment +is rarely full. In Win98/ME the dirSz is the physical size of the segment(s), +but in Win95 it is the amount of space used in the segment(s).

+ +The time stamps MSQIC displays for the individual files +in the archive look correct. However the time stamp for the VTBL +creation time, data, was off by two years into the future. I added +a corrective fudge factor, but its odd. In the process of +trying to figure out this time stamp issue and address why +MSBackUp won't recognize my output files, I looked at the +second volume table region with tag = 'MDID'. A sample dump +follows: +

+ 00080: 4D 44 49 44  4D 65 64 69  75 6D 49 44  34 35 37 33 |MDIDMediumID4573
+ 00090: 38 31 33 31  39 30 30 38  35 30 36 38  37 37 34 B0 |813190085068774.
+ 000A0: 56 52 30 31  30 30 B0 43  53 42 36 44  37 B0 46 4D |VR0100.CSB6D7.FM
+ 000B0: 32 B0 55 4C  64 6F 68 65  6C 70 2D 74  73 74 B0 44 |2.ULdohelp-tst.D
+ 000C0: 54 33 46 37  39 37 32 44  44 B0 FF FD  FE F0 B0 00 |T3F7972DD.......
+ 000D0: 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00 |................
+ 000E0: 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00 |................
+ 000F0: 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00 |................
+ 
+It appears to be a series of id tags followed by ascii strings, +except that the string terminator is 0xB0. My best guess at +the id tags is as follows: +
+Tag        used as
+MDID     - vtbl tag
+MediumID - unique 19 decimal digits for identification
+VR       - version? always 0100
+CS       - ? followed by 4 hex bytes
+FM       - ? always followed by '2'?  format?
+UL       - user label, ascii input string
+DT       - datetime of archive creation as 8 hex bytes
+
+The DT string seems to be in the same format as the file time stamps. +It matches the time stamp of the archive file within +/- 10 hours. +The difference is still puzzling, but much closer than the +VTBL.datetime. Possibly just a timezone issue? The CS tag +is a puzzle, it varies without any logic I can determine. +Nor can I figure out how the Unique MediumID value is generated. +Either of these could be the problem in getting MSBackUp to +recognize my files, but I just don't get it.

+ + + +Each directory entry contains two or more variable length strings. The general format +is similar to the QIC113 specification but the internal structure +is significantly different as indicated by my ms_dir_fix* structures +above. Every field with a name starting with 'unknw' has an unknown +function, ie I have a significant amount to learn! But the ones +I do understand should be enough to reconstruct a file at its original +location. The directory contains repeats of the following: +

+  {  struct ms_dir_fixed,
+     variable length long file name,
+     struct ms_dir_fixed2,
+     variable length short (MSDOS) file name,
+     a path string (may be empty)
+  }
+
+The discussion below relates to how this information is arranged in +the directory (catalog) region of the file. As mentioned its slightly +different in the data region of the file where it is duplicated. +The first field, rec_len, in ms_dir_fixed, is the length of +the entire variable length block so one can read the entire +thing or break the read up into segments. ms_dir_fixed contains most +of the key file information and is followed by the long filename. +This is in turn followed by ms_dir_fixed2 and the MSDOS 8.3 format +sort file name. Both structures contain a nm_len field +which is the number of data bytes in the variable length string which immediately follows +the structure. This length field may be zero as it seems to be for the +root directory. The names appear to be in unicode, in my samples +every second byte in the name is zero. They are not NUL terminated. +As indicated in the structure definition above, the path string at the +end only exists in the data region, not the catalog region. path_len +may also be zero representing an empty string.

+ +The key data is in the ms_dir_fixed structure. Its flag field is +a bitmap which uses the defines {SUBDIR,LASTDIR,ENDDIR} above. +The meaning is consistent with the QIC113 specification, but the +bit values are different. As indicated in the structure definition +the file length, time stamps {creation, modification, last access}, +and file attributes have been identified. The attribute byte +matches the standard MSDOS attributes. The names in the directory +listing appear to be in alphbetic sorted order (case sensitive) based on +the long file name for each subdirectoy containing a file. +I have not yet identified the link from the +directory to the individual files in the data section. However the +order in the directory set seems to match the order in the data region. +Ie one can determine the files location in the data region by an appropriate +summation of the file and header bytes. Also note that a compressed archive +file has struct cseg_head records embedded in its directory region even though +the region is not compressed (the RAW_SEG bit is set in the seg_sz field).

+ +The structure of the data region is similar, but each entry additionally +contains the DAT_SIG and EDAT_SIG signature fields and if the entry +represents a file rather than a directory is followed by the file data. +Per the comment following EDAT_SIG above, there +is also a WORD value between the EDAT_SIG and file data that I ignore. +Note there is more information in the directory set fields than the data set +fields for equivant structures. My guess is that additional information is added as +the file is opened and read. Then MSBackUp updates the structures +and writes them to the directory set (the catalog in MSBackUp terms). +In particluar for Win98/ME the first 10 +bytes of the ms_fixed_dir are always 0xFF. Therefore if one were +to attempt to directly parse the data set regions (ie for emergency +recovery per msqic -x) the rec_len and ndx[] fields are not valid. Also as mentioned +above, the data region is the only place the data path string occurs, +when looking at the directory set region one must generate the path from +preceeding subdir entries.

+ +I had someone point out that MSBACKUP *.qic files can span multiple volumes +of media. The person I talked to had 3 Zip disks in a single archive. I +have duplicated this behavior with floppy disks. I only tried it once, and +created an archive that filled the 1st floppy and spilled over onto a 2nd. +There as a *.qic file on each that was consistent, ie MSQIC recognized them. +The first had the flags set indicating "Volume spans multiple cartidges" +and the second did not. The catalog for the first only included the files +that were in the archive on that disk. The catalog for the second included +all the files in both archive files. It is not apparent to me how one would +know which of the files in this catalog were on the prior disk. Again one +expects this information to be in some of the fields I don't understand, +but I have no idea where! +

+ +For that matter, I expect a linkage from the catalog to the data region, +but just don't see it. There are a number of fields in the struct ms_dir_fixed +that I do not understand, but nothing jumps out at me regarding this linkage. + + +

Win95 QIC Backup File Format

+I had read a couple review articles that suggested the Win9x +family all used the same *.qic format. This doesn't appear +to be the case as I just did a trial with my Win95 machine and +its a little different! Won't you know. The two programs +do not recognize each others output.

+ +The major and minor version numbers from the VTBL header from my Win95 program +are 0x71 and 0x6. The about box for the Win95 program displays: +

+ Microsoft (R) Backup
+ Windows 95
+ Developed by Colorado Memory Systems
+ a division of Hewlett-Packard corporation
+
+ +In General the Win98 structures described above are valid, but they +are arranged slightly differently. +The data section starts immediately after the first and only +128 byte VTBL record. There is no VTBL record tagged MDID. +If there are multiple drives in the backup, they are all in the +same VTBL section with different subtrees for the different volumes. +The lack of an MDID record is probably sufficient reason for the Win98/ME +version to reject the Win95 data, but I found a couple other small differences. +The segment compression algorithm seems a little different in that all +data segments are compressed and the RAW_SEG flag never occurs in +the data section of a Win95 archive +(at least it wasn't in the one file I looked at...). +The Win95 data section format includes 18 extra bytes (3 pairs of +the EDAT_SIG each followed by one WORD) after a subdirectory, and +12 extra bytes (2 of the 3 subdir groups) after a file's data. Although +the format of the catalog directory entries is the same, the Win95 +program names the root node(s) by volume lable and drive letter +while WinME leaves the root name empty and has a different VTBL for each +volume. +

+ +The value of VTBL.dirSz is also different. For Win95 its seems to be the +actual size of the directory data in the directory segment. +In the WinME archives dirSz was the offset +back into the file from EOF to the start of the directory data. +In WIN95 VTBL.end field does not point to the first directory segment +and the dirSz field is the number of actual bytes used by the directory rather +than the total number of bytes in the directory segments. The following algorithm +is used to find the directory data in WIN95 archives, and also works for a later +WIN98 and WINME archives which only contains a signle volume (one VTBL entry) +and therefore only have one catalog region. +

+If archive is NOT compressed 
+   sz = 29696 = SEG_SZ
+else
+   sz = 29686 (leave space for a cseg_head)
+cnt = VTBL.dirSz/sz
+if(VTBL.dirSZ % sz) cnt = cnt + 1 (increment if there is a remainder)
+seek back cnt * sz bytes from EOF
+
+Ie always back up an integer # of segments segment based on the amount of +space required for the directory and seg_head records +which occur at the start of all segments if its a compressed archive. +

+ +Just for fun I decided to back up multiple drives in a single +archive. The Win95 program did what makes sense to me, it +put all the drives in one archive. WinME +conversely makes a new VTBL entry for each drive. It appears to +create one VTBL region at the beginning of the file containing a separate +entry for each drive. These WinME +archives are concatenated together as a data region +and catalog, one per drive. In my simple test cases this made for a very large, +sparsely populated archive as 6 segments were required (two +per drive). WinME fills in the vtbl.sdrv[] and .ldev +fields for each drive whereas Win95 leaves them blank as they +can be associated with multiple drives which is indicated in the +catalog of a Win95 archive. + + +

Disastor Recovery? version of *.QIC MSBackUp File Format

+In early February of 2004 I was contacted by the first *.QIC user I've talked +to who has an archive larger than 4Gb. Its a pretty strange archive compared to +the discussion above, but does show a lot of common features. I have not +studied this in detail as it is the only case I've found so far, but I'm +open to more input.

+ +The biggest difference is that the entire first segment is filled with MDID +blocks with no VTBL blocks. File data starts in the 2nd segment. The first +'file' appears to be a detailed ascii description of the backup options. It does +not have a valid file name in the definition block and pretty clearly +describes the backup. A few typical lines are shown below, I've added line feeds +for readability. +

+<BACKUP_COMPONENTS xmlns="x-schema:#VssComponentMetadata" 
+version="1.0" 
+bootableSystemStateBackup="yes"
+selectComponents="yes"
+backupType="full">
+<WRITER_COMPONENTS instanceId="02dc7a92-fa7a-42cd-a16c-56b5ebe2b1dc"
+writerId="a6ad56c2-b509-4e6c-bb19-49d8f43532f0">
+<COMPONENT componentName="WMI" componentType="database"/>
+</WRITER_COMPONENTS>
+<WRITER_COMPONENTS instanceId="0d56dab1-a14b-43dc-a8cc-70efa3104c18"
+writerId="f2436e37-09f5-41af-9b2a-4ca2435dbfd5">
+<COMPONENT componentName="COM+ Registration Database"
+.....
+
+The "...." above means it continues like this for quite a while. +Then a fairly standard *.QIC WinME format file follows. +This was a compressed file, but we had to parse the blocks for cseg_headers +to be sure since there were no apparent VTBL records. The 2nd segment, +the first after the last MDID region, was not compressed. Most of the remainding +segments in the file appear to be compressed although we haven't looked at all of them. +However the cseg_head.seg_sz was 0x73F2 for full segments rather than the +0x73F6 value I've seen in the past. After some review it turns out that +there is a 4 byte long word at the end of each compressed segment in this +file. No idea what this is. It doesn't occur in the other *.QIC files I've looked at. +However the current decompression algorithm seems to work fine. We have yet to find a +catalog region, but this is larger than I thought *.QIC files could be, and the current +versions of MSQIC don't handle this case. +An alternate compilation is available and included in the source code distribution +as Avik.c, but it seems rare enough I haven't bothered with a binary distribution. +As described
below I've added 64 bit long integer support to +NTBKUP, and the same logic could easily be added to Avik.c if someone finds another +backup archive of this nature. +

+ +The owner of this file says he believes it was created +"as the output of a disaster recovery as oppopsed to a straight files only backup". +The system on which it was created is long gone. +I don't find this option in my version of MSBackUp, but haven't explored it in detail. +I'm putting this note here in case someone else has this experience. More information +about how to such a file is created would be interesting.

+ + +

Multi-Volume *.QIC MSBackup issues

+I looked at how MSBackup creates multi-volume backups in the summer of 2004 +when someone pointed out that my MSQIC program does not work with multiple +floppy disk based archives. I made a small three volume backup by writing to +720Kb floppies. It was interesting, each of the three disks contained a VTBL, +a data region, and a catalog. However only the first of the three could be +recovered directly with my MSQIC program. +The VTBL of each disk has the multi-volume bit set in the flag byte and the +seq byte set to the sequence number in the series. The data region matches +that of a single volume file. However each successive catalog includes all +the files contained in prior disks in the backup set and the offsets to file data in +the data region are those that would result if the data from all prior disks +had been appended into a single file. They are not the offset into the current +volume, which is why MSQIC fails on all but the first disk.

+ +As proof of concept, I've written a stand alone program, Nseg.c, that will construct +a single decompressed file by successively appending the data regions of successive volumes. On the last volume in the set you tell it to finalize the file by appending +the catalog from this last volume to the end of the new archive. This produces a single +uncompressed file that works with MSQIC. It assumes you have enough space to write +everything to your hard disk. I have not bothered to document this carefully, nor put +the code on this web site as I have had very little interest in MSQIC over the last year. +However if anyone needs this get in touch with me and we'll work something out. +The only person I've actually discussed this with had one floppy in their set which +was corrupted, and this can complicate things... + + + +

QIC Data Decompression

+I seem to have this working. Its often between difficult +and impossible to reverse engineer compression by inspection. +However we seem to be in luck as the authors (Microsoft/Segate?) +were nice enough to set the Volume Table compression +bitmap byte, comp, correctly. Per QIC123D.pdf the +identifier 000001 indicates the standard described +in QIC122B.pdf. These are available from +QIC.org as mentioned near the top of the page.

+ +For the record I can't reproduce the example in QIC122B, I fear +there may be some typos. I have now decompressed a couple sample +archives and have feedback from others who have used my alorithm +successfully, so I'm pretty sure its correct. Interestingly enough the first +segment( roughtly 30Kb) in Win98 is not compressed, but the other segments are. +Conversely Win95 MSBackup seems to compress all the segments in a volume.

+ +A compressed archive is broken up into a series of segments. +I'm not sure why it was done this way as files often cross +the segment boundries. This allows one to decompress subsections +on a segment by segment basis in large archives. +Each segment is preceeded by the 10 +byte cseg_head record shown above. These only occur in +compressed files. The first cseg_head record immediately follows the +Volume table normally at file byte offset 256 for Win98 backups (assuming +there is only one volume) and byte offset +128 for Win96 backups. The cseg_head records form a linked +list of the segments as there are often some unused bytes +at the end of a segment which must be skipped.

+ +In a compressed archive there will be a cseg_head record at the start +of each segment, at increments of SEG_SZ (0x7400) following the end +of the volume table. As mentioned above, the RAW_SEG flag in the +cseg_head.seg_sz field indicates if the data has been compressed. +The first segment of a data region and the catalog segments are not compressed. +One obtains the physical size of the segment data by masking the high +order RAW_SEG bit in the seg_sz field. In the data region, the size will be +0x73F6 if the entire segment is used (10 bytes are used by the cseg_head). +There is always a terminating cseg_head in the data region with seg_sz = 0. +The preceeding cseg_head.seg_sz will be @#60 0x73ED as the terminating header +is always inserted inside a segment and does not occur at the 0x7400 block +boundry. In small archive the first data region cseg_head may point to this +terminating header. If and apparently only if ther terminator occurs in +the first uncompressed segment the first word of the cseg_head.cum_sz field +contains the byte offset from the end of this word to the next cseg_head. +One only needs to know this because normally the terminating cseg_head.cum_sz +is zero, but not when it occurs in the first data segment.

+When you find the terminating header, you are done with the data section of the file. +If you were decompressing the segments as they were traversed, you should have +decompressed the number of bytes indicated in the VTBL.dataSz +field.

+ +Note in a compressed file the catalog segments also have cseg_head records at +the start of each segment, however there is not a terminating record for the +catalog section. All catalog records seem to have a seg_sz = 0xF7F6. +The actual data length is determined by the flags in the catalog data.

+ +I am able to decode each segment independantly +using a slightly modified QIC122B algorithm. With my +sample text files its about a 2:1 compression factor, not +bad for a fairly simple algorithm. I've found one major difference +between the publish specification and practical application. +When copying a string of bytes +from the history buffer, the example in QIC122B uses an +offset to the start of the region to copy which is an absolute +index into the history buffer. MSBackUp apparently uses +a relative offset from the current position in the history buffer +to the start of the data to copy. Care must be taken to +wrap this relative offset back to the end of the history +buffer when required (basically a modulo operation to prevent +a negative index). With this system an offset +of zero is still the termination marker for the algorithm. +As with many compression algorithms, this depends on the +nearby data so you have to decode segments as a unified block, +you can't jump in and start in the middle of a segment. One can +get a handle on which segments contain which files by comparing +the file set directory with the cseg_head records as records in +the directory and data regions occur in the same order. The point +is that if one has a large archive and were desperate you could +unpack portions of it rather than the entire archive. However I +suggest you just get a large spare drive and decompress the hole +thing if you need to play around with the data. +

+ + +

Large File Issues

+ +The default behavior of programs I have created for MSDOS, WIN32, and Linux +is ANSI C compliant. The programs used 32 bit +signed integers for lseek() positioning within a file. This was good enough +for a long time with PCs, but disk capacity and current usage has gone +past this now. Under Linux and WIN32, there are large disk options that allow +64 bit file offsets to solve this problem. I found a nice +review +of the file and disk sizes supported by current file systems. It appears that +the systems that supported MSBackUp and *.QIC files {Win95, Win98, WinME} ONLY +supported FAT32 which has a 4 GB file size limit. This can be handled by +casting the ANSI C return from lseek() to an unsigned long. This is the approach +I've taken in MSQIC. If you have a *.QIC file larger than 4 GB please tell me +about it. How was it created, what operating system? Note see the +section above for a brief discussion of an exception to this +rule! +

+ +When one ventures into NTBackUp *.BKF files one quickly runs into the 4GB boundry. +My MSDOS binary for NTBKUP is limited to 4GB files, but the WIN32 and Linux versions +use long longs for 64 bit file offsets. The part that becomes a little ugly +is displaying such an offset. Although GNU's gcc supports long long format +specifications for printf() its not portable (at least not to MSVC 5.0). +For simplicity long longs are only displayed in hex in these programs.

+ +I'm working on a FAT32 system so I can't test these large file options. +If you have a *.BKF file > 4GB and are interested in this project, please test +it with my code. This code is known to work with smaller archives +on FAT32 systems. Please do NOT send me a sample archive, I have no place to put it! +

+ + +

XP/NTBackUp File Format

+Once again, I feel stupid. To a significant extent I find I have +re-invented the wheel. I spent a significant amount of time reverse +engineering the NTBackUp File format and writing test code. +On 12/22/03 a better researcher than I informed me +that most of the information I presented on my web page was +available in a much nicer and more detailed format on a +web page +by Alan Stewart. This page provides links to a +document, MTF_100a.PDF, which describes the *.bkf file format and a +Linux source code archive for reading MTF backup Tapes (as opposed to disk image files). +The source code for this tape reader is released under the GNU Pulbic license. +Although it was news to me, the file format is apparently offically +known as "Microsoft Tape Format", MTF. The specification above was published in +September of 2000 by Seagate, but the original source seems to have disappeared +from the internet along with the other seagatesoftware.com pages. I wish I'd known +about this document before I started my reverse engineering project! +Thanks to Wolfgang Keller for bringing this information to my attention, and +the author, D. Alan Stewart, for making it all available. +In a private communication Alan Stewart told me there are plans to make +his MTF reader into an open source project at Source Forge. Watch for it. + +

+Once I knew the appropriate name I also found a brief summary + +article by Microsoft that confirms NTBackUp files are MTF compatible. +However no links are provided to the supporting MTF documentation. +More recently, 1/27/2004, a +JAVA MTF Reader was released by Michael Bauer. I have not looked at this +yet, but it seems like a nice cross platform solution to the problem. +

+My structures and trial program only dealt with a semi-functional subset of this +MTF specification. The portions of the MTF document I've reviewed so far +appear to be VERY similar to the *.bkf archive samples I've seen. +Its not exactly light reading but seems to cover everything. +A few high points below: +

+MTF_DB_HDR - section 5.1 describes a common header for the main file 
+             blocks. This maps to my tag_head, although I didn't know 
+             what a lot of it was about.  See the block ID table, for
+             blocks that conform to the common header block checksum rule.
+
+MTF_STREAM_HDR - section 6.1 and 
+Type 1 Media Based Catalog - section 7.3 describe the TFDD catalog region.
+             Note I treat this like the other common blocks, but apparently
+             it is technically a data stream. In the *.bkf files I've seen
+             its position is padded to an 0x400 byte boundry, but there
+             seems to be nothing in the specification to require this.
+
+Format Logical Block - section 3.4.  The logical block has been  0x400 for 
+             the *.bkf I've seen. The specification says 0x200 is also a valid 
+             size and it is defined in the tape header, MTF_TAPE.format.
+
+MTF_TAPE.major version = 1 in *.bkf files I've looked at.
+MTF_SSET.minnor version = 0 These match the version numbers for this document.
+
+MTF_DIRB    - section 5.2.4  There is one of these for each directory
+              backed up on the media.  All MTF_FILE blocks following
+              an MTF_DIRB are located in this directory. They are often LARGE!  
+
+MTF_FILE    - section 5.2.5, maps to my xp_file
+
+MTF_TAPE_ADDRESS - section 4.2 clarifys how to locate the variable
+             length data sections.  I had identified the length field,
+             but not the offset as its the same in all my *.bkf examples.
+
+OS specific data is covered in Appendix A.  Most MTF_DB_HDRs contain a
+pointer to some sort of OS specific data.  This spec talks about NT
+specific data for OS ID = 14 and OS Versions 0 and 1.  The *.bkf files
+I've seen are OS ID 14 with OS Version 2 which is not covered. However 
+the attribute and short name fields seem to be in the same locations 
+(I have not tried to figure out what is different in Version 2).
+
+
+ + +After releasing this, the author of JMTF and I have both discovered that +some regions beginning with the FILE tag do not contain the STAN stream +record. In July of 2004 Geoff Nordli emailed me a sample *.bkf file that +explains this behavior. It appears that empty files are stored without a +STAN stream, so as of version 1.06, if no STAN stream is detected the file +is created with no data which matches the normal behavior of NTBackUp.

+ +Given the MTF information I now see how the entire file can be represented +as a linked list of data elements. Each main block common header has a 'next event' +offset. This either points to the next main block header, or the start of a +chain of stream headers which are linked together in a similar mannor. +The last stream header in a chain points to the next main block header. +I added some of this to my proof of concept program which enhanced its +ability to traverse *.bkf files. In particular finding the start of +each individual file's data is now totally generic and I see there is normally +a checksum after the file data which can be used for validation.

+ +The original section titled "Obsolete Structure Analysis" describing +my reverse engineering has been deleted as the document above is much better. +The only point worth making is that the *.QIC concept of 30Kb segments seems +to have been dropped making the files a little more compact. +

+ + + +

MSQIC, features and limitations

+As proof of concept I wrote a 16 bit MSDOS program that +compiles with MS QC 2.5. I later extended this to compile with +gcc under Linux and MSVC 5.0 as a console application under WIN32. +These are console level applications (no GUI) which will allow one to view key +areas in and extract files from a *.QIC file produced by +Win95 or Win98's MSBackUp program. I'm slowly enhancing the data +recovery options available as I talk to people and see how files get +broken. See the Downloads section for availability.

+ +I believe it decompresses my Win9x MSBackup compressed +archives correctly, ie the data recovered looks like +the original. My original goal was do this decompression and then let +the NTBackUp that comes with WinXP to manipulate entire archives +decompressed with this program. However my WinME MSBackUp doesn't +recognize the decompressed file MSQIC produces so I doubt NTBackup will either. +It must be a very subtle difference as I have done byte comparisons +between MSQIC's output and what MSBackUp produces without compression +and do not see the difference! Its close BUT being off by 1% +is the same as being off by a mile. Sigh.

+ +However MSQIC stands alone and can extract individual files or groups +of files from a sub-directory from either +compressed or uncompressed archives. It can also decompress +the entire archive, and will recognize it afterwards. +Its useful for recovering files you desperately need, or as a testing tool for +examining an archives internals. Or if you are a brave soul, try the -p or @ +options to restore large blocks of the directory tree stored in the archive. +The command line options are shown below: +

+MSQIC Ver 1.11  compiled for OS_STR
+Copyright 2003 William T. Kranz
+...
+
+msqic <file> [@] -p [-x] [-t] [-v]  [-s{c|d}#] [-f{d|e|s}] [-d] [-r]
+ @<cmd> to extract directories based on command file
+-p<path> extract all FILES from ONE path in directory tree
+-x to extract file, nm, using paths in tree structure
+-t[ds] to display catalog as tree, d => directory only, s => with segment info
+-v just display VTBL and exit
+-fd find file id 0x33cc33cc in data
+-fe find file id 0x66996699 in data
+-fs find & display compressed file segments
+-sc# force start catalog (directory set) at hex offset
+-sd# force start data region at hex offset
+-D to decompress archive and write output to dcomp.out
+-d##[:#] to decompress a segment(s) starting at hex offset ## in file
+     use optional hex :cnt to decompress cnt contiguous segments
+-r[filter] attempt raw file data recovery, use -sd to set data region start 
+     use optional filter string, ie *.txt, to limit hits
+
+ +An archive file name must be supplied or you get the display above.
+Under MSDOS it must be a 8.3 style short filename.
+MSDOS systems also only display 8.3 style paths while Linux and Win32 +systems can handle long file names. The OS_STR above indicates +the Operating System the program was compiled for: MSDOS, WIN32, Unix, or CYGWIN. +

+ +By default when run with just a +file name argument the catalog is displayed with each of the +files attributes and the file names truncated to 18 characters so they fit on one line. +Adding -v or -t changes the display as indicated above.

+ +The first options listed, {@, -p, -x, -t}, all depend on a valid catalog +in the *.QIC archive and will fail if it doesn't exist. They parse the +catalog dynamically allocating the directory tree in memory. Large archives +and systems with limited memory could have problems with these. Alternatively +try the -r option that does not depend on the catalog nor directory tree.

+ +The -t option attempts to display the full file name with +indentation below the associated sub-directories to indicate the tree structure +on the disk when the backup was created. There are two additional options which +may follow -t. A 'd' only displays subdirectores (see @ option below) without +the files. An 's' appends numbers after the file name which are the +segment:offset to help you locate a specific file in a compressed segment.

+ +The -x option allows extracting a single file from the archive to the current +directory. It depends on the paths shown via -t, and on all but MSDOS systems +the path and file name search is case sensitive. File time stamps and the +read/write permission attribute are preserved as of version 1.08. +

+ +The -s options allow forcing the file position used for the data and directory regions. +This is required if your file has a corrupted VTBL region (which occurs more often +than you might think) but other parts of the file are in tact. Typically you use +the -f option below to find appropriate values for the -s options.

+ +The -f options search the archive file, display hits, and then exit. +A compressed file is a series of compressed segments, each preceeded by +a struct cseg_head. These segment locations are listed via +the -fs option. -fs accepts an optional start address for the search, +and its only a best guess which doesn't work well unless there are several +segments in the chain. Look at the output to be sure it makes sense. +After finding the beginning of one or more +compressed segments, they can be individually decompressed with +the -d### option (note use a hex offset as displayed by -fs, prior to version +1.09 this was decimal and you had to add 10 bytes to skip the cseg_head).

+ +The -D option attempts to decompress an entire compressed file, or for +Win98 and WinME multi-volume archives, one of the volumes. Using the +-s option in conjunction can help when the VTBL is corrupted. + +The -x option will do a case sensitive search +for a path specification and extract the file if found.

+ +The bulk of the code was written before I discovered that WinME (but not Win95) +produces a separate VTBL entry for each drive. If decompressing an +archive created by Win98/ME which included multiple drives with MSQIC, you +can currently only access one at a time. At startup you will be +prompted to select the drive of interest which will the be labled 'ROOT' +in the tree display. Otherwise the operations are the same. If doing +data recovery on a file with Multiple VTBL entries for the different drives, +you will find its broken up into separate +sections as if the data and directory regions from separate archives +were concatonated together. MSQIC lets you work with one section +at a time, you can use the -v option to see where each of the +sections in the file is located.

+ +Since version 1.10 some interactive prompts have been added at startup +when a valid VTBL region can't be found. These ask you to use the +-s options to set the regions of interest and confirm the archive +type as there are differences between Win95 and the latter Win98 and WinME +format.

+ + +The @ and -p options were introduced with version 1.07. The -p option +is a special case of the more general @ options. A command file path +specification must immediately follow the '@' symbol. This file controls +extraction of files at the directory level and has the following format:
+One line for each source directory to be extracted.
+The line must contain a source directory specification for the archive followed +by a redirection path to the destination disk +separated from the source by white space. With the -p option only one +source path can be specified, and the destination is always the current directory.

+ +Use the -td option to get a list of directories in the archive. I recommend +redirecting this output to a file and editing it to be sure you don't have +an upper/lower case error. Use it to create the desired command file or source path. +The current implimentation forces the use of the OS specific path +delimiter, DOS and Windows use '\' and Unix uses '/'. +If the source directory path ends with a delimiter ('\' or '/') +only the files in this directory will be extracted. If the path ends +with '*' all sub-directory contents below this directory will also +be extracted to corresponding +directories below the destination directory. If the source path ends with +'+' the program will attempt to create subdirectories before doing +the extraction. To be parsed the source path must end with the appropriate OS +specific delimitor, '*' or '+'.
+With the @ option, a redirection path must also be added on the same line. +Be sure to add some spaces to separate it from the source specification and to +add quotes around any paths containing white space. The redirection +path will be subsituted for the source path when the file(s) and optional +sub-directories are extracted.

+If you have spaces in a path specification you must enclose the complete +path in quotes. Note I do special processing for the OS specific destination +path ".\" and "./", these map to the system's current drive and directory. +Further more there are some odd side effects with the -p option +when processing a quoted path that ends in '\' as required by MSDOS and WIN32. +See the examples and discussion page. +

+The following sample Windows file would extract all files from \temp in the archive +to the same directory on the current source drive. The second line says extract +all files and sub-directories in or below \dos in the archive to "d:\old dos". +The last line extracts all files from \test in the archive to the current directory. +

+  ROOT\temp\   \temp\
+  ROOT\dos*   "d:\old dos\"
+  ROOT\test\  .\
+
+In the example above I've assumed these files were generated on Win98 systems +and that the path separators are '\'. When used on a linux system you should use +the redirect path with appropriate '/' separators. +The default is to +write to the current drive, but the redirect path is free format +and should support MSDOS style drive specifiers as well as mounted linux drives. +File time stamps and ownership are not preserved on extraction. +Destination directories WILL NOT be created, they must exist for the +extraction to work.

+ +Possibly the most confusing +thing is the Win95 versus Win98 format issue. In Win95 the root node has +a name preceeding the separator, while in Win98 its embedded in the MDID +and does not occur in the archive tree display. +I force the top level name 'ROOT' for for the Win98 systems. +Again the -t option will +show this and you should use the same format when generating your command file. +

+ +I've also added a command line option, -p to perform +directory based extraction similar to one line in the @cmd_file. +Supply a source path in the archive terminated by '\','/' or '*'. The +system will supply a redirect path to the current directory where the +files from the source directory are extracted. If a recursive +copy is requested with the '*' terminator the files in all the +source subdirectories will be copied as long as the required subdirectory +exists under the current directory. Note that entire an entire drive +in a Win98 style archive could be restored with the following command +if all the sub-directories existed and the root directory, \, was the +current directory:
+-pROOT*

+ +A word of caution about using the @ option with compressed files. +Its not a very efficient algorithm. If you are doing a lot of small +files you would be well advised to decompress the entire archive +and then do the extraction. When extracting from a compressed archive +the segment(s) containing EACH file are decompressed to extract the +file. If there are 10 files in a given segment, it will be decompressed +10 times during the extraction process! One could do this more +efficiently, but at this point that isn't the way its being done.

+ +I only own and have looked at two OS specific versions of *.qic archives and +both were slightly different, just enough so the data had to be handled +differently during extraction. +Several other people have used this code successfully, but that's not to say +there isn't a 3rd or 4th variation out there somewhere. +Another limitation of the original proof of concept code was that +it used 32 bit signed longs for file lengths and seeks +within the backup archive file. The data structures appear +to be designed for 64 bit values, but for simplicity I have +ignored the four high order bytes. See the large file issues +discussion above. +In Version 1.03, I fixed the logic for archives where the dirSz is greater than 29696 +(aproximately 250 files). +

+ +The time stamps my program displays for the individual files +in the archive look correct. However for some reason the time stamp +for the VTBL creation time was off by two years into the future. +A corrective fudge factor has been added, but don't put complete faith +in this value.

+ +In discussions with people who have tried this program for data recovery +I came across some whose VTBL section were corrupted (all zeros). The +command line options -sd# and -sc# were added to override the VTBL and +work in this case. For Win95 use -sd128 and for Win98 use -sd256 for +the start of the data region. For both, the catalog normally starts at the +beginning of the last segment and is not compressed, see the +details above. +The changes in MSQIC 1.11 were prompted by Darryl Hunt from Austrialia wrote +regarding a data recovery problem. His archive was mostly in tact, but since +VTBL region was corrupt MSQIC did not know it was compressed. I +added some interactive prompts allow the user to set some of the VTBL fields to +correct the problem. See the end of the MSQIC sample output +for more detail on this. If your archive is not corrupted in this mannor the changes +should be transparent. +

+ + +

+The -r option was added to recover data from damaged files. If your archive +is not damaged the -x or @ options maybe the method of choice, but you are +welcome to try -r. It only works with a decompressed file, or sections +of one produced with either the -D or -d options. However it does not +use the catalog, it directly parses the information in the data region +and is intended for people whose catalog has been corrupted/destroyed. +This is a bit tricky as the file length is not included in the data region +headers (the field is always 0xFFFFFFFF). For each file found the program +searches ahead for what looks like a another file header block and estimates +the length. This seems to work, but is chancey. Use the -sd option to +control where the search starts. If you have a damaged or non-existant VTBL +header you will also need to use the -sc option to set the end of the data +region. For a group of decompressed segments extracted from a file use +-sd0 to start at the beginning and -sc### where the numbers represent the +length of the datafile. Note that there is no next header for the last file +in an archive or archive segment. Appropriate use of the -sc parameter can +limit garbage appended to the end of a file, however in an intact archive the +'garbage' is typically NUL bytes inserted to pad to the end of the segment. +The optional filter selects files by case +insensitive matches with the MSDOS (short) file names. Note that during +extraction all files are written to the current directory.

+ +When using the -r option +there are a couple interactive prompts. In particular it asks for +the data format (Win95 versus Win98), whether you want to display +or extract files, and if extracting whether you want to interactively select +the files to be extracted. It needs to know which OS version created the +archive to estimate the file lengths (Win95 has 12 extra bytes after the +file's data) but the default of Win98 will display files from either type +of archive correctly. If it thinks you have choosen unwisely it will tell +you as a warning.

+ +Another 'feature' introduced in Version 1.03 is -sv. This is not documented in the +standard usage display as its a fairly dangerous option. It allows the +use of a data file to create or overwrite the VTBL region. Sample data files and +instruction available on request, but this is only recommended in an emergency. +Its the only operation preformed by this program that writes to the archive.

+ +Ralf Westram from Germany has been a consistent gadfly, friend, and great +tester. He has so far discovered some signed/unsigned errors in my +4 GB file access implementation and bugs in my parsing of the catalog for compressed +files when they exceeded one segment (Ver < 1.03), and the directory tree generation +(Ver < 1.04) which affects file extraction via the -x option. He also +introduced me to the CYGWIN Linux environment under windows. +Thanks for the tips Ralf. + + +

NTBKUP

+Being curious I have also looked at the NTBackUp format in *.BKF +files. I've spent less time on this so its not as robust +as MSQIC. However, since one expects Microsoft to come out with a new +incompatible version of Backup with their next OS release I thought it +was time to start looking at this issue. As mentioned above, +NTBackUp doesn't seem to have a file compression option so there are +no decompression options. This program is also freely available, +but you have to provide feedback...

+ +

+NTBKUP Ver 1.07c  compiled for OS_STR with MAX_PATH = 100
+Copyright 2003 William T. Kranz
+...
+
+usage: ntbkup <file name> [-x[filter]] [-l] [-p] [@<cmd>] [-c] [-d] [-f] [-j#] [-s#] [-t] [-v]
+     -x to unconditionally extract all files matching filter
+     -l<path> where full case sensitive path limits extract 
+     -p<path> recursive path based directory extract
+     @<cmd> use path based extract and redirection command file
+         all extracts use [filter] from -x, default filter is *.*
+     -c to display catalog regions for TAG == TFDD
+     -d display directory tree from raw data region
+     -f<tag>[:start:[len]] to find 4 char tag with optional start pos and length
+     -j#[:#] jump to start position in file for data recovery (modulo 0x400)
+         optionally follow start offset with :# for an end offset
+     -s# to limit options above to a particular SET by #
+     -t[:start[:end]] display tags only from start to end offset
+     -v  to set verbose mode
+
+
+The <file name> argument is required, without it you get the display above.
+Under MSDOS the file name must be a 8.3 style short filename.
+The OS_STR above indicates the Operating System the program was compiled for: +MSDOS, WIN32, Unix, or CYGWIN. +

+ +Currently NTBKUP will by default display all the tags in the source archive file.
+The -c option lists the files in the archive by parsing the catalog region(s).
+The rest of the options ignore the catalog and parse (or control parsing in) the data +region directly which can be useful for data recovery.
+The -d option only lists the directories (DIRB tags) +and can be useful to determine the paths to be used with the -l, -p, or @ commands. +As indicated below the -x has an optional filter argument.
+When -x is used without -p or -l it ignores the directories and extracts all files +in the archive matching the filter. You can use -l with a path description +to limit file extraction to a single directory (this path description should not +include a drive specification, to select a drive use the -s option).
+The -p option is similar but also extracts and optionally creates subdirectories +below the specified archive directory.
+The -l and -p options are mutually exclusive. +In both cases the extraction starts in the current directory. You can additionally +define a filter to be used in these directories with -x. This filter specification +is also used with the @<cmd> file. The default filter is *.* for all files.
+Since version 1.02 the time stamp and attribute (READ/WRITE status) is preserved +when the files are extracted. A command file can also be used to specify +a series of paths for the extraction with the @ command, its format is discussed below. +Under MSDOS the short, 8.3, filename is used when the file is extracted. +Under WIN32 and Linux the full file name is used.
+The -s# option may be used in multi-set volumes to restrict the operations +above to a single set. Run NTBKUP with no options to see the set numbers. +If -s is not used, operations are performed on all sets in the archive.

+ +Although its not indicated in the program output above because there isn't +space on the console screen, the options -f, -j, and -t may all be +followed by the letter 'h'. By default the arguments are treated as +unsigned integers (ie decimal). Appending either an 'h' or an 'H' ahead +of the numeric value interpruts the value as hexidecimal. Ie -jA would +fail and be ignored, but -jhA would jump to hex offset 0xA before starting +to process the file. +

+The -p and @ options are new with version 1.02 (and relatively untested so use +prudence). They mimic logic developed in MSQIC. A preliminary pass is made through +the data file and the names and location of all the directoires (DIRB regions) in +the archive are stored in a dynamically allocated tree structure. Then this tree is +searched for the user supplied directory strings. Unlike -l, a drive specification +is required, ie "C:". The source path is the name stored in the archive, these paths +may be viewed with the -d option. A source path +description is valid if it has one of 3 terminators. 1) the OS specific directory +terminator, '/' in unix and '\' in an MSDOS or WIN32 environment. This denotes only +this directory should be extracted. 2) A '*' which denotes that this directory and +all those below it in the directory tree should be extracted if, and only if, the +matching directory exists on the target system. 3) A '+' is similar to '*', but +will attempt to create sub directories as required.
+With the -p option you just specify the source path and extraction starts at the +current directory. With option 2 or 3 above the archive subdirectories below the +source directory will be copied to corresponding location below the current directory +on the target system.
+With the @<cmd> option, the <cmd> represents a command file name. This +ascii text file will be opened and read line by line. Each line should contain +an archive source path as described above, one or more spaces, ' ', and a +redirection path. Rather than starting in the current directory, the redirection +path is used for the starting directory. For proper parsing the redirection path +must end with the OS specific delimitor, '\' or '/'. +

+Note that if any of the directory names in a path include spaces, ' ', then +the entire path must be quoted. Further more there are some odd side effects with the -p +option when processing a quoted path that ends in '\' as required by MSDOS and WIN32. +See the examples and discussion page. +

+CAUTION: Due to the way this program evolved, files are extracted by changing +the current directory on the target machine to a desired directory and then +doing the extraction in this directory. This has a couple implications. First, +your current directory is likely to change after using the @ command. +Second, this has not been extensively tested. I've tried to trap errors, but +if it gets out of sync during extraction from a large archive you could have a +real mess. Be cautious with use of the '+' terminator enabling directory +creation. Try some small sub paths to be sure you know how it works before attempting +to extract the entire archive as indicated in my example. I've done this successfully, +but you might not be so fortunate. +

+Potential file command lines are shown in quotes below followed by a comment +which should NOT be in the file: +

+"c:+ \croot\"     create & extact all archive files & directories at or below C:\
+                  in the archive to \croot and below on the current drive
+
+"c:\temp\ d:\temp\" extract files from archive c:\temp to d:\temp
+
+"c:\csource* \csource" extact files and directories from archive at pr below
+                       c:\csource to matching directorys on current drive if
+                       the directory exists, otherwise skip over it.
+
+

+I've now talked to a couple people with corrupted *.BKF files. I was +surprised, but apparently NTBackUp isn't always happy with archive files +after they have been created. Especially if they have been passed around +networks. Still exploring this, but added a few more command line +options. The -j and -f options should not be used together. The +program terminates after -f. You can force it to start at a +particular offset with -j. -f will search for identifiers at ALL +file offsets rather than just block boundries. You must enter 4 ascii +characters for the desired tag, you may optionally enter a ':' delimited start +offset and byte length for the search area. Use -fh if hex offsets +are to be used for these qualifiers. Note I was sloppy about this, +the maximum value you can enter here is a 32 bit unsigned integer. If +you have a file that requires 64 bit offsets you won't be able to +specify start or end points beyond 4GB. +

+It appears that the lack of file compression and the advent of +large disks causes people to make what I concider very large +backup archives, see large file issues. +Dan and I worked together in March of 2004 to debug my -p and @ +logic. Ultimately he was able to extract 4GB from the directory +tree in a 20GB *.bkf file with a single -p command line option. +

+Warning in the Spring of 2007 I was contacted by two different people who +were attempting to recover files with NTBackUp and discovered to their dismay that +it completely ignores unicode in the file names when it tires to extract them. Sorry +I was pretty lazy about this. The *.BKF file format supports the use of unicode file names, +but I ignore the 16 bit information and just strip the 8 bit char portion in the +function getn_unicode() in ntbkup.c. This could be corrected fairly easily if you have +a compiler that supports unicode in its open() function (my MSVC 5.0 does not, but I believe linux gcc with glibc-2.2 does). At the moment I have no plan to revisit this myself, but will post here if someone +else takes the effort. + + + +

Multi-Volume *.BKF Backup Issues

+In 2005 I spoke to someone who had made an MTF style backup to floppy +disks. Apparently its still possible to do this, and although one +can recover a lot of the data with the current version of NTBKUP by +processing the floppies one by one you can't get it all. The problem +is NTBKUP.exe expects a single contiguous file and doesn't know how +to skip ahead to the next floppy. Presumably there is a continuation +header of some sort, but I have not looked at this problem. The person +who brought this to my attention recovered everything they wanted by +processing the disks individually. If anyone is desperate, I could work +it out, but it would cost you. It took a couple days to get a workable +solution for *.qic files. + + + + +

Downloads & Version History

+I am making these executable and the associated source code freely available. The source +code is distributed under the +GNU General Public License in the hope of promting +free, expandable software. The source code for both MSQIC and NTBKUP +are availabe in a single archive. Binary OS specific distributions are +available in separate archives. All distributions are LHA compatible as +that has become my standard since it was one of the first early freely available +archive systems. As a favor to Angelique I'm also making the raw *.exe for +the WIN32 versions directly available if you don't want to expand the archive +and are runing a 32 bit version of Microsoft Windows.
+Current versions: MSQIC V 1.12 NTBkUp V 1.07c +
    +
  • MSBackup Source archive for QIC and BKF files (see environments below) +
  • MSBackup + archive of binaries for MSDOS (via Microsoft QuickC 2.5) +
  • MSBackup + archive of binaries for WIN32 (via Microsoft Visual C 5.0) +
  • NTBKUP.exe Ver 1.07c executable for WIN32 +
  • MSQIC.exe Ver 1.12 executable for WIN32 +
  • MSBackup + archive of binaries for Linux V2.4 (via gcc 2.91.66) +
+I own/have compilers for each of the environments above and have attempted +to test these programs in these environments. I've also verified the Linux +binaries run under Linux 2.2. For reasons I currently don't understand the +binaries give a 'segmentation fault' under the Linux 2.0 kernel and the +version of make (3.76.1) with this kernel doesn't like my msbkup.lin make file. +Version 3.79 of GNU make is perfectly happy. I can manually build the programs +with my 2.0.36 kernel, but gcc 2.7.2.3 does not appear to support the 64 bit +file offset required by NTBKUP (although it can be built without this if the +error check in main() is removed). There are also a couple of +gcc ports. Ralf introduced me to CYGWIN +which provides a GNU/Linux environment under Win32. I have not provided +CYGWIN binaries, but the source compiles and runs with gcc 3.3.1 in the +CYGWIN 1.3.x environment. +This implies it would also compile with +DJGPP but I have not tested this. I'd greatly appreciate some +input from a DJGPP user on this issue. +

+Note that although this is 'free' software I'm very willing +to accept job offers or cash contributions if this +does something useful for you. To date exactly one person has +given me a $50 Ebay gift certificate (I would have preferred something at +the local liquor store). Three people have paid me for a few hours actively +consulting on issues related to corrupted archives, and one person +(Thanks Jack) has failed to send me their promised compensation. +One person paid for a minnor modification and recompilation of NTBKUP. +This works out to about $0.15 per hour for my effort. +I guess freeware isn't very cost effective...

+Version history:
+02/07/04 Initial source release was MSQIC 1.09 and NTBKUP 1.02
+02/08/04 MSQIC 1.09a, correct minnor error preserving file attributes with -r option +
+03/19/04 MSQIC 1.10 and NTBKUP 1.03, + In MSQIC correct error in file attribute preservation with -r option + and add interactive option to continue if the VTBL is corrupt by + using the -sc and -sd options + In NTBKUP correct some errors in -p and @ options so it correctly + creates the directory tree with the '+' terminator. + In both versions change get_paths() logic so correctly handles a + quoted destination path when the @ option is used.
+ Todo: Add Unix code supplied by Berend de Boer to auto configure make file
+04/29/04 MSQIC 1.11 + Add more interactive logic for the case where the VTBL is not + found to recreate enough of this information that an entire volume + may be decompressed via -D. +
+05/17/04 NTBKUP 1.04, + Allow volume name to be a network share. Previously assumed it was + a drive letter. Also change filter parsing to allow more than one + period in file names per user requests. +
+06/03/04 NTBKUP 1.05, minnor change to display logic, removes display + of directory parsing during -d, -p options in the hope that error + messages will be noticed. +
+07/08/04 NTBKUP 1.06, create empty files when there is no file data + associated with a FILE region (ie STAN stream is not found). Remove + 'do_file' error message which was displayed in this case. +
+04/06/2005 NTBKUP 1.07, fix some bugs in parsing of command line arguments + for -s and -f arguments which had stopped working. Add + optional end specification for the -j command line argument. + Allow specification of 64 bit long integer file offsets on the command + line for those OS which support files larger than 4Gb. Warning + it appears the -p and @ options may not create empty directories when + recreating a sub-tree. I doubt I have the energy to chase this, but + be careful, empty nodes may be missing in re-created trees. + +
+12/30/2006 NTBKUP 1.07c, was created and is now offered as the default WIN32 + download. +
+06/01/2007 MSQIC 1.12, was notified by a user the Win95 backups are have a + slightly different format, add -st95 command line option to force this mode. + +01/22/2008 Update source file distribution archive to reflect changes in + NTBKUP 1.07c. +
+ +

+I think I'll give it a rest for a while, but if you find a bug +or want to contribute some information please +contact me. I'll attempt to fix bugs, and look at enhancements, +but I want to move on to something else. I'd rather provide a link to +your enhancement and let you support it yourself. +

+I was contacted in January about moving the project to SourcForge, and +this will probably happen soon. When it does I will provide a link here. +

+ +

Acknowledgments

+I want to give thanks to a couple people who gave significant +help or feedback during the development cycle:
+
+Ralf Westram for significant testing and bug detection on MSQIC
+Alan Stewart for making the MTF_100a specification available
+Wolfgang Keller for showing me where to find MTF_100a, and for
+    providing sample *.bkf test files
+Phillip Luebke for sending me my first set of *.bkf test files
+Berend de Boer for the Unix auto configuration logic
+Dan Boyne for identifing bugs in NTBKUP and testing my fixes
+Darryl Hunt for sample output and motivation for MSQIC 1.11 enhancements
+Peter Feldman for sample NTBackup archives containing network shares per NTBKUP 1.04 enhancement
+Geoff Nordli for clarification of the empty file issue per NTBKUP 1.06
+
+Also a couple people who followed through after offering to pay me to help +recover data. Thanks to a nice gentalman from Canada who wishes to remain +annoymous and Gregg Paquette who is starting a computer consulting business . The latest version of NTBKUP, 1.07c, +was requested and funded by Andrew Deatherage of symcas.com, thanks for the check! + + + + + +

Links of Interest

+ + + + \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.LIN" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.LIN" new file mode 100644 index 0000000..17ac793 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.LIN" @@ -0,0 +1,47 @@ +# make build file for msbackup programs +# in a single compiler environment just include the correct file + +#macro defines for gcc environment +OS_STR = UNIX +EXE = +OBJ = o +DEL = rm +DELIM = +CC = gcc -c -ggdb +LNK = gcc -ggdb + + + +#dependancies below generic for Unix gcc and the Evil Empire's cl +#remove match, its not unix compatible right now... + +ALL: msqic ntbkup + +CLEAN: + $(DEL) *.$(OBJ) + +#*.QIC reader +msqic: msqic.$(OBJ) qicdcomp.$(OBJ) msqicrcv.$(OBJ) + $(LNK) -o msqic msqic.$(OBJ) qicdcomp.$(OBJ) msqicrcv.$(OBJ) $(DELIM) + +msqic.$(OBJ): msqic.c msqic.h + $(CC) msqic.c + +msqicrcv.$(OBJ): msqicrcv.c msqic.h + $(CC) msqicrcv.c + +qicdcomp.$(OBJ): qicdcomp.c msqic.h + $(CC) qicdcomp.c + + +#*.BKF reader +ntbkup: ntbkup.$(OBJ) nttree.$(OBJ) + $(LNK) -o ntbkup ntbkup.$(OBJ) nttree.$(OBJ) $(DELIM) + +ntbkup.$(OBJ): ntbkup.c ntbkup.h + $(CC) -D_FILE_OFFSET_BITS=64 ntbkup.c + + +nttree.$(OBJ): nttree.c ntbkup.h + $(CC) -D_FILE_OFFSET_BITS=64 nttree.c + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.MAK" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.MAK" new file mode 100644 index 0000000..61e0ae0 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSBKUP.MAK" @@ -0,0 +1,48 @@ +#Microsoft Nmake build file for msbackup programs +#OS = {MSDOS or WIN32} in environment +# in a single compiler environment just include the correct file + +!if defined(OS) +! if "$(OS)" == "MSDOS" +!include "macro.qcl" +! else +!include "macro.vc" +! endif +!endif + + +#dependancies below generic for Unix gcc and the Evil Empire's qcl/cl + +ALL: msqic.$(EXE) ntbkup.$(EXE) + +CLEAN: + $(DEL) *.$(OBJ) +# delete *.exe cause can conflict with CYGWIN + $(DEL) ntbkup.$(EXE) + $(DEL) msqic.$(EXE) + + +#*.QIC reader +msqic.$(EXE): msqic.$(OBJ) qicdcomp.$(OBJ) msqicrcv.$(OBJ) + $(LNK) msqic qicdcomp msqicrcv $(DELIM) + +msqic.$(OBJ): msqic.c msqic.h + $(CC) msqic.c + +msqicrcv.$(OBJ): msqicrcv.c msqic.h + $(CC) msqicrcv.c + +qicdcomp.$(OBJ): qicdcomp.c msqic.h + $(CC) qicdcomp.c + +#*.BKF reader +ntbkup.$(EXE): ntbkup.$(OBJ) nttree.$(OBJ) + $(LNK) ntbkup.$(OBJ) nttree.$(OBJ) $(DELIM) + +ntbkup.$(OBJ): ntbkup.c ntbkup.h + $(CC) ntbkup.c + +nttree.$(OBJ): nttree.c ntbkup.h + $(CC) nttree.c + + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.C" new file mode 100644 index 0000000..c2a638b --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.C" @@ -0,0 +1,1408 @@ +/* msqic.c *.QIC file reader for Win9x MSBackUp images + Check http://www.fpns.net/willy/msbackup.htm for updates. + contact info above or via snailmail: p.o. box 333, bradford, nh 03221 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +----------------------------------------------------------------------------------- + History: + Look at MSBackup fall of 2003 + see also earlier work in qic80.c doing raw read of QIC80 trakker tapes + with dd in Linux with ftape. + + Note below is abridged, see msqic8.c for full cumulative history. + + +9/30/03 copy qic80.c to this file and modify. + Turns out there isn't a lot of similarity... + Worked on it pretty much full time for a week + +10/9/03 oh dear, win95 version has different VTBL structure + and some differences in how to get to catalog. + see notes in dcomp.txt. Add display of major, minnor versions. + Change how I get to the catalog (directory set). + +10/15/03 tweek for gcc, replace getchar() with fgetc(stdin) + gets() with fgets() add defines for strnicmp and strncmp + #pragma pack(1) doesn't work with GCC under linux 2.4 + see changes in msqic.h + add some logic to be sure a final '\n' printed for linux + +11/29/03 current version on disk and in msqic.lzh is + MSQIC C 46,720 10-17-03 9:23a msqic.c + works reasonably well with a consistent file, try + adding -r recovery option. + most of file depends on VTBL but can override some of it + with -sd and -sc + + add free_cat_list() so can use cat_list routines to + grow each node during recovery. Turned out didn't need this! + + The path is in the data region, but the file length isn't + I guess at the length. Seems to work with uncompressed + WinME backup, messier with compressed and haven't dealt with + Win95 issues + +12/01/03 File up to 57kb, time to split again. + create msqicrcv.c for recovery and patch code. + Ie do_recover() and related routines + write_file() and next_data_sig() + + +12/03/03 expand -fs a little + CAUTION need to verify this with multiple DRIVE WINME backups + it calls find_seg() to get location of catalog for segment of interest + +12/05/03 recompiled in linux, interesting -ss option was a little different. + Changed include file to pack in both versions. + Watch -t option, have email that says it blows up but can't reproduce. + +12/13/03 ok Ralf send a sample archive. Turns out it gives an error message + and exits 'gracefully'. Problem only seems to occur with a compressed + archive because there are seg_head regions in the catalog. His + sample file contains more than one directory segment and the seg_head + occurs in the middle of a file name. Sigh. Need to fix this + and strip them out when totally decompress a file. change + argument to do_decompress() to make this work + +12/21/03 look at Ralf's 4_dir example of how my -t option was WRONG + over a day on this! Go to version 1.04 and archive as msqicv4.lzh + +12/22/03 look into 4 Gb large file issues now. Try define 4GB + to force DWORD offsets via use of FOFFSET as my offset parameter + ignore large file, 64GB issue for now as FAT32 doesn't support + define _4GB at compile time to enable lseek4() + see FOFFSET and LSEEK_ERR defines to handle this semi portably + move tree routines and do_extract() to msqicrcv.c for space + Bump version to 1.05 + +01/11/04 change arguments to do_extract() so pass ccat pointer + force name in tree to MSDOS name from fix2 when MSDOS defined + allows MSDOS to use MSDOS names, do_extract() doesn't care + NM_LEN was 128, bump to 255 + Include Ralf's CYGWIN compiler defines + +01/20/04 backup up current version with included DELIM now + defined in msqic.h in place of '\\' for root. + Now try replacing with "ROOT" + +01/22/04 allow '+' as optional terminator for -p option + change args to do_extract() + +02/03/04 minnor change to find_seg() appears to have a logic error. + create msqic8.c with these changes. + Now change to version 1.09 and go to all hex display and input + as I have done in qicdcomp.c to prepare for possible 64 bit + large file version. + also change logic for -d so it uses start address of cseg_head + rather than data following it so now consistent with -fs option + + oh ho, add start and end logic in get_vtbl() remove call to find_seg() +02/12/04 minnor correction in msqicrcv.c, the -r option did the + timestamp/attrib preservation wrong cause I didn't save them before + the global buffer was overwritten. + Also add a bques() after getting the vtbl so one can attempt to + continue. Bump to version 1.10 +04/28/04 add interactive options to update memory image + of VTBL if it is known to be corrupt. See msqicrcv.c create_vtbl() + Add interactive prompt in find_seg() to optional look at next chain. + Add argument to bques() to allow a little customization, + ie returning -1 on ESC. Bump to version 1.11 + +06/01/07 minnor update, bump version to 1.12 after copy current to msqic9.c + see changes for Win95 compression I just detected from email + add option -st95 for force to Win95 format, default is old behavior Win98 & ME + add argument to find_seg() to handle this + +---------------------- + +deleted inserts from QIC113G.pdf to get more memory for ED + +*/ + +#include +#include +#include +#include // define memset +#include +#include /* for open() defines */ + +#if defined(MSDOS) || defined(_WIN32) +# ifdef MSDOS +# define OS_STR "MSDOS" +# else +# define OS_STR "WIN32" +# endif +# include +# include // for lseek +#else +# ifdef unix +# include // for read/write etc. +# define strnicmp strncasecmp +# define stricmp strcasecmp +# ifdef __CYGWIN__ +# define OS_STR "CYGWIN" +#else +# define OS_STR "LINUX" +# endif +# endif +#endif + +#ifndef OS_STR +# error Unknown build environment. +#endif + +#include "msqic.h" + + +#define BLK_SZ 1024 +BYTE buf[BLK_SZ]; // used by most read routines. Assumes + // all blocks smaller than this, checks if correct + + +#define BASEYR 1970 // for Unix or 1904 for Mac just to be different! +unsigned char mondays[] = {31,28,31,30,31,30,31,31,31,30,31,31}; + +#ifdef _4GB +#define MAXINC 0x7fffffffL +/* FOFFSET is an unsigned long when _4GB is defined + this limits SEEK_CUR and SEEK_END to calls with off == 0 + to get file size or current position, ie not a generic routine +*/ +FOFFSET lseek4(int fp,FOFFSET off,int origin) +{ + FOFFSET roff,lret=LSEEK_ERR; + long inc; + if(origin == SEEK_CUR || origin == SEEK_END) + { + if(off != 0L) + printf( +"\nfatal error lseek called with none zero offset for SEEK_CUR or SEEK_END"); + else + return((FOFFSET)lseek(fp,0L,origin)); + } + // at two calls may be required + else if(origin == SEEK_SET) + { + while(lret == LSEEK_ERR) + { + if(off <= MAXINC) + inc = off; + if((roff = lseek(fp,inc,origin)) == LSEEK_ERR) + break; + off -= inc; + if(off == 0) + lret = roff; // we are done + } + } + return(lret); +} +#endif + +// see pp 204 in runtime C lib ref for my C/C++ ref (used only in testing) +void disp_dosdt(unsigned long date) +{ + int mon,day,yr,hour,min,sec; + sec = 2 *(date & 0x1f); + date = date >> 5; + min = (date & 0x3f); + date = date >> 6; + hour = date & 0x1f; + date = date >> 5; + day = date & 0x1f; + date = date >> 5; + mon = date & 0xf; + date = date >> 4; + yr = date+1980; // high order 7 bits + printf("%02d/%02d/%04d %02d:%02d:%02d",mon+1,day,yr,hour,min,sec); +} + +/* may be an off by one day in below, but seems to work + should be able to do more directly! +*/ +void disp_datetime(unsigned long date) +{ + unsigned short yr=BASEYR,mon=0,day,hour,min,sec; + char lpyr; + sec = date % 60; + date /=60; + min = date % 60; + date /= 60; + hour = date % 24; + date /= 24; + do { + if((yr % 100) == 0 || (yr % 4) != 0) + { + day = 365; // not a leap year + lpyr=0; + } + else + { + day = 366; // is a leap year + lpyr = 1; + } + if(date > day) + { + yr++; + date -= day; + } + } while (date > day); + day = date; + + mondays[1] += lpyr; + while(mon < 12) + { + if(mondays[mon] >= day) + break; + else + day -= mondays[mon++]; + } + mondays[1] -= lpyr; + printf("%02d/%02d/%04d %02d:%02d:%02d",mon+1,day,yr,hour,min,sec); +} + +/* try to reverse process above. Currently only called from + msqicrcv.c create_vtbl() + + assumes buf[] delimited with '/' and has format + "mon/day/year" + + returns seconds elapsed since 1970 till date in string + +*/ +unsigned long mk_date(char *buf) +{ + unsigned long date=0; + int i,mon=0,day=0,yr=0; + char *ptr=buf; + if(sscanf(ptr,"%d",&mon) == 1 && + (ptr = strchr(ptr,'/')) != NULL && + sscanf(ptr+1,"%d",&day) == 1 && + (ptr = strchr(ptr+1,'/')) != NULL && + sscanf(ptr+1,"%d",&yr) == 1) + { + if(yr < 70) // probably only entered 2 digits + yr+= 2000; + if(mon > 0) + mon--; // make an index, ie zero based + if(yr >= BASEYR) + { + date = (yr - BASEYR) * 365; // total normal year days + date += (yr - BASEYR)/4; + if(yr >= 2000) + date--; // this adds number of leap years, ie extra days + for(i=0;ipseg buffer for compressed files + this is all done in cat_read() routine which replaces original read() + This strips the embedded cseg_head structures from a + compressed archives catalog region +*/ +int cat_read(int fp,struct cseg_head *pseg,BYTE *buf,WORD sz) +{ + int i,rd,trd = 0,suc=0; + if(pseg == NULL) // just do a normal read operation (old behavior) + trd = read(fp,buf,sz); + else + while(suc == 0 && trd < sz) + { + rd = 0; + if(pseg->seg_sz == 0) // read next seg head + { + if((i = read(fp,pseg,sizeof(struct cseg_head))) != sizeof(struct cseg_head)) + break; // error reading next seg head + pseg->seg_sz &= ~RAW_SEG; // clear raw bit + } + else if(pseg->seg_sz < sz) + { // must read last bytes in this segment + if((rd = read(fp,buf+trd,pseg->seg_sz)) != pseg->seg_sz) + suc++; + } + else if ((rd = read(fp,buf+trd,sz-trd)) != sz-trd) + suc++; + if(rd > 0) + { + trd += rd; + if(pseg->seg_sz < rd) + pseg->seg_sz = 0; // corrected, no longer seems to occur + pseg->seg_sz -= rd; + } + } + return(trd); +} + +// returns bytes read on success and ptr->err == 0 +// or a value < 0 indicating error and ptr->err == bytes read +// on success *ptr is initialized with directory fields +int next_dir(int fp,struct dir_blk *ptr, BYTE *buf,WORD bsz,BYTE mode) +{ + int i,len,rd=0,suc=0; + char tst; + ptr->fix1 = NULL; ptr->fix2 = NULL; + ptr->nm1 = ptr->nm2 = NULL; + ptr->err = 0; + + if((i=cat_read(fp,ptr->pseg,buf,sizeof(struct ms_dir_fixed))) != + sizeof(struct ms_dir_fixed)) + { + printf("\nerror reading 1st fixed region"); + suc = -2; + } + else + { + ptr->fix1 = (struct ms_dir_fixed *)buf; + rd +=i; + i = 0; // name can be empty for root + tst = 0; // assume false, ignores rec_len + if(!(mode & W98) && // in catalog or W95 data mode, rec_len is valid + ptr->fix1->nm_len +rd > ptr->fix1->rec_len +2) + tst = 1; + if(ptr->fix1->nm_len > 0 && (ptr->fix1->nm_len > bsz - rd || tst || + (i=cat_read(fp,ptr->pseg,buf+rd,ptr->fix1->nm_len)) != ptr->fix1->nm_len) ) + { + printf("\nerror reading 1st fixed region"); + suc = -3; + } + else + { + if( i > 0) + { + ptr->nm1 = buf+rd; + rd +=i; + } + len = sizeof(struct ms_dir_fixed2); + /* prior versions just warned if couldn't read all + of fixed2, but its now fatal error, don't know where we are! + */ + if(len+rd > bsz || (i=cat_read(fp,ptr->pseg,buf+rd,(WORD)len)) != len) + { + printf("\nerror reading 2nd fixed region"); + suc = -4; + } + else + { + ptr->fix2 = (struct ms_dir_fixed2 *)(buf + rd); + rd += i; + i = 0; // name can be empty for root + if(ptr->fix2->nm_len > 0) + { + tst = 0; // assume false, ignores rec_len + if(!(mode & W98) && // in catalog or W95 data mode, rec_len is valid + ptr->fix2->nm_len +rd > ptr->fix1->rec_len +2) + tst = 1; + + if ( ptr->fix2->nm_len > bsz - rd || tst || + (i=cat_read(fp,ptr->pseg,buf+rd,ptr->fix2->nm_len)) != ptr->fix2->nm_len) + { + printf("\nerror reading 2nd name"); + suc = -5; + } + else if(i > 0) + { + ptr->nm2 = buf+rd; + rd +=i; + } + } + } + } + + } + if(suc == 0) + suc = rd; + else + ptr->err = suc; // save it here for fun + return(suc); +} + + + +#define RD_SZ (BLK_SZ -3) +int do_search(int fp,DWORD targ) +{ + int len,i; + FOFFSET offset; + DWORD *lptr; + printf("\nsearching file for occurances of 0x%lx",targ); + if((offset = lseek(fp,0L,SEEK_CUR)) < LSEEK_ERR || read(fp,buf,3) != 3) + return(-1); + do { + if((len = read(fp,buf+3,RD_SZ)) != RD_SZ) + return(-1); + for(i=0;idesc,vtbl->nseg); +// oh dear added 10/4, thought would be easy, but need to fudge? why? +// 63072000 would be two years in seconds + 691200 is 8 days??? +// above is value of VTBL_DATE_FUDGE also used in msqicrcv.c:create_vtbl() + printf("\ncreated (aprox): "); disp_datetime(vtbl->date - VTBL_DATE_FUDGE); +// printf("\ncreated: "); disp_dosdt(vtbl->date); way off yr=2519 + + printf("\nflag 0x%x:",vtbl->flag); + for(i=0,rd=1;i<5;i++) + { + if(rd & vtbl->flag) + printf("\n%s",flagbits[i]); + rd = rd << 1; + } + if((vtbl->flag & 1) == 0) // generic, not vendor specific + { + // fields after flag not valid if vendor specific + + // ignore quad word, assume vtbl->dataSz[1] == 0 + printf("\nversion: %0x:%0x",vtbl->rev_major,vtbl->rev_minor); + printf("\ndir size 0x%lx data size 0x%lx", + vtbl->dirSz, vtbl->dataSz[0]); + printf("\nQFA physical start block 0x%lx end block 0x%lx", + vtbl->start, vtbl->end ); + printf("\ncompression byte 0x%x",vtbl->comp); + if(vtbl->comp & 0x80) + printf("\nCompression used, type 0x%x",vtbl->comp & 0x3f); + if(vtbl->OStype < 8) + printf("\nOS type: %s",OStype[vtbl->OStype]); + } +} + +/* 1/16/04 update so ctrl controls display mode and + if it gets cur_pos from current file pos + or seeks to end param passed (note this is called from get_vtbl() + + also change to FOFFSET and LSEEK_ERR for 4GB option + I don't think *end = -1 was ever used. + now done with ctrl & FIND_POS + 2/2/04 see avik.c for alternative search handles Avik's file + 2/29/04 If ctrl & DISPLAY add interactive prompt to allow skipping + ahead to next chain. Useful in multi-volume file. + 6/01/07 add arg ver to allow WIN95 format to be forced + add min_seg_sz test for WIN95 +*/ + +int find_seg(int fp,char ctrl,FOFFSET *end,BYTE ver) +{ + char hit = 0,ques=0; + int i,tstsz,min_seg_sz,rd,sz,cnt=SEG_SZ/BLK_SZ +2; // 2 blocks more than a segment + FOFFSET cur_pos,tst_pos,adv; + struct cseg_head cseg,*pcseg; + tstsz = SEG_SZ - sizeof(struct cseg_head); // bytes in a full segment + min_seg_sz = tstsz - 0x20; // wild guess WIN95 uses actual bytes used + if(ctrl & FIND_POS) // equivalent to *end = -1 in prior version + cur_pos = lseek(fp,0L,SEEK_CUR); // where are we now + else + cur_pos = *end; // force position + if(cur_pos == LSEEK_ERR) + return(LSEEK_ERR); + if((tst_pos = lseek(fp,0L,SEEK_END)) != LSEEK_ERR && // found EOF + ((FOFFSET)cnt * BLK_SZ) +SEG_SZ > tst_pos) + cnt = (tst_pos - SEG_SZ)/BLK_SZ; // remainder fits in a segment + // won't try if there isn't more than one segment in file... + + tst_pos = cur_pos; // will still duplicate old logic if no hit + while(hit==0 && cnt-- > 0 && + (FOFFSET)lseek(fp,tst_pos,SEEK_SET) == tst_pos && + (rd = read(fp,buf,BLK_SZ)) == BLK_SZ) + { + for(i = 0;iseg_sz & ~RAW_SEG) == tstsz || + ((ver&WIN_MASK) == WIN95 && sz <= tstsz && sz >= min_seg_sz) || // less restrictive + (pcseg->cum_sz == 0 && pcseg->cum_sz_hi == 0 && sz > 0) ) + { + adv = tst_pos + SEG_SZ +i; // should be ok WIN95 & WINME +/* + adv = tst_pos+i; + 6/14/07 this must be wrong! think I just added, but not right, above already did most + if(ver == WIN95) // new 6/1/07 else default to prior Win98 + adv += SEG_SZ; + else + adv += sz+sizeof(struct cseg_head); +*/ + // try a seek to next segment header + if((FOFFSET)lseek(fp,adv,SEEK_SET) != adv || + (rd = read(fp,&cseg,sizeof(cseg))) != sizeof(cseg)) + hit--; // fatal error testing for chain + else if((cseg.cum_sz == 0 && cseg.cum_sz_hi == 0 && pcseg->seg_sz == 0) || + // special case below if 1st uncompressed seg, has cksum? + (pcseg->seg_sz & RAW_SEG && pcseg->cum_sz == 0 && sz < 0x7F6 && + cseg.cum_sz & 0xFFFF0000 == 0 && cseg.cum_sz_hi == 0 && + cseg.seg_sz == 0) || + + // last is a lose test, is cum_sz consistent? + (cseg.cum_sz >= pcseg->cum_sz + sz && + cseg.cum_sz_hi == 0)) // asssume <= 4GB + hit++; + cur_pos = tst_pos+i; + } + } + tst_pos += BLK_SZ -sizeof(struct cseg_head); + } + if(ctrl & DISPLAY && hit > 0) + { + printf("\nFound what looks like a valid segment chain"); + *end = cur_pos; // set start point if interactive + } + cnt = 0; + while((FOFFSET)lseek(fp,cur_pos,SEEK_SET) == cur_pos && + (rd = read(fp,&cseg,sizeof(cseg))) == sizeof(cseg)) + { + cnt++; + sz = cseg.seg_sz & ~RAW_SEG; + if(ctrl & DISPLAY) + { + printf("\n%3d: @ 0x%lx cum size = 0x%lx segment size 0x%x", + cnt,cur_pos,cseg.cum_sz,sz); + if(cseg.seg_sz & RAW_SEG) + printf(" - not compressed"); + if(cseg.seg_sz == 0) + { + printf("\nend of current list, try to step into next? (Y/N) "); + ques = bques(0); // only do interactive logic in DISPLAY mode + } + } + cur_pos += SEG_SZ; // change 6/01/07 should work for all versions + if(sz == 0) + { + if(ques == 1) // attempt to advance through catalog to next segment + { + tst_pos = (cur_pos - *end) % SEG_SZ; + if(tst_pos != 0) + cur_pos += SEG_SZ - tst_pos; + // used compressed flag below, must be if has cseg_heads! + adv = get_dir_len(fp,1,cur_pos); + printf("\nCatalog expected at 0x%lx",cur_pos); + if(adv != LSEEK_ERR) + { + printf(" len 0x%ld",adv); + i = adv / SEG_SZ; // # of segments + tst_pos = adv % SEG_SZ; + if(tst_pos != 0) + { + i++; + adv += SEG_SZ - tst_pos; // next boundry + } + // catalog always an even # of full segments + cur_pos += i * SEG_SZ; + printf("\n\nAdvance to 0x%lx for next segment chain",cur_pos); + } + else + { + printf(" can't determine length"); + ques = 0; + } + + } + if(ques == 0) + { + *end = cur_pos; // return where we are + return(cnt); // # of segments + } + } + rd = 0; + } + if(ctrl & DISPLAY) + printf("\nnever found an end segment\n"); + return(LSEEK_ERR); +} + +char *VTBL_TAG = "VTBL", + *MDID_TAG = "MDID"; + +/* side effect leave positioned where I think data starts + note the WinME logic is messy as must call find_seg() to + locate where each compressed data region ends, then round + up to next segment boundry to find start of its catalog + 2/4/04 oh how use start and end -3 for zero based + index to start data and start catalog + +*/ + +int get_vtbl(int fp,struct vtbl_ver *cur_vtbl) +{ + char query[30]; + long tag,*lptr = (DWORD *)&cur_vtbl->vtbl; + int suc = 0,cnt = 0,i,rd,trd=0; + cur_vtbl->ver = WIN95; // default + + while(read(fp,&tag,sizeof(long)) == sizeof(long) && + tag == *((long *)VTBL_TAG) && + (rd = read(fp,lptr+1,sizeof(struct qic_vtbl) - sizeof(DWORD))) == + sizeof(struct qic_vtbl) - sizeof(DWORD)) + { + *lptr = tag; // insert at start of vtbl + trd += rd+sizeof(DWORD); + cnt++; + if(cnt == 1) + printf("\n %.44s",cur_vtbl->vtbl.desc); + // Win95 has no sdrv, for winME they are valid + if(cur_vtbl->vtbl.sdrv[0] > 0) + printf("\n%d: %c: %.16s",cnt,cur_vtbl->vtbl.ldev-1+'A', + cur_vtbl->vtbl.sdrv); + else printf("\n%d: logical dev 0x%x",cnt,cur_vtbl->vtbl.ldev); + + } + if(trd == 0 || trd != cnt * sizeof(struct qic_vtbl)) + { + printf("\nerror reading VTBL region"); + suc++; + } + if(tag == *((long *)MDID_TAG) ) + { + printf("\n%s tag found at end of VTLB region => WinME format",MDID_TAG); + cur_vtbl->ver = WINME; + cur_vtbl->database = 128; // extra record at end + } + else + cur_vtbl->database = 0; + + cur_vtbl->cnt = cnt; + if((cur_vtbl->flen = (DWORD)lseek(fp,0L,SEEK_END)) == LSEEK_ERR || + cur_vtbl->flen < SEG_SZ) + { + printf("\nerror finding file length"); + suc++; + } + + + // need additional logic for multiple embedded archives + // note only one copy of VTBL at start of file + if(cnt > 1) // WinME generates a VTBL for each drive accessed! + { + printf("\nSelect one of archives above (1 - %d): ",cnt); + fgets(query,10,stdin); + if(sscanf(query,"%d",&i) != 1 || + (cur_vtbl->ndx = (BYTE) i) < 1 || cur_vtbl->ndx > cnt) + { + printf("\nabort - invalid choice"); + suc++; + } + else // must reread selected entry + { + i--; // above is 1 base, want 0 based + // seek to next vtbl entry and read it + if(lseek(fp,128L*i,SEEK_SET) != 128L*i || + (rd = read(fp,&cur_vtbl->vtbl,sizeof(struct qic_vtbl))) != + sizeof(struct qic_vtbl)) + { + suc++; + printf("\nfailed to reread desired VTBL"); + } + } + } + else + cur_vtbl->ndx = cnt; + + if(suc == 0) + { + if((cur_vtbl->ver & WIN_MASK) == WINME && + (cur_vtbl->vtbl.start < 3 || cur_vtbl->vtbl.end < 3)) + { + printf("\nInvalid QFA block #'s"); + if(cnt > 1) + suc++; // fatal error + } + } + + if(suc == 0) + { + // all are relative to end VTBL region + cur_vtbl->database += cnt * 128; + cur_vtbl->dirbase = cur_vtbl->database; + if(cnt > 1 && (cur_vtbl->ver & WIN_MASK)== WINME) + { // may have multiple volumes + cur_vtbl->database += + (FOFFSET)(cur_vtbl->vtbl.start -3) * SEG_SZ; + + cur_vtbl->dirbase += + (FOFFSET)(cur_vtbl->vtbl.end -3) * SEG_SZ; + } + else // assume one contiguous volume to EOF + { + i = cur_vtbl->vtbl.dirSz/SEG_SZ; + if(cur_vtbl->vtbl.dirSz % SEG_SZ != 0) + i++; + cur_vtbl->dirbase = cur_vtbl->flen - (FOFFSET)i * SEG_SZ; + } + } + else // try fudging, set data to full range of file + { + cur_vtbl->database = 0; + cur_vtbl->dirbase = cur_vtbl->flen; + } + printf("\nstart data 0x%lx start dir 0x%lx", + cur_vtbl->database,cur_vtbl->dirbase); + + + if((DWORD)lseek(fp,cur_vtbl->database,SEEK_SET) !=cur_vtbl->database) + { + printf("\nfailed to seek to start of data region %lx",cur_vtbl->database); + suc++; + } + + return(suc); +} + +/* adjust to handle multiple questions, + unless in raw mode, doesn't return without CR + so need to eat trailing control char + 4/28/04 add esc argument +*/ +int bques(char esc) +{ + int ret=0; + char ch; + // Enter on pc returns 0x10, exit loop on any control key + while((ch = fgetc(stdin)) > ' ' && ch != esc) + { + if(ch == 'y' || ch == 'Y') + ret = 1; + else + ret = 0; + } + if(ch == esc) + ret = -1; + return(ret); +} + + +#define NM_LEN 255 // size preallocated buffer for node name + +PATH_ELEM paths[MAX_PATH]; + +// the tstbackup.qic files directory set starts at off = 0x2B90A +// have auto detect working now, can override with -s178442 +int main(int argc,char *argv[]) +{ + extern int fin,fout; // decompression globals + int fo=EOF,fp=EOF,i,cnt = 0,dcnt=0; + int nm_len=0,targetlen,rcnt=0,rd,suc=1; + long dir_len=0,*lptr; + FOFFSET off = 0,datapos,l; + DWORD fdata=0; + char *target=NULL,dch,ch,flag=0,tmode=0,doexit=0; + char name[NM_LEN+1]; + struct cseg_head seg; + struct dir_blk dir; + struct vtbl_ver cur_vtbl; + // root,current,temp, next child + CAT_LIST *root=NULL,*ccat,*tcat,*ncat=NULL; + cur_vtbl.vtbl.dataSz[0] = 0; // used in test for validity below + + printf("\nMSQIC Ver 1.12 compiled for %s",OS_STR); +#ifdef _4GB + printf("\n this version compiled for 4 Gb file access"); +#endif + printf("\nCopyright (C) 2003 William T. Kranz"); + printf("\nMSQIC comes with ABSOLUTELY NO WARRANTY"); + printf( +"\nFree software distributed under the terms of the GNU General Public license"); + printf( +"\nSee http://www.gnu.org/licenses/gpl.html for license information"); + printf( +"\nCheck http://www.fpns.net/willy/msbackup.htm for Updates & Documentation\n"); + // debug aid check structure sizes + if(argc > 1 && strnicmp(argv[1],"-ss",3) == 0) + { + printf("\nstructure sizes"); + printf("\nsizeof(struct qic_vtbl) = %d",sizeof(struct qic_vtbl)); + printf("\nsizeof(struct cseg_head) = %d",sizeof(struct cseg_head)); + printf("\nsizeof(struct ms_dir_fixed) = %d",sizeof(struct ms_dir_fixed)); + printf("\nsizeof(struct ms_dir_fixed2) = %d",sizeof(struct ms_dir_fixed2)); + fputc('\n',stdout); + exit(0); + } + if(argc < 2) + { + + printf( +"\nmsqic [@cmd] [-p] [-x] [-v] [-t] [-s{c|d}#] [-f{d|e|s}] [-d] [-r]"); + printf("\n@cmd to extract directories based on cmd file"); + printf("\n-p extract ALL files from ONE path in tree structure"); + printf("\n-x to extract a file, nm, using paths in tree structure"); + printf("\n-v just display VTBL and exit"); + printf("\n-t[ds] to display catalog as tree, d=> dir only, s=> with segment info"); + printf("\n-fd find file id 0x%lx in data",DAT_SIG); + printf("\n-fe find file id 0x%lx in data",EDAT_SIG); + printf("\n-fs find & display compressed file segments"); + printf("\n-sc# force start catalog (directory set) at hex offset"); + printf("\n-sd# force start data region at hex offset"); + printf("\n-st95 force Win95 decompression decode, default is Win98 & ME"); + // -sv is hidden, update VTBL from data file + printf("\n-D to decompress archive write output to %s",dcompfnm); + printf("\n-d##[:#] to decompress segment(s) starting at hex offset ## in file"); + printf("\n use optional hex :cnt to decompress cnt contiguous segments"); + printf("\n-r[filter] attempt raw file data recovery, use -sd# to force data region start"); + printf("\n use optional filter string, ie *.txt to limit hits"); + doexit++; + } + else if ((fp = open(argv[1],O_BINARY|O_RDONLY)) == EOF) + { + printf("\nfailed to open %s",argv[1]); + doexit++; + } + else if((suc=get_vtbl(fp,&cur_vtbl)) != 0) + { + printf("\nfailed to find a valid vtbl, try using -sc and -sd?"); + // above positions at start data if found + printf("\nContinue with invalid VTBL? (Y/N) "); + if(bques(0) == 1) + { + suc = create_vtbl(&cur_vtbl); // allow to continue + } + } + + + for(i=2;doexit == 0 && fp != EOF && i 0) + { + flag |= TREE; + } + // don't overwrite @, give it precedence + else if (strnicmp(argv[i],"-p",2) == 0 && rcnt <= 0 && + (rd = strlen(argv[i]+2)) > 0) + { // extract a directory to current directory + // set up for a call to do_redirect() with one path + paths[0].term = *(argv[i]+rd+1); + if(paths[0].term != DELIM && paths[0].term != '*' && paths[0].term != '+') + { + printf("\nerror in -p option, path must end in %c, '*', or '+'",DELIM); + doexit++; // exit so see error message + } + else + { + + if((paths[0].path = malloc(rd)) == NULL) + { + printf("\nfailed to allocate path for -p option"); + doexit++; // exit so see error message + } + else + { + rd--; + strncpy(paths[0].path,argv[i]+2,rd); + *(paths[0].path+rd) = 0; // terminator, del DELIM + paths[0].redirect = paths[0].path+rd; // current dir => empty redirect + rcnt = 1; + flag |= TREE; // tree is required + } + } + } + else if(strnicmp(argv[i],"-t",2) == 0) + { + flag |= TREE; + // may have two conditional arguments + for(ch=2;ch<5 && argv[i][(int)ch] != 0;ch++) + { + dch=argv[i][(int)ch]; + if( dch == 'd' || dch == 'D') + tmode |= DIRONLY; // show dir only + else if( dch == 's' || dch == 'S') + tmode |= S_SEGS; // show segs + } + } + else if(strnicmp(argv[i],"-sc",3) == 0 && + sscanf(argv[i]+3,"%lx",&l) == 1) + { + cur_vtbl.dirbase = l; + } + else if(strnicmp(argv[i],"-sd",3) == 0 && + sscanf(argv[i]+3,"%lx",&l) == 1) + { + if((FOFFSET)lseek(fp,l,SEEK_SET) != l) + printf("\nfailed to seek to data start at %lx",l); + else + printf("\nfile pointer at data start %lx",l); + cur_vtbl.database = l; + } + else if(strnicmp(argv[i],"-st95",5) == 0 ) + cur_vtbl.ver = WIN95; // force WIN95 flag for decompression + else if(strnicmp(argv[i],"-sv",3) == 0) + { + if(fp != EOF) + close(fp); + write_vtbl(argv[1],argv[i]+3); + doexit++; + } + else if(strnicmp(argv[i],"-fd",3) == 0) + { + do_search(fp,DAT_SIG); + doexit++; + } + else if(strnicmp(argv[i],"-fe",3) == 0) + { + do_search(fp,EDAT_SIG); + doexit++; + } + else if(strnicmp(argv[i],"-fs",3) == 0) + { + if((cur_vtbl.vtbl.comp & 0x80) == 0) + { + printf("\nWarning, this file not compressed, continue (Y/N) "); + if(bques(0) == 0) // No + doexit++; + } + if(!doexit) + { + l = cur_vtbl.database; // use currently assigned database + find_seg(fp,DISPLAY,&l,cur_vtbl.ver); // display compressed segment list + doexit++; + } + } + else if(strnicmp(argv[i],"-d",2) == 0) + { + if(!(cur_vtbl.vtbl.comp & 0x80)) + { + printf("\nVTBL says file data was not compressed! force it (Y/N)? "); + if(bques(0) != 1) + exit(-1); + } + if ((fo=open(dcompfnm,O_BINARY|O_RDWR|O_CREAT|O_TRUNC, + S_IREAD|S_IWRITE)) == EOF) + printf("\nfailed to open decompress output file"); + else if(*(argv[i]+1) == 'D') // do entire file + { + do_decompress(fp,fo,&cur_vtbl); + doexit++; + } + else if(*(argv[i]+1) == 'd') + { + // Note argument should be address AFTER cseg_head + if(strlen(argv[i]) > 2 && sscanf(argv[i]+2,"%lx",&l) == 1) + { + // for DATA RECOVERY check for a length qualifier + if((target = strchr(argv[i]+2,':')) != NULL && + sscanf(target+1,"%x",&cnt) == 1 && cnt > 0) + printf( + "\n Attempt to read 0x%x continguous segments",cnt); + else if(target != NULL) + { + printf("\nsyntax error, ignore :# format"); + target = NULL; + } + rd = sizeof(struct cseg_head); + + if((FOFFSET)lseek(fp,l,SEEK_SET) != l) + { + printf("\nseek to %lx failed",l); + doexit++; + } + if(target == NULL) + { + cnt = 1; + l += rd; // skip cseg_head, go right to data + } + fin = fp; fout = fo; // set globals + while(!doexit && cnt > 0) + { + if(target != NULL) + { + if(read(fin,&seg,rd) != rd) + { + printf("\nabort, failed to read segment header"); + break; + } + else if(seg.seg_sz == 0) + { + printf("\nend of segment header chain"); + break; + } + // use ch as compressed flag + if(seg.seg_sz & RAW_SEG) + { + ch = 0; + seg.seg_sz &= ~RAW_SEG; // clear it + } + else + ch = 1; // is compressed + printf("\n\nseg start %lx seg size %x", + l,seg.seg_sz); + if(!ch) // don't decompress this one! + { + printf("\nRaw copy of uncompressed segment starting at %lx",l); + // do a raw copy of the segment, ignore return.... + copy_region((long)(seg.seg_sz)); + } + else + decomp_seg(); // repeat for cnt segs with no checks + // new 6/1/07 + l += SEG_SZ; + // was WINME specific: l += seg.seg_sz+rd; + } + else + decomp_seg(); // let it rip ONCE with no checks + if(--cnt > 0 && (FOFFSET)lseek(fin,l,SEEK_SET) != l) + { + printf("\nerror seeking to next segment header at %lx",l); + break; + } + } + doexit++; + } + + } + } + else if(strnicmp(argv[i],"-r",2) == 0) + { + target = argv[i]+2; + if(strlen(target) == 0) + target = NULL; + // flen set via lseek() in get_vtbl() its valid even if vtbl isn't + if(cur_vtbl.dirbase > cur_vtbl.flen) + cur_vtbl.dirbase = cur_vtbl.flen; + do_recover(fp,cur_vtbl.database,cur_vtbl.dirbase,buf,BLK_SZ,target); + doexit++; // just display VTBL + } + else if(strnicmp(argv[i],"-v",2) == 0) + { + disp_vtbl(&cur_vtbl.vtbl); + doexit++; // just display VTBL + } + + if(suc || doexit) + { +#ifdef unix + printf("\n"); // cleanup for unix +#endif + exit(suc); + } + + // we have parsed command line args, and need to process catalog + // move to start dir region + else if((DWORD)lseek(fp,cur_vtbl.dirbase,SEEK_SET) !=cur_vtbl.dirbase) + { + printf("\nfailed to seek to start of catalog at %lx\n\n",cur_vtbl.dirbase); + suc++; + } + else // parse directory display files, or build tree + { + // cseg_head values are relative to start data region. + datapos = 0; // initialize to start data region + // developed logic for WinME where root is not named + ccat = new_cat_list(NULL,"ROOT",datapos,NULL); // create root node + printf("\ndirectory display starting at offset %lx",cur_vtbl.dirbase); + // faults out at end of list terminate before then! + + if( cur_vtbl.vtbl.comp & 0x80) // compressed file + { + dir.pseg = &seg; // uncompressed flag + memset(&seg,0,sizeof(struct cseg_head)); + } + else + dir.pseg = NULL; // uncompressed flag + while((suc = next_dir(fp,&dir,buf,BLK_SZ,0)) > 0) + { + if(dir.fix1 == NULL || dir.fix1->rec_len == 0) + { + printf("\nbig oops, must be end of directory"); + break; + } + if((dir.fix1->flag & SUBDIR)) + { + dch = 'D'; + dcnt++; + } + else + { + dch = ' '; + cnt++; + fdata +=dir.fix1->file_len; + } + dir_len += suc; + off += suc; + if(suc != dir.fix1->rec_len+2) // len is for rest of rec + printf("\nwarning rec_len = %d != bytes read %d", + dir.fix1->rec_len,suc); + + + // rec_len is length of rest of record without its own WORD length + datapos += dir.fix1->rec_len + 2 + sizeof(long)+ // DAT_SIG + dir.fix1->path_len; // path data in data section + // if sub dir pointing to next DAT_SIG in next subdir + // if its a file advance a little further + if(dir.fix1->file_len > 0) // to start of file data + datapos += sizeof(long)+2; // EDAT_SIG + WORD(?) + else if((cur_vtbl.ver & WIN_MASK) == WIN95) // assume subdir & win95 + datapos +=18; // some other EDAT_SIG stuff in here! + // helps but not enough + + // this skips root record which has no name in WinME + // Win95 style do have a name + if(dir.fix1->nm_len > 0 && dir.nm1 != NULL) + { + /* convert first, LFN style, unicode name + to a string, made NM_LEN 255 so should fit but validate below + */ + for(i=0,nm_len=0;inm_len;i++) + { + ch = *(dir.nm1+i++); // unicode, skip alternate chars + if(nm_len < NM_LEN) + name[nm_len++] = ch;// turncates name to NM_LEN + } + name[nm_len] = 0; // make it a string + + if(!(flag & TREE)) // display raw catalog + { + printf("\n\n%-18.18s %c %8ld ",name,dch,dir.fix1->file_len); + disp_datetime(dir.fix1->m_datetime); + printf(" attrib: %2x",dir.fix1->attrib); +#ifdef VERBOSE + // display more of data on 2nd line + putchar('\n'); + disp_datetime(dir.fix1->c_datetime); + putchar(' '); + disp_datetime(dir.fix1->a_datetime); + lptr = (long *)(buf+2); + printf(" @ 2 = 0x%4lx",*lptr); // something, no idea.... + +// mostly zeros, and they same, unknwn3[20] seems to be attributes + printf("\nunknww1 %x unknww2 %x ",dir.fix1->unknww1,dir.fix1->unknww2); + printf("\n unknwb1[] = "); + for(i=0;i < 20;i++) + { + printf(" %02x",dir.fix1->unknwb1[i]); + } + printf("\n unknwb2[] = "); + for(i=0;i < 4;i++) + printf(" %02x",dir.fix1->unknwb2[i]); +#endif + } + else // TREE mode is active + { +#ifdef MSDOS // need to use 8.3 MSDOS names LFN is NOT supported + if(dir.nm2 != 0 && // there is an MSDOS 8.3 style name + dir.fix2 != NULL && dir.fix2->nm_len > 0) + { + for(i=0,nm_len=0;inm_len && nm_len< NM_LEN;i++) + name[nm_len++] = *(dir.nm2+i++); // unicode, skip alternate chars + name[nm_len] = 0; // make it a string + } + +#endif + // dynamically build tree of file/subdir nodes + // keep appending till find DIRLAST flag, then adjust + if(ccat == NULL) + { + printf("\nfatal error ccat undefined"); + break; + } + else if((tcat = + new_cat_list(ccat,name,datapos,dir.fix1)) == NULL) + { + printf("\nnew_cat_list() alloc error"); + break; + } + + else + { // add to list of children + if(ccat->child == NULL) + { // create first child + ccat->child = tcat; + ncat = tcat; + } + else if(ncat != NULL) + { // append to end of child list + ncat->next = tcat; + ncat = tcat; + } + else + { + printf("\nncat undefined"); + break; + } + } + } + + } +// flags are different from QIC113 + if((dir.fix1->flag & DIRLAST) && (flag & TREE)) + /* Before Ralf's work (pre 1/04) thought + an empty sub dir is a SUBDIR and DIRLAST + as I had never included an empty subdir in a backup + but its perfectly valid + + need to adjust CAT_LIST any time see DIRLAST + */ + { // printf("\nDIRLAST"); + if(root == NULL) // ignore 1st one for root with WinME + { + root = ccat; // set root to clear it + if((cur_vtbl.ver & WIN_MASK) == WIN95 && // assume win95? + ccat->child != NULL) + { + root = ccat->child; // root has a name + root->parent = NULL; // tested by -t for out of bounds + free(ccat); + ccat = root; // assed backwards way to do it + } + + } + else // normal transition on DIRLAST + { + ncat = ccat; // save current location + // try to advance to a child or next node + ccat = next_tree_node(ccat); + /* if ccat == NULL still, everything below and at this level failed, + try for a parent's next + by definition a parent is a subdir, but next may not be.... + must ignore direct parent and its children + */ + while(ccat == NULL && ncat->parent != NULL) + { + ncat = ncat->parent; + if(ncat->next != NULL) + { + if(ncat->next->flag & SUBDIR) + ccat = ncat->next; + else + ccat = next_tree_node(ncat->next); + } + } + if(ccat != NULL) + ncat = ccat->next; + else + ncat = NULL; + } + } + + + if(dir.fix1->flag & DIREND) + { + printf("\nend of volume directory"); + printf("\n%d files contain %lx bytes of data, %d directories found", + cnt,fdata,dcnt); + break; // probably VOL END & END this dir + } + if(dir.fix1->file_len > 0) + { + datapos += dir.fix1->file_len; // append this files length + if((cur_vtbl.ver & WIN_MASK)== WIN95) // assume subdir & win95 + datapos +=12; // some more EDAT_SIG stuff in here! + } + } // while no errors + + // ------- now have a directory tree, use it for display or extract + if(rcnt) + { + flag = 0; // block other options + printf("\n extracted %ld files", + do_redirect(fp,&cur_vtbl,root,paths,rcnt)); + } + + if(flag & TREE) // show tree + disp_tree(root,0,tmode); + if((flag & EXTRACT)) // look at tree, extract named file + { + /* Change logic 1/20/04 must have a named root in extact + if Win98 format use "ROOT" if WIN98 use actual name + adds a couple letters must input for -x + but cleans up -p and @ logic + */ + ccat = tree_node(root,target,ISFILE); + if(ccat != NULL) + { + printf("\nfound %s at data section offset %lx length %lx", + target,ccat->data_off,ccat->file_len); + printf("\nExtracting to current directory"); +#ifdef MSDOS + printf("\nForcing MSDOS filename (ie 8.3 format)"); +#endif + if(cur_vtbl.vtbl.comp & 0x80) + printf("\nCaution overwrites temporary file %s in process",dcompfnm); + do_extract(fp,&cur_vtbl,ccat->name,ccat); + + } + else + printf("\n target %s not found (its case sensitive!)",target); + } + } + fputc('\n',stdout); // some cleanup for linux + return(suc); // printf() above and this return makes gcc happy +} + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.H" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.H" new file mode 100644 index 0000000..9a2d87b --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQIC.H" @@ -0,0 +1,295 @@ +/* defines for msqic work + made separate file 10/14/03 + + need __attribute__ ((packed)) + for linux gcc + +note the qic_vtbl seems ok without PACKED, but cseg_head requires! + +see section 7.1.3 of QIC 113 Spec for this info, below works in qic80.c +in *.qic there are two related structures which I named ms_qic_fix* + +struct dir_fixed { +BYTE len, // of the rest of the record + vendor data + attrib; +DWORD datetime, + size; // file size in bytes + data header size, or 0 for empty dir +BYTE extra; // flag byte containing file info +// min length is 0xA, ie include extra but no vendor data +} __attribute__ ((packed)); + +above not what Win9x MSBackUp uses! +also note EDAT_SIG and DAT_SIG only occur in data region not dir set + +11/31/03 add some bitmap defines and .err field in struct dir_blk +12/14/03 +add field struct cseg_head *pseg; + in struct dir_blk +12/16/03 per Ralf Westram's input try to clean up some of +signed unsigned issues in DWORD verus long for file offsets +If I use as offset should probably be a long! +in struct vtbl_ver change .database and .dirbase +12/22/03 try some FOFFSET logic + change struct vtbl_ver offset fields back to DWORD + +1/7/04 add Ralf's EMPTYDIR define +1/11/04 change params to do_extract() + remove #define SEARCH 2 may never have been used... + update tree_node() adding BYTE mode argumnet + add do_redirect() prototype +1/20/04 add DELIM definition for path separator +1/22/04 change args for do_extract() +4/28/04 add RECREAT to indicate the original VTBL was invalid + Also requires WIN_MASK and change value of WIN95 and WINME + Need in do_decompress() if attempt to reconstruct archive + Note there are a couple places where I should be using + FOFFSET instead of DWORD, I've left them alone for ease + in displaying data, but see vtbl_ver and seg_head + + add mk_date() prototype + add define for VTBL_DATE_FUDGE +*/ + +#if defined(MSDOS) || defined(_WIN32) +#pragma pack(1) +// default for MSVC _WIN32 should be byte packing, but need pack(1)! +#define PACKED ; +#define DELIM '\\' +#else // Unix +#define DELIM '/' +#ifdef __CYGWIN__ // note O_BINARY is defined +#pragma pack(1) // Linux gcc won't compile with this, see above +#define PACKED ; +#else +// to pack linux structures selectively +#define PACKED __attribute__ ((packed)); +#define O_BINARY 0 // this Microsoft mode flag undefined in Linux gcc +#endif +#endif + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +#ifdef _4GB +typedef unsigned long FOFFSET; +#define LSEEK_ERR ((DWORD)-1L) +#else + #ifdef HAS_INT64 // force this manually for 64 bit access +// for now WIN32 specific + typedef __int64 FOFFSET; + #define lseek _lseeki64 // redirect to 64 bit access routine + #define LSEEK_ERR (-1) // this works for standard lseek stuff + #else + typedef long FOFFSET; // 2 GB is default std C + #define LSEEK_ERR (-1L) + #endif +#endif + +#define SEG_SZ 29696 // MSBackUP wants data and dir segs to be multiple of this +// in compressed file each segment including catalog start with cseg_head + + +// from pp9 of QIC113G, +struct qic_vtbl { +BYTE tag[4]; // should be 'VTBL' +DWORD nseg; // # of logical segments +char desc[44]; +DWORD date; // date and time created +BYTE flag; // bitmap +BYTE seq; // multi catridge sequence # +WORD rev_major,rev_minor; // revision numberrs +BYTE vres[14]; // reserved for vendor extensions +DWORD start,end; // physical QFA block numbers + /* In Win98 & ME subtract 3 from above for zero based SEGMENT index + to start first data segment and start first directory segment + */ +BYTE passwd[8]; // if not used, start with a 0 byte +DWORD dirSz, // size of file set directory region in bytes + dataSz[2]; // total size of data region in bytes +BYTE OSver[2]; // major and minor # +BYTE sdrv[16]; // source drive volume lable +BYTE ldev, // logical dev file set originated from + res, // should be 0 + comp, // compression bitmap, 0 if not used + OStype, + res2[2]; // more reserved stuff +} PACKED + + + + +/* If its a compressed volume there will be cseg_head + records ahead of each segment. The first immediately + follows the Volume Table. + For the sake of argument, lets assume per QIC133 segments are + supposed to be < 32K, ie seg_sz high order bit isn't required. + So its a flag bit, set to indicate raw data? IE do not + decompress this segment. Use seg_sz to jump to the + next segment header. +*/ +#define RAW_SEG 0x8000 // flag for a raw data segment + +struct cseg_head { +DWORD cum_sz, // cumlative uncompressed bytes at end this segment + cum_sz_hi;// normally zero. High order DWORD of above for > 4Gb +WORD seg_sz; // physical bytes in this segment, offset to next header +} PACKED + + +struct ms_dir_fixed { +WORD rec_len; // only valid in dir set or Win95 Data region +DWORD ndx[2]; // thought this was quad word pointer to data? apparently not + // ndx[0] varies, ndx[1] = 0, was unknow[8] + // in data section always seems to be 0xffffffff +WORD path_len, // @ 0xA # path chars, exits in catalog and data section + // however path chars only present in data section + unknww1; // 0xA always? +BYTE flag; // flag bytes +WORD unknww2; // 0x7 always? +DWORD file_len; // @ 0x11 # bytes in original file +BYTE unknwb1[20], // was flags[0x18] but attrib at flags[20] + attrib, + unknwb2[3]; +DWORD c_datetime, // created + unknwl1, // always 0xFFFFFFFF? + a_datetime, // accessed + unknwl2, // always 0xFFFFFFFF? + m_datetime, // modified, as shown in DOS + unknwl3; // so can be expanded? always 0xFFFFFFFF? +WORD nm_len; // length of the long variable length name +} PACKED + +// contains var length name, case sensitive, unicode + +struct ms_dir_fixed2 { +BYTE unkwn1[13]; // was [0x15]; this region fairly constant +DWORD var1; // these vars change file to file +DWORD var2; +WORD nm_len; // length of 2nd, short ie DOS, variable length name +} PACKED + +#ifdef MSDOS +#pragma pack() // don't read rest from file so don't care +#endif + +// var length name, always upper case => DOS, unicode +// if in data region path follows, not in directory set +// var length path per ms_dir_fixed.path_len, unicode + + +// use to return VTBL header and version detection from get_vtbl() +#define WIN95 1 +#define WINME 3 // used for 98 and ME, ie had an MDID +#define RECREAT 0x80 // original VTBL corrupt, recreated +#define WIN_MASK 0x7 + +// tmode defines passed to disp_tree() +#define S_SEGS 1 // show segments in tree +#define DIRONLY 2 +// bitmap flags for control flags in main and next_dir() +#define EXTRACT 2 // used in main and msqicrcv.c +#define TREE 4 +// below only used in msqicrcv.c:do_recover( mode) +// but W95 and W98 set in main? +#define QUERY 1 +#define W95 4 // not used in same flag as TREE so no conflict +#define W98 8 + +struct vtbl_ver { +BYTE ver, + cnt, // # of regions + ndx; // which VTBL when more than one (1 based!) +DWORD database; // start of data segment +DWORD dirbase; // start of directory segment +DWORD flen; // file length +struct qic_vtbl vtbl; // copy of vtbl of interest +}; + + +/* use to return data from next_dir() routine + Not pointers are to buffer locations which can be over written + if care not taken after call! +*/ +struct dir_blk { +char err; +struct ms_dir_fixed *fix1; +BYTE *nm1; +struct ms_dir_fixed2 *fix2; +BYTE *nm2, + *path; +struct cseg_head *pseg; // NULL if NOT compressed, otherwise points to buffer +}; + +/* for my dynamic link list of nodes + order of subdirs in msbkup2.qic + root|DIRLAST,temp,dos,csource + could save entire record, but not now.... +*/ +typedef struct cat_list { +char *name; +BYTE flag, // its a subdir (maybe only one) or file + attrib; // file attributes +FOFFSET data_off; // offset into uncompressed data region for start +DWORD datetime, // m_datetime -> dos time, ie modified + file_len; // @ 0x11 # bytes in original file +struct cat_list * next, + * child, // only subdir has children + * parent; +} CAT_LIST; + +// used in path remapping functions +typedef struct path_elem { +char term, + *path, + *redirect; +} PATH_ELEM; + +#define MAX_PATH 25 // max PATH_ELEM array allocation + +// bitmap flags for ctrl in find_seg +#define DISPLAY 1 +#define FIND_POS 2 // probably not used... + +/* Bitmap defines for flags + flags below work with my QIC tape images, see qic80.c + msc seems to do this differently +#define SUBDIR 0x20 // this is a directory entry, not a file +#define DIRLAST 0x40 // last entry in this directory +#define DIREND 0x80 // last entry in entire volume directory + + below seem to work with my current ms_dir_fixed.flags[2] + Thanks to Ralf Westram for identifing this flag + Given the unused bits, there are more to be identified! +*/ +#define ISFILE 0 // no bit set for file, this define for readability +#define SUBDIR 0x1 // this is a directory entry, not a file +#define EMPTYDIR 0x2 // this marks an empty subdirectory +#define DIRLAST 0x8 // last entry in this directory +#define DIREND 0x30 // last entry in entire volume directory + +#define DAT_SIG 0x33CC33CCL // signature at start of Data Segment +#define EDAT_SIG 0x66996699L // just before start of data file +#define UDEF_SIG 0xFFFFFFFFL // undefined DWORDS in Data Segment + +#define VTBL_DATE_FUDGE 63072000L // date fudge for vtbl date + + +// add some function prototypes +// following in msqic.c +int next_dir(int fp,struct dir_blk *ptr, BYTE *buf,WORD bsz,BYTE mode); +// following in msqicrcv.c +int get_paths(char *fn,struct path_elem paths[],int sz); +void disp_tree(CAT_LIST *ccat,int level,BYTE mode); +CAT_LIST *tree_node(CAT_LIST *ccat,char *str,BYTE mode); +CAT_LIST *next_tree_node(CAT_LIST * ccat); +CAT_LIST *new_cat_list(CAT_LIST *prev,char *name,long off,struct ms_dir_fixed *fixed); +void free_cat_list(CAT_LIST *root); +int do_extract(int fp,struct vtbl_ver *v,char *nm,CAT_LIST * ccat); +long do_redirect(int fp,struct vtbl_ver *v,CAT_LIST *root,PATH_ELEM paths[],int rcnt); +WORD do_recover(int fp,DWORD dataoff,DWORD dataend, BYTE *buf,WORD bsz,char *target); +int write_vtbl(char *qicnm,char *datnm); +// following in qicdcomp.c, see sloppy use of globals, sorry +int copy_region(long len); +void decomp_seg(); +int do_decompress(int fi,int fo,struct vtbl_ver *cvtbl); +unsigned long mk_date(char *str); \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQICRCV.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQICRCV.C" new file mode 100644 index 0000000..a032f0f --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/MSQICRCV.C" @@ -0,0 +1,1342 @@ +/* msqicrcv.c - support routines for main() in msqic.c + Check http://www.fpns.net/willy/msbackup.htm for updates. + contact info above or via snailmail: p.o. box 333, bradford, nh 03221 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +----------------------------------------------------------------------------------- + History: + +11/31/03 +split out of msqic V1.01 11/31/03 +add write_vtbl() stub +copy current version to msqicrc1.c + it had path error and seems to be in an infinit loop. + exceeds path length, breaks out of search and then + tests for EDAT_SIG beyond end of buffer. This fials + off wasn't updated so calls next_data_sig with dataoff + that failed last time.... + +12/05/03 seems ok under linux also, but reguires a strupr.c + modules, must look equivantlent unix funcion and define. +12/16/03 wrote and tested write_vtbl(), seems ok +12/22/03 add some code changes, ie FOFFSET and LSEEK_ERR + so new defines in msqic.h for _4GB can work affected next_data_sig() + will probably screw up some of Ralf's warning issues again.... + + also append tree routines and do_extract() as ran out of space + for ed in msqic.c + +1/8/04 was out of sync with Ralf's stuff, see sub dir \ralf + Apparently his didn't crash with '-t' but mine did. + see notes in msqicsrc.lst + + apparently disp_tree() blew up if nesting was too deep + checked out by reducing nesting level. see new IND_LEV define + Yes this seems to produce problem, have corrected so appending + last NUL doesn't overwrite stack. + + Also added EMPTYDIR logic based on Ralf's info. + seems to work with my test file: \tempty2.qic + + very odd, do_extract() does fgets() to obtain file name + but doesn't wipe /n so open fails. How did it every work? + Oh I bet I started with gets() converted to linux and never rechecked it + +1/11/04 diddle do_extract() changing arguments so pass in + ccat pointer with file name. Also change tree routines to used + MSDOS name if MSDOS defined and it exists (ie there is a long name) + add ralf's CYGWIN compiler defines + add mode to disp_tree so can change mode at run time + add get_paths(), tree_copy(), and do_redirect() + required new parameter in call to tree_node() +1/15/04 do_extract was decompression one too many segments + see off by one error on incrementation + testing with big note by change + found some more FOFFSET issues and an error in next_data_sig() + major error in logic to estimate file length in do_recover() + how did it ever work. +1/17/04 add check for ".\" as redirect path in get_paths() + tweak do_redirect() to ignore empty redirect path which + is current directory and must exits. + also required changing where tree_path() appends DELIM +1/22/04 start adding setting timestamp and attribute on file extraction + and '+' option in do_redirect to create directories. + remove strupr() again, only required if MSDOS when it exists in stdlib +1/24/04 in do_redirect() add ccat != NULL before copy_tree(..ccat->child + in get_paths() add incrementation of i while looking for redirect path +2/08/04 error in do_recover() needed to save attrib and datetime + before next_data_sig() call, was restoring incorrectly! +3/19/04 tweak get_paths() to match nttree.c, think it didn't handle + termination of quoated destination correctly +4/28/04 add create_vtbl() attempt to create a vtbl if the archives is + unreadable. +5/03/04 oops need to set cvtbl->ver not cvtbl->vtbl.OStype! +*/ +#include +#include +#include +#include // for toupper() +#include // define memset +#include +#include /* for open() defines */ + +#if defined(MSDOS) || defined(_WIN32) +# include +# include // for lseek +#include // for utime() +#else +# ifdef unix +# include // for read/write etc. +# include +# define strnicmp strncasecmp +# define stricmp strcasecmp +// note I have a custom strupr() below +# else +# error Unknown build environment. +# endif +#endif + +#include "msqic.h" + +extern int bques(); + + +/* trash global buffer looking for start next DAT_SIG + note 11/30/03 was working with just lptr and code below for WinME + if( *lptr == DAT_SIG && *(lptr+1) == UDEF_SIG && *(lptr+2) == UDEF_SIG) + for win95 the WORD after DAT_SIG is defined, so this test failed + new one should be generic + 1/15/04 oops! revisting, when I made change above I left + minsz = 3*sizeof(long) + must add sizeof(WORD) I've added + + This is looing for following hex pattern of bytes where ?? is don't care + {CC,33,CC,33,??,??,FF,FF,FF,FF,FF,FF,FF,FF} +*/ +FOFFSET next_data_sig(int fp,FOFFSET off,BYTE *buf,WORD bsz) +{ + int i,rd,suc=0,minsz = 3*sizeof(DWORD) + sizeof(WORD); + DWORD *lptr,*lptru; + while(suc == 0) + { + // read minsz more than need each pass, + // then inc off by rd - minsz so end up seeking back minsz + if((FOFFSET)lseek(fp,off,SEEK_SET) != off || + (rd = read(fp,buf,bsz)) < minsz) + suc = -1; + else + { + + i=0; + do { + lptr = (DWORD *)(buf+i); + // skip WORD which may be defined in Win95 format + lptru = (DWORD *)(buf+sizeof(DWORD)+sizeof(WORD) +i); + if( *lptr == DAT_SIG) + { + if( *(lptru) == UDEF_SIG && *(lptru+1) == UDEF_SIG) + suc = 1; + } + if(suc == 0) + i++; + } while(suc == 0 && i 0) + return(off); + else + return(LSEEK_ERR); +} + +int write_file(char *nm,int fp,FOFFSET start,FOFFSET end,BYTE *buf,WORD bsz) +{ + int fo=-2,wr,rd,ret=0; + FOFFSET l; + if ( (l = (FOFFSET)lseek(fp,start,SEEK_SET)) != start || + (fo=open(nm,O_BINARY|O_RDWR|O_CREAT|O_TRUNC, + S_IREAD|S_IWRITE)) == EOF) + ret = -1; + while(ret == 0 && start < end) + { + if(bsz + start < end) + rd = bsz; + else + rd = end - start; + if((wr = read(fp,buf,rd)) != rd || (rd = write(fo,buf,wr)) != wr) + ret = -2; + start += rd; + } + if(fo > 0) + close(fo); + return(ret); +} + + + +int is_match(char *str,char *filter) +{ + char ret=1; +#ifdef MSDOS + strupr(str); // DOS name compare, case insensitive + strupr(filter); +#endif + while(*str != 0 && ret == 1) + { + if(*str == '.' && *filter == '*') + filter++; + if(*filter == '*' || *filter == '?') // match all + str++; + else if (*filter++ != *str++) + ret = 0; + if(*filter == '?') + filter++; + + } + return(ret); +} + + +/* set_fattrib() to set both the datetime, and file attribute after written + called from do_recover() and do_extract() +*/ +int set_fattrib(char *name,DWORD datetime,BYTE attrib) +{ + struct utimbuf times; + int i,suc=0; + times.actime = times.modtime = datetime; + if(utime(name,×) != 0) // set time stamp + suc = 1; + + i = S_IFREG | S_IREAD; + if((attrib & 1) == 0) // its writable + i |= S_IWRITE; + /* via generic chmod, I fail to see how to do DOS + hidden 0x20 (under linux can insert '.' at start name) + system 0x40 + archive 0x20 + executable in DOS is done by file name extension + */ + if(chmod(name,i) != 0) + suc |= 2; + if(suc) + printf("\nerror setting file: "); + if(suc & 1) + printf("timestamp "); + if(suc & 2) + printf("attributes"); + return(suc); +} + +/* try to parse data region + use the DOS 8.3 file names + + Tricky as don't know file length! not in valid in fixed region + + think will need extra for Win95? + + bitmap defines below for cntrl byte set interactively + +move to msqic.h +#define QUERY 1 +// EXTRACT 2 defined above +#define W95 4 // else its W98 version of archive + + +11/31/03 + 1st mode, change to incrementally read buffer via calls to + next_dir() + +extra=6 defined for W98 as {DWORD,WORD} the EDAT_SIG and WORD following + it in Win 98 files, It turns out to be the same in Win95 files, + but is 3 * large, ie 18 bytes = 3 pairs for subdir entries. + since I ignore them, its always 6 below. + +However +extrae=0 for Win98, but for W95 there are 12 extra words after the + file before the next DAT_SIG, ie if don't do the format properly + on gets files that are 12 bytes too long. +12/14/14 initialize dir.pseg = NULL to show we are parsing + a data region, not a catalog +1/15/04 weird, after searching for next_sig after start file did: + if(next_sig != LSEEK_ERR && (next_sig = lseek(fp,0L,SEEK_END)) > dataoff) + printf("\nWarning searched to EOF with no DAT_SIG"); + what was I thinking? + add dataend and change both to DWORD as set from + cur_vtbl.database and cur_vtbl.dirbase in main +*/ + + + +WORD do_recover(int fp,DWORD dataoff,DWORD dataend,BYTE *buf,WORD bsz,char *filter) +{ + BYTE attrib; + DWORD datetime; + int rd,i,off,extra=6,extrae=0; + WORD fcnt=0,xfcnt=0,tcnt=0; + FOFFSET next_sig; + char name[15],*ch,cntrl=W98,match = 1,warn=0; + struct dir_blk dir; // storage space for dir pointers + printf("\nDATA recovery options:"); + printf("\n Default is Win98 format, use Win95 instead (Y/N)? "); + if(bques(0) == 1) + { + cntrl = W95; + // adjust extra for additional data after 1st EDAT_SIG + extrae=12; // two pairs of {DWORD,WORD} before next DAT_SIG + } + printf("\n Default displays files, extract them (Y/N)? "); + if(bques(0) == 1) + { + cntrl |= EXTRACT; + printf("\n Default, interactive query for each file, extract all (Y/N)? "); + if(bques(0) != 1) + cntrl |= QUERY; + if(filter != NULL && strlen(filter) > 0) + { + printf("Match extracted files to: %s (Y/N)? ",filter); + if(bques(0) != 1) + filter = NULL; + } + + } + + + dir.pseg = NULL; // signal not a compressed catalog region + while((dataoff = next_data_sig(fp,dataoff,buf,bsz)) != LSEEK_ERR && + dataoff < dataend && + (FOFFSET)lseek(fp,dataoff+sizeof(DWORD),SEEK_SET) == dataoff+sizeof(DWORD)) + { // now pointing to start of a File spec, read it + dataoff += sizeof(DWORD); + off = 0; + tcnt++; // increment total count think we found something + if((rd = next_dir(fp,&dir,buf,bsz,cntrl)) > 0) + { + off += rd; // bytes read in record + // ignore if subdir 0x10 or volid 0x8 + if(dir.fix1->attrib & 0x18) + { + if(dir.fix1->attrib & 0x10 && warn == 0 && fcnt == 0) + /* 1st hit on subdir verify format + W95 subdir has two extra pairs of {EDAT_SIG,WORD} + but the file entries don't + */ + { + if(cntrl & W98 && + *((DWORD *)(buf+off+dir.fix1->path_len+6)) == EDAT_SIG) + printf("\nWarning, looks like Win95 format, files may be 12 bytes too long"); + else if(cntrl & W95 && + *((DWORD *)(buf+off+dir.fix1->path_len+6)) != EDAT_SIG) + printf("\nWarning, looks like Win98 format, files may be 12 bytes too short"); + warn++; //only give message once + } + dataoff += off; + continue; + } + } + else + { + printf("\nfatal error reading dir spec @%lx skip to next", + dataoff); + dataoff += off; // search for next + continue; + } + + // next_dir() does NOT read path info, not valid in a catalog + rd = dir.fix1->path_len + extra; + if(rd < 1 || rd > bsz -off || dir.nm2 == NULL || + (i = read(fp,buf+off,rd)) != rd || + *((DWORD *)(buf+off+dir.fix1->path_len)) != EDAT_SIG) + { + printf("\nerror verifing or reading to start data file @%lx skip to next", + dataoff); + continue; + } + + fcnt++; // found a valid file + dataoff += off + rd; // skip extra words to start file + ch = dir.nm2; // start MSDOS name + for(i=0;inm_len/2 && i < 14;i++) + { + name[i] = *(ch++); + ch++; // skip 2nd unicode + } + name[i] = 0; + if(filter != NULL) + match = is_match(name,filter); + if(match) + { + printf("\n@%lx Path: ",dataoff); + for(i=0;ipath_len;i++) + { + if(*ch < ' ') + fputc(DELIM,stdout); // 0 or 0xA ? + else + fputc(*ch,stdout); + ch++;ch++;i++; // skip 2nd unicode byte + } + // must save these, io below overwrites them as use global buf + attrib = dir.fix1->attrib; + datetime = dir.fix1->m_datetime; + // overwrite buffer to find file length, destorys dir.* ptrs + next_sig = next_data_sig(fp,dataoff,buf,bsz); + if(next_sig == LSEEK_ERR || next_sig > dataend) + { + if(next_sig == LSEEK_ERR) + printf("\nSearched to EOF with no next DAT_SIG, "); + else + printf("\nNext DAT_SIG after end of data, "); + printf("use end data region %lx",dataend); + next_sig = dataend; + } + if(next_sig < dataoff) + printf("\ncan't estimate length for %s",name); + else + { + if(match && cntrl & EXTRACT) + i = 1; + else + i = 0; + + if(match) + { + if(cntrl & QUERY) + { + printf("\nsave as %14s est length %lu (Y/N) ?", + name,(DWORD)next_sig-dataoff); + i = bques(0); // set to 0 to skip save + } + else + printf("\n%14s est length %lu",name,(DWORD)next_sig-dataoff); + } + if(i) + { + if((i=write_file(name,fp,dataoff,next_sig,buf,bsz)) != 0) + printf(" - error %d saving file\n",i); + else + { + set_fattrib(name,datetime,attrib); + xfcnt++; + } + } + dataoff = next_sig; // always advance + } + } + } + printf("\n%u potential files, %u parsed file names",tcnt,fcnt); + if(xfcnt > 0) + printf(", %u files extracted\n",xfcnt); + return(fcnt); +} + +#define VTBL_SZ 128 + + +char *key_reg[] = {"VTBL","MDID",NULL}; +char *reg_opts[] = {"null","read",NULL}; +char *key_words[2][7] = + { + {"desc","flag","dirsz","datasz","label","comp",NULL}, + {"MediumID","VR","CS","FM","UL","DT",NULL}, + }; + +#define MDID_SZ 6 +#define MDID_TERM ((char) 0xb0) +char *mdid_str[MDID_SZ]; + + +int has_key_word(char *targ,char *str[],char **args) +{ + int len=0,klen,i = 0; + while(*targ == ' ') + targ++; + while(*(targ+len) != ' ' && *(targ+len) != 0) + len++; + + if(args != NULL) + { + *args = targ+len; // return start of args + while(**args == ' ') + (*args)++; + } + while(len > 0 && str[i] != NULL) + { + klen = strlen(str[i]); + if(len >= klen && strnicmp(targ,str[i],klen) == 0) + return(i); + i++; + } + return(-1); +} + + +int get_mdid(char *buf) +{ + int i,key,len,klen,suc = 0; + char *str,*arg; + for(i=0;i= 0 && + key < MDID_SZ) + { + klen = strlen(key_words[1][key]); + arg = buf+i+klen; + while(*(arg+len) != MDID_TERM && i+klen+len < VTBL_SZ) + len++; + + if(len == 0) + str = NULL; + else + { + if((str = malloc(len+1)) == NULL) + suc++; + else + { + strncpy(str,buf+i+klen,len); + *(str+len) = 0; // make it a string + } + } + mdid_str[key] = str; + + } + i+= len+klen; + while(*(buf+i) != MDID_TERM && i < VTBL_SZ) + i++; + i++; + } + return(suc); +} + + +void put_mdid(char *buf) +{ + int i,j=4,len,klen; + memset(buf+j,0,VTBL_SZ-j); + + for(i=0;i VTBL_SZ) + { + printf("\nskipping MDID: %s",mdid_str[i]); + continue; + } + strcpy(buf+j,key_words[1][i]); + j+=klen; + strcpy(buf+j,mdid_str[i]); + j+=len; + *(buf+j) = MDID_TERM; + j++; + } + } + +} + +#define LN_LEN 100 +/* start write data to VTBL + Each key_reg[] word advances loc by 128 bytes + if any changes done, they are written + ie following would write new UL to MDID at offset 128 +------- + VTBL + MDID read + UL a new title +------- +*/ +int write_vtbl(char *qicnm,char *datnm) +{ + int fp=EOF,suc=-1,vcnt=-1,hits=0,reg=-1,key,v,tst; + FILE *fd = NULL; + FOFFSET off; + DWORD lv,*lptr; + char *args,*lret,ln[LN_LEN+1],buf[VTBL_SZ],*str; + struct qic_vtbl *vtbl = (struct qic_vtbl *) buf; + if ((fp = open(qicnm,O_BINARY|O_RDWR)) == EOF) + printf("\nFailed to open archive %s",qicnm); + else if ((fd = fopen(datnm,"r")) == NULL) + printf("\nFailed to open VTBL data file %s",datnm); + else + { + do + { + lret = fgets(ln,LN_LEN,fd); + v = strlen(ln)-1; + if(v >= 0 && *(ln+v) == '\n') + *(ln+v) = 0; // remove CR + + if(lret == NULL || (key=has_key_word(ln,key_reg,&args)) >= 0) + { // terminate current region, and optionally start a new one + if(hits && vcnt >= 0) + { + off = vcnt * VTBL_SZ; + if(reg == 1) // MDID + put_mdid(buf); // fill buffer with new strings + if((FOFFSET)lseek(fp,off,SEEK_SET) != off || + write(fp,buf,VTBL_SZ) != VTBL_SZ) + { + printf("\nerror writting region at %lx",off); + break; + } + } + if(lret == NULL) + break; // we are done + reg = key; + hits = 0; + vcnt++; + if((key = has_key_word(args,reg_opts,NULL)) >= 0) + { + if(key == 0) // null it out + { + hits = 1; // force overwrite on close + memset(buf,0,VTBL_SZ); + } + else // read it in + { + off = vcnt * VTBL_SZ; + if((FOFFSET)lseek(fp,off,SEEK_SET) != off || + read(fp,buf,VTBL_SZ) != VTBL_SZ) + { + printf("\nerror reading region at %lx",off); + break; + } + + } + if(reg ==1 && get_mdid(buf)) + { + printf("\nfailed to parse MDID strings"); + break; + } + // force tag + lptr = (DWORD*) key_reg[reg]; + *((DWORD *)buf) = *lptr; // set keyword identifier + } + } + else if((key=has_key_word(ln,key_words[reg],&args)) >= 0) + { + + printf("\nkey: %s arg %s",key_words[reg][key],args); + hits++; + if(reg == 0) // doing a VTBL + { + tst = 1; + switch(key) + { //"desc","flag","dirsz","datasz","label","comp" + // should desc and lable be padded with blanks? + case 0: // desc + strncpy(vtbl->desc,args,44); + break; + case 1: // flag + if((tst=sscanf(args,"%x",&v)) == 1) + vtbl->flag = v & 0xff; + break; + case 2: // dirsz + if((tst=sscanf(args,"%lx",&lv)) == 1) + vtbl->dirSz = lv; + break; + case 3: // datasz + if((tst=sscanf(args,"%lx",&lv)) == 1) + vtbl->dataSz[0] = lv; + break; + case 4: // label + strncpy(vtbl->sdrv,args,16); + break; + case 5: // comp + if((tst=sscanf(args,"%x",&v)) == 1) + vtbl->comp = v & 0xff; + break; + } + if(tst != 1) + printf("\nfailed to parse: %s",ln); + } + else if(reg == 1 && key < MDID_SZ) + { + hits++; + tst = strlen(args); + if(tst == 0) + str = NULL; + else if((str = malloc(tst)) == NULL) + { + printf("\nalloc error for: %s",args); + break; + } + else + strcpy(str,args); + + if(mdid_str[key] != NULL) + free(mdid_str[key]); + mdid_str[key] = str; // new string + +// MediumID - unique 19 decimal digits for identification +// VR - version? always 0100 +// CS - ? followed by 4 hex bytes +// FM - ? always followed by '2'? format? +// UL - user label, ascii input string +// DT - datetime of archive creation as 8 hex bytes + } + } + } while(lret != NULL); + } + if(fp != EOF) + close(fp); + if(fd != NULL) + fclose(fd); + return(suc); +} + +int getnstr(char *buf,int len) +{ + int ret; + char *res; + if((res = fgets(buf,len,stdin)) != NULL) + { + ret = strlen(buf); + if(ret > 0 && buf[ret-1] == '\n') + buf[--ret] = 0; // remove it + } + return(ret); +} + +int create_vtbl(struct vtbl_ver *cvtbl) +{ + int i,len,ret=0; + char cdate[15]; + while(ret == 0) + { + printf( +"\nVTBL memory image has been cleared, Win95 output format is set"); + printf( +"\nThe offsets for data and catalog set will be obtained from -sd and -sc"); + printf( +"\n if the -sd option is omitted the catalog is assumed to follow last data segment"); + memset((char *)cvtbl, 0, sizeof(struct vtbl_ver)); + strncpy(cvtbl->vtbl.tag,"VTBL",4); + cvtbl->ver = RECREAT; + cvtbl->vtbl.OStype = 7; // Win 95 format, I ignore, but use this as default + printf( +"\nDefault is WINME format, ie first segment of each volume's data not compressed"); + printf("\nIs this actually WIN95 format - ie no MDID region (Y/N) "); + if(bques(0) == 1) // this controls headers written + cvtbl->ver |= WIN95; + else + cvtbl->ver |= WINME; + + cvtbl->vtbl.rev_major = 1; // these are my ver # not MicroSoft + cvtbl->vtbl.rev_minor = 11; + printf("\nWhat date would you like displayed (format mon/day/year): "); + i = getnstr(cdate,14); +// never understood this fudge, see msqic.c:disp_vtbl() +// my file dates work why does this require the fudge + cvtbl->vtbl.date = mk_date(cdate) + VTBL_DATE_FUDGE; + cvtbl->cnt = cvtbl->ndx = 1; // make do_decompress do WIN95 style output + printf("\nForce data compression flag? (Y/N) "); + if(bques(0) == 1) + { + cvtbl->vtbl.comp = 0x81; + // only update descriptions for compressed file, only time vtbl may be written + printf("\nDevice Lable (max 15 chars): "); + i = getnstr(cvtbl->vtbl.sdrv,15); + printf("\nDescription (max 43 chars): "); + i = getnstr(cvtbl->vtbl.desc,43); + } + printf("\nESC to abort, 'N' repeat above, or 'Y' to use above: "); + ret = bques(27); + } + if(ret == 1) + ret = 0; + else + ret = 1; + return(ret); +} + +/* from msqic.c on 12/22/03 */ +// directory tree routines are recursive (ie not efficient, but easy) + +#define IND_LEV 25 // was hard coded as 10 + +void disp_tree(CAT_LIST *ccat,int level,BYTE mode) +{ + BYTE i; + // make static so they don't take a lot of stack space + static char indent[IND_LEV*2+1]; // need a +1 for trailing nul + // definately was crashing without it + if(level <= 0) + { + i = 0; + indent[0] = 0; + if(mode & DIRONLY) + printf("\nListing of directory tree, no FILES displayed"); + } + else + { + i = (level-1) *2; + if(i < IND_LEV) + strcpy(&indent[i]," "); + } + while(ccat != NULL) // remove debug break on: cnt++ < 30 + { // add offsets in segment 10/8 off a little if compressed + if(!(mode & DIRONLY) || (ccat->flag & SUBDIR)) // display name + { + printf("\n%s%s",indent,ccat->name); + if(mode & S_SEGS) // display segment/offset debug info + printf(" %lx:%lx", + ccat->data_off / SEG_SZ, ccat->data_off % SEG_SZ); + } + if(ccat->child != NULL) + disp_tree(ccat->child,level+1,mode); + ccat = ccat->next; + } + indent[i] = 0; // remove padding on exit +} + +// add mode flag so can search for SUBDIR or a FILE == 0 +CAT_LIST *tree_node(CAT_LIST *ccat,char *str,BYTE mode) +{ + int len; + while(ccat != NULL) + { + len = strlen(ccat->name); + if(strncmp(str,ccat->name,len)==0) + { + if(*(str+len ) == 0 && (ccat->flag & mode) == mode) + // exact match, end of target string, has correct mode + return(ccat); + else if ((ccat->flag & SUBDIR) && *(str+len) == DELIM && + ccat->child != NULL) + return(tree_node(ccat->child,str+len+1,mode)); + } + ccat = ccat->next; + } + return(NULL); // not found +} + +/* recursively look for next node + at first tried depth first, but it isn't really + or wouldn't allow sub dir's at same level before those below + see sample from ralf and notes in 4_dir.lst + this routine tries to localize search from current node + when hit ENDDIR + Interesting, it can go down as may have added some directories + below this. An can scan next, but don't let it recursively + scan up +*/ +CAT_LIST * next_tree_node(CAT_LIST * ccat) +{ + CAT_LIST * rnode=NULL, *ncat; + // check for valid children after this node + + if(ccat != NULL && !(ccat->flag & EMPTYDIR) && (ccat->flag & SUBDIR)) + { + if(ccat->child != NULL && + !(ccat->child->flag & EMPTYDIR) && (ccat->child->flag & SUBDIR)) + rnode = ccat->child; + else // check for others after immediate child + rnode = next_tree_node(ccat->child); + } + + /* this node has no valid children, try next + some serious questions about order here. + apparently trace down children of next (depth first) + rather than (breadth first) checking all next in current chain + */ + if(rnode == NULL && ccat != NULL) + { + ncat = ccat->next; + while(ncat != NULL && rnode == NULL) + { + if(!(ncat->flag & EMPTYDIR) && (ncat->flag & SUBDIR)) + { // check for children before using this node + if((rnode = next_tree_node(ncat->child)) == NULL) + rnode = ncat; // use this as has no valid children + } + else + ncat = ncat->next; + } + } + return(rnode); +} + + +CAT_LIST *new_cat_list(CAT_LIST *prev,char *name,long off,struct ms_dir_fixed *fixed) +{ char *str; + CAT_LIST * new=NULL; + if((str = strdup(name)) != NULL && + (new = malloc(sizeof(CAT_LIST))) != NULL) + { + new->name = str; + new->parent = prev; + new->child = new->next = NULL; + new->data_off = off; + if(fixed != NULL) + { + new->flag = fixed->flag; + new->attrib = fixed->attrib; + new->datetime = fixed->m_datetime; + new->file_len = fixed->file_len; + } + else + { + new->flag = SUBDIR; // this is all to handle root, must be better way + new->attrib = 0; + new->datetime = 0; + new->file_len = 0; + } + } + return(new); +} + + +void free_cat_list(CAT_LIST *root) +{ + CAT_LIST *this; + while(root != NULL) + { + free_cat_list(root->child); + this = root; + root = root->next; + free(this); + } +} + +/* extract one file. may be called from main via -x or from do_redirect() + CAUTION if the file is compressed, it repeats the decompression cycle + for EACH file. If you have a lot of small files in a segment this is + REDUNDANT. Oh well. Maybe you should decompress the entire file first. +*/ +int do_extract(int fp,struct vtbl_ver *v,char *name,CAT_LIST *ccat) +{ + // extern references to stuff in decompression section which may get moved + extern int fin,fout; + extern long comp_wr; + extern char *dcompfnm; // working file name + int i,j,rd,fo=EOF,ft=EOF,suc = 1,cnt=0; + WORD sz; // temp store for segment size with RAW_SEG cleared + char *suc_xmsg = "\nSuccessful extraction"; + FOFFSET soff=LSEEK_ERR, // save start offset into working file dcompfnm after decompress + cur_pos, // position in input file + tlng; + struct cseg_head shead; + + + if(access(name,0) == 0) + { + printf("\nfile exits: %s\n overwrite (Y/N) ",name); + if(bques(0) != 1) + return(-1); + } + if ((fo=open(name,O_BINARY|O_RDWR|O_CREAT|O_TRUNC, + S_IREAD|S_IWRITE)) == EOF) + printf("\nfailed to open %s",name); + else if((v->vtbl.comp & 0x80) == 0) // not compressed, just grab it + { + fin = fp; + fout = fo; + if(lseek(fp,ccat->data_off+v->database,SEEK_SET) == LSEEK_ERR) + { + printf("\nseek failed"); + suc = -1; + } + else if((suc = copy_region(ccat->file_len)) != 0) + printf("\nextract failed"); + else + printf(suc_xmsg); + } + else // its compressed, need to decompress seg(s) of interest first + // do limited range of logic in do_decompress() + { + if ((ft=open(dcompfnm,O_BINARY|O_RDWR|O_CREAT|O_TRUNC, + S_IREAD|S_IWRITE)) == EOF) + printf("\nfailed to open working file: %s",dcompfnm); + fin = fp; // set up globals for decompression + fout = ft; + + soff = cur_pos = v->database; // start of data section + rd = 0; + // loop through looking for segment(s) or interest + while((tlng=lseek(fin,cur_pos,SEEK_SET)) == cur_pos && + (rd=read(fin,&shead,sizeof(shead))) == sizeof(shead)) + { +/* either it is known to start in prior segment or + prior is last segment need to process to find out for sure +*/ + if(ccat->data_off < shead.cum_sz || + shead.seg_sz == 0) + cnt++; + else if(cnt == 0) // update soff + soff = cur_pos; + if(ccat->data_off + ccat->file_len <= shead.cum_sz || + shead.seg_sz == 0) // there are no more segments! + break; // know we got it all in prior segment + sz = shead.seg_sz & ~RAW_SEG; // clear flag + // add 6/01/07 for WIN95 decompress + cur_pos += SEG_SZ; + // cur_pos += sz+rd; // old code was WINME specific + rd = 0; // be sure this gets updated + } + if(cnt ==0) + { + printf("\nFailed to find segment(s) containing data"); + return(suc); + } + // now extract data region of interest, cnt segments to working file + rd = 0; + cur_pos = soff; + soff = LSEEK_ERR; + i = 0; + while((tlng=lseek(fin,cur_pos,SEEK_SET)) == cur_pos && + (rd=read(fin,&shead,sizeof(shead))) == sizeof(shead)) + { + sz = shead.seg_sz & ~RAW_SEG; // clear flag + if(soff == LSEEK_ERR) // get start offset in 1st working file seg + soff = ccat->data_off - shead.cum_sz; + if(shead.seg_sz & RAW_SEG) // its not compressed + { + if((j = copy_region((long)sz)) != 0) + break; + } + else + decomp_seg(); // currently has no return! + if(++i >= cnt) + break; // do not clear rd, use as read(cseg_head) test + // input file position is a little random, do a seek + cur_pos += rd+sz; // where we want to be for next shead + rd = 0; // be sure this gets updated + } + // depends on global comp_wr being set by copy_region & decomp_seg() + // last seg needs special handling as shead.cum_sz = 0 + if(shead.cum_sz == 0 && shead.seg_sz == 0) + shead.cum_sz = v->vtbl.dataSz[0]; + if(rd < sizeof(shead) || i < cnt || + ccat->data_off + ccat->file_len > shead.cum_sz+comp_wr) + printf("\nabort: error decompressing data"); + else + { + printf("\nDecompressed %d segments to temp working file",cnt); + fin = ft; + fout = fo; + if( (cur_pos = lseek(ft,soff,SEEK_SET)) != soff || + (suc = copy_region(ccat->file_len)) != 0) + printf("\nextract from working file failed"); + else + printf(suc_xmsg); + } + + } + if(fo != EOF) + { + close(fo); + set_fattrib(name,ccat->datetime,ccat->attrib); + } + if(ft != EOF) + close(ft); + return(suc); +} + + +#define PATH_LEN 255 +// make the following global to limit amount of stack space used in tree_copy() +char tree_path[PATH_LEN+1]; // make this global to simplify recursion stack +static struct stat sbuf; // used by do_redirect() and tree_copy() + + +/* this is a recursive copy routine + copies all files in this subdir + if term == '*' will also copy all files in all subdir's + + note uses global tree_path[PATH_LEN] to store file paths + +ccat->flag & SUBDIR && !(ccat->flag & EMPTYDIR) +*/ +int tree_copy(int fp,struct vtbl_ver *v,CAT_LIST *ccat,int start,char term) +{ + int len,cnt=0,err; + while(ccat != NULL) + { + err = 0; + len = strlen(ccat->name); + if(start+len >= PATH_LEN) + { + printf("\nbuffer PATH_LEN exceeded: %s%c%s", + tree_path,DELIM,ccat->name); + if(term == '*' || term == '+') + fputc(DELIM,stdout); + err++; + } + else if(len > 0) + strcpy(tree_path+start,ccat->name); // append file of sub dir name + else + { + printf("\ndirectory name can not be empty"); + err++; + } + + if(ccat->flag & SUBDIR) + { + if(err == 0 && (term == '*' || term == '+')&& !(ccat->flag & EMPTYDIR)) + { + if((err = stat(tree_path,&sbuf)) == 0 && // it exists + !(sbuf.st_mode & S_IFDIR) ) + { + printf("\nsub dir path used as file: %s",tree_path); + err++; + } + else if(term == '+') // try to create the directory + { +#ifdef unix + // accessable & searchable + err = mkdir(tree_path,S_IREAD | S_IWRITE | S_IEXEC); +#else + err = mkdir(tree_path); +#endif + if(err) + printf("\nmkdir(%s) failed",tree_path); + } + + if(err == 0) + { + // attempts to copy all sub directories + tree_path[start+len++] = DELIM; + cnt += tree_copy(fp,v,ccat->child,start+len,term); + } + } + // else if term == DELIM ignore subdir + } + else // must be a file in this dir, extract it + if(err == 0 && + (len=do_extract(fp,v,tree_path,ccat)) == 0) + cnt++; + ccat = ccat->next; + } + return(cnt); +} + +/* step through list of paths + find via tree_node() + then step through copying file +*/ +long do_redirect(int fp,struct vtbl_ver *v,CAT_LIST *root,PATH_ELEM paths[],int rcnt) +{ + CAT_LIST *ccat; + PATH_ELEM *pe; + char *des; + int p,i,derr; + long cnt=0; + printf("\nDoing Redirectable path based extract with %d source paths",rcnt); + for(i=0;iredirect != NULL) + des = pe->redirect; // use redirect path (may be empty) + else + des = pe->path; // use source path + /* + 1/20/04 try new system, named root for all trees, Win98 or Win95 + */ + ccat = tree_node(root,pe->path,SUBDIR); + + derr = 0; + p = strlen(des); + if(ccat == NULL) + printf("\nsource path %d not found: %s",i,pe->path); + else if(ccat->flag & EMPTYDIR || ccat->child == NULL) + printf("\nsource directory empty: %s",pe->path); + else if(p > 0 && ((derr = stat(des,&sbuf)) != 0 || + !(sbuf.st_mode & S_IFDIR) ) ) + // if p == 0 we are using current directory, it better exist! + { + if(!(sbuf.st_mode & S_IFDIR)) + { + printf("\nredirect path is not a subdir: %s",des); + derr++; + } + else if(pe->term == '+') // try to create the directory + { +#ifdef unix + // accessable & searchable + derr = mkdir(des,S_IREAD | S_IWRITE | S_IEXEC); +#else + derr = mkdir(des); +#endif + if(derr) + printf("\nmkdir(%s) failed",des); + } + } + + if(derr) + printf("\n unavailable destination path: %s",des); + else if(p+1 >= PATH_LEN) + printf("\nabort, path buffer length exceeded"); + else if(ccat != NULL) + { + if(p > 0) // there is a des path, its not current directory + { + strcpy(tree_path,des); // global tree_path[] modified by tree_copy + tree_path[p++] = DELIM; + } + cnt += tree_copy(fp,v,ccat->child,p,pe->term); + } + } + return(cnt); +} + + +/* path remapping routines added 1/11/04 + handles quoted strings which are required for LFN access +*/ +#define LN_SZ 255 + +int get_paths(char *fn,struct path_elem paths[],int sz) +{ + FILE *fp; + int i,r=0,cnt = 0,ln=0,len; + char quoted,*ch,line[LN_SZ+1]; + if((fp = fopen(fn,"r")) == NULL) + { + printf("\nfailed to open redirect command file: %s ",fn); + return(-1); // open error + } + while(cnt < sz) + { + if(fgets(line,LN_SZ,fp) == NULL) + { + if(feof(fp)) + printf("\neof after line %d",ln); + break; + } + else + { + paths[cnt].term = 0; // validatation + quoted = 0; + ln++; + len = strlen(line); + while(len > 0) + if(line[len-1] == '\n' || line[len-1] == ' ') + line[--len] = 0; // strip lf && trailing spaces so have string + else + break; + if(len <= 0 || line[0] == '#') + { + printf("\nskipping empty line %d",ln); + continue; + } + + ch = line; + // skip leading blanks and possible quote + while(*ch != 0 && *ch == ' ') + { + len--; + ch++; + } + + // allocate room for trailing NUL + if((paths[cnt].path = malloc(len+1)) == NULL) + { + printf("\nfatal alloc error"); + break; + } + i = 0; + while(*ch != 0 && (quoted || *ch != ' ') && i < len) + { + if(*ch == '"') + quoted = quoted ^ 1; + else + *(paths[cnt].path+ i++) = *ch; + ch++; + } + + if(i > 0) + paths[cnt].term = *(paths[cnt].path+ --i); // has a last char, save it + if(i == 0) + printf("\nerror line %d source path has length 0!",ln); + if(paths[cnt].term != DELIM && paths[cnt].term != '*' && + paths[cnt].term != '+') + { + printf("\nerror line %d in source path must end in %c, *, +", + ln,DELIM); + i = -1; + } + else if (quoted) + { + printf("\nerror line %d unmatched quotes in source path",ln); + i = -1; + } + + if(i > 0) + { + *(paths[cnt].path+ i++) = 0; // terminate source path + + while(*ch != 0 && *ch == ' ' && i < len) // find start next + { + ch++; + } + + + if(*ch != 0 && i < len) + paths[cnt].redirect = paths[cnt].path+ i; // 2nd path exists + else + paths[cnt].redirect = NULL; // not redirected + while(*ch != 0 && (quoted || *ch != ' ') && i < len) + { + if(*ch == '"') + quoted = quoted ^ 1; + else + *(paths[cnt].path+ i++) = *ch; + ch++;r++; + } + *(paths[cnt].path+ i) = 0; // terminate redirect + if(!(*ch == 0 || *ch == ' ') || quoted || r == 0) + { + printf("\nerror line %d invalid redirect path %s", + ln,paths[cnt].redirect); + i = -1; + } + // check termination, will append strings to this. + // for consistency must end with DELIM + else if(*(paths[cnt].path+ i -1) != DELIM) + { + printf("redirect path must end with %c\n %s", + DELIM,paths[cnt].redirect); + i = -1; + } + else + { + if(strcmp(paths[cnt].redirect,"./") == 0 || + strcmp(paths[cnt].redirect,".\\") == 0) + // its redirected to current directory, redirect path empty + paths[cnt].redirect = paths[cnt].path+ i; + // terminate string, r == strlen(paths[].redirect) + *(paths[cnt].path+ i-1) = 0; // delete DELIM + cnt++; + } + } + if(i <= 0) + { + printf("\n skipping: %s",line); + free(paths[cnt].path); + continue; + } + } + } + return(cnt); // # valid paths +} + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.C" new file mode 100644 index 0000000..48d8838 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.C" @@ -0,0 +1,1438 @@ +/* ntbkup.c + Copyright (C) 2003 William T. Kranz + Check http://www.fpns.net/willy/msbackup.htm for updates. + contact info above or via snailmail: p.o. box 333, bradford, nh 03221 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +----------------------------------------------------------------- + History: + + 12/29/03 rename xpbkup.c version 1.03 and change to + the MTF structures which I was forcing with the _MTF define. + 12/31/03 seems to be working. Should add CSUM stuff and + do a little more testing, but think its there. + 01/01/04 added FOFFSET logic for 4GB + also tweak do_file() changing return value if read file + and try to add CSUM logic. Think I have it, but not + quite sure why my padding algorithm works! + + Interesting MSVC 5.0 cl seemed to promote the addition of + two WORDs to sizeof(int). see disp_unicode() calls. + I moved addition of params outside paramater block + ie ud = start+end + disp_unicode(start,ud) instead of disp_unicode(start,start+end) + and warnings went away. Seems dumb! + + watch leading '/' stuff in do_filter() logic + + 01/15/04 add prt_foff() to handle FOFFSET display + gets a bit messy, I only parse command line args for start + position as longs, then cast so limits some of my searches. + + changed some of returns link do_tfdd() to FOFFSET + 01/19/04 fix some Linux -Wall warnings about unused vars + and add my own strupr() if unix (may be a problem in CYGWIN...) + 01/23/04 change version to 1.04, change -p to -l option + to limit based on path and use -p in future as I do in msqic + Add mode & LPATH define to control new -l option instead of depending + on path != null. Want to use path in XTREE mode also + + 01/24/04 make return from do_file and FOFFSET, should have been + add SILENT to mode in main() for most options + 01/27/04 add set exclusion concept to tree creation, if -s + is used, it limits what's added to directory tree + -p no call do_redirect allowing * and + as terminator + 03/16/04 bump to version 1.03 see nttree and -p issue + also tweak mode flags for LPATH so display less info + 05/17/04 bump to version 1.04 tweat is_match() for multiple '.' + and nttree.c:find_path() to allow network drive specifications + 06/02/04 bump version to 1.05, add mode argument + to do_dirb() so don't display directories if mode & SILENT + 07/08/04 bump version to 1.06, change do_file() so that it creates + an empty file for FILE tags where there is no STAN stream + remove obsolete error message regarding 'failed to find STAN' + 04/05/05 bump version to 1.07 copy prior source to ntbkup04.c + correct some bugs parsing args, -s and -f + add include get_foff.c for parsing 64 bit offsets + + may be getting carried away, but change type of len + in fnd_target() to FOFFSET, oops it had an error also + change how deal with rd + + add optional end option via -j#:# for main loop + 12/20/06 change version to 1.07b to reflect change in nttree increasing + LN_SZ from 255 to 600 + 12/30/06 change version to 1.07c as surround MAX_PATH define + with an ifndef to allow changing on command line and + increase default for 25 to 100. Also add display at startup +------- + per dos.h: File attribute constants + +#define _A_NORMAL 0x00 Normal file - No read/write restrictions +#define _A_RDONLY 0x01 Read only file +#define _A_HIDDEN 0x02 Hidden file +#define _A_SYSTEM 0x04 System file +#define _A_VOLID 0x08 Volume ID file +#define _A_SUBDIR 0x10 Subdirectory +#define _A_ARCH 0x20 Archive file + +*/ + +#include +#include +#include +#include // for toupper() +#include // define memset +#include +#include /* for open() defines */ + +#if defined(MSDOS) || defined(_WIN32) +# ifdef MSDOS +# define OS_STR "MSDOS" +# else +# define OS_STR "WIN32" +# endif +#define DELIM '\\' + +#include +#include // for lseek +#include +#else +# ifdef __CYGWIN__ +# define OS_STR "CYGWIN" +#else +# define OS_STR "LINUX" +#endif +#include // for lseek, read, write +#include +#define DELIM '/' +#define strnicmp strncasecmp +#define stricmp strcasecmp +#endif + +#ifdef _WIN32 +// this redefinition must be last! ie after io.h +#define lseek _lseeki64 +#endif + +#include "ntbkup.h" // includes FOFFSET defines + +#include "get_foff.c" + +// a prototypes for functions called before defined +FOFFSET do_file(int fp, FOFFSET soff,int mode); // uses global char *filter +// prototypes for functions in nttree.c +DIR_LIST *find_tree(DIR_LIST *next,char *str); +DIR_LIST *find_path(DIR_LIST *next, char *str); +void disp_tree(DIR_LIST *d); +int add_tree_node(DIR_LIST **root, FOFFSET off, int key); +int path_extract(int fp, DIR_LIST *node,unsigned long *fskip); // not used in this module +int get_paths(char *fn,struct path_elem paths[],int sz); +int do_redirect(int fp, DIR_LIST *root,struct path_elem *paths,int rcnt); + + + + +#define LBUF_SZ 512 // local buffer on stack + +BYTE buf[XPBLK_SZ]; // used by most read routines. Assumes + // all blocks smaller than this, checks if correct + +char tree_path[PATH_LEN+1]; // make this global to simplify recursion stack + +#ifdef _4GB +#define MAXINC 0x7fffffffL + +/* FOFFSET is an unsigned long when _4GB is defined + this limits SEEK_CUR and SEEK_END to calls with off == 0 + to get file size or current position, ie not a generic routine +*/ +FOFFSET lseek4(int fp,FOFFSET off,int origin) +{ + FOFFSET roff,lret=LSEEK_ERR; + long inc; + if(origin == SEEK_CUR || origin == SEEK_END) + { + if(off != 0L) + printf( +"\nfatal error lseek called with none zero offset for SEEK_CUR or SEEK_END"); + else + return((FOFFSET)lseek(fp,0L,origin)); + } + // at two calls may be required + else if(origin == SEEK_SET) + { + while(lret == LSEEK_ERR) + { + if(off <= MAXINC) + inc = off; + if((roff = lseek(fp,inc,origin)) == LSEEK_ERR) + break; + off -= inc; + if(off == 0) + lret = roff; // we are done + } + } + return(lret); +} +#endif + + + +// see ntbkup.h for index defines into array below for is_keyword() +char *keywords[] = { + "TAPE", // 0 + "SFMB", + "SSET", // 2 + "VOLB", + "DIRB", // 4 + "FILE", // 5 + "ESET", + "EOTM", + "CFIL", + "ESPB", // 9 +// MTF_MAX_BLK = ESPB = 9 rest are stream hdrs, although some on blk boundry + "TSMP", + "TFDD", // 11 + // from here on not on XPBLK_SZ boundry, above seem to be + "NACL", + "NTQU", // 13 + "CSUM", + "STAN", + "SPAD", + "FEND", // 17 terminates TFDD region + "NTOI", // follows CSUM in an NACL region + NULL +}; + +// return -1 on failure, or index into keywords[] +int is_keyword(char *str) +{ + int i=0,j; + if(*((long *)str) == 0) + return(NUL_KEY); + while(keywords[i] != NULL) + { + for(j=0;j<4;j++) + if(*(keywords[i]+j) != *(str+j)) + break; + if(j == 4) + return(i); + i++; + } + return(-1); +} + +void get_xpdatetime(BYTE t[],unsigned short *yr, BYTE *mon, BYTE *day, BYTE *hr, + BYTE *min, BYTE *sec) +{ + *sec = t[4] & 0x3f; // 6 bits + *min = ((t[3] << 2) + (t[4] >> 6)) & 0x3f; // next 6 bits + *hr = t[3] >> 4; // low order 4 bits of hour. + if(t[2] & 1) // high bit of hour for full 5 bit value + *hr += 0x10; + *day = (t[2] >> 1) & 0x1f; // 5 bits + *mon = ((t[1] << 2) + (t[2] >> 6)) & 0xf; // 4 bits + *yr = t[0]; + *yr = (*yr << 6) + (t[1] >> 2); // 14 bits +} + + +// warning no length qualifiers allowed in fmt, "ld" or "lx" expected +void disp_offset(char *fmt,FOFFSET off) +{ + unsigned long ll=0x8000000L; + if(off < 0) + printf("< 0"); + else if(off < ll) // can display directly + { + ll = off; + printf(fmt,ll); + } + else // its a 64 bit long long, break it up + { + ll = off >> 32; + printf(fmt,ll); // high order bytes + ll = off & 0xffffffff; + printf(fmt,ll); // low order bytes + } +} + +void disp_xpdatetime(BYTE t[]) +{ + WORD yr; + BYTE mon,day,hr,min,sec; + char str[4]; + get_xpdatetime(t,&yr,&mon,&day,&hr,&min,&sec); + if(hr < 12) + { + strcpy(str,"AM"); + if (hr == 0) + hr = 12; + } + else + { + strcpy(str,"PM"); + if(hr > 12) + hr -= 12; + } + printf("%02d/%02d/%04d %02d:%02d:%02d %s", + mon,day,yr,hr,min,sec,str); + +} + +/* both unicode routines assume global buf[start] is start of unicode + and MAX is next char after end of string + ignores every 2nd char, ie treats as ascii + return # chars transfered just for giggles +*/ +int disp_unicode(WORD start, WORD max) +{ + int len = 0; + char *ch= (char*)buf+start; + while(start < max) + { + fputc(*ch,stdout); + ch+=2; + start+=2; + len++; + } + return(len); +} +/* my original notes suggested if it was just the root the unicode + started with '/' but if there was a path it started with first + char. + + Above seem incorrect. There is never a leading '/' + The path delimiter is a WORD = 0 in the string. + These should be replaced with the OS specific DELIM + Prior to 1/23/04 I used '/' + I insert a leading '/' if the path does not start with 0 + AND contains more than one char. + ie its not just the root, but sub dir spec follows. + replace all occurances of 0 with DELIM in string which + always ends with a 0. +*/ +int disp_unipath(WORD start, WORD max) +{ + int len = 0; + char *ch= (char*)buf+start,cout; + // odd, if root start with '/', else start with 1st path and no '/' + // maybe it has a zero for last '/' ?? + if(max - start > 0 && *ch != 0) + fputc(DELIM,stdout); + // else code below puts up the leading and trailing '/' is 0 + while(start < max) + { + if((cout = *ch) == 0) + cout = DELIM; + + fputc(cout,stdout); + ch+=2; + start+=2; + len++; + } + return(len); +} + +/* Warning returns chars read, but may insert a leading '/' + so string is longer by 1 char +*/ +int getn_unipath(char *ch, WORD start, WORD max, int len) +{ + char inc=0; + if(max - start > 0 && *((char*)buf+start) != 0) + { + *(ch++) = DELIM; + inc = 1; + } + if(start+2*(len-inc) < max) + max = start+2*(len-inc); + len = 0; // use as counter now for chars read, does not include 1st DELIM + // else code below puts up the leading and trailing '/' is 0 + while(start < max) + { + *ch = *((char *)buf+start); + if(*ch == 0) + *ch = DELIM; + ch++; + start+=2; + len++; + } + *ch = 0; // always terminate + return(len); +} + + +// copy uni-code to ascii buffer with max len chars +int getn_unicode(char *ch,WORD start,WORD max,int len) +{ + if(start+2*len < max) + max = start+2*len; + len = 0; // use as counter now + while(start < max) + { + *(ch++) = buf[start]; + start+=2; + len++; + } + *ch = 0; // assume space after len for NUL, always terminate + return(len); +} + + +// do_ routines below which don't return a long assume all data in one block +// those that do return a long return # bytes in region or -1 if don't know +void do_tape() // assume fits in one block +{ + MTF_TAPE *tptr = (MTF_TAPE *)buf; + MTF_TAPE_ADR *sdat; + WORD end; + fputc('\n',stdout); + sdat = &tptr->media_name; + end = sdat->offset+sdat->size; + // add end for MSVC 5.0, gives warning if don't construct per below + disp_unicode(sdat->offset,end); + fputc('\n',stdout); + sdat = &tptr->media_label; + end = sdat->offset+sdat->size; + disp_unicode(sdat->offset,end); + fputc('\n',stdout); + sdat = &tptr->software_name; + end = sdat->offset+sdat->size; + disp_unicode(sdat->offset,end); + fputc('\n',stdout); + fputc('\n',stdout); +} + +void do_sset(int set) // assume fits in one block +{ + WORD end; + MTF_SSET *sptr = (MTF_SSET *)buf; + printf("\nSet %d: ",sptr->set_num); + printf("\nName: "); + end = sptr->set_name.offset +sptr->set_name.size; + disp_unicode(sptr->set_name.offset,end); + printf("\nDescription: "); + end = sptr->set_descript.offset +sptr->set_descript.size; + disp_unicode(sptr->set_descript.offset,end); + printf("\nUser: "); + end = sptr->user_name.offset +sptr->user_name.size; + disp_unicode(sptr->user_name.offset,end); + printf("\n"); +} + +// note main reads XPBLK_SZ bytes, first MTF_STREAM_HDR will always fit +// data following may... +// add display arg 12/8/05 if 0 advances without display to skip region +FOFFSET do_tfdd(int fp, FOFFSET diroff,char display) +{ + int i,j,key,rd; + WORD ud,*wptr,off; + FOFFSET foff=0; // foff for cumulative read offset + // initializations below only valid after reads + MTF_FDD_FILE *file = (MTF_FDD_FILE *)buf; // same as DIRB + MTF_FDD_VOLB *volb = (MTF_FDD_VOLB *)buf; + MTF_FDD_HDR *head = (MTF_FDD_HDR *)buf; + + off = sizeof(MTF_STREAM_HDR); // skip FDD stream header + /* the sub sections are all part of FDD, contained in .var_sz + should terminate with FEND, but watch for next block ID + next MTF_FDD_HDR starts with {WORD length, BYTE tag[]} ie 8 bytes + */ + while((key=is_keyword(buf+off+sizeof(WORD))) != FEND && key > -1) + { + if(key != VOLB && key != DIRB && key != FILE_K) + break; // invalid key for FDD area + wptr = (WORD *)(buf+off); // length next region + foff+=off; + off = *wptr; // next offset after read + i = j = 0; + rd = *wptr + 8; // makes next length and tag valid + if(rd > XPBLK_SZ ||(FOFFSET) lseek(fp,diroff+foff,SEEK_SET) != diroff+foff || + (j = read(fp,buf,rd)) != rd) + { + printf("\nTFDD fatal read error"); + return(LSEEK_ERR); + } + else if(display) + { + printf("\n%4.4s: ",head->tag); + if(key == VOLB) + { + if(volb->dev_name.size > 0) + { + printf("\nDevice Name: "); + ud = volb->dev_name.offset+volb->dev_name.size; + disp_unicode(volb->dev_name.offset,ud); + } + if(volb->vol_name.size > 0) + { + printf("\nVolume Name: "); + ud = volb->vol_name.offset+volb->vol_name.size; + disp_unicode(volb->vol_name.offset,ud); + } + if(volb->mach_name.size > 0) + { + printf("\nMachine Name: "); + ud = volb->mach_name.offset+volb->mach_name.size; + disp_unicode(volb->mach_name.offset,ud); + } + + } + else if (key == DIRB) + { + if(file->name.size) + { + ud = file->name.offset+file->name.size; + disp_unipath(file->name.offset,ud); + } + } + else if(key == FILE_K) + { + printf(" %8lu @ blk = %4lu ", + (DWORD)head->disp_sz,(DWORD)head->fmt_adr); + disp_xpdatetime(file->modify); + printf(" "); + // this is the long name [need to add MTF_TAPE_ADR] + ud = file->name.offset+file->name.size; + disp_unicode(file->name.offset,ud); + } + } + if(key == FEND) + { + if(display) + printf(" end of directory\n"); + foff+= 8; // we read last 8 bytes, add it + break; + } + + } + return(foff); // bytes read + +} + + + +/* + was unsure of this. MTF clearifies + 12/08/03 with additional examples, below isn't generic + Phillip's C: hits an NACL region first and its different. + I think its safer to just scan for next tag than guess at this + leave this routine for reference, but don't call it! + This worked when keyword at offset tag_head.var_sz was NTQU + + Below was developed from only two drives so may well be + specific to them. No idea how many other keywords might be here! + + if((key = is_keyword(buf+off)) == NTQU) + { + wptr = (WORD *)(buf+off+20); // offset into NTQU region + lret = off + *wptr; // data length of DIRB, may have word checksum at end? + } above takes one to within 3 bytes of end of SPAD region + below is better. + + Algorithm below will fail if doesn't find an SPAD and + on return(-1) should resync +*/ +long do_dirb(int fp,FOFFSET soff,int mode, int *nmlen) +{ + long lret=0; + MTF_DIRB *dir = (MTF_DIRB *)buf; + WORD sz,start; + int i; + char ch,*cptr; + + if(dir->head.str_type) // have OS string + { + sz = dir->head.OS_data.size; + start = dir->head.OS_data.offset; +#ifdef TEST + printf("\nOS data[%d] @ 0x%x ",sz,start); +#endif + } + sz = dir->name.size; + start = dir->name.offset; + + if(!(mode & SILENT) && sz && start+sz <= XPBLK_SZ) + { + printf("\nDir Name[%d]: ",sz); // leading slash off root assumed + disp_unipath(start,(WORD)(start+sz-1)); + } + + // return actual name length, copy up to max length to global tree_path[] + i = getn_unipath(tree_path,start,start+sz-1,PATH_LEN); + if(i != sz/2) // read truncated + i = -1; + else + i = 1; + *nmlen = i * strlen(tree_path); // actual string length always valid + + return(lret); +} + +int match_path(char *path,int path_len) +{ + int i,sz,suc = 0; + char *ch; + MTF_DIRB *dir = (MTF_DIRB *)buf; + sz = dir->name.size; + ch = buf+dir->name.offset; + if(sz/2 >= path_len && dir->name.offset+path_len <= XPBLK_SZ) + { + if(*(path+path_len-1) != '*' && sz/2 != path_len) + path_len = -1; // no wild card, no match + for(i=0;i XPBLK_SZ) + { + coff += off; + off = 0; + if ((FOFFSET)lseek(fp,coff,SEEK_SET) != coff || + (rd=read(fp,buf,XPBLK_SZ)) != XPBLK_SZ) + { + if((off=(FOFFSET)lseek(fp,0L,SEEK_END)) != coff+rd) + { + printf("\nEOF at ");disp_foffset(off); + } + else + { + printf("\ndo_streams() file advance error at "); + disp_foffset(coff); + } + break; + } + blk = coff/XPBLK_SZ; // current block # were are in + // may have started read anywhere in this block + if(LSEEK_ERR != end) + if(coff >= end) + break; // end of range + } + key=is_keyword(buf+off); + toff = coff+off; + if(key == -1) + { + printf("\nkey 0x%lx not recognized at",*((DWORD*)(buf+off))); + disp_foffset(toff); + printf(", skip to next block boundry"); + off = XPBLK_SZ; + } + else if(key >= 0 && key <= MTF_MAX_BLK) + { + hdr =(MTF_DB_HDR *)(buf+off); + printf("\n\n%4.4s Block Region attrib 0x%08lx @ ",hdr->tag,hdr->attrib); + disp_foffset(toff); + printf(" contains:"); + off += hdr->var_sz; + } + else if(key > MTF_MAX_BLK) + { + shdr = (MTF_STREAM_HDR *)(buf+off); + printf("\n %4.4s attrib 0x%04x 0x%04x @ ", + shdr->tag,shdr->sys_attrib,shdr->media_attrib); + disp_foffset(toff); + off += sizeof(MTF_STREAM_HDR)+shdr->length; // skip struct + data + } + else + break; + // at least on case with ESET were (hdr->var_sz % 4) != 0 + // most shdr->length need modulo work + if((rd = off % 4) != 0) + off += 4-rd; // next DWORD boundry + } + + return(suc); +} + +unsigned char mondays[] = {31,28,31,30,31,30,31,31,31,30,31,31}; +#define BASEYR 1970 +DWORD unix_time(BYTE t[]) +{ + DWORD ret = t[4] & 0x3f; // low order 6 bits for seconds; + int i,tmp; + long mult = 60; + tmp = ((t[3] << 2) + (t[4] >> 6)) & 0x3f; // next 6 bits for min + ret += mult * tmp; + mult *= 60; + tmp = t[3] >> 4; // low order 4 bits of hour. + if(t[2] & 1) // high bit of hour for full 5 bit value + tmp += 0x10; + ret += mult * tmp; + mult *= 24; + tmp = (t[2] >> 1) & 0x1f; // 5 bits for day of month + if(tmp > 0) + tmp--; + ret += mult * tmp; + tmp = ((t[1] << 2) + (t[2] >> 6)) & 0xf; // 4 bits for month of year + if(tmp > 12) + tmp = 12; // an error, don't let array go out of bounds + if(tmp > 0) + tmp--; // make it a zero based array index + for(i=0;i> 2); // 14 bits for year this is base 0 + if(tmp < BASEYR) + tmp = BASEYR; + i = BASEYR; + while(i < tmp) // step through years from base checking for leap years + { + if((i %100) == 0 || (i % 4) == 0) + ret += mult * 366; // leap year + else + ret += mult *365; + i++; + }; + return(ret); +} + +// may want to add a VERBOSE mode to query about extract later +// note this is called both to skip over data, and extract depending on mode +// CAREFUL my lbuf use a little screwy with the pad factor +// may read further into it than LBUF_SZ bytes? +BYTE lbuf[LBUF_SZ+4]; // make global so not on stack + +// global filter, set in main if used +char *filter = NULL; + +FOFFSET do_file(int fp, FOFFSET soff,int mode) +{ + int fo=EOF,rd,ir,iw,key; + WORD wlen; + BYTE docksum=0,pad=0; // local buffer + DWORD flen=0,tlen,cksum=0,*cptr; // where we are now + FOFFSET off,toff; + MTF_FILE *ff; // fixed region + NT_FILE *ff2; // 2nd fixed region + MTF_STREAM_HDR *shdr; + struct utimbuf times; + ff = (MTF_FILE *)buf; // point to start local buffer + /* ff->var_sz points to first in list of streams as before + but now know structure, and should be able to skip ahead + to find the file data in STAN + 1st cut lets assume its in the buffer + */ + off = ff->head.var_sz; + key = -1; + while(off < XPBLK_SZ && key != STAN) + { + shdr = (MTF_STREAM_HDR *)(buf+off); + key=is_keyword(buf+off); + if(key == NUL_KEY || (key >=0 && key <= MTF_MAX_BLK)) + break; // end of this data region + off += sizeof(MTF_STREAM_HDR); // always add header length + if(key != STAN) // advance to next stream + { + off += shdr->length; // skip data + if((rd = off % 4) != 0) + off += 4-rd; // next DWORD boundry + } + else // the file is the data + flen = shdr->length; // warning its a QWORD in stream header! + } + + /* Notes: + as of 6/8/04 try removing error message below, let flen==0 for an empty file + If there is no file data, the file is empty but should be created + + if(key != STAN) + { + mode &= ~EXTRACT; // cancel xtract if active + mode |= SILENT; // display nothing more + printf("\ndo_file() failed to find STAN"); + } + + below was a much older note when first found MTF spec: + this is a simple patch to my older none MTF code + it stops searching the stream on detecting STAN + There is more after this, see do_stream() with -v option + Typically at least a checksum + */ + + if(flen > 0 && (mode & EXTRACT || !(mode & SILENT)) ) + { + printf(" data from "); + toff = soff+off; + disp_foffset(toff); + printf(" to "); + toff = soff+off+flen; + disp_foffset(toff); + } + + + // if mode | extract, get the file + if(mode & EXTRACT) + { + if(shdr->media_attrib & STREAM_CHECKSUMED) + docksum++; // only updated if actually do extract, otherwise ignore + + wlen = ff->name.offset; // LFN filename length + // 1st string from end struct to var_sz + ff2 = (NT_FILE *)(buf+ff->head.OS_data.offset); // 2nd fixed region + // ff2->nmlen > 0 if 2nd MSDOS file name exists +#ifndef HAS_INT64 // it doesn't handle long file names either! + if(ff2->size > 0) // get the msdos name if it exists + { + wlen = ff->head.OS_data.offset+ff2->offset; // point to start short name + getn_unicode(lbuf,wlen,(WORD)(wlen+ff2->size),15); + } + else +#endif // always use LFN if HAS_INT64 =>(LINUX || WIN32) + // use LFN if there is no MSDOS name, ie name is MSDOS 8.3 compatible + getn_unicode(lbuf,wlen,(WORD)(wlen+ff->name.size),LBUF_SZ); + + // buf is never overwritten as use lbuf for copy + // so ff->name etc stay valid + // 12/4/03 add filter to skip some files + if (filter != NULL && !is_match(lbuf,filter)) + docksum=0; // no-op do nothing + else if((fo=open(lbuf,O_BINARY|O_RDWR|O_CREAT|O_TRUNC, + S_IREAD|S_IWRITE)) == EOF) + printf("\nfailed to open %s for output",lbuf); + + else if(flen > 0 && (FOFFSET)lseek(fp,soff+off,SEEK_SET) != soff+off) + // file data starts immediately after MTF_STREAM_HDR + printf("\nfailed to seek to start of data"); + else + { // ff2->attrib is a DWORD, MSDOS byte is low order one, rest 0? + printf("\nlength %6ld atrib 0x%2x ",flen,(BYTE)ff2->attrib); + disp_xpdatetime(ff->modify); + printf("\nextracing: %s: ",lbuf); + // use local buffer for copy, preserve FILE info in buf[] + tlen = flen; + if(flen > 0 && docksum) + docksum++; // increment to 2, we are trying extract + // if flen == 0 skip check sum logic + while(tlen > 0) + { + if(tlen > LBUF_SZ) + rd = LBUF_SZ; + else + { + rd = tlen; // tail end of the file + /* 1/1/04 add pading based on off+flen, ie enough + to get me to the next stream header, normally a CSUM + */ + pad = (off+flen) % 4; + if(pad) // wasn't modulo of 4 bytes pad the read + pad = 4 - pad; + memset(lbuf,0,LBUF_SZ); // clear buffer before last read + // this is an attempt to get a valid cksum + } + if((ir=read(fp,lbuf,rd+pad)) != rd+pad || + (iw = write(fo,lbuf,rd)) != rd) // only write rd bytes + { + printf("\nio error writing file"); + break; + } + else if(mode & VERBOSE) + fputc('.',stdout); + tlen -= rd; // tlen goes to zero breaking us out of loop + + if(docksum) // do checksum logic + { /* below seems to work, but worries me + the MTF_STREAM_HDR is 22 bytes long + a two byte file would have a pad of 0, ir = 2 + the cksum below would wrap two extra letters into lbuf + */ + cptr = (DWORD*)lbuf; + for(iw=0;iwsize > 0) // get the msdos name if it exists + { + wlen = ff->head.OS_data.offset+ff2->offset; // point to start short name + getn_unicode(lbuf,wlen,(WORD)(wlen+ff2->size),15); + } + else +#endif // always use LFN if HAS_INT64 =>(LINUX || WIN32) + // use LFN if there is no MSDOS name, ie name is MSDOS 8.3 compatible + getn_unicode(lbuf,wlen,(WORD)(wlen+ff->name.size),LBUF_SZ); + + // attempt to set time stamp and attribute with generic functions + times.actime = times.modtime = unix_time(ff->modify); + rd = 0; + if(utime(lbuf,×) != 0) // set time stamp + rd = 1; + + ir = S_IFREG | S_IREAD; + if((ff->attrib & 1) == 0) // its writable + ir |= S_IWRITE; + if(chmod(lbuf,ir) != 0) + rd |= 2; + if(rd) + printf("\nerror setting file: "); + if(rd & 1) + printf("timestamp "); + if(rd & 2) + printf("attributes"); + fputc('\n',stdout); // be sure there is an lf + } + } + // always need to skip over file data, if extracted or not + off += flen+pad; // offset to end of data + pad alignment if extract + // WARNING for MTF should scan to end of stream list, and possibly do CSUM + if(docksum > 1) // we tried to extract file, advanced fp, cksum is valid + { + shdr = (MTF_STREAM_HDR *)lbuf; + if((rd = read(fp,lbuf,sizeof(MTF_STREAM_HDR))) != + sizeof(MTF_STREAM_HDR) || is_keyword(lbuf) != CSUM || + (ir = read(fp,lbuf+rd,(int)shdr->length)) != shdr->length) + printf("\ninput error reading file checksum"); + else if(cksum != *((DWORD*)(lbuf+rd)) ) + printf(" - checksum error!"); + off += rd+ir; + } + return(off); // bytes to read or skip to get past file data + // Warning, still must advance to next block boundry +} + +// warning, the input variables start and len are DWORD +// I was orignally lazy parsing in main, but change to +// len and start as FOFFSET 4/6/05 +void fnd_target(int fp,DWORD targ,FOFFSET start,FOFFSET len) +{ + int i,rd; + + printf("\nsearching file for 0x%lx => ascii:%4.4s from offset ", + targ,(char *)&targ); + disp_foffset(start); + printf(" to "); + if(len == LSEEK_ERR) + printf("EOF"); + else + { + disp_foffset(start+len); + } + if((FOFFSET)lseek(fp,start,SEEK_SET) != start) + { + printf("\nseek to start failed"); + return; + } + + while((len == LSEEK_ERR || len > 0) && (rd = read(fp,buf,XPBLK_SZ)) > 4) + { + if(len > 0) + { + if(len < rd) + { + rd = len; + len = 0; + } + else + len -= rd; + } + for(i=0;idev_name.offset; + sz = volb->dev_name.size; + max = start+sz-1; + if(sz > 0 && max < XPBLK_SZ) + { + printf("\ndevice name: "); + disp_unicode(start,max); + } + start = volb->vol_name.offset; + sz = volb->vol_name.size; + max = start+sz-1; + if(sz > 0 && max < XPBLK_SZ) + { + printf("\nvolume name: "); + disp_unicode(start,max); + } + start = volb->dev_name.offset; + sz = volb->dev_name.size; + nmlen = getn_unicode(tree_path,start,start+sz-1,PATH_LEN); + return(nmlen); +} + +#ifndef MAX_PATH +#define MAX_PATH 100 +#endif + +int main(int argc, char *argv[]) +{ + struct path_elem paths[MAX_PATH]; + MTF_DB_HDR *th; + DIR_LIST *root=NULL; + int fp,i,rd,key,mode = 0,tmode,path_len,dir_len,rcnt=0, + tset=0,cset=0; // target and current set values, tset = 0 for all + // NTbackup uses 1 based sets, ie 1st is #1, 2nd is #2 + FOFFSET off = 0,skip,len,toff,start=LSEEK_ERR,end= LSEEK_ERR; + DWORD targ=0; + char *ch,flag,*fmt,*path=NULL; + printf("\nNTBKUP Ver 1.07c compiled for %s with MAX_PATH = %d", + OS_STR,MAX_PATH); +#ifdef _4GB + printf("\n max file size 4Gb"); +#endif +#ifdef HAS_INT64 + printf("\n compiled for 64 bit file offsets"); + if(sizeof(FOFFSET) != 2 * sizeof(long)) + { + printf("\nfatal error, however sizeof(FOFFSET) = %d",sizeof(FOFFSET)); + printf( +"\nif compiled with gcc, you probably forgot -D_FILE_OFFSET_BITS=64"); + exit(0); + } +#endif + printf("\nCopyright (C) 2003 William T. Kranz"); + printf("\nNTBKUP comes with ABSOLUTELY NO WARRANTY"); + printf( +"\nFree software distributed under the terms of the GNU General Public license"); + printf( +"\nSee http://www.gnu.org/licenses/gpl.html for license information"); + + printf( +"\nCheck http://www.fpns.net/willy/msbackup.htm for Updates & Documentation\n"); + + // debug aid, check structure sizes + if(argc > 1 && strnicmp(argv[1],"-ss",3) == 0) + { + printf("\nknown strutures:"); + printf("\nsizeof(FOFFSET) = %d",sizeof(FOFFSET)); + printf("\nsizeof(MTF_TAPE_ADR) = %d",sizeof(MTF_TAPE_ADR)); + printf("\nsizeof(MTF_DB_HDR) = %d",sizeof(MTF_DB_HDR)); + printf("\nsizeof(MTF_STREAM_HDR) = %d",sizeof(MTF_STREAM_HDR)); + printf("\nsizeof(MTF_TAPE) = %d",sizeof(MTF_TAPE)); + printf("\nsizeof(MTF_VOLB) = %d",sizeof(MTF_VOLB)); + printf("\nsizeof(MTF_DIRB) = %d",sizeof(MTF_DIRB)); + printf("\nsizeof(MTF_SSET) = %d",sizeof(MTF_SSET)); + printf("\nsizeof(MTF_FILE) = %d",sizeof(MTF_FILE)); + printf("\nsizeof(MTF_FDD_HDR) = %d",sizeof(MTF_FDD_HDR)); + printf("\nsizeof(MTF_FDD_VOLB) = %d",sizeof(MTF_FDD_VOLB)); + printf("\nsizeof(MTF_FDD_DIRB) = %d",sizeof(MTF_FDD_DIRB)); + printf("\nsizeof(NT_FILE) = %d",sizeof(NT_FILE)); + + fputc('\n',stdout); //cleanup for linux + exit(0); // we are done + } + + if(argc < 2) + { + printf( +"\nntbkup [-x] [-l] [-p] [@] [-c] [-d] [-f] [-j#] [-s#] [-t] [-v]"); + printf("\n -x[filter] to unconditionally extract all files matching filter"); + printf("\n -l where full case sensitive path limits extract"); + printf("\n -p recursive path based directory extract"); + printf("\n @ use path based extract and redirection command file"); + printf("\n all extracts use [filter] from -x, default filter is *.*"); + printf("\n -c to display catalog regions for TAG == TFDD"); + printf("\n -d display directory tree from raw data region"); + printf( +"\n -f[:start[:len]] finds 4 char tag, optional start pos and length"); + printf( +"\n -j#[:#] jump to start position in file for data recovery (modulo 0x400)"); + printf("\n optionally follow start offset with :# for an end offset"); + printf("\n -s# to limit options above to a particular SET by #"); + printf("\n -t[:start[:end]] display tags only from start to end"); + printf("\n -v to set verbose mode"); + // -ss to show structure sizes is hidden + fputc('\n',stdout); //cleanup for linux + exit(0); + } + else // start at i = 1 as special case so can have -ss as 1st argument + for(i=1;i 0) + { + printf("\nresrict operations to backup set %d",rd); + tset = rd; + } + else if(strnicmp(argv[i],"-v",2) == 0) + { + mode &= ~SILENT; + mode |= VERBOSE; + } + else if(strnicmp(argv[i],"-f",2) == 0) + { + ch = argv[i] + 2; // start target string + if(strlen(ch) >= 4) + { + targ = *((long *)ch); + start = 0L;len = LSEEK_ERR; // to eof + if((ch = strchr(ch,':')) != NULL) // have start + { + get_foffset(ch+1,&start); // aborts on error + if((ch = strchr(ch+1,':')) != NULL) // have len + get_foffset(ch+1,&len); + } + } + else + printf("\ninvalid argument for -f, min 4 chars required\n"); + } + else if(strnicmp(argv[i],"-t",2) == 0) + { + start = 0L;len = LSEEK_ERR; // to eof + get_foffset(argv[i]+2,&start); // aborts on error! + if((ch = strchr(argv[i]+3,':')) != NULL) // there is a len + get_foffset(ch+1,&len); + mode |= TAGS; + } + else if(strnicmp(argv[i],"-j",2) == 0) + { + get_foffset(argv[i]+2,&toff); // aborts on error! + + if((rd = (toff % XPBLK_SZ)) != 0) + toff -=rd; // modulo 0x400 + off = toff; + if((ch = strchr(argv[i]+3,':')) != NULL) // there is an end + get_foffset(ch+1,&end); + } + else if(strnicmp(argv[i],"-p",2) == 0) + { + mode &= ~LPATH; // clear it, last options gets control + mode |= TREE|XTREE|SILENT; + path = argv[i]+2; + } + else if(strnicmp(argv[i],"-l",2) == 0) + { + // this was first try, worked, limits on -x style extract + // to a single path, change to -l 1/23/04 + mode &= ~XTREE; // clear it + mode |= LPATH|SILENT; + // force extract mode so don't need -x also + path = argv[i]+2; + path_len = strlen(path); + if(path_len == 0) + continue; + if(*(path+path_len-1) != '\\' && + *(path+path_len-1) != '/') + path_len++; // treat trailing NUL as last slash + for(rd = 0;rd 1) + { + path_len--; + path++; // skip expected leading '/', but ignore if not there + } + } + else if(strnicmp(argv[i],"-x",2) == 0) + { + mode |= EXTRACT|SILENT; + if(*(argv[i]+2) != 0) + filter = argv[i]+2; + } + else if(*argv[i] == '@' && + (rcnt = get_paths(argv[i]+1,paths,MAX_PATH)) > 0) + mode |= TREE|SILENT; + + } + + if((fp = open(argv[1],O_BINARY|O_RDONLY)) == EOF) + printf("\nfailed to open %s",argv[1]); + else if(targ != 0L) // just do search and exit + fnd_target(fp,targ,start,len); + else if(mode & TAGS) // just display range of tags + do_streams(fp,start,len); + else + { + if(off != 0L) + { + printf("\nSEEK to file position "); + disp_foffset(off); + printf(" for data recovery"); + if((FOFFSET)lseek(fp,off,SEEK_SET) != off) + { + printf(" - FAILED"); + off = 0L; // best guess, affects display of position + } + } + while((rd = read(fp,buf,XPBLK_SZ)) > 4) + { + /* + 12/03/04 think I now skip in most cases, see skip below + except DIRB. Can conditionally enable skip there. + */ + skip = 0; + if((key=is_keyword((char *)buf)) > -1 && !(mode & SILENT)) + { + printf("\n%4.4s found keyword at offset ",buf); + disp_foffset(off); + } + + // look at MTF checksum now that I know it exists + if(key >= 0 && key <= MTF_MAX_BLK) + i = hd_cksum((WORD *)buf,0x19); // block header cksum + else if (key > 0) // assume stream header + i = hd_cksum((WORD *)buf,0xa); + if(key >= 0 && (mode & VERBOSE)) + { + if(i !=0) + printf("\ncksum error in header"); + } + + if(key == TAPE && !(mode & SILENT)) + { + do_tape(); // descriptive strings + } + else if(key == SSET) + { + if(!(mode & SILENT)) + printf(" blk = %3lu",(unsigned long)(off/XPBLK_SZ)); + cset++; //increment current set + if(tset == 0 || cset == tset) + do_sset(cset); // display descriptive strings + } + else if(key == VOLB) + { + dir_len = do_volb(); + // don't care if truncated, although not likely, use string + if((tset == 0 || cset == tset) && mode & TREE) + rd = add_tree_node(&root, off, key); + } + else if(key == DIRB) + { + /* both examples I've seen have had tag_head + followed by what looks like a disk specific structure + */ + th = (MTF_DB_HDR *)buf; + if(!(mode & SILENT)) + printf(" 1st Stream: %4.4s",buf+th->var_sz); + if(mode & LPATH && path != NULL) // its _MTF aware, and doing an extract + { + if(match_path(path,path_len)) + mode |= EXTRACT; // turn it on + else + mode &= ~EXTRACT; // turn off + } + skip = do_dirb(fp,off,mode,&dir_len); // this can be big... + if((tset == 0 || cset == tset) && mode & TREE) + { + if(dir_len <= 0) + printf("\nskip truncated path"); + else + rd = add_tree_node(&root, off, key); + } + } + else if(key == FILE_K) + { + tmode = mode; + if(tset > 0 && tset != cset) + { + tmode &= ~EXTRACT; // don't extract, not current set + tmode |= SILENT; // in fact, display nothing! + } + skip = do_file(fp,off,tmode); // skips over file data, could extract + } + else if(key == TFDD) + { + if( (mode & CATALOG) && + (tset == 0 || tset == cset)) // doing all or just this one + flag = 1; + else + flag = 0; // parse region so skip over all of it + + skip = do_tfdd(fp,off,flag); + } + if(skip == LSEEK_ERR) + printf("\nWaring skip error"); // 12/8/03 do nothing break; // fatal error + if(skip > XPBLK_SZ) + off += skip; + else + off += XPBLK_SZ; // always advances one block + + /* did routine actually do the advance? do_file() only does if EXTRACT + and if it did, did to skip ahead to next block boundry + */ + if((skip = (FOFFSET)lseek(fp,0L,SEEK_CUR)) != off || skip % XPBLK_SZ != 0) + { + if(off % XPBLK_SZ) + off -= off % XPBLK_SZ; + if((skip = (FOFFSET)lseek(fp,off,SEEK_SET)) != off) + { + printf(" failed to skip ahead to next block"); + exit(1); + } + + } + if(end != LSEEK_ERR && off >= end) + { + printf("\nparse terminated by end value from -j option at "); + disp_foffset(off); + break; + } + } + + } + + // TREE routines require a pass through the file to build the tree + if(root != NULL) + { + if(mode & DTREE) + disp_tree(root); + if(mode & TREE && rcnt > 0) + do_redirect(fp,root,paths,rcnt); + else if(mode & XTREE && path != NULL) + { + i = strlen(path); + i--; + paths[0].term = *(path+i); + if(i > 0 && (paths[0].term == DELIM || + paths[0].term == '*' || paths[0].term == '+')) + { + paths[0].path = path; + *(path+i) = DELIM; // force for level detection + paths[0].redirect = path+i+1; // empty string + do_redirect(fp,root,paths,1); // allows recursive directory creation + } + else + printf("\n-p option, invalid path %s",path); +/* note: following logic is a good test for path_extract() + assumes path terminated with DELIM? + have since replace with call to do_redirect() which I + developed later to allow sub directory creation + DIR_LIST *dir; // required allocation + unsigned long fskipped=0; + if((dir = find_path(root,path)) != NULL) + { + printf("\nDIRB record containing path at offset "); + disp_foffset(dir->data_off); + path_extract(fp,dir,&fskiped); + } +*/ + } + + } + fputc('\n',stdout); //cleanup for linux + return(0); +} + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.H" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.H" new file mode 100644 index 0000000..04170c8 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTBKUP.H" @@ -0,0 +1,359 @@ +/* ntbkup.h see ntbkup.c and xpbkup.h + original concepts developed in xpbkup.c + Then discovered MTF100a.pdf and attempt to + change over to MTF structures. + +Sorry about the mess below with structure packing. +#pragma pack(1) is a little sloppy, but works well with + MSDOS, WIN32, and CYGWIN +However my Linux 2.4 gcc Version doesn't like it +while CYGWIN's gcc Version 3.3.1 ignores this alternate approach + __attribute__ ((packed)) for each structure +If someone can tell me why I'd like to know. +See the hidden -ss option in main to check your +structure sizes if you try another compiler. + + +time is an array of 5 bytes which is treated as +a bit field with the 1st byte being the MSB and the 5th +byte being the LSB. Parsing from LSB to MSB +seconds 6 bits range 0-59 +minutes 6 bits range 0-59 +hour 5 bits range 0-23 +day 5 bits range 1-31 +month 4 bits range 1-12 +year 14 bits range 0 - 16383 + +definately room after/in year for other info, but for +files I've looked at which were created in 2003, the value +of year has been 2003 + +see dos.h for DOS bitmap of attribute byte. + +12/29/03 copy from xpbkup.h go to MTF specific defines + which were triggered by defining _MTF in prior work +01/01/04 add the FOFFSET logic from Msqic to allow + 4GB files under dos +01/02/04 add WIN32 logic for MSVC 5.0 +01/17/04 neither MTF_DB_HDR nor MTF_FDD_VOLB needs PACKED +12/30/06 remove define MAX_PATH=25, its done in ntbkup.c and + then passed as an argument to get_paths in nttree.c + Can one modify on command line when compile ntbkup.c +*/ + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; + + +#ifndef MSDOS // => _WIN32 or Linux +#define HAS_INT64 +#define LSEEK_ERR (-1) // this works for standard lseek stuff +#ifdef _WIN32 +#pragma pack(1) +#define PACKED ; +typedef __int64 FOFFSET; +#define QWORD FOFFSET // close enough +#else // Unix gcc assumed +typedef unsigned long long QWORD; +// return from lseek, or lseeki64 controlled via -D_FILE_OFFSET_BITS=64 +typedef off_t FOFFSET; +#ifdef __CYGWIN__ +#pragma pack(1) // Linux gcc won't compile with this, see above +#define PACKED ; +#else +// to pack linux structures selectively +#define PACKED __attribute__ ((packed)); +#define O_BINARY 0 // this Microsoft mode flag undefined in Linux gcc +#endif +#endif + +#else // its MSDOS +// in DOS just pack everything +#pragma pack(1) +#define PACKED ; + +#ifdef _4GB +typedef unsigned long FOFFSET; +#define LSEEK_ERR ((DWORD)-1L) +#else +typedef long FOFFSET; // 2 GB is default std C +#define LSEEK_ERR (-1L) +#endif + +#endif + + +#define XPBLK_SZ 0x400 // max blocking factor +#define PATH_LEN 255 // max path length in tree_path[] + + +/* had defined mode bits 0-3, through SILENT and tested + for mode == 0 to display normal message. + should now be mode & SILENT inhibits messages + mode & VERBOSE gives additional messages +*/ +#define EXTRACT 1 +#define CATALOG 2 +#define SILENT 4 +#define VERBOSE 8 +#define LPATH 0x10 // limit xtract based on path +#define TAGS 0x20 +#define TREE 0x40 +#define DTREE 0x80 +#define XTREE 0x100 + + +// index defines into array string for is_keyword() in ntbkup.c +// they make error checks generic, but must be updated if array changed +#define NUL_KEY (-2) // returns if the long is == 0 +#define TAPE 0 +#define SSET 2 +#define VOLB 3 +#define DIRB 4 +#define FILE_K 5 // FILE would conflict stdio.h +#define MTF_MAX_BLK 9 +#define TFDD 11 +#define NACL 12 +#define NTQU 13 +#define CSUM 14 +#define STAN 15 +#define SPAD 16 +#define FEND 17 +#define NTOI 18 + + + + +// MTF defines +// string type: id MTF_DB_HDR.str_type +#define NO_STRINGS 0 +#define ANSI_STR 1 +#define UNICODE_STR 2 + + +typedef struct mtf_tape_adr { +WORD size, // string length (its not nul terminated) + offset; // from start struct containing MTF_TAPE_ADR +} MTF_TAPE_ADR; + +typedef struct mtf_db_hdr { +BYTE tag[4]; +DWORD attrib; +WORD var_sz; +BYTE OS_id,OS_ver; // see MTF appendix A, {0xE,0x2} in samples +#ifdef HAS_INT64 +QWORD disp_sz, + fmt_adr; +#else +DWORD disp_sz, + disp_sz_hi, + fmt_adr, + fmt_adr_hi; +#endif +BYTE res_MBC[2], + res[6]; +DWORD blk_id; +BYTE res2[4]; +MTF_TAPE_ADR OS_data; +BYTE str_type, + res3; +WORD cksum; +} MTF_DB_HDR; + +// note the common header above embedded at start of each common block +struct mtf_tape { +MTF_DB_HDR head; +DWORD media_id, + attrib; +WORD media_seq, + pass_encryp, + soft_blk_sz, + media_cat_type; +MTF_TAPE_ADR media_name, + media_label, + media_pass, + software_name; +DWORD fmt_blk_sz, + vendor_id; +BYTE media_date[5], + MTF_major_ver; +} PACKED + +typedef struct mtf_tape MTF_TAPE; + +struct mtf_volb { +MTF_DB_HDR head; +DWORD attrib; +MTF_TAPE_ADR dev_name, + vol_name, + mach_name; +BYTE date_written[5]; +} PACKED + +typedef struct mtf_volb MTF_VOLB; + +typedef struct mtf_dirb { +MTF_DB_HDR head; +DWORD attrib; +// following are 4 times +BYTE modify[5], + create[5], + backup[5], + access[5]; +DWORD dir_id; +MTF_TAPE_ADR name; // includes trailing NUL => '/' +} MTF_DIRB; + +struct mtf_sset { +MTF_DB_HDR head; +DWORD attrib; +WORD encrypt_alg, + comp_alg, + vendor_id, + set_num; +MTF_TAPE_ADR set_name, + set_descript, + set_passwd, + user_name; +#ifdef HAS_INT64 +QWORD blk_adr; +#else +DWORD blk_adr, + blk_adr_hi; +#endif +BYTE written[5], // date/time written + sft_major_ver, + sft_minor_ver, + time_zone, + MTF_minor_ver, + media_cat_ver; +} PACKED +typedef struct mtf_sset MTF_SSET; + +typedef struct mtf_file { +MTF_DB_HDR head; +DWORD attrib; +// following are 4 times +BYTE modify[5], + create[5], + backup[5], + access[5]; +DWORD dir_id, + file_id; +MTF_TAPE_ADR name; +} MTF_FILE; + + + +// Stream Media Format Attributes +#define STREAM_CONTINUE 1 //This is a continuation stream. BIT0 +#define STREAM_VARIABLE 2 //Data size for this stream is variable. BIT1 +#define STREAM_VAR_END 4 //Last piece of the variable length data. BIT2 +#define STREAM_ENCRYPTED 8 //This stream is encrypted. BIT3 +#define STREAM_COMPRESSED 0x10 //This stream is compressed. BIT4 +#define STREAM_CHECKSUMED 0x20 // checksum stream follows. BIT5 +#define STREAM_EMBEDDED_LENGTH 0x40 //Stream length embedded in data. BIT6 + +struct mtf_stream_hdr { +BYTE tag[4]; +WORD sys_attrib, + media_attrib; +#ifdef HAS_INT64 +QWORD length; +#else +DWORD length, + length_hi; // and you better hope its 0! +#endif +WORD encrypt_alg, + compress_alg, + cksum; +} PACKED +typedef struct mtf_stream_hdr MTF_STREAM_HDR; + + +// embedded in TFDD stream, mini headers: +typedef struct mtf_fdd_hdr { +WORD length; +BYTE tag[4]; +WORD media_seq; +DWORD attrib; // of parent common block +#ifdef HAS_INT64 +QWORD fmt_adr, // maps to XPBLK_SZ block # in file (was blk in xpbkup.h) + disp_sz; // for a data file = file length, ie in MTF_FDD_FILE +#else +DWORD fmt_adr, + fmt_adr_hi, + disp_sz, + disp_sz_hi; +#endif +long link; +BYTE os_id, + os_ver, + str_type, + pad; +} MTF_FDD_HDR; + +struct mtf_fdd_volb { +MTF_FDD_HDR head; +DWORD attrib; // distrinct from head.attrib!? +MTF_TAPE_ADR dev_name, + vol_name, + mach_name, + OS_data; +BYTE date_written[5]; +} PACKED +typedef struct mtf_fdd_volb MTF_FDD_VOLB; + +struct mtf_fdd_dirb { +MTF_FDD_HDR head; +// following are 4 date/times +BYTE modify[5], + create[5], + backup[5], + access[5]; +DWORD attrib; +MTF_TAPE_ADR name, // includes trailing NUL => '/' + OS_data; +}; + +typedef struct mtf_fdd_dirb MTF_FDD_DIRB; +typedef struct mtf_fdd_dirb MTF_FDD_FILE; +// note MTF_FDD_FILE is currently identical to MTF_FDD_DIRB + +/* The only place things become slightly unclear is with regard + to the OS_data for these last two typedefs. + Its OS ID and OS version specific. I assume ID=14 for NT + in this program, and use the Version 1 file structure. + The current files I've seen are OS Version 2, but it doesn't seem + to have changed. There is a similar structure (without the flags field) + for NT_DIRB which I am currently ignoring. +*/ +typedef struct nt_file { +DWORD attrib; // low order byte matches MSDOS attribute byte +WORD offset, // point to short file name unicode string + size; +DWORD flags; +} NT_FILE; + + +//------------------- new tree routines for lookup ------------- +/* for my dynamic link list of directory nodes +*/ +typedef struct dir_list { +char *name; +BYTE key; // VOLB or DIRB +FOFFSET data_off; // offset to DIRB record in file +struct dir_list * next, + * child; // only subdir has children + // ignore parent, shouldn't need +} DIR_LIST; + +// used in path remapping functions +typedef struct path_elem { +char term, + *path, + *redirect; +} PATH_ELEM; + + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTTREE.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTTREE.C" new file mode 100644 index 0000000..39edd7c --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/NTTREE.C" @@ -0,0 +1,845 @@ +/* nttree.c tree and path routines for ntbkup.c + +Add overview below 3/17/04 as on review for version 1.03 +I find I've forgotten the underlying logic. +The tree is created via calls to add_tree_node() +This dynamically allocates space for the new node +and if the full path string copied from global tree_path[]. +It then calls insert_tree() to put the new node into tree +order in memory. The 2/6/04 1.02 version of this seems to +work correctly. + +From ntbkup.h +typedef struct dir_list { +char *name; +BYTE key; // VOLB or DIRB +FOFFSET data_off; // offset to DIRB record in file +struct dir_list * next, // at same logical level + * child; // of current node, always points to a DIRB + // ignore parent,used in msqic, shouldn't need +} DIR_LIST; + +The intent is to allow seeking to the location of the +record in the archive using data_off. +If its a VOLB record next points to the next physical drive +on the system and child points to the directory tree on this drive. +name is the drive specification (or as of 5/17/05 network drive spec). + +If its a DIRB record, next points to directories at the same +level, ie the chain representing all the subdirectories of the parent. +FILEs are not included in this tree. One must seek to the +DIRB record in the archive and traverse the FILE data that +follows these records to get at FILE information. See +path_extract() which calls do_file() once for each FILE found. + +Each name in a DIRB record is the full path within its volume, +ie it does not include the drive specification "?:". +This is important for my restore logic as I allow redirection +where the beginning of the destination path may be specified +by the user and the remainder taken from the archives structure. + +do_redirect() is the routine that handles the command line +options -p and @ in NTBKUP. The -p is just a special case +where the user specifies one top level archive source node +and the extraction is done starting at the current level. +@ allows the user to specify multiple top level source nodes +and corresponding destination nodes in a command file. + +do_redirect() makes one call to the recursive do_path() +routine for each top level archive node to be extracted. +If called via the @ option, prior to each call to do_path() +it sets the current directory to the desired destination directory. +do_path() is called with a parameter, par, which is the amount +of the source path to be masked when changing to the next +directory. + +With hind site I'm not real comfortable with my use of +chdir() system calls to handle this. It is easy to get out +of sync and cause errors. A rewrite where the full paths +are used instead of relative paths is probably desirable. + +find_tree() is the lookup routine, it can be entered at any +level in the tree and recursively searches from that point +down (through the children in the tree) for a full +case sensitive match with the supplied path string. +Per above, unless the tree is entered at to top level +the search path should NOT include a drive spec. + +1/24/04 created, splitting earlier tree work out of ntbkup.c +1/25/04 this compiled and tst_path() with chg2path() seem + to work with relative paths. As initially written + it assumed the gap between a parent/child was one + level. This is not the case. Add logic to create + children child by child in chg2path(). + + Also change get_paths() a little more. The concept of + using the current directory just doesn't make sense + with this implimentation as it did in MSQIC. + +2/6/04 see do_redirect, play with l and lev + there was a short int mismatch in calls to chg2path +3/16/04 fix error in chg2path when term == '+' +3/17/04 back up version above to nttree2.c apparently it has + problems with level stuff. Make some significant changes. + see nt_dan.c and dan_files.txt for some notes an testing. + do_path() was heart of changes. + some error messages added included skipped file + count in path_extract() and skipped directories + in do_redirect(). + tweak get_paths() to allow quoated string for destination + +5/17/04 see emails from Peter Feldman + turns out drive (ie VOLB spec) and be a network spec. + change find_tree() +4/06/05 troubles with Jack's big archive, in add_tree_node() + test or empty path, only allocated if plen > 0 + Its messy, maybe just shouldn't add to tree at all???? + at least warn now. + +12/20/06 increase LN_SZ define from 255 to 600 +*/ + +#include +#include +#include +#include // for toupper() +#include // define memset +#include +#include // for open() defines + +#if defined(MSDOS) || defined(_WIN32) +# ifdef MSDOS +# define OS_STR "MSDOS" +# else +# define OS_STR "WIN32" +# endif +#define DELIM '\\' +#include +#include +#include // for lseek +#include +#else +# ifdef __CYGWIN__ +# define OS_STR "CYGWIN" +#else +# define OS_STR "LINUX" +#endif +#include // for lseek, read, write +#include +#define DELIM '/' +#define strnicmp strncasecmp +#define stricmp strcasecmp +#endif + +#ifdef _WIN32 +// this redefinition must be last! ie after io.h +#define lseek _lseeki64 +#endif + + +#include "ntbkup.h" // includes FOFFSET logic + +// called in ntbkup.c to copy one file +FOFFSET do_file(int fp, FOFFSET soff,int mode); // uses global char *filter + +// add some tree routines + +// make the following global to limit amount of stack space used in tree_copy() +extern char tree_path[]; // see ntbkup.c +extern BYTE buf[]; // " " +static struct stat sbuf; // used by do_redirect() and tree_copy() + +// recursive: see insert_tree() this routine mimic that behavior to find a node +DIR_LIST *find_tree(DIR_LIST *next,char *str) +{ + int i,t; + char ch; + while(next != NULL) + { + i=0; + if(next->name != NULL) // test for no name, shouldn't happen, but... + { + while((t = *(next->name+i) - *(str+i)) == 0 && + (ch = *(str+i)) != 0) + i++; + if(*(next->name+i) == 0) + { + if( t != 0) + { + if(next->child == NULL) + next = NULL; + else + next = find_tree(next->child,str); + } + break; // we found it + } + } + next = next->next; + } + return(next); +} + +/* prior to 5/17/04 I looked for 1st DELIM and assumed VOLB name + was everything ahead of this DELIM. Works fine for C: + but not network drive spec, ie \\ANDY\C + so change to any match with Volume name +*/ +DIR_LIST *find_path(DIR_LIST *next, char *str) +{ + int len,slen=strlen(str); + while(next != NULL) // find volume + { + if(next->name != NULL) // add error check + { + len = strlen(next->name); + if(strnicmp(str,next->name,len) == 0) + { + next = next->child; + break; + } + } + next = next->next; + } + // 2nd case below allows + or * to recursively do entire volume + if(next == NULL || (slen-len == 1 && *(str+len) == DELIM)) + return(next); // NULL or top level node in a volume + else + return(find_tree(next,str+len)); // recursive search of a volumes tree +} + +void disp_tree(DIR_LIST *d) +{ + while(d != NULL) + { + if(d->name == NULL) // add error check + printf("\n oops there was no name!"); + else if(d->key == VOLB) + printf("\n%s",d->name); + else + printf("\n %s",d->name); + if(d->child != NULL) + disp_tree(d->child); + d = d->next; + } +} + +/* Make some assumptions. Mainly it inserts top node first and additional ones + will be below those in tree already. therefore only check for children + try if at end of current node name, the other is an actual child + for now assume all end with a DELIM + otherwise insert at end of next +*/ + +int insert_tree(DIR_LIST *next, DIR_LIST *node) +{ + + int i,t,ldelim=0,suc=0,depth=1; + char ch; + while(next != NULL) + { + i=0; + if(next->name != NULL && node->name != NULL) // add error check + { + while((t = *(next->name+i) - *(node->name+i)) == 0 + && (ch = *(node->name+i)) != 0) + { + if(t == 0 && ch == DELIM) + ldelim = i; + i++; + } + if(*(next->name+i) == 0) + { + if( t != 0) + { + if(next->child == NULL) + next->child = node; + else + depth += insert_tree(next->child,node); + break; + } + else + printf("\nerror duplicate DIRB %s",next->name); + } + } + if(next->next == NULL) + { + next->next = node; + break; + } + next = next->next; + } + return(depth); // just for fun +} + +int add_tree_node(DIR_LIST **root, FOFFSET off, int key) +{ + int suc = 0,plen = strlen(tree_path); + DIR_LIST *node,*tmp; + if((node = malloc(sizeof(DIR_LIST))) == NULL || + (plen > 0 && (node->name = malloc(plen+1)) == NULL)) + { + suc++; + printf("\nDIR Tree allocation error"); + } + else + { + node->next = node->child = NULL; + node->key = key; + node->data_off = off; + if(plen <= 0) + { + printf("\nWarning empty path in add_tree_node()"); + node->name = NULL; + } + else + { + strncpy(node->name,tree_path,plen); + *(node->name+plen) = 0; // insure termination + } + + tmp = *root; + if(tmp != NULL) // find last node at drive level + while(tmp->next != NULL) + tmp = tmp->next; + + if(*root == NULL) // always want a VOLB for a drive at root level + { + if(key != VOLB) + { + printf("\nDIR Tree warning, 1st node not a VOLUME! force '?:'"); + strcpy(tree_path,"?:"); + if((suc = add_tree_node(root,(FOFFSET)0,VOLB)) != 0) + return(suc); + else + tmp = *root; // we just forced a root + } + else + *root = node; // set 1st VOLB to root + } + else if(key == VOLB) + tmp->next = node; // its definately a volume spec, append + + if(key == DIRB) // in partial archive may hit DIRB with no VOLB + { + if(tmp->child == NULL) + tmp->child = node; + else + insert_tree(tmp->child,node); + } + else if(key != VOLB) + { + printf("\ninvalid key %d",key); + suc++; + } + } + + return(suc); +} + + +/* extract all files in DIRB block to current directory via do_file() + note this means its filtered by global filter -x option sets + 3/18/04 find from Dan's stdout.txt that the algorithm below + periodically hits a SPAD or CSUM key. Think this happens + if file ends near the XPBLK_SZ increment, the trialing stuff + in the stream header is hit. Used to quite if key != FILE_K + add logic to ignore these false hits. Also ignore if key < 0 + as DIRB often more the XPBLK_SZ bytes long so get some + unrecognized hits in here. + + Returns a negative # on error, or # >=0 which is the number + of files successfully extracted +*/ +int path_extract(int fp, DIR_LIST *node,unsigned long *fskip) +{ + FOFFSET off,skip; + int key,rd; + unsigned int cnt=0; + char dir_cnt=0; + // seek to location of DIRB for directory in the archive + if((off = (FOFFSET)lseek(fp,node->data_off,SEEK_SET)) != node->data_off) + return(-1); + // extract all FILE tags + while((rd = read(fp,buf,XPBLK_SZ)) > 4) + { + skip = 0; + key=is_keyword((char *)buf); + if(dir_cnt > 0 && key >= 0 && key != FILE_K && key <= MTF_MAX_BLK) + return(cnt); // have done all files in directory + if(dir_cnt == 0) // only do this once on first pass + { + if(key != DIRB) + return(-2); // should have landed on a dirb + else + dir_cnt++; // assume its the correct one! + } + if(key == FILE_K) + { + skip = do_file(fp,off,EXTRACT|SILENT); + if(skip > 0 && skip != LSEEK_ERR) + cnt++; + else + (*fskip)++; // extract failed + } + if(skip != LSEEK_ERR && skip > XPBLK_SZ) + off += skip; + else + off += rd; // always tries to advance one block + + /* did routine actually do the advance? do_file() only does if EXTRACT + and if it did, did to skip ahead to next block boundry + */ + if((skip = (FOFFSET)lseek(fp,0L,SEEK_CUR)) != off || skip % XPBLK_SZ != 0) + { + if(off % XPBLK_SZ) + off -= off % XPBLK_SZ; + if((skip = (FOFFSET)lseek(fp,off,SEEK_SET)) != off) + { + printf(" failed to skip ahead to next block"); + return(-1); + } + } + } + return(-2); // only get to here on read error +} + +/* path remapping routines copied from msqicrcv.c 1/24/04 + handles quoted strings which are required for LFN access + 3/19/04 looks like delimiter check on destination saws '"' + instead of delim if path was quoated + + note 12/20/06 increase LN_SZ from 255 +*/ +#define LN_SZ 600 + + +int get_paths(char *fn,struct path_elem paths[],int sz) +{ + FILE *fp; + int i,r=0,cnt = 0,ln=0,len; + char quoted,*ch,line[LN_SZ+1]; + if((fp = fopen(fn,"r")) == NULL) + { + printf("\nfailed to open redirect command file: %s ",fn); + return(-1); // open error + } + while(cnt < sz) + { + if(fgets(line,LN_SZ,fp) == NULL) + { + if(feof(fp)) + printf("\neof after line %d",ln); + break; + } + else + { + paths[cnt].term = 0; // validatation + quoted = 0; + ln++; + len = strlen(line); + while(len > 0) + if(line[len-1] == '\n' || line[len-1] == ' ') + line[--len] = 0; // strip lf && trailing spaces so have string + else + break; + if(len <= 0 || line[0] == '#') // allow comments in line + { + printf("\nskipping empty line %d",ln); + continue; + } + + ch = line; + // skip leading blanks + while(*ch != 0 && *ch == ' ') + { + len--; + ch++; + } + + // allocate room for trailing NUL + // both source and destination are in same allocation + if((paths[cnt].path = malloc(len+2)) == NULL) + { + printf("\nfatal alloc error"); + break; + } + i = 0; + while(*ch != 0 && (quoted || *ch != ' ') && i <= len) + { + if(*ch == '"') + quoted = quoted ^ 1; + else + *(paths[cnt].path+ i++) = *ch; + ch++; + } + + if(i > 0) + { +/* below for msqic where do not want trialing delimiter + paths[cnt].term = *(paths[cnt].path+ --i); // has a last char, save it + change 1/24/04 for NTBKUP to insert terminator +*/ + paths[cnt].term = *(paths[cnt].path+ i-1); // has a last char, save it + *(paths[cnt].path+ i -1) = DELIM; // replace with DELIM + + } + if(i == 0) + printf("\nerror line %d source path has length 0!",ln); + if(paths[cnt].term != DELIM && paths[cnt].term != '*' && + paths[cnt].term != '+') + { + printf("\nerror line %d in source path must end in %c, *, +", + ln,DELIM); + i = -1; + } + else if (quoted) + { + printf("\nerror line %d unmatched quotes in source path",ln); + i = -1; + } + + if(i > 0) + { + *(paths[cnt].path+ i++) = 0; // terminate source path + + while(*ch != 0 && *ch == ' ' && i <= len) // find start next + { + ch++; + } + + + if(*ch != 0 && i <= len) + paths[cnt].redirect = paths[cnt].path+ i; // 2nd path exists + else + paths[cnt].redirect = NULL; // not redirected + while(*ch != 0 && (quoted || *ch != ' ') && i <= len) + { + if(*ch == '"') + quoted = quoted ^ 1; + else + *(paths[cnt].path+ i++) = *ch; + ch++;r++; + } + *(paths[cnt].path+ i) = 0; // terminate redirect + if(!(*ch == 0 || *ch == ' ') || quoted || r == 0) + { + printf("\nerror line %d invalid redirect path %s", + ln,paths[cnt].redirect); + i = -1; + } + // check termination, will append strings to this. + // for consistency must end with DELIM + else if(*(paths[cnt].path+ i -1) != DELIM) + { + printf("redirect path must end with %c",DELIM); + i = -1; + } + else + { + // 1/25/04 remove empty string for current directory logic + // in msqic.c I delete delimiter, leave for NTBKUP + cnt++; + } + } + if(i <= 0) + { + printf("\n skipping: %s",line); + free(paths[cnt].path); + continue; + } + } + } + return(cnt); // # valid paths +} + +/* set current directory based on path + checks for a leading DELIM (only passed from do_redirect() with level == 0) + to determine if its an absolute or relative path + It may step down more than one directory level to new path + The number of levels stepped down is returned + + 1/28/04 change return, add parameter level that used to be return on success + NOTE if term == '+' this will attempt to create directories + return -1 on fatal error, + 1 on warning + 3/16/03 add reset of suc = 0 when term == '+' and + create directory successfully after not finding it + also always return suc, the return was undefined on a error! + 3/17/03 This routine passes back *level=0 if its the root + else *level = # of subdir's created + appears this is always 1 and we don't need a while loop! +*/ +int chg2path(char *path,char term,WORD *level) +{ + int suc = 0,l = strlen(path),rd; + char savch; + *level=0; + l--; + /* at this point *(path + l) should be the last DELIM + however if its a path to the root this can also be the + root path spec, ie l == 0. This routine attempts to + handle both absolute paths (start with DELIM) and relative + paths that don't begin with DELIM. + This routine aborts if: + no path, length 1 but not root, or a subdir without trailing DELIM + */ + if(l < 0 || (l == 0 && *path != DELIM) || + (l > 0 && *(path+l) != DELIM)) + suc = 1; + else + { + while(suc == 0 && l > 0) + { + rd = 0; + while(*(path+rd) != 0 && *(path+rd) != DELIM) + rd++; + if(rd > 0) + *(path+rd) = 0; // overwrite all but 1st DELIM with 0, then restore below + else if(*level == 0 && l > 0) + { + savch = *(path+rd+1); // allow absolute path to root at level 0 + *(path+rd+1) = 0; + } + if(rd > 0 ) + { + suc = stat(path,&sbuf); // check status of directory + if(term == '+') // try to create the sub dir(s) if not there + { + if(suc == 0 && // directory exists + !(sbuf.st_mode & S_IFDIR) ) // as a file + suc = -1; // fatal error + else if (suc) // it doesn't exist try to create + { +#ifdef unix + // accessable & searchable + if(mkdir(path,S_IREAD | S_IWRITE | S_IEXEC) != 0) + suc = -1; +#else + if(mkdir(path) != 0) + suc = -1; +#endif + else + suc = 0; // clear error from test for directory above + } + } + else if(suc || !(sbuf.st_mode & S_IFDIR)) + suc = 1; // just a warning, its no a directory + } + if(suc == 0 && chdir(path)) // finally try to go there + suc = -1; + if(rd > 0) + { + *(path+l) = DELIM; // restore + (*level)++; // don't increment for root + } + else if(*level == 0 && l > 0) + *(path+rd+1) = savch; + rd++; // skip the DELIM + path+=rd; // advance to next node + l -= rd; + } + } + if(suc != 0) + printf("\nFailed: chdir(%s)",path); + return(suc); +} + + +/* called from do_redirect() only if recursive extract + attempts to step through the nested paths + and extract files with a call to path_extract() for each sub directory + + 4/17/04 think was messed up + a) its when level == 0 that there is no parent, had logic reversed + b) did initialize lev, and didn't pass new nested level to + recursive do_path() cause of how I used level. Change. + if there is no child need lev = 0 so don't step back up + c) add a chdir("..") before chg2dir() while getting next + at end. Getting fuzzy, but looks right + d) move display of node->name and its len inside while loop + so each node displayed and len, which is critical to chg2dir() + and do_path(), gets updated as move between directories at the + same level. + e) add lev == 1 test and error message on failure to do child + f) add skip parameter to track if directories skipped +*/ +short do_path(int fp,DIR_LIST *node,WORD level,WORD *dskip,unsigned long *fskip,short par,char term) +{ + short suc = 0,len; + short lev; + while(suc == 0 && node != NULL) + { + printf("\nlevel %d: %s",level,node->name); +#ifndef TEST // define to test directory routines without file extraction + if((len=path_extract(fp,node,fskip)) < 0) // get all files for this node + { + printf("\nfatal error while extracting files from this directory"); + (*dskip)++; + } + +#endif + len=strlen(node->name); + lev = 0; // assume don't change level + if(node->child != NULL) + { + if(strlen(node->child->name) > len && + chg2path(node->child->name+len,term,&lev) == 0 && lev == 1) + suc = do_path(fp,node->child,level+lev,dskip,fskip,len,term); + else + { + printf("\nskipping unreachable source child node lev %d:\n %s", + lev,node->child->name); + (*dskip)++; + } + /* prior to 1/28/04 was fatal error if chg2path failed + now continue, just skip this directory + This allows term '*' to use directory existance as a filter + */ + } + + if(level == 0) + break; // there is no parent for start node + // do_path() returns to current directory level + // if not level 0, restore to parent level so chg2dir(next->node) will work + else if(suc == 0 && chdir("..") != 0) + suc = -2; + else // its not the start node, has a valid parent + { + while(suc==0) + { // look for a valid next directory + if((node = node->next) == NULL) + break; // no more nodes + if(par >= strlen(node->name)) + { + printf("\nskip, invalid next node: %s",node->name); + (*dskip)++; + } + else if(chg2path(node->name+par,term,&lev) == 0) + { if(lev == 1) + break; // positioned correctly + else + { + printf("\nwarning can't reach next source node lev %d:\n %s", + lev,node->name); + while(lev > 0 && chdir("..") == 0) + lev--; // attempt to recover + if(lev != 0) + suc = -2; // fatal if can't + else + (*dskip)++; + } + } + } + } + } + + if(suc == -2) + printf("\nfatal error returning to parent dir"); + return(suc); +} + +/* step through path list extracting files + one call to do_path() for each entry in paths[] array + This routine makes each top level redirected path the current directory. + It handles drive parsing, routines below this assume current drive. + +*/ +int do_redirect(int fp, DIR_LIST *root,struct path_elem *paths,int rcnt) +{ + int i,l,drvn,suc=0; + WORD lev,dskip; + unsigned long fskip=0; + char *drv,*path,doit; + DIR_LIST *node,*parent=NULL; + printf("\nDoing redirect:"); + for(i=0;ipath); + if(paths->redirect != NULL) + printf("\nredirect: %s",paths->redirect); + if((node = find_path(root,paths->path)) == NULL) + { + printf("\ncan't find source path"); + continue; + } + else + { + suc = 0; +#ifndef unix // its MSDOS or WIN32 so has physical drives + if(paths->redirect == NULL) + path = paths->path; + else + path = paths->redirect; + + l = 0; + drv = path; + while(*path != 0 && *path != DELIM) + { + path++; + l++; + } + if(l && *path == DELIM) // there is a drive spec + { // temp terminator for drive spec + *path = 0; + strupr(drv); + if(l != 2 || *(drv+1) != ':' || (drvn = *drv - 'A') < 0) + suc = 1; + else // DOS WIN32 specific A: is 1, C: is 3 etc + suc = _chdrive(*drv-'A'+1); // failed to change drive + if(suc) + { + continue; + printf("\nfailed to select drive: %s",drv); + } + *path = DELIM; // restore + } +#else + // need to redirect physical drive to a mount point + if(paths->redirect == NULL) + { + printf("\nSKIP, for Unix redirect path must be defined or current dir"); + continue; + } + else + path = paths->redirect; +#endif + // now see if we can select the directory + doit = 1; + if((l=strlen(path)) == 0 && rcnt > 1) + { + printf("\n Warning, empty path not allowed with multiple sources, its unpredictable!"); + doit = 0; + } + if(doit && (l == 0 || chg2path(path,paths->term,&lev) == 0) ) + { // chg2path handles directory creation if there is a path + fskip = dskip = 0; // initialize + if(paths->term == DELIM) // just this directory + { + if((l = path_extract(fp,node,&fskip)) > 0) + { + printf("\n%d files extracted from this directory",l); + l = 0; // so no error message below + } + } + else // do nested directories + { + l = do_path(fp,node,0,&dskip,&fskip,0,paths->term); + } + // no error check, an error in one path does NOT terminate extact + if(l != 0) + printf("\n extract aborted, fatal error parsing this path"); + if(dskip != 0) + printf("\n warning %u subdirectories were skipped",dskip); + if(fskip != 0) + printf("\n warning %lu files were skipped",fskip); + } + + } + paths++; + } + return(suc); +} + + + + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/QICDCOMP.C" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/QICDCOMP.C" new file mode 100644 index 0000000..b137095 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/QICDCOMP.C" @@ -0,0 +1,766 @@ +/* qicdcomp.c decompression *.qic files, see main() in msqic.c + Check http://www.fpns.net/willy/msbackup.htm for updates. + contact info above or via snailmail: p.o. box 333, bradford, nh 03221 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +----------------------------------------------------------------------------- + History: + + + see qic docs: qic122b.pdf + should add some exit codes etc, got it working quick and dirty + then for space reasons split out of msqic.c + + 10/18/03 the do_decompress is messed up for multiple drives + under WinME. add this comment, thought about a patch, + but a little messy. Don't need to do this NOW! + + 12/13/03 start to change code per above + do_decompress needs to handle stripping seg_head regions + from catalog. In process add passing vtbl_ver to + do_decompress() instead of just data_off. Want to be able + to create a single volume generic archive from one of members + in Win98 multi volume. + + 12/14/03 + change msqic.c so cvtbl->dirbase points to actual base rather + than offset after cseg_head + + 12/22/03 go to FOFFSET and LSEEK_ERR defines in msqic.h + 1/11/08 add Ralf's CYGWIN compiler defines + 2/2/04 add HAS_INT64 logic for Avik, basically just add prt_foff() + and add calls to it. Most of FOFFSET logic already in place + to get me to the 4Gb boundry + 5/03/04 try to make minimal modifications to do_decompress() + so it will work with a recreated VTBL See RECREAT tests. + Also set vtbl.comp = 0, was only clear high order bit + + Looks like can normally determine the length of the data + region by chasing down the segment chain, but not + the catalogs which always seem to have cseg_head.seg_sz = + SEG_SZ & 0x8000. So add get_dir_len() routine to chase + down chain to find length. + 5/31/07 backuped up current version to qicdcomp2.c Now try to fix + WIN95 issues per Sven Verhaegen's email of 5/22/07 his sample archive + us like my single b95c.qic backup. see moeenv~1.qic + see qicdcmp4.c for modes which depend on the ver info flag + I create and pass in, but just realized this is DUMB + both the WIN95 and WINME format advance SEG_SZ bytes + each time, they just treat shead->seg_sz differently + Old code: only works for WINME were seg_sz is 0x73F6 + cur_pos += shead.seg_sz; // where we want to be next + seems to me the WIN95 logic should work for all! try it + cur_pos += SEG_SZ - sizeof(shead); // always 0x73F6 + + yuk, do_decompress() was incorrectly testing cvtbl->ver + without WIN_MASK so always assumed WINME format + +*/ +#include +#include +#include +#include // define memset +#include +#include /* for open() defines */ + +#if defined(MSDOS) || defined(_WIN32) +# include +# include // for lseek +#else +# ifdef unix +# include // for read/write etc. +//# define exit _exit // ralf likes this.. +# define strnicmp strncasecmp +# define stricmp strcasecmp +# ifndef O_BINARY +# define O_BINARY 0 // no difference between text and binary mode for LINUX +# endif +# else +# error Unknown build environment. +# endif +#endif + +#include "msqic.h" + +void prt_foff(FOFFSET * off) +{ +#ifndef HAS_INT64 + printf("%lx",*off); +#else + DWORD *l = (DWORD *)off; + if(*(l+1) != 0) + printf("%lx%08lx",*(l+1),*l); + else + printf("%lx",*l); +#endif +} + +/*------------DECOMPRESSION see dcomp.c for initial trial ----------*/ +#define HBUF_SZ 2048 +unsigned char hbuf[HBUF_SZ]; // history buffer + +int fin,fout,bits=0,bcnt = 0,hptr=0; +FOFFSET comp_rd=0,comp_wr; + +void flush_hbuf() +{ + int i; + if((i=write(fout,hbuf,hptr)) != hptr) + { + printf("\nerror writting output file"); + exit(2); + } + hptr = 0; + comp_wr += i; +} + +unsigned char getbit() +{ + unsigned char mask,ret; + if(bcnt == 0) + { + if(read(fin,&bits,1) == 1) // not efficent, but easy + { + bcnt = 8; + comp_rd++; + } + else + { + printf("\n fatal error reading byte stream"); + flush_hbuf(); // write anything we can.... + exit(1); + } + } + bcnt--; + if(bcnt < 0) + { + printf("\nlogic error getbit() cnt went negative"); + flush_hbuf(); // write anything we can.... + exit(1); + } + else if (bcnt == 0) + mask = 1; + else + mask = 1 << bcnt; + if(mask & bits) + { + ret = 1; + bits &= ~mask; // clear this bit if set + } + else + ret = 0; + return(ret); +} + +unsigned char getbyte() // for raw char in stream +{ + unsigned char ret = bits; + int nbit = bcnt; // + bcnt = 0; + // this could be done a lot more efficently, but not now + while(nbit < 8) + { + ret = (ret << 1) + getbit(); + nbit++; + } + return(ret); +} + +int getsoff() +{ + unsigned short off = 0,bits=7,i; + if(getbit() == 0) + bits = 11; + for(i=0;iseg_sz & ~RAW_SEG) == 0) + { + printf("\nWarning 0 length segment in catalog"); + break; // don't think this happens, would be end of chain + } + if(!(cseg->seg_sz & RAW_SEG)) + { + printf("\ncompressed segment found in catalog"); + suc++; + } + } + } + if(suc) break; + + if(comp == 0) // simple case, no compression headers + { + if((brd=read(fp,&rd,2)) != 2) + suc++; + else if(rd == 0) + break; // end of catalog + else if(read(fp,hbuf,rd) != rd) + suc++; + else + dlen += brd + rd; + } + else // compressed and len != 0 + { + // get word len byte by byte as may span cseg_head + while(brd < 2 && len > 0 && suc == 0) + if(read(fp,prd + brd++,1) != 1) + suc++; + else + len--; + + if(brd == 2 && len > 0 && suc == 0) + { + // ok now rd value is valid + if(rd == 0) // we are done + break; + else if(rd <= len) // get the data + { + if(read(fp,hbuf,rd) != rd) + suc++; + else + { + len -= brd + rd; + dlen += brd +rd; + brd = 0; // clear for next read + } + } + else // can only read part of it, spans a cseg_head + { + if(read(fp,hbuf,(int)len) != len) + suc++; + else + { + dlen += len; + rd -=len; // did partial read + len = 0; // will force cseg_head read + } + } + } + } + } + if(lseek(fp,ioff,SEEK_CUR) == LSEEK_ERR) + printf("\nWarning failed to restore initial file position"); + + if(suc) + return(LSEEK_ERR); + else + return(dlen); + +} + + +/*----------------- + + do { + if(rd < 2) // read more into buffer + { + if(lseek(fp,off,SEEK_SET) == LSEEK_ERR || + (rd = read(fp,hbuf,HBUF_SZ)) < 2) + suc++; + else + w = (WORD *)hbuf; + } + + while(suc == 0 && rd >= 2) + { + if(comp && (off -soff) % SEG_SZ == 0) + { all fucked uo + } + off += *w +2; // add data length + length word itself + rd -= *w +2; + w += *w +2; // advance pointer (may go past end of hbuf!) + n++; // count directory entries found + } + } while(suc == 0 && *w != 0); +----------*/ + + +void decomp_seg() // was being sloppy, no return! +{ + int off,len,i,rawndx=-1,limit=29696; // was 5000 prior to 10/14/03 + char verb=0; + FOFFSET foff; + + hptr=0;bits=0;bcnt=0;comp_rd=0,comp_wr=0; // init so repeat calls can be made + // early termination so don't worry about buffer wraps + while(hptr < limit) // or till terminator, pat == 0x180, off == 0 + { + if(getbit() == 0) // raw byte + { + if(hptr == HBUF_SZ) + flush_hbuf(); + if(rawndx < 0) // always track this, use verb to control display + rawndx = hptr; // get first raw byte entered + // only raw chars go in history buffer + hbuf[hptr] = getbyte(); + hptr++; + } + else // a string + { + if(rawndx >= 0) + { + if(verb) + { + printf("\nraw chars: "); + for(i=rawndx;i= ' ') // is printable, not control char + putchar(hbuf[i]); + printf(" 0x%02x, ",hbuf[i]); + } + } + rawndx = -1; // end raw data block (1 or more chars) + } + off = getsoff(); + if(off == 0) + { + foff = (FOFFSET)lseek(fin,0L,SEEK_CUR); + printf("\nfound compression terminator in byte stream at 0x"); + prt_foff(&foff); + break; // this is the terminator + } + + len = getslen(); + if(verb) + printf("\nstring: len = %d offset = %d",len,off); +// note the unmodified offset is always displayed +#ifdef CARRAY +// the base 0 or 1 for array off handled below +// if don't comment out decrement example is better, qic dcomp worse + off--; // convert to C array offset where 0 is valid +#endif + // copy from history buffer to output buffer +// off in *.qic is displacement from current buffer position +// error check it + if((i = hptr-off) < 0) + { + if(verb) + printf( +"\nwarning offset %d from current pos %d out of range, wrap it around", + off,hptr); + i += HBUF_SZ; // allow it to continue + } + else if (i >= HBUF_SZ) + { + if(verb) + printf( +"\nwarning offset %d subtracted from current pos %d out of range, use modulo", + off,hptr); + i = i % HBUF_SZ; // wrap it + } + while(len-- > 0) + { + if(hptr == HBUF_SZ) + flush_hbuf(); + hbuf[hptr++] = hbuf[i++]; // copy in string + i = i % HBUF_SZ; + } + } + + } + if(hptr) + flush_hbuf(); + printf("\nread 0x"); + prt_foff(&comp_rd); + printf(" input bytes, generated 0x"); + prt_foff(&comp_wr); + printf(" output bytes"); +} + +/* copy from len bytes from fin to fout + file points must be positioned before call + side effect comp_wr set to # bytes written + uses global hbuf[] as transfer area, clobbering any data in it +*/ + +// similar to below but write len NUL bytes to fout +int pad_seg(FOFFSET len) +{ + int i,sz,suc = 0; + memset(hbuf,0,HBUF_SZ); // clear buffer + comp_wr = 0; + while(suc == 0 && len > 0) + { + if(len > HBUF_SZ) + sz = HBUF_SZ; + else + sz = len; + if(sz > 0 && (i = write(fout,hbuf,sz)) != sz) + suc = -2; + else + len -= sz; + } + return(suc); +} + +int copy_region(long len) +{ + int i,sz,suc = 0; + + comp_wr = 0; + while(suc == 0 && comp_wr < len) + { + if(len - comp_wr > HBUF_SZ) + sz = HBUF_SZ; + else + sz = len - comp_wr; + if(sz > 0 && ((i = read(fin,hbuf,sz)) != sz || + write(fout,hbuf,sz) != sz)) + suc = -2; + else + comp_wr += sz; + } + return(suc); +} + +/* add top level logic + assumes fo is a the beginning of a new empty file. + seeks back and rereads VTBL on fi + + Needs work for multiple drives in WinME, see get_vtbl() + + Originally hoped could convert from one format to another, + but its more than the header that is different, so give that up. + VER 1.02 copied database bytes from beginning of original file to + new file. Thus allowing recreation of Win95 or WinME style. + + Now check cvtbl->ver and ->cnt to determin if MDID required + + 12/13/03 change from int database + to struct vtbl_ver *cvtbl + keep a local copy of modified header and write at end + + 2/5/04 ah ha just realized what vtbl.start and .end + fields are really for. Normally change when decompress a + volume, attempt to add this logic + + Oh dear, in process find never dealt with multi-volume + WINME stuff, ie never did seek(fin, to cvtbl->database + + 4/28/04 add logic for cvtbl->OSver == RECREAT + to do this easily remove test below on cum_wr in read loops + This may allow archive larger than 4GB, and solve the problem + that one does NOT know dataSz for RECREAT case + cum_wr >= (long)vtbl.dataSz[0]) // safety net... + + Remove ability to use -sc## to set location of catalog for RECREAT + assume it immediately follows data region. This makes it 64 bit compatible. + + 6/12/07 added some breaks in main loop on error and about a week + ago logic to change ad + +*/ +int do_decompress(int fi,int fo,struct vtbl_ver *cvtbl) +{ + int i,suc = 0,sz,nshead=1; // always reads 1 seg_head past last segment + FOFFSET cum_rd = 0,cum_wr=0,cur_pos,pad; + struct qic_vtbl vtbl; + struct cseg_head shead; +// minimize parameter passing by making fin and fout locally global + fin = fi; fout = fo; + sz = sizeof(struct qic_vtbl); + + if(!(cvtbl->vtbl.comp & 0x80)) + { + printf("\nimage is not compressed, nothing to do!"); + suc = 1; // do nothing + } + + else if((cvtbl->ver & WIN_MASK) == WINME) // obtain and write MDID region + { + if(cvtbl->ver & RECREAT) // create empty block with tag + { + memset(hbuf,0,sz); + strncpy(hbuf,"MDID",4); + // required so parsed correctly after decompression + } + else // get copy of existing + { + cur_pos = 128L*cvtbl->cnt; + if (lseek(fin,cur_pos,SEEK_SET) != cur_pos || + (i = read(fin,hbuf,sz)) != sz) + suc = 3; + } + + if( suc == 0 && lseek(fout,(FOFFSET)128,SEEK_SET) != 128 || + (i = write(fout,hbuf,sz)) != sz) + suc = 3; + else + sz *= 2; // bytes in header region VTBL + MDID + if(suc != 0) + printf("\nerror creating WinME MDID"); + } + + + if((FOFFSET)lseek(fin,cvtbl->database,SEEK_SET) !=cvtbl->database) + { + printf("\nerror positioning input file to data region"); + suc = 2; + } + else if((FOFFSET)lseek(fout,(FOFFSET)sz,SEEK_SET) != (FOFFSET)sz) + { + printf("\nerror positioning output file after vtbl"); + suc = 2; + } + + + if(suc == 0) + { + // get copy of vtbl in local buffer + memcpy((void *)&vtbl,(void*)&cvtbl->vtbl,sizeof(struct qic_vtbl)); // save for mods + /* Compressed dir starts with a cseg_head, must pad to next seg boundry + as remove cseg_head from dir, so dir now starts earlier + Note in Win98&ME dirSz = # segments * SEG_SZ + in Win95 dirSz = space used in segment + in RECREAT don't know them obtain from seg_heads, force WIN95 format + 12/13/03 couple issues here + a) may be more than one segment I ignored that and + the additional seg_heads that must be removed previously + b) what if its a multi drive backup from WinME/98?? + Answer: only write the currently selected volume + */ + vtbl.comp = 0; // clear compressed byte + if((cvtbl->ver & WIN_MASK) == WINME) + vtbl.start = 3; // I only write single volumes starting after VTBL + /* was hopping to make ME to 95 conversion, but takes more work + there are extra bytes in data stream.... + */ + } + while(suc == 0) + { + if((i=read(fin,&shead,sizeof(struct cseg_head))) != + sizeof(struct cseg_head)) + { + suc = -2; + break; // 6/12/07 can't do anything more exit loop! + } + else if(shead.seg_sz == 0 || // end of data + (!(cvtbl->ver & RECREAT) && cum_wr >= (long)vtbl.dataSz[0])) // saftety net + { + printf("\nDecompressed all data segments"); + break; + } + // get current position, may need to seek ahead to next block + else if((cur_pos = lseek(fin,0L,SEEK_CUR)) == LSEEK_ERR) + { + suc = -1; + break; // 6/12/07 shouldn't do anything more exit loop! + } + else if(shead.seg_sz & RAW_SEG) // don't decompress this one! + { + printf("\n\nRaw copy of uncompressed segment starting at 0x"); + prt_foff(&cur_pos); + // do a raw copy of the segment + suc = copy_region((long)(shead.seg_sz & ~RAW_SEG)); + comp_rd = comp_wr; //setup for totals at end of loop + } + else // decompress the segment + { + printf("\n\nDecompress segment starting at 0x"); + prt_foff(&cur_pos); + decomp_seg(); // currently has no return! + // input file position is a little random, do a seek + // but force advance to SEG_SZ between headers + cur_pos += SEG_SZ - sizeof(shead); // always 0x73F6 + if(lseek(fin,cur_pos,SEEK_SET) != cur_pos) + suc = -1; + } + nshead++; // read another seg_head, required for RECREAT + cum_rd += comp_rd; + cum_wr += comp_wr; + printf("\ncumulative decompressed bytes written 0x"); + prt_foff(&cum_wr); + + } + /* must still write table of contents to complete process + 12/13/03 It turns out although a compressed files table + of contents is not compressed, it does have seg_head regions + which must be stripped (stupid, but true) + */ + if(suc == 0) + { + if((cvtbl->ver & WIN_MASK) == WINME) + { + // update end field, must be done before pad_seg() overwrites cum_wr + vtbl.end = 3; // base + vtbl.end += cum_wr/SEG_SZ; // point to 1st directory segment + if(cum_wr%SEG_SZ) + vtbl.end++; // round up + } + if(cvtbl->ver & RECREAT) + { + vtbl.dataSz[0] = cum_wr; + // assume no overflow into upper 32 bits, vtbl.dataSz[1] + } + else if(cum_wr != vtbl.dataSz[0]) // ignore high order long + printf("\nWarning: Decompressed Data bytes != dataSz"); + printf("\n append a copy of the original directory"); + // fudge it, use dataSz as abs position by include of VTBL sizes + // compressed files dataSz is actually the decompressed size! + // dir must start on a Segment boundry + // for WinME, two 128 byte headers before data + // for Win95, only one + pad = vtbl.dataSz[0] % SEG_SZ; + if(pad) + pad = SEG_SZ - pad; + if((cur_pos = lseek(fout,0L,SEEK_CUR)) > vtbl.dataSz[0]+sz) // have written too far + suc = -1; // separate these for debugging so I can see who fails + else if(pad && pad_seg(pad) != 0) // pad output data region to next segment + suc = -2; + else // add RECREAT test to set dirbase, assume next segment + { + if(cvtbl->ver & RECREAT) + { + if(cvtbl->dirbase == 0) + { + // if it was set from command line it won't be zero + cvtbl->dirbase = cum_rd + nshead * sizeof(struct cseg_head); + pad = cvtbl->dirbase % SEG_SZ; + if(pad) + cvtbl->dirbase += SEG_SZ - pad; + cvtbl->dirbase += cvtbl->database; // add in start offset + } + if((pad = get_dir_len(fin,1,cvtbl->dirbase)) == LSEEK_ERR) + { + printf("\nError determining catalog length"); + suc = -4; + } + else + vtbl.dirSz = pad; + } + if(suc == 0 && (cur_pos = lseek(fin,cvtbl->dirbase,SEEK_SET)) !=cvtbl->dirbase) + suc = -3; // failed to move to dir cseg_head + } + + if(suc == 0) + { + /* may want validation above, below assumes we are at seg_head + strip cseg_head regions + */ + nshead = 1; + cum_wr = 0; + do { + if(read(fin,&shead,sizeof(struct cseg_head)) != sizeof(struct cseg_head)) + suc = 5; + else if((shead.seg_sz & RAW_SEG) == 0) + { + printf("\ncompressed catalog detected???"); + suc = 6; + } + else if((pad = (shead.seg_sz & ~ RAW_SEG)) == 0) + break; // done with directory [oops doesn't happen in single volume reach EOF] + else if(copy_region((long)pad) != 0) + suc = 5; + else + cum_wr += comp_wr; + if(cum_wr >= vtbl.dirSz) break; + } while(suc == 0); + // if(cvtbl->ver & RECREAT) don't know dirSz, set from cum_wr + if(suc == 5 && (cum_wr == vtbl.dirSz || cvtbl->ver == RECREAT)) + suc = 0; // read it all, probably a single volume archive + pad = cum_wr % SEG_SZ; + if(suc == 0 && pad) + { + pad = SEG_SZ - pad; + suc = pad_seg(pad); // go to next seg boundry + } + if((cvtbl->ver & WIN_MASK) == WINME) + vtbl.dirSz = cum_wr +pad; + else + vtbl.dirSz = cum_wr; // WIN95 std + } + + sz = sizeof(struct qic_vtbl); + + if(suc != 0) + printf("\nerror %d during copy",suc); + else if(lseek(fout,0L,SEEK_SET) != 0L || + (i = write(fout,&vtbl,sz)) != sz) + { + printf("\nerror writing final VTBL header"); + suc = -2; + } + + } + close(fout); + return(suc); +} + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/README.TXT" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/README.TXT" new file mode 100644 index 0000000..be5320d --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/README.TXT" @@ -0,0 +1,32 @@ +Distribution date 07/17/05 +NTBKUP version 1.07 +MSQIC version 1.11 + +This is a source code distribtion library for Windows backup +readers. I have included a snapshot of my current +web page, msbackup.htm. Use it as your documentation. + +These will remain beta distributions. I've done as much +testing as I can, but trying to support 3 OS leads to +too many combinations to be sure I've tested every option. +If you have a problem or bug please get in touch but +don't expect too much, my interest has dwindled. +See contact.htm. + +Expand everything in a directory and try one of the make files. +I apologize for the current state of the Linux build. + +msbkup.mak works with various Microsoft compilers + as indicated in the file. It should detect the OS. + If you use nmake, it will probably make sense. + +msbkup.lin works in my linux environment. Its fairly + generic if you use 'make -f msbkup.lin' + However Berend de Boer went to the trouble of explaining + and creating a sample autoconfigure script for me. + I've been attempting to understand this and incorporate + it, but time is tight and energy is low so I'm afraid + its still not yet in this source distribution. Sorry, + as its the right way to do it.... + + diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/UpgradeLog.XML" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/UpgradeLog.XML" new file mode 100644 index 0000000..c7b19d0 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/UpgradeLog.XML" @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.css" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.css" new file mode 100644 index 0000000..3411f63 --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.css" @@ -0,0 +1,207 @@ +BODY +{ + BACKGROUND-COLOR: white; + FONT-FAMILY: "Verdana", sans-serif; + FONT-SIZE: 100%; + MARGIN-LEFT: 0px; + MARGIN-TOP: 0px +} +P +{ + FONT-FAMILY: "Verdana", sans-serif; + FONT-SIZE: 70%; + LINE-HEIGHT: 12pt; + MARGIN-BOTTOM: 0px; + MARGIN-LEFT: 10px; + MARGIN-TOP: 10px +} +.note +{ + BACKGROUND-COLOR: #ffffff; + COLOR: #336699; + FONT-FAMILY: "Verdana", sans-serif; + FONT-SIZE: 100%; + MARGIN-BOTTOM: 0px; + MARGIN-LEFT: 0px; + MARGIN-TOP: 0px; + PADDING-RIGHT: 10px +} +.infotable +{ + BACKGROUND-COLOR: #f0f0e0; + BORDER-BOTTOM: #ffffff 0px solid; + BORDER-COLLAPSE: collapse; + BORDER-LEFT: #ffffff 0px solid; + BORDER-RIGHT: #ffffff 0px solid; + BORDER-TOP: #ffffff 0px solid; + FONT-SIZE: 70%; + MARGIN-LEFT: 10px +} +.issuetable +{ + BACKGROUND-COLOR: #ffffe8; + BORDER-COLLAPSE: collapse; + COLOR: #000000; + FONT-SIZE: 100%; + MARGIN-BOTTOM: 10px; + MARGIN-LEFT: 13px; + MARGIN-TOP: 0px +} +.issuetitle +{ + BACKGROUND-COLOR: #ffffff; + BORDER-BOTTOM: #dcdcdc 1px solid; + BORDER-TOP: #dcdcdc 1px; + COLOR: #003366; + FONT-WEIGHT: normal +} +.header +{ + BACKGROUND-COLOR: #cecf9c; + BORDER-BOTTOM: #ffffff 1px solid; + BORDER-LEFT: #ffffff 1px solid; + BORDER-RIGHT: #ffffff 1px solid; + BORDER-TOP: #ffffff 1px solid; + COLOR: #000000; + FONT-WEIGHT: bold +} +.issuehdr +{ + BACKGROUND-COLOR: #E0EBF5; + BORDER-BOTTOM: #dcdcdc 1px solid; + BORDER-TOP: #dcdcdc 1px solid; + COLOR: #000000; + FONT-WEIGHT: normal +} +.issuenone +{ + BACKGROUND-COLOR: #ffffff; + BORDER-BOTTOM: 0px; + BORDER-LEFT: 0px; + BORDER-RIGHT: 0px; + BORDER-TOP: 0px; + COLOR: #000000; + FONT-WEIGHT: normal +} +.content +{ + BACKGROUND-COLOR: #e7e7ce; + BORDER-BOTTOM: #ffffff 1px solid; + BORDER-LEFT: #ffffff 1px solid; + BORDER-RIGHT: #ffffff 1px solid; + BORDER-TOP: #ffffff 1px solid; + PADDING-LEFT: 3px +} +.issuecontent +{ + BACKGROUND-COLOR: #ffffff; + BORDER-BOTTOM: #dcdcdc 1px solid; + BORDER-TOP: #dcdcdc 1px solid; + PADDING-LEFT: 3px +} +A:link +{ + COLOR: #cc6633; + TEXT-DECORATION: underline +} +A:visited +{ + COLOR: #cc6633; +} +A:active +{ + COLOR: #cc6633; +} +A:hover +{ + COLOR: #cc3300; + TEXT-DECORATION: underline +} +H1 +{ + BACKGROUND-COLOR: #003366; + BORDER-BOTTOM: #336699 6px solid; + COLOR: #ffffff; + FONT-SIZE: 130%; + FONT-WEIGHT: normal; + MARGIN: 0em 0em 0em -20px; + PADDING-BOTTOM: 8px; + PADDING-LEFT: 30px; + PADDING-TOP: 16px +} +H2 +{ + COLOR: #000000; + FONT-SIZE: 80%; + FONT-WEIGHT: bold; + MARGIN-BOTTOM: 3px; + MARGIN-LEFT: 10px; + MARGIN-TOP: 20px; + PADDING-LEFT: 0px +} +H3 +{ + COLOR: #000000; + FONT-SIZE: 80%; + FONT-WEIGHT: bold; + MARGIN-BOTTOM: -5px; + MARGIN-LEFT: 10px; + MARGIN-TOP: 20px +} +H4 +{ + COLOR: #000000; + FONT-SIZE: 70%; + FONT-WEIGHT: bold; + MARGIN-BOTTOM: 0px; + MARGIN-TOP: 15px; + PADDING-BOTTOM: 0px +} +UL +{ + COLOR: #000000; + FONT-SIZE: 70%; + LIST-STYLE: square; + MARGIN-BOTTOM: 0pt; + MARGIN-TOP: 0pt +} +OL +{ + COLOR: #000000; + FONT-SIZE: 70%; + LIST-STYLE: square; + MARGIN-BOTTOM: 0pt; + MARGIN-TOP: 0pt +} +LI +{ + LIST-STYLE: square; + MARGIN-LEFT: 0px +} +.expandable +{ + CURSOR: hand +} +.expanded +{ + color: black +} +.collapsed +{ + DISPLAY: none +} +.foot +{ +BACKGROUND-COLOR: #ffffff; +BORDER-BOTTOM: #cecf9c 1px solid; +BORDER-TOP: #cecf9c 2px solid +} +.settings +{ +MARGIN-LEFT: 25PX; +} +.help +{ +TEXT-ALIGN: right; +margin-right: 10px; +} diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.xslt" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.xslt" new file mode 100644 index 0000000..142057a --- /dev/null +++ "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport.xslt" @@ -0,0 +1,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ 解决方案: + 项目: + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + src + + + + + + + + + + + + +
文件名状态错误警告
+ javascript:document.images[''].click()src + + + + 已转换 + + + + 已转换 + +
+ + 个文件 + + + 1 个文件 + + + 已转换:
+ 未转换: +
+
+
+ + + + : + + + + + + + + + 转换报告 + <xsl:if test="Properties/Property[@Name='LogNumber']"> + <xsl:value-of select="Properties/Property[@Name='LogNumber']/@Value"/> + </xsl:if> + + + + +

转换报告 -

+ +

+ 转换时间:
+

+ + + + + + + + + + + + + + + + + + + + + + + + +

+ + + + + +
+ 转换设置 +

+ + +
+
diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Minus.gif" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Minus.gif" new file mode 100644 index 0000000..17751cb Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Minus.gif" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Plus.gif" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Plus.gif" new file mode 100644 index 0000000..f6009ca Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/_UpgradeReport_Files/UpgradeReport_Plus.gif" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/msbksrc.lzh" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/msbksrc.lzh" new file mode 100644 index 0000000..67f54db Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/MTF/msbksrc/msbksrc.lzh" differ diff --git "a/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/\346\267\261\345\205\245\347\240\224\347\251\266Windows 7\344\270\255\347\232\204Virtual Hard Disk (VHD) \346\212\200\346\234\257.doc" "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/\346\267\261\345\205\245\347\240\224\347\251\266Windows 7\344\270\255\347\232\204Virtual Hard Disk (VHD) \346\212\200\346\234\257.doc" new file mode 100644 index 0000000..3c1c0bc Binary files /dev/null and "b/\346\226\207\344\273\266\346\240\274\345\274\217\347\240\224\347\251\266/\346\267\261\345\205\245\347\240\224\347\251\266Windows 7\344\270\255\347\232\204Virtual Hard Disk (VHD) \346\212\200\346\234\257.doc" differ