328 lines
9.0 KiB
C++
328 lines
9.0 KiB
C++
//
|
|
// This file is part of sffview, a program to view structured fax files (sff)
|
|
//
|
|
// Copyright (C) 1998-2012 Peter Schaefer-Hutter and contributors ("THE AUTHORS")
|
|
//
|
|
// Permission to use, copy, modify, distribute, and sell this software and
|
|
// its documentation for any purpose is hereby granted without fee.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
|
|
// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
|
//
|
|
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
|
|
// INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY
|
|
// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
// WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY
|
|
// THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE
|
|
// OR PERFORMANCE OF THIS SOFTWARE.
|
|
//
|
|
// Contributor(s):
|
|
// None
|
|
//
|
|
// You can contact the original author by email at peter.schaefer@gmx.de.
|
|
//
|
|
// I'm always pleased to hear that somebody is actually using my software.
|
|
// If you can manage it, e-mail me a quick notice. Thanks!
|
|
//
|
|
/*-RCS-Info----------------------------------------------------
|
|
|
|
$Id: sfffile.cpp,v 1.4 2008/06/29 09:30:48 pschaefer Exp $
|
|
|
|
---RCS-Info--------------------------------------------------*/
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <vector>
|
|
#include <iostream>
|
|
|
|
#include "sfftypes.h"
|
|
#include "common.h"
|
|
#include "codes.h"
|
|
#include "decoder.h"
|
|
#include "sfffile.h"
|
|
|
|
using namespace std;
|
|
|
|
//-Constants-------------------------------------------------------
|
|
|
|
sff_byte CSffFile::m_SFFID[4] = { 0x53, 0x66, 0x66, 0x66 };
|
|
|
|
//-Types-----------------------------------------------------------
|
|
|
|
typedef enum {
|
|
NEED_MAGIC,
|
|
NEED_PAGESTART,
|
|
NEED_PAGEHEADER,
|
|
NEED_RECORD,
|
|
LAST_PAGE
|
|
} TScannerState;
|
|
|
|
//-----------------------------------------------------------------
|
|
|
|
CSffFile::CSffFile(const std::string& strFileName) :
|
|
CFile(strFileName)
|
|
{
|
|
ScanFile();
|
|
}
|
|
|
|
CSffFile::~CSffFile()
|
|
{
|
|
PAGEVECTOR::iterator it = m_acPages.begin();
|
|
for (; it != m_acPages.end(); it++) {
|
|
delete (*it);
|
|
}
|
|
}
|
|
|
|
void CSffFile::ScanFile()
|
|
{
|
|
TSFFFileHeader dh;
|
|
TSFFPageHeader ph;
|
|
sff_byte b1 = 0, b2 = 0;
|
|
sff_word w;
|
|
int nLineCount = 0;
|
|
int fuzz = 0;
|
|
TSFFPage *pPage;
|
|
|
|
if (Eof())
|
|
return;
|
|
|
|
TScannerState state = NEED_MAGIC;
|
|
do {
|
|
switch (state) {
|
|
case NEED_MAGIC :
|
|
for (fuzz = 0; fuzz < 2048; ++fuzz) {
|
|
Seek(fuzz, CFile::sk_from_start);
|
|
Read(&dh, sizeof(dh));
|
|
if (Eof())
|
|
throw CSimpleException(CSimpleException::err_invalidfile);
|
|
if (::memcmp(&dh.sff_id, &m_SFFID, sizeof(m_SFFID)) == 0)
|
|
break;
|
|
}
|
|
if (::memcmp(&dh.sff_id, &m_SFFID, sizeof(m_SFFID)) != 0)
|
|
throw CSimpleException(CSimpleException::err_invalidfile);
|
|
if (dh.version > 1)
|
|
throw CSimpleException(CSimpleException::err_invalidversion);
|
|
if (fuzz>0) {
|
|
dh.first_page += fuzz;
|
|
}
|
|
Seek(dh.first_page, CFile::sk_from_start);
|
|
state = NEED_PAGESTART;
|
|
break;
|
|
case NEED_PAGESTART :
|
|
b1 = GetC(); // Recordheader (0xFE fuer Seitenanfang)
|
|
if (Eof())
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
if (b1 != 0xFE)
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
b1 = GetC(); // Recordlaenge (normalerweise 0x10)
|
|
if (b1 == 0)
|
|
state = LAST_PAGE;
|
|
else
|
|
state = NEED_PAGEHEADER;
|
|
break;
|
|
case NEED_PAGEHEADER :
|
|
Read(&ph, sizeof(TSFFPageHeader));
|
|
if (Eof())
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
if (ph.coding > 0)
|
|
throw CSimpleException(CSimpleException::err_unknowncoding);
|
|
Seek(b1 - sizeof(TSFFPageHeader), CFile::sk_current); // skip user data
|
|
pPage = new TSFFPage;
|
|
pPage->filepos = Tell();
|
|
pPage->width = ph.linelen;
|
|
pPage->height = ph.pagelen;
|
|
// Values 254/255 are known for RVS COM
|
|
pPage->dpi = (ph.horiz_res == 0) ? 203 :
|
|
(ph.horiz_res == 255) ? 300 :
|
|
(ph.horiz_res == 254) ? 400 : 0;
|
|
pPage->lpi = (ph.vert_res == 0) ? 98 :
|
|
(ph.vert_res == 1) ? 196 :
|
|
(ph.vert_res == 255) ? 300 :
|
|
(ph.vert_res == 254) ? 400 : 0;
|
|
m_acPages.push_back(pPage);
|
|
state = NEED_RECORD;
|
|
nLineCount = 0;
|
|
break;
|
|
case NEED_RECORD :
|
|
b1 = GetC(); // Recordtyp einlesen
|
|
if (Eof()) {
|
|
m_acPages[GetPageCount()-1]->height = nLineCount;
|
|
state = LAST_PAGE;
|
|
} else if (b1 == 0) {
|
|
// variable Anzahl Bytes folgt
|
|
b1 = GetC(); // LSB
|
|
if (Eof())
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
b2 = GetC(); // MSB
|
|
if (Eof())
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
w = ((b2 << 8) | b1);
|
|
Seek(w, CFile::sk_current); // Skip data
|
|
if (Eof())
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
++nLineCount;
|
|
} else if (b1 < 217) {
|
|
// normale Anzahl BYTEs folgt
|
|
Seek((long)b1, CFile::sk_current); // Skip data
|
|
if (Eof()) {
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
}
|
|
++nLineCount;
|
|
} else if (b1 < 254) {
|
|
// Whiteskip
|
|
nLineCount+=(b1 - 216);
|
|
} else if (b1 < 255) {
|
|
// 254 -> Pageheader
|
|
m_acPages[GetPageCount()-1]->height = nLineCount;
|
|
nLineCount = 0;
|
|
b1 = GetC(); // Recordlaenge (normalerweise 0x10)
|
|
if (Eof())
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
if (b1 == 0) {
|
|
state = LAST_PAGE;
|
|
} else {
|
|
state = NEED_PAGEHEADER;
|
|
}
|
|
} else {
|
|
// Fehlerhafte Zeile oder Benutzerinfo
|
|
b1 = GetC(); // LSB
|
|
if (Eof())
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
if (b1 == 0) {
|
|
++nLineCount;
|
|
} else {
|
|
Seek(b1, CFile::sk_current); // Skip user info
|
|
if (Eof())
|
|
throw CSimpleException(CSimpleException::err_corruptfile);
|
|
}
|
|
}
|
|
break;
|
|
case LAST_PAGE :
|
|
break;
|
|
}
|
|
} while(state != LAST_PAGE);
|
|
return;
|
|
}
|
|
|
|
bool CSffFile::PageIsValid(int nPage)
|
|
{
|
|
return ((nPage >= 0) && (nPage < GetPageCount()));
|
|
}
|
|
|
|
bool CSffFile::SeekPage(int nPage)
|
|
{
|
|
if (!PageIsValid(nPage))
|
|
return false;
|
|
Seek(m_acPages[nPage]->filepos, CFile::sk_from_start);
|
|
return true;
|
|
}
|
|
|
|
TSFFPage *CSffFile::GetPage(int nPage)
|
|
{
|
|
return PageIsValid(nPage) ? m_acPages[nPage] : (TSFFPage *)NULL;
|
|
}
|
|
|
|
bool CSffFile::IsLowRes(int nPage)
|
|
{
|
|
return PageIsValid(nPage) && (m_acPages[nPage]->lpi == 98);
|
|
}
|
|
|
|
sff_word CSffFile::GetHorizontalResolution(int nPage)
|
|
{
|
|
return PageIsValid(nPage) ? m_acPages[nPage]->dpi :0;
|
|
}
|
|
|
|
sff_word CSffFile::GetVerticalResolution(int nPage)
|
|
{
|
|
return PageIsValid(nPage) ? m_acPages[nPage]->lpi :0;
|
|
}
|
|
|
|
sff_dword CSffFile::GetPageWidth(int nPage)
|
|
{
|
|
return PageIsValid(nPage) ? m_acPages[nPage]->width :0;
|
|
}
|
|
|
|
sff_dword CSffFile::GetPageHeight(int nPage)
|
|
{
|
|
return PageIsValid(nPage) ? m_acPages[nPage]->height :0;
|
|
}
|
|
|
|
bool CSffFile::GetRecord(TSFFRecord& rec)
|
|
{
|
|
sff_byte b1, b2;
|
|
sff_word w;
|
|
bool result;
|
|
|
|
if (Eof()) {
|
|
return false;
|
|
}
|
|
|
|
b1 = GetC(); // Recordtyp einlesen
|
|
if (Eof()) {
|
|
result = false;
|
|
} else if (b1 == 0) {
|
|
// variable Anzahl Bytes folgt
|
|
b1 = GetC(); // LSB
|
|
b2 = GetC(); // MSB
|
|
w = ((b2 << 8) | b1);
|
|
rec.type = NORMAL;
|
|
rec.cb = w;
|
|
rec.pData = (sff_byte *)malloc(w);
|
|
Read(rec.pData, w);
|
|
result = true;
|
|
} else if (b1 < 217) {
|
|
// normale Anzahl BYTEs folgt
|
|
rec.type = NORMAL;
|
|
rec.cb = b1;
|
|
rec.pData = (sff_byte *)malloc(b1);
|
|
Read(rec.pData, b1);
|
|
result = true;
|
|
} else if (b1 < 254) {
|
|
// Whiteskip
|
|
rec.type = WHITESKIP;
|
|
rec.cb = (b1 - 216);
|
|
rec.pData = 0;
|
|
result = true;
|
|
} else if (b1 < 255) {
|
|
// 254 -> Pageheader
|
|
result = false;
|
|
} else {
|
|
// Fehlerhafte Zeile oder Benutzerinfo
|
|
b1 = GetC(); // LSB
|
|
if (b1 == 0) {
|
|
rec.type = BADLINE;
|
|
rec.cb = 1;
|
|
rec.pData = 0;
|
|
} else {
|
|
rec.type = USERINFO;
|
|
rec.cb = b1;
|
|
rec.pData = (sff_byte *)malloc(b1);
|
|
Read(rec.pData, b1);
|
|
}
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool CSffFile::DecodeRecord(TSFFRecord& rec, IBitSink& bitsink)
|
|
{
|
|
bool rc;
|
|
|
|
if (rec.type != NORMAL)
|
|
return false;
|
|
|
|
CHuffDecoder source(rec.pData, rec.cb);
|
|
|
|
try {
|
|
rec.runlength = source.DecodeLine(bitsink);
|
|
rc = true;
|
|
}
|
|
catch(CSimpleException e)
|
|
{
|
|
cerr << "ERROR: " << e.what() << endl;
|
|
rc = false;
|
|
}
|
|
return rc;
|
|
}
|