/* -*- 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 <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <set>
#include <sstream>
#include <stack>
#include <librevenge/librevenge.h>
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWListener.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPosition.hxx"
#include "MWAWSubDocument.hxx"
#include "RagTime5ClusterManager.hxx"
#include "RagTime5Parser.hxx"
#include "RagTime5StructManager.hxx"
#include "RagTime5StyleManager.hxx"
#include "RagTime5Pipeline.hxx"
#include "libmwaw_internal.hxx"
/** Internal: the structures of a RagTime5Pipeline */
namespace RagTime5PipelineInternal
{
//! the pipeline cluster ( 4001 zone)
struct ClusterPipeline final : public RagTime5ClusterManager::Cluster {
//! constructor
ClusterPipeline()
: Cluster(C_Pipeline)
, m_dataId(0)
, m_masterId(0)
, m_layoutId(0)
, m_data2Link()
{
}
//! destructor
~ClusterPipeline() final;
//! the data id
int m_dataId;
//! the master id
int m_masterId;
//! the layout id
int m_layoutId;
//! the second data link(rare)
RagTime5ClusterManager::Link m_data2Link;
};
ClusterPipeline::~ClusterPipeline()
{
}
////////////////////////////////////////
//! Internal: the state of a RagTime5Pipeline
struct State {
//! constructor
State()
: m_idPipelineMap()
{
}
//! map data id to text zone
std::map<int, std::shared_ptr<ClusterPipeline> > m_idPipelineMap;
};
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
RagTime5Pipeline::RagTime5Pipeline(RagTime5Parser &parser)
: m_mainParser(parser)
, m_structManager(m_mainParser.getStructManager())
, m_parserState(parser.getParserState())
, m_state(new RagTime5PipelineInternal::State)
{
}
RagTime5Pipeline::~RagTime5Pipeline()
{
}
int RagTime5Pipeline::version() const
{
return m_parserState->m_version;
}
bool RagTime5Pipeline::send(int pipelineId, MWAWListenerPtr listener, MWAWPosition const &pos, int partId)
{
if (m_state->m_idPipelineMap.find(pipelineId)==m_state->m_idPipelineMap.end() ||
!m_state->m_idPipelineMap.find(pipelineId)->second) {
MWAW_DEBUG_MSG(("RagTime5Pipeline::send: can not find container for pipeline %d\n", pipelineId));
return false;
}
int dataId=m_state->m_idPipelineMap.find(pipelineId)->second->m_dataId;
if (dataId==0)
return true;
return m_mainParser.send(dataId, listener, pos, partId);
}
RagTime5ClusterManager::Cluster::Type RagTime5Pipeline::getContainerType(int pipelineId) const
{
if (m_state->m_idPipelineMap.find(pipelineId)==m_state->m_idPipelineMap.end() ||
!m_state->m_idPipelineMap.find(pipelineId)->second) {
MWAW_DEBUG_MSG(("RagTime5Pipeline::getContainerType: can not find container for pipeline %d\n", pipelineId));
return RagTime5ClusterManager::Cluster::C_Unknown;
}
int dataId=m_state->m_idPipelineMap.find(pipelineId)->second->m_dataId;
if (dataId==0) // rare, but can happens
return RagTime5ClusterManager::Cluster::C_Unknown;
return m_mainParser.getClusterType(dataId);
}
////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
//
// Low level
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// interface send function
////////////////////////////////////////////////////////////
void RagTime5Pipeline::flushExtra()
{
MWAW_DEBUG_MSG(("RagTime5Pipeline::flushExtra: not implemented\n"));
}
////////////////////////////////////////////////////////////
// cluster parser
////////////////////////////////////////////////////////////
namespace RagTime5PipelineInternal
{
//! 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_clusterManager(clusterManager)
{
if (m_fieldSize<56) {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::ClustListParser: bad field size\n"));
m_fieldSize=0;
}
}
//! destructor
~ClustListParser() final;
//! return the cluster name
std::string getClusterName(int id) const
{
return m_clusterManager.getClusterName(id);
}
//! returns the not null list dataId list
std::vector<int> getIdList() const
{
std::vector<int> res;
for (auto const &lnk : m_linkList) {
if (lnk.m_dataId>0)
res.push_back(lnk.m_dataId);
}
return res;
}
//! try to parse a data
bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int /*n*/, libmwaw::DebugStream &f) final
{
long pos=input->tell();
if (endPos-pos!=m_fieldSize) {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::ClustListParser::parse: bad data size\n"));
return false;
}
std::vector<int> listIds;
if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::ClustListParser::parse: can not read an cluster id\n"));
f << "##clusterIds,";
return false;
}
RagTime5StructManager::ZoneLink link;
link.m_dataId=listIds[0];
if (listIds[0])
f << getClusterName(listIds[0]) << ",";
link.m_subZoneId[0]=long(input->readULong(4)); // subId[3]
f << link;
float dim[2];
for (auto &d : dim) d=float(input->readULong(4))/65536.f;
f << "dim=" << MWAWVec2f(dim[0],dim[1]) << ",";
int val;
f << "unkn=[";
for (int i=0; i<8; ++i) {
val=static_cast<int>(input->readLong(2));
if (val) f << val << ",";
else f << "_,";
}
f << "],";
for (int i=0; i<12; ++i) { // always 0
val=static_cast<int>(input->readLong(2));
if (val) f << "f" << i << "=" << val << ",";
}
m_linkList.push_back(link);
return true;
}
//! the field size
int m_fieldSize;
//! the list of read cluster
std::vector<RagTime5StructManager::ZoneLink> m_linkList;
private:
//! the main zone manager
RagTime5ClusterManager &m_clusterManager;
//! copy constructor, not implemented
ClustListParser(ClustListParser &orig) = delete;
//! copy operator, not implemented
ClustListParser &operator=(ClustListParser &orig) = delete;
};
ClustListParser::~ClustListParser()
{
}
//! Internal: the helper to read a unknown
struct UnknownParser final : public RagTime5StructManager::DataParser {
//! constructor
UnknownParser(int fieldSize, std::string const &zoneName)
: RagTime5StructManager::DataParser(zoneName)
, m_fieldSize(fieldSize)
{
if (m_fieldSize<12) {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::UnknownParser: bad field size\n"));
m_fieldSize=0;
}
}
//! destructor
~UnknownParser() final;
//! try to parse a data
bool parseData(MWAWInputStreamPtr &input, long endPos, RagTime5Zone &/*zone*/, int /*n*/, libmwaw::DebugStream &f) final
{
long pos=input->tell();
if (endPos-pos!=m_fieldSize) {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::UnknownParser::parse: bad data size\n"));
return false;
}
for (int i=0; i<6; ++i) { // f0=1, f1=5|6, f2=1|2, f5=0|1
auto val=static_cast<int>(input->readLong(2));
if (val)
f << "f" << i << "=" << val << ",";
}
return true;
}
//! the field size
int m_fieldSize;
private:
//! copy constructor, not implemented
UnknownParser(UnknownParser &orig) = delete;
//! copy operator, not implemented
UnknownParser &operator=(UnknownParser &orig) = delete;
};
UnknownParser::~UnknownParser()
{
}
//
//! try to read a pipeline cluster: 104,204,4104, 4204
//
struct PipelineCParser final : public RagTime5ClusterManager::ClusterParser {
//! constructor
PipelineCParser(RagTime5ClusterManager &parser, int type)
: ClusterParser(parser, type, "ClustPipeline")
, m_cluster(new ClusterPipeline)
{
}
//! destructor
~PipelineCParser() final;
//! return the current cluster
std::shared_ptr<RagTime5ClusterManager::Cluster> getCluster() final
{
return m_cluster;
}
//! return the pipeline cluster
std::shared_ptr<ClusterPipeline> getPipelineCluster()
{
return m_cluster;
}
//! parse a field
bool parseField(RagTime5StructManager::Field const &field, int /*m*/, libmwaw::DebugStream &f) final
{
if (field.m_type==RagTime5StructManager::Field::T_FieldList && field.m_fileType==0x146c015) {
f << "unkn0=[";
for (auto const &child : field.m_fieldList) {
if (child.m_type==RagTime5StructManager::Field::T_Unstructured && child.m_fileType==0xce017) { // find 2
f << child << ",";
continue;
}
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseField: find unexpected child\n"));
f << "##[" << child << "],";
}
f << "],";
}
else {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseField: find unknow field\n"));
f << "##[" << field << "],";
}
return true;
}
//! parse a zone
bool parseZone(MWAWInputStreamPtr &input, long fSz, int N, int flag, libmwaw::DebugStream &f) final
{
if (flag!=0x31)
f << "fl=" << std::hex << flag << std::dec << ",";
if (m_dataId || N!=-5) {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: find unexpected header\n"));
f << "###type" << std::hex << N << std::dec;
return true;
}
if (fSz!=76 && fSz!=110) {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: find unexpected file size\n"));
f << "###fSz=" << fSz << ",";
return true;
}
int val;
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(("RagTime5PipelineInternal::PipelineCParser::parseZone: the zone type seems odd\n"));
f << "##zoneType=" << std::hex << val << std::dec << ",";
}
val=static_cast<int>(input->readLong(2)); // always 0?
if (val) f << "f4=" << val << ",";
for (int i=0; i<7; ++i) { // g1, g2, g3 small int other 0
val=static_cast<int>(input->readLong(4));
if (i==2)
m_link.m_N=val;
else if (val) f << "g" << i << "=" << val << ",";
}
m_link.m_fileType[1]=long(input->readULong(2));
m_link.m_fieldSize=static_cast<int>(input->readULong(2));
std::vector<int> listIds;
long actPos=input->tell();
if (!RagTime5StructManager::readDataIdList(input, 2, listIds)) {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: can not read the first list id\n"));
f << "##listIds,";
input->seek(actPos, librevenge::RVNG_SEEK_SET);
}
else {
if (listIds[0]) {
m_link.m_ids.push_back(listIds[0]);
m_cluster->m_dataLink=m_link;
f << "parent[list]=data" << listIds[0] << "A,";
}
if (listIds[1]) { // the object corresponding to the pipeline
m_cluster->m_dataId=listIds[1];
f << "data[id]=" << getClusterName(listIds[1]) << ",";
}
}
unsigned long ulVal=input->readULong(4);
if (ulVal) {
f << "h0=" << (ulVal&0x7FFFFFFF);
if (ulVal&0x80000000) f << "[h],";
else f << ",";
}
val=static_cast<int>(input->readLong(2)); // always 1?
if (val!=1) f << "h1=" << val << ",";
listIds.clear();
if (!RagTime5StructManager::readDataIdList(input, 2, listIds)) {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: can not read the cluster list id\n"));
f << "##listClusterIds,";
return true;
}
if (listIds[0]) { // find some master layout and some master pipeline
m_cluster->m_masterId=listIds[0];
f << "id[master]=" << getClusterName(listIds[0]) << ",";
}
if (listIds[1]) { // find always layout
m_cluster->m_layoutId=listIds[1];
f << "id[layout]=" << getClusterName(listIds[1]) << ",";
}
val=static_cast<int>(input->readULong(2)); // 2[08a][01]
f << "fl=" << std::hex << val << std::dec << ",";
for (int i=0; i<2; ++i) { // h2=0|4|a, h3=small number
val=static_cast<int>(input->readLong(2));
if (val) f << "h" << i+2 << "=" << val << ",";
}
if (fSz==76) return true;
for (int i=0; i<7; ++i) { // g1, g2, g3 small int other 0
val=static_cast<int>(input->readLong(i==0 ? 2 : 4));
if (i==2)
m_link.m_N=val;
else if (val) f << "g" << i << "=" << val << ",";
}
m_link.m_fileType[1]=long(input->readULong(2));
m_link.m_fieldSize=static_cast<int>(input->readULong(2));
listIds.clear();
if (!RagTime5StructManager::readDataIdList(input, 1, listIds)) {
MWAW_DEBUG_MSG(("RagTime5PipelineInternal::PipelineCParser::parseZone: can not read the second list id\n"));
f << "##listIds2,";
return true;
}
if (listIds[0]) {
m_link.m_ids.clear();
m_link.m_ids.push_back(listIds[0]);
m_cluster->m_data2Link=m_link;
f << "data2=data" << listIds[0] << "A,";
}
return true;
}
protected:
//! the current cluster
std::shared_ptr<ClusterPipeline> m_cluster;
};
PipelineCParser::~PipelineCParser()
{
}
}
std::shared_ptr<RagTime5ClusterManager::Cluster> RagTime5Pipeline::readPipelineCluster(RagTime5Zone &zone, int zoneType)
{
auto clusterManager=m_mainParser.getClusterManager();
if (!clusterManager) {
MWAW_DEBUG_MSG(("RagTime5Pipeline::readPipelineCluster: oops can not find the cluster manager\n"));
return std::shared_ptr<RagTime5ClusterManager::Cluster>();
}
RagTime5PipelineInternal::PipelineCParser parser(*clusterManager, zoneType);
if (!clusterManager->readCluster(zone, parser) || !parser.getPipelineCluster()) {
MWAW_DEBUG_MSG(("RagTime5Pipeline::readPipelineCluster: oops can not find the cluster\n"));
return std::shared_ptr<RagTime5ClusterManager::Cluster>();
}
auto cluster=parser.getPipelineCluster();
if (cluster->m_dataLink.empty()) {
MWAW_DEBUG_MSG(("RagTime5Parser::readClusterPipelineData: can not find the main data\n"));
}
else {
RagTime5PipelineInternal::ClustListParser linkParser(*clusterManager, cluster->m_dataLink.m_fieldSize, "PipelineParent");
m_mainParser.readFixedSizeZone(cluster->m_dataLink, linkParser);
}
if (!cluster->m_data2Link.empty()) {
RagTime5PipelineInternal::UnknownParser data2Parser(cluster->m_data2Link.m_fieldSize, "PipelineUnknown");
m_mainParser.readFixedSizeZone(cluster->m_data2Link, data2Parser);
}
if (m_state->m_idPipelineMap.find(zone.m_ids[0])!=m_state->m_idPipelineMap.end()) {
MWAW_DEBUG_MSG(("RagTime5Pipeline::readPipelineCluster: cluster %d already exists\n", zone.m_ids[0]));
}
else
m_state->m_idPipelineMap[zone.m_ids[0]]=cluster;
return cluster;
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: