You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
605 lines
13 KiB
605 lines
13 KiB
15 years ago
|
/* This file is part of the LibMSWrite Library
|
||
|
Copyright (C) 2001-2003 Clarence Dang <clarencedang@users.sourceforge.net>
|
||
|
|
||
|
This library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Library General Public
|
||
|
License Version 2 as published by the Free Software Foundation.
|
||
|
|
||
|
This library is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
Library General Public License Version 2 for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Library General Public License
|
||
|
Version 2 along with this library; see the file COPYING.LIB. If not,
|
||
|
write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
|
||
|
LibMSWrite Project Website:
|
||
|
http://sourceforge.net/projects/libmswrite/
|
||
|
*/
|
||
|
|
||
|
#ifndef __LIBMSWRITE_DEFS_H__
|
||
|
#define __LIBMSWRITE_DEFS_H__
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "config.libmswrite.h"
|
||
|
#include "list.h"
|
||
|
|
||
|
namespace MSWrite
|
||
|
{
|
||
|
// library version
|
||
|
extern const char *Version;
|
||
|
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* Basic data types
|
||
|
* (should be correct for 16-bit, 32-bit and 64-bit systems)
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
typedef unsigned char Byte; // 1 byte
|
||
|
typedef unsigned short Word; // 2 bytes
|
||
|
typedef signed short Short; // 2 bytes
|
||
|
|
||
|
// undefine for 16-bit (although the code is untested)
|
||
|
#define ATLEAST32BIT
|
||
|
|
||
|
#ifdef ATLEAST32BIT // 32-bit or 64-bit system
|
||
|
typedef unsigned int DWord; // 4 bytes
|
||
|
typedef signed int Long; // 4 bytes
|
||
|
#else
|
||
|
typedef unsigned long DWord; // 4 bytes
|
||
|
typedef signed long Long; // 4 Bytes
|
||
|
#endif
|
||
|
|
||
|
|
||
|
//
|
||
|
// possible errors passed to Devices
|
||
|
//
|
||
|
class Error
|
||
|
{
|
||
|
public:
|
||
|
static const int Ok = 0;
|
||
|
static const int Warn = 1;
|
||
|
static const int InvalidFormat = 2;
|
||
|
static const int OutOfMemory = 3;
|
||
|
static const int InternalError = 4;
|
||
|
static const int Unsupported = 5;
|
||
|
static const int FileError = 6;
|
||
|
|
||
|
static const int LastOne = 255;
|
||
|
// for your own Errors, only use values >= 256 (values < 256 are reserved)
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Handy conversion functions for the following:
|
||
|
*
|
||
|
* Twip = 1/20 Points/Pixels
|
||
|
* Point = 1/72 inch
|
||
|
* Inch = 25.4 mm
|
||
|
*
|
||
|
* LibMSWrite will return all values (except font sizes) in twips for
|
||
|
* maximum accuracy. It is your responsibility to convert the
|
||
|
* measurements to whatever you want.
|
||
|
*
|
||
|
*/
|
||
|
#define Twip2Point(val) ((val) / 20)
|
||
|
#define Point2Twip(val) ((val) * 20)
|
||
|
|
||
|
#define Twip2Inch(val) ((val) / 1440)
|
||
|
#define Inch2Twip(val) ((val) * 1440)
|
||
|
|
||
|
#define Point2Inch(val) ((val) / 72)
|
||
|
#define Inch2Point(val) ((val) * 72)
|
||
|
|
||
|
#define Twip2Milli(val) ((val) / 56.6929)
|
||
|
#define Milli2Twip(val) ((val) * 56.6929)
|
||
|
|
||
|
#define Point2Milli(val) ((val) / 2.83465)
|
||
|
#define Milli2Point(val) ((val) * 2.83465)
|
||
|
|
||
|
#define Inch2Mill(val) ((val) * 25.4)
|
||
|
#define Milli2Inch(val) ((val) / 25.4)
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* Portable functions for reading a basic data type from a Byte array
|
||
|
*
|
||
|
*/
|
||
|
inline Byte ReadByte (Byte &dest, const Byte *src)
|
||
|
{
|
||
|
return dest = *src;
|
||
|
}
|
||
|
|
||
|
inline Word ReadWord (Word &dest, const Byte *src)
|
||
|
{
|
||
|
return dest = ((Word) src [0]) |
|
||
|
(((Word) src [1]) << 8);
|
||
|
}
|
||
|
|
||
|
inline Short ReadShort (Short &dest, const Byte *src)
|
||
|
{
|
||
|
return (Short) ReadWord ((Word &) dest, src);
|
||
|
}
|
||
|
|
||
|
inline DWord ReadDWord (DWord &dest, const Byte *src)
|
||
|
{
|
||
|
return dest = ((DWord) src [0]) |
|
||
|
(((DWord) src [1]) << 8) |
|
||
|
(((DWord) src [2]) << 16) |
|
||
|
(((DWord) src [3]) << 24);
|
||
|
}
|
||
|
|
||
|
inline Long ReadLong (Long &dest, const Byte *src)
|
||
|
{
|
||
|
return (Long) ReadDWord ((DWord &) dest, src);
|
||
|
}
|
||
|
|
||
|
inline Byte WriteByte (const Byte &src, Byte *dest)
|
||
|
{
|
||
|
*dest = src;
|
||
|
return src;
|
||
|
}
|
||
|
|
||
|
inline Word WriteWord (const Word &src, Byte *dest)
|
||
|
{
|
||
|
dest [0] = (Byte) (src & 255);
|
||
|
dest [1] = (Byte) (src >> 8);
|
||
|
return src;
|
||
|
}
|
||
|
|
||
|
inline Short WriteShort (const Short &src, Byte *dest)
|
||
|
{
|
||
|
WriteWord ((Word) src, dest);
|
||
|
return src;
|
||
|
}
|
||
|
|
||
|
inline DWord WriteDWord (const DWord &src, Byte *dest)
|
||
|
{
|
||
|
dest [0] = (Byte) (src & 255);
|
||
|
dest [1] = (Byte) ((src >> 8) & 255);
|
||
|
dest [2] = (Byte) ((src >> 16) & 255);
|
||
|
dest [3] = (Byte) (src >> 24);
|
||
|
return src;
|
||
|
}
|
||
|
|
||
|
inline Long WriteLong (const Long &src, Byte *dest)
|
||
|
{
|
||
|
WriteDWord ((DWord) src, dest);
|
||
|
return src;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* Portable functions for reading/writing some bits from/to a Byte
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
// optimisation freak, I know :)
|
||
|
#define BitMask1 1
|
||
|
#define BitMask2 3
|
||
|
#define BitMask3 7
|
||
|
#define BitMask4 15
|
||
|
#define BitMask5 31
|
||
|
#define BitMask6 63
|
||
|
#define BitMask7 127
|
||
|
#define BitMask8 255
|
||
|
|
||
|
#define ReadBitsFromByte(destbits,srcbyte,start,len) \
|
||
|
((destbits)=((srcbyte)>>(start))&(BitMask##len))
|
||
|
// NOTE: it is your fault if destbyte is not 0 to start with!
|
||
|
#define WriteBitsToByte(srcbits,destbyte,start,len) \
|
||
|
((destbyte)|=((Byte(srcbits)&(BitMask##len))<<(start)))
|
||
|
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* Device that either reads or writes from/to .WRI files
|
||
|
*
|
||
|
*/
|
||
|
class Device
|
||
|
{
|
||
|
private:
|
||
|
static const int MaxCacheDepth = 32;
|
||
|
long m_posInternal;
|
||
|
Byte *m_cache [MaxCacheDepth];
|
||
|
int m_cacheCount;
|
||
|
|
||
|
static const int MaxDebugLen = 1024;
|
||
|
char m_debugTemp [MaxDebugLen];
|
||
|
|
||
|
protected:
|
||
|
int m_error;
|
||
|
|
||
|
public:
|
||
|
Device () : m_posInternal (0), m_cacheCount (0), m_error (0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
virtual ~Device ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Do _not_ try to override these functions!
|
||
|
// They are used internally by LibMSWrite so that reads/writes can
|
||
|
// be redirected from/to memory blocks.
|
||
|
//
|
||
|
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* setCache specifies whether or not Device should start reading from
|
||
|
* memory block specified by @param cache, instead of from user defined
|
||
|
* functions (which probably read from a file).
|
||
|
*
|
||
|
* setting @p cache to NULL stops the Device from reading from the
|
||
|
* last memory block specified.
|
||
|
*
|
||
|
*/
|
||
|
bool setCache (Byte *const cache)
|
||
|
{
|
||
|
if (cache)
|
||
|
{
|
||
|
m_cache [m_cacheCount] = cache;
|
||
|
m_cacheCount++;
|
||
|
if (m_cacheCount > MaxCacheDepth)
|
||
|
{
|
||
|
error (Error::InternalError, "too many caches\n");
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
--m_cacheCount;
|
||
|
if (m_cacheCount < 0)
|
||
|
{
|
||
|
error (Error::InternalError, "too few caches\n");
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool readInternal (Byte *buf, const long numBytes)
|
||
|
{
|
||
|
bool ret;
|
||
|
|
||
|
if (m_cacheCount)
|
||
|
{
|
||
|
memcpy (buf, m_cache [m_cacheCount - 1], numBytes);
|
||
|
m_cache [m_cacheCount - 1] += numBytes;
|
||
|
ret = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ret = read (buf, numBytes);
|
||
|
if (ret) m_posInternal += numBytes;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool writeInternal (const Byte *buf, const long numBytes)
|
||
|
{
|
||
|
bool ret;
|
||
|
|
||
|
if (m_cacheCount)
|
||
|
{
|
||
|
memcpy (m_cache [m_cacheCount - 1], buf, numBytes);
|
||
|
m_cache [m_cacheCount - 1] += numBytes;
|
||
|
ret = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ret = write (buf, numBytes);
|
||
|
if (ret) m_posInternal += numBytes;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool seekInternal (const long offset, const int whence)
|
||
|
{
|
||
|
bool ret = seek (offset, whence);
|
||
|
|
||
|
if (ret)
|
||
|
{
|
||
|
switch (whence)
|
||
|
{
|
||
|
case SEEK_SET:
|
||
|
m_posInternal = offset;
|
||
|
break;
|
||
|
case SEEK_CUR:
|
||
|
m_posInternal += offset;
|
||
|
break;
|
||
|
case SEEK_END:
|
||
|
m_posInternal = tell ();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
long tellInternal (void) const
|
||
|
{
|
||
|
// does not reflect the cache!
|
||
|
return m_posInternal;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// convenience functions
|
||
|
//
|
||
|
|
||
|
void debug (const char *s, const int i)
|
||
|
{
|
||
|
snprintf (m_debugTemp, MaxDebugLen - 1, "%s%i\n", s, i);
|
||
|
m_debugTemp [MaxDebugLen - 1] = 0;
|
||
|
debug (m_debugTemp);
|
||
|
}
|
||
|
void debug (const char *s1, const char *s2)
|
||
|
{
|
||
|
snprintf (m_debugTemp, MaxDebugLen - 1, "%s%s\n", s1, s2);
|
||
|
m_debugTemp [MaxDebugLen - 1] = 0;
|
||
|
debug (m_debugTemp);
|
||
|
}
|
||
|
|
||
|
void debug (const char *s1, const Byte *s2)
|
||
|
{
|
||
|
debug (s1, (const char *) s2);
|
||
|
}
|
||
|
|
||
|
#define Dump(name) m_device->debug("\t" #name ": ", m_##name)
|
||
|
|
||
|
bool good (void) const
|
||
|
{
|
||
|
return m_error == 0;
|
||
|
}
|
||
|
|
||
|
int bad (void) const
|
||
|
{
|
||
|
return m_error;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* Functions that the user must implement
|
||
|
*
|
||
|
* If the seek function is called to move past EOF (when writing),
|
||
|
* pad from EOF to the new location with 0's and then seek.
|
||
|
*
|
||
|
* It is your responsibility to call error() if a file op fails.
|
||
|
*
|
||
|
*/
|
||
|
virtual bool read (Byte *buf, const DWord numBytes) = 0;
|
||
|
virtual bool write (const Byte *buf, const DWord numBytes) = 0;
|
||
|
virtual bool seek (const long offset, const int whence) = 0;
|
||
|
virtual long tell (void) = 0;
|
||
|
virtual void debug (const char *s)
|
||
|
{
|
||
|
fprintf (stderr, "%s", s);
|
||
|
}
|
||
|
virtual void debug (const int i)
|
||
|
{
|
||
|
fprintf (stderr, "%i", i);
|
||
|
}
|
||
|
static const DWord NoToken = DWord (0xABCD1234); // hopefully won't clash with any values
|
||
|
virtual void error (const int errorCode, const char *message,
|
||
|
const char *file = "", const int lineno = 0,
|
||
|
DWord token = NoToken)
|
||
|
{
|
||
|
// errors do not really include warnings after all...
|
||
|
if (errorCode != Error::Warn)
|
||
|
m_error = errorCode;
|
||
|
|
||
|
if (lineno)
|
||
|
fprintf (stderr, "%s:%i:", file, lineno);
|
||
|
|
||
|
// TODO: why the \n?
|
||
|
if (token == NoToken)
|
||
|
fprintf (stderr, "%s\n", message);
|
||
|
else
|
||
|
fprintf (stderr, "%s (val=%li)\n", message, long (token));
|
||
|
}
|
||
|
|
||
|
#define ErrorAndQuit(errorCode,message) \
|
||
|
{ \
|
||
|
m_device->error(errorCode,message); \
|
||
|
return false; \
|
||
|
}
|
||
|
|
||
|
#define Verify(errorCode,expr,token) \
|
||
|
((expr)? true : (m_device->error(errorCode, "check \'" #expr "\' failed",__FILE__,__LINE__,token),m_device->good ()))
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* Device capable only of reading/writing to memory blocks
|
||
|
* (used internally by LibMSWrite)
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
class MemoryDevice : public Device
|
||
|
{
|
||
|
public:
|
||
|
MemoryDevice ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
virtual ~MemoryDevice ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
bool read (Byte * /*buf*/, const DWord /*numBytes*/)
|
||
|
{
|
||
|
error (Error::InternalError, "memory device not reading from memory?\n");
|
||
|
return false;
|
||
|
}
|
||
|
bool write (const Byte * /*buf*/, const DWord /*numBytes*/)
|
||
|
{
|
||
|
error (Error::InternalError, "memory device not writing to memory?\n");
|
||
|
return false;
|
||
|
}
|
||
|
bool seek (const long /*offset*/, const int /*whence*/)
|
||
|
{
|
||
|
error (Error::InternalError, "memory device cannot seek full stop!\n");
|
||
|
return false;
|
||
|
}
|
||
|
long tell (void)
|
||
|
{
|
||
|
error (Error::InternalError, "memory device not accessing memory?\n");
|
||
|
return -1;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class NeedsDevice
|
||
|
{
|
||
|
protected:
|
||
|
Device *m_device;
|
||
|
|
||
|
public:
|
||
|
NeedsDevice (Device *device = NULL)
|
||
|
{
|
||
|
setDevice (device);
|
||
|
}
|
||
|
|
||
|
virtual ~NeedsDevice ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void setDevice (Device *const device)
|
||
|
{
|
||
|
m_device = device;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
#ifdef CHECK_INTERNAL
|
||
|
#define CHECK_DEVICE_ERROR fprintf (stderr, "%s:%i: INTERNAL ERROR - device not set\n", __FILE__, __LINE__)
|
||
|
#define CHECK_DEVICE \
|
||
|
if (!m_device) \
|
||
|
{ \
|
||
|
CHECK_DEVICE_ERROR; \
|
||
|
return false; \
|
||
|
}
|
||
|
#else
|
||
|
#define CHECK_DEVICE_ERROR
|
||
|
#define CHECK_DEVICE
|
||
|
#endif
|
||
|
|
||
|
// [PRIVATE]
|
||
|
class UseThisMuchPrefixSize
|
||
|
{
|
||
|
private:
|
||
|
int m_val;
|
||
|
|
||
|
public:
|
||
|
UseThisMuchPrefixSize (const int val = 0)
|
||
|
{
|
||
|
setVal (val);
|
||
|
}
|
||
|
|
||
|
~UseThisMuchPrefixSize ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
bool operator== (const UseThisMuchPrefixSize &rhs)
|
||
|
{
|
||
|
return m_val == rhs.m_val;
|
||
|
}
|
||
|
|
||
|
UseThisMuchPrefixSize &operator= (const UseThisMuchPrefixSize &rhs)
|
||
|
{
|
||
|
if (this == &rhs)
|
||
|
return *this;
|
||
|
|
||
|
m_val = rhs.m_val;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
int getVal (void) const { return m_val; }
|
||
|
void setVal (const int val) { m_val = val; }
|
||
|
};
|
||
|
|
||
|
// [PRIVATE]
|
||
|
class UseThisMuch
|
||
|
{
|
||
|
private:
|
||
|
List <UseThisMuchPrefixSize> m_notDefaultBits;
|
||
|
|
||
|
protected:
|
||
|
UseThisMuch &operator= (const UseThisMuch &rhs)
|
||
|
{
|
||
|
if (this == &rhs)
|
||
|
return *this;
|
||
|
|
||
|
this->m_notDefaultBits = rhs.m_notDefaultBits;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
void signalHaveSetData (const bool isDefault, const int needNumBits)
|
||
|
{
|
||
|
if (isDefault)
|
||
|
{
|
||
|
// it's a delete operation then
|
||
|
List <UseThisMuchPrefixSize>::Iterator it = m_notDefaultBits.search (needNumBits);
|
||
|
if (it != m_notDefaultBits.end ()) m_notDefaultBits.erase (it);
|
||
|
}
|
||
|
// possibly a new value
|
||
|
else
|
||
|
{
|
||
|
List <UseThisMuchPrefixSize>::Iterator it = m_notDefaultBits.search (needNumBits);
|
||
|
|
||
|
// new value
|
||
|
if (it == m_notDefaultBits.end ())
|
||
|
{
|
||
|
UseThisMuchPrefixSize utmps (needNumBits);
|
||
|
m_notDefaultBits.addToBack (utmps);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// not bits
|
||
|
int getNeedNumDataBytes (void) const
|
||
|
{
|
||
|
int biggest = 0;
|
||
|
List <UseThisMuchPrefixSize>::Iterator it;
|
||
|
for (it = m_notDefaultBits.begin (); it != m_notDefaultBits.end (); it++)
|
||
|
{
|
||
|
if ((*it).getVal () > biggest)
|
||
|
biggest = (*it).getVal ();
|
||
|
}
|
||
|
|
||
|
if (biggest % 8)
|
||
|
return biggest / 8 + 1; // account for fractional byte
|
||
|
else
|
||
|
return biggest / 8;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
UseThisMuch ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
virtual ~UseThisMuch ()
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // namespace MSWrite {
|
||
|
|
||
|
#endif // __LIBMSWRITE_DEFS_H__
|
||
|
|
||
|
// end of libmswrite_defs.h
|