/* C/C++ code */
typedef unsigned __int64 u64;
typedef unsigned long u32;
typedef unsigned short u16;
typedef unsigned char u8;
typedef LARGE_INTEGER i64;
typedef __int64 s64;
typedef long s32;
typedef short s16;
typedef char s8;
typedef float f32;
typedef double f64;
struct SSaveToVar
{
s8 name[11]; // First 10 of name, "FOO",00,00,00,00,00,00,00,[trailing NULL]
u8 type; // CH,N,I,etc, if lower-case, then variable name is long and is stored in the data area
u8 unk1[1]; // 00
u32 length; // 00,00,00,18 (includes trailing null)
u8 decimals; // 00
u8 unk2[7]; // 00,00,00,00,00,00,00
u8 val; // 03
u8 unk3[6]; // 00,00,00,00,00,00
// Total 32 bytes
};
// These formats immediately follow SSaveToVar
struct SSaveToVarLongName
{
u16 length; // Length of the name (bytes of name immediately follow)
};
struct SSaveToVarArray
{
u16 rows; // 02,00
u16 cols; // 03,00
};
struct SSaveToVarFloat
{
f64 fVal; // 8-byte floating point value
};
struct SSaveToVarLogical
{
u8 lVal; // 8-bit indicator, 0=false, 1=true
};
struct SSaveToVarDate
{
u64 dVal; // 8-byte date value
};
struct SSaveToVarDatetime
{
u64 dVal; // 8-byte datetime value
};
//////////
//
// Called to parse the SAVE TO filename file, to read all variables
// and return them in a list of quote-comma delimited line items,
// such as:
//
// (C:8)foo = whatever
// (N:5,2)i = 12.03
// lnArray[1,1](C:4) = foo
// lnArray[1,2](N:1) = 0
//
//////
u32 process_save_to_file(s8* tcInputFile, s8* tcOutputFile)
{
u32 lnLength, lnRow, lnCol;
STemplate* builder;
// Holds loaded content
s8* lcData;
u32 lnDataLength;
SSaveToVar* stv;
SSaveToVarArray* stva;
//////////
// Try to open the specified input file
//////
iLoadFileContents(tcInputFile, NULL, &lcData, &lnDataLength, NULL);
if (lcData == NULL)
return(0);
// If we get here, we're good, we have our loaded buffer
//////////
// Allocate our builder accumulation buffer
//////
iAllocateAndInitializeAccumulationTemplate(&builder);
//////////
// Iterate through the data
//////
stv = (SSaveToVar*)lcData;
while ((u32)stv < (u32)lcData + lnDataLength && stv->name[0] != 0x1a/* VFP uses CHR(26) (which is hexadecimal 0x1a) as the terminator for the file (the CTRL+Z character)*/)
{
// Find out what type of variable it is
if (stv->type == 'a' || stv->type == 'A')
{
// It's an array, special processing
stva = (SSaveToVarArray*)(stv + 1); // Array data comes immediately after initial header
//////////
// Store the name
/////
iSaveTo_StoreName(builder, stv);
//////////
// Store the array portion after the name above, so it's like:
// foo[5,5] - array
//////
iAppendToTemplate(builder, "[", 1, NULL, NULL);
iAppendToTemplateInteger(builder, (s32)stva->rows, NULL, NULL);
if (stva->cols != 0)
{
iAppendToTemplate(builder, ",", 1, NULL, NULL);
iAppendToTemplateInteger(builder, (s32)stva->cols, NULL, NULL);
}
iAppendToTemplate(builder, "] - array", 9, NULL, NULL);
// Append CR/LF after
iAppendToTemplate(builder, "\r\n", 2, NULL, NULL);
//////////
// Now, move stv forward to the start of the next thing
//////
stv = (SSaveToVar*)((s8*)(stv+1) + sizeof(SSaveToVarArray));
//////////
// Repeat for each entry that will follow, for both dimensions of the array
//////
for (lnRow = 1; lnRow <= (u32)stva->rows; lnRow++)
{
// Store the array reference
if (stva->cols == 0)
{
// No columns, single-dimension array
// Store the name
iSaveTo_StoreName(builder, stv);
iAppendToTemplate(builder, "[", 1, NULL, NULL);
iAppendToTemplateInteger(builder, lnRow, NULL, NULL);
iAppendToTemplate(builder, "]", 1, NULL, NULL);
// Store the data
stv = iSaveTo_StoreData(builder, stv, false);
// Append CR/LF after
iAppendToTemplate(builder, "\r\n", 2, NULL, NULL);
} else {
// Two-dimensional array
for (lnCol = 1; lnCol <= (u32)stva->cols; lnCol++)
{
// Store the name
iSaveTo_StoreName(builder, stv);
iAppendToTemplate(builder, "[", 1, NULL, NULL);
iAppendToTemplateInteger(builder, lnRow, NULL, NULL);
iAppendToTemplate(builder, ",", 1, NULL, NULL);
iAppendToTemplateInteger(builder, lnCol, NULL, NULL);
iAppendToTemplate(builder, "]", 1, NULL, NULL);
// Store the data
stv = iSaveTo_StoreData(builder, stv, false);
// Append CR/LF after
iAppendToTemplate(builder, "\r\n", 2, NULL, NULL);
}
}
}
// When we get here, every array has been handld
} else {
// Store the type and data
stv = iSaveTo_StoreData(builder, stv, true);
// Append CR/LF after
iAppendToTemplate(builder, "\r\n", 2, NULL, NULL);
}
}
//////////
// Copy it back out for the caller
/////
lnLength = builder->currentLength;
utils_write_to_file(tcOutputFile, 0, 0, builder->buffer, lnLength);
//////////
// Release our accumulation buffer and source data buffer
/////
iFreeAndReleaseAccumulationTemplate(&builder, true);
free(lcData);
// All done
return(lnLength);
}
void iSaveTo_StoreName(STemplate* builder, SSaveToVar* stv)
{
u32 length;
s8* updateAfter;
s8* start;
SSaveToVarLongName* stvln;
if (stv->type >= 'a' && stv->type <= 'z')
{
// It's a lower-case letter, meaning it has a long variable name
stvln = (SSaveToVarLongName*)(stv+1);
length = stvln->length;
start = (s8*)(stvln+1);
} else {
// Regular short variable name
start = (s8*)&stv->name;
length = strlen((s8*)&stv->name);
}
// Store the name
updateAfter = builder->buffer + builder->currentLength;
iAppendToTemplate(builder, start, length, NULL, NULL);
// Fix it up for proper capitalization
if (length >= 2)
{
if (iIsNeedleInHaystack((s8*)cgcKnownFirsts, sizeof(cgcKnownFirsts) - 1, start, 1) && iIsNeedleInHaystack((s8*)cgcKnownSeconds, sizeof(cgcKnownSeconds) - 1, start + 1, 1))
{
// It matches the format, so we lower-case the first two characters
iLowercase(updateAfter, 2);
// And every character after that
if (length >= 4)
iLowercase(updateAfter + 3, length - 4 + 1/*base-1*/);
}
}
}
SSaveToVar* iSaveTo_StoreData(STemplate* builder, SSaveToVar* stv, bool tlStoreName)
{
s8* start;
SSaveToVarFloat* stvf;
SSaveToVarLogical* stvl;
SSaveToVarDate* stvd;
SSaveToVarDatetime* stvt;
s8 buffer[32];
//////////
// Store the type, length and decimals, looks like this:
// (C:1024)
// (L:1)
// (N:18,7)
//////
// Store leading parenthesis
iAppendToTemplate(builder, "(", 1, NULL, NULL);
// Store the type as upper-case
sprintf_s(buffer, sizeof(buffer), "%c\000", stv->type);
if (buffer[0] >= 'a' && buffer[0] <= 'z')
buffer[0] -= 0x20; // Convert to upper-case
iAppendToTemplate(builder, buffer, 1, NULL, NULL);
// Store colon between type and length data
iAppendToTemplate(builder, ":", 1, NULL, NULL);
// Store length data
iAppendToTemplateInteger(builder, iSwapEndian(stv->length), NULL, NULL);
if (stv->decimals != 0)
{
iAppendToTemplate(builder, ",", 1, NULL, NULL);
iAppendToTemplateInteger(builder, stv->decimals, NULL, NULL);
}
// Store closing parenthesis
iAppendToTemplate(builder, ")", 1, NULL, NULL);
//////////
// Store the name, ends up looking like this
// (C:1024)foo =
//////
// Store the variable name
if (tlStoreName)
iSaveTo_StoreName(builder, stv);
iAppendToTemplate(builder, " = ", 3, NULL, NULL);
//////////
// Get an offset to the actual data
//////
start = (s8*)(stv + 1) +
(
(stv->type >= 'a' && stv->type <= 'z')
? sizeof(SSaveToVarLongName) + ((SSaveToVarLongName*)(stv+1))->length
: 0
);
//////////
// Store the actual data
// (C:1024)foo = "whatever"
// (N:10)foo2 = 5
// (N:18,7)foo3 = 1.1234567
//////
if (stv->type == 'c' || stv->type == 'C' || stv->type == 'h' || stv->type == 'H')
{
// Character (h/H is HUGE character data)
iAppendToTemplateSkipNulls(builder, (s8*)start, iSwapEndian(stv->length), NULL, NULL);
// Indicate where the next one will go after this entry
stv = (SSaveToVar*)(start + iSwapEndian(stv->length));
} else if (stv->type == 'n' || stv->type == 'N') {
// Numeric
stvf = (SSaveToVarFloat*)start;
iAppendToTemplateDouble(builder, stvf->fVal, iSwapEndian(stv->length), stv->decimals, NULL, NULL);
// Indicate where the next one will go after this entry
stv = (SSaveToVar*)(start + sizeof(SSaveToVarFloat));
} else if (stv->type == 'l' || stv->type == 'L') {
// Logical
// Indicate where the next one will go after this entry
stvl = (SSaveToVarLogical*)start;
if (stvl->lVal == 0)
{
// False
iAppendToTemplate(builder, ".F.", 3, NULL, NULL);
} else {
// True
iAppendToTemplate(builder, ".T.", 3, NULL, NULL);
}
stv = (SSaveToVar*)(start + sizeof(SSaveToVarLogical));
} else if (stv->type == 'd' || stv->type == 'D') {
// Date
stvd = (SSaveToVarDate*)start;
// REMEMBER need to decode the date structure
// Indicate where the next one will go after this entry
stv = (SSaveToVar*)(start + sizeof(SSaveToVarDate));
} else if (stv->type == 't' || stv->type == 'T') {
// Datetime
stvt = (SSaveToVarDatetime*)start;
// REMEMBER need to decode the date structure
// Indicate where the next one will go after this entry
stv = (SSaveToVar*)(start + sizeof(SSaveToVarDatetime));
} else {
// Unknown type
iAppendToTemplate(builder, "Unknown", 1, NULL, NULL);
// Indicate where the next one will go after this entry
stv = (SSaveToVar*)start;
}
//////////
// All done, indicate where the new pointer will be
//////
return(stv);
}