Add support for excel sheets with subrows
This also fixes strings not loading past another string column
This commit is contained in:
parent
cd6033a77b
commit
fa8fa9093a
3 changed files with 108 additions and 88 deletions
|
@ -8,7 +8,7 @@
|
||||||
// taken from https://xiv.dev/game-data/file-formats/excel
|
// taken from https://xiv.dev/game-data/file-formats/excel
|
||||||
struct ExhHeader {
|
struct ExhHeader {
|
||||||
char magic[0x4];
|
char magic[0x4];
|
||||||
uint16_t unknown;
|
uint16_t version;
|
||||||
uint16_t dataOffset;
|
uint16_t dataOffset;
|
||||||
uint16_t columnCount;
|
uint16_t columnCount;
|
||||||
uint16_t pageCount;
|
uint16_t pageCount;
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
|
||||||
struct ExcelDataHeader {
|
struct ExcelDataHeader {
|
||||||
char magic[0x4];
|
char magic[4];
|
||||||
uint16_t version;
|
uint16_t version;
|
||||||
uint16_t unknown1;
|
uint16_t unknown1;
|
||||||
uint32_t indexSize;
|
uint32_t indexSize;
|
||||||
uint32_t unknown2[5];
|
uint16_t unknown2[10];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ExcelDataOffset {
|
struct ExcelDataOffset {
|
||||||
|
@ -63,7 +63,7 @@ EXD readEXD(EXH& exh, std::string_view path, ExcelDataPagination& page) {
|
||||||
endianSwap(&header.indexSize);
|
endianSwap(&header.indexSize);
|
||||||
|
|
||||||
std::vector<ExcelDataOffset> dataOffsets;
|
std::vector<ExcelDataOffset> dataOffsets;
|
||||||
dataOffsets.resize(header.indexSize / sizeof( ExcelDataOffset ));
|
dataOffsets.resize(header.indexSize / sizeof(ExcelDataOffset));
|
||||||
|
|
||||||
fread(dataOffsets.data(), sizeof(ExcelDataOffset) * dataOffsets.size(), 1, file);
|
fread(dataOffsets.data(), sizeof(ExcelDataOffset) * dataOffsets.size(), 1, file);
|
||||||
|
|
||||||
|
@ -72,106 +72,124 @@ EXD readEXD(EXH& exh, std::string_view path, ExcelDataPagination& page) {
|
||||||
endianSwap(&offset.rowId);
|
endianSwap(&offset.rowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& offset : dataOffsets) {
|
for(int i = 0; i < exh.header.rowCount; i++) {
|
||||||
Row row;
|
for(auto& offset : dataOffsets) {
|
||||||
|
if (offset.rowId == i) {
|
||||||
|
fseek(file, offset.offset, SEEK_SET);
|
||||||
|
|
||||||
fseek(file, exh.header.dataOffset + offset.offset, SEEK_SET);
|
ExcelDataRowHeader rowHeader;
|
||||||
|
fread(&rowHeader, sizeof(ExcelDataRowHeader), 1, file);
|
||||||
|
|
||||||
ExcelDataRowHeader rowHeader;
|
endianSwap(&rowHeader.dataSize);
|
||||||
fread(&rowHeader, sizeof(ExcelDataRowHeader), 1, file);
|
endianSwap(&rowHeader.rowCount);
|
||||||
|
|
||||||
endianSwap(&rowHeader.dataSize);
|
const int headerOffset = offset.offset + 6;
|
||||||
endianSwap(&rowHeader.rowCount);
|
|
||||||
|
|
||||||
const int rowOffset = offset.offset + 6;
|
const auto readRow = [&exd, &exh, file, rowHeader](int offset) {
|
||||||
|
Row row;
|
||||||
|
|
||||||
for(auto column : exh.columnDefinitions) {
|
for (auto column: exh.columnDefinitions) {
|
||||||
Column c;
|
Column c;
|
||||||
|
|
||||||
switch(column.type) {
|
switch (column.type) {
|
||||||
case String:
|
case String: {
|
||||||
{
|
fseek(file, offset + column.offset, SEEK_SET);
|
||||||
fseek(file, rowOffset + column.offset, SEEK_SET);
|
|
||||||
|
|
||||||
uint32_t stringLength;
|
uint32_t stringLength = 0; // this is actually offset?
|
||||||
fread(&stringLength, sizeof(uint32_t), 1, file);
|
fread(&stringLength, sizeof(uint32_t), 1, file);
|
||||||
|
|
||||||
fseek(file, rowOffset + exh.header.dataOffset + stringLength, SEEK_SET);
|
endianSwap(&stringLength);
|
||||||
|
|
||||||
std::string string;
|
fseek(file, offset + exh.header.dataOffset + stringLength, SEEK_SET);
|
||||||
|
|
||||||
int ch;
|
std::string string;
|
||||||
while ((ch = fgetc(file)) != '\0' && ch != '\377') {
|
|
||||||
string.push_back((char)ch);
|
uint8_t byte;
|
||||||
|
fread(&byte, sizeof(uint8_t), 1, file);
|
||||||
|
while(byte != 0) {
|
||||||
|
string.push_back(byte);
|
||||||
|
fread(&byte, sizeof(uint8_t), 1, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print("read {}\n", string);
|
||||||
|
|
||||||
|
c.data = string;
|
||||||
|
c.type = "String";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Int8:
|
||||||
|
c.data = readData<int8_t>(file, offset + column.offset);
|
||||||
|
c.type = "Int";
|
||||||
|
break;
|
||||||
|
case UInt8:
|
||||||
|
c.data = readData<uint8_t>(file, offset + column.offset);
|
||||||
|
c.type = "Unsigned Int";
|
||||||
|
break;
|
||||||
|
case Int16:
|
||||||
|
c.data = readData<int16_t>(file, offset + column.offset);
|
||||||
|
c.type = "Int";
|
||||||
|
break;
|
||||||
|
case UInt16:
|
||||||
|
c.data = readData<uint16_t>(file, offset + column.offset);
|
||||||
|
c.type = "Unsigned Int";
|
||||||
|
break;
|
||||||
|
case Int32:
|
||||||
|
c.data = readData<int32_t>(file, offset + column.offset);
|
||||||
|
c.type = "Int";
|
||||||
|
break;
|
||||||
|
case UInt32:
|
||||||
|
c.data = readData<uint32_t>(file, offset + column.offset);
|
||||||
|
c.type = "Unsigned Int";
|
||||||
|
break;
|
||||||
|
case Float32:
|
||||||
|
c.data = readData<float>(file, offset + column.offset);
|
||||||
|
c.type = "Float";
|
||||||
|
break;
|
||||||
|
case Int64:
|
||||||
|
c.data = readData<int64_t>(file, offset + column.offset);
|
||||||
|
c.type = "Int";
|
||||||
|
break;
|
||||||
|
case UInt64:
|
||||||
|
c.data = readData<uint64_t>(file, offset + column.offset);
|
||||||
|
c.type = "Unsigned Int";
|
||||||
|
break;
|
||||||
|
case PackedBool0:
|
||||||
|
case PackedBool1:
|
||||||
|
case PackedBool2:
|
||||||
|
case PackedBool3:
|
||||||
|
case PackedBool4:
|
||||||
|
case PackedBool5:
|
||||||
|
case PackedBool6:
|
||||||
|
case PackedBool7: {
|
||||||
|
int shift = (int) column.type - (int) PackedBool0;
|
||||||
|
int bit = 1 << shift;
|
||||||
|
int32_t data = readDataRaw<int32_t>(file, offset + column.offset);
|
||||||
|
c.data = std::to_string((data & bit) == bit);
|
||||||
|
c.type = "Boolean";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
c.data = "undefined";
|
||||||
|
c.type = "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
row.data.push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::print("{}\n", string.data());
|
exd.rows.push_back(row);
|
||||||
|
};
|
||||||
|
|
||||||
c.data = string;
|
if (rowHeader.rowCount > 1) {
|
||||||
c.type = "String";
|
for (int i = 0; i < rowHeader.rowCount; i++) {
|
||||||
|
int subrowOffset = headerOffset + (i * exh.header.dataOffset + 2 * (i + 1));
|
||||||
|
readRow(subrowOffset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
readRow(headerOffset);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case Int8:
|
|
||||||
c.data = readData<int8_t>(file, rowOffset + column.offset);
|
|
||||||
c.type = "Int";
|
|
||||||
break;
|
|
||||||
case UInt8:
|
|
||||||
c.data = readData<uint8_t>(file, rowOffset + column.offset);
|
|
||||||
c.type = "Unsigned Int";
|
|
||||||
break;
|
|
||||||
case Int16:
|
|
||||||
c.data = readData<int16_t>(file, rowOffset + column.offset);
|
|
||||||
c.type = "Int";
|
|
||||||
break;
|
|
||||||
case UInt16:
|
|
||||||
c.data = readData<uint16_t>(file, rowOffset + column.offset);
|
|
||||||
c.type = "Unsigned Int";
|
|
||||||
break;
|
|
||||||
case Int32:
|
|
||||||
c.data = readData<int32_t>(file, rowOffset + column.offset);
|
|
||||||
c.type = "Int";
|
|
||||||
break;
|
|
||||||
case UInt32:
|
|
||||||
c.data = readData<uint32_t>(file, rowOffset + column.offset);
|
|
||||||
c.type = "Unsigned Int";
|
|
||||||
break;
|
|
||||||
case Float32:
|
|
||||||
c.data = readData<float>(file, rowOffset + column.offset);
|
|
||||||
c.type = "Float";
|
|
||||||
break;
|
|
||||||
case Int64:
|
|
||||||
c.data = readData<int64_t>(file, rowOffset + column.offset);
|
|
||||||
c.type = "Int";
|
|
||||||
break;
|
|
||||||
case UInt64:
|
|
||||||
c.data = readData<uint64_t>(file, rowOffset + column.offset);
|
|
||||||
c.type = "Unsigned Int";
|
|
||||||
break;
|
|
||||||
case PackedBool0:
|
|
||||||
case PackedBool1:
|
|
||||||
case PackedBool2:
|
|
||||||
case PackedBool3:
|
|
||||||
case PackedBool4:
|
|
||||||
case PackedBool5:
|
|
||||||
case PackedBool6:
|
|
||||||
case PackedBool7: {
|
|
||||||
int shift = (int)column.type - (int)PackedBool0;
|
|
||||||
int bit = 1 << shift;
|
|
||||||
int32_t data = readDataRaw<int32_t>(file, rowOffset + column.offset);
|
|
||||||
c.data = std::to_string((data & bit) == bit);
|
|
||||||
c.type = "Boolean";
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
c.data = "undefined";
|
|
||||||
c.type = "Unknown";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
row.data.push_back(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exd.rows.push_back(row);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return exd;
|
return exd;
|
||||||
|
|
|
@ -17,6 +17,8 @@ EXH readEXH(const std::string_view path) {
|
||||||
|
|
||||||
fread(&exh.header, sizeof(ExhHeader), 1, file);
|
fread(&exh.header, sizeof(ExhHeader), 1, file);
|
||||||
|
|
||||||
|
fseek(file, 0x20, SEEK_SET);
|
||||||
|
|
||||||
endianSwap(&exh.header.dataOffset);
|
endianSwap(&exh.header.dataOffset);
|
||||||
endianSwap(&exh.header.columnCount);
|
endianSwap(&exh.header.columnCount);
|
||||||
endianSwap(&exh.header.pageCount);
|
endianSwap(&exh.header.pageCount);
|
||||||
|
|
Reference in a new issue