/* -*- 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 #include #include #include #include #include #include #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(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 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 getIdList() const { std::vector 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 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 m_linkList; //! the name std::map 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 > m_zonesList; //! a map id to type string std::map m_idToTypeMap; //! the zone info zone (ie. the first zone) std::shared_ptr m_zoneInfo; //! a map: data id->entry (datafork) std::map > m_dataIdZoneMap; //! a map: page->main zone id std::map > m_pageZonesIdMap; //! a set used to avoid looping when sending zone std::set 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 RagTime5Parser::getClusterManager() { return m_clusterManager; } std::shared_ptr RagTime5Parser::getStructManager() { return m_structManager; } std::shared_ptr RagTime5Parser::getStyleManager() { return m_styleManager; } std::shared_ptr RagTime5Parser::readChartCluster(RagTime5Zone &zone, int zoneType) { return m_chartParser->readChartCluster(zone, zoneType); } std::shared_ptr RagTime5Parser::readGraphicCluster(RagTime5Zone &zone, int zoneType) { return m_graphParser->readGraphicCluster(zone, zoneType); } std::shared_ptr RagTime5Parser::readLayoutCluster(RagTime5Zone &zone, int zoneType) { return m_layoutParser->readLayoutCluster(zone, zoneType); } std::shared_ptr RagTime5Parser::readPipelineCluster(RagTime5Zone &zone, int zoneType) { return m_pipelineParser->readPipelineCluster(zone, zoneType); } std::shared_ptr RagTime5Parser::readPictureCluster(RagTime5Zone &zone, int zoneType) { return m_graphParser->readPictureCluster(zone, zoneType); } std::shared_ptr RagTime5Parser::readSpreadsheetCluster(RagTime5Zone &zone, int zoneType) { return m_spreadsheetParser->readSpreadsheetCluster(zone, zoneType); } std::shared_ptr RagTime5Parser::readTextCluster(RagTime5Zone &zone, int zoneType) { return m_textParser->readTextCluster(zone, zoneType); } std::shared_ptr RagTime5Parser::getDataZone(int dataId) const { if (m_state->m_dataIdZoneMap.find(dataId)==m_state->m_dataIdZoneMap.end()) return std::shared_ptr(); 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 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; im_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; im_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 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 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; ireadULong(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 &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 &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; ireadLong(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 &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 &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 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 listChilds; m_clusterManager->readClusterMainList(cluster, listChilds, listClusters); std::set 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 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 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 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 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 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 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(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(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 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 &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 &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; itell(); f.str(""); f << zoneName << "-" << i+1 << ":"; RagTime5StructManager::ZoneLink cLink; std::vector 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(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(input->readULong(2)); if (val) f << "f" << i << "=" << val << ","; } auto sSz=static_cast(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; ireadULong(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(input->readULong(2)); if (val) f << std::hex << val << std::dec << ","; else f << "_,"; } f << "],"; sSz=static_cast(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; ireadULong(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(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(input->readLong(2)); if (val) f << "f" << i << "=" << val << ","; } val=static_cast(input->readLong(1)); // 0 if (val) f << "f11=" << val << ","; sSz=static_cast(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; ireadULong(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(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(input->readLong(4)); if (val) f << std::hex << val << std::dec << ","; else f << "_,"; } f << "],"; val=static_cast(input->readLong(2)); // always 2 if (val!=2) f << "f0=" << val << ","; sSz=static_cast(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; ireadULong(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 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 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 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; ilastPos || 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; itell(); 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 idToNameMap; if (!cluster.m_nameLink.empty()) { readUnicodeStringList(cluster.m_nameLink, idToNameMap); cluster.m_nameLink=RagTime5ClusterManager::Link(); } std::vector 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; iendPos) { 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(input->readLong(4)); if (val!=1) f << "numUsed=" << val << ","; f << "f1=" << std::hex << input->readULong(2) << std::dec << ","; val=static_cast(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(input->readLong(2)); if (val!=1) f << "numUsed=" << val << ","; val=static_cast(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(input->readLong(4)); // 1 or 3 if (val!=1) f << "numUsed?=" << val << ","; val=static_cast(input->readLong(4)); // always 0 if (val) f << "f0=" << val << ","; f << "ID=" << std::hex << input->readULong(4) << ","; // a big number val=static_cast(input->readLong(4)); if (val!=0x1f6817) // doc info type f << "type=" << std::hex << val << std::dec << ","; val=static_cast(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 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(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(entry.length()))); else newStream->append(dt, static_cast(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 &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(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 > mapToString; mapToString.reserve(size_t(entry.length()-6)); bool ok=false; while (!input->isEnd()) { if (static_cast(mapPos)==(1<tell()>=endPos) { MWAW_DEBUG_MSG(("RagTime5Parser::unpackZone: oops can not find last data\n")); ok=false; break; } do { read = (read<<8)+static_cast(input->readULong(1)); nBytesRead+=8; } while (nBytesRead> (nBytesRead-szField)); nBytesRead-=szField; read &= ((1<(val); data.push_back(c); if (mapPos>= mapToString.size()) mapToString.resize(mapPos+1); mapToString[mapPos++]=std::vector(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 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 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 newStream(new MWAWStringStream(&newData[0], static_cast(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(input->readLong(1)); // find 2-4 f << "f0=" << val << ","; val=static_cast(input->readLong(1)); // always 0 if (val) f << "f1=" << val << ","; auto N=int(entry.length()/6); for (int i=0; i(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(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 actualZone, actualChildZone; // the actual zone while (!input->isEnd()) { pos=input->tell(); if (pos>=entry.end()) break; auto level=static_cast(input->readULong(1)); if (level==0x18) { while (input->tell()readULong(1)==0xFF) continue; input->seek(-1, librevenge::RVNG_SEEK_CUR); break; } ascii().addPos(pos); ascii().addNote("_"); continue; } f.str(""); std::shared_ptr 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(input->readULong(2)); // alway 0/1? zone->m_ids[i]=static_cast(input->readULong(2)); } bool ok=true; do { auto type2=static_cast(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(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(input->readULong(2)); break; case 0x18: while (input->tell()readULong(1)==0xFF) continue; input->seek(-1, librevenge::RVNG_SEEK_CUR); break; } ok=input->tell()+1m_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(input->readLong(2)); if (val!=i) f << "f" << i << "=" << val << ","; } val=static_cast(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(input->readLong(1)); if (val==1) f << "compacted,"; else if (val) f << "g0=" << val << ","; val=static_cast(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(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(m_state->m_zonesList.size()))); } } // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: