/* -*- 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 <librevenge/librevenge.h>
#include "MWAWInputStream.hxx"
#include "MWAWEntry.hxx"
#include "MWAWRSRCParser.hxx"
MWAWRSRCParser::MWAWRSRCParser(MWAWInputStreamPtr const &input)
: m_input(input)
, m_entryMap()
, m_asciiName("")
, m_asciiFile(MWAWInputStreamPtr())
, m_parsed(false)
{
}
MWAWRSRCParser::~MWAWRSRCParser()
{
#ifdef DEBUG
try {
libmwaw::DebugStream f;
for (auto it : m_entryMap) {
MWAWEntry &tEntry = it.second;
if (tEntry.isParsed()) continue;
if (tEntry.type()=="CODE") { // skip the code...
ascii().skipZone(tEntry.begin()-4, tEntry.end()-1);
continue;
}
f.str("");
f << "Entries(RSRC" << tEntry.type() << "):" << tEntry.id();
ascii().addPos(tEntry.begin()-4);
ascii().addNote(f.str().c_str());
ascii().addPos(tEntry.end());
ascii().addNote("_");
}
}
catch (...) {
}
#endif
ascii().reset();
}
MWAWEntry MWAWRSRCParser::getEntry(std::string type, int id) const
{
if (!m_parsed)
const_cast<MWAWRSRCParser *>(this)->parse();
auto it = m_entryMap.lower_bound(type);
while (it != m_entryMap.end()) {
if (it->first != type)
break;
MWAWEntry const &tEntry = (it++)->second;
if (tEntry.id()==id)
return tEntry;
}
return MWAWEntry();
}
bool MWAWRSRCParser::parse()
{
if (m_parsed)
return !m_entryMap.empty();
m_parsed = true;
if (!m_input) return false;
if (m_asciiName.length()) {
ascii().setStream(m_input);
ascii().open("RSRC");
}
try {
libmwaw::DebugStream f;
m_input->seek(0, librevenge::RVNG_SEEK_SET);
long pos = m_input->tell();
MWAWEntry data, map;
data.setBegin(m_input->readLong(4));
map.setBegin(m_input->readLong(4));
data.setLength(m_input->readLong(4));
map.setLength(m_input->readLong(4));
// data.length()==0 can be ok, if no data
if (!map.valid() || (!data.valid()&&data.length()!=0)) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parse: can not read the header\n"));
return false;
}
long endPos = data.end() > map.end() ? data.end() : map.end();
m_input->seek(endPos, librevenge::RVNG_SEEK_SET);
if (m_input->tell() != endPos) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parse: stream seems too small\n"));
return false;
}
f << "Header:";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
ascii().addPos(16);
ascii().addNote("_");
if (!parseMap(map, data.begin())) return false;
for (auto &it : m_entryMap) {
MWAWEntry &tEntry = it.second;
if (tEntry.begin()+4 >= data.end()) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseMap: can not read entry %s[%d]\n", tEntry.type().c_str(), tEntry.id()));
continue;
}
m_input->seek(tEntry.begin(), librevenge::RVNG_SEEK_SET);
tEntry.setBegin(tEntry.begin()+4);
const auto length = long(m_input->readULong(4));
if (tEntry.begin() + length > data.end()) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parse: entry %s[%d] has got invalid length %ld\n", tEntry.type().c_str(), tEntry.id(), length));
continue;
}
tEntry.setLength(length);
}
auto it = m_entryMap.lower_bound("vers");
while (it != m_entryMap.end()) {
if (it->first != "vers")
break;
MWAWEntry &tEntry = (it++)->second;
Version vers;
parseVers(tEntry, vers);
}
it = m_entryMap.lower_bound("STR ");
while (it != m_entryMap.end()) {
if (it->first != "STR ")
break;
std::string str;
MWAWEntry &tEntry = (it++)->second;
parseSTR(tEntry, str);
}
it = m_entryMap.lower_bound("STR#");
while (it != m_entryMap.end()) {
if (it->first != "STR#")
break;
std::vector<std::string> list;
MWAWEntry &tEntry = (it++)->second;
parseSTRList(tEntry, list);
}
}
catch (...) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parse: can not parse the input\n"));
return false;
}
return true;
}
bool MWAWRSRCParser::parseMap(MWAWEntry const &entry, long dataBegin)
{
if (!m_input) return false;
if (entry.length() < 28) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseMap: entry map is two short\n"));
return false;
}
libmwaw::DebugStream f, f2;
m_input->seek(entry.begin()+24, librevenge::RVNG_SEEK_SET);
f << "Entries(RSRCMap):";
auto offsetTypes=long(m_input->readULong(2));
auto offsetNameLists=long(m_input->readULong(2));
auto numTypes = static_cast<int>(m_input->readULong(2));
if (offsetTypes+2 > entry.length() || offsetNameLists > entry.length()) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseMap: the offsets seems bad\n"));
return false;
}
// this case can appear if no data
if (numTypes == 0xFFFF)
numTypes = -1;
f << "N=" << numTypes+1;
ascii().addPos(entry.begin());
ascii().addNote(f.str().c_str());
if (m_input->tell() != entry.begin()+offsetTypes+2) {
ascii().addPos(m_input->tell());
ascii().addNote("_");
}
long pos = entry.begin()+offsetTypes+2;
m_input->seek(pos, librevenge::RVNG_SEEK_SET);
if (pos+8*(numTypes+1) > entry.end()) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseMap: the type zones seems too short\n"));
return false;
}
std::vector<MWAWEntry> typesList;
for (int i = 0; i <= numTypes; i++) {
pos = m_input->tell();
f.str("");
f << "RSRCMap[Type" << i << "]:";
std::string type("");
for (int c = 0; c < 4; c++)
type+=char(m_input->readULong(1));
MWAWEntry tEntry;
tEntry.setType(type);
tEntry.setId(static_cast<int>(m_input->readULong(2))+1); // the number of entry
tEntry.setBegin(entry.begin()+offsetTypes+long(m_input->readULong(2)));
typesList.push_back(tEntry);
f << tEntry << ":" << std::hex << tEntry.begin() << std::dec << ",";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
m_input->seek(pos+8, librevenge::RVNG_SEEK_SET);
}
ascii().addPos(m_input->tell());
ascii().addNote("_");
for (auto const &tEntry : typesList) {
if (tEntry.begin()+12*tEntry.id() > entry.end()) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseMap: can not read entry %s[%d]\n", tEntry.type().c_str(), tEntry.id()));
continue;
}
m_input->seek(tEntry.begin(), librevenge::RVNG_SEEK_SET);
for (int n = 0; n < tEntry.id(); n++) {
pos = m_input->tell();
f.str("");
f << "RSRCMap[Rsrc" << n << "]:";
MWAWEntry rsrc(tEntry);
rsrc.setId(static_cast<int>(m_input->readLong(2)));
auto offset = long(m_input->readULong(2));
if (offset != 0xFFFF) {
std::string name("");
if (offset+offsetNameLists+1 <= entry.length()) {
long actPos = m_input->tell();
m_input->seek(entry.begin()+offset+offsetNameLists, librevenge::RVNG_SEEK_SET);
auto nSz = static_cast<int>(m_input->readULong(1));
if (offset+offsetNameLists+1+nSz <= entry.length()) {
for (int j = 0; j < nSz; j++)
name+=char(m_input->readULong(1));
f2.str("");
f2 << "nameList:" << name;
ascii().addPos(entry.begin()+offset+offsetNameLists);
ascii().addNote(f2.str().c_str());
}
m_input->seek(actPos, librevenge::RVNG_SEEK_SET);
}
if (!name.length()) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseMap: can not read name of entry %s[%d]\n", tEntry.type().c_str(), tEntry.id()));
f << "#listNamesOffset=" << std::hex << offset << std::dec << ",";
}
else {
rsrc.setName(name);
f << "name=" << name << ",";
}
}
unsigned long dOffset = m_input->readULong(4);
if (dOffset & 0xFF000000) {
f << "attributes=" << (dOffset>>12) << ",";
dOffset &= 0xFFFFFF;
}
rsrc.setBegin(dataBegin+long(dOffset));
m_entryMap.insert(std::multimap<std::string, MWAWEntry>::value_type(rsrc.type(), rsrc));
f << rsrc.type() << ":" << rsrc.id() << ",";
f << "pos=" << std::hex << rsrc.begin() << std::dec << ",";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
m_input->seek(pos+12, librevenge::RVNG_SEEK_SET);
}
}
if (offsetNameLists != entry.length()) {
ascii().addPos(entry.begin()+offsetNameLists);
ascii().addNote("RSRCMap[nameList]");
}
return true;
}
////////////////////////////////////////////////////////////
// read a string resource
// -16396: application-missing name string resource
// -16397: application-missing string resource
////////////////////////////////////////////////////////////
bool MWAWRSRCParser::parseSTR(MWAWEntry const &entry, std::string &str)
{
str="";
if (!m_input || !entry.valid()) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseSTR: entry is invalid\n"));
return false;
}
entry.setParsed(true);
libmwaw::DebugStream f;
m_input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
auto sz = long(m_input->readULong(1));
if (sz+1 > entry.length()) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseSTR: string length is too small\n"));
return false;
}
for (long i = 0; i < sz; i++) {
if (m_input->isEnd()) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseSTR: file is too short\n"));
return false;
}
str += char(m_input->readULong(1));
}
f << "Entries(RSRCSTR)[" << entry.type() << ":" << entry.id() << "]:" << str;
if (sz+1 != entry.length()) {
// can this happens ?
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseSTR: ARGGS multiple strings\n"));
ascii().addDelimiter(m_input->tell(),'|');
f << "###";
}
ascii().addPos(entry.begin()-4);
ascii().addNote(f.str().c_str());
return true;
}
bool MWAWRSRCParser::parseSTRList(MWAWEntry const &entry, std::vector<std::string> &list)
{
list.resize(0);
if (!m_input || !entry.valid() || entry.length()<2) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseSTRList: the entry is bad\n"));
return false;
}
entry.setParsed(true);
long pos = entry.begin();
long endPos = entry.end();
m_input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(RSRCListStr)[" << entry.type() << ":" << entry.id() << "]:";
auto N=static_cast<int>(m_input->readULong(2));
ascii().addPos(pos-4);
ascii().addNote(f.str().c_str());
for (int i = 0; i < N; i++) {
f.str("");
f << "RSRCListStr-" << i << ":";
pos = m_input->tell();
if (pos+1 > endPos) {
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseSTRList: can not read string %d\n", i));
return false;
}
auto sz = static_cast<int>(m_input->readULong(1));
if (pos+1+sz > endPos) {
f.str("");
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseSTRList: string size %d is bad\n", i));
return false;
}
std::string str("");
for (int c=0; c < sz; c++)
str += char(m_input->readULong(1));
list.push_back(str);
f << str << ",";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
return true;
}
bool MWAWRSRCParser::parseClut(MWAWEntry const &entry, std::vector<MWAWColor> &list)
{
list.resize(0);
if (!m_input || !entry.valid() || entry.length()<8) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseClut: the entry is bad\n"));
return false;
}
entry.setParsed(true);
long pos = entry.begin();
// skip seed
m_input->seek(pos+4, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "Entries(RSRCClut)[" << entry.type() << ":" << entry.id() << "]:";
auto flags = static_cast<int>(m_input->readULong(2));
if (flags==0x8000) f << "indexed,";
else if (flags) f << "#flags=" << std::hex << flags << ",";
auto N= static_cast<int>(m_input->readULong(2));
if (entry.length()==8+8*(N+1)) // N can be num or maxId
N++;
f << "N=" << N << ",";
if (entry.length()!=8+8*N) {
f << "###";
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseClut: find unexpected size/format\n"));
ascii().addPos(pos-4);
ascii().addNote(f.str().c_str());
return false;
}
ascii().addPos(pos-4);
ascii().addNote(f.str().c_str());
for (int i = 0; i < N; i++) {
pos = m_input->tell();
f.str("");
f << "RSRCClut-" << i << ":";
auto index = static_cast<int>(m_input->readULong(2));
if (index != i) {
static bool first=true;
if (first) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseClut: find some odd index value\n"));
first = false;
}
f << "#index=" << index << ",";
}
unsigned char col[3];
for (auto &c : col) c = static_cast<unsigned char>(m_input->readULong(2)>>8);
MWAWColor color(col[0],col[1],col[2]);
list.push_back(color);
f << color << ",";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
return true;
}
////////////////////////////////////////////////////////////
// read the version:
// id=1 version of the appliction
// id=2 version of the file
////////////////////////////////////////////////////////////
std::ostream &operator<< (std::ostream &o, MWAWRSRCParser::Version const &vers)
{
o << vers.m_string;
if (vers.m_versionString.length())
o << "(" << vers.m_versionString << ")";
o << ",";
o << "vers=" << vers.m_majorVersion;
if (vers.m_minorVersion)
o << "(" << vers.m_minorVersion << ")";
o << ",";
if (vers.m_countryCode)
o << "country=" << std::hex << vers.m_countryCode << std::dec << ",";
o << vers.m_extra;
return o;
}
bool MWAWRSRCParser::parseVers(MWAWEntry const &entry, Version &vers)
{
vers = Version();
if (!m_input || !entry.valid() || entry.length()<8) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseVers: entry is invalid\n"));
return false;
}
entry.setParsed(true);
libmwaw::DebugStream f;
m_input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
vers.m_majorVersion = static_cast<int>(m_input->readULong(1));
vers.m_minorVersion = static_cast<int>(m_input->readULong(1));
auto val = long(m_input->readULong(1));
if (val) f << "devStage=" << val << ",";
val = long(m_input->readULong(1));
if (val) f << "preReleaseLevel=" << std::hex << val << std::dec << ",";
vers.m_countryCode = static_cast<int>(m_input->readULong(2));
for (int i = 0; i < 2; i++) {
auto sz = static_cast<int>(m_input->readULong(1));
long pos = m_input->tell();
if (pos+sz > entry.end()) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parseVers: can not read strings %d\n",i));
return false;
}
std::string str("");
for (int c = 0; c < sz; c++)
str+=char(m_input->readULong(1));
if (i==0)
vers.m_versionString = str;
else
vers.m_string = str;
}
vers.m_extra = f.str();
f << "Entries(RSRCvers)[" << entry.id() << "]:" << vers;
ascii().addPos(entry.begin()-4);
ascii().addNote(f.str().c_str());
return true;
}
////////////////////////////////////////////////////////////
// read a pict resource
////////////////////////////////////////////////////////////
bool MWAWRSRCParser::parsePICT(MWAWEntry const &entry, librevenge::RVNGBinaryData &pict)
{
pict.clear();
if (!m_input || !entry.valid() || entry.length()<0xd) {
MWAW_DEBUG_MSG(("MWAWRSRCParser::parsePICT: entry is invalid\n"));
return false;
}
libmwaw::DebugStream f;
f << "Entries(RSRC" << entry.type() << ")[" << entry.id() << "]:";
m_input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
m_input->readDataBlock(entry.length(), pict);
#ifdef DEBUG_WITH_FILES
if (!entry.isParsed()) {
ascii().skipZone(entry.begin(), entry.end()-1);
libmwaw::DebugStream f2;
f2 << "RSRC-" << entry.type() << "_" << entry.id() << ".pct";
libmwaw::Debug::dumpFile(pict, f2.str().c_str());
}
#endif
ascii().addPos(entry.begin()-4);
ascii().addNote(f.str().c_str());
entry.setParsed(true);
return true;
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: