2005-02-19 00:00:47 +00:00

533 lines
16 KiB
C++

// Filename: txaFile.cxx
// Created by: drose (30Nov00)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "txaFile.h"
#include "pal_string_utils.h"
#include "palettizer.h"
#include "paletteGroup.h"
#include "textureImage.h"
#include "notify.h"
#include "pnmFileTypeRegistry.h"
////////////////////////////////////////////////////////////////////
// Function: TxaFile::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
TxaFile::
TxaFile() {
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::read
// Access: Public
// Description: Reads the indicated stream, and returns true
// if successful, or false if there is an error.
////////////////////////////////////////////////////////////////////
bool TxaFile::
read(istream &in, const string &filename) {
string line;
int line_number = 1;
int ch = get_line_or_semicolon(in, line);
while (ch != EOF || !line.empty()) {
bool okflag = true;
// Strip off the comment.
size_t hash = line.find('#');
if (hash != string::npos) {
line = line.substr(0, hash);
}
line = trim_left(line);
if (line.empty()) {
// Empty lines are ignored.
} else if (line[0] == ':') {
// This is a keyword line.
vector_string words;
extract_words(line, words);
if (words[0] == ":group") {
okflag = parse_group_line(words);
} else if (words[0] == ":palette") {
okflag = parse_palette_line(words);
} else if (words[0] == ":margin") {
okflag = parse_margin_line(words);
} else if (words[0] == ":background") {
okflag = parse_background_line(words);
} else if (words[0] == ":coverage") {
okflag = parse_coverage_line(words);
} else if (words[0] == ":imagetype") {
okflag = parse_imagetype_line(words);
} else if (words[0] == ":shadowtype") {
okflag = parse_shadowtype_line(words);
} else if (words[0] == ":round") {
okflag = parse_round_line(words);
} else if (words[0] == ":remap") {
okflag = parse_remap_line(words);
} else {
nout << "Invalid keyword " << words[0] << "\n";
okflag = false;
}
} else {
_lines.push_back(TxaLine());
TxaLine &txa_line = _lines.back();
okflag = txa_line.parse(line);
}
if (!okflag) {
nout << "Error on line " << line_number << " of " << filename << "\n";
return false;
}
if (ch == '\n') {
line_number++;
}
ch = get_line_or_semicolon(in, line);
}
if (!in.eof()) {
nout << "I/O error reading " << filename << "\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::match_egg
// Access: Public
// Description: Searches for a matching line in the .txa file for the
// given egg file and applies its specifications. If a
// match is found, returns true; otherwise, returns
// false. Also returns false if all the matching lines
// for the egg file include the keyword "cont".
////////////////////////////////////////////////////////////////////
bool TxaFile::
match_egg(EggFile *egg_file) const {
Lines::const_iterator li;
for (li = _lines.begin(); li != _lines.end(); ++li) {
if ((*li).match_egg(egg_file)) {
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::match_texture
// Access: Public
// Description: Searches for a matching line in the .txa file for the
// given texture and applies its specifications. If a
// match is found, returns true; otherwise, returns
// false. Also returns false if all the matching lines
// for the texture include the keyword "cont".
////////////////////////////////////////////////////////////////////
bool TxaFile::
match_texture(TextureImage *texture) const {
Lines::const_iterator li;
for (li = _lines.begin(); li != _lines.end(); ++li) {
if ((*li).match_texture(texture)) {
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::write
// Access: Public
// Description: Outputs a representation of the lines that were read
// in to the indicated output stream. This is primarily
// useful for debugging.
////////////////////////////////////////////////////////////////////
void TxaFile::
write(ostream &out) const {
Lines::const_iterator li;
for (li = _lines.begin(); li != _lines.end(); ++li) {
out << (*li) << "\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::get_line_or_semicolon
// Access: Private, Static
// Description: Reads the next line, or the next semicolon-delimited
// phrase, from the indicated input stream. Returns
// the character that marks the end of the line, or EOF
// if the end of file has been reached.
////////////////////////////////////////////////////////////////////
int TxaFile::
get_line_or_semicolon(istream &in, string &line) {
line = string();
int ch = in.get();
char semicolon = ';';
while (ch != EOF && ch != '\n' && ch != semicolon) {
if (ch == '#') {
// We don't consider a semicolon within a comment to be a line
// break.
semicolon = EOF;
}
line += ch;
ch = in.get();
}
return ch;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_group_line
// Access: Private
// Description: Handles the line in a .txa file that begins with the
// keyword ":group" and indicates the relationships
// between one or more groups.
////////////////////////////////////////////////////////////////////
bool TxaFile::
parse_group_line(const vector_string &words) {
vector_string::const_iterator wi;
wi = words.begin();
assert (wi != words.end());
++wi;
const string &group_name = (*wi);
PaletteGroup *group = pal->get_palette_group(group_name);
++wi;
enum State {
S_none,
S_on,
S_includes,
S_dir,
};
State state = S_none;
bool first_on = true;
while (wi != words.end()) {
const string &word = (*wi);
if (word == "with") {
// Deprecated keyword: "with" means the same thing as "on".
state = S_on;
} else if (word == "on") {
state = S_on;
} else if (word == "includes") {
state = S_includes;
} else if (word == "dir") {
state = S_dir;
} else {
switch (state) {
case S_none:
nout << "Invalid keyword: " << word << "\n";
return false;
case S_on:
{
PaletteGroup *on_group = pal->get_palette_group(word);
if (first_on) {
if (!group->has_dirname() && on_group->has_dirname()) {
group->set_dirname(on_group->get_dirname());
}
first_on = false;
}
group->group_with(on_group);
}
break;
case S_includes:
pal->get_palette_group(word)->group_with(group);
break;
case S_dir:
group->set_dirname(word);
state = S_none;
break;
}
}
++wi;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_palette_line
// Access: Private
// Description: Handles the line in a .txa file that begins with the
// keyword ":palette" and indicates the appropriate size
// for the palette images.
////////////////////////////////////////////////////////////////////
bool TxaFile::
parse_palette_line(const vector_string &words) {
if (words.size() != 3) {
nout << "Exactly two parameters required for :palette, the x and y "
<< "size of the palette images to generate.\n";
return false;
}
if (!string_to_int(words[1], pal->_pal_x_size) ||
!string_to_int(words[2], pal->_pal_y_size)) {
nout << "Invalid palette size: " << words[1] << " " << words[2] << "\n";
return false;
}
if (pal->_pal_x_size <= 0 || pal->_pal_y_size <= 0) {
nout << "Invalid palette size: " << pal->_pal_x_size
<< " " << pal->_pal_y_size << "\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_margin_line
// Access: Private
// Description: Handles the line in a .txa file that begins with the
// keyword ":margin" and indicates the default margin
// size.
////////////////////////////////////////////////////////////////////
bool TxaFile::
parse_margin_line(const vector_string &words) {
if (words.size() != 2) {
nout << "Exactly one parameter required for :margin, the "
<< "size of the default margin to apply.\n";
return false;
}
if (!string_to_int(words[1], pal->_margin)) {
nout << "Invalid margin: " << words[1] << "\n";
return false;
}
if (pal->_margin < 0) {
nout << "Invalid margin: " << pal->_margin << "\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_background_line
// Access: Private
// Description: Handles the line in a .txa file that begins with the
// keyword ":background" and indicates the palette
// background color.
////////////////////////////////////////////////////////////////////
bool TxaFile::
parse_background_line(const vector_string &words) {
if (words.size() != 5) {
nout << "Exactly four parameter required for :background: the "
<< "four [r g b a] components of the background color.\n";
return false;
}
if (!string_to_double(words[1], pal->_background[0]) ||
!string_to_double(words[2], pal->_background[1]) ||
!string_to_double(words[3], pal->_background[2]) ||
!string_to_double(words[4], pal->_background[3])) {
nout << "Invalid color: "
<< words[1] << " " << words[2] << " "
<< words[3] << " " << words[4] << " " << "\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_coverage_line
// Access: Private
// Description: Handles the line in a .txa file that begins with the
// keyword ":coverage" and indicates the default
// coverage threshold.
////////////////////////////////////////////////////////////////////
bool TxaFile::
parse_coverage_line(const vector_string &words) {
if (words.size() != 2) {
nout << "Exactly one parameter required for :coverage, the "
<< "value for the default coverage threshold.\n";
return false;
}
if (!string_to_double(words[1], pal->_coverage_threshold)) {
nout << "Invalid coverage threshold: " << words[1] << "\n";
return false;
}
if (pal->_coverage_threshold <= 0.0) {
nout << "Invalid coverage threshold: " << pal->_coverage_threshold << "\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_imagetype_line
// Access: Private
// Description: Handles the line in a .txa file that begins with the
// keyword ":imagetype" and indicates the default image
// file type to convert palettes and textures to.
////////////////////////////////////////////////////////////////////
bool TxaFile::
parse_imagetype_line(const vector_string &words) {
if (words.size() != 2) {
nout << "Exactly one parameter required for :imagetype.\n";
return false;
}
const string &imagetype = words[1];
if (!parse_image_type_request(imagetype, pal->_color_type, pal->_alpha_type)) {
nout << "\nKnown image types are:\n";
PNMFileTypeRegistry::get_global_ptr()->write(nout, 2);
nout << "\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_shadowtype_line
// Access: Private
// Description: Handles the line in a .txa file that begins with the
// keyword ":shadowtype" and indicates the image file
// type to convert working copies of the palette images
// to.
////////////////////////////////////////////////////////////////////
bool TxaFile::
parse_shadowtype_line(const vector_string &words) {
if (words.size() != 2) {
nout << "Exactly one parameter required for :shadowtype.\n";
return false;
}
const string &shadowtype = words[1];
if (!parse_image_type_request(shadowtype, pal->_shadow_color_type,
pal->_shadow_alpha_type)) {
nout << "\nKnown image types are:\n";
PNMFileTypeRegistry::get_global_ptr()->write(nout, 2);
nout << "\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_round_line
// Access: Private
// Description: Handles the line in a .txa file that begins with the
// keyword ":round" and indicates how or whether to
// round up UV minmax boxes.
////////////////////////////////////////////////////////////////////
bool TxaFile::
parse_round_line(const vector_string &words) {
if (words.size() == 2) {
if (words[1] == "no") {
pal->_round_uvs = false;
return true;
} else {
nout << "Invalid round keyword: " << words[1] << ".\n"
<< "Expected 'no', or a round fraction and fuzz factor.\n";
return false;
}
}
if (words.size() != 3) {
nout << "Exactly two parameters required for :round, the fraction "
<< "to round to, and the fuzz factor.\n";
return false;
}
if (!string_to_double(words[1], pal->_round_unit) ||
!string_to_double(words[2], pal->_round_fuzz)) {
nout << "Invalid rounding: " << words[1] << " " << words[2] << "\n";
return false;
}
if (pal->_round_unit <= 0.0 || pal->_round_fuzz < 0.0) {
nout << "Invalid rounding: " << pal->_round_unit
<< " " << pal->_round_fuzz << "\n";
return false;
}
pal->_round_uvs = true;
return true;
}
////////////////////////////////////////////////////////////////////
// Function: TxaFile::parse_remap_line
// Access: Private
// Description: Handles the line in a .txa file that begins with the
// keyword ":remap" and indicates how or whether to
// remap UV coordinates in egg files to the unit box.
////////////////////////////////////////////////////////////////////
bool TxaFile::
parse_remap_line(const vector_string &words) {
int i = 1;
while (i < (int)words.size()) {
const string &keyword = words[i];
if (keyword == "char") {
// Defining how to remap UV's for characters.
i++;
if (i == (int)words.size()) {
nout << "Keyword expected following 'char'\n";
return false;
}
pal->_remap_char_uv = Palettizer::string_remap(words[i]);
if (pal->_remap_char_uv == Palettizer::RU_invalid) {
nout << "Invalid remap keyword: " << words[i] << "\n";
return false;
}
} else {
// Defining how to remap UV's in general.
pal->_remap_uv = Palettizer::string_remap(words[i]);
if (pal->_remap_uv == Palettizer::RU_invalid) {
nout << "Invalid remap keyword: " << words[i] << "\n";
return false;
}
pal->_remap_char_uv = pal->_remap_uv;
}
i++;
}
return true;
}