ipack.cc

Go to the documentation of this file.
00001 
00008 /*
00009  *  Copyright (C) 2002  The Exult Team
00010  *
00011  *  This program is free software; you can redistribute it and/or modify
00012  *  it under the terms of the GNU General Public License as published by
00013  *  the Free Software Foundation; either version 2 of the License, or
00014  *  (at your option) any later version.
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU General Public License for more details.
00020  *
00021  *  You should have received a copy of the GNU General Public License
00022  *  along with this program; if not, write to the Free Software
00023  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00024  */
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #  include <config.h>
00028 #endif
00029 
00030 #ifndef ALPHA_LINUX_CXX
00031 #  include <unistd.h>
00032 #  include <fstream>
00033 #  include <cstdio>
00034 #  include <cstdlib>
00035 #endif
00036 #include <iostream>
00037 #include <iomanip>
00038 #include <vector>
00039 #include "Flex.h"
00040 #include "utils.h"
00041 #include "exceptions.h"
00042 #include "vgafile.h"
00043 #include "pngio.h"
00044 #include "ibuf8.h"
00045 
00046 using std::atoi;
00047 using std::cerr;
00048 using std::cout;
00049 using std::endl;
00050 using std::exit;
00051 using std::ifstream;
00052 using std::ofstream;
00053 using std::istream;
00054 using std::ostream;
00055 using std::setw;
00056 using std::size_t;
00057 using std::strlen;
00058 using std::strncmp;
00059 using std::strcpy;
00060 using std::strcat;
00061 using std::vector;
00062 
00063 /*
00064  *  A shape specification:
00065  */
00066 class Shape_spec
00067   {
00068 public:
00069   char *filename;     // Should be allocated.
00070   int nframes;      // # frames in shape.
00071   bool flat;      // A 'flat' shape.
00072   bool bycol;     // If dim0_tiles > 0, go down first.
00073   int dim0_tiles;     // File consists of 8x8 (flat) tiles.
00074 public:
00075   Shape_spec() : filename(0), nframes(0), flat(false), bycol(false),
00076           dim0_tiles(0)
00077     {  }
00078   ~Shape_spec()
00079     { delete filename; }
00080   };
00081 typedef vector<Shape_spec> Shape_specs;
00082 
00083 /*
00084  *  Skip white space.
00085  */
00086 
00087 static char *Skip_space
00088   (
00089   char *ptr
00090   )
00091   {
00092   while (isspace(*ptr))
00093     ptr++;
00094   return ptr;
00095   }
00096 
00097 /*
00098  *  Find white space (or null).
00099  */
00100 
00101 static char *Find_space
00102   (
00103   char *ptr
00104   )
00105   {
00106   while (*ptr && !isspace(*ptr))
00107     ptr++;
00108   return ptr;
00109   }
00110 
00111 /*
00112  *  Pass a filename spec. which can have a "(nnn xxx)" at its end.
00113  */
00114 
00115 static char *Pass_file_spec
00116   (
00117   char *ptr
00118   )
00119   {
00120   int paren_depth = 0;
00121   while (*ptr && (paren_depth > 0 || !isspace(*ptr)))
00122     {
00123     if (*ptr == '(')
00124       paren_depth++;
00125     else if (*ptr == ')')
00126       paren_depth--;
00127     ptr++;
00128     }
00129   return ptr;
00130   }
00131 
00132 /*
00133  *  Parse a number, and quit with an error msg. if not found.
00134  */
00135 
00136 static long Get_number
00137   (
00138   int linenum,      // For printing errors.
00139   char *errmsg,
00140   char *ptr,
00141   char *& endptr      // ->past number and spaces returned.
00142   )
00143   {
00144   long num = strtol(ptr, &endptr, 0);
00145   if (endptr == ptr)    // No #?
00146     {
00147     cerr << "Line " << linenum << ":  " << errmsg << endl;
00148     exit(1);
00149     }
00150   endptr = Skip_space(endptr);
00151   return num;
00152   }
00153 
00154 /*
00155  *  Return the next token, or quit with an error.
00156  *
00157  *  Output: ->copy of token.
00158  */
00159 
00160 static char *Get_token
00161   (
00162   int linenum,      // For printing errors.
00163   char *ptr,      // Where to start looking.
00164   char *& endptr      // ->past token returned.
00165   )
00166   {
00167   ptr = Skip_space(ptr + 7);
00168   endptr = Find_space(ptr);
00169   if (endptr == ptr)
00170     {
00171     cerr << "Line #" << linenum <<
00172         ":  Expecting a name" << endl;
00173     exit(1);
00174     }
00175   char sav = *endptr;
00176   *endptr = 0;
00177   char *token = newstrdup(ptr);
00178   *endptr = sav;
00179   return token;
00180   }
00181 
00182 /*
00183  *  Read in script, with the following format:
00184  *    Max. text length is 1024.
00185  *    A line beginning with a '#' is a comment.
00186  *    'archive imgfile' specifies name of the image archive.
00187  *    'palette palfile' specifies the palette file (which, if a
00188  *      Flex, contains palette in entry 0).
00189  *    'nnn/fff:filename' indicates shape #nnn will consist of fff
00190  *      frames in files "filename-iii.png", where iii is the
00191  *      frame #.
00192  *    'nnn/fff:filename(cc across)' indicates filename is a .png
00193  *      consisting of 8x8 flat tiles, to be taken rowwise with
00194  *      each row having cc columns.
00195  *    'nnn/fff:filename(rr down)' indicates filename is a .png
00196  *      consisting of 8x8 flat tiles, to be taken columnwise
00197  *      with each column having rr rows.
00198  *    Filename may be followed by 'flat' to indicate 8x8 non-RLE
00199  *      shape.
00200  */
00201 
00202 static void Read_script
00203   (
00204   istream& in,
00205   char *& imagename,    // Archive name returned.
00206   char *& palname,    // Palette name returned.
00207   Shape_specs& specs    // Shape specs. returned here.
00208   )
00209   {
00210   imagename = 0;
00211   specs.resize(0);    // Initialize.
00212   specs.reserve(1200);
00213   char buf[1024];
00214   int linenum = 0;
00215   while (!in.eof())
00216     {
00217     ++linenum;
00218     in.get(buf, sizeof(buf));
00219     char delim;   // Check for end-of-line.
00220     in.get(delim);
00221     if (delim != '\n' && !in.eof())
00222       {
00223       cerr << "Line #" << linenum << " is too long" << endl;
00224       exit(1);
00225       }
00226     if (!buf[0])
00227       continue; // Empty line.
00228     char *ptr = Skip_space(&buf[0]);
00229     if (*ptr == '#')
00230       continue; // Comment.
00231     char *endptr;
00232     if (strncmp(ptr, "archive", 7) == 0)
00233       {   // Archive name.
00234       imagename = Get_token(linenum, ptr, endptr);
00235       continue;
00236       }
00237     if (strncmp(ptr, "palette", 7) == 0)
00238       {
00239       palname = Get_token(linenum, ptr, endptr);
00240       continue;
00241       }
00242           // Get shape# in decimal, hex, or oct.
00243     long shnum = Get_number(linenum, "Shape # missing",
00244                 ptr, endptr);
00245     if (*endptr != '/')
00246       {
00247       cerr << "Line #" << linenum << 
00248         ":  Missing '/' after shape number" << endl;
00249       exit(1);
00250       }
00251     ptr = endptr + 1;
00252     long nframes = Get_number(linenum, "Frame count missing",
00253                 ptr, endptr);
00254     if (*endptr != ':')
00255       {
00256       cerr << "Line #" << linenum << 
00257         ":  Missing ':' after frame count" << endl;
00258       exit(1);
00259       }
00260     ptr = Skip_space(endptr + 1);
00261     endptr = Pass_file_spec(ptr);
00262     if (endptr == ptr)
00263       {
00264       cerr << "Line #" << linenum <<
00265         ":  Missing filename" << endl;
00266       exit(1);
00267       }
00268           // Get ->past filename.
00269     char *past_end = *endptr ? Skip_space(endptr + 1) : endptr;
00270     *endptr = 0;
00271     if (shnum >= specs.size())
00272       specs.resize(shnum + 1);
00273     specs[shnum].flat = (strncmp(past_end, "flat", 4) == 0);
00274     specs[shnum].nframes = nframes;
00275     char fname[300], dir[300];
00276     int dim0_cnt;   // See if it's a tiles spec.
00277     if (sscanf(ptr, "%[^(](%d %s)", &fname[0], &dim0_cnt, &dir[0])
00278               == 3)
00279       {
00280       if (!specs[shnum].flat)
00281         {
00282         cerr << "Line #" << linenum <<
00283           ":  Tiled file not specified 'flat'"
00284                 << endl;
00285         exit(1);
00286         }
00287       specs[shnum].dim0_tiles = dim0_cnt;
00288       specs[shnum].bycol = strncmp(dir, "down", 4) == 0;
00289       specs[shnum].filename = strdup(fname);
00290       }
00291     else
00292       specs[shnum].filename = strdup(ptr);
00293     }
00294   }
00295 
00296 /*
00297  *  Modify a palette by fixed amounts and/or percentages.
00298  */
00299 
00300 static void Modify_palette
00301   (
00302   unsigned char *from,    // Rgb values to start with,
00303           //   each range 0-255.
00304   unsigned char *to,    // Result stored here.
00305   int palsize,      // 0-256.
00306   int roff, int goff, int boff, // Add these offsets first.
00307   int r256, int g256, int b256  // Modify by x/256.
00308   )
00309   {
00310   int cnt = 3*palsize;
00311   for (int i = 0; i < 3*palsize; )
00312     {
00313     int r = from[i] + roff; // First the offsets.
00314           // Then percentage.
00315     r += (r*r256)/256;
00316     if (r < 0)
00317       r = 0;
00318     if (r > 255)
00319       r = 255;
00320     to[i++] = r;
00321     int g = from[i] + goff;
00322     g += (g*g256)/256;
00323     if (g < 0)
00324       g = 0;
00325     if (g > 255)
00326       g = 255;
00327     to[i++] = g;
00328     int b = from[i] + boff;
00329     b += (b*b256)/256;
00330     if (b < 0)
00331       b = 0;
00332     if (b > 255)
00333       b = 255;
00334     to[i++] = b;
00335     }
00336   }
00337 
00338 /*
00339  *  Modify a palette by removing all color.
00340  */
00341 
00342 static void Greyify_palette
00343   (
00344   unsigned char *from,    // Rgb values to start with,
00345           //   each range 0-255.
00346   unsigned char *to,    // Result stored here.
00347   int palsize     // 0-256.
00348   )
00349   {
00350   for (int i = 0; i < palsize; i++)
00351     {
00352     int ind = i*3;
00353           // Take average.
00354     int ave = (from[ind] + from[ind + 1] + from[ind + 2])/3;
00355     to[ind] = to[ind + 1] = to[ind + 2] = ave;
00356     }
00357   }
00358 
00359 /*
00360  *  Convert a palette to values 0-63.
00361  */
00362 
00363 static void Convert_palette63
00364   (
00365   unsigned char *from,    // 3*palsize, values 0-255.
00366   unsigned char *to,    // 3*256.  Values 0-63 returned, with
00367           //   colors > palsize 0-filled.
00368   int palsize     // # entries.
00369   )
00370   {
00371   int i;        // Convert 0-255 to 0-63 for Exult.
00372   for (i = 0; i < 3*palsize; i++)
00373     to[i] = from[i]/4;
00374   memset(to + i, 0, 3*256 - i); // 0-fill the rest.
00375 
00376   }
00377 
00378 /*
00379  *  Write out a palette as text.
00380  */
00381 
00382 static void Write_text_palette
00383   (
00384   char *palname,      // Base name.  '.txt' is appended.
00385   unsigned char *palette,   // RGB's, 0-255.
00386   int palsize     // # colors in palette
00387   )
00388   {
00389           // Write out as (Gimp) text.
00390   char *txtpal = new char[strlen(palname) + 10];
00391   strcpy(txtpal, palname);
00392   strcat(txtpal, ".txt");
00393   cout << "Creating text (Gimp) palette '" << txtpal << "'" << endl;
00394   ofstream pout(txtpal);    // OKAY that it's a 'text' file.
00395   pout << "GIMP palette" << endl; // MUST be this for Gimp to use.
00396   pout << "Palette from Exult's Ipack" << endl;
00397   int i;        // Skip 0's at end.
00398   for (i = palsize - 1; i > 0; i--)
00399     if (palette[3*i] != 0 || palette[3*i+1] != 0 || 
00400               palette[3*i+2] != 0)
00401       break;
00402   int last_color = i;
00403   for (i = 0; i <= last_color; i++)
00404     {
00405     int r = palette[3*i],
00406         g = palette[3*i + 1],
00407         b = palette[3*i + 2];
00408     pout << setw(3) << r << ' ' << setw(3) << g << ' ' << 
00409             setw(3) << b << endl;
00410     }
00411   pout.close();
00412   delete txtpal;
00413   }
00414 
00415 const unsigned char transp = 255; // Transparent pixel.
00416 
00417 /*
00418  *  Write out one frame as a .png.
00419  */
00420 
00421 static void Write_frame
00422   (
00423   char *basename,     // Base filename to write.
00424   int frnum,      // Frame #.
00425   Shape_frame *frame,   // What to write.
00426   unsigned char *palette    // 3*256 bytes.
00427   )
00428   {
00429   assert(frame != 0);
00430   char *fullname = new char[strlen(basename) + 30];
00431   sprintf(fullname, "%s%02d.png", basename, frnum);
00432   cout << "Writing " << fullname << endl;
00433   int w = frame->get_width(), h = frame->get_height();
00434   Image_buffer8 img(w, h);  // Render into a buffer.
00435   img.fill8(transp);    // Fill with transparent pixel.
00436   frame->paint(&img, frame->get_xleft(), frame->get_yabove());
00437   int xoff = 0, yoff = 0;
00438   if (frame->is_rle())
00439     {
00440     xoff = -frame->get_xright();
00441     yoff = -frame->get_ybelow();
00442     }
00443           // Write out to the .png.
00444   if (!Export_png8(fullname, transp, w, h, w, xoff, yoff, img.get_bits(),
00445           palette, 256, true))
00446     throw file_write_exception(fullname);
00447   delete fullname;
00448   }
00449 
00450 /*
00451  *  Write out all (8x8 flat) frames by tiling them into one file.
00452  */
00453 
00454 static void Write_tiled_frames
00455   (
00456   char *filename,     // Filename to write.
00457   Shape *shape,     // What to write.
00458   int shnum,      // For printing errors.
00459   bool bycol,     // If true, go down each column first,
00460           //   else go across each row first.
00461   int dim0_cnt,     // If bycol, #rows; else #cols.
00462   unsigned char *palette    // 3*256 bytes.
00463   )
00464   {
00465   assert(shape != 0);
00466   cout << "Writing " << filename << " tiled" 
00467     << (bycol ? ", by cols" : ", by rows") << " first" << endl;
00468   int nframes = shape->get_num_frames();
00469           // Figure #tiles in other dim.
00470   int dim1_cnt = (nframes + dim0_cnt - 1)/dim0_cnt;
00471   int w, h;
00472   if (bycol)
00473     { h = dim0_cnt*8; w = dim1_cnt*8; }
00474   else
00475     { w = dim0_cnt*8; h = dim1_cnt*8; }
00476   Image_buffer8 img(w, h);
00477   img.fill8(transp);    // Fill with transparent pixel.
00478   for (int f = 0; f < nframes; f++)
00479     {
00480     Shape_frame *frame = shape->get_frame(f);
00481     if (!frame)
00482       continue; // We'll just leave empty ones blank.
00483     if (!frame->is_rle() || frame->get_width() != 8 ||
00484           frame->get_height() != 8)
00485       {
00486       cerr << "Can only tile 8x8 flat shapes, but shape "
00487         << shnum << " doesn't qualify" << endl;
00488       exit(1);
00489       }
00490     int x, y;
00491     if (bycol)
00492       { y = f%dim0_cnt; x = f/dim0_cnt; }
00493     else
00494       { x = f%dim0_cnt; y = f/dim0_cnt; }
00495     frame->paint(&img, x*8 + frame->get_xleft(), 
00496             y*8 + frame->get_yabove());
00497     }
00498           // Write out to the .png.
00499   if (!Export_png8(filename, transp, w, h, w, 0, 0, img.get_bits(),
00500           palette, 256))
00501     throw file_write_exception(filename);
00502   }
00503 
00504 /*
00505  *  Write out palettes.  The first is the one given, and the rest are
00506  *  automatically generated to work with the Exult engine (see
00507  *  palettes.h for definitions).
00508  */
00509 
00510 void Write_palettes
00511   (
00512   char *palname, 
00513   unsigned char *palette,   // 3 bytes for each color.
00514   int palsize     // # entries.
00515   )
00516   {
00517   cout << "Creating new palette file '" << palname <<
00518       "' using first file's palette" << endl;
00519   unsigned char palbuf[3*256];  // We always write 256 colors.
00520   if (palsize > 256)    // Shouldn't happen.
00521     palsize = 256;
00522   ofstream out;
00523   U7open(out, palname);   // May throw exception.
00524   Flex_writer writer(out, "Exult palette by Ipack", 11);
00525           // Entry 0 (DAY):
00526   Convert_palette63(palette, &palbuf[0], palsize);
00527   out.write((const char*)&palbuf[0], sizeof(palbuf));
00528   writer.mark_section_done();
00529           // Entry 1 (DUSK):
00530   Modify_palette(palette, palbuf, palsize, 0, 0, 0, -64, -64, -64);
00531   Convert_palette63(palbuf, palbuf, palsize);
00532   out.write((const char*)&palbuf[0], sizeof(palbuf));
00533   writer.mark_section_done();
00534           // Entry 2 (NIGHT):
00535   Modify_palette(palette, palbuf, palsize, 0, 0, 0, -128, -128, -128);
00536   Convert_palette63(palbuf, palbuf, palsize);
00537   out.write((const char*)&palbuf[0], sizeof(palbuf));
00538   writer.mark_section_done();
00539           // Entry 3 (INVISIBLE):
00540   Greyify_palette(palette, palbuf, palsize);
00541   Convert_palette63(palbuf, palbuf, palsize);
00542   out.write((const char*)&palbuf[0], sizeof(palbuf));
00543   writer.mark_section_done();
00544           // Entry 4 (unknown):
00545   Convert_palette63(palette, palbuf, palsize);
00546   out.write((const char*)&palbuf[0], sizeof(palbuf));
00547   writer.mark_section_done();
00548           // Entry 5 (HAZE):
00549   Modify_palette(palette, palbuf, palsize, 184, 184, 184, -32, -32, -32);
00550   Convert_palette63(palbuf, palbuf, palsize);
00551   out.write((const char*)&palbuf[0], sizeof(palbuf));
00552   writer.mark_section_done();
00553           // Entry 6 (a bit brighter than 2):
00554   Modify_palette(palette, palbuf, palsize, 0, 0, 0, -96, -96, -96);
00555   Convert_palette63(palbuf, palbuf, palsize);
00556   out.write((const char*)&palbuf[0], sizeof(palbuf));
00557   writer.mark_section_done();
00558           // Entry 7 (a bit warmer):
00559   Modify_palette(palette, palbuf, palsize, 30, 0, 0, -96, -96, -96);
00560   Convert_palette63(palbuf, palbuf, palsize);
00561   out.write((const char*)&palbuf[0], sizeof(palbuf));
00562   writer.mark_section_done();
00563           // Entry 8 (hit in combat):
00564   Modify_palette(palette, palbuf, palsize, 64, 0, 0, 384, -20, -20);
00565   Convert_palette63(palbuf, palbuf, palsize);
00566   out.write((const char*)&palbuf[0], sizeof(palbuf));
00567   writer.mark_section_done();
00568           // Entry 9 (unknown):
00569   Convert_palette63(palette, palbuf, palsize);
00570   out.write((const char*)&palbuf[0], sizeof(palbuf));
00571   writer.mark_section_done();
00572           // Entry 10 (LIGHTNING):
00573   Modify_palette(palette, palbuf, palsize, 32, 32, 0, 256, 256, -20);
00574   Convert_palette63(palbuf, palbuf, palsize);
00575   out.write((const char*)&palbuf[0], sizeof(palbuf));
00576   writer.mark_section_done();
00577   if (!writer.close())
00578     throw file_write_exception(palname);
00579           // Write out in Gimp format.
00580   Write_text_palette(palname, palette, palsize);
00581   }
00582 
00583 /*
00584  *  Write tiles from a single input file to and Exult image file.
00585  */
00586 
00587 static void Write_exult_from_tiles
00588   (
00589   ostream& out,     // What to write to.
00590   char *filename,     // Filename to read.
00591   int nframes,      // # frames.
00592   bool bycol,     // If true, go down each column first,
00593           //   else go across each row first.
00594   int dim0_cnt,     // If bycol, #rows; else #cols.
00595   char *palname     // Store palette here if !0.
00596   )
00597   {
00598   cout << "Reading " << filename << " tiled" 
00599     << (bycol ? ", by cols" : ", by rows") << " first" << endl;
00600           // Figure #tiles in other dim.
00601   int dim1_cnt = (nframes + dim0_cnt - 1)/dim0_cnt;
00602   int needw, needh;   // Figure min. image dims.
00603   if (bycol)
00604     { needh = dim0_cnt*8; needw = dim1_cnt*8; }
00605   else
00606     { needw = dim0_cnt*8; needh = dim1_cnt*8; }
00607           // Save starting position.
00608   unsigned long startpos = out.tellp();
00609   int w, h, rowsize, xoff, yoff, palsize;
00610   unsigned char *pixels, *palette;
00611           // Import, with 255 = transp. index.
00612   if (!Import_png8(filename, 255, w, h, rowsize, xoff, yoff,
00613             pixels, palette, palsize))
00614     throw file_read_exception(filename);
00615   if (w < needw || h < needh)
00616     {
00617     cerr << "File " << filename << " image is too small.  " <<
00618       needw << 'x' << needh << " required" << endl;
00619     exit(1);
00620     }
00621   for (int frnum = 0; frnum < nframes; frnum++)
00622     {
00623     int x, y;
00624     if (bycol)
00625       { y = frnum%dim0_cnt; x = frnum/dim0_cnt; }
00626     else
00627       { x = frnum%dim0_cnt; y = frnum/dim0_cnt; }
00628     unsigned char *src = pixels + w*8*y + 8*x;
00629     for (int row = 0; row < 8; row++)
00630       {   // Write it out.
00631       out.write(reinterpret_cast<char *>(src), 8);
00632       src += w;
00633       }
00634     }
00635   delete pixels;
00636   if (palname)
00637     Write_palettes(palname, palette, palsize);
00638   delete palette;
00639   }
00640 
00641 /*
00642  *  Write a shape's frames to an Exult image file.
00643  */
00644 
00645 static void Write_exult
00646   (
00647   ostream& out,     // What to write to.
00648   char *basename,     // Base filename for files to read.
00649   int nframes,      // # frames.
00650   bool flat,      // Store as 8x8 flats.
00651   char *palname     // Store palette with here if !0.
00652   )
00653   {
00654   Shape shape(nframes);
00655   char *fullname = new char[strlen(basename) + 30];
00656   int frnum;      // Read in frames.
00657   for (frnum = 0; frnum < nframes; frnum++)
00658     {
00659     sprintf(fullname, "%s%02d.png", basename, frnum);
00660     cout << "Reading " << fullname << endl;
00661     int w, h, rowsize, xoff, yoff, palsize;
00662     unsigned char *pixels, *palette;
00663           // Import, with 255 = transp. index.
00664     if (!Import_png8(fullname, 255, w, h, rowsize, xoff, yoff,
00665             pixels, palette, palsize))
00666       throw file_read_exception(fullname);
00667     int xleft, yabove;
00668     if (flat)
00669       {
00670       xleft = yabove = 8;
00671       if (w != 8 || h != 8 || rowsize != 8)
00672         {
00673         cerr << "Image in '" << fullname <<
00674           "' is not flat" << endl;
00675         exit(1);
00676         }
00677       }
00678     else      // RLE. xoff,yoff are neg. from bottom.
00679       {
00680       xleft = w + xoff - 1;
00681       yabove = h + yoff - 1;
00682       }
00683     shape.set_frame(new Shape_frame(pixels,
00684           w, h, xleft, yabove, !flat), frnum);
00685     delete pixels;
00686     if (palname)    // Write palette for first frame.
00687       {
00688       Write_palettes(palname, palette, palsize);
00689       palname = 0;
00690       }
00691     delete palette;
00692     }
00693   shape.write(out);   // Write them out.
00694   delete fullname;
00695   }
00696 
00697 /*
00698  *  Create an archive from a set of image files.  May throw an exception.
00699  */
00700 
00701 static void Create
00702   (
00703   char *imagename,    // Image archive name.
00704   char *palname,      // Palettes file (palettes.flx).
00705   Shape_specs& specs,   // List of things to extract.
00706   const char *title   // For storing in Flex file.
00707   )
00708   {
00709   if (palname && U7exists(palname))   // Palette?
00710     {
00711     cout << "Palette file '" << palname << 
00712       "' exists, so we won't overwrite it" << endl;
00713     palname = 0;
00714     }
00715   ofstream out;
00716   U7open(out, imagename);   // May throw exception.
00717   Flex_writer writer(out, title, specs.size());
00718   for (Shape_specs::const_iterator it = specs.begin();
00719             it != specs.end();  ++it)
00720     {
00721     int shnum = it - specs.begin();
00722     char *basename = (*it).filename;
00723     if (basename)   // Not empty?
00724       {
00725       int dim0_cnt = (*it).dim0_tiles;
00726       if (dim0_cnt > 0)
00727         Write_exult_from_tiles(out, basename,
00728           (*it).nframes, (*it).bycol,
00729           (*it).dim0_tiles, palname);
00730       else      
00731         Write_exult(out, basename, (*it).nframes, 
00732             (*it).flat, palname);
00733       palname = 0;  // Only write 1st palette.
00734       }
00735     writer.mark_section_done();
00736     }
00737   if (!writer.close())
00738     throw file_write_exception(imagename);
00739   }
00740 
00741 /*
00742  *  Update an archive with a set of image files.  May throw an exception.
00743  *  The archive is rewritten with its original shapes, plus those
00744  *  specified in the script.
00745  */
00746 
00747 static void Update
00748   (
00749   char *imagename,    // Image archive name.
00750   char *palname,      // Palettes file (palettes.flx).
00751   Shape_specs& specs,   // List of things to extract.
00752   const char *title   // For storing in Flex file.
00753   )
00754   {
00755   if (palname && U7exists(palname))   // Palette?
00756     {
00757     cout << "Palette file '" << palname << 
00758       "' exists, so we won't overwrite it" << endl;
00759     palname = 0;
00760     }
00761   Flex in(imagename);   // May throw exception.
00762   int oldcnt = in.number_of_objects();
00763   vector<char *> data(oldcnt);  // Read in all the entries.
00764   vector<int> lengths(oldcnt);
00765   int i;
00766   for (i = 0; i < oldcnt; i++)
00767     {
00768     size_t len;
00769     data[i] = in.retrieve(i, len);
00770     lengths[i] = len;
00771     if (!len)   // Empty?
00772       {
00773       delete data[i];
00774       data[i] = 0;
00775       }
00776     }
00777   ofstream out;
00778   U7open(out, imagename);   // May throw exception.
00779   int newcnt = oldcnt > specs.size() ? oldcnt : specs.size();
00780   Flex_writer writer(out, title, newcnt);
00781   for (i = 0; i < newcnt; i++)  // Write out new entries.
00782     {
00783           // New entry for this shape?
00784     if (i < specs.size() && specs[i].filename != 0)
00785       {
00786       Write_exult(out, specs[i].filename,
00787         specs[i].nframes, specs[i].flat, palname);
00788       palname = 0;  // Only write 1st palette.
00789       }
00790           // Write old entry.
00791     else if (i < oldcnt && data[i])
00792       out.write(data[i], lengths[i]);
00793     writer.mark_section_done();
00794     }
00795   if (!writer.close())
00796     throw file_write_exception(imagename);
00797   for (i = 0; i < oldcnt; i++)  // Clean up.
00798     delete data[i];
00799   }
00800 
00801 /*
00802  *  Extract from the archive.  May throw an exception.
00803  */
00804 
00805 static void Extract
00806   (
00807   char *imagename,    // Image archive name.
00808   char *palname,      // Palettes file (palettes.flx).
00809   Shape_specs& specs    // List of things to extract.
00810   )
00811   {
00812   if (!palname)
00813     {
00814     cerr << "No palette name (i.e., 'palettes.flx') given" << endl;
00815     exit(1);
00816     }
00817   U7object pal(palname, 0); // Get palette 0.
00818   size_t len;
00819   unsigned char *palbuf = (unsigned char *)
00820     pal.retrieve(len);  // This may throw an exception
00821   for (int i = 0; i < len; i++) // Turn into full bytes.
00822     palbuf[i] *= 4;   // Exult palette vals are 0-63.
00823   Vga_file ifile(imagename);  // May throw an exception.
00824   for (Shape_specs::const_iterator it = specs.begin();
00825             it != specs.end();  ++it)
00826     {
00827     char *basename = (*it).filename;
00828     if (!basename)    // Empty?
00829       continue;
00830     int shnum = it - specs.begin();
00831     if (shnum >= ifile.get_num_shapes())
00832       {
00833       cerr << "Shape #" << shnum << " > #shapes in file" <<
00834                 endl;
00835       continue;
00836       }
00837           // Read in all frames.
00838     Shape *shape = ifile.extract_shape(shnum);
00839     int nframes = shape->get_num_frames();
00840     if (nframes != (*it).nframes)
00841       cerr << "Warning: # frames (" << (*it).nframes <<
00842         ") given for shape " << shnum <<
00843         " doesn't match actual count (" << nframes <<
00844         ")" << endl;
00845     for (int f = 0; f < nframes; f++)
00846       Write_frame(basename, f, shape->get_frame(f), palbuf);
00847     }
00848   delete palbuf;
00849   }
00850 
00851 /*
00852  *  Print usage and exit.
00853  */
00854 
00855 static void Usage()
00856   {
00857   cerr << "Usage: ipack -[x|c|u] script" << endl;
00858   exit(1);
00859   }
00860 
00861 /*
00862  *  Create or extract from Flex files consisting of shapes.
00863  */
00864 
00865 int main
00866   (
00867   int argc,
00868   char **argv
00869   )
00870   {
00871   if (argc < 3 || argv[1][0] != '-')
00872     Usage();    // (Exits.)
00873   char *scriptname = argv[2];
00874   char *imagename = 0, *palname = 0;
00875   Shape_specs specs;    // Shape specs. stored here.
00876   ifstream specin;
00877   try {
00878     U7open(specin, scriptname, true);
00879   } catch (exult_exception& e) {
00880     cerr << e.what() << endl;
00881     exit(1);
00882   }
00883   Read_script(specin, imagename, palname, specs);
00884   if (!imagename)
00885     {
00886     cerr << "No archive name (i.e., 'shapes.vga') given" << endl;
00887     exit(1);
00888     }
00889   specin.close();
00890   switch (argv[1][1])   // Which function?
00891     {
00892   case 'c':     // Create.
00893     try {
00894       Create(imagename, palname, specs, "Exult image file");
00895     } catch (exult_exception& e){
00896       cerr << e.what() << endl;
00897       exit(1);
00898     }
00899     break;
00900   case 'x':     // Extract .png files.
00901     try {
00902       Extract(imagename, palname, specs);
00903     } catch (exult_exception& e){
00904       cerr << e.what() << endl;
00905       exit(1);
00906     }
00907     break;
00908   case 'u':
00909     try {
00910       Update(imagename, palname, specs, "Exult image file");
00911     } catch (exult_exception& e){
00912       cerr << e.what() << endl;
00913       exit(1);
00914     }
00915     break;
00916   default:
00917     Usage();
00918     }
00919   return 0;
00920   }

Generated on Mon Jul 9 14:42:50 2007 for ExultEngine by  doxygen 1.5.1