diff --git a/BtrieveFileSaverLib.c b/BtrieveFileSaverLib.c index f11d266..a4e47e7 100644 --- a/BtrieveFileSaverLib.c +++ b/BtrieveFileSaverLib.c @@ -100,11 +100,48 @@ unsigned short int freeClient (CLIENT_STRUCT *cl, unsigned short int ErrorCode) * Just swap the bytes of a 32 bit int value * */ -static unsigned long int byte_swap(unsigned long int i ) +static uint32_t byte_swap(uint32_t i ) { return ((i>>16)&0xFFFF) | (i<<16); } +static uint16_t read_le16(const void *data) +{ + const unsigned char *bytes = (const unsigned char*)data; + return (uint16_t)(bytes[0] | ((uint16_t)bytes[1] << 8)); +} + +static uint32_t read_le32(const void *data) +{ + const unsigned char *bytes = (const unsigned char*)data; + return (uint32_t)bytes[0] | + ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16) | + ((uint32_t)bytes[3] << 24); +} + +static uint32_t read_word_swapped_u32(const void *data) +{ + return (uint32_t)byte_swap(read_le32(data)); +} + +static void write_le32(void *data, uint32_t value) +{ + unsigned char *bytes = (unsigned char*)data; + bytes[0] = (unsigned char)(value & 0xff); + bytes[1] = (unsigned char)((value >> 8) & 0xff); + bytes[2] = (unsigned char)((value >> 16) & 0xff); + bytes[3] = (unsigned char)((value >> 24) & 0xff); +} + +static uint32_t read_vrec_page_id_v5(const char *data) +{ + uint32_t value = (uint32_t)(unsigned char)data[0] | + ((uint32_t)(unsigned char)data[1] << 16) | + ((uint32_t)(unsigned char)data[2] << 24); + return value == PAGE_KICK_OFF ? value : byte_swap(value); +} + /* * * Function getPageType @@ -117,28 +154,38 @@ static unsigned long int byte_swap(unsigned long int i ) * this way. * */ -char getPageType (CLIENT_STRUCT *cl, char *tmpPage) -{ - switch (cl->fVersion){ - case BTRIEVE_FILE_V3: - case BTRIEVE_FILE_V4: - case BTRIEVE_FILE_V5:{ - if (*(unsigned short int*)(tmpPage + 4) & 0x8000) return DAT_PAGE_ID; - }break; - case BTRIEVE_FILE_V6: - case BTRIEVE_FILE_V61: - case BTRIEVE_FILE_V7:{ - return tmpPage[1]; - }break; - case BTRIEVE_FILE_V8: - case BTRIEVE_FILE_V9: -// case BTRIEVE_FILE_V95: - { - return tmpPage[4]; - }break; - } - return 0x00; -} +#if defined(_MSC_VER) && _MSC_VER >= 1950 +/* + * MSVC 19.50's global optimizer drops the V3/V4/V5 arm from this sparse + * 16-bit switch in the complete library translation unit. Keep the original + * decoder and disable only that optimization pass for this function. The + * WineVDM golden fixtures exercise this path in release builds. + */ +#pragma optimize("g", off) +#endif +char getPageType (CLIENT_STRUCT *cl, char *tmpPage) +{ + switch (cl->fVersion){ + case BTRIEVE_FILE_V3: + case BTRIEVE_FILE_V4: + case BTRIEVE_FILE_V5:{ + if (read_le16(tmpPage + 4) & 0x8000) return DAT_PAGE_ID; + }break; + case BTRIEVE_FILE_V6: + case BTRIEVE_FILE_V61: + case BTRIEVE_FILE_V7:{ + return tmpPage[1]; + }break; + case BTRIEVE_FILE_V8: + case BTRIEVE_FILE_V9:{ + return tmpPage[4]; + }break; + } + return 0x00; +} +#if defined(_MSC_VER) && _MSC_VER >= 1950 +#pragma optimize("", on) +#endif /* * @@ -190,13 +237,13 @@ unsigned long int addPage (CLIENT_STRUCT *cl, char *tmpPage, char pageType) }else return NO_ERROR; if (cl->fVersion >= BTRIEVE_FILE_V8){ - pId = *(short*) tmpPage; - pUsage = (short)*(tmpPage + 6); + pId = read_le16(tmpPage); + pUsage = read_le16(tmpPage + 6); }else{ - pId = *(unsigned long int*)tmpPage; + pId = read_le32(tmpPage); pId &= PAGE_KICK_OFF; pId = byte_swap(pId); - pUsage = *(signed short*)(tmpPage + 4); + pUsage = read_le16(tmpPage + 4); } /* check if you have allready a page using the same id but lower usage counter */ @@ -281,7 +328,7 @@ unsigned short int readAllPagesFromFile (CLIENT_STRUCT *cl) while ((pType = getNextPhysicalPage(cl, cl->CUR_DPAGE)) != 0x00) addPage (cl, cl->CUR_DPAGE, pType); /* reset the header of the data page container */ - *(unsigned long int*)cl->CUR_DPAGE = 0L; + write_le32(cl->CUR_DPAGE, 0); return NO_ERROR; } @@ -394,13 +441,13 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName) /* read the shadow page to validate both pairs and retrieve the number of records */ if ((fread (&lcFCR_shadow, sizeof(FCR), 1, cl->fHandle)) != 1) return freeClient (cl, IO_ERROR); if (lcFCR.FCRUsageCount > lcFCR_shadow.FCRUsageCount) - cl->numRecs = byte_swap (*((unsigned long*)&lcFCR.numRecs)); + cl->numRecs = read_word_swapped_u32(lcFCR.numRecs); else - cl->numRecs = byte_swap (*((unsigned long*)&lcFCR_shadow.numRecs)); - }else{ /* less then 6x format */ + cl->numRecs = read_word_swapped_u32(lcFCR_shadow.numRecs); + }else{ /* less then 6x format */ cl->fPageSize = lcFCR.PageSize; cl->VFragParam = VFRAG_CUT_V5; - cl->numRecs = byte_swap (*((unsigned long*)&lcFCR.numRecs)); + cl->numRecs = read_word_swapped_u32(lcFCR.numRecs); } if (cl->fVersion >= BTRIEVE_FILE_V6 && cl->fVersion < BTRIEVE_FILE_V7){ @@ -409,7 +456,7 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName) /* allocate memory to hold the current data page */ if ((cl->CUR_DPAGE = (char*) malloc (cl->fPageSize)) == NULL) return IO_ERROR; - else *(long*)cl->CUR_DPAGE = 0L; + else write_le32(cl->CUR_DPAGE, 0); /* get the max number of pages */ fseek (cl->fHandle, 0, SEEK_END); @@ -423,7 +470,7 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName) /* if var-rec's, allocate memory to hold the current var-data page */ if (cl->VarRecsAllowed){ if ((cl->CUR_VPAGE = (char*) malloc (cl->fPageSize)) == NULL) return IO_ERROR; - else *(long*)cl->CUR_VPAGE = 0L; + else write_le32(cl->CUR_VPAGE, 0); } return NO_ERROR; @@ -436,7 +483,7 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName) * * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) -{ +{ unsigned long int ret, pat1, pat2; unsigned short int u1, u2, pppat; @@ -451,10 +498,16 @@ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) pat1 = ret * (long)cl->fPageSize; // first PAT of pair pat2 = pat1 + (long)cl->fPageSize; // second right after it - fseek( cl->fHandle, pat1+4L, 0 ); // get both usage counts - fread( &u1, 2, 1, cl->fHandle ); - fseek( cl->fHandle, pat2+4L, 0 ); - fread( &u2, 2, 1, cl->fHandle ); + { + unsigned char rawUsage[2]; + + fseek( cl->fHandle, pat1+4L, 0 ); // get both usage counts + fread( rawUsage, sizeof(rawUsage), 1, cl->fHandle ); + u1 = read_le16(rawUsage); + fseek( cl->fHandle, pat2+4L, 0 ); + fread( rawUsage, sizeof(rawUsage), 1, cl->fHandle ); + u2 = read_le16(rawUsage); + } if( u1 > u2 ) // choose most recent one ret = pat1; else @@ -462,9 +515,12 @@ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) ret += (long)(( lp << 2 ) + 4L ); // position in PAT fseek( cl->fHandle, ret, 0 ); - fread( &lp, 4, 1, cl->fHandle ); // read it into LP + { + unsigned char rawLp[4] = {0, 0, 0, 0}; + fread( rawLp, 4, 1, cl->fHandle ); // read it into LP - lp = byte_swap( lp ); // and un-word-swap it + lp = byte_swap( read_le32(rawLp) ); // and un-word-swap it + } lp &= 0xFFFFFFL; } @@ -485,32 +541,38 @@ static long int lp_pp(CLIENT_STRUCT *cl, long int lp ) * */ unsigned short int getNextDataPage (CLIENT_STRUCT *cl) -{ - unsigned short int retval = NO_ERROR; - unsigned long int pOff; +{ + unsigned short int retval = NO_ERROR; + long int pOff; unsigned long int cPId = cl->curDPageID; memset (cl->CUR_DPAGE, 0x00, cl->fPageSize); - while ((getPageType (cl, cl->CUR_DPAGE)) != DAT_PAGE_ID /*&& cPId < cl->fNumPages-2*/){ + while (getPageType (cl, cl->CUR_DPAGE) != DAT_PAGE_ID){ + uint64_t fileSize; + + if (cPId >= cl->fNumPages) + return END_OF_FILE; + pOff = lp_pp (cl, cPId++); - - if (pOff > (cl->fNumPages * cl->fPageSize)) continue; + fileSize = (uint64_t)cl->fNumPages * cl->fPageSize; + if (pOff < 0 || (uint64_t)pOff >= fileSize) + continue; - if ((retval = readPageFromFile (cl, cl->CUR_DPAGE, pOff)) != NO_ERROR) return retval; + if ((retval = readPageFromFile (cl, cl->CUR_DPAGE, (unsigned long)pOff)) != NO_ERROR) + return retval; - if (cl->fVersion == BTRIEVE_FILE_V6 && *(unsigned short*)(cl->CUR_DPAGE+2) != cPId -1){ - *(unsigned long*)cl->CUR_DPAGE = 0L; + if (cl->fVersion == BTRIEVE_FILE_V6 && read_le16(cl->CUR_DPAGE + 2) != cPId - 1){ + write_le32(cl->CUR_DPAGE, 0); continue; } if (cl->fVersion == BTRIEVE_FILE_V3){ - unsigned long usage = 0L; - ((unsigned char*)&usage)[2] = cl->CUR_DPAGE[4]; - ((unsigned char*)&usage)[1] = cl->CUR_DPAGE[6]; - ((unsigned char*)&usage)[0] = cl->CUR_DPAGE[7]; + uint32_t usage = ((uint32_t)(unsigned char)cl->CUR_DPAGE[4] << 16) + | ((uint32_t)(unsigned char)cl->CUR_DPAGE[6] << 8) + | (unsigned char)cl->CUR_DPAGE[7]; if (usage == 0){ - memset (cl->CUR_DPAGE, 0x00 , 10); + memset (cl->CUR_DPAGE, 0x00, 10); continue; } } @@ -534,7 +596,7 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne { unsigned short int numFrag = 0; unsigned short int retval = 0; - unsigned long int VPageId = 0; + uint32_t VPageId = 0; unsigned short int FragId = 0; unsigned short int FragOff = 0; unsigned long int pCnt = 0L; @@ -547,15 +609,11 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne memcpy (dataBuffer, curRecAdr + cl->recHeaderSize, (cl->FixRecLen > *dbLen) ? *dbLen : cl->FixRecLen); if (cl->fVersion >= BTRIEVE_FILE_V8){ - VPageId = *(short*)(curRecAdr + cl->FixRecLen + cl->recHeaderSize); - numFrag = *(short*)(curRecAdr + cl->FixRecLen + 10); + VPageId = read_le16(curRecAdr + cl->FixRecLen + cl->recHeaderSize); + numFrag = read_le16(curRecAdr + cl->FixRecLen + 10); }else{ - ((unsigned char*)&VPageId)[0] = * (curRecAdr + cl->recHeaderSize + curLen); - ((unsigned char*)&VPageId)[1] = 0x00; - ((unsigned char*)&VPageId)[2] = *(curRecAdr + cl->recHeaderSize + curLen + 1); - ((unsigned char*)&VPageId)[3] = *(curRecAdr + cl->recHeaderSize + curLen + 2); - ((unsigned char*)&numFrag)[0] = *(curRecAdr + cl->recHeaderSize + curLen + 3); - if (VPageId != PAGE_KICK_OFF) VPageId = byte_swap (VPageId); + VPageId = read_vrec_page_id_v5(curRecAdr + cl->recHeaderSize + curLen); + numFrag = (unsigned char)*(curRecAdr + cl->recHeaderSize + curLen + 3); } while (VPageId != PAGE_KICK_OFF && FragId != MAX_SHORT_INT_VAL){ @@ -579,30 +637,25 @@ unsigned short int getVariableData (CLIENT_STRUCT *cl, char *dataBuffer, unsigne || (fread (cl->CUR_VPAGE, cl->fPageSize, 1, cl->fHandle)) != 1) return IO_ERROR; FragId = ((cl->fPageSize -1) >> 1) - numFrag; - FragOff = ((short*)cl->CUR_VPAGE)[FragId] & 0x7FFF; + FragOff = read_le16(cl->CUR_VPAGE + (FragId * sizeof(uint16_t))) & 0x7FFF; /* get to the end of the fragment */ - for(fEId = 1; ((short*)cl->CUR_VPAGE)[FragId - fEId] == -1; fEId++); - + for(fEId = 1; read_le16(cl->CUR_VPAGE + ((FragId - fEId) * sizeof(uint16_t))) == UINT16_MAX; fEId++); + /* calculate the length of the fragment */ - addLen = (((short*)cl->CUR_VPAGE)[ FragId - fEId] & 0x7FFF ) - FragOff - cl->VFragParam; + addLen = (read_le16(cl->CUR_VPAGE + ((FragId - fEId) * sizeof(uint16_t))) & 0x7FFF) - FragOff - cl->VFragParam; if (addLen > cl->fPageSize * 2 || addLen == 0) break; if (cl->fVersion >= BTRIEVE_FILE_V8){ - VPageId = *(short*)(&((char*)cl->CUR_VPAGE)[ FragOff]); - numFrag = *(short*)(&((char*)cl->CUR_VPAGE)[ FragOff +4]); + VPageId = read_le16(&((char*)cl->CUR_VPAGE)[ FragOff]); + numFrag = read_le16(&((char*)cl->CUR_VPAGE)[ FragOff +4]); }else{ - ((unsigned char*)&VPageId)[0] = * (cl->CUR_VPAGE + FragOff); - ((unsigned char*)&VPageId)[1] = 0x00; - ((unsigned char*)&VPageId)[2] = *(cl->CUR_VPAGE + FragOff + 1); - ((unsigned char*)&VPageId)[3] = *(cl->CUR_VPAGE + FragOff + 2); - ((unsigned char*)&numFrag)[0] = *(cl->CUR_VPAGE + FragOff + 3); - - if (VPageId != PAGE_KICK_OFF) VPageId = byte_swap (VPageId); + VPageId = read_vrec_page_id_v5(cl->CUR_VPAGE + FragOff); + numFrag = (unsigned char)*(cl->CUR_VPAGE + FragOff + 3); if (cl->fVersion < BTRIEVE_FILE_V6){ - if (((short*)cl->CUR_VPAGE)[ FragId] & 0x8000){ + if (read_le16(cl->CUR_VPAGE + (FragId * sizeof(uint16_t))) & 0x8000){ FragOff += sizeof (VRECPTR); addLen -= sizeof(VRECPTR); }else VPageId = PAGE_KICK_OFF; @@ -675,13 +728,13 @@ unsigned short int BF_GET_REC (CLIENT_STRUCT *cl, char *dataBuffer, unsigned lon if (cl->numRecs == 0L) return END_OF_FILE; while ((retval = getNextRecord (cl)) == NO_ERROR){ - if (cl->fVersion >= BTRIEVE_FILE_V6 && *(unsigned short*)(cl->curRecAdr) != 0x0000){ + if (cl->fVersion >= BTRIEVE_FILE_V6 && read_le16(cl->curRecAdr) != 0x0000){ break; }else if (cl->fVersion < BTRIEVE_FILE_V6){ unsigned long int i = 0; char isEmpty = true; - if (cl->fVersion == BTRIEVE_FILE_V3 && *(unsigned short int*)cl->curRecAdr == 0x0000) continue; + if (cl->fVersion == BTRIEVE_FILE_V3 && read_le16(cl->curRecAdr) == 0x0000) continue; for (i = ((cl->fVersion >= BTRIEVE_FILE_V4)? RECORD_HEADER_SIZE_V4 : RECORD_HEADER_SIZE_V3) ; i < cl->IFixedRecLen ; i++){ @@ -698,7 +751,10 @@ unsigned short int BF_GET_REC (CLIENT_STRUCT *cl, char *dataBuffer, unsigned lon if (retval != NO_ERROR) return retval; - cl->curRecordId ++; + if (cl->curRecordId == MAX_LONG_INT_VAL) + cl->curRecordId = 0; + else + cl->curRecordId ++; if (cl->VarRecsAllowed){ retval = getVariableData (cl, dataBuffer, dbLen, cl->curRecAdr); diff --git a/BtrieveFileSaverLib.h b/BtrieveFileSaverLib.h index 5a42f4f..04ef7d0 100644 --- a/BtrieveFileSaverLib.h +++ b/BtrieveFileSaverLib.h @@ -31,8 +31,9 @@ #include #endif -#include -#include +#include +#include +#include #pragma pack(push) #pragma pack(1) @@ -62,11 +63,11 @@ /* project defines */ #define MAX_SINGLE_INT_VAL 0xFF // single byte int max value #define MAX_SHORT_INT_VAL 0xFFFF // 2 byte int max value -#define MAX_LONG_INT_VAL 0xFFFFFFFF // 4 byte int max value - -#define PAGE_KICK_OFF 0xFFFF00FF // define to overcome the page id at three byte page header - -#define FCR_IDENT 0x00004346 // byte swaped 'FC' +#define MAX_LONG_INT_VAL UINT32_C(0xFFFFFFFF) // 4 byte int max value + +#define PAGE_KICK_OFF UINT32_C(0xFFFF00FF) // define to overcome the page id at three byte page header + +#define FCR_IDENT UINT32_C(0x00004346) // byte swaped 'FC' /* File type/feature flags */ @@ -125,8 +126,8 @@ /* macro definition */ -#define ReverseLongInt(x) ((x) << 16 | (x) >> 16) -#define VRPage(x) (long) (((long) (x).hi << 16) | ((x).mid << 8) | (x).lo) +#define ReverseLongInt(x) (((uint32_t)(x) << 16) | ((uint32_t)(x) >> 16)) +#define VRPage(x) (uint32_t) (((uint32_t) (x).hi << 16) | ((uint32_t)(x).mid << 8) | (x).lo) /* struct defines */ typedef struct @@ -137,16 +138,16 @@ typedef struct unsigned char frag; }VRECPTR; -typedef struct{ - unsigned long int pId; /* page id, note that it is 16Bit */ - unsigned long int offset; /* physical position within the file */ - unsigned short int indexA; /* sort order based on pID */ -}PAGE_LINK; - -typedef struct{ - unsigned long int PageId; - unsigned short int FCRUsageCount; - short int Version; +typedef struct{ + uint32_t pId; /* page id, note that it is 16Bit */ + unsigned long int offset; /* physical position within the file */ + unsigned short int indexA; /* sort order based on pID */ +}PAGE_LINK; + +typedef struct{ + uint32_t PageId; + unsigned short int FCRUsageCount; + short int Version; short int PageSize; char buffer1[12]; unsigned short int FixedRecordLength; /* 0 means compressed */ @@ -201,4 +202,4 @@ unsigned short int BF_OPEN (CLIENT_STRUCT *cl, char *fName); unsigned short int BF_GET_REC (CLIENT_STRUCT *cl, char *dataBuffer, unsigned long *dbLen); unsigned short int BF_CLOSE (CLIENT_STRUCT *cl); -#pragma pack(pop) \ No newline at end of file +#pragma pack(pop)