raze/polymer/eduke32/build/src/util/arttool.cc
hendricks266 a418bec8c2 Tools updates:
- Eliminate use of the C++ standard library from arttool. I improved some of the program's workings but it continues to corrupt my art files in certain circumstances. (Note: Not a regression; it happens with previous revisions.)
- Construct "make utils" for the game side following the pattern of the engine.
- Move ivfrate to this new distinction.
- Minor Makefile cleanup and tweaks, mainly to facilitate CLANG=1 and to formalize text output in special cases.

git-svn-id: https://svn.eduke32.com/eduke32@3163 1a8010ca-5511-0410-912e-c29ae57300e0
2012-11-15 06:42:37 +00:00

1051 lines
29 KiB
C++

/**
* BUILD ART file editing tool
* @author Jonathon Fowler
* @license Artistic License 2.0 (http://www.perlfoundation.org/artistic_license_2_0)
*/
// Bstring and C++ STL --> C conversion by Hendricks266
#include <stdio.h>
#include <string.h>
#include "compat.h"
////////// Bstring //////////
class Bstring {
public:
Bstring(void);
Bstring(const char*);
Bstring(const Bstring&);
~Bstring(void);
operator const char*() const;
const char* operator()(void) const;
char& operator[](int);
Bstring& operator=(const char*);
Bstring& operator=(const Bstring&);
Bstring& operator+=(const char*);
Bstring& operator+=(const Bstring&);
bool operator==(const Bstring&) const;
bool operator!=(const Bstring&) const;
bool operator< (const Bstring&) const;
bool operator<=(const Bstring&) const;
bool operator> (const Bstring&) const;
bool operator>=(const Bstring&) const;
bool operator==(const char*) const;
bool operator!=(const char*) const;
bool operator< (const char*) const;
bool operator<=(const char*) const;
bool operator> (const char*) const;
bool operator>=(const char*) const;
int compare(const char*) const;
int compare(const Bstring&) const;
unsigned length(void) const;
void clear(void);
protected:
char* data;
};
Bstring::Bstring(void) { data = NULL; }
Bstring::Bstring(const Bstring &value) {
if (&value != this)
(*this)=value();
}
Bstring::Bstring(const char *str) {
data = NULL;
(*this)=str;
}
Bstring::~Bstring(void) { clear(); }
Bstring::operator const char*() const { return data; }
const char* Bstring::operator()(void) const { return data; }
char& Bstring::operator[](int index) { return data[index]; }
Bstring& Bstring::operator=(const Bstring &value)
{
if (&value != this)
(*this)=value();
return *this;
}
Bstring& Bstring::operator=(const char *str)
{
clear();
data = Bstrdup(str);
return *this;
}
Bstring& Bstring::operator+=(const Bstring &value)
{
(*this)+=value();
return *this;
}
Bstring& Bstring::operator+=(const char *str)
{
data = (char*) Brealloc(data, (Bstrlen(data) + Bstrlen(str) + 1) * sizeof(char));
Bstrcat(data, str);
return *this;
}
bool Bstring::operator==(const Bstring &value) const { return Bstrcmp(data, value()) == 0; }
bool Bstring::operator!=(const Bstring &value) const { return Bstrcmp(data, value()) != 0; }
bool Bstring::operator< (const Bstring &value) const { return Bstrcmp(data, value()) < 0; }
bool Bstring::operator<=(const Bstring &value) const { return Bstrcmp(data, value()) <= 0; }
bool Bstring::operator> (const Bstring &value) const { return Bstrcmp(data, value()) > 0; }
bool Bstring::operator>=(const Bstring &value) const { return Bstrcmp(data, value()) >= 0; }
bool Bstring::operator==(const char *str) const { return Bstrcmp(data, str) == 0; }
bool Bstring::operator!=(const char *str) const { return Bstrcmp(data, str) != 0; }
bool Bstring::operator< (const char *str) const { return Bstrcmp(data, str) < 0; }
bool Bstring::operator<=(const char *str) const { return Bstrcmp(data, str) <= 0; }
bool Bstring::operator> (const char *str) const { return Bstrcmp(data, str) > 0; }
bool Bstring::operator>=(const char *str) const { return Bstrcmp(data, str) >= 0; }
int Bstring::compare(const Bstring &value) const { return Bstrcmp(data,value()); }
int Bstring::compare(const char *str) const { return Bstrcmp(data,str); }
unsigned Bstring::length(void) const { return Bstrlen(data); }
void Bstring::clear(void)
{
if (data != NULL)
Bfree(data);
data = NULL;
}
////////// arttool //////////
void usage()
{
Bprintf("BUILD ART file editing tool\n");
Bprintf("Copyright (C) 2008 Jonathon Fowler <jf@jonof.id.au>\n");
Bprintf("Released under the Artistic License 2.0\n");
Bprintf("\n");
Bprintf(" arttool create [options]\n");
Bprintf(" -f <filenum> Selects which numbered ART file to create (default 0)\n");
Bprintf(" -o <offset> Specifies the first tile in the file (default 0)\n");
Bprintf(" -n <ntiles> The number of tiles for the art file (default 256)\n");
Bprintf(" Creates an empty ART file named 'tilesXXX.art'\n");
Bprintf("\n");
Bprintf(" arttool addtile [options] <tilenum> <filename>\n");
Bprintf(" -x <pixels> X-centre\n");
Bprintf(" -y <pixels> Y-centre\n");
Bprintf(" -ann <frames> Animation frame span\n");
Bprintf(" -ant <type> Animation type (0=none, 1=oscillate, 2=forward, 3=reverse)\n");
Bprintf(" -ans <speed> Animation speed\n");
Bprintf(" Adds a tile to the 'tilesXXX.art' set from a TGA or PCX source\n");
Bprintf("\n");
Bprintf(" arttool rmtile <tilenum>\n");
Bprintf(" Removes a tile from the 'tilesXXX.art' set\n");
Bprintf("\n");
Bprintf(" arttool tileprop [options] <tilenum>\n");
Bprintf(" -x <pixels> X-centre\n");
Bprintf(" -y <pixels> Y-centre\n");
Bprintf(" -ann <frames> Animation frame span, may be negative\n");
Bprintf(" -ant <type> Animation type (0=none, 1=oscillate, 2=forward, 3=reverse)\n");
Bprintf(" -ans <speed> Animation speed\n");
Bprintf(" Changes tile properties\n");
Bprintf("\n");
}
class ARTFile {
private:
Bstring filename_;
long localtilestart_;
long localtileend_;
short * tilesizx_;
short * tilesizy_;
long * picanm_;
// for removing or replacing tile data
int markprelength_, markskiplength_, markpostlength_;
char * insert_;
int insertlen_;
void writeShort(BFILE *ofs, short s)
{
Bassert(ofs);
char d[2] = { static_cast<char>(s&255), static_cast<char>((s>>8)&255) };
Bfwrite(d,1,2,ofs); // 2 == sizeof(d)
}
void writeLong(BFILE *ofs, long l)
{
Bassert(ofs);
char d[4] = { static_cast<char>(l&255), static_cast<char>((l>>8)&255), static_cast<char>((l>>16)&255), static_cast<char>((l>>24)&255) };
Bfwrite(d,1,4,ofs); // 4 == sizeof(d)
}
short readShort(BFILE *ifs)
{
Bassert(ifs);
unsigned char d[2];
unsigned short s;
Bfread(d,1,2,ifs); // 2 == sizeof(d)
s = (unsigned short)d[0];
s |= (unsigned short)d[1] << 8;
return (short)s;
}
long readLong(BFILE *ifs)
{
Bassert(ifs);
unsigned char d[4];
unsigned long l;
Bfread(d,1,4,ifs); // 4 == sizeof(d)
l = (unsigned long)d[0];
l |= (unsigned long)d[1] << 8;
l |= (unsigned long)d[2] << 16;
l |= (unsigned long)d[3] << 24;
return (long)l;
}
void dispose()
{
if (tilesizx_) delete [] tilesizx_;
if (tilesizy_) delete [] tilesizy_;
if (picanm_) delete [] picanm_;
if (insert_) delete [] insert_;
insert_ = 0;
insertlen_ = 0;
}
void load()
{
BFILE *infile = Bfopen(filename_(),"rb");
int i, ntiles;
if (infile != NULL && readLong(infile) == 1)
{
readLong(infile); // skip the numtiles
dispose();
localtilestart_ = readLong(infile);
localtileend_ = readLong(infile);
ntiles = localtileend_ - localtilestart_ + 1;
tilesizx_ = new short[ntiles];
tilesizy_ = new short[ntiles];
picanm_ = new long[ntiles];
for (i = 0; i < ntiles; ++i) {
tilesizx_[i] = readShort(infile);
}
for (i = 0; i < ntiles; ++i) {
tilesizy_[i] = readShort(infile);
}
for (i = 0; i < ntiles; ++i) {
picanm_[i] = readLong(infile);
}
}
}
public:
ARTFile(Bstring filename)
: filename_(filename), localtilestart_(0), localtileend_(-1),
tilesizx_(0), tilesizy_(0), picanm_(0),
markprelength_(0), markskiplength_(0), markpostlength_(0),
insert_(0), insertlen_(0)
{
load();
}
~ARTFile()
{
dispose();
}
/**
* Sets up for an empty file
* @param start the starting tile
* @param ntiles the number of tiles total
*/
void init(int start, int ntiles)
{
dispose();
localtilestart_ = start;
localtileend_ = start + ntiles - 1;
tilesizx_ = new short[ntiles];
tilesizy_ = new short[ntiles];
picanm_ = new long[ntiles];
memset(tilesizx_, 0, sizeof(short)*ntiles);
memset(tilesizy_, 0, sizeof(short)*ntiles);
memset(picanm_, 0, sizeof(long)*ntiles);
markprelength_ = 0;
markskiplength_ = 0;
markpostlength_ = 0;
insert_ = 0;
insertlen_ = 0;
}
/**
* Returns the number of tiles in the loaded file
* @return 0 means no file loaded
*/
int getNumTiles()
{
return (localtileend_ - localtilestart_ + 1);
}
int getFirstTile()
{
return localtilestart_;
}
int getLastTile()
{
return localtileend_;
}
void removeTile(int tile)
{
int i, end;
if (tile < localtilestart_ || tile > localtileend_) {
return;
}
end = localtileend_ - tile;
tile -= localtilestart_;
markprelength_ = markpostlength_ = 0;
for (i = 0; i < tile; ++i) {
markprelength_ += tilesizx_[i] * tilesizy_[i];
}
markskiplength_ = tilesizx_[tile] * tilesizy_[tile];
for (i = tile + 1; i <= end; ++i) {
markpostlength_ += tilesizx_[i] * tilesizy_[i];
}
tilesizx_[tile] = tilesizy_[tile] = 0;
}
void replaceTile(int tile, char * replace, int replacelen)
{
if (tile < localtilestart_ || tile > localtileend_) {
return;
}
removeTile(tile);
insert_ = replace;
insertlen_ = replacelen;
}
void setTileSize(int tile, int x, int y)
{
if (tile < localtilestart_ || tile > localtileend_) {
return;
}
tile -= localtilestart_;
tilesizx_[tile] = x;
tilesizy_[tile] = y;
}
void setXOfs(int tile, int x)
{
if (tile < localtilestart_ || tile > localtileend_) {
return;
}
tile -= localtilestart_;
picanm_[tile] &= ~(255<<8);
picanm_[tile] |= ((long)((unsigned char)x) << 8);
}
void setYOfs(int tile, int y)
{
if (tile < localtilestart_ || tile > localtileend_) {
return;
}
tile -= localtilestart_;
picanm_[tile] &= ~(255<<16);
picanm_[tile] |= ((long)((unsigned char)y) << 16);
}
void setAnimType(int tile, int type)
{
if (tile < localtilestart_ || tile > localtileend_) {
return;
}
tile -= localtilestart_;
picanm_[tile] &= ~(3<<6);
picanm_[tile] |= ((long)(type&3) << 6);
}
void setAnimFrames(int tile, int frames)
{
if (tile < localtilestart_ || tile > localtileend_) {
return;
}
tile -= localtilestart_;
picanm_[tile] &= ~(63);
picanm_[tile] |= ((long)(frames&63));
}
void setAnimSpeed(int tile, int speed)
{
if (tile < localtilestart_ || tile > localtileend_) {
return;
}
tile -= localtilestart_;
picanm_[tile] &= ~(15<<24);
picanm_[tile] |= ((long)(speed&15) << 24);
}
int write()
{
BFILE *outfile = tmpfile();
BFILE *infile = Bfopen(filename_(),"rb");
int tmp, left;
static const unsigned int blksize = 4096;
char blk[blksize];
if (infile == NULL && (markprelength_ > 0 || markskiplength_ > 0 || markpostlength_ > 0)) {
return -1; // couldn't open the original file for copying
} else if (infile != NULL) {
// skip to the start of the existing ART data
int ofs = 4+4+4+4+(2+2+4)*(localtileend_-localtilestart_+1);
Bfseek(infile, ofs, SEEK_CUR);
}
// write a header to the temporary file
writeLong(outfile, 1); // version
writeLong(outfile, 0); // numtiles
writeLong(outfile, localtilestart_);
writeLong(outfile, localtileend_);
for (int i = 0; i < localtileend_ - localtilestart_ + 1; ++i) {
writeShort(outfile, tilesizx_[i]);
}
for (int i = 0; i < localtileend_ - localtilestart_ + 1; ++i) {
writeShort(outfile, tilesizy_[i]);
}
for (int i = 0; i < localtileend_ - localtilestart_ + 1; ++i) {
writeLong(outfile, picanm_[i]);
}
// copy the existing leading tile data to be kept
left = markprelength_;
while (left > 0) {
tmp = left;
if ((unsigned int)tmp > blksize) {
tmp = blksize;
}
Bfread(blk, 1, tmp, infile);
Bfwrite(blk, 1, tmp, outfile);
left -= tmp;
}
// insert the replacement data
if (insertlen_ > 0) {
Bfwrite(insert_, 1, insertlen_, outfile);
}
if (markskiplength_ > 0) {
Bfseek(infile, markskiplength_, SEEK_CUR);
}
// copy the existing trailing tile data to be kept
left = markpostlength_;
while (left > 0) {
tmp = left;
if ((unsigned int)tmp > blksize) {
tmp = blksize;
}
Bfread(blk, 1, tmp, infile);
Bfwrite(blk, 1, tmp, outfile);
left -= tmp;
}
// clean up
const long int tempsize = Bftell(outfile);
Brewind(outfile);
Bfclose(infile);
infile = Bfopen(filename_(),"wb");
char * buffer = (char*)Bmalloc(tempsize * sizeof(char));
Bfread(buffer, 1, tempsize, outfile);
Bfwrite(buffer, 1, tempsize, infile);
Bfclose(infile);
Bfclose(outfile);
return 0;
}
};
/**
* Decodes a PCX file directly to BUILD's column-major pixel order
* @param data the raw file data
* @param datalen the length of the raw file data
* @param imgdata receives a pointer to the decoded image data
* @param imgdataw receives the decoded image width
* @param imgdatah receives the decoded image height
* @return 0 on success, 1 if the format is invalid
*/
int loadimage_pcx(unsigned char * data, int datalen ATTRIBUTE((unused)), char ** imgdata, int& imgdataw, int& imgdatah)
{
if (data[0] != 10 ||
data[1] != 5 ||
data[2] != 1 ||
data[3] != 8 ||
data[64] != 0 ||
data[65] != 1) {
return 1;
}
int bpl = data[66] + ((int)data[67] << 8);
int x, y, repeat, colour;
unsigned char *wptr, *rptr;
imgdataw = (data[8] + ((int)data[9] << 8)) - (data[4] + ((int)data[5] << 8)) + 1;
imgdatah = (data[10] + ((int)data[11] << 8)) - (data[6] + ((int)data[7] << 8)) + 1;
*imgdata = new char [imgdataw * imgdatah];
rptr = data + 128;
for (y = 0; y < imgdatah; y++) {
wptr = (unsigned char *) (*imgdata + y);
x = 0;
do {
repeat = *(rptr++);
if ((repeat & 192) == 192) {
colour = *(rptr++);
repeat = repeat & 63;
} else {
colour = repeat;
repeat = 1;
}
for (; repeat > 0; repeat--, x++) {
if (x < imgdataw) {
*wptr = (unsigned char) colour;
wptr += imgdatah; // next column
}
}
} while (x < bpl);
}
return 0;
}
/**
* Loads a tile from a picture file into memory
* @param filename the filename
* @param imgdata receives a pointer to the decoded image data
* @param imgdataw receives the decoded image width
* @param imgdatah receives the decoded image height
* @return 0 on success
*/
int loadimage(Bstring filename, char ** imgdata, int& imgdataw, int& imgdatah)
{
BFILE *infile = Bfopen(filename(),"rb");
unsigned char * data = 0;
int datalen = 0, err = 0;
if (infile == NULL)
return 1;
struct Bstat stbuf;
if (Bfstat(Bfileno(infile), &stbuf) == -1)
return 1;
datalen = stbuf.st_size;
data = new unsigned char [datalen];
Bfread(data, 1, datalen, infile);
Bfclose(infile);
err = loadimage_pcx(data, datalen, imgdata, imgdataw, imgdatah);
delete [] data;
return err;
}
class Operation {
protected:
Bstring makefilename(int n)
{
Bstring filename("tilesXXX.art");
filename[5] = '0' + (n / 100) % 10;
filename[6] = '0' + (n / 10) % 10;
filename[7] = '0' + (n / 1) % 10;
return filename;
}
public:
typedef enum {
ERR_NO_ERROR = 0,
ERR_BAD_OPTION = 1,
ERR_BAD_VALUE = 2,
ERR_TOO_MANY_PARAMS = 3,
ERR_NO_ART_FILE = 4,
ERR_INVALID_IMAGE = 5,
} Result;
static char const * translateResult(Result r)
{
switch (r) {
case ERR_NO_ERROR: return "no error";
case ERR_BAD_OPTION: return "bad option";
case ERR_BAD_VALUE: return "bad value";
case ERR_TOO_MANY_PARAMS: return "too many parameters given";
case ERR_NO_ART_FILE: return "no ART file was found";
case ERR_INVALID_IMAGE: return "a nonexistent, corrupt, or unrecognised image was given";
default: return "unknown error";
}
}
virtual ~Operation()
{
}
/**
* Sets an option
* @param opt the option name
* @param value the option value
* @return a value from the Result enum
*/
virtual Result setOption(const Bstring &opt, const Bstring &value) = 0;
/**
* Sets a parameter from the unnamed sequence
* @param number the parameter number
* @param value the parameter value
* @return a value from the Result enum
*/
virtual Result setParameter(const int &number, const Bstring &value) = 0;
/**
* Do the operation
* @return a value from the Result enum
*/
virtual Result perform() = 0;
};
class CreateOp : public Operation {
private:
int filen_, offset_, ntiles_;
public:
CreateOp() : filen_(0), offset_(0), ntiles_(256) { }
virtual Result setOption(const Bstring &opt, const Bstring &value)
{
if (opt == "f") {
filen_ = atoi(value());
if (filen_ < 0 || filen_ > 999) {
return ERR_BAD_VALUE;
}
} else if (opt == "o") {
offset_ = atoi(value());
if (offset_ < 0) {
return ERR_BAD_VALUE;
}
} else if (opt == "n") {
ntiles_ = atoi(value());
if (ntiles_ < 1) {
return ERR_BAD_VALUE;
}
} else {
return ERR_BAD_OPTION;
}
return ERR_NO_ERROR;
}
virtual Result setParameter(const int &number ATTRIBUTE((unused)), const Bstring &value ATTRIBUTE((unused)))
{
return ERR_TOO_MANY_PARAMS;
}
virtual Result perform()
{
ARTFile art(makefilename(filen_));
art.init(offset_, ntiles_);
art.write();
return ERR_NO_ERROR;
}
};
class AddTileOp : public Operation {
private:
int xofs_, yofs_;
int animframes_, animtype_, animspeed_;
int tilenum_;
Bstring filename_;
public:
AddTileOp()
: xofs_(0), yofs_(0),
animframes_(0), animtype_(0), animspeed_(0),
tilenum_(-1), filename_("")
{ }
virtual Result setOption(const Bstring &opt, const Bstring &value)
{
if (opt == "x") {
xofs_ = atoi(value());
} else if (opt == "y") {
yofs_ = atoi(value());
} else if (opt == "ann") {
animframes_ = atoi(value());
if (animframes_ < 0 || animframes_ > 63) {
return ERR_BAD_VALUE;
}
} else if (opt == "ant") {
animtype_ = atoi(value());
if (animtype_ < 0 || animtype_ > 3) {
return ERR_BAD_VALUE;
}
} else if (opt == "ans") {
animspeed_ = atoi(value());
if (animspeed_ < 0 || animspeed_ > 15) {
return ERR_BAD_VALUE;
}
} else {
return ERR_BAD_OPTION;
}
return ERR_NO_ERROR;
}
virtual Result setParameter(const int &number, const Bstring &value)
{
switch (number) {
case 0:
tilenum_ = atoi(value());
return ERR_NO_ERROR;
case 1:
filename_ = value;
return ERR_NO_ERROR;
default:
return ERR_TOO_MANY_PARAMS;
}
}
virtual Result perform()
{
int tilesperfile = 0, nextstart = 0;
int filenum = 0;
char * imgdata = 0;
int imgdataw = 0, imgdatah = 0;
// open the first art file to get the file size used by default
{
ARTFile art(makefilename(0));
tilesperfile = art.getNumTiles();
if (tilesperfile == 0) {
return ERR_NO_ART_FILE;
}
}
// load the tile image into memory
switch (loadimage(filename_, &imgdata, imgdataw, imgdatah)) {
case 0: break; // win
default: return ERR_INVALID_IMAGE;
}
// open art files until we find one that encompasses the range we need
// and when we find it, make the change
for (filenum = 0; filenum < 1000; filenum++) {
ARTFile art(makefilename(filenum));
bool dirty = false, done = false;
if (art.getNumTiles() == 0) {
// no file exists, so we treat it as though it does
art.init(nextstart, tilesperfile);
dirty = true;
}
if (tilenum_ >= art.getFirstTile() && tilenum_ <= art.getLastTile()) {
art.replaceTile(tilenum_, imgdata, imgdataw * imgdatah);
art.setTileSize(tilenum_, imgdataw, imgdatah);
art.setXOfs(tilenum_, xofs_);
art.setYOfs(tilenum_, yofs_);
art.setAnimFrames(tilenum_, animframes_);
art.setAnimSpeed(tilenum_, animspeed_);
art.setAnimType(tilenum_, animtype_);
done = true;
dirty = true;
imgdata = 0; // ARTFile.replaceTile took ownership of the pointer
}
nextstart += art.getNumTiles();
if (dirty) {
art.write();
}
if (done) {
return ERR_NO_ERROR;
}
}
if (imgdata) {
delete [] imgdata;
}
return ERR_NO_ART_FILE;
}
};
class RmTileOp : public Operation {
private:
int tilenum_;
public:
RmTileOp() : tilenum_(-1) { }
virtual Result setOption(const Bstring &opt ATTRIBUTE((unused)), const Bstring &value ATTRIBUTE((unused)))
{
return ERR_BAD_OPTION;
}
virtual Result setParameter(const int &number, const Bstring &value)
{
switch (number) {
case 0:
tilenum_ = atoi(value());
return ERR_NO_ERROR;
default:
return ERR_TOO_MANY_PARAMS;
}
}
virtual Result perform()
{
int filenum = 0;
// open art files until we find one that encompasses the range we need
// and when we find it, remove the tile
for (filenum = 0; filenum < 1000; filenum++) {
ARTFile art(makefilename(filenum));
if (art.getNumTiles() == 0) {
// no file exists, so give up
break;
}
if (tilenum_ >= art.getFirstTile() && tilenum_ <= art.getLastTile()) {
art.removeTile(tilenum_);
art.write();
return ERR_NO_ERROR;
}
}
return ERR_NO_ART_FILE;
}
};
class TilePropOp : public Operation {
private:
int xofs_, yofs_;
int animframes_, animtype_, animspeed_;
int tilenum_;
int settings_;
enum {
SET_XOFS = 1,
SET_YOFS = 2,
SET_ANIMFRAMES = 4,
SET_ANIMTYPE = 8,
SET_ANIMSPEED = 16,
};
public:
TilePropOp()
: xofs_(0), yofs_(0),
animframes_(0), animtype_(0), animspeed_(0),
tilenum_(-1), settings_(0)
{ }
virtual Result setOption(const Bstring &opt, const Bstring &value)
{
if (opt == "x") {
xofs_ = atoi(value());
settings_ |= SET_XOFS;
} else if (opt == "y") {
yofs_ = atoi(value());
settings_ |= SET_YOFS;
} else if (opt == "ann") {
animframes_ = atoi(value());
settings_ |= SET_ANIMFRAMES;
if (animframes_ < 0 || animframes_ > 63) {
return ERR_BAD_VALUE;
}
} else if (opt == "ant") {
animtype_ = atoi(value());
settings_ |= SET_ANIMTYPE;
if (animtype_ < 0 || animtype_ > 3) {
return ERR_BAD_VALUE;
}
} else if (opt == "ans") {
animspeed_ = atoi(value());
settings_ |= SET_ANIMSPEED;
if (animspeed_ < 0 || animspeed_ > 15) {
return ERR_BAD_VALUE;
}
} else {
return ERR_BAD_OPTION;
}
return ERR_NO_ERROR;
}
virtual Result setParameter(const int &number, const Bstring &value)
{
switch (number) {
case 0:
tilenum_ = atoi(value());
return ERR_NO_ERROR;
default:
return ERR_TOO_MANY_PARAMS;
}
}
virtual Result perform()
{
int filenum = 0;
if (settings_ == 0) {
return ERR_NO_ERROR;
}
// open art files until we find one that encompasses the range we need
// and when we find it, make the change
for (filenum = 0; filenum < 1000; filenum++) {
ARTFile art(makefilename(filenum));
if (art.getNumTiles() == 0) {
// no file exists, so give up
break;
}
if (tilenum_ >= art.getFirstTile() && tilenum_ <= art.getLastTile()) {
if (settings_ & SET_XOFS) {
art.setXOfs(tilenum_, xofs_);
}
if (settings_ & SET_YOFS) {
art.setYOfs(tilenum_, yofs_);
}
if (settings_ & SET_ANIMFRAMES) {
art.setAnimFrames(tilenum_, animframes_);
}
if (settings_ & SET_ANIMSPEED) {
art.setAnimSpeed(tilenum_, animspeed_);
}
if (settings_ & SET_ANIMTYPE) {
art.setAnimType(tilenum_, animtype_);
}
art.write();
return ERR_NO_ERROR;
}
}
return ERR_NO_ART_FILE;
}
};
int main(int argc, char ** argv)
{
int showusage = 0;
Operation * oper = 0;
Operation::Result err = Operation::ERR_NO_ERROR;
if (argc < 2) {
showusage = 1;
} else {
Bstring opt(argv[1]);
Bstring value;
// create the option handler object according to the first param
if (opt == "create") {
oper = new CreateOp;
} else if (opt == "addtile") {
oper = new AddTileOp;
} else if (opt == "rmtile") {
oper = new RmTileOp;
} else if (opt == "tileprop") {
oper = new TilePropOp;
} else {
showusage = 2;
}
// apply the command line options given
if (oper) {
int unnamedParm = 0;
for (int i = 2; i < argc && !showusage; ++i) {
if (argv[i][0] == '-') {
opt = argv[i] + 1;
if (i+1 >= argc) {
showusage = 2;
break;
}
value = argv[i+1];
++i;
switch (err = oper->setOption(opt, value)) {
case Operation::ERR_NO_ERROR: break;
default:
Bfprintf(stderr, "error: %s\n", Operation::translateResult(err));
showusage = 2;
break;
}
} else {
value = argv[i];
switch (oper->setParameter(unnamedParm, value)) {
case Operation::ERR_NO_ERROR: break;
default:
Bfprintf(stderr, "error: %s\n", Operation::translateResult(err));
showusage = 2;
break;
}
++unnamedParm;
}
}
}
}
if (showusage) {
usage();
if (oper) delete oper;
return (showusage - 1);
} else if (oper) {
err = oper->perform();
delete oper;
switch (err) {
case Operation::ERR_NO_ERROR: return 0;
default:
Bfprintf(stderr, "error: %s\n", Operation::translateResult(err));
return 1;
}
}
return 0;
}