DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/ints/bios_vhd.cpp
00001 /*
00002 *
00003 *  Copyright (c) 2018 Shane Krueger
00004 *
00005 *  This program is free software; you can redistribute it and/or modify
00006 *  it under the terms of the GNU General Public License as published by
00007 *  the Free Software Foundation; either version 2 of the License, or
00008 *  (at your option) any later version.
00009 *
00010 *  This program is distributed in the hope that it will be useful,
00011 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 *  GNU General Public License for more details.
00014 *
00015  *  You should have received a copy of the GNU General Public License along
00016  *  with this program; if not, write to the Free Software Foundation, Inc.,
00017  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "dosbox.h"
00021 #include "callback.h"
00022 #include "bios.h"
00023 #include "bios_disk.h"
00024 #include "regs.h"
00025 #include "mem.h"
00026 #include "dos_inc.h" /* for Drives[] */
00027 #include "../dos/drives.h"
00028 #include "mapper.h"
00029 #include "SDL.h"
00030 
00031 /*
00032 * imageDiskVHD supports fixed, dynamic, and differential VHD file formats
00033 *
00034 * Public members:  Open(), GetVHDType()
00035 *
00036 * Notes:
00037 * - create instance using Open()
00038 * - code assumes that c/h/s values stored within VHD file are accurate, and does not scan the MBR
00039 * - VHDs with 511 byte footers are not yet supported
00040 * - VHDX files are not supported
00041 * - checksum values are verified; and the backup header is used if the footer cannot be found or has an invalid checksum
00042 * - code does not prevent loading if the VHD is marked as being part of a saved state
00043 * - code prevents loading if parent does not match correct guid
00044 * - code does not prevent loading if parent does not match correct datestamp
00045 * - for differencing disks, parent paths are converted to ASCII; unicode characters will cancel loading
00046 * - differencing disks only support absolute paths on Windows platforms
00047 *
00048 */
00049 
00050 imageDiskVHD::ErrorCodes imageDiskVHD::Open(const char* fileName, const bool readOnly, imageDisk** disk) {
00051         return Open(fileName, readOnly, disk, 0);
00052 }
00053 
00054 imageDiskVHD::ErrorCodes imageDiskVHD::Open(const char* fileName, const bool readOnly, imageDisk** disk, const Bit8u* matchUniqueId) {
00055         //validate input parameters
00056         if (fileName == NULL) return ERROR_OPENING;
00057         if (disk == NULL) return ERROR_OPENING;
00058         //verify that C++ didn't add padding to the structure layout
00059         assert(sizeof(VHDFooter) == 512);
00060         assert(sizeof(DynamicHeader) == 1024);
00061         //open file and clear C++ buffering
00062         FILE* file = fopen64(fileName, readOnly ? "rb" : "rb+");
00063         if (!file) return ERROR_OPENING;
00064         setbuf(file, NULL);
00065         //check that length of file is > 512 bytes
00066         if (fseeko64(file, 0L, SEEK_END)) { fclose(file); return INVALID_DATA; }
00067         if (ftello64(file) < 512) { fclose(file); return INVALID_DATA; }
00068         //read footer
00069         if (fseeko64(file, -512L, SEEK_CUR)) { fclose(file); return INVALID_DATA; }
00070         Bit64u footerPosition = (Bit64u)((Bit64s)ftello64(file)); /* make sure to sign extend! */
00071         if ((Bit64s)footerPosition < 0LL) { fclose(file); return INVALID_DATA; }
00072         VHDFooter originalfooter;
00073         VHDFooter footer;
00074         if (fread(&originalfooter, 512, 1, file) != 1) { fclose(file); return INVALID_DATA; }
00075         //convert from big-endian if necessary
00076         footer = originalfooter;
00077         footer.SwapByteOrder();
00078         //verify checksum on footer
00079         if (!footer.IsValid()) {
00080                 //if invalid, read header, and verify checksum on header
00081                 if (fseeko64(file, 0L, SEEK_SET)) { fclose(file); return INVALID_DATA; }
00082                 if (fread(&originalfooter, sizeof(Bit8u), 512, file) != 512) { fclose(file); return INVALID_DATA; }
00083                 //convert from big-endian if necessary
00084                 footer = originalfooter;
00085                 footer.SwapByteOrder();
00086                 //verify checksum on header
00087                 if (!footer.IsValid()) { fclose(file); return INVALID_DATA; }
00088         }
00089         //check that uniqueId matches
00090         if (matchUniqueId && memcmp(matchUniqueId, footer.uniqueId, 16)) { fclose(file); return INVALID_MATCH; }
00091         //calculate disk size
00092         Bit64u calcDiskSize = (Bit64u)footer.geometry.cylinders * (Bit64u)footer.geometry.heads * (Bit64u)footer.geometry.sectors * (Bit64u)512;
00093         if (!calcDiskSize) { fclose(file); return INVALID_DATA; }
00094         //if fixed image, return plain imageDisk rather than imageDiskVFD
00095         if (footer.diskType == VHD_TYPE_FIXED) {
00096                 //make sure that the image size is at least as big as the geometry
00097                 if (calcDiskSize > footerPosition) {
00098                         fclose(file);
00099                         return INVALID_DATA;
00100                 }
00101                 *disk = new imageDisk(file, fileName, footer.geometry.cylinders, footer.geometry.heads, footer.geometry.sectors, 512, true);
00102                 return OPEN_SUCCESS;
00103         }
00104 
00105         //set up imageDiskVHD here
00106         imageDiskVHD* vhd = new imageDiskVHD();
00107         vhd->footerPosition = footerPosition;
00108         vhd->footer = footer;
00109         vhd->vhdType = footer.diskType;
00110         vhd->originalFooter = originalfooter;
00111         vhd->cylinders = footer.geometry.cylinders;
00112         vhd->heads = footer.geometry.heads;
00113         vhd->sectors = footer.geometry.sectors;
00114         vhd->sector_size = 512;
00115         vhd->diskSizeK = calcDiskSize / 1024;
00116         vhd->diskimg = file;
00117         vhd->diskname = fileName;
00118         vhd->hardDrive = true;
00119         vhd->active = true;
00120         //use delete vhd from now on to release the disk image upon failure
00121 
00122         //if not dynamic or differencing, fail
00123         if (footer.diskType != VHD_TYPE_DYNAMIC && footer.diskType != VHD_TYPE_DIFFERENCING) {
00124                 delete vhd;
00125                 return UNSUPPORTED_TYPE;
00126         }
00127         //read dynamic disk header (applicable for dynamic and differencing types)
00128         DynamicHeader dynHeader;
00129         if (fseeko64(file, (off_t)footer.dataOffset, SEEK_SET)) { delete vhd; return INVALID_DATA; }
00130         if (fread(&dynHeader, sizeof(Bit8u), 1024, file) != 1024) { delete vhd; return INVALID_DATA; }
00131         //swap byte order and validate checksum
00132         dynHeader.SwapByteOrder();
00133         if (!dynHeader.IsValid()) { delete vhd; return INVALID_DATA; }
00134         //check block size is valid (should be a power of 2, and at least equal to one sector)
00135         if (dynHeader.blockSize < 512 || (dynHeader.blockSize & (dynHeader.blockSize - 1))) { delete vhd; return INVALID_DATA; }
00136 
00137         //if differencing, try to open parent
00138         if (footer.diskType == VHD_TYPE_DIFFERENCING) {
00139 
00140                 //loop through each reference and try to find one we can open
00141                 for (int i = 0; i < 8; i++) {
00142                         //ignore entries with platform code 'none'
00143                         if (dynHeader.parentLocatorEntry[i].platformCode != 0) {
00144                                 //load the platform data, if there is any
00145                                 Bit64u dataOffset = dynHeader.parentLocatorEntry[i].platformDataOffset;
00146                                 Bit32u dataLength = dynHeader.parentLocatorEntry[i].platformDataLength;
00147                                 Bit8u* buffer = 0;
00148                                 if (dataOffset && dataLength && ((Bit64u)dataOffset + dataLength) <= footerPosition) {
00149                                         if (fseeko64(file, (off_t)dataOffset, SEEK_SET)) { delete vhd; return INVALID_DATA; }
00150                                         buffer = (Bit8u*)malloc(dataLength + 2);
00151                                         if (buffer == 0) { delete vhd; return INVALID_DATA; }
00152                                         if (fread(buffer, sizeof(Bit8u), dataLength, file) != dataLength) { free(buffer); delete vhd; return INVALID_DATA; }
00153                                         buffer[dataLength] = 0; //append null character, just in case
00154                                         buffer[dataLength + 1] = 0; //two bytes, because this might be UTF-16
00155                                 }
00156                                 ErrorCodes ret = TryOpenParent(fileName, dynHeader.parentLocatorEntry[i], buffer, buffer ? dataLength : 0, &(vhd->parentDisk), dynHeader.parentUniqueId);
00157                                 if (buffer) free(buffer);
00158                                 if (vhd->parentDisk) { //if successfully opened
00159                                         vhd->parentDisk->Addref();
00160                                         break;
00161                                 }
00162                                 if (ret != ERROR_OPENING) {
00163                                         delete vhd;
00164                                         return (ErrorCodes)(ret | PARENT_ERROR);
00165                                 }
00166                         }
00167                 }
00168                 //check and see if we were successful in opening a file
00169                 if (vhd->parentDisk == 0) { delete vhd; return ERROR_OPENING_PARENT; }
00170         }
00171 
00172         //calculate sectors per block
00173         Bit32u sectorsPerBlock = dynHeader.blockSize / 512;
00174         //calculate block map size
00175         Bit32u blockMapSectors = ((
00176                 ((sectorsPerBlock /* 4096 sectors/block (typ) = 4096 bits required */
00177                 + 7) / 8) /* convert to bytes and round up to nearest byte; 4096/8 = 512 bytes required */
00178                 + 511) / 512); /* convert to sectors and round up to nearest sector; 512/512 = 1 sector */
00179         //check that the BAT is large enough for the disk
00180         Bit32u tablesRequired = (Bit32u)((calcDiskSize + (dynHeader.blockSize - 1)) / dynHeader.blockSize);
00181         if (dynHeader.maxTableEntries < tablesRequired) { delete vhd; return INVALID_DATA; }
00182         //check that the BAT is contained within the file
00183         if (((Bit64u)dynHeader.tableOffset + ((Bit64u)dynHeader.maxTableEntries * (Bit64u)4)) > footerPosition) { delete vhd; return INVALID_DATA; }
00184 
00185         //set remaining variables
00186         vhd->dynamicHeader = dynHeader;
00187         vhd->blockMapSectors = blockMapSectors;
00188         vhd->blockMapSize = blockMapSectors * 512;
00189         vhd->sectorsPerBlock = sectorsPerBlock;
00190         vhd->currentBlockDirtyMap = (Bit8u*)malloc(vhd->blockMapSize);
00191         if (vhd->currentBlockDirtyMap == 0) { delete vhd; return INVALID_DATA; }
00192 
00193         //try loading the first block
00194         if (!vhd->loadBlock(0)) { 
00195                 delete vhd; 
00196                 return INVALID_DATA; 
00197         }
00198 
00199         *disk = vhd;
00200         return OPEN_SUCCESS;
00201 }
00202 
00203 int iso8859_1_encode(int utf32code) {
00204         return ((utf32code >= 32 && utf32code <= 126) || (utf32code >= 160 && utf32code <= 255)) ? utf32code : -1;
00205 }
00206 
00207 //this function (1) converts data from UTF-16 to a native string for fopen (depending on the host OS), and (2) converts slashes, if necessary
00208 bool imageDiskVHD::convert_UTF16_for_fopen(std::string &string, const void* data, const Bit32u dataLength) {
00209         //note: data is UTF-16 and always little-endian, with no null terminator
00210         //dataLength is not the number of characters, but the number of bytes
00211 
00212         string.reserve((size_t)(string.size() + (dataLength / 2) + 10)); //allocate a few extra bytes
00213         char* indata = (char*)data;
00214     const char* lastchar = indata + dataLength;
00215 #if !defined (WIN32) && !defined(OS2)
00216         char temp[10];
00217         char* tempout;
00218         char* tempout2;
00219         char* tempoutmax = &temp[10];
00220 #endif
00221         while (indata < lastchar) {
00222                 //decode the character
00223                 int utf32code = utf16le_decode((const char**)&indata, lastchar);
00224                 if (utf32code < 0) return false;
00225 #if defined (WIN32) || defined(OS2)
00226                 //MSDN docs define fopen to accept strings in the windows default code page, which is typically Windows-1252
00227                 //convert unicode string to ISO-8859, which is a subset of Windows-1252, and a lot easier to implement
00228                 int iso8859_1code = iso8859_1_encode(utf32code);
00229                 if (iso8859_1code < 0) return false;
00230                 //and note that backslashes stay as backslashes on windows
00231                 string += (char)iso8859_1code;
00232 #else
00233                 //backslashes become slashes on Linux
00234                 if (utf32code == '\\') utf32code = '/';
00235                 //Linux ext3 filenames are byte strings, typically in UTF-8
00236                 //encode the character to a temporary buffer
00237                 tempout = temp;
00238                 if (utf8_encode(&tempout, tempoutmax, (unsigned int)utf32code) < 0) return false;
00239                 //and append the byte(s) to the string
00240                 tempout2 = temp;
00241                 while (tempout2 < tempout) string += *tempout2++;
00242 #endif
00243         }
00244         return true;
00245 }
00246 
00247 imageDiskVHD::ErrorCodes imageDiskVHD::TryOpenParent(const char* childFileName, const imageDiskVHD::ParentLocatorEntry& entry, const Bit8u* data, const Bit32u dataLength, imageDisk** disk, const Bit8u* uniqueId) {
00248         std::string str = "";
00249         const char* slashpos = NULL;
00250 
00251         switch (entry.platformCode) {
00252         case 0x57327275:
00253                 //Unicode relative pathname (UTF-16) on Windows
00254 
00255 #if defined (WIN32) || defined(OS2)
00256                 /* Windows */
00257                 slashpos = strrchr(childFileName, '\\');
00258 #else
00259                 /* Linux */
00260                 slashpos = strrchr(childFileName, '/');
00261 #endif
00262                 if (slashpos != NULL) {
00263                         //copy all characters up to and including the slash, to str
00264             for (const char* pos = (char*)childFileName; pos <= slashpos; pos++) {
00265                                 str += *pos;
00266                         }
00267                 }
00268 
00269                 //convert byte order, and UTF-16 to ASCII, and change backslashes to slashes if on Linux
00270                 if (!convert_UTF16_for_fopen(str, data, dataLength)) break;
00271 
00272                 return imageDiskVHD::Open(str.c_str(), true, disk, uniqueId);
00273 
00274         case 0x57326B75:
00275                 //Unicode absolute pathname (UTF-16) on Windows
00276 
00277 #if defined (WIN32) || defined(OS2)
00278                 /* nothing */
00279 #else
00280                 // Linux
00281                 // Todo: convert absolute pathname to something applicable for Linux
00282                 break;
00283 #endif
00284 
00285                 //convert byte order, and UTF-16 to ASCII, and change backslashes to slashes if on Linux
00286                 if (!convert_UTF16_for_fopen(str, data, dataLength)) break;
00287 
00288                 return imageDiskVHD::Open(str.c_str(), true, disk, uniqueId);
00289 
00290         case 0x4D616320:
00291                 //Mac OS alias stored as blob
00292                 break;
00293         case 0x4D616358:
00294                 //Mac OSX file url (RFC 2396)
00295                 break;
00296         }
00297         return ERROR_OPENING; //return ERROR_OPENING if the file does not exist, cannot be accessed, etc
00298 }
00299 
00300 Bit8u imageDiskVHD::Read_AbsoluteSector(Bit32u sectnum, void * data) {
00301         Bit32u blockNumber = sectnum / sectorsPerBlock;
00302         Bit32u sectorOffset = sectnum % sectorsPerBlock;
00303         if (!loadBlock(blockNumber)) return 0x05; //can't load block
00304         if (currentBlockAllocated) {
00305                 Bit32u byteNum = sectorOffset / 8;
00306                 Bit32u bitNum = sectorOffset % 8;
00307                 bool hasData = currentBlockDirtyMap[byteNum] & (1 << (7 - bitNum));
00308                 if (hasData) {
00309                         if (fseeko64(diskimg, (off_t)(((Bit64u)currentBlockSectorOffset + blockMapSectors + sectorOffset) * 512ull), SEEK_SET)) return 0x05; //can't seek
00310                         if (fread(data, sizeof(Bit8u), 512, diskimg) != 512) return 0x05; //can't read
00311                         return 0;
00312                 }
00313         }
00314         if (parentDisk) {
00315                 return parentDisk->Read_AbsoluteSector(sectnum, data);
00316         }
00317         else {
00318                 memset(data, 0, 512);
00319                 return 0;
00320         }
00321 }
00322 
00323 Bit8u imageDiskVHD::Write_AbsoluteSector(Bit32u sectnum, const void * data) {
00324         Bit32u blockNumber = sectnum / sectorsPerBlock;
00325         Bit32u sectorOffset = sectnum % sectorsPerBlock;
00326         if (!loadBlock(blockNumber)) return 0x05; //can't load block
00327         if (!currentBlockAllocated) {
00328                 if (!copiedFooter) {
00329                         //write backup of footer at start of file (should already exist, but we never checked to be sure it is readable or matches the footer we used)
00330                         if (fseeko64(diskimg, (off_t)0, SEEK_SET)) return 0x05;
00331                         if (fwrite(&originalFooter, sizeof(Bit8u), 512, diskimg) != 512) return 0x05;
00332                         copiedFooter = true;
00333                         //flush the data to disk after writing the backup footer
00334                         if (fflush(diskimg)) return 0x05;
00335                 }
00336                 //calculate new location of footer, and round up to nearest 512 byte increment "just in case"
00337                 Bit64u newFooterPosition = (((footerPosition - 512ull + blockMapSize + dynamicHeader.blockSize) + 511ull) / 512ull) * 512ull;
00338                 //attempt to extend the length appropriately first (on some operating systems this will extend the file)
00339                 if (fseeko64(diskimg, (off_t)newFooterPosition + 512, SEEK_SET)) return 0x05;
00340                 //now write the footer
00341                 if (fseeko64(diskimg, (off_t)newFooterPosition, SEEK_SET)) return 0x05;
00342                 if (fwrite(&originalFooter, sizeof(Bit8u), 512, diskimg) != 512) return 0x05;
00343                 //save the new block location and new footer position
00344                 Bit32u newBlockSectorNumber = (Bit32u)((footerPosition + 511ul) / 512ul);
00345                 footerPosition = newFooterPosition;
00346                 //clear the dirty flags for the new footer position
00347                 for (Bit32u i = 0; i < blockMapSize; i++) currentBlockDirtyMap[i] = 0;
00348                 //write the dirty map
00349                 if (fseeko64(diskimg, (off_t)(newBlockSectorNumber * 512ull), SEEK_SET)) return 0x05;
00350                 if (fwrite(currentBlockDirtyMap, sizeof(Bit8u), blockMapSize, diskimg) != blockMapSize) return 0x05;
00351                 //flush the data to disk after expanding the file, before allocating the block in the BAT
00352                 if (fflush(diskimg)) return 0x05;
00353                 //update the BAT
00354                 if (fseeko64(diskimg, (off_t)(dynamicHeader.tableOffset + (blockNumber * 4ull)), SEEK_SET)) return 0x05;
00355                 Bit32u newBlockSectorNumberBE = SDL_SwapBE32(newBlockSectorNumber);
00356                 if (fwrite(&newBlockSectorNumberBE, sizeof(Bit8u), 4, diskimg) != 4) return false;
00357                 currentBlockAllocated = true;
00358                 currentBlockSectorOffset = newBlockSectorNumber;
00359                 //flush the data to disk after allocating a block
00360                 if (fflush(diskimg)) return 0x05;
00361         }
00362         //current block has now been allocated
00363         Bit32u byteNum = sectorOffset / 8;
00364         Bit32u bitNum = sectorOffset % 8;
00365         bool hasData = currentBlockDirtyMap[byteNum] & (1 << (7 - bitNum));
00366         //if the sector hasn't been marked as dirty, mark it as dirty
00367         if (!hasData) {
00368                 currentBlockDirtyMap[byteNum] |= 1 << (7 - bitNum);
00369                 if (fseeko64(diskimg, (off_t)(currentBlockSectorOffset * 512ull), SEEK_SET)) return 0x05; //can't seek
00370                 if (fwrite(currentBlockDirtyMap, sizeof(Bit8u), blockMapSize, diskimg) != blockMapSize) return 0x05;
00371         }
00372         //current sector has now been marked as dirty
00373         //write the sector
00374         if (fseeko64(diskimg, (off_t)(((Bit64u)currentBlockSectorOffset + (Bit64u)blockMapSectors + (Bit64u)sectorOffset) * 512ull), SEEK_SET)) return 0x05; //can't seek
00375         if (fwrite(data, sizeof(Bit8u), 512, diskimg) != 512) return 0x05; //can't write
00376         return 0;
00377 }
00378 
00379 imageDiskVHD::VHDTypes imageDiskVHD::GetVHDType(const char* fileName) {
00380         imageDisk* disk;
00381         if (Open(fileName, true, &disk)) return VHD_TYPE_NONE;
00382     const imageDiskVHD* vhd = dynamic_cast<imageDiskVHD*>(disk);
00383         VHDTypes ret = VHD_TYPE_FIXED; //fixed if an imageDisk was returned
00384         if (vhd) ret = vhd->footer.diskType; //get the actual type if an imageDiskVHD was returned
00385         delete disk;
00386         return ret;
00387 }
00388 
00389 bool imageDiskVHD::loadBlock(const Bit32u blockNumber) {
00390         if (currentBlock == blockNumber) return true;
00391         if (blockNumber >= dynamicHeader.maxTableEntries) return false;
00392         if (fseeko64(diskimg, (off_t)(dynamicHeader.tableOffset + (blockNumber * 4ull)), SEEK_SET)) return false;
00393         Bit32u blockSectorOffset;
00394         if (fread(&blockSectorOffset, sizeof(Bit8u), 4, diskimg) != 4) return false;
00395         blockSectorOffset = SDL_SwapBE32(blockSectorOffset);
00396         if (blockSectorOffset == 0xFFFFFFFFul) {
00397                 currentBlock = blockNumber;
00398                 currentBlockAllocated = false;
00399         }
00400         else {
00401                 if (fseeko64(diskimg, (off_t)(blockSectorOffset * (Bit64u)512), SEEK_SET)) return false;
00402                 currentBlock = 0xFFFFFFFFul;
00403                 currentBlockAllocated = true;
00404                 currentBlockSectorOffset = blockSectorOffset;
00405                 if (fread(currentBlockDirtyMap, sizeof(Bit8u), blockMapSize, diskimg) != blockMapSize) return false;
00406                 currentBlock = blockNumber;
00407         }
00408         return true;
00409 }
00410 
00411 imageDiskVHD::~imageDiskVHD() {
00412         if (currentBlockDirtyMap) {
00413                 free(currentBlockDirtyMap);
00414                 currentBlockDirtyMap = 0;
00415         }
00416         if (parentDisk) {
00417                 parentDisk->Release();
00418                 parentDisk = 0;
00419         }
00420 }
00421 
00422 void imageDiskVHD::VHDFooter::SwapByteOrder() {
00423         features = SDL_SwapBE32(features);
00424         fileFormatVersion = SDL_SwapBE32(fileFormatVersion);
00425         dataOffset = SDL_SwapBE64(dataOffset);
00426         timeStamp = SDL_SwapBE32(timeStamp);
00427         creatorVersion = SDL_SwapBE32(creatorVersion);
00428         creatorHostOS = SDL_SwapBE32(creatorHostOS);
00429         originalSize = SDL_SwapBE64(originalSize);
00430         currentSize = SDL_SwapBE64(currentSize);
00431         geometry.cylinders = SDL_SwapBE16(geometry.cylinders);
00432         diskType = (VHDTypes)SDL_SwapBE32((Bit32u)diskType);
00433         checksum = SDL_SwapBE32(checksum);
00434         //guid might need the byte order swapped also
00435         //however, for our purposes (comparing to the parent guid on differential disks),
00436         //  it doesn't matter so long as we are consistent
00437 }
00438 
00439 Bit32u imageDiskVHD::VHDFooter::CalculateChecksum() {
00440         //checksum is one's complement of sum of bytes excluding the checksum
00441         //because of that, the byte order doesn't matter when calculating the checksum
00442         //however, the checksum must be stored in the correct byte order or it will not match
00443 
00444         Bit32u ret = 0;
00445     const Bit8u* dat = (Bit8u*)this->cookie;
00446         Bit32u oldChecksum = checksum;
00447         checksum = 0;
00448         for (size_t i = 0; i < sizeof(VHDFooter); i++) {
00449                 ret += dat[i];
00450         }
00451         checksum = oldChecksum;
00452         return ~ret;
00453 }
00454 
00455 bool imageDiskVHD::VHDFooter::IsValid() {
00456         return (
00457                 memcmp(cookie, "conectix", 8) == 0 &&
00458                 fileFormatVersion >= 0x00010000 &&
00459                 fileFormatVersion <= 0x0001FFFF &&
00460                 checksum == CalculateChecksum());
00461 }
00462 
00463 void imageDiskVHD::DynamicHeader::SwapByteOrder() {
00464         dataOffset = SDL_SwapBE64(dataOffset);
00465         tableOffset = SDL_SwapBE64(tableOffset);
00466         headerVersion = SDL_SwapBE32(headerVersion);
00467         maxTableEntries = SDL_SwapBE32(maxTableEntries);
00468         blockSize = SDL_SwapBE32(blockSize);
00469         checksum = SDL_SwapBE32(checksum);
00470         parentTimeStamp = SDL_SwapBE32(parentTimeStamp);
00471         for (int i = 0; i < 256; i++) {
00472                 parentUnicodeName[i] = SDL_SwapBE16(parentUnicodeName[i]);
00473         }
00474         for (int i = 0; i < 8; i++) {
00475                 parentLocatorEntry[i].platformCode = SDL_SwapBE32(parentLocatorEntry[i].platformCode);
00476                 parentLocatorEntry[i].platformDataSpace = SDL_SwapBE32(parentLocatorEntry[i].platformDataSpace);
00477                 parentLocatorEntry[i].platformDataLength = SDL_SwapBE32(parentLocatorEntry[i].platformDataLength);
00478                 parentLocatorEntry[i].reserved = SDL_SwapBE32(parentLocatorEntry[i].reserved);
00479                 parentLocatorEntry[i].platformDataOffset = SDL_SwapBE64(parentLocatorEntry[i].platformDataOffset);
00480         }
00481         //parent guid might need the byte order swapped also
00482         //however, for our purposes (comparing to the parent guid on differential disks),
00483         //  it doesn't matter so long as we are consistent
00484 }
00485 
00486 Bit32u imageDiskVHD::DynamicHeader::CalculateChecksum() {
00487         //checksum is one's complement of sum of bytes excluding the checksum
00488         //because of that, the byte order doesn't matter when calculating the checksum
00489         //however, the checksum must be stored in the correct byte order or it will not match
00490 
00491         Bit32u ret = 0;
00492     const Bit8u* dat = (Bit8u*)this->cookie;
00493         Bit32u oldChecksum = checksum;
00494         checksum = 0;
00495         for (size_t i = 0; i < sizeof(DynamicHeader); i++) {
00496                 ret += dat[i];
00497         }
00498         checksum = oldChecksum;
00499         return ~ret;
00500 }
00501 
00502 bool imageDiskVHD::DynamicHeader::IsValid() {
00503         return (
00504                 memcmp(cookie, "cxsparse", 8) == 0 &&
00505                 headerVersion >= 0x00010000 &&
00506                 headerVersion <= 0x0001FFFF &&
00507                 checksum == CalculateChecksum());
00508 }