/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */
/* libmwaw
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
* Copyright (C) 2006, 2007 Andrew Ziem
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <limits>
#include <set>
#include <sstream>
#include <librevenge/librevenge.h>
#include "MWAWTextListener.hxx"
#include "MWAWFont.hxx"
#include "MWAWGraphicStyle.hxx"
#include "MWAWGraphicShape.hxx"
#include "MWAWHeader.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPictBitmap.hxx"
#include "MWAWPictMac.hxx"
#include "MWAWPosition.hxx"
#include "MWAWPrinter.hxx"
#include "MWAWRSRCParser.hxx"
#include "MWAWStringStream.hxx"
#include "RagTime5Chart.hxx"
#include "RagTime5ClusterManager.hxx"
#include "RagTime5Graph.hxx"
#include "RagTime5Layout.hxx"
#include "RagTime5Pipeline.hxx"
#include "RagTime5StructManager.hxx"
#include "RagTime5StyleManager.hxx"
#include "RagTime5Spreadsheet.hxx"
#include "RagTime5Text.hxx"
#include "RagTime5Parser.hxx"
/** Internal: the structures of a RagTime5Parser */
namespace RagTime5ParserInternal
{
//! Internal: the helper to read doc info parse
struct DocInfoFieldParser final : public RagTime5StructManager::FieldParser {
//! constructor
explicit DocInfoFieldParser(RagTime5Parser &parser)
: RagTime5StructManager::FieldParser("DocInfo")
, m_mainParser(parser)
{
}
//! destructor
~DocInfoFieldParser() final;
//! parse a field
bool parseField(RagTime5StructManager::Field &field, RagTime5Zone &zone, int /*n*/, libmwaw::DebugStream &f) final
{
if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x1f7827) {
for (auto const &child : field.m_fieldList) {
if (child.m_type==RagTime5StructManager::Field::T_Unstructured && child.m_fileType==0x32040 && child.m_entry.valid()) {
f << child;
long actPos=zone.getInput()->tell();
m_mainParser.readDocInfoClusterData(zone, child.m_entry);
zone.getInput()->seek(actPos, librevenge::RVNG_SEEK_SET);
return true;
}
MWAW_DEBUG_MSG(("RagTime5ParserInternal::DocInfoFieldParser::parseField: find some unknown mainData block\n"));
f << "##mainData=" << child << ",";
}
}
else
f << field;
return true;
}
protected:
//! the main parser
RagTime5Parser &m_mainParser;
};
DocInfoFieldParser::~DocInfoFieldParser()
{
}
//! Internal: the helper to read index + unicode string for a RagTime5Parser
struct IndexUnicodeParser final : public RagTime5StructManager::DataParser {
//! constructor
IndexUnicodeParser(RagTime5Parser &, bool readIndex, std::string const &zoneName)
: RagTime5StructManager::DataParser(zoneName)
, m_readIndex(readIndex)
, m_idToStringMap()
{
}
//! destructor
~IndexUnicodeParser() final;
//! try to parse a data
bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int n, libmwaw::DebugStream &f) final
{
long pos=input->tell();
int id=n;
if (m_readIndex) {
if (endPos-pos<4) {
MWAW_DEBUG_MSG(("RagTime5ParserInternal::IndexUnicodeParser::parse: bad data size\n"));
return false;
}
id=static_cast<int>(input->readULong(4));
f << "id=" << id << ",";
}
librevenge::RVNGString str("");
if (endPos==input->tell())
;
else if (!RagTime5StructManager::readUnicodeString(input, endPos, str))
f << "###";
f << "\"" << str.cstr() << "\",";
m_idToStringMap[id]=str;
return true;
}
//! a flag to know if we need to read the index
bool m_readIndex;
//! the data
std::map<int, librevenge::RVNGString> m_idToStringMap;
};
IndexUnicodeParser::~IndexUnicodeParser()
{
}
//! Internal: the helper to read a clustList
struct ClustListParser final : public RagTime5StructManager::DataParser {
//! constructor
ClustListParser(RagTime5ClusterManager &clusterManager, int fieldSize, std::string const &zoneName)
: RagTime5StructManager::DataParser(zoneName)
, m_fieldSize(fieldSize)
, m_linkList()
, m_idToNameMap()
, m_clusterManager(clusterManager)
{
if (m_fieldSize<4) {
MWAW_DEBUG_MSG(("RagTime5ParserInternal::ClustListParser: bad field size\n"));
m_fieldSize=0;
}
}
//! destructor
~ClustListParser() final;
//! returns the not null list dataId list
std::vector<int> getIdList() const
{
std::vector<int> res;
for (auto const &lnk : m_linkList) {
if (lnk.m_dataId>0)
res.push_back(lnk.m_dataId);
}
return res;
}
//! return the cluster name
std::string getClusterName(int id) const
{
return m_clusterManager.getClusterName(id);
}
//! try to parse a data
bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int n, libmwaw::DebugStream &f) final
{
long pos=input->tell();
if (m_idToNameMap.find(n)!=m_idToNameMap.end())
f << m_idToNameMap.find(n)->second.cstr() << ",";
if (endPos-pos!=m_fieldSize) {
MWAW_DEBUG_MSG(("RagTime5ParserInternal::ClustListParser::parse: bad data size\n"));
return false;
}
std::vector<int> listIds;
if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
MWAW_DEBUG_MSG(("RagTime5ParserInternal::ClustListParser::parse: can not read an cluster id\n"));
f << "##clusterIds,";
return false;
}
RagTime5StructManager::ZoneLink link;
link.m_dataId=listIds[0];
if (listIds[0])
// a e,2003,200b, ... cluster
f << getClusterName(listIds[0]) << ",";
if (m_fieldSize>=10) {
link.m_subZoneId[0]=long(input->readULong(4));
link.m_subZoneId[1]=long(input->readLong(2));
}
f << link;
m_linkList.push_back(link);
return true;
}
//! the field size
int m_fieldSize;
//! the list of read cluster
std::vector<RagTime5StructManager::ZoneLink> m_linkList;
//! the name
std::map<int, librevenge::RVNGString> m_idToNameMap;
private:
//! the main zone manager
RagTime5ClusterManager &m_clusterManager;
//! copy constructor, not implemented
ClustListParser(ClustListParser &orig);
//! copy operator, not ximplemented
ClustListParser &operator=(ClustListParser &orig);
};
ClustListParser::~ClustListParser()
{
}
////////////////////////////////////////
//! Internal: the state of a RagTime5Parser
struct State {
//! constructor
State()
: m_zonesEntry()
, m_zonesList()
, m_idToTypeMap()
, m_zoneInfo()
, m_dataIdZoneMap()
, m_pageZonesIdMap()
, m_sendZoneSet()
, m_hasLayout(false)
, m_actPage(0)
, m_numPages(0)
, m_headerHeight(0)
, m_footerHeight(0)
{
}
//! the main zone entry
MWAWEntry m_zonesEntry;
//! the zone list
std::vector<std::shared_ptr<RagTime5Zone> > m_zonesList;
//! a map id to type string
std::map<int, std::string> m_idToTypeMap;
//! the zone info zone (ie. the first zone)
std::shared_ptr<RagTime5Zone> m_zoneInfo;
//! a map: data id->entry (datafork)
std::map<int, std::shared_ptr<RagTime5Zone> > m_dataIdZoneMap;
//! a map: page->main zone id
std::map<int, std::vector<int> > m_pageZonesIdMap;
//! a set used to avoid looping when sending zone
std::set<int> m_sendZoneSet;
//! a flag to know if the file has some layout
bool m_hasLayout;
int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */;
int m_headerHeight /** the header height if known */,
m_footerHeight /** the footer height if known */;
};
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
RagTime5Parser::RagTime5Parser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
: MWAWTextParser(input, rsrcParser, header), m_state()
, m_chartParser()
, m_graphParser()
, m_layoutParser()
, m_pipelineParser()
, m_spreadsheetParser()
, m_textParser()
, m_clusterManager()
, m_structManager()
, m_styleManager()
{
init();
}
RagTime5Parser::~RagTime5Parser()
{
}
void RagTime5Parser::init()
{
m_structManager.reset(new RagTime5StructManager);
m_clusterManager.reset(new RagTime5ClusterManager(*this));
m_styleManager.reset(new RagTime5StyleManager(*this));
m_chartParser.reset(new RagTime5Chart(*this));
m_graphParser.reset(new RagTime5Graph(*this));
m_layoutParser.reset(new RagTime5Layout(*this));
m_pipelineParser.reset(new RagTime5Pipeline(*this));
m_spreadsheetParser.reset(new RagTime5Spreadsheet(*this));
m_textParser.reset(new RagTime5Text(*this));
resetTextListener();
setAsciiName("main-1");
m_state.reset(new RagTime5ParserInternal::State);
// reduce the margin (in case, the page is not defined)
getPageSpan().setMargins(0.1);
}
std::shared_ptr<RagTime5ClusterManager> RagTime5Parser::getClusterManager()
{
return m_clusterManager;
}
std::shared_ptr<RagTime5StructManager> RagTime5Parser::getStructManager()
{
return m_structManager;
}
std::shared_ptr<RagTime5StyleManager> RagTime5Parser::getStyleManager()
{
return m_styleManager;
}
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Parser::readChartCluster(RagTime5Zone &zone, int zoneType)
{
return m_chartParser->readChartCluster(zone, zoneType);
}
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Parser::readGraphicCluster(RagTime5Zone &zone, int zoneType)
{
return m_graphParser->readGraphicCluster(zone, zoneType);
}
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Parser::readLayoutCluster(RagTime5Zone &zone, int zoneType)
{
return m_layoutParser->readLayoutCluster(zone, zoneType);
}
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Parser::readPipelineCluster(RagTime5Zone &zone, int zoneType)
{
return m_pipelineParser->readPipelineCluster(zone, zoneType);
}
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Parser::readPictureCluster(RagTime5Zone &zone, int zoneType)
{
return m_graphParser->readPictureCluster(zone, zoneType);
}
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Parser::readSpreadsheetCluster(RagTime5Zone &zone, int zoneType)
{
return m_spreadsheetParser->readSpreadsheetCluster(zone, zoneType);
}
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Parser::readTextCluster(RagTime5Zone &zone, int zoneType)
{
return m_textParser->readTextCluster(zone, zoneType);
}
std::shared_ptr<RagTime5Zone> RagTime5Parser::getDataZone(int dataId) const
{
if (m_state->m_dataIdZoneMap.find(dataId)==m_state->m_dataIdZoneMap.end())
return std::shared_ptr<RagTime5Zone>();
return m_state->m_dataIdZoneMap.find(dataId)->second;
}
RagTime5ClusterManager::Cluster::Type RagTime5Parser::getClusterType(int zId) const
{
return m_clusterManager->getClusterType(zId);
}
RagTime5ClusterManager::Cluster::Type RagTime5Parser::getPipelineContainerType(int pipelineId) const
{
return m_pipelineParser->getContainerType(pipelineId);
}
////////////////////////////////////////////////////////////
// new page
////////////////////////////////////////////////////////////
void RagTime5Parser::newPage(int number)
{
if (number <= m_state->m_actPage || number > m_state->m_numPages)
return;
while (m_state->m_actPage < number) {
m_state->m_actPage++;
if (!getTextListener() || m_state->m_actPage == 1)
continue;
getTextListener()->insertBreak(MWAWTextListener::PageBreak);
}
}
////////////////////////////////////////////////////////////
// the parser
////////////////////////////////////////////////////////////
void RagTime5Parser::parse(librevenge::RVNGTextInterface *docInterface)
{
if (!getInput().get() || !checkHeader(nullptr)) throw(libmwaw::ParseException());
bool ok = true;
try {
// create the asciiFile
ascii().setStream(getInput());
ascii().open(asciiName());
checkHeader(nullptr);
ok=createZones();
if (ok) {
createDocument(docInterface);
sendZones();
#ifdef DEBUG
flushExtra();
#endif
}
ascii().reset();
}
catch (...) {
MWAW_DEBUG_MSG(("RagTime5Parser::parse: exception catched when parsing\n"));
ok = false;
}
resetTextListener();
if (!ok) throw(libmwaw::ParseException());
}
////////////////////////////////////////////////////////////
// create the document
////////////////////////////////////////////////////////////
void RagTime5Parser::createDocument(librevenge::RVNGTextInterface *documentInterface)
{
if (!documentInterface) return;
if (getTextListener()) {
MWAW_DEBUG_MSG(("RagTime5Parser::createDocument: listener already exist\n"));
return;
}
// update the page
int numPages=m_layoutParser->numPages();
if (numPages<=0)
numPages=1;
else
m_state->m_hasLayout=true;
m_state->m_actPage = 0;
m_state->m_numPages=numPages;
// create the page list
MWAWPageSpan ps(getPageSpan());
std::vector<MWAWPageSpan> pageList;
ps.setPageSpan(m_state->m_numPages);
pageList.push_back(ps);
//
MWAWTextListenerPtr listen(new MWAWTextListener(*getParserState(), pageList, documentInterface));
setTextListener(listen);
listen->startDocument();
}
////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
bool RagTime5Parser::createZones()
{
int const vers=version();
if (vers<5) {
MWAW_DEBUG_MSG(("RagTime5Parser::createZones: must be called for %d document\n", vers));
return false;
}
if (!findDataZones(m_state->m_zonesEntry))
return false;
ascii().addPos(m_state->m_zonesEntry.end());
ascii().addNote("FileHeader-End");
if (m_state->m_zonesList.size()<20) {
// even an empty file seems to have almost ~80 zones, so...
MWAW_DEBUG_MSG(("RagTime5Parser::createZones: the zone list seems too short\n"));
return false;
}
// we need first to join dissociate zone and to read the type data
libmwaw::DebugStream f;
m_state->m_zoneInfo=m_state->m_zonesList[0];
for (size_t i=1; i<m_state->m_zonesList.size(); ++i) {
if (!m_state->m_zonesList[i])
continue;
auto &zone=*m_state->m_zonesList[i];
// id=0 correspond to the file header already read, so ignored it
if (zone.m_ids[0]==0 && zone.m_level==1) {
zone.m_isParsed=true;
continue;
}
if (!update(zone))
continue;
std::string what("");
if (zone.m_idsFlag[1]!=0 || (zone.m_ids[1]!=23 && zone.m_ids[1]!=24) || zone.m_ids[2]!=21 ||
!readString(zone, what) || what.empty())
continue;
if (m_state->m_idToTypeMap.find(zone.m_ids[0])!=m_state->m_idToTypeMap.end()) {
MWAW_DEBUG_MSG(("RagTime5Parser::createZones: a type with id=%d already exists\n", zone.m_ids[0]));
}
else {
m_state->m_idToTypeMap[zone.m_ids[0]]=what;
f.str("");
f << what << ",";
ascii().addPos(zone.m_defPosition);
ascii().addNote(f.str().c_str());
}
}
// first find the type of all zone and unpack the zone if needed...
for (size_t i=1; i<m_state->m_zonesList.size(); ++i) {
if (!m_state->m_zonesList[i])
continue;
auto &zone=*m_state->m_zonesList[i];
if (zone.m_isParsed)
continue;
if (zone.m_level==1) {
if (m_state->m_dataIdZoneMap.find(zone.m_ids[0])!=m_state->m_dataIdZoneMap.end()) {
MWAW_DEBUG_MSG(("RagTime5Parser::createZones: data zone with id=%d already exists\n", zone.m_ids[0]));
}
else
m_state->m_dataIdZoneMap[zone.m_ids[0]]=m_state->m_zonesList[i];
}
for (int j=1; j<3; ++j) {
if (!zone.m_ids[j]) continue;
if (m_state->m_idToTypeMap.find(zone.m_ids[j])==m_state->m_idToTypeMap.end()) {
// the main zone seems to point to a cluster id...
if (zone.m_ids[0]<=6) continue;
MWAW_DEBUG_MSG(("RagTime5Parser::createZones: can not find the type for %d:%d\n", zone.m_ids[0],j));
ascii().addPos(zone.m_defPosition);
ascii().addNote("###type,");
}
else {
zone.m_kinds[j-1]=m_state->m_idToTypeMap.find(zone.m_ids[j])->second;
f.str("");
f << zone.m_kinds[j-1] << ",";
ascii().addPos(zone.m_defPosition);
ascii().addNote(f.str().c_str());
}
}
// first unpack the packed zone
int usedId=zone.m_kinds[1].empty() ? 0 : 1;
std::string actType=zone.getKindLastPart(usedId==0);
if (actType=="Pack") {
if (zone.m_entry.valid() && !unpackZone(zone)) {
MWAW_DEBUG_MSG(("RagTime5Parser::createZones: can not unpack the zone %d\n", zone.m_ids[0]));
libmwaw::DebugFile &ascFile=zone.ascii();
f.str("");
f << "Entries(BADPACK)[" << zone << "]:###" << zone.m_kinds[usedId];
ascFile.addPos(zone.m_entry.begin());
ascFile.addNote(f.str().c_str());
continue;
}
size_t length=zone.m_kinds[usedId].size();
if (length>5)
zone.m_kinds[usedId].resize(length-5);
else
zone.m_kinds[usedId]="";
}
// check hilo
usedId=zone.m_kinds[1].empty() ? 0 : 1;
actType=zone.getKindLastPart(usedId==0);
if (actType=="HiLo" || actType=="LoHi") {
zone.m_hiLoEndian=actType=="HiLo";
size_t length=zone.m_kinds[usedId].size();
if (length>5)
zone.m_kinds[usedId].resize(length-5);
else
zone.m_kinds[usedId]="";
}
std::string kind=zone.getKindLastPart();
if (kind=="Type") {
size_t length=zone.m_kinds[0].size();
if (length>5)
zone.m_kinds[0].resize(length-5);
else
zone.m_kinds[0]="";
zone.m_extra += "type,";
}
}
if (!readZoneInfo()) return false;
// check for unread clusters
for (auto zone : m_state->m_zonesList) {
if (!zone || zone->m_isParsed || zone->getKindLastPart(zone->m_kinds[1].empty())!="Cluster")
continue;
if (zone->m_entry.valid()) {
MWAW_DEBUG_MSG(("RagTime5Parser::createZones: find unparsed cluster zone %d\n", zone->m_ids[0]));
}
readClusterZone(*zone);
}
// now read the screen rep list zone: CHECKME: can we remove this check, now ?
for (auto zone : m_state->m_zonesList) {
if (!zone || zone->m_isParsed || (!zone->m_entry.valid()&&zone->m_variableD[0]!=1) || zone->getKindLastPart(zone->m_kinds[1].empty())!="ScreenRepList")
continue;
m_graphParser->readPictureList(*zone);
}
return true;
}
bool RagTime5Parser::readZoneInfo()
{
if (!m_state->m_zoneInfo || m_state->m_zoneInfo->m_ids[0]!=1) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: can not find the zone information zone, impossible to continue\n"));
return false;
}
auto &zoneInfo=*m_state->m_zoneInfo;
zoneInfo.m_isParsed=true;
int mainClusterId=0;
for (auto it : zoneInfo.m_childIdToZoneMap) {
std::shared_ptr<RagTime5Zone> zone=it.second;
if (!zone) continue;
zone->m_isParsed=true;
switch (it.first) {
case 3: // alway with gd=[1,_]
if (zone->m_variableD[0]==1 && zone->m_variableD[1]) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: find a zone 3\n"));
ascii().addPos(zone->m_defPosition);
ascii().addNote("###");
}
break;
case 4: // list of zones limits, safe to ignore
case 5: // file limits, safe to ignore
break;
case 6: // alway with gd=[_,_]
if (zone->m_variableD[1]) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: find a zone 6\n"));
ascii().addPos(zone->m_defPosition);
ascii().addNote("###");
}
break;
case 10: { // the type zone
if (zone->m_variableD[0]!=1) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: the type zone seems bads\n"));
break;
}
auto dZone=getDataZone(zone->m_variableD[1]);
if (!dZone || !dZone->m_entry.valid()) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: can not find the type zone\n"));
break;
}
dZone->m_name=zone->getZoneName();
if (dZone->getKindLastPart()=="ItemData" && m_structManager->readTypeDefinitions(*dZone))
break;
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: unexpected list of block type\n"));
break;
}
case 11:
if (zone->m_variableD[0]!=1) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: the main cluster zone seems bads\n"));
break;
}
mainClusterId=zone->m_variableD[1];
break;
default:
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: find unknown main zone %d\n", it.first));
ascii().addPos(zone->m_defPosition);
ascii().addNote("###");
break;
}
}
if (!mainClusterId) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: can not find the cluster id try 13\n"));
mainClusterId=13;
}
// the main cluster
auto dZone=getDataZone(mainClusterId);
if (!dZone) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: can not find the main cluster zone\n"));
return true;
}
dZone->m_extra+="main,";
if (dZone->getKindLastPart(dZone->m_kinds[1].empty())!="Cluster" || !readClusterZone(*dZone, 0)) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneInfo: unexpected main cluster zone type\n"));
return true;
}
return true;
}
bool RagTime5Parser::readZoneData(RagTime5Zone &zone)
{
if (!zone.m_entry.valid()) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: can not find the entry\n"));
return false;
}
libmwaw::DebugStream f;
int usedId=zone.m_kinds[1].empty() ? 0 : 1;
std::string actType=zone.getKindLastPart(usedId==0);
std::string kind=zone.getKindLastPart();
// the "RagTime" string
if (kind=="CodeName") {
std::string what;
if (zone.m_kinds[1]!="BESoftware:7BitASCII:Type" || !readString(zone, what)) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: can not read codename for zone %d\n", zone.m_ids[0]));
zone.m_isParsed=true;
f << "Entries(CodeName)[" << zone << "]:###";
libmwaw::DebugFile &ascFile=zone.ascii();
ascFile.addPos(zone.m_entry.begin());
ascFile.addNote(f.str().c_str());
}
for (auto it : zone.m_childIdToZoneMap) {
std::shared_ptr<RagTime5Zone> child=it.second;
if (!child || child->m_isParsed) continue;
if (child->getKindLastPart()=="DocuVersion" && readDocumentVersion(*child))
continue;
if (child->getKindLastPart()=="7BitASCII") {
child->m_isParsed=true;
ascii().addPos(child->m_defPosition);
ascii().addNote("codeName[type]");
continue;
}
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: find unknown child for codename for zone %d\n", zone.m_ids[0]));
ascii().addPos(child->m_defPosition);
ascii().addNote("###unkCodeName");
}
return true;
}
//
// first test for picture data
//
// checkme: find how we can retrieve the next data without parsing unparsed data
if (kind=="ScreenRepMatchData" || kind=="ScreenRepMatchDataColor") {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: find unexpected %s for zone %d\n", kind.c_str(), zone.m_ids[0]));
return m_graphParser->readPictureMatch(zone, kind=="ScreenRepMatchDataColor");
}
if (kind=="DocuVersion") {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: find unexpected docuVersion\n"));
return readDocumentVersion(zone);
}
if (kind=="Thumbnail")
return m_graphParser->readPictureData(zone);
if (m_graphParser->readPictureData(zone)) {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: find some unparsed picture %d\n", zone.m_ids[0]));
ascii().addPos(zone.m_defPosition);
ascii().addNote("###unparsed");
return true;
}
if (kind=="ScriptComment" || kind=="ScriptName") {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: find unexpected %s\n", kind.c_str()));
return readScriptComment(zone);
}
std::string name("");
if (kind=="OSAScript" || kind=="TCubics") {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: find unexpected %s\n", kind.c_str()));
name=kind;
}
else if (kind=="ItemData" || kind=="Unicode") {
actType=zone.getKindLastPart(zone.m_kinds[1].empty());
if (actType=="Unicode" || kind=="Unicode") {
// hilo/lohi is not always set, so this can cause problem....
if (readUnicodeString(zone))
return true;
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: can not read a unicode zone %d\n", zone.m_ids[0]));
f << "Entries(StringUnicode)[" << zone << "]:###";
zone.m_isParsed=true;
libmwaw::DebugFile &ascFile=zone.ascii();
ascFile.addPos(zone.m_entry.begin());
ascFile.addNote(f.str().c_str());
return true;
}
if (zone.m_entry.length()==164 && zone.m_level==1)
name="ZoneUnkn0";
else {
name="ItemDta";
// checkme: often Data22 is not parsed, but there can be others
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: find a unparsed %s zone %d\n", zone.m_level==1 ? "data" : "main", zone.m_ids[0]));
}
}
else {
MWAW_DEBUG_MSG(("RagTime5Parser::readZoneData: find a unknown type for zone=%d\n", zone.m_ids[0]));
name="UnknownZone";
}
libmwaw::DebugFile &ascFile=zone.ascii();
f << "Entries(" << name << "):" << zone;
zone.m_isParsed=true;
ascFile.addPos(zone.m_entry.begin());
ascFile.addNote(f.str().c_str());
ascFile.addPos(zone.m_entry.end());
ascFile.addNote("_");
return true;
}
////////////////////////////////////////////////////////////
// parse the different zones
////////////////////////////////////////////////////////////
bool RagTime5Parser::readString(RagTime5Zone &zone, std::string &text)
{
if (!zone.m_entry.valid()) return false;
MWAWInputStreamPtr input=zone.getInput();
libmwaw::DebugFile &ascFile=zone.ascii();
libmwaw::DebugStream f;
f << "Entries(StringZone)[" << zone << "]:";
input->seek(zone.m_entry.begin(), librevenge::RVNG_SEEK_SET);
text="";
for (long i=0; i<zone.m_entry.length(); ++i) {
auto c=char(input->readULong(1));
if (c==0 && i+1==zone.m_entry.length()) break;
if (c<0x1f)
return false;
text+=c;
}
f << "\"" << text << "\",";
if (input->tell()!=zone.m_entry.end()) {
MWAW_DEBUG_MSG(("RagTime5Parser::readString: find extra data\n"));
f << "###";
ascFile.addDelimiter(input->tell(),'|');
}
zone.m_isParsed=true;
ascFile.addPos(zone.m_entry.begin());
ascFile.addNote(f.str().c_str());
ascFile.addPos(zone.m_entry.end());
ascFile.addNote("_");
return true;
}
bool RagTime5Parser::readUnicodeString(RagTime5Zone &zone, std::string const &what)
{
if (zone.m_entry.length()==0) return true;
MWAWInputStreamPtr input=zone.getInput();
libmwaw::DebugFile &ascFile=zone.ascii();
libmwaw::DebugStream f;
if (what.empty())
f << "Entries(StringUnicode)[" << zone << "]:";
else
f << "Entries(" << what << ")[" << zone << "]:";
input->setReadInverted(!zone.m_hiLoEndian);
input->seek(zone.m_entry.begin(), librevenge::RVNG_SEEK_SET);
librevenge::RVNGString string;
if (!m_structManager->readUnicodeString(input, zone.m_entry.end(), string))
f << "###";
else
f << string.cstr();
zone.m_isParsed=true;
ascFile.addPos(zone.m_entry.begin());
ascFile.addNote(f.str().c_str());
ascFile.addPos(zone.m_entry.end());
ascFile.addNote("_");
input->setReadInverted(false);
return true;
}
bool RagTime5Parser::readUnicodeStringList(RagTime5ClusterManager::Link const &link, std::map<int, librevenge::RVNGString> &idToStringMap)
{
RagTime5ParserInternal::IndexUnicodeParser dataParser(*this, false, "UnicodeList");
if (!readListZone(link, dataParser))
return false;
idToStringMap=dataParser.m_idToStringMap;
return true;
}
bool RagTime5Parser::readLongListWithSize(int dataId, int fSz, std::vector<long> &listPosition, std::string const &zoneName)
{
listPosition.clear();
if (!dataId || fSz<=0 || fSz>4)
return false;
auto zone=getDataZone(dataId);
if (!zone || !zone->m_entry.valid() || (zone->m_entry.length()%fSz) ||
zone->getKindLastPart(zone->m_kinds[1].empty())!="ItemData") {
MWAW_DEBUG_MSG(("RagTime5Parser::readLongListWithSize: the zone %d seems bad\n", dataId));
return false;
}
MWAWEntry entry=zone->m_entry;
MWAWInputStreamPtr input=zone->getInput();
input->setReadInverted(!zone->m_hiLoEndian);
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
zone->m_isParsed=true;
libmwaw::DebugStream f;
if (!zoneName.empty())
f << "Entries(" << zoneName << ")[" << *zone << "]:";
else
f << "Entries(ListLong" << fSz << ")[" << *zone << "]:";
auto N=int(entry.length()/fSz);
for (int i=0; i<N; ++i) {
long ptr=input->readLong(fSz);
listPosition.push_back(ptr);
if (ptr)
f << ptr << ",";
else
f << "_,";
}
input->setReadInverted(false);
zone->ascii().addPos(entry.begin());
zone->ascii().addNote(f.str().c_str());
zone->ascii().addPos(entry.end());
zone->ascii().addNote("_");
return true;
}
bool RagTime5Parser::readLongList(RagTime5ClusterManager::Link const &link, std::vector<long> &list)
{
if (!link.m_ids.empty() && link.m_ids[0] &&
readLongListWithSize(link.m_ids[0], link.m_fieldSize, list, link.m_name))
return true;
list=link.m_longList;
return !list.empty();
}
bool RagTime5Parser::readPositions(int posId, std::vector<long> &listPosition)
{
return readLongListWithSize(posId, 4, listPosition, "Positions");
}
////////////////////////////////////////////////////////////
// Cluster
////////////////////////////////////////////////////////////
bool RagTime5Parser::readClusterRootData(RagTime5ClusterManager::ClusterRoot &cluster)
{
// first read the list of child cluster and update the list of cluster for the cluster manager
std::vector<int> listClusters;
for (auto zone : m_state->m_zonesList) {
if (!zone || zone->m_isParsed || !zone->m_entry.valid() || zone->getKindLastPart(zone->m_kinds[1].empty())!="Cluster")
continue;
listClusters.push_back(zone->m_ids[0]);
}
if (cluster.m_listClusterId==0) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readClusterRootData: cluster list id is not set, try zone id+1\n"));
cluster.m_listClusterId=cluster.m_zoneId+1;
}
std::vector<int> listChilds;
m_clusterManager->readClusterMainList(cluster, listChilds, listClusters);
std::set<int> seens;
// the list of graphic type
if (!cluster.m_graphicTypeLink.empty() && m_graphParser->readGraphicTypes(cluster.m_graphicTypeLink)) {
if (cluster.m_graphicTypeLink.m_ids.size()>2 && cluster.m_graphicTypeLink.m_ids[1])
seens.insert(cluster.m_graphicTypeLink.m_ids[1]);
}
// the different styles ( beginning with colors, then graphic styles and text styles )
for (int i=0; i<8; ++i) {
int const order[]= {7, 6, 1, 2, 0, 4, 3, 5};
int cId=cluster.m_styleClusterIds[order[i]];
if (!cId) continue;
int const wh[]= {0x480, 0x480, 0x480, 0x480, 0x480, -1, 0x480, 0x8042};
auto dZone= getDataZone(cId);
if (!dZone || dZone->getKindLastPart(dZone->m_kinds[1].empty())!="Cluster" || !readClusterZone(*dZone, wh[order[i]])) {
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterRootData: can not find cluster style zone %d\n", cId));
continue;
}
seens.insert(cId);
}
//! the field def cluster list
if (!cluster.m_listClusterLink[1].empty()) {
RagTime5ParserInternal::ClustListParser parser(*m_clusterManager.get(), 4, "RootClustLst1");
readFixedSizeZone(cluster.m_listClusterLink[1], parser);
// TODO: read the field cluster's data here
}
// now the main cluster list
for (int i=0; i<1; ++i) {
int cId=cluster.m_clusterIds[i];
if (cId==0) continue;
auto data=getDataZone(cId);
if (!data || !data->m_entry.valid() || data->getKindLastPart(data->m_kinds[1].empty())!="Cluster") {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readClusterRootData: the cluster zone %d seems bad\n", cId));
continue;
}
int const wh[]= {0x10000};
if (readClusterZone(*data, wh[i]))
seens.insert(cId);
}
if (!cluster.m_fieldClusterLink.empty())
m_clusterManager->readFieldClusters(cluster.m_fieldClusterLink);
for (int wh=0; wh<2; ++wh) {
auto const &list=wh==0 ? cluster.m_conditionFormulaLinks : cluster.m_settingLinks;
for (auto const &lnk : list) {
if (lnk.empty()) continue;
RagTime5ClusterManager::Cluster unknCluster(RagTime5ClusterManager::Cluster::C_Unknown);
unknCluster.m_dataLink=lnk;
RagTime5StructManager::FieldParser defaultParser(wh==0 ? "CondFormula" : "Settings");
readStructZone(unknCluster, defaultParser, 0);
}
}
if (!cluster.m_docInfoLink.empty()) {
RagTime5ClusterManager::Cluster unknCluster(RagTime5ClusterManager::Cluster::C_Unknown);
unknCluster.m_dataLink=cluster.m_docInfoLink;
RagTime5ParserInternal::DocInfoFieldParser parser(*this);
readStructZone(unknCluster, parser, 18);
}
if (!cluster.m_listUnicodeLink.empty()) {
RagTime5ParserInternal::IndexUnicodeParser parser(*this, true, "RootUnicodeLst");
readListZone(cluster.m_listUnicodeLink, parser);
}
// unknown link
if (!cluster.m_linkUnknown.empty()) { // find always an empty list
RagTime5StructManager::DataParser parser("RootUnknC");
readListZone(cluster.m_linkUnknown, parser);
}
// now read the not parsed childs
for (auto cId : listChilds) {
if (cId==0 || seens.find(cId)!=seens.end())
continue;
auto dZone= getDataZone(cId);
if (!dZone || dZone->getKindLastPart(dZone->m_kinds[1].empty())!="Cluster" || !readClusterZone(*dZone)) {
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterRootData: can not find cluster zone %d\n", cId));
continue;
}
seens.insert(cId);
}
for (auto const &link : cluster.m_linksList) {
if (link.m_type==RagTime5ClusterManager::Link::L_List) {
readListZone(link);
continue;
}
else if (link.m_type==RagTime5ClusterManager::Link::L_LongList) {
std::vector<long> list;
readLongList(link, list);
continue;
}
else if (link.m_type==RagTime5ClusterManager::Link::L_LinkDef) {
m_textParser->readLinkZones(cluster, link);
continue;
}
else if (link.m_type==RagTime5ClusterManager::Link::L_UnknownClusterC) {
m_clusterManager->readUnknownClusterC(link);
continue;
}
if (link.empty()) continue;
std::shared_ptr<RagTime5Zone> data=getDataZone(link.m_ids[0]);
if (!data || data->m_isParsed) {
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterRootData: can not find data zone %d\n", link.m_ids[0]));
continue;
}
data->m_hiLoEndian=cluster.m_hiLoEndian;
if (link.m_fieldSize==0 && !data->m_entry.valid())
continue;
switch (link.m_type) {
case RagTime5ClusterManager::Link::L_FieldsList:
case RagTime5ClusterManager::Link::L_List:
case RagTime5ClusterManager::Link::L_LongList:
case RagTime5ClusterManager::Link::L_UnicodeList:
case RagTime5ClusterManager::Link::L_UnknownClusterC:
break;
case RagTime5ClusterManager::Link::L_ClusterLink: {
std::vector<RagTime5StructManager::ZoneLink> links;
readClusterLinkList(*data, link, links);
break;
}
case RagTime5ClusterManager::Link::L_LinkDef:
case RagTime5ClusterManager::Link::L_Unknown:
#if !defined(__clang__)
default:
#endif
readFixedSizeZone(link, "");
break;
}
}
return true;
}
bool RagTime5Parser::checkClusterList(std::vector<int> const &list)
{
bool ok=true;
for (auto cId : list) {
if (cId==0) continue;
auto data=getDataZone(cId);
if (!data || !data->m_entry.valid() || data->getKindLastPart(data->m_kinds[1].empty())!="Cluster") {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::checkClusterList: the cluster zone %d seems bad\n", cId));
ok=false;
}
}
return ok;
}
bool RagTime5Parser::checkClusterList(std::vector<RagTime5StructManager::ZoneLink> const &list)
{
bool ok=true;
for (auto const &lnk : list) {
int cId=lnk.m_dataId;
if (cId==0) continue;
auto data=getDataZone(cId);
if (!data || !data->m_entry.valid() || data->getKindLastPart(data->m_kinds[1].empty())!="Cluster") {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::checkClusterList: the cluster zone %d seems bad\n", cId));
ok=false;
}
}
return ok;
}
bool RagTime5Parser::readClusterZone(RagTime5Zone &zone, int zoneType)
{
std::shared_ptr<RagTime5ClusterManager::Cluster> cluster;
if (!m_clusterManager->readCluster(zone, cluster, zoneType) || !cluster)
return false;
checkClusterList(cluster->m_clusterIdsList);
switch (cluster->m_type) {
// main zone
case RagTime5ClusterManager::Cluster::C_ChartZone:
case RagTime5ClusterManager::Cluster::C_GraphicZone:
case RagTime5ClusterManager::Cluster::C_Layout:
case RagTime5ClusterManager::Cluster::C_PictureZone:
case RagTime5ClusterManager::Cluster::C_Pipeline:
case RagTime5ClusterManager::Cluster::C_SpreadsheetZone:
case RagTime5ClusterManager::Cluster::C_TextZone: // parsing already done
return true;
case RagTime5ClusterManager::Cluster::C_Script: {
auto *clust=dynamic_cast<RagTime5ClusterManager::ClusterScript *>(cluster.get());
if (!clust) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readClusterZone: can not find the script pointer\n"));
return false;
}
return readClusterScriptData(*clust);
}
case RagTime5ClusterManager::Cluster::C_ClusterGProp:
return readClusterGProp(*cluster);
case RagTime5ClusterManager::Cluster::C_ClusterC:
return readUnknownClusterCData(*cluster);
case RagTime5ClusterManager::Cluster::C_ColorPattern:
return m_graphParser->readColorPatternZone(*cluster);
case RagTime5ClusterManager::Cluster::C_Fields:
return readClusterFieldsData(*cluster);
case RagTime5ClusterManager::Cluster::C_Root: {
auto *root=dynamic_cast<RagTime5ClusterManager::ClusterRoot *>(cluster.get());
if (!root) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readClusterZone: can not find the root pointer\n"));
return false;
}
readClusterRootData(*root);
return true;
}
// style
case RagTime5ClusterManager::Cluster::C_FormatStyles:
return m_styleManager->readFormats(*cluster);
case RagTime5ClusterManager::Cluster::C_ColorStyles:
return m_styleManager->readGraphicColors(*cluster);
case RagTime5ClusterManager::Cluster::C_GraphicStyles:
return m_styleManager->readGraphicStyles(*cluster);
case RagTime5ClusterManager::Cluster::C_TextStyles:
return m_styleManager->readTextStyles(*cluster);
case RagTime5ClusterManager::Cluster::C_UnitStyles: {
RagTime5StructManager::FieldParser defaultParser("Units");
return readStructZone(*cluster, defaultParser, 14);
}
case RagTime5ClusterManager::Cluster::C_Empty:
case RagTime5ClusterManager::Cluster::C_Unknown:
#if !defined(__clang__)
default:
#endif
break;
}
if (!cluster->m_nameLink.empty()) {
std::map<int, librevenge::RVNGString> idToStringMap;
readUnicodeStringList(cluster->m_nameLink, idToStringMap);
}
for (auto const &link : cluster->m_linksList) {
if (link.m_type==RagTime5ClusterManager::Link::L_List)
readListZone(link);
else
readFixedSizeZone(link, "");
}
return true;
}
bool RagTime5Parser::readClusterLinkList
(RagTime5ClusterManager::Link const &link, RagTime5ClusterManager::Link const &nameLink,
std::vector<RagTime5StructManager::ZoneLink> &list, std::string const &name)
{
RagTime5ParserInternal::ClustListParser parser(*m_clusterManager.get(), 10, !name.empty() ? name : link.getZoneName());
if (!nameLink.empty())
readUnicodeStringList(nameLink, parser.m_idToNameMap);
if (!link.empty())
readListZone(link, parser);
list=parser.m_linkList;
return true;
}
bool RagTime5Parser::readClusterLinkList(RagTime5Zone &zone, RagTime5ClusterManager::Link const &link,
std::vector<RagTime5StructManager::ZoneLink> &listLinks)
{
listLinks.clear();
if (!zone.m_entry.valid()) {
if (link.m_N && link.m_fieldSize) {
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterLinkList: can not find data zone %d\n", link.m_ids[0]));
}
return false;
}
MWAWInputStreamPtr input=zone.getInput();
bool const hiLo=zone.m_hiLoEndian;
input->setReadInverted(!hiLo);
input->seek(zone.m_entry.begin(), librevenge::RVNG_SEEK_SET);
zone.m_isParsed=true;
libmwaw::DebugFile &ascFile=zone.ascii();
libmwaw::DebugStream f;
std::string zoneName=link.m_name.empty() ? "ClustLink" : link.m_name;
f << "Entries(" << zoneName << ")[" << zone << "]:";
if (link.m_N*link.m_fieldSize>zone.m_entry.length() || link.m_N*link.m_fieldSize < 0 ||
link.m_N>zone.m_entry.length() || link.m_fieldSize!=12) {
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterLinkList: bad fieldSize/N for zone %d\n", link.m_ids[0]));
f << "###";
ascFile.addPos(zone.m_entry.begin());
ascFile.addNote(f.str().c_str());
return true;
}
ascFile.addPos(zone.m_entry.begin());
ascFile.addNote(f.str().c_str());
listLinks.resize(size_t(link.m_N)+1);
for (int i=0; i<link.m_N; ++i) {
long pos=input->tell();
f.str("");
f << zoneName << "-" << i+1 << ":";
RagTime5StructManager::ZoneLink cLink;
std::vector<int> listIds;
if (!m_structManager->readDataIdList(input, 1, listIds)) {
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterLinkList: a link seems bad\n"));
f << "###id,";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pos+12, librevenge::RVNG_SEEK_SET);
continue;
}
else if (listIds[0]==0) {
ascFile.addPos(pos);
ascFile.addNote("_");
input->seek(pos+12, librevenge::RVNG_SEEK_SET);
continue;
}
cLink.m_dataId=listIds[0];
f << m_clusterManager->getClusterName(listIds[0]) << ",";
cLink.m_subZoneId[0]=long(input->readULong(4)); // 0 or 80000000 and a small int
cLink.m_subZoneId[1]=long(input->readLong(4)); // small int
f << cLink;
listLinks[size_t(i+1)]=cLink;
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pos+12, librevenge::RVNG_SEEK_SET);
}
if (input->tell()!=zone.m_entry.end()) {
f.str("");
f << zoneName << ":end";
ascFile.addPos(input->tell());
ascFile.addNote(f.str().c_str());
}
return true;
}
////////////////////////////////////////////////////////////
// structured zone
////////////////////////////////////////////////////////////
bool RagTime5Parser::readClusterFieldsData(RagTime5ClusterManager::Cluster &cluster)
{
RagTime5ClusterManager::Link const &link=cluster.m_dataLink;
m_textParser->readFieldZones(cluster, link, link.m_fileType[0]==0x20000);
for (auto const &lnk : cluster.m_linksList)
readFixedSizeZone(lnk, "Data2_FieldUnkn");
return true;
}
bool RagTime5Parser::readDocInfoClusterData(RagTime5Zone &zone, MWAWEntry const &entry)
{
if (!entry.valid() || entry.length()<160) {
MWAW_DEBUG_MSG(("RagTime5Parser::readDocInfoClusterData: the entry does not seems valid\n"));
return false;
}
libmwaw::DebugFile &ascFile=zone.ascii();
libmwaw::DebugStream f;
MWAWInputStreamPtr input=zone.getInput();
long pos=entry.begin();
input->seek(pos, librevenge::RVNG_SEEK_SET);
f << "DocInfo[dataA]:";
// checkme the field data seems always in hilo endian...
bool actEndian=input->readInverted();
input->setReadInverted(false);
auto val=static_cast<int>(input->readULong(2)); // always 0
if (val) f << "f0=" << val;
auto dataSz=long(input->readULong(4));
if (pos+dataSz>entry.end()) {
MWAW_DEBUG_MSG(("RagTime5Parser::readDocInfoClusterData: the main data size seems bad\n"));
f << "###dSz=" << dataSz << ",";
ascFile.addDelimiter(input->tell(),'|');
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
input->setReadInverted(actEndian);
return true;
}
for (int i=0; i<2; ++i) { // f1=2
val=static_cast<int>(input->readULong(2));
if (val) f << "f" << i << "=" << val << ",";
}
auto sSz=static_cast<int>(input->readULong(1));
long actPos=input->tell();
if (sSz>25) {
MWAW_DEBUG_MSG(("RagTime5Parser::readDocInfoClusterData: the dataA string size seems bad\n"));
f << "###sSz=" << sSz << ",";
sSz=0;
}
std::string text("");
for (int i=0; i<sSz; ++i) text += char(input->readULong(1));
f << text << ",";
input->seek(actPos+25, librevenge::RVNG_SEEK_SET);
f << "IDS=["; // maybe some char
for (int i=0; i<7; ++i) { // _, ?, ?, ?, 0, 0|4, ?
val=static_cast<int>(input->readULong(2));
if (val) f << std::hex << val << std::dec << ",";
else f << "_,";
}
f << "],";
sSz=static_cast<int>(input->readULong(1));
actPos=input->tell();
if (sSz>62) {
MWAW_DEBUG_MSG(("RagTime5Parser::readDocInfoClusterData: the dataA string2 size seems bad\n"));
f << "###sSz2=" << sSz << ",";
sSz=0;
}
text=("");
for (int i=0; i<sSz; ++i) text += char(input->readULong(1));
f << text << ",";
input->seek(actPos+63, librevenge::RVNG_SEEK_SET);
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << "DocInfo[dataB]:";
f << "IDS=["; // maybe some char
for (int i=0; i<8; ++i) {
val=static_cast<int>(input->readULong(2));
if (val) f << std::hex << val << std::dec << ",";
else f << "_,";
}
f << "],";
for (int i=0; i<11; ++i) { // f0=-1|2|6, f1=-1|2|4, f3=0|17|21,
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
val=static_cast<int>(input->readLong(1)); // 0
if (val) f << "f11=" << val << ",";
sSz=static_cast<int>(input->readULong(1));
if (sSz>64||pos+sSz+4>entry.end()) {
MWAW_DEBUG_MSG(("RagTime5Parser::readDocInfoClusterData: the string size for dataB data seems bad\n"));
f << "###sSz3=" << sSz << ",";
ascFile.addDelimiter(input->tell(),'|');
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
input->setReadInverted(actEndian);
return true;
}
text=("");
for (int i=0; i<sSz; ++i) text += char(input->readULong(1));
f << text << ",";
if ((sSz%2)==1)
input->seek(1, librevenge::RVNG_SEEK_CUR);
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << "DocInfo[dataC]:";
if (input->readLong(2)!=1 || (val=static_cast<int>(input->readLong(2)))<=0 || (val%4) || pos+6+val>entry.end()) {
MWAW_DEBUG_MSG(("RagTime5Parser::readDocInfoClusterData: oops something is bad[dataC]\n"));
f << "###val=" << val << ",";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
input->setReadInverted(actEndian);
return true;
}
int N=val/4;
f << "list=[";
for (int i=0; i<N; ++i) {
val=static_cast<int>(input->readLong(4));
if (val)
f << std::hex << val << std::dec << ",";
else
f << "_,";
}
f << "],";
val=static_cast<int>(input->readLong(2)); // always 2
if (val!=2) f << "f0=" << val << ",";
sSz=static_cast<int>(input->readULong(2));
if (input->tell()+sSz+4>entry.end()) {
MWAW_DEBUG_MSG(("RagTime5Parser::readDocInfoClusterData: string size seems bad[dataC]\n"));
f << "###sSz=" << sSz << ",";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(entry.end(), librevenge::RVNG_SEEK_SET);
input->setReadInverted(actEndian);
return true;
}
text=("");
for (int i=0; i<sSz; ++i) text += char(input->readULong(1));
f << text << ",";
if ((sSz%2)==1)
input->seek(1, librevenge::RVNG_SEEK_CUR);
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << "DocInfo[dataD]:";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->setReadInverted(actEndian);
return true;
}
bool RagTime5Parser::readScriptComment(RagTime5Zone &zone)
{
if (!zone.m_entry.valid() ||
zone.getKindLastPart(zone.m_kinds[1].empty())!="Unicode") {
zone.addErrorInDebugFile("ScriptComment");
MWAW_DEBUG_MSG(("RagTime5Parser::readScriptComment: the script comment zone %d seems bad\n", zone.m_ids[0]));
return true;
}
readUnicodeString(zone, "ScriptComment");
libmwaw::DebugStream f;
for (auto it : zone.m_childIdToZoneMap) {
auto child=it.second;
if (!child || child->m_isParsed) continue;
child->m_isParsed=true;
switch (it.first) {
case 3: // find one time with no data
if (child->m_entry.valid()) {
MWAW_DEBUG_MSG(("RagTime5Parser::readScriptComment: find data with child3\n"));
libmwaw::DebugFile &ascFile=child->ascii();
f.str("");
f << "ScriptComment[" << *child << "child3]:";
ascFile.addPos(child->m_entry.begin());
ascFile.addNote(f.str().c_str());
ascFile.addPos(child->m_entry.end());
ascFile.addNote("_");
}
break;
case 8:
ascii().addPos(child->m_defPosition);
ascii().addNote("scriptComment[refCount]");
break;
default: {
std::string kind=child->getKindLastPart();
if (kind=="Unicode") { // the script name
child->m_hiLoEndian=zone.m_hiLoEndian;
readUnicodeString(*child, "ScriptNameData");
break;
}
if (kind=="32Bit") {
if (child->m_variableD[0]!=0 || child->m_variableD[1]!=1) { // do not show in meny
MWAW_DEBUG_MSG(("RagTime5Parser::readScriptComment: find unknown flag\n"));
ascii().addPos(child->m_defPosition);
ascii().addNote("scriptData[showInMenu]:###");
}
if (child->m_entry.valid()) {
libmwaw::DebugFile &ascFile=child->ascii();
f.str("");
f << "Entries(ScriptData)[" << *child << "]:###";
MWAW_DEBUG_MSG(("RagTime5Parser::readScriptComment: find unknown script data\n"));
ascFile.addPos(child->m_entry.begin());
ascFile.addNote(f.str().c_str());
ascFile.addPos(child->m_entry.end());
ascFile.addNote("_");
}
break;
}
if (kind=="OSAScript") {
if (child->m_entry.valid()) {
libmwaw::DebugFile &ascFile=child->ascii();
f.str("");
f << "Entries(OSAScript)[" << *child << "]:";
ascFile.addPos(child->m_entry.begin());
ascFile.addNote(f.str().c_str());
ascFile.addPos(child->m_entry.end());
ascFile.addNote("_");
}
break;
}
MWAW_DEBUG_MSG(("RagTime5Parser::readScriptComment: find unknown child zone\n"));
child->addErrorInDebugFile("ScriptComment");
break;
}
}
}
return true;
}
bool RagTime5Parser::readClusterScriptData(RagTime5ClusterManager::ClusterScript &cluster)
{
std::shared_ptr<RagTime5Zone> dataZone;
int id=cluster.m_scriptComment.m_ids.empty() ? 0 : cluster.m_scriptComment.m_ids[0];
if (id) dataZone=getDataZone(cluster.m_scriptComment.m_ids[0]);
if (!dataZone && id) {
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterScriptData: the script comment zone %d seems bad\n", id));
}
else if (dataZone) {
dataZone->m_hiLoEndian=cluster.m_hiLoEndian;
readScriptComment(*dataZone);
}
std::vector<RagTime5StructManager::ZoneLink> listCluster;
readClusterLinkList(cluster.m_dataLink, cluster.m_nameLink, listCluster, "ScriptClustLst");
checkClusterList(listCluster);
for (auto const &lnk : cluster.m_linksList) {
if (lnk.m_type==RagTime5ClusterManager::Link::L_List) {
readListZone(lnk);
continue;
}
std::stringstream s;
s << "DataScript_" << lnk.m_fieldSize;
RagTime5StructManager::DataParser defaultParser(s.str());
readFixedSizeZone(lnk, defaultParser);
}
return true;
}
bool RagTime5Parser::readClusterGProp(RagTime5ClusterManager::Cluster &cluster)
{
RagTime5ClusterManager::Link const &link=cluster.m_dataLink;
if (link.m_ids.size()<2 || !link.m_ids[1]) {
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterGProp: can not find the main data\n"));
return false;
}
RagTime5StructManager::GObjPropFieldParser defaultParser("RootGObjProp");
if (!readStructZone(cluster, defaultParser, 8)) {
auto dataZone=getDataZone(link.m_ids[1]);
if (dataZone)
dataZone->addErrorInDebugFile("RootGObjProp");
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterGProp: unexpected type for zone %d\n", link.m_ids[1]));
}
for (auto const &lnk : cluster.m_linksList) {
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterGProp: find extra data\n"));
RagTime5StructManager::DataParser defParser("UnknBUnknown2");
readFixedSizeZone(lnk, defParser);
}
return true;
}
bool RagTime5Parser::readUnknownClusterCData(RagTime5ClusterManager::Cluster &cluster)
{
RagTime5ClusterManager::Link const &link=cluster.m_dataLink;
if (link.m_ids.empty()) {
MWAW_DEBUG_MSG(("RagTime5Parser::readUnknownClusterCData: can not find the main data\n"));
return false;
}
std::stringstream s;
s << "UnknC_" << char('A'+link.m_fileType[0]) << "_";
std::string zoneName=s.str();
if (link.m_type==RagTime5ClusterManager::Link::L_List) {
if (link.m_fileType[1]==0x310) {
// find id=8,"Rechenblatt 1": spreadsheet name ?
RagTime5ParserInternal::IndexUnicodeParser parser(*this, true, zoneName+"0");
readListZone(link, parser);
}
else {
RagTime5StructManager::DataParser parser(zoneName+"0");
readListZone(link, parser);
}
}
else {
RagTime5StructManager::DataParser defaultParser(zoneName+"0");
readFixedSizeZone(link, defaultParser);
}
for (auto const &lnk : cluster.m_linksList) {
RagTime5StructManager::DataParser parser(zoneName+"1");
readFixedSizeZone(lnk, parser);
}
return true;
}
bool RagTime5Parser::readListZone(RagTime5ClusterManager::Link const &link)
{
RagTime5StructManager::DataParser parser(link.getZoneName());
return readListZone(link, parser);
}
bool RagTime5Parser::readListZone(RagTime5ClusterManager::Link const &link, RagTime5StructManager::DataParser &parser)
{
if (link.m_ids.size()<2 || !link.m_ids[1])
return false;
std::vector<long> decal;
if (link.m_ids[0])
readPositions(link.m_ids[0], decal);
if (decal.empty())
decal=link.m_longList;
int const dataId=link.m_ids[1];
auto dataZone=getDataZone(dataId);
auto N=int(decal.size());
if (!dataZone || !dataZone->m_entry.valid() ||
dataZone->getKindLastPart(dataZone->m_kinds[1].empty())!="ItemData" || N<=1) {
if (N==1 && dataZone && !dataZone->m_entry.valid()) {
// a zone with 0 zone is ok...
dataZone->m_isParsed=true;
libmwaw::DebugStream f;
f << "[" << parser.getZoneName() << "]";
ascii().addPos(dataZone->m_defPosition);
ascii().addNote(f.str().c_str());
return true;
}
MWAW_DEBUG_MSG(("RagTime5Parser::readListZone: the data zone %d seems bad\n", dataId));
return false;
}
dataZone->m_isParsed=true;
MWAWEntry entry=dataZone->m_entry;
libmwaw::DebugFile &ascFile=dataZone->ascii();
libmwaw::DebugStream f;
f << "Entries(" << parser.getZoneName() << ")[" << *dataZone << "]:";
ascFile.addPos(entry.end());
ascFile.addNote("_");
ascFile.addPos(entry.begin());
ascFile.addNote(f.str().c_str());
MWAWInputStreamPtr input=dataZone->getInput();
input->setReadInverted(!dataZone->m_hiLoEndian);
long debPos=entry.begin();
long endPos=entry.end();
for (int i=0; i<N-1; ++i) {
long pos=decal[size_t(i)], lastPos=decal[size_t(i+1)];
if (pos==lastPos) continue;
if (pos<0 || pos>lastPos || debPos+lastPos>endPos) {
MWAW_DEBUG_MSG(("RagTime5Parser::readListZone: can not read the data zone %d-%d seems bad\n", dataId, i));
continue;
}
input->seek(debPos+pos, librevenge::RVNG_SEEK_SET);
f.str("");
f << parser.getZoneName(i+1) << ":";
if (!parser.parseData(input, debPos+lastPos, *dataZone, i+1, f))
f << "###";
ascFile.addPos(debPos+pos);
ascFile.addNote(f.str().c_str());
ascFile.addPos(debPos+lastPos);
ascFile.addNote("_");
}
input->setReadInverted(false);
return true;
}
bool RagTime5Parser::readFixedSizeZone(RagTime5ClusterManager::Link const &link, std::string const &name)
{
RagTime5StructManager::DataParser parser(name.empty() ? link.getZoneName() : name);
return readFixedSizeZone(link, parser);
}
bool RagTime5Parser::readFixedSizeZone(RagTime5ClusterManager::Link const &link, RagTime5StructManager::DataParser &parser)
{
if (link.m_ids.empty() || !link.m_ids[0])
return false;
int const dataId=link.m_ids[0];
auto dataZone=getDataZone(dataId);
if (!dataZone || !dataZone->m_entry.valid() ||
dataZone->getKindLastPart(dataZone->m_kinds[1].empty())!="ItemData" ||
link.m_fieldSize<=0 || link.m_N>dataZone->m_entry.length()/link.m_fieldSize ||
link.m_N>dataZone->m_entry.length() || link.m_N<0) {
if ((link.m_N==0 || link.m_fieldSize==0) && dataZone && !dataZone->m_entry.valid()) {
// a zone with 0 zone is ok...
dataZone->m_isParsed=true;
return true;
}
MWAW_DEBUG_MSG(("RagTime5Parser::readFixedSizeZone: the data zone %d seems bad\n", dataId));
if (dataZone) dataZone->addErrorInDebugFile(parser.getZoneName());
return false;
}
dataZone->m_isParsed=true;
MWAWEntry entry=dataZone->m_entry;
libmwaw::DebugFile &ascFile=dataZone->ascii();
libmwaw::DebugStream f;
f << "Entries(" << parser.getZoneName() << ")[" << *dataZone << "]:";
ascFile.addPos(entry.end());
ascFile.addNote("_");
ascFile.addPos(entry.begin());
ascFile.addNote(f.str().c_str());
MWAWInputStreamPtr input=dataZone->getInput();
input->setReadInverted(!dataZone->m_hiLoEndian);
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
long endPos=entry.end();
for (int i=0; i<link.m_N; ++i) {
long pos=input->tell();
f.str("");
f << parser.getZoneName(i+1) << ":";
if (!parser.parseData(input, pos+link.m_fieldSize, *dataZone, i+1, f))
f << "###";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(pos+link.m_fieldSize, librevenge::RVNG_SEEK_SET);
}
long pos=input->tell();
if (pos!=endPos) {
f.str("");
f << parser.getZoneName() << ":#end";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
}
input->setReadInverted(false);
return true;
}
bool RagTime5Parser::readStructZone(RagTime5ClusterManager::Cluster &cluster, RagTime5StructManager::FieldParser &parser, int headerSz)
{
RagTime5ClusterManager::Link const &link=cluster.m_dataLink;
if (link.m_ids.size()<2 || !link.m_ids[1])
return false;
std::map<int, librevenge::RVNGString> idToNameMap;
if (!cluster.m_nameLink.empty()) {
readUnicodeStringList(cluster.m_nameLink, idToNameMap);
cluster.m_nameLink=RagTime5ClusterManager::Link();
}
std::vector<long> decal;
if (link.m_ids[0])
readPositions(link.m_ids[0], decal);
if (decal.empty())
decal=link.m_longList;
int const dataId=link.m_ids[1];
auto dataZone=getDataZone(dataId);
if (!dataZone || !dataZone->m_entry.valid() ||
dataZone->getKindLastPart(dataZone->m_kinds[1].empty())!="ItemData") {
if (decal.size()==1) {
// a zone with 0 zone is ok...
if (dataZone)
dataZone->m_isParsed=true;
return true;
}
MWAW_DEBUG_MSG(("RagTime5Parser::readStructZone: the data zone %d seems bad\n", dataId));
return false;
}
dataZone->m_isParsed=true;
MWAWEntry entry=dataZone->m_entry;
libmwaw::DebugFile &ascFile=dataZone->ascii();
libmwaw::DebugStream f;
f << "Entries(" << parser.getZoneName() << ")[" << *dataZone << "]:";
ascFile.addPos(entry.end());
ascFile.addNote("_");
ascFile.addPos(entry.begin());
ascFile.addNote(f.str().c_str());
auto N=int(decal.size());
MWAWInputStreamPtr input=dataZone->getInput();
input->setReadInverted(!dataZone->m_hiLoEndian);
long debPos=entry.begin();
long endPos=entry.end();
if (N==0) {
MWAW_DEBUG_MSG(("RagTime5Parser::readStructZone: can not find decal list for zone %d, let try to continue\n", dataId));
input->seek(debPos, librevenge::RVNG_SEEK_SET);
int n=0;
while (input->tell()+8 < endPos) {
long pos=input->tell();
int id=++n;
librevenge::RVNGString name("");
if (idToNameMap.find(id)!=idToNameMap.end())
name=idToNameMap.find(id)->second;
if (!readStructData(*dataZone, endPos, id, headerSz, parser, name)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
}
if (input->tell()!=endPos) {
static bool first=true;
if (first) {
MWAW_DEBUG_MSG(("RagTime5Parser::readStructZone: can not read some block\n"));
first=false;
}
ascFile.addPos(debPos);
ascFile.addNote("###");
}
}
else {
for (int i=0; i<N-1; ++i) {
long pos=decal[size_t(i)];
long nextPos=decal[size_t(i+1)];
if (pos<0 || debPos+pos>endPos) {
MWAW_DEBUG_MSG(("RagTime5Parser::readStructZone: can not read the data zone %d-%d seems bad\n", dataId, i));
continue;
}
librevenge::RVNGString name("");
if (idToNameMap.find(i+1)!=idToNameMap.end())
name=idToNameMap.find(i+1)->second;
input->seek(debPos+pos, librevenge::RVNG_SEEK_SET);
readStructData(*dataZone, debPos+nextPos, i+1, headerSz, parser, name);
if (input->tell()!=debPos+nextPos) {
static bool first=true;
if (first) {
MWAW_DEBUG_MSG(("RagTime5Parser::readStructZone: can not read some block\n"));
first=false;
}
ascFile.addPos(debPos+pos);
ascFile.addNote("###");
}
}
}
return true;
}
bool RagTime5Parser::readStructData(RagTime5Zone &zone, long endPos, int n, int headerSz,
RagTime5StructManager::FieldParser &parser, librevenge::RVNGString const &dataName)
{
MWAWInputStreamPtr input=zone.getInput();
long pos=input->tell();
if ((headerSz && pos+headerSz>endPos) || (headerSz==0 && pos+5>endPos)) return false;
libmwaw::DebugFile &ascFile=zone.ascii();
libmwaw::DebugStream f;
std::string const zoneName=parser.getZoneName(n);
int m=0;
if (headerSz>0) {
f << zoneName << "[A]:";
if (!dataName.empty()) f << dataName.cstr() << ",";
int val;
if (headerSz==14) {
val=static_cast<int>(input->readLong(4));
if (val!=1) f << "numUsed=" << val << ",";
f << "f1=" << std::hex << input->readULong(2) << std::dec << ",";
val=static_cast<int>(input->readLong(2)); // sometimes form an increasing sequence but not always
if (val!=n) f << "id=" << val << ",";
RagTime5StructManager::Field field;
field.m_fileType=long(input->readULong(4));
field.m_type=RagTime5StructManager::Field::T_Long;
field.m_longValue[0]=input->readLong(2);
parser.parseHeaderField(field, zone, n, f);
}
else if (headerSz==8) {
val=static_cast<int>(input->readLong(2));
if (val!=1) f << "numUsed=" << val << ",";
val=static_cast<int>(input->readLong(2)); // sometimes form an increasing sequence but not always
if (val!=n) f << "id=" << val << ",";
f << "type=" << std::hex << input->readULong(4) << std::dec << ","; // 0 or 01458042
}
else if (headerSz==18) { // docinfo header
val=static_cast<int>(input->readLong(4)); // 1 or 3
if (val!=1) f << "numUsed?=" << val << ",";
val=static_cast<int>(input->readLong(4)); // always 0
if (val) f << "f0=" << val << ",";
f << "ID=" << std::hex << input->readULong(4) << ","; // a big number
val=static_cast<int>(input->readLong(4));
if (val!=0x1f6817) // doc info type
f << "type=" << std::hex << val << std::dec << ",";
val=static_cast<int>(input->readLong(2)); // always 0
if (val) f << "f1=" << val << ",";
input->seek(pos+headerSz, librevenge::RVNG_SEEK_SET);
}
else {
MWAW_DEBUG_MSG(("RagTime5Parser::readStructData: find unknown header size\n"));
f << "###hSz";
input->seek(pos+headerSz, librevenge::RVNG_SEEK_SET);
}
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
}
pos=input->tell();
if (parser.m_regroupFields) {
f.str("");
f << zoneName << "[B]:";
if (headerSz==0 && !dataName.empty()) f << dataName.cstr() << ",";
}
while (!input->isEnd()) {
long actPos=input->tell();
if (actPos>=endPos) break;
if (!parser.m_regroupFields) {
f.str("");
f << zoneName << "[B" << ++m << "]:";
if (m==1 && headerSz==0 && !dataName.empty()) f << dataName.cstr() << ",";
}
RagTime5StructManager::Field field;
if (!m_structManager->readField(input, endPos, ascFile, field, headerSz ? 0 : endPos-actPos)) {
input->seek(actPos, librevenge::RVNG_SEEK_SET);
break;
}
if (!parser.parseField(field, zone, n, f))
f << "#" << field;
if (!parser.m_regroupFields) {
ascFile.addPos(actPos);
ascFile.addNote(f.str().c_str());
}
}
if (parser.m_regroupFields && pos!=input->tell()) {
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
}
return true;
}
////////////////////////////////////////////////////////////
// zone unpack/create ascii file, ...
////////////////////////////////////////////////////////////
bool RagTime5Parser::update(RagTime5Zone &zone)
{
if (zone.m_entriesList.empty())
return true;
std::stringstream s;
s << "Zone" << std::hex << zone.m_entriesList[0].begin() << std::dec;
zone.setAsciiFileName(s.str());
if (zone.m_entriesList.size()==1) {
zone.m_entry=zone.m_entriesList[0];
return true;
}
libmwaw::DebugStream f;
f << "Entries(" << zone.getZoneName() << "):";
MWAWInputStreamPtr input = getInput();
std::shared_ptr<MWAWStringStream> newStream;
int n=0;
for (auto const &entry : zone.m_entriesList) {
if (!entry.valid() || !input->checkPosition(entry.end())) {
MWAW_DEBUG_MSG(("RagTime5Parser::update: can not read some data\n"));
f << "###";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
return false;
}
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
unsigned long read;
const unsigned char *dt = input->read(static_cast<unsigned long>(entry.length()), read);
if (!dt || long(read) != entry.length()) {
MWAW_DEBUG_MSG(("RagTime5Parser::update: can not read some data\n"));
f << "###";
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
return false;
}
ascii().skipZone(entry.begin(), entry.end()-1);
if (n++==0)
newStream.reset(new MWAWStringStream(dt, static_cast<unsigned int>(entry.length())));
else
newStream->append(dt, static_cast<unsigned int>(entry.length()));
}
MWAWInputStreamPtr newInput(new MWAWInputStream(newStream, false));
zone.setInput(newInput);
zone.m_entry.setBegin(0);
zone.m_entry.setLength(newInput->size());
return true;
}
bool RagTime5Parser::unpackZone(RagTime5Zone &zone, MWAWEntry const &entry, std::vector<unsigned char> &data)
{
if (!entry.valid())
return false;
MWAWInputStreamPtr input=zone.getInput();
long pos=entry.begin(), endPos=entry.end();
if (entry.length()<4 || !input->checkPosition(endPos)) {
MWAW_DEBUG_MSG(("RagTime5Parser::unpackZone: the input seems bad\n"));
return false;
}
bool actEndian=input->readInverted();
input->setReadInverted(false);
input->seek(pos, librevenge::RVNG_SEEK_SET);
data.resize(0);
auto sz=static_cast<unsigned long>(input->readULong(4));
if (sz==0) {
input->setReadInverted(actEndian);
return true;
}
auto flag=int(sz>>24);
sz &= 0xFFFFFF;
if ((flag&0xf) || (flag&0xf0)==0 || !(sz&0xFFFFFF)) {
input->setReadInverted(actEndian);
return false;
}
int nBytesRead=0, szField=9;
unsigned int read=0;
size_t mapPos=0;
data.reserve(size_t(sz));
std::vector<std::vector<unsigned char> > mapToString;
mapToString.reserve(size_t(entry.length()-6));
bool ok=false;
while (!input->isEnd()) {
if (static_cast<int>(mapPos)==(1<<szField)-0x102)
++szField;
if (input->tell()>=endPos) {
MWAW_DEBUG_MSG(("RagTime5Parser::unpackZone: oops can not find last data\n"));
ok=false;
break;
}
do {
read = (read<<8)+static_cast<unsigned int>(input->readULong(1));
nBytesRead+=8;
}
while (nBytesRead<szField);
unsigned int val=(read >> (nBytesRead-szField));
nBytesRead-=szField;
read &= ((1<<nBytesRead)-1);
if (val<0x100) {
auto c=static_cast<unsigned char>(val);
data.push_back(c);
if (mapPos>= mapToString.size())
mapToString.resize(mapPos+1);
mapToString[mapPos++]=std::vector<unsigned char>(1,c);
continue;
}
if (val==0x100) { // begin
if (!data.empty()) {
// data are reset when mapPos=3835, so it is ok
mapPos=0;
mapToString.resize(0);
szField=9;
}
continue;
}
if (val==0x101) {
ok=read==0;
if (!ok) {
MWAW_DEBUG_MSG(("RagTime5Parser::unpackZone: find 0x101 in bad position\n"));
}
break;
}
auto readPos=size_t(val-0x102);
if (readPos >= mapToString.size()) {
MWAW_DEBUG_MSG(("RagTime5Parser::unpackZone: find bad position\n"));
ok = false;
break;
}
std::vector<unsigned char> final=mapToString[readPos++];
if (readPos==mapToString.size())
final.push_back(final[0]);
else
final.push_back(mapToString[readPos][0]);
data.insert(data.end(), final.begin(), final.end());
if (mapPos>= mapToString.size())
mapToString.resize(mapPos+1);
mapToString[mapPos++]=final;
}
if (ok && data.size()!=size_t(sz)) {
MWAW_DEBUG_MSG(("RagTime5Parser::unpackZone: oops the data file is bad\n"));
ok=false;
}
if (!ok) {
MWAW_DEBUG_MSG(("RagTime5Parser::unpackZone: stop with mapPos=%ld and totalSize=%ld/%ld\n", long(mapPos), long(data.size()), long(sz)));
}
input->setReadInverted(actEndian);
return ok;
}
bool RagTime5Parser::unpackZone(RagTime5Zone &zone)
{
if (!zone.m_entry.valid())
return false;
std::vector<unsigned char> newData;
if (!unpackZone(zone, zone.m_entry, newData))
return false;
long pos=zone.m_entry.begin(), endPos=zone.m_entry.end();
MWAWInputStreamPtr input=zone.getInput();
if (input->tell()!=endPos) {
MWAW_DEBUG_MSG(("RagTime5Parser::unpackZone: find some extra data\n"));
return false;
}
if (newData.empty()) {
// empty zone
zone.ascii().addPos(pos);
zone.ascii().addNote("_");
zone.m_entry.setLength(0);
zone.m_extra += "packed,";
return true;
}
if (input.get()==getInput().get())
ascii().skipZone(pos, endPos-1);
std::shared_ptr<MWAWStringStream> newStream(new MWAWStringStream(&newData[0], static_cast<unsigned int>(newData.size())));
MWAWInputStreamPtr newInput(new MWAWInputStream(newStream, false));
zone.setInput(newInput);
zone.m_entry.setBegin(0);
zone.m_entry.setLength(newInput->size());
zone.m_extra += "packed,";
return true;
}
////////////////////////////////////////////////////////////
// read the different zones
////////////////////////////////////////////////////////////
bool RagTime5Parser::readDocumentVersion(RagTime5Zone &zone)
{
MWAWInputStreamPtr input = zone.getInput();
MWAWEntry &entry=zone.m_entry;
zone.m_isParsed=true;
ascii().addPos(zone.m_defPosition);
ascii().addNote("doc[version],");
libmwaw::DebugFile &ascFile=zone.ascii();
libmwaw::DebugStream f;
f << "Entries(DocVersion):";
ascFile.addPos(entry.end());
ascFile.addNote("_");
if ((entry.length())%6!=2) {
MWAW_DEBUG_MSG(("RagTime5Parser::readDocumentVersion: the entry size seem bads\n"));
f << "###";
ascFile.addPos(entry.begin());
ascFile.addNote(f.str().c_str());
return true;
}
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
auto val=static_cast<int>(input->readLong(1)); // find 2-4
f << "f0=" << val << ",";
val=static_cast<int>(input->readLong(1)); // always 0
if (val)
f << "f1=" << val << ",";
auto N=int(entry.length()/6);
for (int i=0; i<N; ++i) {
// v0: last used version, v1: first used version, ... ?
f << "v" << i << "=" << input->readLong(1);
val = static_cast<int>(input->readULong(1));
if (val)
f << "." << val;
val = static_cast<int>(input->readULong(1)); // 20|60|80
if (val != 0x80)
f << ":" << std::hex << val << std::dec;
for (int j=0; j<3; ++j) { // often 0 or small number
val = static_cast<int>(input->readULong(1));
if (val)
f << ":" << val << "[" << j << "]";
}
f << ",";
}
ascFile.addPos(entry.begin());
ascFile.addNote(f.str().c_str());
return true;
}
////////////////////////////////////////////////////////////
// find the different zones in a OLE1 struct files
////////////////////////////////////////////////////////////
bool RagTime5Parser::findDataZones(MWAWEntry const &entry)
{
libmwaw::DebugStream f;
MWAWInputStreamPtr input = getInput();
long pos=entry.begin();
if (!input->checkPosition(entry.end())) {
MWAW_DEBUG_MSG(("RagTime5Parser::findDataZones: main entry seems too bad\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return false;
}
int n=0;
input->seek(pos, librevenge::RVNG_SEEK_SET);
std::shared_ptr<RagTime5Zone> actualZone, actualChildZone; // the actual zone
while (!input->isEnd()) {
pos=input->tell();
if (pos>=entry.end()) break;
auto level=static_cast<int>(input->readULong(1));
if (level==0x18) {
while (input->tell()<entry.end()) {
if (input->readULong(1)==0xFF)
continue;
input->seek(-1, librevenge::RVNG_SEEK_CUR);
break;
}
ascii().addPos(pos);
ascii().addNote("_");
continue;
}
f.str("");
std::shared_ptr<RagTime5Zone> zone(new RagTime5Zone(input, ascii()));
zone->m_defPosition=pos;
zone->m_level=level;
// level=3: 0001, 59-78 + sometimes g4=[_,1]
if (pos+4>entry.end() || level < 1 || level > 3) {
zone->m_extra=f.str();
if (n++==0)
f << "Entries(Zones)[1]:";
else
f << "Zones-" << n << ":";
f << *zone << "###";
MWAW_DEBUG_MSG(("RagTime5Parser::findDataZones: find unknown level\n"));
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
break;
}
for (int i=0; i<4-level; ++i) {
zone->m_idsFlag[i]=static_cast<int>(input->readULong(2)); // alway 0/1?
zone->m_ids[i]=static_cast<int>(input->readULong(2));
}
bool ok=true;
do {
auto type2=static_cast<int>(input->readULong(1));
switch (type2) {
case 4: // always 0, 1
case 0xa: // always 0, 0: never seens in v5 but frequent in v6
case 0xb: { // find in some pc file
ok = input->tell()+4+(type2==4 ? 1 : 0)<=entry.end();
if (!ok) break;
int data[2];
for (int &i : data)
i=static_cast<int>(input->readULong(2));
if (type2==4) {
if (data[0]==0 && data[1]==1)
f << "selected,";
else if (data[0]==0)
f << "#selected=" << data[1] << ",";
else
f << "#selected=[" << data[0] << "," << data[1] << "],";
}
else
f << "g" << std::hex << type2 << std::dec << "=[" << data[0] << "," << data[1] << "],";
break;
}
case 5:
case 6: { // 6 entry followed by other data
ok = input->tell()+8+(type2==6 ? 1 : 0)<=entry.end();
if (!ok) break;
MWAWEntry zEntry;
zEntry.setBegin(long(input->readULong(4)));
zEntry.setLength(long(input->readULong(4)));
zone->m_entriesList.push_back(zEntry);
break;
}
case 9:
ok=input->tell()<=entry.end();
break;
case 0xd: // always 0 || c000
ok = input->tell()+4<=entry.end();
if (!ok) break;
for (int &i : zone->m_variableD)
i=static_cast<int>(input->readULong(2));
break;
case 0x18:
while (input->tell()<entry.end()) {
if (input->readULong(1)==0xFF)
continue;
input->seek(-1, librevenge::RVNG_SEEK_CUR);
break;
}
ok=input->tell()+1<entry.end();
break;
default:
ok=false;
MWAW_DEBUG_MSG(("RagTime5Parser::findDataZones: find unknown type2=%d\n", type2));
f << "type2=" << type2 << ",";
break;
}
if (!ok || (type2&1) || (type2==0xa))
break;
}
while (1);
switch (zone->m_level) {
case 1:
actualZone=zone;
actualChildZone.reset();
break;
case 2:
if (!actualZone || actualZone->m_childIdToZoneMap.find(zone->m_ids[0])!=actualZone->m_childIdToZoneMap.end()) {
MWAW_DEBUG_MSG(("RagTime5Parser::findDataZones: can not add child to a zone\n"));
f << "##badChild";
}
else {
zone->m_parentName=actualZone->getZoneName();
actualZone->m_childIdToZoneMap[zone->m_ids[0]]=zone;
}
actualChildZone=zone;
break;
case 3:
if (!actualChildZone || actualChildZone->m_childIdToZoneMap.find(zone->m_ids[0])!=actualChildZone->m_childIdToZoneMap.end()) {
// checkme: can happen in 6.0 files after a jpeg picture with level 1, ...
MWAW_DEBUG_MSG(("RagTime5Parser::findDataZones: can not add child to a zone\n"));
f << "#noparent";
}
else {
zone->m_parentName=actualChildZone->getZoneName();
actualChildZone->m_childIdToZoneMap[zone->m_ids[0]]=zone;
}
break;
default:
break;
}
m_state->m_zonesList.push_back(zone);
zone->m_extra=f.str();
f.str("");
if (n++==0)
f << "Entries(Zones)[1]:";
else
f << "Zones-" << n << ":";
f << *zone;
if (!ok) {
MWAW_DEBUG_MSG(("RagTime5Parser::findDataZones: find unknown data\n"));
f << "###";
if (input->tell()!=pos)
ascii().addDelimiter(input->tell(),'|');
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
break;
}
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
return true;
}
////////////////////////////////////////////////////////////
//
// Low level
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// color map
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// read the header
////////////////////////////////////////////////////////////
bool RagTime5Parser::checkHeader(MWAWHeader *header, bool strict)
{
*m_state = RagTime5ParserInternal::State();
MWAWInputStreamPtr input = getInput();
if (!input || !input->hasDataFork())
return false;
libmwaw::DebugStream f;
f << "FileHeader:";
if (!input->checkPosition(32)) {
MWAW_DEBUG_MSG(("RagTime5Parser::checkHeader: file is too short\n"));
return false;
}
input->seek(0,librevenge::RVNG_SEEK_SET);
if (input->readULong(4)!=0x43232b44 || input->readULong(4)!=0xa4434da5
|| input->readULong(4)!=0x486472d7)
return false;
int val;
for (int i=0; i<3; ++i) {
val=static_cast<int>(input->readLong(2));
if (val!=i) f << "f" << i << "=" << val << ",";
}
val=static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "f3=" << val << ",";
m_state->m_zonesEntry.setBegin(long(input->readULong(4)));
m_state->m_zonesEntry.setLength(long(input->readULong(4)));
if (m_state->m_zonesEntry.length()<137 ||
!input->checkPosition(m_state->m_zonesEntry.begin()+137))
return false;
if (strict && !input->checkPosition(m_state->m_zonesEntry.end()))
return false;
val=static_cast<int>(input->readLong(1));
if (val==1)
f << "compacted,";
else if (val)
f << "g0=" << val << ",";
val=static_cast<int>(input->readLong(1));
setVersion(5);
switch (val) {
case 0:
f << "vers=5,";
break;
case 4:
f << "vers=6.5,";
setVersion(6);
break;
default:
f << "#vers=" << val << ",";
break;
}
for (int i=0; i<2; ++i) {
val=static_cast<int>(input->readLong(1));
if (val) f << "g" << i+1 << "=" << val << ",";
}
// ok, we can finish initialization
if (header)
header->reset(MWAWDocument::MWAW_T_RAGTIME, version());
ascii().addPos(0);
ascii().addNote(f.str().c_str());
ascii().addPos(input->tell());
ascii().addNote("_");
return true;
}
////////////////////////////////////////////////////////////
// send data to the listener
////////////////////////////////////////////////////////////
bool RagTime5Parser::sendZones()
{
MWAWListenerPtr listener=getMainListener();
if (!listener) {
MWAW_DEBUG_MSG(("RagTime5Parser::sendZones: can not find the listener\n"));
return false;
}
if (m_state->m_hasLayout)
m_layoutParser->sendPageContents();
else {
MWAW_DEBUG_MSG(("RagTime5Parser::sendZones: no layout, try to send the main zones\n"));
m_clusterManager->sendClusterMainList();
}
return true;
}
bool RagTime5Parser::send(int zoneId, MWAWListenerPtr listener, MWAWPosition const &pos, int partId, int part2Id)
{
if (m_state->m_sendZoneSet.find(zoneId)!=m_state->m_sendZoneSet.end()) {
MWAW_DEBUG_MSG(("RagTime5Parser::send: argh zone %d is already in the sent set\n", zoneId));
return false;
}
m_state->m_sendZoneSet.insert(zoneId);
auto type=m_clusterManager->getClusterType(zoneId);
bool ok=false;
if (type==RagTime5ClusterManager::Cluster::C_GraphicZone || type==RagTime5ClusterManager::Cluster::C_PictureZone)
ok=m_graphParser->send(zoneId, listener, pos);
else if (type==RagTime5ClusterManager::Cluster::C_TextZone)
ok=m_textParser->send(zoneId, listener, partId, part2Id);
else if (type==RagTime5ClusterManager::Cluster::C_SpreadsheetZone)
ok=m_spreadsheetParser->send(zoneId, listener, pos, partId);
else if (type==RagTime5ClusterManager::Cluster::C_Pipeline)
ok=m_pipelineParser->send(zoneId, listener, pos, partId);
m_state->m_sendZoneSet.erase(zoneId);
if (ok)
return true;
static bool first=true;
if (first) {
MWAW_DEBUG_MSG(("RagTime5Parser::send: not fully implemented\n"));
first=false;
}
return false;
}
void RagTime5Parser::flushExtra()
{
MWAWListenerPtr listener=getMainListener();
if (!listener) {
MWAW_DEBUG_MSG(("RagTime5Parser::flushExtra: can not find the listener\n"));
return;
}
m_textParser->flushExtra();
m_graphParser->flushExtra();
m_spreadsheetParser->flushExtra();
// look for unparsed data
int notRead=0;
for (auto zone : m_state->m_zonesList) {
if (!zone || zone->m_isParsed || !zone->m_entry.valid())
continue;
ascii().addPos(zone->m_defPosition);
ascii().addNote("[notParsed]");
readZoneData(*zone);
++notRead;
}
if (notRead) {
MWAW_DEBUG_MSG(("RagTime5Parser::flushExtra: find %d/%d unparsed data\n", notRead, static_cast<int>(m_state->m_zonesList.size())));
}
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: