Move chunk parsing into PRS1DataChunk class.

The diff looks messy, but it's mostly chunk -> this search-and-replace.
This commit is contained in:
sawinglogz 2019-05-14 22:49:41 -04:00
parent 7103650023
commit 451963de25
2 changed files with 107 additions and 81 deletions

View File

@ -3384,10 +3384,11 @@ QList<PRS1DataChunk *> PRS1Loader::ParseFile(const QString & path)
int firstsession = 0; int firstsession = 0;
do { do {
chunk = ParseChunk(f, cnt); chunk = PRS1DataChunk::ParseNext(f);
if (chunk == nullptr) { if (chunk == nullptr) {
break; break;
} }
chunk->SetIndex(cnt); // for logging/debugging purposes
if (lastchunk != nullptr) { if (lastchunk != nullptr) {
// If there's any mismatch between header information, try and skip the block // If there's any mismatch between header information, try and skip the block
@ -3436,44 +3437,63 @@ QList<PRS1DataChunk *> PRS1Loader::ParseFile(const QString & path)
} }
PRS1DataChunk* PRS1Loader::ParseChunk(QFile & f, int cnt) PRS1DataChunk::PRS1DataChunk(QFile & f)
{
m_path = QFileInfo(f).canonicalFilePath();
}
PRS1DataChunk* PRS1DataChunk::ParseNext(QFile & f)
{ {
PRS1DataChunk* out_chunk = nullptr; PRS1DataChunk* out_chunk = nullptr;
PRS1DataChunk* chunk = new PRS1DataChunk(f);
PRS1DataChunk* chunk = new PRS1DataChunk();
chunk->m_path = QFileInfo(f).canonicalFilePath();
chunk->m_filepos = f.pos();
chunk->m_index = cnt;
do { do {
chunk->m_header = f.read(15); bool ok = chunk->ReadHeader(f);
if (chunk->m_header.size() != 15) { if (!ok) break;
qWarning() << chunk->m_path << "file too short?";
// Only return the chunk if it has passed all tests above.
out_chunk = chunk;
} while (false);
if (out_chunk == nullptr) delete chunk;
return out_chunk;
}
bool PRS1DataChunk::ReadHeader(QFile & f)
{
bool ok = false;
do {
this->m_filepos = f.pos();
this->m_header = f.read(15);
if (this->m_header.size() != 15) {
qWarning() << this->m_path << "file too short?";
break; break;
} }
unsigned char * header = (unsigned char *)chunk->m_header.data(); unsigned char * header = (unsigned char *)this->m_header.data();
chunk->fileVersion = header[0]; // Correlates to DataFileVersion in PROP[erties].TXT, only 2 or 3 has ever been observed this->fileVersion = header[0]; // Correlates to DataFileVersion in PROP[erties].TXT, only 2 or 3 has ever been observed
chunk->blockSize = (header[2] << 8) | header[1]; this->blockSize = (header[2] << 8) | header[1];
chunk->htype = header[3]; // 00 = normal, 01=waveform this->htype = header[3]; // 00 = normal, 01=waveform
chunk->family = header[4]; this->family = header[4];
chunk->familyVersion = header[5]; this->familyVersion = header[5];
chunk->ext = header[6]; this->ext = header[6];
chunk->sessionid = (header[10] << 24) | (header[9] << 16) | (header[8] << 8) | header[7]; this->sessionid = (header[10] << 24) | (header[9] << 16) | (header[8] << 8) | header[7];
chunk->timestamp = (header[14] << 24) | (header[13] << 16) | (header[12] << 8) | header[11]; this->timestamp = (header[14] << 24) | (header[13] << 16) | (header[12] << 8) | header[11];
if (chunk->blockSize == 0) { if (this->blockSize == 0) {
qWarning() << chunk->m_path << "blocksize 0?"; qWarning() << this->m_path << "blocksize 0?";
break; break;
} }
if (chunk->fileVersion < 2 || chunk->fileVersion > 3) { if (this->fileVersion < 2 || this->fileVersion > 3) {
qWarning() << chunk->m_path << "Never seen PRS1 header version < 2 or > 3 before"; qWarning() << this->m_path << "Never seen PRS1 header version < 2 or > 3 before";
break; break;
} }
bool hasHeaderDataBlock = (chunk->fileVersion == 3); bool hasHeaderDataBlock = (this->fileVersion == 3);
if (chunk->ext < 5) { // Not a waveform chunk if (this->ext < 5) { // Not a waveform chunk
QByteArray headerB2; QByteArray headerB2;
// Check if this is a newer machine with a header data block // Check if this is a newer machine with a header data block
@ -3483,85 +3503,85 @@ PRS1DataChunk* PRS1Loader::ParseChunk(QFile & f, int cnt)
// followed by variable, data byte pairs // followed by variable, data byte pairs
QByteArray extra = f.read(1); QByteArray extra = f.read(1);
if (extra.size() < 1) { if (extra.size() < 1) {
qWarning() << chunk->m_path << "read error extended header"; qWarning() << this->m_path << "read error extended header";
break; break;
} }
chunk->m_header.append(extra); this->m_header.append(extra);
header = (unsigned char *)chunk->m_header.data(); header = (unsigned char *)this->m_header.data();
int hdb_len = header[15]; int hdb_len = header[15];
int hdb_size = hdb_len * 2; int hdb_size = hdb_len * 2;
headerB2 = f.read(hdb_size); headerB2 = f.read(hdb_size);
if (headerB2.size() != hdb_size) { if (headerB2.size() != hdb_size) {
qWarning() << chunk->m_path << "read error in extended header"; qWarning() << this->m_path << "read error in extended header";
break; break;
} }
chunk->m_header.append(headerB2); this->m_header.append(headerB2);
header = (unsigned char *)chunk->m_header.data(); header = (unsigned char *)this->m_header.data();
const unsigned char * hd = (unsigned char *)headerB2.constData(); const unsigned char * hd = (unsigned char *)headerB2.constData();
int pos = 0; int pos = 0;
int recs = header[15]; int recs = header[15];
for (int i=0; i<recs; i++) { for (int i=0; i<recs; i++) {
chunk->hblock[hd[pos]] = hd[pos+1]; this->hblock[hd[pos]] = hd[pos+1];
pos += 2; pos += 2;
} }
} else { } else {
headerB2 = QByteArray(); headerB2 = QByteArray();
} }
chunk->m_headerblock = headerB2; this->m_headerblock = headerB2;
} else { // Waveform Chunk } else { // Waveform Chunk
QFileInfo fi(f); QFileInfo fi(f);
bool ok; bool ok;
int sessionid_base = (chunk->fileVersion == 2 ? 10 : 16); int sessionid_base = (this->fileVersion == 2 ? 10 : 16);
QString session_s = fi.fileName().section(".", 0, -2); QString session_s = fi.fileName().section(".", 0, -2);
quint32 sid = session_s.toInt(&ok, sessionid_base); quint32 sid = session_s.toInt(&ok, sessionid_base);
if (!ok || sid != chunk->sessionid) { if (!ok || sid != this->sessionid) {
qDebug() << chunk->m_path << chunk->sessionid; // log mismatched waveforum session IDs qDebug() << this->m_path << this->sessionid; // log mismatched waveforum session IDs
} }
QByteArray extra = f.read(5); QByteArray extra = f.read(5);
if (extra.size() != 5) { if (extra.size() != 5) {
qWarning() << chunk->m_path << "read error in waveform header"; qWarning() << this->m_path << "read error in waveform header";
break; break;
} }
chunk->m_header.append(extra); this->m_header.append(extra);
// Get the header address again to be safe // Get the header address again to be safe
header = (unsigned char *)chunk->m_header.data(); header = (unsigned char *)this->m_header.data();
chunk->duration = header[0x0f] | header[0x10] << 8; this->duration = header[0x0f] | header[0x10] << 8;
int always_1 = header[0x11]; int always_1 = header[0x11];
if (always_1 != 1) { if (always_1 != 1) {
qWarning() << chunk->m_path << always_1 << "!= 1"; qWarning() << this->m_path << always_1 << "!= 1";
//break; // don't break to avoid changing behavior (for now) //break; // don't break to avoid changing behavior (for now)
} }
quint16 wvfm_signals = header[0x12] | header[0x13] << 8; quint16 wvfm_signals = header[0x12] | header[0x13] << 8;
int ws_size = (chunk->fileVersion == 3) ? 4 : 3; int ws_size = (this->fileVersion == 3) ? 4 : 3;
int sbsize = wvfm_signals * ws_size; int sbsize = wvfm_signals * ws_size;
extra = f.read(sbsize); extra = f.read(sbsize);
if (extra.size() != sbsize) { if (extra.size() != sbsize) {
qWarning() << chunk->m_path << "read error in waveform header 2"; qWarning() << this->m_path << "read error in waveform header 2";
break; break;
} }
chunk->m_header.append(extra); this->m_header.append(extra);
header = (unsigned char *)chunk->m_header.data(); header = (unsigned char *)this->m_header.data();
// Read the waveform information in reverse. // TODO: Double-check this, always seems to be flow then pressure. // Read the waveform information in reverse. // TODO: Double-check this, always seems to be flow then pressure.
int pos = 0x14 + (wvfm_signals - 1) * ws_size; int pos = 0x14 + (wvfm_signals - 1) * ws_size;
for (int i = 0; i < wvfm_signals; ++i) { for (int i = 0; i < wvfm_signals; ++i) {
quint16 interleave = header[pos] | header[pos + 1] << 8; // samples per block (Usually 05 00) quint16 interleave = header[pos] | header[pos + 1] << 8; // samples per block (Usually 05 00)
if (chunk->fileVersion == 2) { if (this->fileVersion == 2) {
quint8 sample_format = header[pos + 2]; // TODO: sample_format seems to be unused anywhere else in the loader. quint8 sample_format = header[pos + 2]; // TODO: sample_format seems to be unused anywhere else in the loader.
chunk->waveformInfo.push_back(PRS1Waveform(interleave, sample_format)); this->waveformInfo.push_back(PRS1Waveform(interleave, sample_format));
pos -= 3; pos -= 3;
} else if (chunk->fileVersion == 3) { } else if (this->fileVersion == 3) {
//quint16 sample_size = header[pos + 2] | header[pos + 3] << 8; // size in bits?? (08 00) //quint16 sample_size = header[pos + 2] | header[pos + 3] << 8; // size in bits?? (08 00)
// Possibly this is size in bits, and sign bit for the other byte? // Possibly this is size in bits, and sign bit for the other byte?
chunk->waveformInfo.push_back(PRS1Waveform(interleave, 0)); this->waveformInfo.push_back(PRS1Waveform(interleave, 0));
pos -= 4; pos -= 4;
} }
} }
@ -3570,64 +3590,63 @@ PRS1DataChunk* PRS1Loader::ParseChunk(QFile & f, int cnt)
// The 8bit checksum comes at the end. // The 8bit checksum comes at the end.
QByteArray checksum = f.read(1); QByteArray checksum = f.read(1);
if (checksum.size() < 1) { if (checksum.size() < 1) {
qWarning() << chunk->m_path << "read error header checksum"; qWarning() << this->m_path << "read error header checksum";
break; break;
} }
chunk->storedChecksum = checksum.data()[0]; this->storedChecksum = checksum.data()[0];
// Calculate 8bit additive header checksum. // Calculate 8bit additive header checksum.
header = (unsigned char *)chunk->m_header.data(); // important because its memory location could move header = (unsigned char *)this->m_header.data(); // important because its memory location could move
int header_size = chunk->m_header.size(); int header_size = this->m_header.size();
quint8 achk=0; quint8 achk=0;
for (int i=0; i < header_size; i++) { for (int i=0; i < header_size; i++) {
achk += header[i]; achk += header[i];
} }
chunk->calcChecksum = achk; this->calcChecksum = achk;
// Append the stored checksum to the raw data *after* calculating the checksum on the preceding data. // Append the stored checksum to the raw data *after* calculating the checksum on the preceding data.
chunk->m_header.append(checksum); this->m_header.append(checksum);
// Make sure the calculated checksum matches the stored checksum. // Make sure the calculated checksum matches the stored checksum.
if (chunk->calcChecksum != chunk->storedChecksum) { // Header checksum mismatch? if (this->calcChecksum != this->storedChecksum) { // Header checksum mismatch?
qWarning() << chunk->m_path << "header checksum calc" << chunk->calcChecksum << "!= stored" << chunk->storedChecksum; qWarning() << this->m_path << "header checksum calc" << this->calcChecksum << "!= stored" << this->storedChecksum;
break; break;
} }
// Read data block // Read data block
int data_size = chunk->blockSize - chunk->m_header.size(); int data_size = this->blockSize - this->m_header.size();
chunk->m_data = f.read(data_size); this->m_data = f.read(data_size);
if (chunk->m_data.size() < data_size) { if (this->m_data.size() < data_size) {
qWarning() << chunk->m_path << "less data in file than specified in header"; qWarning() << this->m_path << "less data in file than specified in header";
break; break;
} }
if (chunk->fileVersion==3) { if (this->fileVersion==3) {
//int ds = chunk->m_data.size(); //int ds = this->m_data.size();
//quint32 crc16 = chunk->m_data.at(ds-2) | chunk->m_data.at(ds-1) << 8; //quint32 crc16 = this->m_data.at(ds-2) | this->m_data.at(ds-1) << 8;
chunk->m_data.chop(4); this->m_data.chop(4);
} else { } else {
// last two bytes contain crc16 checksum. // last two bytes contain crc16 checksum.
int ds = chunk->m_data.size(); int ds = this->m_data.size();
quint16 crc16 = chunk->m_data.at(ds-2) | chunk->m_data.at(ds-1) << 8; quint16 crc16 = this->m_data.at(ds-2) | this->m_data.at(ds-1) << 8;
chunk->m_data.chop(2); this->m_data.chop(2);
#ifdef PRS1_CRC_CHECK #ifdef PRS1_CRC_CHECK
// This fails.. it needs to include the header! // This fails.. it needs to include the header!
quint16 calc16 = CRC16((unsigned char *)chunk->m_data.data(), chunk->m_data.size()); quint16 calc16 = CRC16((unsigned char *)this->m_data.data(), this->m_data.size());
if (calc16 != crc16) { if (calc16 != crc16) {
// corrupt data block.. bleh.. // corrupt data block.. bleh..
// qDebug() << "CRC16 doesn't match for chunk" << chunk->sessionid << "for" << path; // qDebug() << "CRC16 doesn't match for chunk" << this->sessionid << "for" << path;
} }
#endif #endif
} }
// Only return the chunk if it has passed all tests above. ok = true;
out_chunk = chunk;
} while (false); } while (false);
if (out_chunk == nullptr) delete chunk; return ok;
return out_chunk;
} }
void InitModelMap() void InitModelMap()
{ {
ModelMap[0x34] = QObject::tr("RemStar Pro with C-Flex+"); // 450/460P ModelMap[0x34] = QObject::tr("RemStar Pro with C-Flex+"); // 450/460P

View File

@ -62,15 +62,19 @@ class PRS1DataChunk
friend class PRS1DataGroup; friend class PRS1DataGroup;
public: public:
PRS1DataChunk() { PRS1DataChunk() {
timestamp = 0; fileVersion = 0;
blockSize = 0;
ext = 255; ext = 255;
sessionid = 0;
htype = 0; htype = 0;
family = 0; family = 0;
familyVersion = 0; familyVersion = 0;
timestamp = 0;
sessionid = 0;
duration = 0; duration = 0;
} }
PRS1DataChunk(class QFile & f);
~PRS1DataChunk() { ~PRS1DataChunk() {
} }
inline int size() const { return m_data.size(); } inline int size() const { return m_data.size(); }
@ -82,8 +86,7 @@ public:
QString m_path; QString m_path;
qint64 m_filepos; // file offset qint64 m_filepos; // file offset
int m_index; // nth chunk in file int m_index; // nth chunk in file
inline void SetIndex(int index) { m_index = index; }
SessionID sessionid;
quint8 fileVersion; quint8 fileVersion;
quint16 blockSize; quint16 blockSize;
@ -92,6 +95,7 @@ public:
quint8 family; quint8 family;
quint8 familyVersion; quint8 familyVersion;
quint32 timestamp; quint32 timestamp;
SessionID sessionid;
quint16 duration; quint16 duration;
@ -100,6 +104,12 @@ public:
quint8 storedChecksum; // header checksum stored in file, last byte of m_header quint8 storedChecksum; // header checksum stored in file, last byte of m_header
quint8 calcChecksum; // header checksum as calculated when parsing quint8 calcChecksum; // header checksum as calculated when parsing
//! \brief Parse and return the next chunk from a PRS1 file
static PRS1DataChunk* ParseNext(class QFile & f);
//! \brief Read and parse the next chunk header from a PRS1 file
bool ReadHeader(class QFile & f);
}; };
class PRS1Loader; class PRS1Loader;
@ -235,9 +245,6 @@ class PRS1Loader : public CPAPLoader
//! \brief Parse a PRS1 summary/event/waveform file and break into invidivual session or waveform chunks //! \brief Parse a PRS1 summary/event/waveform file and break into invidivual session or waveform chunks
QList<PRS1DataChunk *> ParseFile(const QString & path); QList<PRS1DataChunk *> ParseFile(const QString & path);
//! \brief Parse and return the next chunk from a PRS1 file
PRS1DataChunk* ParseChunk(class QFile & f, int index=0);
//! \brief Register this Module to the list of Loaders, so it knows to search for PRS1 data. //! \brief Register this Module to the list of Loaders, so it knows to search for PRS1 data.
static void Register(); static void Register();