/* -*- 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 <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include "MWAWDebug.hxx"
#include "MWAWPosition.hxx"
#include "RagTime5Parser.hxx"
#include "RagTime5StructManager.hxx"
#include "RagTime5ClusterManager.hxx"
/** Internal: the structures of a RagTime5ClusterManager */
namespace RagTime5ClusterManagerInternal
{
//! cluster information
struct ClusterInformation {
//! constructor
ClusterInformation()
: m_type(-1)
, m_fileType(-1)
, m_name("")
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, ClusterInformation const &info)
{
switch (info.m_type) {
case 0:
o << "root,";
break;
case 0x1:
o << "layout,";
break;
case 0x2:
o << "script,";
break;
// case 0xe: mainTextZone? either a graphic zone or a text zone
case 0x42:
o << "colPat,";
break;
case 0x80:
o << "style,";
break;
case 0x104:
o << "pipeline,";
break;
case 0x10000:
o << "gObjProp,";
break;
case 0x20000:
o << "fieldDef,";
break;
case 0x20001:
o << "fieldPos,";
break;
case 0x30000:
o << "unkC_A,";
break;
case 0x30001:
o << "unkC_B,";
break;
case 0x30002:
o << "unkC_C,";
break;
case 0x30003:
o << "unkC_D,";
break;
case 0x40000:
o << "picture,";
break;
case 0x40001:
o << "graphic,";
break;
case 0x40002:
o << "spreadsheet,";
break;
case 0x40003:
o << "text,";
break;
case 0x40004:
o << "chart,";
break;
default:
if (info.m_fileType>=0)
o << "typ=" << std::hex << std::hex << info.m_fileType << std::dec << ",";
}
if (!info.m_name.empty())
o << info.m_name.cstr() << ",";
return o;
}
//! the cluster type
int m_type;
//! the cluster file type
int m_fileType;
//! the cluster name
librevenge::RVNGString m_name;
};
//! Internal: the state of a RagTime5ClusterManager
struct State {
//! constructor
State()
: m_idToClusterInfoMap()
, m_idToClusterMap()
, m_rootIdList()
{
}
//! map id to cluster information map
std::map<int, ClusterInformation> m_idToClusterInfoMap;
//! map id to cluster map
std::map<int, std::shared_ptr<RagTime5ClusterManager::Cluster> > m_idToClusterMap;
//! the root id list
std::vector<int> m_rootIdList;
};
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
RagTime5ClusterManager::RagTime5ClusterManager(RagTime5Parser &parser)
: m_state(new RagTime5ClusterManagerInternal::State)
, m_mainParser(parser)
, m_structManager(m_mainParser.getStructManager())
{
}
RagTime5ClusterManager::~RagTime5ClusterManager()
{
}
RagTime5ClusterManager::Cluster::~Cluster()
{
}
RagTime5ClusterManager::ClusterParser::~ClusterParser()
{
}
RagTime5ClusterManager::ClusterRoot::~ClusterRoot()
{
}
RagTime5ClusterManager::ClusterScript::~ClusterScript()
{
}
RagTime5ClusterManager::Cluster::Type RagTime5ClusterManager::getClusterType(int zId) const
{
if (m_state->m_idToClusterMap.find(zId) == m_state->m_idToClusterMap.end() ||
!m_state->m_idToClusterMap.find(zId)->second) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::getClusterType: can not find cluster type for zone %d\n", zId));
return RagTime5ClusterManager::Cluster::C_Unknown;
}
return m_state->m_idToClusterMap.find(zId)->second->m_type;
}
////////////////////////////////////////////////////////////
// read basic structures
////////////////////////////////////////////////////////////
bool RagTime5ClusterManager::readFieldHeader(RagTime5Zone &zone, long endPos, std::string const &headerName, long &endDataPos, long expectedLVal)
{
MWAWInputStreamPtr input=zone.getInput();
long pos=input->tell();
libmwaw::DebugFile &ascFile=zone.ascii();
libmwaw::DebugStream f;
f << headerName << ":";
long lVal, sz;
bool ok=true;
if (pos>=endPos || !RagTime5StructManager::readCompressedLong(input, endPos, lVal) ||
!RagTime5StructManager::readCompressedLong(input,endPos,sz) || sz <= 7 || input->tell()+sz>endPos) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readFieldHeader: can not read the main item\n"));
f << "###";
ok=false;
}
else {
if (lVal!=expectedLVal)
f << "f0=" << lVal << ",";
f << "sz=" << sz << ",";
endDataPos=input->tell()+sz;
}
if (!headerName.empty()) {
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
}
return ok;
}
std::string RagTime5ClusterManager::getClusterName(int id)
{
if (!id) return "";
std::stringstream s;
s << "data" << id << "A";
if (m_state->m_idToClusterInfoMap.find(id)!=m_state->m_idToClusterInfoMap.end())
s << "[" << m_state->m_idToClusterInfoMap.find(id)->second << "]";
return s.str();
}
////////////////////////////////////////////////////////////
// link to cluster
////////////////////////////////////////////////////////////
bool RagTime5ClusterManager::readClusterMainList(RagTime5ClusterManager::ClusterRoot &root, std::vector<int> &lists, std::vector<int> const &clusterIdList)
{
std::map<int, librevenge::RVNGString> idToNameMap;
if (!root.m_listClusterName.empty())
m_mainParser.readUnicodeStringList(root.m_listClusterName, idToNameMap);
std::vector<long> unknList;
if (!root.m_listClusterLink[0].empty())
m_mainParser.readLongList(root.m_listClusterLink[0], unknList);
auto zone=m_mainParser.getDataZone(root.m_listClusterId);
if (!zone || zone->getKindLastPart(zone->m_kinds[1].empty())!="ItemData" ||
zone->m_entry.length()<24 || (zone->m_entry.length()%8)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readClusterMainList: the item list seems bad\n"));
return false;
}
MWAWEntry &entry=zone->m_entry;
zone->m_isParsed=true;
MWAWInputStreamPtr input=zone->getInput();
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
input->setReadInverted(!zone->m_hiLoEndian);
libmwaw::DebugFile &ascFile=zone->ascii();
libmwaw::DebugStream f;
ascFile.addPos(entry.end());
ascFile.addNote("_");
auto N=int(entry.length()/8);
for (int i=0; i<N; ++i) {
long pos=input->tell();
f.str("");
if (i==0)
f << "Entries(RootClustMain)[" << *zone << "]:";
else
f << "RootClustMain-" << i+1 << ":";
librevenge::RVNGString name("");
if (idToNameMap.find(i+1)!=idToNameMap.end()) {
name=idToNameMap.find(i+1)->second;
f << name.cstr() << ",";
}
std::vector<int> listIds;
if (!m_structManager->readDataIdList(input, 1, listIds)) {
input->seek(pos+8, librevenge::RVNG_SEEK_SET);
f << "###";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
continue;
}
if (listIds[0]==0) {
input->seek(pos+8, librevenge::RVNG_SEEK_SET);
ascFile.addPos(pos);
ascFile.addNote("_");
continue;
}
f << "data" << listIds[0] << "A,";
m_state->m_rootIdList.push_back(listIds[0]);
auto val=static_cast<int>(input->readULong(2)); // the type
if (val) f << "type=" << std::hex << val << std::dec << ",";
RagTime5ClusterManagerInternal::ClusterInformation info;
info.m_fileType=val;
info.m_name=name;
m_state->m_idToClusterInfoMap[listIds[0]]=info;
lists.push_back(listIds[0]);
val=static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "#f1=" << val << ",";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
}
input->setReadInverted(false);
// update the cluster info zone
for (auto cId : clusterIdList) {
auto data=m_mainParser.getDataZone(cId);
if (!data) continue;
if (m_state->m_idToClusterInfoMap.find(cId)==m_state->m_idToClusterInfoMap.end()) {
RagTime5ClusterManagerInternal::ClusterInformation info;
info.m_fileType=getClusterFileType(*data);
info.m_type=getClusterType(*data, info.m_fileType);
m_state->m_idToClusterInfoMap[cId]=info;
continue;
}
auto &info=m_state->m_idToClusterInfoMap.find(cId)->second;
info.m_type=getClusterType(*data, info.m_fileType);
}
return true;
}
bool RagTime5ClusterManager::readFieldClusters(Link const &link)
{
if (link.m_ids.size()!=2) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readFieldClusters: call with bad ids\n"));
return false;
}
for (size_t i=0; i<2; ++i) { // fielddef and fieldpos
if (!link.m_ids[i]) continue;
auto data=m_mainParser.getDataZone(link.m_ids[i]);
if (!data || data->m_isParsed || data->getKindLastPart(data->m_kinds[1].empty())!="Cluster") {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readFieldClusters: the child cluster id %d seems bad\n", link.m_ids[i]));
continue;
}
m_mainParser.readClusterZone(*data, 0x20000+int(i));
}
return true;
}
bool RagTime5ClusterManager::readUnknownClusterC(Link const &link)
{
if (link.m_ids.size()!=4) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readUnknownClusterC: call with bad ids\n"));
return false;
}
for (size_t i=0; i<4; ++i) {
if (!link.m_ids[i]) continue;
auto data=m_mainParser.getDataZone(link.m_ids[i]);
if (!data || data->m_isParsed || data->getKindLastPart(data->m_kinds[1].empty())!="Cluster") {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readUnknownClusterC: the child cluster id %d seems bad\n", link.m_ids[i]));
continue;
}
m_mainParser.readClusterZone(*data, 0x30000+int(i));
}
return true;
}
////////////////////////////////////////////////////////////
// main cluster fonction
////////////////////////////////////////////////////////////
bool RagTime5ClusterManager::readCluster(RagTime5Zone &zone, RagTime5ClusterManager::ClusterParser &parser, bool warnForUnparsed)
{
MWAWEntry &entry=zone.m_entry;
if (entry.length()<13) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: the zone %d seems bad\n", zone.m_ids[0]));
return false;
}
auto cluster=parser.getCluster();
if (!cluster) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: oops, the cluster is not defined\n"));
return false;
}
cluster->m_hiLoEndian=parser.m_hiLoEndian=zone.m_hiLoEndian;
cluster->m_zoneId=zone.m_ids[0];
MWAWInputStreamPtr input=zone.getInput();
long endPos=entry.end();
input->setReadInverted(!zone.m_hiLoEndian);
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f.str("");
f << "Entries(" << parser.getZoneName() << ")[" << zone << "]:";
if (m_state->m_idToClusterInfoMap.find(zone.m_ids[0])!=m_state->m_idToClusterInfoMap.end() &&
!m_state->m_idToClusterInfoMap.find(zone.m_ids[0])->second.m_name.empty())
f << m_state->m_idToClusterInfoMap.find(zone.m_ids[0])->second.m_name.cstr() << ",";
for (int i=0; i<4; ++i) { // f0=f1=0, f2=1, f3=small number
static int const expected[]= {0,0,1,0};
auto val=static_cast<int>(input->readLong(2));
if (val!=expected[i]) f << "f" << i << "=" << val << ",";
}
zone.m_isParsed=true;
libmwaw::DebugFile &ascFile=zone.ascii();
ascFile.addPos(entry.begin());
ascFile.addNote(f.str().c_str());
ascFile.addPos(endPos);
ascFile.addNote("_");
// first create the list of zone to parse
parser.m_dataId=-1;
std::map<int, MWAWEntry> idToEntryMap;
std::set<int> toParseSet;
MWAWEntry zEntry;
while (!input->isEnd()) {
long pos=input->tell();
if (pos>=endPos) break;
long endDataPos;
if (!readFieldHeader(zone, endPos, parser.getZoneName(++parser.m_dataId), endDataPos) ||
!input->checkPosition(endDataPos)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
zEntry.setId(parser.m_dataId);
zEntry.setBegin(input->tell());
zEntry.setEnd(endDataPos);
idToEntryMap[parser.m_dataId]=zEntry;
toParseSet.insert(parser.m_dataId);
input->seek(endDataPos, librevenge::RVNG_SEEK_SET);
}
long pos=input->tell();
if (pos!=endPos) {
f.str("");
f << parser.getZoneName() << "###";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
}
while (!toParseSet.empty()) {
int id=parser.getNewZoneToParse();
if (id>=0 && toParseSet.find(id)==toParseSet.end()) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: zone %d is not valid, reset to basic method\n", id));
id=-1;
}
if (id<0)
id=*(toParseSet.begin());
toParseSet.erase(id);
auto it=idToEntryMap.find(id);
if (it==idToEntryMap.end()) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: can not find some id=%d\n", id));
continue;
}
parser.m_dataId=id;
parser.m_link=Link();
parser.startZone();
pos=it->second.begin();
long endDataPos=it->second.end();
input->seek(pos, librevenge::RVNG_SEEK_SET);
f.str("");
f << parser.getZoneName(parser.m_dataId) << "-A:";
if (!zone.m_hiLoEndian) f << "lohi,";
long fSz;
if (!RagTime5StructManager::readCompressedLong(input, endDataPos, fSz) || fSz<6 || input->tell()+fSz>endDataPos) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: can not read item A\n"));
f << "###fSz";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
input->seek(endDataPos, librevenge::RVNG_SEEK_SET);
continue;
}
long debSubDataPos=input->tell();
long endSubDataPos=debSubDataPos+fSz;
auto fl=static_cast<int>(input->readULong(2)); // [01][13][0139b]
auto N=static_cast<int>(input->readLong(4));
if (!parser.parseZone(input, fSz, N, fl, f) && warnForUnparsed) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: find an unparsed zone\n"));
f << "###";
}
if (input->tell()!=endSubDataPos) {
ascFile.addDelimiter(input->tell(),'|');
input->seek(endSubDataPos, librevenge::RVNG_SEEK_SET);
}
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
int m=-1;
while (!input->isEnd()) {
pos=input->tell();
if (pos+4>endDataPos)
break;
++m;
RagTime5StructManager::Field field;
if (!m_structManager->readField(input, endDataPos, ascFile, field)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
f.str("");
f << parser.getZoneName(parser.m_dataId,m) << ":";
if (!parser.parseField(field, m, f)) {
if (warnForUnparsed) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: find an unparsed field\n"));
f << "###";
}
f << field;
}
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
}
pos=input->tell();
if (pos!=endDataPos) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: find some extra data\n"));
f.str("");
f << parser.getZoneName(parser.m_dataId) << ":" << "###";
ascFile.addPos(pos);
ascFile.addNote(f.str().c_str());
}
parser.endZone();
input->seek(endDataPos, librevenge::RVNG_SEEK_SET);
}
input->setReadInverted(false);
return true;
}
////////////////////////////////////////////////////////////
// send data
////////////////////////////////////////////////////////////
bool RagTime5ClusterManager::sendClusterMainList()
{
MWAWPosition pos(MWAWVec2f(0,0), MWAWVec2f(200,200), librevenge::RVNG_POINT);
pos.m_anchorTo=MWAWPosition::Char;
for (auto id : m_state->m_rootIdList) {
if (!id) continue;
if (m_state->m_idToClusterMap.find(id) == m_state->m_idToClusterMap.end() ||
!m_state->m_idToClusterMap.find(id)->second) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::sendClusterMainList: can not find cluster type for zone %d\n", id));
continue;
}
auto cluster=m_state->m_idToClusterMap.find(id)->second;
if (cluster->m_isSent) continue;
auto type=cluster->m_type;
if (type==Cluster::C_ChartZone|| type==Cluster::C_GraphicZone || type==Cluster::C_PictureZone ||
type==Cluster::C_SpreadsheetZone || type==Cluster::C_TextZone)
m_mainParser.send(id, MWAWListenerPtr(), pos);
}
return true;
}
////////////////////////////////////////////////////////////
// pattern cluster implementation
////////////////////////////////////////////////////////////
std::string RagTime5ClusterManager::ClusterParser::getClusterName(int id)
{
return m_parser.getClusterName(id);
}
bool RagTime5ClusterManager::ClusterParser::readLinkHeader(MWAWInputStreamPtr &input, long fSz, Link &link, long(&values)[4], std::string &msg)
{
if (fSz<28)
return false;
long pos=input->tell();
std::stringstream s;
link.m_fileType[0]=long(input->readULong(4));
bool shortFixed=link.m_fileType[0]==0x3c052 ||
(fSz<30 && (link.m_fileType[0]==0x34800||link.m_fileType[0]==0x35800||link.m_fileType[0]==0x3e800));
if (shortFixed) {
link.m_type=RagTime5ClusterManager::Link::L_LongList;
link.m_fieldSize=4;
}
else if (fSz<30) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
if (link.m_fileType[0])
s << "type1=" << std::hex << link.m_fileType[0] << std::dec << ",";
values[0]=long(input->readULong(4));
if (values[0])
s << "f0=" << std::hex << values[0] << std::dec << ",";
for (int i=1; i<3; ++i) { // always 0?
values[i]=input->readLong(2);
if (values[i]) s << "f" << i << "=" << values[i] << ",";
}
values[3]=long(input->readULong(4));
if (values[3]) s << "f3=" << std::hex << values[3] << std::dec << ",";
link.m_fileType[1]=long(input->readULong(2));
bool done=false;
if (!shortFixed) {
link.m_fieldSize=static_cast<int>(input->readULong(2));
if (link.m_fieldSize==0 || link.m_fieldSize==1 || link.m_fieldSize==0x100) {
if (fSz<32) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
input->seek(-2, librevenge::RVNG_SEEK_CUR);
if (!RagTime5StructManager::readDataIdList(input, 2, link.m_ids) || !link.m_ids[1]) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
link.m_fieldSize=0;
link.m_type= RagTime5ClusterManager::Link::L_List;
done=true;
}
else if ((link.m_fieldSize%2)!=0 || link.m_fieldSize>=0x100) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
}
if (!done && !RagTime5StructManager::readDataIdList(input, 1, link.m_ids)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
if ((link.m_ids[0]==0 && (link.m_fileType[1]&0x20)==0 && link.m_N) ||
(link.m_ids[0] && (link.m_fileType[1]&0x20)!=0)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
return false;
}
link.m_fileType[1] &=0xFFDF;
msg=s.str();
return true;
}
namespace RagTime5ClusterManagerInternal
{
//
//! low level: parser of color pattern cluster : zone 0x8042
//
struct ColPatCParser final : public RagTime5ClusterManager::ClusterParser {
//! constructor
explicit ColPatCParser(RagTime5ClusterManager &parser)
: ClusterParser(parser, 0x8042, "ClustColPat")
, m_cluster(new RagTime5ClusterManager::Cluster(RagTime5ClusterManager::Cluster::C_ColorPattern))
{
}
//! destructor
~ColPatCParser() final;
//! return the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> getCluster() final
{
return m_cluster;
}
//! parse a zone
bool parseZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f) final
{
if ((m_dataId==0&&flag!=0x30) || (m_dataId==1&&flag!=0x10) || m_dataId>=2)
f << "fl=" << std::hex << flag << std::dec << ",";
if (N==-5) {
int val;
if (m_dataId || (fSz!=82 && fSz!=86)) {
f << "###data,";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ColPatCParser::parseZone: find unexpected field\n"));
return false;
}
for (int i=0; i<2; ++i) { // always 0?
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+1 << "=" << val << ",";
}
val=static_cast<int>(input->readULong(4));
if (val!=0x16a8042) f << "#fileType=" << std::hex << val << std::dec << ",";
for (int i=0; i<2; ++i) {
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+3 << "=" << val << ",";
}
for (int wh=0; wh<2; ++wh) {
long actPos=input->tell();
RagTime5ClusterManager::Link link;
f << "link" << wh << "=[";
val=static_cast<int>(input->readLong(2));
if (val!= 0x10)
f << "f0=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val) // always 0?
f << "f1=" << val << ",";
link.m_N=static_cast<int>(input->readLong(2));
link.m_fileType[1]=long(input->readULong(4));
if ((wh==0 && link.m_fileType[1]!=0x84040) ||
(wh==1 && link.m_fileType[1]!=0x16de842))
f << "#fileType=" << std::hex << link.m_fileType[1] << std::dec << ",";
for (int i=0; i<7; ++i) {
val=static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "f" << i+2 << "=" << val << ",";
}
link.m_fieldSize=static_cast<int>(input->readULong(2));
std::vector<int> listIds;
if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ColPatCParser::parseZone: can not read the data id\n"));
f << "##link=" << link << "],";
input->seek(actPos+30, librevenge::RVNG_SEEK_SET);
continue;
}
if (listIds[0]) {
link.m_ids.push_back(listIds[0]);
m_cluster->m_linksList.push_back(link);
}
f << link;
f << "],";
}
std::vector<int> listIds;
if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
f << "##clusterIds";
return true;
}
if (listIds[0]) {
m_cluster->m_clusterIdsList.push_back(listIds[0]);
f << "clusterId1=" << getClusterName(listIds[0]) << ",";
}
if (fSz==82)
return true;
val=static_cast<int>(input->readLong(4));
if (val!=2)
f << "g0=" << val << ",";
return true;
}
if (N<=0 || m_dataId!=1) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ColPatCParser::parseZone: find unexpected header N\n"));
f << "###N=" << N << ",";
return false;
}
if (fSz!=30) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ColPatCParser::parseZone: find unexpected data size\n"));
f << "###fSz=" << fSz << ",";
return false;
}
std::string mess;
RagTime5ClusterManager::Link link;
link.m_N=N;
long linkValues[4]; // f0=2b|2d|85|93
if (readLinkHeader(input,fSz,link,linkValues,mess) && link.m_fieldSize==10) {
if (link.m_fileType[1]!=0x40)
f << "###fileType1=" << std::hex << link.m_fileType[1] << std::dec << ",";
f << link << "," << mess;
if (!link.empty())
m_cluster->m_linksList.push_back(link);
}
else {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ColPatCParser::parseZone: can not read a link\n"));
f << "###link" << link << ",";
}
return true;
}
//! parse a field
bool parseField(RagTime5StructManager::Field const &field, int /*m*/, libmwaw::DebugStream &f) final
{
if (m_dataId==0 && field.m_type==RagTime5StructManager::Field::T_FieldList && (field.m_fileType==0x16be055 || field.m_fileType==0x16be065)) {
f << "unk" << (field.m_fileType==0x16be055 ? "0" : "1") << "=";
for (auto const &child : field.m_fieldList) {
if (child.m_type==RagTime5StructManager::Field::T_Long && child.m_fileType==0xcf817) {
f << child.m_longValue[0] << ",";
continue;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ColPatCParser::parseField: find unexpected color/pattern child field\n"));
f << "#[" << child << "],";
}
}
else {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ColPatCParser::parseField: find unexpected sub field\n"));
f << "#" << field;
}
return true;
}
protected:
//! the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> m_cluster;
};
ColPatCParser::~ColPatCParser()
{
}
//
//! try to read a root cluster: 4001
//
struct RootCParser final : public RagTime5ClusterManager::ClusterParser {
//! constructor
explicit RootCParser(RagTime5ClusterManager &parser)
: ClusterParser(parser, 0, "ClustRoot")
, m_cluster(new RagTime5ClusterManager::ClusterRoot)
, m_what(-1)
, m_expectedId(-1)
, m_linkId(-1)
, m_fieldName("")
{
}
//! destructor
~RootCParser() final;
//! return the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> getCluster() final
{
return m_cluster;
}
//! end of a start zone call
void endZone() final
{
if (m_link.empty())
return;
if (m_dataId==0) {
if (m_cluster->m_dataLink.empty())
m_cluster->m_dataLink=m_link;
else {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::endZone: oops the main link is already set\n"));
m_cluster->m_linksList.push_back(m_link);
}
}
else if (m_what==3)
m_cluster->m_graphicTypeLink=m_link;
else {
bool ok=true;
switch (m_linkId) {
case 0:
ok=m_cluster->m_listClusterName.empty();
m_cluster->m_listClusterName=m_link;
break;
case 1:
ok=m_cluster->m_docInfoLink.empty();
m_cluster->m_docInfoLink=m_link;
break;
case 2:
ok=m_cluster->m_linkUnknown.empty();
m_cluster->m_linkUnknown=m_link;
break;
case 3:
m_cluster->m_settingLinks.push_back(m_link);
break;
case 4:
m_cluster->m_conditionFormulaLinks.push_back(m_link);
break;
case 5:
case 6:
ok=m_cluster->m_listClusterLink[m_linkId-5].empty();
m_cluster->m_listClusterLink[m_linkId-5]=m_link;
break;
case 7:
ok=m_cluster->m_listUnicodeLink.empty();
m_cluster->m_listUnicodeLink=m_link;
break;
default:
m_cluster->m_linksList.push_back(m_link);
}
if (!ok) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::endZone: oops link %d is already set\n", m_linkId));
}
}
}
//! parse the header zone
bool parseZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f) final
{
m_what=m_linkId=-1;
m_fieldName="";
++m_expectedId;
if (m_dataId==0)
return parseHeaderZone(input, fSz, N, flag, f);
if (isANameHeader(N)) {
if (m_expectedId<5 || m_expectedId >7) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseZone: expected n seems bad\n"));
f << "#n=" << m_expectedId << ",";
m_expectedId=7;
}
f << "fileName,";
m_fieldName="filename";
m_what=1;
return true;
}
if (N<0) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseZone: expected N value\n"));
f << "###N=" << N << ",";
return true;
}
return parseDataZone(input, fSz, N, flag, f);
}
//! parse a field
bool parseField(RagTime5StructManager::Field const &field, int /*m*/, libmwaw::DebugStream &f) final
{
if (!m_fieldName.empty())
f << m_fieldName << ",";
switch (m_what) {
case 0: // main
if (field.m_type==RagTime5StructManager::Field::T_ZoneId && field.m_fileType==0x14510b7) {
if (field.m_longValue[0]) {
m_cluster->m_styleClusterIds[7]=static_cast<int>(field.m_longValue[0]);
f << "col/pattern[id]=dataA" << field.m_longValue[0] << ",";
}
break;
}
if (field.m_type==RagTime5StructManager::Field::T_Long && field.m_fileType==0x3c057) {
// small number between 8 and 10
if (field.m_longValue[0])
f << "unkn0=" << field.m_longValue[0] << ",";
break;
}
if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x1451025) {
f << "decal=[";
for (auto const &child : field.m_fieldList) {
if (child.m_type==RagTime5StructManager::Field::T_Unstructured && child.m_fileType==0xce017) {
// can be very long, seems to contain more 0 than 1
f << "unkn1="<<child.m_extra << ",";
continue;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseField: find unexpected decal child[main]\n"));
f << "###[" << child << "],";
}
f << "],";
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseField: find unexpected child[main]\n"));
f << "###" << field << ",";
break;
case 1: // filename
if (field.m_type==RagTime5StructManager::Field::T_Unicode && field.m_fileType==0xc8042) {
m_cluster->m_fileName=field.m_string.cstr();
f << field.m_string.cstr();
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseField: find unexpected filename field\n"));
f << "###" << field << ",";
break;
case 2: // list
if (field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0xce842) {
f << "pos=[";
for (auto val : field.m_longList) {
if (val==0)
f << "_,";
else if (val>1000)
f << std::hex << val << std::dec << ",";
else
f << val << ",";
}
f << "],";
m_link.m_longList=field.m_longList;
break;
}
if (field.m_type==RagTime5StructManager::Field::T_Unstructured && field.m_fileType==0xce017) {
// a small value 2|4|a|1c|40
f << "unkn="<<field.m_extra << ",";
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseField: find unexpected list link field\n"));
f << "###" << field << ",";
break;
case 3: // graph type
if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x14eb015) {
f << "decal=[";
for (auto const &child : field.m_fieldList) {
if (child.m_type==RagTime5StructManager::Field::T_LongList && child.m_fileType==0xce842) {
for (auto val : child.m_longList) {
if (val==0)
f << "_,";
else if (val>1000)
f << std::hex << val << std::dec << ",";
else
f << val << ",";
}
m_link.m_longList=child.m_longList;
continue;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseField: find unexpected decal child[graphType]\n"));
f << "###[" << child << "],";
}
f << "],";
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseField: find unexpected graph type field\n"));
f << "###" << field << ",";
break;
case 4:
if (field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0x154f017) {
f << "values=["; // find 1,1,2
for (auto val : field.m_longList) f << val << ",";
f << "],";
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseField: find unexpected child[fieldlists]\n"));
f << "###" << field << ",";
break;
default:
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseField: find unexpected field\n"));
f << "###" << field << ",";
break;
}
return true;
}
protected:
//! parse a data block
bool parseDataZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f)
{
f << "header, fl=" << std::hex << flag << std::dec << ",";
int val;
long pos=input->tell();
m_link.m_N=N;
switch (fSz) {
case 26: // in general block8 or 9 no auxiallary data
if (m_expectedId<8) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseZone: expected n seems bad\n"));
f << "###n=" << m_expectedId << ",";
m_expectedId=8;
}
m_fieldName="block1a";
val=static_cast<int>(input->readULong(4)); // small number or 0
if (val) f << "N2=" << val << ",";
m_link.m_fileType[0]=static_cast<int>(input->readULong(4));
if (m_link.m_fileType[0]!=0x14b4042) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: unexpected type for block1a\n"));
f << "##fileType=" << std::hex << m_link.m_fileType[0] << std::dec << ",";
}
for (int i=0; i<6; ++i) { // f4=c
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
break;
case 28: //expectedN==2 or near 9 or 10...
case 30: // field3 and other near 10
case 32: { // expectedN=1|(5|6)| after 8
long linkValues[4];
std::string mess;
if (!readLinkHeader(input, fSz, m_link, linkValues, mess)) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
if (fSz==30) {
val=static_cast<int>(input->readLong(4));
if (val) f << "N2=" << val << ",";
val=static_cast<int>(input->readULong(4));
if (val==0x15e5042) {
// first near n=9, second near n=15 with no other data
// no auxilliar data expected
m_fieldName="unknDataD";
for (int i=0; i<4; ++i) { // f0, f3: small number
val=static_cast<int>(input->readULong(4));
if (val) f << "f" << i << "=" << val << ",";
}
break;
}
}
f << "###fType=" << std::hex << m_link.m_fileType[0] << std::dec << ",";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseZone: the field fSz28... type seems bad\n"));
return true;
}
m_what=2;
f << m_link << "," << mess;
long expectedFileType1=-1, expectedFieldSize=-1;
if (m_link.m_fileType[0]==0x35800) {
f << "zone[longs],";
m_fieldName="zone:longs";
m_link.m_name="RootLongList1";
}
else if (m_link.m_fileType[0]==0x3e800) {
if (m_expectedId<2) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: unexpected expectedN for list[long]\n"));
f << "##expectedN=" << m_expectedId << ",";
m_expectedId=2;
}
if (m_expectedId==2)
m_linkId=5;
f << "list[long0],";
m_fieldName="list:longs0";
m_link.m_name="RootLongList2";
}
else if (fSz==30 && m_expectedId<=3 && !linkValues[0]) {
if (m_expectedId!=3) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: bad expected N for cluster list id\n"));
f << "##expectedN=" << m_expectedId << ",";
m_expectedId=3;
}
if ((m_link.m_fileType[1]&0xFFD7)!=0x40 || m_link.m_fieldSize!=8) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: find odd definition for cluster list id\n"));
f << "##[" << std::hex << m_link.m_fileType[1] << std::dec << ":" << m_link.m_fieldSize << "],";
}
m_cluster->m_listClusterId=m_link.m_ids[0];
m_fieldName="clusterList";
m_link=RagTime5ClusterManager::Link();
break;
}
else if (fSz==30 && !linkValues[0]) {
expectedFileType1=0;
expectedFieldSize=4;
m_linkId=6;
m_fieldName="clusterFieldLink";
}
else if (fSz==32 && m_dataId==1) {
m_fieldName="zone:names";
expectedFileType1=0x200;
m_linkId=0;
if (linkValues[0]!=0x7d01a) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: unexpected type for zone[name]\n"));
f << "##fileType=" << std::hex << m_link.m_fileType[0] << std::dec << ",";
}
}
else if (fSz==32 && (m_link.m_fileType[1]&0xFFD7)==0x8010) {
m_linkId=1;
m_expectedId=5;
m_link.m_name=m_fieldName="docInfo";
}
else if (fSz==32 && (m_link.m_fileType[1]&0xFFD7)==0xc010) {
m_linkId=2;
m_expectedId=6;
m_link.m_name=m_fieldName="rootUnknC"; // a list, but never find data
}
else if (fSz==32 && m_link.m_fileType[0]==0x47040 && (m_link.m_fileType[1]&0xFFD7)==0) {
m_linkId=3;
m_link.m_name=m_fieldName="settings";
}
else if (fSz==32 && (m_link.m_fileType[1]&0xFFD7)==0x10) {
m_linkId=4;
m_link.m_name=m_fieldName="condFormula";
}
else if (fSz==32 && (m_link.m_fileType[1]&0xFFD7)==0x310) { // always *,+(dagger),0(exp)
m_linkId=7;
m_link.m_name=m_fieldName="rootUnicodeLst";
}
else {
f << "###fType0,";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: the field fSz=28.. seems bad\n"));
}
if (expectedFileType1>=0 && (m_link.m_fileType[1]&0xFFD7)!=expectedFileType1) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseZone: fileType1 seems odd[fSz=28...]\n"));
f << "###fileType1,";
}
if (expectedFieldSize>0 && m_link.m_fieldSize!=expectedFieldSize) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseZone: fieldSize seems odd[fSz=28...]\n"));
f << "###fieldSize,";
}
break;
}
case 38:
m_fieldName="field4";
val=static_cast<int>(input->readULong(4));
if (m_expectedId>4 || val!=0x47040) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: unexpected data of size 38\n"));
f << "##fileType=" << std::hex << val << std::dec << ",";
break;
}
if (m_expectedId!=4) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: bad expected N for field 4\n"));
f << "##expectedN=" << m_expectedId << ",";
m_expectedId=4;
}
for (int i=0; i<6; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
val=static_cast<int>(input->readULong(2));
if ((val&0xFFD7)!=0x10) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: unexpected fileType1 for field 4\n"));
f << "##fileType1=" << std::hex << val << std::dec << ",";
}
f << "val=[";
for (int i=0; i<3; ++i) { // small int, often with f1=f0+1, f2=f1+1
val=static_cast<int>(input->readULong(4));
if (val)
f << val << ",";
else
f << "_,";
}
f << "],";
val=static_cast<int>(input->readULong(2)); // always 0?
if (val) f << "f0=" << val << ",";
break;
case 52:
m_what=3;
m_fieldName="graphTypes";
if (N!=1) f << "##N=" << N << ",";
for (int i=0; i<2; ++i) { // always 0 and small val
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+1 << "=" << val << ",";
}
m_link.m_fileType[0]=static_cast<int>(input->readLong(4));
if (m_link.m_fileType[0]!=0x14e6042) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone[graph]: find unexpected fileType\n"));
f << "###fileType=" << std::hex << m_link.m_fileType[0] << std::dec << ",";
}
for (int i=0; i<14; ++i) { // g1=0-2, g2=10[size?], g4=1-8[N], g13=30
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
if (RagTime5StructManager::readDataIdList(input, 2, m_link.m_ids) && m_link.m_ids[1]) {
m_link.m_fileType[1]=0x30;
m_link.m_fieldSize=16;
}
val=static_cast<int>(input->readLong(2));
if (val) // small number
f << "h0=" << val << ",";
break;
case 78: {
m_what=4;
m_fieldName="fieldLists";
if (N!=1) f << "##N=" << N << ",";
val=static_cast<int>(input->readULong(4));
if (val) f << "id=" << val << ",";
val=static_cast<int>(input->readULong(4));
if (val!=0x154a042) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: find odd type for fSz=78\n"));
f << "##[" << std::hex << val << std::dec << ":" << m_link.m_fieldSize << "],";
}
for (int i=0; i<2; ++i) { // always 0
val=static_cast<int>(input->readULong(2));
if (val)
f << "f" << i << "=" << val << ",";
}
std::vector<int> listIds;
long actPos=input->tell();
if (!RagTime5StructManager::readDataIdList(input, 2, listIds)) {
f << "###fieldId,";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: can not read field ids\n"));
input->seek(actPos+8, librevenge::RVNG_SEEK_SET);
}
else if (listIds[0] || listIds[1]) { // fielddef and fieldpos
RagTime5ClusterManager::Link fieldLink;
fieldLink.m_type= RagTime5ClusterManager::Link::L_ClusterLink;
fieldLink.m_ids=listIds;
m_cluster->m_fieldClusterLink=fieldLink;
f << "fields," << fieldLink << ",";
}
val=static_cast<int>(input->readULong(4));
if (val) f << "id2=" << val << ",";
for (int i=0; i<4; ++i) { // always 0
val=static_cast<int>(input->readULong(2));
if (val)
f << "f" << i+2 << "=" << val << ",";
}
for (int i=0; i<2; ++i) { // always 1,0
val=static_cast<int>(input->readULong(1));
if (val!=1-i)
f << "fl" << i << "=" << val << ",";
}
val=static_cast<int>(input->readLong(2));
if (val!=100)
f << "f6=" << val << ",";
f << "marg?=[";
for (int i=0; i<2; ++i) {
actPos=input->tell();
double res;
bool isNan;
if (input->readDouble8(res, isNan)) // typically 0.01
f << res << ",";
else {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: can not read a double\n"));
f << "##double,";
input->seek(actPos+8, librevenge::RVNG_SEEK_SET);
}
f << ",";
}
f << "],";
listIds.clear();
actPos=input->tell();
if (!RagTime5StructManager::readDataIdList(input, 4, listIds)) {
f << "###clusterCId,";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: can not read clusterC ids\n"));
input->seek(actPos+16, librevenge::RVNG_SEEK_SET);
}
else if (listIds[0] || listIds[1] || listIds[2] || listIds[3]) {
RagTime5ClusterManager::Link fieldLink;
fieldLink.m_type= RagTime5ClusterManager::Link::L_UnknownClusterC;
fieldLink.m_ids=listIds;
m_cluster->m_linksList.push_back(fieldLink);
f << fieldLink << ",";
}
val=static_cast<int>(input->readULong(4));
if (val) f << "id3=" << val << ",";
break;
}
default:
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseDataZone: find unexpected data field\n"));
f << "###N=" << N << ",fSz=" << fSz << ",";
}
if (!m_fieldName.empty())
f << m_fieldName << ",";
return true;
}
//! parse the header zone
bool parseHeaderZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f)
{
f << "header, fl=" << std::hex << flag << std::dec << ",";
m_fieldName="header";
if (N!=-2 || m_dataId!=0 || (fSz!=215 && fSz!=220)) {
f << "###N=" << N << ",fSz=" << fSz << ",";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseHeaderZone: find unexpected main field\n"));
return true;
}
m_what=0;
auto val=static_cast<int>(input->readLong(4)); // 8|9|a
f << "f1=" << val << ",";
for (int i=0; i<4; ++i) { // f2=0-7, f3=1|3
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+2 << "=" << val << ",";
}
val=static_cast<int>(input->readLong(4)); // 7|8
f << "N1=" << val << ",";
std::vector<int> listIds;
long actPos=input->tell();
if (!RagTime5StructManager::readDataIdList(input, 1, listIds) || !listIds[0]) {
f << "###cluster[child],";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseHeaderZone: can not find the cluster's child\n"));
input->seek(actPos+4, librevenge::RVNG_SEEK_SET);
}
else { // link to unknown cluster zone
m_cluster->m_clusterIds[0]=listIds[0];
f << "unknClustB=data" << listIds[0] << "A,";
}
for (int i=0; i<21; ++i) { // always g0=g11=g18=16, other 0 ?
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
val=static_cast<int>(input->readULong(4));
if (val!=0x3c052)
f << "#fileType=" << std::hex << val << std::dec << ",";
for (int i=0; i<9; ++i) { // always h6=6
val=static_cast<int>(input->readLong(2));
if (val) f << "h" << i << "=" << val << ",";
}
for (int i=0; i<3; ++i) { // can be 1,11,10
val=static_cast<int>(input->readULong(1));
if (val)
f << "fl" << i << "=" << std::hex << val << std::dec << ",";
}
if (fSz==220) {
for (int i=0; i<2; ++i) { // h10=1, h11=16
val=static_cast<int>(input->readLong(2));
if (val) f << "h" << i+9 << "=" << val << ",";
}
val=static_cast<int>(input->readLong(1));
if (val) f << "h11=" << val << ",";
}
val=static_cast<int>(input->readLong(4)); // e-5a
if (val) f << "N2=" << val << ",";
for (int i=0; i<9; ++i) { // j8=18
val=static_cast<int>(input->readLong(2));
if (val) f << "j" << i << "=" << val << ",";
}
for (int i=0; i<3; ++i) {
val=static_cast<int>(input->readLong(4));
if (val!=i+2)
f << "j" << 9+i << "=" << val << ",";
}
actPos=input->tell();
listIds.clear();
if (!RagTime5StructManager::readDataIdList(input, 4, listIds)) {
f << "###style[child],";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseHeaderZone: can not find the style's child\n"));
input->seek(actPos+16, librevenge::RVNG_SEEK_SET);
}
else {
for (size_t i=0; i<4; ++i) {
if (listIds[i]==0) continue;
m_cluster->m_styleClusterIds[i]=listIds[i];
static char const *wh[]= { "graph", "units", "units2", "text" };
f << wh[i] << "Style=data" << listIds[i] << "A,";
}
}
val=static_cast<int>(input->readLong(4)); // always 5?
if (val!=80) f << "N3=" << val << ",";
actPos=input->tell();
listIds.clear();
if (!RagTime5StructManager::readDataIdList(input, 3, listIds)) {
f << "###style[child],";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseHeaderZone: can not find the style2's child\n"));
input->seek(actPos+12, librevenge::RVNG_SEEK_SET);
}
else {
for (size_t i=0; i<3; ++i) {
if (listIds[i]==0) continue;
m_cluster->m_styleClusterIds[i+4]=listIds[i];
static char const *wh[]= { "format", "#unk", "graphColor" };
f << wh[i] << "Style=data" << listIds[i] << "A,";
}
}
for (int i=0; i<9; ++i) { // k6=0|6, k7=0|7
val=static_cast<int>(input->readULong(4)); // maybe some dim
static int const expected[]= {0xc000, 0x2665, 0xc000, 0x2665, 0xc000, 0xc000, 0, 0, 0};
if (val!=expected[i])
f << "k" << i << "=" << std::hex << val << std::dec << ",";
}
for (int i=0; i<2; ++i) { // l0=0|1|2, l1=0|1
val=static_cast<int>(input->readLong(2)); // 0|1|2
if (val)
f << "l" << i <<"=" << val << ",";
}
// a very big number
f << "ID=" << std::hex << input->readULong(4) << std::dec << ",";
for (int i=0; i<6; ++i) { // always 0
val=static_cast<int>(input->readLong(2)); // 0|1|2
if (val)
f << "l" << i+2 <<"=" << val << ",";
}
return true;
}
//! the current cluster
std::shared_ptr<RagTime5ClusterManager::ClusterRoot> m_cluster;
//! a index to know which field is parsed : 0: main, 1: filename, 2: list, 3: graph type, 4: fieldList
int m_what;
//! a index to known which field is expected : 0:data ... 7: filename, ...
int m_expectedId;
//! the link id : 0: zone[names], 1: field5=doc[info]?, 2: field6, 3: settings, 4: formula, 5: cluster[list], 6: a def cluster list, 7: a list of unicode string?
int m_linkId;
//! the actual field name
std::string m_fieldName;
};
RootCParser::~RootCParser()
{
}
//
//! try to read a basic root child cluster: either fielddef or fieldpos or a first internal child of the root (unknown) or another child
//
struct RootChildCParser final : public RagTime5ClusterManager::ClusterParser {
//! constructor
RootChildCParser(RagTime5ClusterManager &parser, int type)
: ClusterParser(parser, type, "ClustCRoot_BAD")
, m_cluster(new RagTime5ClusterManager::Cluster(RagTime5ClusterManager::Cluster::C_Unknown))
{
switch (type) {
case 0x10000:
m_name="ClustGObjProp";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_ClusterGProp;
break;
case 0x20000:
m_name="ClustField_Def";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_Fields;
break;
case 0x20001:
m_name="ClustField_Pos";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_Fields;
break;
case 0x30000:
m_name="ClustUnkC_A";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_ClusterC;
break;
case 0x30001:
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootChildCParser::RootChildCParser: find zone ClustUnkC_B\n"));
m_name="ClustUnkC_B";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_ClusterC;
break;
case 0x30002:
m_name="ClustUnkC_C";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_ClusterC;
break;
case 0x30003:
m_name="ClustUnkC_D";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_ClusterC;
break;
default:
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootChildCParser::RootChildCParser: find unknown type\n"));
break;
}
}
//! destructor
~RootChildCParser() final;
//! return the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> getCluster() final
{
return m_cluster;
}
//! parse a zone
bool parseZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f) final
{
if ((m_dataId==0 && flag!=0x30) || (m_dataId==1 && flag !=0x30))
f << "fl=" << std::hex << flag << std::dec << ",";
bool ok=false;
int expectedFileType1=0;
switch (m_type) {
case 0x10000:
ok=m_dataId==0 && fSz==32;
expectedFileType1=0x4010;
break;
case 0x20000:
ok=m_dataId==0 && fSz==41;
expectedFileType1=0x1010;
break;
case 0x20001:
ok=m_dataId==0 && fSz==32;
expectedFileType1=0x1010;
break;
case 0x30000:
ok=m_dataId==0 && fSz==34;
expectedFileType1=0x50;
break;
case 0x30002:
if (m_dataId==0 && fSz==40) {
ok=true;
expectedFileType1=0x8010;
}
else if (m_dataId==1 && fSz==30) {
ok=true;
expectedFileType1=0x50;
}
break;
case 0x30003:
ok=m_dataId==0 && fSz==32;
expectedFileType1=0x310;
break;
default:
break;
}
if (N<=0 || !ok) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootChildCParser::parseZone: find unexpected header\n"));
f << "###type" << std::hex << N << std::dec;
return true;
}
int val;
m_link.m_N=N;
long linkValues[4]; // for type=0x30002, f0=3c|60, for fixed size f0=54, other 0
std::string mess;
if (!readLinkHeader(input, fSz, m_link, linkValues, mess)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootChildCParser::parseZone: can not read the link\n"));
f << "###link";
return true;
}
m_link.m_fileType[0]=m_type < 0x30000 ? m_type : m_type-0x30000;
f << m_link << "," << mess;
if (expectedFileType1>=0 && (m_link.m_fileType[1]&0xFFD7)!=expectedFileType1) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseZone: fileType1 seems odd[fSz=28...]\n"));
f << "###fileType1,";
}
if (m_type==0x20000) {
std::vector<int> listIds;
bool hasCluster=false;
if (RagTime5StructManager::readDataIdList(input, 1, listIds) && listIds[0]) {
m_cluster->m_clusterIdsList.push_back(listIds[0]);
f << "clusterId1=data" << listIds[0] << "A,";
hasCluster=true;
}
val=static_cast<int>(input->readLong(1));
if ((hasCluster && val!=1) || (!hasCluster && val))
f << "#hasCluster=" << val << ",";
for (int i=0; i<2; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
}
else if (m_type==0x30000) {
for (int i=0; i<2; ++i) { // find 0
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
}
else if (m_type==0x30002) {
for (int i=0; i<2; ++i) { // find 0
val=static_cast<int>(input->readLong(4));
if (val) f << "g" << i << "=" << val << ",";
}
}
return true;
}
//! parse a field
bool parseField(RagTime5StructManager::Field const &field, int /*m*/, libmwaw::DebugStream &f) final
{
if (m_dataId==0 && field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0xce842) {
f << "pos=[";
for (auto val : field.m_longList)
f << val << ",";
f << "],";
m_link.m_longList=field.m_longList;
}
else if (m_dataId==0 && field.m_type==RagTime5StructManager::Field::T_Unstructured && field.m_fileType==0xce017)
// pos find 2|4|8
// def find f801|000f00
f << "unkn="<<field.m_extra << ",";
else {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootChildCParser::parseField: find unexpected sub field\n"));
f << "#" << field;
}
return true;
}
//! end of a start zone call
void endZone() final
{
if (m_link.empty())
return;
if (m_dataId==0)
m_cluster->m_dataLink=m_link;
else
m_cluster->m_linksList.push_back(m_link);
}
protected:
//! the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> m_cluster;
};
RootChildCParser::~RootChildCParser()
{
}
//
//! low level: parser of script cluster : zone 2,a,4002,400a
//
struct ScriptCParser final : public RagTime5ClusterManager::ClusterParser {
//! constructor
ScriptCParser(RagTime5ClusterManager &parser, int type)
: ClusterParser(parser, type, "ClustScript")
, m_cluster(new RagTime5ClusterManager::ClusterScript)
, m_what(-1)
, m_fieldName("")
{
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_Script;
}
//! destructor
~ScriptCParser() final;
//! return the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> getCluster() final
{
return m_cluster;
}
//! parse a zone
bool parseZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f) final
{
f << "fl=" << std::hex << flag << std::dec << ",";
m_what=-1;
m_fieldName="";
if (isANameHeader(N)) {
if (m_dataId!=1) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::parseZone: expected n seems bad\n"));
f << "#n=" << m_dataId << ",";
}
f << "scriptName,";
m_fieldName="script:name";
m_what=1;
return true;
}
int val;
if (N==-5) {
if (m_dataId!=0 || (fSz!=38 && fSz!=74)) {
f << "###main,";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::parseZone: can not read the main data\n"));
return true;
}
m_what=0;
m_fieldName="main";
for (int i=0; i<2; ++i) { // always 0?
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+1 << "=" << val << ",";
}
val=static_cast<int>(input->readLong(2));
f << "id=" << val << ",";
val=static_cast<int>(input->readULong(2));
if (val!=m_type) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::parseZone: unexpected zone type[graph]\n"));
f << "##zoneType=" << std::hex << val << std::dec << ",";
}
if (fSz==38) { // find in zone a ( with a next data field which is a link to a cluster )
// probably in relation with zone fSz=60
val=static_cast<int>(input->readLong(4)); // find with 2, probably a number
if (val) f << "f0=" << val << ",";
for (int i=0; i<6; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+1 << "=" << val << ",";
}
std::string code(""); // find betr
for (int i=0; i<4; ++i) code+=char(input->readULong(1));
if (!code.empty()) f << code << ",";
for (int i=0; i<2; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
return true;
}
val=static_cast<int>(input->readLong(4)); // find with 0|3|4, probably a number
if (val) f << "f0=" << val << ",";
for (int i=0; i<10; ++i) { // f4=2|8, f6=0|1|2|5, f7=1-5, f8=3, f10=0-4
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+1 << "=" << val << ",";
}
val=static_cast<int>(input->readLong(4)); // find 0|93037 maybe a type
if (val) f << "fileType=" << std::hex << val << std::dec << ",";
val=static_cast<int>(input->readLong(2)); // always 0
if (val) f << "f11=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val!=0 && val!=2) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::parseZone: the unicode field size seems bad\n"));
f << "#fieldSize=" << val << ",";
}
long actPos=input->tell();
std::vector<int> listIds;
if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::parseZone: can not find the unicode string data\n"));
f << "##noData,";
input->seek(actPos+2, librevenge::RVNG_SEEK_SET);
}
else if (listIds[0]) {
// find a script comment
RagTime5ClusterManager::Link scriptLink;
scriptLink.m_type=RagTime5ClusterManager::Link::L_List;
scriptLink.m_name="scriptComment";
scriptLink.m_ids.push_back(listIds[0]);
m_cluster->m_scriptComment=scriptLink;
f << scriptLink << ",";
}
for (int i=0; i<10; ++i) { // g0=1-8d, g2=2|3, g3=1f40, g5=0-26, g9=0|30
val=static_cast<int>(input->readLong(2));
if (i==3) {
if (val==0x1f40) continue;
f << "#fileType1=" << std::hex << val << std::dec << ",";
}
else if (val)
f << "g" << i << "=" << val << ",";
}
std::string code(""); // find cent, left
for (int i=0; i<4; ++i) code+=char(input->readULong(1));
if (!code.empty()) f << "align=" << code << ",";
return true;
}
if (N<0 || m_dataId==0 || (fSz!=32 && fSz!=36)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::parseZone: find unexpected data block\n"));
f << "###N=" << N << ",";
return true;
}
m_link.m_N=N;
long linkValues[4];
std::string mess;
if (!readLinkHeader(input,fSz,m_link,linkValues,mess)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::parseZone: can not read the link\n"));
f << "###link,";
return true;
}
if ((m_link.m_fileType[1]&0xFFD7)!=(fSz==32 ? 0x600 : 0x10)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::RootCParser::parseZone: fileType1 seems odd[fSz=28...]\n"));
f << "###fileType1,";
}
if (fSz==32) {
m_what=2;
m_fieldName="unicodeList";
m_link.m_type=RagTime5ClusterManager::Link::L_UnicodeList;
}
else {
m_what=3;
m_fieldName="scriptLink";
m_link.m_name="scriptDataA";
}
f << m_link << "," << mess;
if (fSz==32) return true;
for (int i=0; i<2; ++i) { // g0: small number between 38 and 64, g1: 0|-1
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
return true;
}
//! parse a field
bool parseField(RagTime5StructManager::Field const &field, int /*m*/, libmwaw::DebugStream &f) final
{
if (!m_fieldName.empty())
f << m_fieldName << ",";
switch (m_what) {
case 1:
if (field.m_type==RagTime5StructManager::Field::T_Unicode && field.m_fileType==0xc8042) {
m_cluster->m_scriptName=field.m_string.cstr();
f << field.m_string.cstr();
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::parseField: find unexpected script field\n"));
f << "###" << field;
break;
case 2:
case 3: {
if (field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0xce842) {
f << "pos=[";
for (auto val : field.m_longList)
f << val << ",";
f << "],";
m_link.m_longList=field.m_longList;
break;
}
if (field.m_type==RagTime5StructManager::Field::T_Unstructured && field.m_fileType==0xce017) {
// a small value 2 (can be first data)
f << "unkn="<<field.m_extra << ",";
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::parseField: find unexpected list link field\n"));
f << "###" << field;
break;
}
default:
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::parseField: find unexpected field\n"));
f << "###" << field;
break;
}
return true;
}
//! end of a start zone call
void endZone() final
{
if (m_link.empty())
return;
if (m_what==2) {
if (m_cluster->m_nameLink.empty())
m_cluster->m_nameLink=m_link;
else {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::ScriptCParser::endZone: oops the name link is already set\n"));
m_cluster->m_linksList.push_back(m_link);
}
}
else if (m_what==3)
m_cluster->m_dataLink=m_link;
else
m_cluster->m_linksList.push_back(m_link);
}
protected:
//! the current cluster
std::shared_ptr<RagTime5ClusterManager::ClusterScript> m_cluster;
//! a index to know which field is parsed : 0: main, 1:scriptname, 2: list names, 3: list data
int m_what;
//! the actual field name
std::string m_fieldName;
};
ScriptCParser::~ScriptCParser()
{
}
//
//! low level: parser of script cluster : zone 480
//
struct StyleCParser final : public RagTime5ClusterManager::ClusterParser {
//! constructor
explicit StyleCParser(RagTime5ClusterManager &parser)
: ClusterParser(parser, 0x480, "ClustStyle")
, m_cluster(new RagTime5ClusterManager::Cluster(RagTime5ClusterManager::Cluster::C_Unknown))
, m_what(-1)
, m_fieldName("")
{
}
//! destructor
~StyleCParser() final;
//! return the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> getCluster() final
{
return m_cluster;
}
//! try to parse a zone
bool parseZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f) final
{
f << "fl=" << std::hex << flag << std::dec << ",";
m_fieldName="";
int val;
m_what=-1;
if (N!=-5) {
if (N<0 || m_dataId==0 || (fSz!=28 && fSz!=32 && fSz!=36)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: unexpected header\n"));
f << "##N=" << N << ",";
return true;
}
m_link.m_N=N;
if (fSz==28 || fSz==32) { // n=2,3 with fSz=28, type=0x3e800, can have no data
if ((fSz==28 && m_dataId!=2 && m_dataId!=3) || (fSz==32 && m_dataId!=4)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: dataId seems bad\n"));
f << "##n=" << m_dataId << ",";
}
long linkValues[4];
std::string mess;
if (!readLinkHeader(input, fSz, m_link, linkValues, mess)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: link seems bad\n"));
f << "###link,";
return true;
}
f << m_link << "," << mess;
if (m_link.m_fileType[0]==0x35800) {
m_what=2;
m_fieldName="zone:longs";
}
else if (m_link.m_fileType[0]==0x3e800) {
m_what=3;
m_fieldName="list:longs0";
}
else if (fSz==32) {
m_fieldName="unicodeList";
m_link.m_type=RagTime5ClusterManager::Link::L_UnicodeList;
m_what=4;
}
else {
f << "###fType=" << std::hex << m_link.m_fileType[0] << std::dec << ",";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: the field 2,3 type seems bad\n"));
return true;
}
if ((m_link.m_fileType[1]&0xFFD7)!=(fSz==28 ? 0 : 0x200)) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: fileType1 seems odd[fSz=28...]\n"));
f << "###fileType1,";
}
if (fSz==28 && m_cluster->m_type!=RagTime5ClusterManager::Cluster::C_FormatStyles && !m_link.m_ids.empty() && m_link.m_ids[0]) {
f << "###unexpected";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: find link for not format data\n"));
}
if (!m_fieldName.empty())
f << m_fieldName << ",";
return true;
}
if (m_dataId!=1) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: dataId seems bad\n"));
f << "##n=" << m_dataId << ",";
}
m_what=1;
for (int i=0; i<2; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
m_link.m_fileType[0]=static_cast<int>(input->readULong(4));
if (m_link.m_fileType[0]!=0x7d01a) {
f << "###fType=" << std::hex << m_link.m_fileType[0] << std::dec << ",";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: the field 1 type seems bad\n"));
}
for (int i=0; i<4; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+2 << "=" << val << ",";
}
m_link.m_fileType[1]=static_cast<int>(input->readULong(2));
if (m_link.m_fileType[1]!=0x10 && m_link.m_fileType[1]!=0x18) {
f << "###fType1=" << std::hex << m_link.m_fileType[1] << std::dec << ",";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: the field 1 type1 seems bad\n"));
}
for (int i=0; i<3; ++i) { // always 3,4,5 ?
val=static_cast<int>(input->readLong(4));
if (val!=i+3) f << "g" << i << "=" << val << ",";
}
return true;
}
if ((fSz!=58 && fSz!=64 && fSz!=66 && fSz!=68) || m_dataId!=0) {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: find unknown block\n"));
f << "###unknown,";
return true;
}
m_what=0;
for (int i=0; i<2; ++i) { // always 0?
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i+1 << "=" << val << ",";
}
val=static_cast<int>(input->readLong(2));
f << "id=" << val << ",";
val=static_cast<int>(input->readULong(2));
if (val!=0x480) {
f << "###type=" << std::hex << val << std::dec << ",";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: the field format seems bad\n"));
}
m_link.m_N=val;
for (int i=0; i<13; ++i) { // g3=2, g4=10, g6 and g8 2 small int
val=static_cast<int>(input->readLong(2));
if (val) f << "g" << i << "=" << val << ",";
}
m_link.m_fileType[0]=static_cast<int>(input->readULong(4));
if (m_link.m_fileType[0] != 0x01473857 && m_link.m_fileType[0] != 0x0146e827) {
f << "###fileType=" << std::hex << m_link.m_fileType[0] << std::dec << ",";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: the field type seems bad\n"));
}
m_link.m_fileType[1]=static_cast<int>(input->readULong(2)); // c018|c030|c038 or type ?
if (!RagTime5StructManager::readDataIdList(input, 2, m_link.m_ids) || m_link.m_ids[1]==0) {
f << "###noData,";
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseZone: can not find any data\n"));
}
m_link.m_type=RagTime5ClusterManager::Link::L_FieldsList;
if (fSz==58) {
if (m_link.m_fileType[0] == 0x0146e827) {
m_link.m_name=m_fieldName="formats";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_FormatStyles;
}
else {
m_link.m_name=m_fieldName="units";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_UnitStyles;
}
}
else if (fSz==64) {
m_link.m_name=m_fieldName="graphColor";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_ColorStyles;
}
else if (fSz==66) {
m_link.m_name=m_fieldName="textStyle";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_TextStyles;
}
else {
m_link.m_name=m_fieldName="graphStyle";
m_cluster->m_type=RagTime5ClusterManager::Cluster::C_GraphicStyles;
}
f << m_link << ",";
return true;
}
//! parse a field
bool parseField(RagTime5StructManager::Field const &field, int /*m*/, libmwaw::DebugStream &f) final
{
if (!m_fieldName.empty())
f << m_fieldName << ",";
switch (m_what) {
case 0: {
long expectedVal=m_cluster->m_type==RagTime5ClusterManager::Cluster::C_FormatStyles ? 0x146e815 : 0x1473815;
if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==expectedVal) {
f << "decal=[";
for (auto const &child : field.m_fieldList) {
if (child.m_type==RagTime5StructManager::Field::T_LongList && child.m_fileType==0xce842) {
for (auto val : child.m_longList)
f << val << ",";
m_link.m_longList=child.m_longList;
continue;
}
if (child.m_type==RagTime5StructManager::Field::T_Unstructured && child.m_fileType==0xce017) {
// a list of small int 0104|0110|22f8ffff7f3f
f << "unkn0=" << child.m_extra << ",";
continue;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readStyleCluster: find unexpected child[main]\n"));
f << "###[" << child << "],";
}
f << "],";
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readStyleCluster: find unexpected field[main]\n"));
f << "###" << field;
break;
}
case 1:
if (field.m_type==RagTime5StructManager::Field::T_Unstructured && field.m_fileType==0xce017) {
// a small value 2 (can be first data)
f << "unkn="<<field.m_extra << ",";
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseField: find unexpected field[zone1]\n"));
f << "###" << field;
break;
case 2:
case 3:
if (field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0xce842) {
f << "data=[";
for (auto val : field.m_longList) {
if (val==0)
f << "_,";
else if (static_cast<int>(val)==static_cast<int>(0x80000000))
f << "inf,";
else
f << val << ",";
}
f << "],";
m_link.m_longList=field.m_longList;
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseField: find unexpected field[zone23–\n"));
f << "###" << field;
break;
case 4:
if (field.m_type==RagTime5StructManager::Field::T_LongList && field.m_fileType==0xce842) {
f << "data=[";
for (auto val : field.m_longList)
f << val << ",";
f << "],";
m_link.m_longList=field.m_longList;
break;
}
if (field.m_type==RagTime5StructManager::Field::T_Unstructured && field.m_fileType==0xce017) {
// a small value 2 (can be first data)
f << "unkn="<<field.m_extra << ",";
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseField: find unexpected unicode field\n"));
f << "###" << field;
break;
default:
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::parseField: find unexpected list link field\n"));
f << "###" << field;
break;
}
return true;
}
//! end of a start zone call
void endZone() final
{
if (m_link.empty())
return;
if (m_what==0) {
if (m_cluster->m_dataLink.empty())
m_cluster->m_dataLink=m_link;
else {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::endZone: oops the main link is already set\n"));
m_cluster->m_linksList.push_back(m_link);
}
}
else if (m_what==4) {
if (m_cluster->m_nameLink.empty())
m_cluster->m_nameLink=m_link;
else {
MWAW_DEBUG_MSG(("RagTime5ClusterManagerInternal::StyleCParser::endZone: oops the name link is already set\n"));
m_cluster->m_linksList.push_back(m_link);
}
}
else
m_cluster->m_linksList.push_back(m_link);
}
protected:
//! the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> m_cluster;
//! a index to know which field is parsed : 0: main, 1, 2/3: field , 4: unicode list
int m_what;
//! the actual field name
std::string m_fieldName;
};
StyleCParser::~StyleCParser()
{
}
//
//! low level: parser of unknown cluster
//
struct UnknownCParser final : public RagTime5ClusterManager::ClusterParser {
//! constructor
UnknownCParser(RagTime5ClusterManager &parser, int type)
: ClusterParser(parser, type, "ClustUnknown")
, m_cluster(new RagTime5ClusterManager::Cluster(RagTime5ClusterManager::Cluster::C_Unknown))
{
if (type==-1)
return;
}
//! destructor
~UnknownCParser() final;
//! return the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> getCluster() final
{
return m_cluster;
}
protected:
//! the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> m_cluster;
};
UnknownCParser::~UnknownCParser()
{
}
}
bool RagTime5ClusterManager::getClusterBasicHeaderInfo(RagTime5Zone &zone, long &N, long &fSz, long &debHeaderPos)
{
MWAWEntry const &entry=zone.m_entry;
if (entry.length()<13) return false;
MWAWInputStreamPtr input=zone.getInput();
long endPos=entry.end();
input->setReadInverted(!zone.m_hiLoEndian);
input->seek(entry.begin()+8, librevenge::RVNG_SEEK_SET);
long endDataPos;
if (!readFieldHeader(zone, endPos, "", endDataPos) || !RagTime5StructManager::readCompressedLong(input, endDataPos, fSz) ||
fSz<6 || input->tell()+fSz>endDataPos) {
input->setReadInverted(false);
return false;
}
input->seek(2, librevenge::RVNG_SEEK_CUR); // skip flag
N=static_cast<int>(input->readLong(4));
debHeaderPos=input->tell();
input->setReadInverted(false);
return true;
}
int RagTime5ClusterManager::getClusterType(RagTime5Zone &zone, int fileType)
{
if (fileType==-1)
fileType=getClusterFileType(zone);
switch (fileType) {
case -1:
return -1;
case 0: // root
return 0;
case 0x2: // script
case 0xa:
case 0x4002:
case 0x400a:
return 2;
case 0x104: // pipeline
case 0x204:
case 0x4104:
case 0x4204:
return 0x104;
case 0x480: // style
return 0x80;
case 0x4001: // layout
return 1;
case 0x8042: // color pattern
return 0x42;
case 0x10000: // first child of root
case 0x20000: // field def
case 0x20001: // field pos
case 0x30000: // 0th element of child list root
case 0x30001: // 1th element of child list root, never seen
case 0x30002: // 2th element of child list root
case 0x30003: // 3th element of child list root
case 0x40000: // picture cluster
case 0x40001: // graphic cluster
case 0x40002: // spreadsheet cluster
case 0x40003: // text cluster
return fileType;
default:
break;
}
long N, fSz, debDataPos;
if (!getClusterBasicHeaderInfo(zone, N, fSz, debDataPos) || N!=-5)
return -1;
switch (fSz) {
case 64: // movie cluster
case 104:
case 109: // picture cluster
return 0x40000;
case 118: // graphic cluster
return 0x40001;
case 134: // spreadsheet cluster
return 0x40002;
case 135:
case 140:
case 143:
case 208:
case 212:
case 213:
case 216: // text cluster
return 0x40003;
case 331:
case 339: // chart cluster
return 0x40004;
default: // unknown
break;
}
return -1;
}
int RagTime5ClusterManager::getClusterFileType(RagTime5Zone &zone)
{
long N, fSz, debDataPos;
if (!getClusterBasicHeaderInfo(zone, N, fSz, debDataPos))
return -1;
int res=-1;
MWAWInputStreamPtr input=zone.getInput();
input->setReadInverted(!zone.m_hiLoEndian);
switch (N) {
case -2:
res=0;
break;
case -5:
input->seek(debDataPos+6, librevenge::RVNG_SEEK_SET); // skip id, ...
res=static_cast<int>(input->readULong(2));
break;
default:
if (N<0) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::getClusterFileType: unexpected N value\n"));
break;
}
if (fSz==0x20) {
input->seek(debDataPos+16, librevenge::RVNG_SEEK_SET);
auto fieldType=static_cast<int>(input->readULong(2));
if ((fieldType&0xFFD7)==0x1010)
res=0x20001;
else if ((fieldType&0xFFD7)==0x310)
res=0x30003;
else if ((fieldType&0xFFD7)==0x4010)
res=0x10000;
else {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::getClusterFileType: unexpected field type %x\n", unsigned(fieldType)));
}
}
else if (fSz==0x22)
res=0x30000;
else if (fSz==0x28)
res=0x30002;
else if (fSz==0x29)
res=0x20000;
else {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::getClusterFileType: unexpected fSz=%ld\n", fSz));
}
break;
}
input->setReadInverted(false);
return res;
}
bool RagTime5ClusterManager::readClusterGObjProperties(RagTime5Zone &zone)
{
MWAWEntry entry=zone.m_entry;
libmwaw::DebugFile &ascFile=zone.ascii();
libmwaw::DebugStream f;
MWAWInputStreamPtr input=zone.getInput();
input->setReadInverted(!zone.m_hiLoEndian);
long debPos=entry.begin();
long endPos=entry.end();
zone.m_isParsed=true;
f.str("");
f << "Entries(ClustCGObjProp)[" << zone << "]:";
input->seek(debPos, librevenge::RVNG_SEEK_SET);
if (input->readULong(4)==0x5a610600) { // rare, 3 can be good in one file and 1 bad, so...
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readClusterGObjProperties: endian seems bad, reverts it\n"));
input->setReadInverted(zone.m_hiLoEndian);
f << "##badEndian,";
}
ascFile.addPos(debPos);
ascFile.addNote(f.str().c_str());
ascFile.addPos(endPos);
ascFile.addNote("_");
input->seek(debPos, librevenge::RVNG_SEEK_SET);
RagTime5StructManager::GObjPropFieldParser parser("ClustCGObjProp");
m_mainParser.readStructData(zone, endPos, 0, -1, parser, librevenge::RVNGString("ClustCGObjProp"));
long pos=input->tell();
if (pos!=endPos) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readClusterGObjProperties: find extra data\n"));
ascFile.addPos(pos);
ascFile.addNote("ClustCGObjProp:##extra");
}
input->setReadInverted(false);
return true;
}
bool RagTime5ClusterManager::readCluster(RagTime5Zone &zone, std::shared_ptr<RagTime5ClusterManager::Cluster> &cluster, int zoneType)
{
cluster.reset();
int zType=-1;
librevenge::RVNGString name("");
if (m_state->m_idToClusterInfoMap.find(zone.m_ids[0])!=m_state->m_idToClusterInfoMap.end()) {
auto const &info=m_state->m_idToClusterInfoMap.find(zone.m_ids[0])->second;
zoneType=info.m_fileType;
zType=info.m_type;
name=info.m_name;
}
if (zoneType==-1)
zoneType=getClusterFileType(zone);
if (zType==-1)
zType=getClusterType(zone, zoneType);
std::shared_ptr<ClusterParser> parser;
switch (zType) {
case 0:
parser.reset(new RagTime5ClusterManagerInternal::RootCParser(*this));
break;
case 0x1:
cluster=m_mainParser.readLayoutCluster(zone, zoneType);
break;
case 0x2:
parser.reset(new RagTime5ClusterManagerInternal::ScriptCParser(*this, zoneType));
break;
case 0x42:
parser.reset(new RagTime5ClusterManagerInternal::ColPatCParser(*this));
break;
case 0x80:
parser.reset(new RagTime5ClusterManagerInternal::StyleCParser(*this));
break;
case 0x104:
cluster=m_mainParser.readPipelineCluster(zone, zoneType);
break;
case 0x10000: // first child of root
case 0x20000: // field def
case 0x20001: // field pos
case 0x30000: // 0th element of child list root
case 0x30001: // 1th element of child list root, never seen
case 0x30002: // 2th element of child list root
case 0x30003: // 3th element of child list root
parser.reset(new RagTime5ClusterManagerInternal::RootChildCParser(*this, zType));
break;
case 0x40000:
cluster=m_mainParser.readPictureCluster(zone, zoneType);
break;
case 0x40001:
cluster=m_mainParser.readGraphicCluster(zone, zoneType);
break;
case 0x40002:
cluster=m_mainParser.readSpreadsheetCluster(zone, zoneType);
break;
case 0x40003:
cluster=m_mainParser.readTextCluster(zone, zoneType);
break;
case 0x40004:
cluster=m_mainParser.readChartCluster(zone, zoneType);
break;
default:
if (!zone.m_entry.valid()) { // rare, but can append; maybe some deleted cluster
cluster.reset(new Cluster(Cluster::C_Empty));
cluster->m_hiLoEndian=zone.m_hiLoEndian;
break;
}
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: can not find cluster type, use default parser\n"));
parser.reset(new RagTime5ClusterManagerInternal::UnknownCParser(*this, zoneType));
break;
}
bool ok=cluster.get() != nullptr;
if (!ok && parser) {
ok=readCluster(zone, *parser) && parser->getCluster();
cluster=parser->getCluster();
}
else if (!ok) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: can not find any parser\n"));
}
if (!ok)
return false;
// check the level2 child
libmwaw::DebugFile &mainAscii=m_mainParser.ascii();
for (auto cIt : zone.m_childIdToZoneMap) {
auto child=cIt.second;
if (!child) continue;
child->m_isParsed=true;
switch (cIt.first) {
case 8:
if (child->m_variableD[0] || (child->m_variableD[1]<=0&&cluster->m_type!=Cluster::C_Empty) || child->m_entry.valid()) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: refCount seems odd\n"));
mainAscii.addPos(child->m_defPosition);
mainAscii.addNote("Cluster[child]###");
}
break;
default:
if (child->m_entry.valid() && readClusterGObjProperties(*child))
break;
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: find unknown child zone\n"));
mainAscii.addPos(child->m_defPosition);
mainAscii.addNote("Cluster[child]###");
break;
}
}
cluster->m_name=name;
if (m_state->m_idToClusterMap.find(zone.m_ids[0])!=m_state->m_idToClusterMap.end()) {
MWAW_DEBUG_MSG(("RagTime5ClusterManager::readCluster: a cluster for zone %d already exists\n", zone.m_ids[0]));
}
else
m_state->m_idToClusterMap[zone.m_ids[0]]=cluster;
return true;
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: