Jelajahi Sumber

*** empty log message ***

David Rose 25 tahun lalu
induk
melakukan
e2393582ba

+ 4 - 2
pandatool/src/eggbase/eggConverter.cxx

@@ -22,7 +22,9 @@ EggConverter(const string &format_name,
 	     bool allow_last_param,
 	     bool allow_stdout) : 
   EggFilter(allow_last_param, allow_stdout),
-  _format_name(format_name),
-  _preferred_extension(preferred_extension)
+  _format_name(format_name)
 {
+  // Indicate the extension name we expect the user to supply for
+  // output files.
+  _preferred_extension = preferred_extension;
 }

+ 0 - 1
pandatool/src/eggbase/eggConverter.h

@@ -25,7 +25,6 @@ public:
 
 protected:
   string _format_name;
-  string _preferred_extension;
 };
 
 #endif

+ 1 - 1
pandatool/src/eggbase/eggFilter.cxx

@@ -42,7 +42,7 @@ EggFilter(bool allow_last_param, bool allow_stdout) :
 ////////////////////////////////////////////////////////////////////
 bool EggFilter::
 handle_args(ProgramBase::Args &args) {
-  if (!check_last_arg(args)) {
+  if (!check_last_arg(args, 0)) {
     return false;
   }
 

+ 2 - 17
pandatool/src/eggbase/eggToSomething.cxx

@@ -81,23 +81,8 @@ EggToSomething(const string &format_name,
 ////////////////////////////////////////////////////////////////////
 bool EggToSomething::
 handle_args(ProgramBase::Args &args) {
-  if (_allow_last_param && !_got_output_filename && args.size() > 1) {
-    _got_output_filename = true;
-    _output_filename = args.back();
-    args.pop_back();
-
-    if (!_preferred_extension.empty() && 
-	("." + _output_filename.get_extension()) != _preferred_extension) {
-      nout << "Output filename " << _output_filename 
-	   << " does not end in " << _preferred_extension 
-	   << ".  If this is really what you intended, "
-	"use the -o output_file syntax.\n";
-      return false;
-    }
-
-    if (!verify_output_file_safe()) {
-      return false;
-    }
+  if (!check_last_arg(args, 1)) {
+    return false;
   }
 
   return EggConverter::handle_args(args);

+ 7 - 123
pandatool/src/eggbase/eggWriter.cxx

@@ -28,9 +28,12 @@
 //               inappropriate when writing binary file formats).
 ////////////////////////////////////////////////////////////////////
 EggWriter::
-EggWriter(bool allow_last_param, bool allow_stdout) {
-  _allow_last_param = allow_last_param;
-  _allow_stdout = allow_stdout;
+EggWriter(bool allow_last_param, bool allow_stdout) :
+  WithOutputFile(allow_last_param, allow_stdout, false)
+{
+  // Indicate the extension name we expect the user to supply for
+  // output files.
+  _preferred_extension = ".egg";
 
   clear_runlines();
   if (_allow_last_param) {
@@ -77,8 +80,6 @@ EggWriter(bool allow_last_param, bool allow_stdout) {
      "one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'.  The default is "
      "y-up.");
 
-  _output_ptr = (ostream *)NULL;
-
   _normals_mode = NM_preserve;
   _normals_threshold = 0.0;
 
@@ -178,68 +179,6 @@ as_writer() {
   return this;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: EggWriter::get_output
-//       Access: Public
-//  Description: Returns an output stream that corresponds to the
-//               user's intended egg file output--either stdout, or
-//               the named output file.  Normally, you should not need
-//               to call this directly, as write_egg_file() will write
-//               the egg file to the resulting stream.  Call this only
-//               if you cannot use write_egg_file() to write out the
-//               egg file for some reason.
-////////////////////////////////////////////////////////////////////
-ostream &EggWriter::
-get_output() {
-  if (_output_ptr == (ostream *)NULL) {
-    if (!_got_output_filename) {
-      // No filename given; use standard output.
-      assert(_allow_stdout);
-      _output_ptr = &cout;
-      
-    } else {
-      // Attempt to open the named file.
-      unlink(_output_filename.c_str());
-      _output_filename.make_dir();
-      _output_filename.set_text();
-      if (!_output_filename.open_write(_output_stream)) {
-	nout << "Unable to write to " << _output_filename << "\n";
-	exit(1);
-      }
-      nout << "Writing " << _output_filename << "\n";
-      _output_ptr = &_output_stream;
-    }
-  }
-  return *_output_ptr;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: EggWriter::has_output_filename
-//       Access: Public
-//  Description: Returns true if the user specified an output
-//               filename, false otherwise (e.g. the output file is
-//               implicitly stdout).
-////////////////////////////////////////////////////////////////////
-bool EggWriter::
-has_output_filename() const {
-  return _got_output_filename;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: EggWriter::get_output_filename
-//       Access: Public
-//  Description: If has_output_filename() returns true, this is the
-//               filename that the user specified.  Otherwise, it
-//               returns the empty string.
-////////////////////////////////////////////////////////////////////
-Filename EggWriter::
-get_output_filename() const {
-  if (_got_output_filename) {
-    return _output_filename;
-  }
-  return Filename();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: EggWriter::post_process_egg_file
 //       Access: Public, Virtual
@@ -316,7 +255,7 @@ write_egg_file() {
 ////////////////////////////////////////////////////////////////////
 bool EggWriter::
 handle_args(ProgramBase::Args &args) {
-  if (!check_last_arg(args)) {
+  if (!check_last_arg(args, 0)) {
     return false;
   }
 
@@ -330,33 +269,6 @@ handle_args(ProgramBase::Args &args) {
   return true;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: EggWriter::check_last_arg
-//       Access: Protected
-//  Description: Checks if the last filename on the argument list is
-//               an egg file (if _allow_last_param was set true), and
-//               removes it from the argument list if it is.  Returns
-//               true if the arguments are good, false if something is
-//               invalid.
-////////////////////////////////////////////////////////////////////
-bool EggWriter::
-check_last_arg(ProgramBase::Args &args) {
-  if (_allow_last_param && !_got_output_filename && !args.empty()) {
-    Filename filename = args.back();
-    if (filename.get_extension() == "egg") {
-      _got_output_filename = true;
-      _output_filename = filename;
-      args.pop_back();
-
-      if (!verify_output_file_safe()) {
-	return false;
-      }
-    }
-  }
-
-  return true;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: EggWriter::post_command_line
 //       Access: Protected, Virtual
@@ -585,31 +497,3 @@ dispatch_translate(const string &opt, const string &arg, void *var) {
 
   return true;
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: EggWriter::verify_output_file_safe
-//       Access: Protected
-//  Description: This is called when the output file is given as the
-//               last parameter on the command line.  Since this is a
-//               fairly dangerous way to specify the output file (it's
-//               easy to accidentally overwrite an input file this
-//               way), the convention is to disallow this syntax if
-//               the output file already exists.
-//
-//               This function will test if the output file exists,
-//               and issue a warning message if it does, returning
-//               false.  If all is well, it will return true.
-////////////////////////////////////////////////////////////////////
-bool EggWriter::
-verify_output_file_safe() const {
-  nassertr(_got_output_filename, false);
-
-  if (_output_filename.exists()) {
-    nout << "The output filename " << _output_filename << " already exists.  "
-      "If you wish to overwrite it, you must use the -o option to specify "
-      "the output filename, instead of simply specifying it as the last "
-      "parameter.\n";
-    return false;
-  }
-  return true;
-}

+ 3 - 13
pandatool/src/eggbase/eggWriter.h

@@ -10,6 +10,8 @@
 
 #include "eggBase.h"
 
+#include <withOutputFile.h>
+
 #include <filename.h>
 #include <luse.h>
 
@@ -18,7 +20,7 @@
 // Description : This is the base class for a program that generates
 //               an egg file output, but doesn't read any for input.
 ////////////////////////////////////////////////////////////////////
-class EggWriter : virtual public EggBase {
+class EggWriter : virtual public EggBase, public WithOutputFile {
 public:
   EggWriter(bool allow_last_param = false, bool allow_stdout = true);
 
@@ -27,16 +29,11 @@ public:
 
   virtual EggWriter *as_writer();
 
-  ostream &get_output();
-  bool has_output_filename() const;
-  Filename get_output_filename() const;
-
   virtual void post_process_egg_file();
   void write_egg_file();
 
 protected:
   virtual bool handle_args(Args &args);
-  bool check_last_arg(Args &args);
   virtual bool post_command_line();
 
   static bool dispatch_normals(ProgramBase *self, const string &opt, const string &arg, void *mode);
@@ -49,14 +46,7 @@ protected:
   bool ns_dispatch_rotate_axis(const string &opt, const string &arg, void *var);
   static bool dispatch_translate(const string &opt, const string &arg, void *var);
 
-  bool verify_output_file_safe() const;
-
 protected:
-  bool _allow_last_param;
-  bool _allow_stdout;
-  bool _got_output_filename;
-  Filename _output_filename;
-
   enum NormalsMode {
     NM_strip,
     NM_polygon,

+ 1 - 1
pandatool/src/eggprogs/eggTextureCards.cxx

@@ -61,7 +61,7 @@ EggTextureCards() : EggWriter(true, true) {
 ////////////////////////////////////////////////////////////////////
 bool EggTextureCards::
 handle_args(ProgramBase::Args &args) {
-  if (!check_last_arg(args)) {
+  if (!check_last_arg(args, 0)) {
     return false;
   }
 

+ 0 - 14
pandatool/src/flt/Sources.pp

@@ -54,17 +54,3 @@
     fltVertex.h fltVertexList.h
 
 #end ss_lib_target
-
-#begin test_bin_target
-  #define TARGET test_flt
-  #define LOCAL_LIBS \
-    flt
-  #define OTHER_LIBS \
-    express:c pandaexpress:m \
-    dtoolutil:c dconfig:c dtoolconfig:m dtool:m pystub
-
-  #define SOURCES \
-    test_flt.cxx
-
-#end test_bin_target
-

+ 8 - 5
pandatool/src/flt/fltGeometry.cxx

@@ -231,11 +231,14 @@ build_record(FltRecordWriter &writer) const {
     return false;
   }
 
-  datagram.add_be_int16(_texture_mapping_index);
-  datagram.pad_bytes(2);
-  datagram.add_be_uint32(_color_index);
-  datagram.add_be_uint32(_alt_color_index);
-  datagram.pad_bytes(2 + 2);
+  if (_header->get_flt_version() >= 15.2) {
+    // New with 15.2
+    datagram.add_be_int16(_texture_mapping_index);
+    datagram.pad_bytes(2);
+    datagram.add_be_uint32(_color_index);
+    datagram.add_be_uint32(_alt_color_index);
+    datagram.pad_bytes(2 + 2);
+  }
 
   return true;
 }

+ 77 - 21
pandatool/src/flt/fltHeader.cxx

@@ -10,6 +10,7 @@
 #include "config_flt.h"
 
 #include <assert.h>
+#include <math.h>
 
 TypeHandle FltHeader::_type_handle;
 
@@ -55,6 +56,8 @@ FltHeader() : FltBeadID(this) {
   _next_light_id = 1;
   _next_road_id = 1;
   _next_cat_id = 1;
+
+  // New with 15.2
   _earth_model = EM_wgs84;
 
   // New with 15.6
@@ -228,6 +231,21 @@ get_flt_version() const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FltHeader::set_flt_version
+//       Access: Public
+//  Description: Changes the version number of the flt file that will
+//               be reported in the header.
+////////////////////////////////////////////////////////////////////
+void FltHeader::
+set_flt_version(double version) {
+  if (version < 14.2) {
+    _format_revision_level = (int)floor(version + 0.5);
+  } else {
+    _format_revision_level = (int)floor(version * 100.0 + 0.5);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: FltHeader::min_flt_version
 //       Access: Public, Static
@@ -1289,24 +1307,28 @@ build_record(FltRecordWriter &writer) const {
   datagram.pad_bytes(2);
   datagram.add_be_int16(_next_road_id);
   datagram.add_be_int16(_next_cat_id);
-  datagram.pad_bytes(2 + 2 + 2 + 2);
-  datagram.add_be_int32(_earth_model);
-
-  datagram.pad_bytes(4);    
-
-  if (get_flt_version() >= 15.6) {
-    // New with 15.6
-    datagram.add_be_int16(_next_adaptive_id);
-    datagram.add_be_int16(_next_curve_id);
-    datagram.pad_bytes(4);
-
-    if (get_flt_version() >= 15.7) {
-      // New with 15.7
-      datagram.add_be_float64(_delta_z);
-      datagram.add_be_float64(_radius);
-      datagram.add_be_int16(_next_mesh_id);
-      datagram.pad_bytes(2);
+
+  if (get_flt_version() >= 15.2) {
+    // New with 15.2
+    datagram.pad_bytes(2 + 2 + 2 + 2);
+    datagram.add_be_int32(_earth_model);
+
+    datagram.pad_bytes(4);    
+
+    if (get_flt_version() >= 15.6) {
+      // New with 15.6
+      datagram.add_be_int16(_next_adaptive_id);
+      datagram.add_be_int16(_next_curve_id);
       datagram.pad_bytes(4);
+      
+      if (get_flt_version() >= 15.7) {
+	// New with 15.7
+	datagram.add_be_float64(_delta_z);
+	datagram.add_be_float64(_radius);
+	datagram.add_be_int16(_next_mesh_id);
+	datagram.pad_bytes(2);
+	datagram.pad_bytes(4);
+      }
     }
   }
 
@@ -1660,10 +1682,44 @@ FltError FltHeader::
 write_material_palette(FltRecordWriter &writer) const {
   FltError result;
 
-  Materials::const_iterator mi;
-  for (mi = _materials.begin(); mi != _materials.end(); ++mi) {
-    FltMaterial *material = (*mi).second;
-    material->build_record(writer);
+  if (get_flt_version() >= 15.2) {
+    // Write a version 15 material palette.
+    Materials::const_iterator mi;
+    for (mi = _materials.begin(); mi != _materials.end(); ++mi) {
+      FltMaterial *material = (*mi).second;
+      material->build_record(writer);
+
+      result = writer.advance();
+      if (result != FE_ok) {
+	return result;
+      }
+    }
+
+  } else {
+    // Write a version 14 material palette.
+    if (_materials.empty()) {
+      // No palette is OK.
+      return FE_ok;
+    }
+    writer.set_opcode(FO_14_material_palette);
+    Datagram &datagram = writer.update_datagram();
+
+    PT(FltMaterial) dummy_material = new FltMaterial(_header);
+
+    Materials::const_iterator mi = _materials.lower_bound(0);
+    int index;
+    static const int expected_material_entries = 64;
+    for (index = 0; index < expected_material_entries; index++) {
+      if (mi == _materials.end() || index < (*mi).first) {
+	dummy_material->build_14_record(datagram);
+      } else {
+	nassertr(index == (*mi).first, FE_internal);
+	FltMaterial *material = (*mi).second;
+	material->build_14_record(datagram);
+	++mi;
+      }
+    }
+
     result = writer.advance();
     if (result != FE_ok) {
       return result;

+ 1 - 0
pandatool/src/flt/fltHeader.h

@@ -128,6 +128,7 @@ public:
 
 public:
   double get_flt_version() const;
+  void set_flt_version(double version);
   static double min_flt_version();
   static double max_flt_version();
   bool check_version() const;

+ 31 - 0
pandatool/src/flt/fltMaterial.cxx

@@ -137,3 +137,34 @@ extract_14_record(int index, DatagramIterator &di) {
 
   return true;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltMaterial::build_14_record
+//       Access: Public
+//  Description: Fills up the current record on the FltRecordWriter
+//               with data for this record, formatted as a part of a
+//               v14 material palette.  Returns true on success, false
+//               if there is some error.
+////////////////////////////////////////////////////////////////////
+bool FltMaterial::
+build_14_record(Datagram &datagram) {
+  datagram.add_be_float32(_ambient[0]);
+  datagram.add_be_float32(_ambient[1]);
+  datagram.add_be_float32(_ambient[2]);
+  datagram.add_be_float32(_diffuse[0]);
+  datagram.add_be_float32(_diffuse[1]);
+  datagram.add_be_float32(_diffuse[2]);
+  datagram.add_be_float32(_specular[0]);
+  datagram.add_be_float32(_specular[1]);
+  datagram.add_be_float32(_specular[2]);
+  datagram.add_be_float32(_emissive[0]);
+  datagram.add_be_float32(_emissive[1]);
+  datagram.add_be_float32(_emissive[2]);
+  datagram.add_be_float32(_shininess);
+  datagram.add_be_float32(_alpha);
+  datagram.add_be_uint32(_flags);
+  datagram.add_fixed_string(_material_name, 12);
+  datagram.pad_bytes(4 * 28);
+
+  return true;
+}

+ 1 - 0
pandatool/src/flt/fltMaterial.h

@@ -42,6 +42,7 @@ protected:
 
 public:
   bool extract_14_record(int index, DatagramIterator &di);
+  bool build_14_record(Datagram &datagram);
 
 public:
   virtual TypeHandle get_type() const {

+ 44 - 19
pandatool/src/flt/fltVertex.cxx

@@ -59,21 +59,43 @@ get_opcode() const {
 ////////////////////////////////////////////////////////////////////
 int FltVertex::
 get_record_length() const {
-  switch (get_opcode()) {
-  case FO_vertex_c:
-    return 40;
-
-  case FO_vertex_cn:
-    return 56;
-
-  case FO_vertex_cnu:
-    return 64;
-
-  case FO_vertex_cu:
-    return 48;
+  if (_header->get_flt_version() < 15.2) {
+    // Version 14.2
+    switch (get_opcode()) {
+    case FO_vertex_c:
+      return 36;
+      
+    case FO_vertex_cn:
+      return 48;
+      
+    case FO_vertex_cnu:
+      return 56;
+      
+    case FO_vertex_cu:
+      return 44;
+      
+    default:
+      nassertr(false, 0);
+    }
 
-  default:
-    nassertr(false, 0);
+  } else {
+    // Version 15.2 and higher
+    switch (get_opcode()) {
+    case FO_vertex_c:
+      return 40;
+      
+    case FO_vertex_cn:
+      return 56;
+      
+    case FO_vertex_cnu:
+      return 64;
+      
+    case FO_vertex_cu:
+      return 48;
+      
+    default:
+      nassertr(false, 0);
+    }
   }
 
   return 0;
@@ -222,12 +244,15 @@ build_record(FltRecordWriter &writer) const {
     return false;
   }
 
-  datagram.add_be_uint32(_color_index);
+  if (_header->get_flt_version() >= 15.2) {
+    // New with 15.2
+    datagram.add_be_uint32(_color_index);
 
-  if (_has_normal) {
-    // If we added a normal, our double-word alignment is off; now we
-    // have a few extra bytes to add.
-    datagram.pad_bytes(4);
+    if (_has_normal) {
+      // If we added a normal, our double-word alignment is off; now we
+      // have a few extra bytes to add.
+      datagram.pad_bytes(4);
+    }
   }
 
   nassertr((int)datagram.get_length() == get_record_length() - 4, true);

+ 0 - 63
pandatool/src/flt/test_flt.cxx

@@ -1,63 +0,0 @@
-// Filename: test_flt.cxx
-// Created by:  drose (24Aug00)
-// 
-////////////////////////////////////////////////////////////////////
-
-#include "fltHeader.h"
-
-#include <getopt.h>
-
-void
-usage() {
-  cerr << "Usage: test_flt [opts] filename.flt\n";
-}
-
-int
-main(int argc, char *argv[]) {
-  static const char * const opts = "t:";
-  extern char *optarg;
-  extern int optind;
-
-  DSearchPath texture_path;
-
-  int flag = getopt(argc, argv, opts);
-  while (flag != EOF) {
-    switch (flag) {
-    case 't':
-      // t: Texture search path.
-      texture_path.append_directory(optarg);
-      break;
-
-    default:
-      usage();
-      exit(1);
-    } 
-    flag = getopt(argc, argv, opts);
-  }
-  argc -= (optind - 1);
-  argv += (optind - 1);
-
-  if (argc != 2) {
-    usage();
-    exit(1);
-  }
-
-  Filename filename = argv[1];
-
-  PT(FltHeader) header = new FltHeader;
-  header->set_texture_path(texture_path);
-
-  FltError result = header->read_flt(filename);
-  cerr << "Read result is " << result << "\n"
-       << "Version is " << header->get_flt_version() << "\n";
-  header->check_version();
-
-  if (result == FE_ok) {
-    //header->write(cerr);
-
-    result = header->write_flt("t.flt");
-    cerr << "Write result is " << result << "\n\n";
-  }
-
-  return (0);
-}

+ 14 - 0
pandatool/src/fltprogs/Sources.pp

@@ -11,3 +11,17 @@
     fltCopy.cxx fltCopy.h
 
 #end bin_target
+
+#begin bin_target
+  #define TARGET flt-trans
+  #define LOCAL_LIBS \
+    progbase flt
+  #define OTHER_LIBS \
+    linmath:c panda:m \
+    express:c pandaexpress:m \
+    dtoolutil:c dconfig:c dtoolconfig:m dtool:m pystub
+
+  #define SOURCES \
+    fltTrans.cxx fltTrans.h
+
+#end bin_target

+ 130 - 0
pandatool/src/fltprogs/fltTrans.cxx

@@ -0,0 +1,130 @@
+// Filename: fltTrans.cxx
+// Created by:  drose (11Apr01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "fltTrans.h"
+
+#include <fltHeader.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTrans::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+FltTrans::
+FltTrans() :
+  WithOutputFile(true, false, true)
+{
+  // Indicate the extension name we expect the user to supply for
+  // output files.
+  _preferred_extension = ".flt";
+  
+  set_program_description
+    ("This program reads a MultiGen OpenFlight (.flt) file and writes an "
+     "essentially equivalent .flt file, to the file specified with -o (or "
+     "as the second parameter).  Some simple operations may be performed.");
+
+  clear_runlines();
+  add_runline("[opts] input.flt output.flt");
+  add_runline("[opts] -o output.flt input.flt");
+
+  add_option
+    ("tp", "path", 0, 
+     "Add the indicated colon-delimited paths to the path that is searched "
+     "for textures referenced by the flt file.  This "
+     "option may also be repeated to add multiple paths.",
+     &FltTrans::dispatch_search_path, NULL, &_texture_path);
+
+  add_option
+    ("v", "version", 0, 
+     "Upgrade (or downgrade) the flt file to the indicated version.  This "
+     "may not be completely correct for all version-to-version combinations.",
+     &FltTrans::dispatch_double, &_got_new_version, &_new_version);
+
+  add_option
+    ("o", "filename", 0,
+     "Specify the filename to which the resulting .flt file will be written.  "
+     "If this option is omitted, the last parameter name is taken to be the "
+     "name of the output file.",
+     &FltTrans::dispatch_filename, &_got_output_filename, &_output_filename);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTrans::run
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void FltTrans::
+run() {
+  if (_got_new_version) {
+    if (_new_version < FltHeader::min_flt_version() ||
+	_new_version > FltHeader::max_flt_version()) {
+      nout << "Cannot write flt files of version " << _new_version
+	   << ".  This program only understands how to write flt files between version " 
+	   << FltHeader::min_flt_version() << " and " 
+	   << FltHeader::max_flt_version() << ".\n";
+      exit(1);
+    }
+  }
+
+  PT(FltHeader) header = new FltHeader;
+  header->set_texture_path(_texture_path);
+
+  nout << "Reading " << _input_filename << "\n";
+  FltError result = header->read_flt(_input_filename);
+  if (result != FE_ok) {
+    nout << "Unable to read: " << result << "\n";
+    exit(1);
+  } else {
+    if (header->check_version()) {
+      nout << "Version is " << header->get_flt_version() << "\n";
+    }
+
+    if (_got_new_version) {
+      header->set_flt_version(_new_version);
+    }
+
+    result = header->write_flt(get_output());
+    if (result != FE_ok) {
+      nout << "Unable to write: " << result << "\n";
+    } else {
+      nout << "Successfully written.\n";
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: FltTrans::handle_args
+//       Access: Protected, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool FltTrans::
+handle_args(ProgramBase::Args &args) {
+  if (!check_last_arg(args, 1)) {
+    return false;
+  }
+
+  if (args.empty()) {
+    nout << "You must specify the .flt file to read on the command line.\n";
+    return false;
+
+  } else if (args.size() != 1) {
+    nout << "You must specify only one .flt file to read on the command line.\n";
+    return false;
+  }
+
+  _input_filename = args[0];
+
+  return true;
+}
+
+
+int main(int argc, char *argv[]) {
+  FltTrans prog;
+  prog.parse_command_line(argc, argv);
+  prog.run();
+  return 0;
+}

+ 38 - 0
pandatool/src/fltprogs/fltTrans.h

@@ -0,0 +1,38 @@
+// Filename: fltTrans.h
+// Created by:  drose (11Apr01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FLTTRANS_H
+#define FLTTRANS_H
+
+#include <pandatoolbase.h>
+
+#include <programBase.h>
+#include <withOutputFile.h>
+
+#include <dSearchPath.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FltTrans
+// Description : A program to read a flt file and write an equivalent
+//               flt file, possibly performing some minor operations
+//               along the way.
+////////////////////////////////////////////////////////////////////
+class FltTrans : public ProgramBase, public WithOutputFile {
+public:
+  FltTrans();
+
+  void run();
+
+protected:
+  virtual bool handle_args(Args &args);
+
+  Filename _input_filename;
+  DSearchPath _texture_path;
+  bool _got_new_version;
+  double _new_version;
+};
+
+#endif
+

+ 6 - 2
pandatool/src/progbase/Sources.pp

@@ -6,12 +6,16 @@
     linmath:c putil:c express:c panda:m pystub dtool
 
   #define SOURCES \
-    programBase.I programBase.cxx programBase.h wordWrapStream.cxx \
+    programBase.I programBase.cxx programBase.h \
+    withOutputFile.cxx withOutputFile.h \
+    wordWrapStream.cxx \
     wordWrapStream.h wordWrapStreamBuf.I wordWrapStreamBuf.cxx \
     wordWrapStreamBuf.h
 
   #define INSTALL_HEADERS \
-    programBase.I programBase.h wordWrapStream.h wordWrapStreamBuf.I \
+    programBase.I programBase.h \
+    withOutputFile.h \
+    wordWrapStream.h wordWrapStreamBuf.I \
     wordWrapStreamBuf.h
 
 #end ss_lib_target

+ 169 - 0
pandatool/src/progbase/withOutputFile.cxx

@@ -0,0 +1,169 @@
+// Filename: withOutputFile.cxx
+// Created by:  drose (11Apr01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "withOutputFile.h"
+
+#include <notify.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: WithOutputFile::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+WithOutputFile::
+WithOutputFile(bool allow_last_param, bool allow_stdout,
+	       bool binary_output) {
+  _allow_last_param = allow_last_param;
+  _allow_stdout = allow_stdout;
+  _binary_output = binary_output;
+  _got_output_filename = false;
+  _output_ptr = (ostream *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WithOutputFile::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+WithOutputFile::
+~WithOutputFile() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WithOutputFile::get_output
+//       Access: Public
+//  Description: Returns an output stream that corresponds to the
+//               user's intended egg file output--either stdout, or
+//               the named output file.
+////////////////////////////////////////////////////////////////////
+ostream &WithOutputFile::
+get_output() {
+  if (_output_ptr == (ostream *)NULL) {
+    if (!_got_output_filename) {
+      // No filename given; use standard output.
+      nassertr(_allow_stdout, _output_stream);
+      _output_ptr = &cout;
+      
+    } else {
+      // Attempt to open the named file.
+      unlink(_output_filename.c_str());
+      _output_filename.make_dir();
+
+      if (_binary_output) {
+	_output_filename.set_binary();
+      } else {
+	_output_filename.set_text();
+      }
+
+      if (!_output_filename.open_write(_output_stream)) {
+	nout << "Unable to write to " << _output_filename << "\n";
+	exit(1);
+      }
+      nout << "Writing " << _output_filename << "\n";
+      _output_ptr = &_output_stream;
+    }
+  }
+  return *_output_ptr;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WithOutputFile::has_output_filename
+//       Access: Public
+//  Description: Returns true if the user specified an output
+//               filename, false otherwise (e.g. the output file is
+//               implicitly stdout).
+////////////////////////////////////////////////////////////////////
+bool WithOutputFile::
+has_output_filename() const {
+  return _got_output_filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WithOutputFile::get_output_filename
+//       Access: Public
+//  Description: If has_output_filename() returns true, this is the
+//               filename that the user specified.  Otherwise, it
+//               returns the empty string.
+////////////////////////////////////////////////////////////////////
+Filename WithOutputFile::
+get_output_filename() const {
+  if (_got_output_filename) {
+    return _output_filename;
+  }
+  return Filename();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WithOutputFile::check_last_arg
+//       Access: Protected
+//  Description: Checks if the last filename on the argument list is
+//               a file with the expected extension (if
+//               _allow_last_param was set true), and removes it from
+//               the argument list if it is.  Returns true if the
+//               arguments are good, false if something is invalid.
+//
+//               minimum_args is the number of arguments we know must
+//               be input parameters and therefore cannot be
+//               interpreted as output filenames.
+////////////////////////////////////////////////////////////////////
+bool WithOutputFile::
+check_last_arg(ProgramBase::Args &args, int minimum_args) {
+  if (_allow_last_param && !_got_output_filename && 
+      (int)args.size() > minimum_args) {
+    Filename filename = args.back();
+
+    if (!_preferred_extension.empty() && 
+	("." + filename.get_extension()) != _preferred_extension) {
+      // This argument must not be an output filename.
+      if (!_allow_stdout) {
+	nout << "Output filename " << filename 
+	     << " does not end in " << _preferred_extension 
+	     << ".  If this is really what you intended, "
+	  "use the -o output_file syntax.\n";
+	return false;
+      }
+
+    } else {
+      // This argument appears to be an output filename.
+      _got_output_filename = true;
+      _output_filename = filename;
+      args.pop_back();
+
+      if (!verify_output_file_safe()) {
+	return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: WithOutputFile::verify_output_file_safe
+//       Access: Protected
+//  Description: This is called when the output file is given as the
+//               last parameter on the command line.  Since this is a
+//               fairly dangerous way to specify the output file (it's
+//               easy to accidentally overwrite an input file this
+//               way), the convention is to disallow this syntax if
+//               the output file already exists.
+//
+//               This function will test if the output file exists,
+//               and issue a warning message if it does, returning
+//               false.  If all is well, it will return true.
+////////////////////////////////////////////////////////////////////
+bool WithOutputFile::
+verify_output_file_safe() const {
+  nassertr(_got_output_filename, false);
+
+  if (_output_filename.exists()) {
+    nout << "The output filename " << _output_filename << " already exists.  "
+      "If you wish to overwrite it, you must use the -o option to specify "
+      "the output filename, instead of simply specifying it as the last "
+      "parameter.\n";
+    return false;
+  }
+  return true;
+}

+ 53 - 0
pandatool/src/progbase/withOutputFile.h

@@ -0,0 +1,53 @@
+// Filename: outputFile.h
+// Created by:  drose (11Apr01)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef WITHOUTPUTFILE_H
+#define WITHOUTPUTFILE_H
+
+#include <pandatoolbase.h>
+
+#include "programBase.h"
+#include <filename.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : WithOutputFile
+// Description : This is the bare functionality (intended to be
+//               inherited from along with ProgramBase or some
+//               derivative) for a program that might generate an
+//               output file.
+//
+//               This provides the has_output_filename() and
+//               get_output_filename() methods.
+////////////////////////////////////////////////////////////////////
+class WithOutputFile {
+public:
+  WithOutputFile(bool allow_last_param, bool allow_stdout,
+		 bool binary_output);
+  virtual ~WithOutputFile();
+
+  ostream &get_output();
+  bool has_output_filename() const;
+  Filename get_output_filename() const;
+
+protected:
+  bool check_last_arg(ProgramBase::Args &args, int minimum_args);
+  bool verify_output_file_safe() const;
+
+protected:
+  bool _allow_last_param;
+  bool _allow_stdout;
+  bool _binary_output;
+  string _preferred_extension;
+  bool _got_output_filename;
+  Filename _output_filename;
+
+private:
+  ofstream _output_stream;
+  ostream *_output_ptr;
+};
+
+#endif
+
+