#include <sstream>
#include <stdexcept>
#include <list>
-#include <physfs.h>
+//#include <physfs.h>
+#include <unison/vfs/FileSystem.hpp>
+#include <unison/vfs/sdl/Utils.hpp>
#include <sys/stat.h>
#include <stdio.h>
+#include "SDL.h"
#include "addon_manager.hpp"
#include "config.h"
#include "log.hpp"
return size * nmemb;
}
- size_t my_curl_physfs_write(void *ptr, size_t size, size_t nmemb, void *f_p)
+ /*size_t my_curl_physfs_write(void *ptr, size_t size, size_t nmemb, void *f_p)
{
PHYSFS_file* f = static_cast<PHYSFS_file*>(f_p);
PHYSFS_sint64 written = PHYSFS_write(f, ptr, size, nmemb);
log_debug << "read " << size * nmemb << " bytes of data..." << std::endl;
return size * written;
+ }*/
+
+ size_t my_curl_sdl_write(void *ptr, size_t size, size_t nmemb, void *f_p)
+ {
+ SDL_RWops* f = static_cast<SDL_RWops*>(f_p);
+ int written = SDL_RWwrite(f, ptr, size, nmemb);
+ log_debug << "wrote " << size * nmemb << " bytes of data..." << std::endl;
+ return size * written;
}
}
{
std::vector<Addon> addons;
- // iterate over complete search path (i.e. directories and archives)
- char **i = PHYSFS_getSearchPath();
- if (!i) throw std::runtime_error("Could not query physfs search path");
- for (; *i != NULL; i++) {
-
+ Unison::VFS::FileSystem &fs = Unison::VFS::FileSystem::get();
+ std::vector<std::string> search_path = fs.get_search_path();
+ for(std::vector<std::string>::iterator iter = search_path.begin();iter != search_path.end();++iter)
+ {
// get filename of potential archive
- std::string fileName = *i;
+ std::string fileName = *iter;
// make sure it's in the writeDir
- static const std::string writeDir = PHYSFS_getWriteDir();
+ static const std::string writeDir = fs.get_write_dir();
if (fileName.compare(0, writeDir.length(), writeDir) != 0) continue;
// make sure it looks like an archive
Addon addon;
// extract nice title as fallback for when the Add-on has no addoninfo file
- static const char* dirSep = PHYSFS_getDirSeparator();
+ static std::string dirSep = fs.get_dir_sep();
std::string::size_type n = fileName.rfind(dirSep) + 1;
if (n == std::string::npos) n = 0;
addon.title = fileName.substr(n, fileName.length() - n - archiveExt.length());
// read an accompaining .nfo file, if it exists
static const std::string infoExt = ".nfo";
std::string infoFileName = fileName.substr(n, fileName.length() - n - archiveExt.length()) + infoExt;
- if (PHYSFS_exists(infoFileName.c_str())) {
+ if (fs.exists(infoFileName)) {
addon.parse(infoFileName);
if (addon.file != shortFileName) {
log_warning << "Add-on \"" << addon.title << "\", contained in file \"" << shortFileName << "\" is accompained by an addoninfo file that specifies \"" << addon.file << "\" as the Add-on's file name. Skipping." << std::endl;
throw std::runtime_error("Add-on has unsafe file name (\""+addon.file+"\")");
}
+ Unison::VFS::FileSystem &fs = Unison::VFS::FileSystem::get();
+
#ifdef HAVE_LIBCURL
char error_buffer[CURL_ERROR_SIZE+1];
char* url = (char*)malloc(addon.http_url.length() + 1);
strncpy(url, addon.http_url.c_str(), addon.http_url.length() + 1);
- PHYSFS_file* f = PHYSFS_openWrite(addon.file.c_str());
+ //PHYSFS_file* f = PHYSFS_openWrite(addon.file.c_str());
+ SDL_RWops *f = Unison::VFS::SDL::Utils::open_physfs_in(addon.file);
log_debug << "Downloading \"" << url << "\"" << std::endl;
curl_handle = curl_easy_init();
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL");
- curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, my_curl_physfs_write);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, my_curl_sdl_write);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, f);
curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error_buffer);
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
CURLcode result = curl_easy_perform(curl_handle);
curl_easy_cleanup(curl_handle);
- PHYSFS_close(f);
+ //PHYSFS_close(f);
+ SDL_RWclose(f);
free(url);
if (result != CURLE_OK) {
- PHYSFS_delete(addon.file.c_str());
+ //PHYSFS_delete(addon.file.c_str());
+ fs.rm(addon.file);
std::string why = error_buffer[0] ? error_buffer : "unhandled error";
throw std::runtime_error("Downloading Add-on failed: " + why);
}
std::string infoFileName = addon.file.substr(0, addon.file.length()-archiveExt.length()) + infoExt;
addon.write(infoFileName);
- static const std::string writeDir = PHYSFS_getWriteDir();
- static const std::string dirSep = PHYSFS_getDirSeparator();
+ //static const std::string writeDir = PHYSFS_getWriteDir();
+ //static const std::string dirSep = PHYSFS_getDirSeparator();
+ static const std::string writeDir = fs.get_write_dir();
+ static const std::string dirSep = fs.get_dir_sep();
std::string fullFilename = writeDir + dirSep + addon.file;
log_debug << "Finished downloading \"" << fullFilename << "\"" << std::endl;
- PHYSFS_addToSearchPath(fullFilename.c_str(), 1);
+ fs.mount(fullFilename, "/", true);
+ //PHYSFS_addToSearchPath(fullFilename.c_str(), 1);
#else
(void) addon;
#endif
}
log_debug << "deleting file \"" << addon.file << "\"" << std::endl;
- PHYSFS_removeFromSearchPath(addon.file.c_str());
- PHYSFS_delete(addon.file.c_str());
+
+ Unison::VFS::FileSystem &fs = Unison::VFS::FileSystem::get();
+ fs.umount(addon.file);
+ fs.rm(addon.file);
// remove an accompaining .nfo file
static const std::string archiveExt = ".zip";
static const std::string infoExt = ".nfo";
std::string infoFileName = addon.file.substr(0, addon.file.length()-archiveExt.length()) + infoExt;
- if (PHYSFS_exists(infoFileName.c_str())) {
+ if (fs.exists(infoFileName)) {
log_debug << "deleting file \"" << infoFileName << "\"" << std::endl;
- PHYSFS_delete(infoFileName.c_str());
+ fs.rm(infoFileName);
}
}
Config::Config()
{
use_fullscreen = true;
- video = AUTO_VIDEO;
+ //video = AUTO_VIDEO;
+ video = "auto";
try_vsync = true;
show_fps = false;
sound_enabled = true;
const lisp::Lisp* config_video_lisp = config_lisp->get_lisp("video");
if(config_video_lisp) {
config_video_lisp->get("fullscreen", use_fullscreen);
- std::string video_string;
- config_video_lisp->get("video", video_string);
- video = get_video_system(video_string);
+ //std::string video_string;
+ //config_video_lisp->get("video", video_string);
+ //video = get_video_system(video_string);
+ config_video_lisp->get("video", video);
config_video_lisp->get("vsync", try_vsync);
config_video_lisp->get("width", screenwidth);
config_video_lisp->get("height", screenheight);
writer.start_list("video");
writer.write_bool("fullscreen", use_fullscreen);
- writer.write_string("video", get_video_string(video));
+ //writer.write_string("video", get_video_string(video));
+ writer.write_string("video", video);
writer.write_bool("vsync", try_vsync);
writer.write_int("width", screenwidth);
writer.write_int("height", screenheight);
#include <string>
-#include "video/video_systems.hpp"
+//#include "video/video_systems.hpp"
class Config
{
float aspect_ratio;
bool use_fullscreen;
- VideoSystem video;
+ std::string video;
bool try_vsync;
bool show_fps;
bool sound_enabled;
const Lisp*
Parser::parse(const std::string& filename)
{
- IFileStreambuf ins(filename);
- std::istream in(&ins);
+ Unison::VFS::istream in(filename);
+ //IFileStreambuf ins(filename);
+ //std::istream in(&ins);
if(!in.good()) {
std::stringstream msg;
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
-#include <physfs.h>
+//#include <physfs.h>
#include <SDL.h>
-#include <SDL_image.h>
+//#include <SDL_image.h>
#ifdef MACOSX
namespace supertux_apple {
}
#endif
+#include <unison/video/Window.hpp>
+#include <unison/vfs/FileSystem.hpp>
+
#include "gameconfig.hpp"
#include "resources.hpp"
#include "gettext.hpp"
#include "audio/sound_manager.hpp"
#include "video/surface.hpp"
-#include "video/texture_manager.hpp"
+//#include "video/texture_manager.hpp"
#include "video/drawing_context.hpp"
#include "video/glutil.hpp"
#include "control/joystickkeyboardcontroller.hpp"
static void init_physfs(const char* argv0)
{
+ Unison::VFS::FileSystem &fs = Unison::VFS::FileSystem::get();
+ std::string application = "supertux2";
+ std::string userdir = fs.get_user_dir();
+ std::string dirsep = fs.get_dir_sep();
+ std::string writedir = userdir + "." + application;
+ fs.set_write_dir(writedir);
+ fs.mount(writedir, "/", true);
+
+ // Search for archives and add them to the search path
+ const char* archiveExt = ".zip";
+ std::vector<std::string> rc = fs.ls("/");
+ for(std::vector<std::string>::iterator iter = rc.begin(); iter != rc.end(); ++iter)
+ {
+ std::string ext = iter->substr(iter->length() - 4);
+ if(strcasecmp(ext.c_str(), archiveExt) == 0)
+ {
+ std::string dir = fs.get_real_dir(*iter);
+ fs.mount(dir + fs.get_dir_sep() + *iter, "/", true);
+ }
+ }
+
+ // when started from source dir...
+ std::string dir = fs.get_base_dir();
+ dir += "/data";
+ std::string testfname = dir;
+ testfname += "/credits.txt";
+ bool sourcedir = false;
+ FILE* f = fopen(testfname.c_str(), "r");
+ if(f) {
+ fclose(f);
+ fs.mount(dir, "/", true);
+ sourcedir = true;
+ /*if(!PHYSFS_addToSearchPath(dir.c_str(), 1)) {
+ log_warning << "Couldn't add '" << dir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
+ } else {
+ sourcedir = true;
+ }*/
+ }
+
+#ifdef MACOSX
+ // when started from Application file on Mac OS X...
+ char path[PATH_MAX];
+ CFBundleRef mainBundle = CFBundleGetMainBundle();
+ assert(mainBundle != 0);
+ CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle);
+ assert(mainBundleURL != 0);
+ CFStringRef pathStr = CFUrlCopyFileSystemPath(mainBundleURL, kCFURLPOSIXPathStyle);
+ assert(pathStr != 0);
+ CFStringGetCString(pathStr, path, PATH_MAX, kCFStringEncodingUTF8);
+ CFRelease(mainBundleURL);
+ CFRelease(pathStr);
+
+ dir = std::string(path) + "/Contents/Resources/data";
+ testfname = dir + "/credits.txt";
+ sourcedir = false;
+ f = fopen(testfname.c_str(), "r");
+ if(f) {
+ fclose(f);
+ fs.mount(dir, "/", true);
+ sourcedir = true;
+ /*if(!PHYSFS_addToSearchPath(dir.c_str(), 1)) {
+ log_warning << "Couldn't add '" << dir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
+ } else {
+ sourcedir = true;
+ }*/
+ }
+#endif
+
+#ifdef _WIN32
+ fs.mount(".\\data", "/", true);
+ //PHYSFS_addToSearchPath(".\\data", 1);
+#endif
+
+ if(!sourcedir) {
+#if defined(APPDATADIR) || defined(ENABLE_BINRELOC)
+ std::string datadir;
+#ifdef ENABLE_BINRELOC
+
+ char* dir;
+ br_init (NULL);
+ dir = br_find_data_dir(APPDATADIR);
+ datadir = dir;
+ free(dir);
+
+#else
+ datadir = APPDATADIR;
+#endif
+ datadir += "/";
+ datadir += application;
+ fs.mount(datadir, "/", true);
+ /*if(!PHYSFS_addToSearchPath(datadir.c_str(), 1)) {
+ log_warning << "Couldn't add '" << datadir << "' to physfs searchpath: " << PHYSFS_getLastError() << std::endl;
+ }*/
+#endif
+ }
+
+ // allow symbolic links
+ //PHYSFS_permitSymbolicLinks(1);
+ //fs.follow_sym_links(true);
+
+ //show search Path
+ std::vector<std::string> searchpath = fs.get_search_path();
+ for(std::vector<std::string>::iterator iter = searchpath.begin(); iter != searchpath.end(); ++iter)
+ log_info << "[" << *iter << "] is in the search path" << std::endl;
+ /*
+ char** searchpath = PHYSFS_getSearchPath();
+ for(char** i = searchpath; *i != NULL; i++)
+ log_info << "[" << *i << "] is in the search path" << std::endl;
+ PHYSFS_freeList(searchpath);
+ */
+
+#if 0
if(!PHYSFS_init(argv0)) {
std::stringstream msg;
msg << "Couldn't initialize physfs: " << PHYSFS_getLastError();
for(char** i = searchpath; *i != NULL; i++)
log_info << "[" << *i << "] is in the search path" << std::endl;
PHYSFS_freeList(searchpath);
+#endif
}
static void print_usage(const char* argv0)
context_pointer->init_renderer();
screen = SDL_GetVideoSurface();
- SDL_WM_SetCaption(PACKAGE_NAME " " PACKAGE_VERSION, 0);
+ Unison::Video::Window::get().set_title(PACKAGE_NAME " " PACKAGE_VERSION);
+ Unison::Video::Window::get().set_icon(Unison::Video::Surface("images/engine/icons/supertux.xpm"));
+ //SDL_WM_SetCaption(PACKAGE_NAME " " PACKAGE_VERSION, 0);
// set icon
+ /*
SDL_Surface* icon = IMG_Load_RW(
get_physfs_SDLRWops("images/engine/icons/supertux.xpm"), true);
if(icon != 0) {
log_warning << "Couldn't find icon 'images/engine/icons/supertux.xpm'" << std::endl;
}
#endif
+ */
SDL_ShowCursor(0);
// we have a normal path specified at commandline not physfs paths.
// So we simply mount that path here...
std::string dir = FileSystem::dirname(config->start_level);
- PHYSFS_addToSearchPath(dir.c_str(), true);
+ Unison::VFS::FileSystem::get().mount(dir, "/", true);
+ //PHYSFS_addToSearchPath(dir.c_str(), true);
if(config->start_level.size() > 4 &&
config->start_level.compare(config->start_level.size() - 5, 5, ".stwm") == 0) {
delete Console::instance;
Console::instance = NULL;
Scripting::exit_squirrel();
- delete texture_manager;
- texture_manager = NULL;
+ //delete texture_manager;
+ //texture_manager = NULL;
SDL_Quit();
- PHYSFS_deinit();
+ //PHYSFS_deinit();
return result;
}
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#include <unison/vfs/stream.hpp>
+#include <unison/vfs/sdl/Utils.hpp>
+
+#include <stdexcept>
+
+SDL_RWops* get_physfs_SDLRWops(const std::string& filename)
+{
+ // check this as PHYSFS seems to be buggy and still returns a
+ // valid pointer in this case
+ if(filename == "") {
+ throw std::runtime_error("Couldn't open file: empty filename");
+ }
+ return Unison::VFS::SDL::Utils::open_physfs_in(filename);
+}
+
+#if 0
#include <config.h>
#include "physfs_sdl.hpp"
ops->close = funcClose;
return ops;
}
+#endif
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#if 0
#include <config.h>
#include "physfs_stream.hpp"
{
delete rdbuf();
}
+#endif
#ifndef __PHYSFSSTREAM_HPP__
#define __PHYSFSSTREAM_HPP__
+#include <unison/vfs/stream.hpp>
+
+typedef Unison::VFS::istream IFileStream;
+typedef Unison::VFS::ostream OFileStream;
+
+#if 0
#include <stddef.h>
#include <physfs.h>
#include <string>
OFileStream(const std::string& filename);
~OFileStream();
};
+#endif
#endif
#include <float.h>
#include <math.h>
#include <limits>
-#include <physfs.h>
+//#include <physfs.h>
+#include <unison/vfs/FileSystem.hpp>
#include "sector.hpp"
#include "object/player.hpp"
if (backgroundimage == "arctis2.jpg") backgroundimage = "arctis.jpg";
if (backgroundimage == "ocean.png") backgroundimage = "ocean.jpg";
backgroundimage = "images/background/" + backgroundimage;
- if (!PHYSFS_exists(backgroundimage.c_str())) {
+ if (!Unison::VFS::FileSystem::get().exists(backgroundimage)) {
log_warning << "Background image \"" << backgroundimage << "\" not found. Ignoring." << std::endl;
backgroundimage = "";
}
#include "tinygettext.hpp"
#include "log.hpp"
#include "physfs/physfs_stream.hpp"
+#include <unison/vfs/FileSystem.hpp>
#include "log.hpp"
#include "findlocale.hpp"
Dictionaries::iterator i = dictionaries.find(get_language_from_spec(lang));
if (i != dictionaries.end())
- {
- return i->second;
- }
+ {
+ return i->second;
+ }
else // Dictionary for languages lang isn't loaded, so we load it
- {
- //log_debug << "get_dictionary: " << lang << std::endl;
- Dictionary& dict = dictionaries[lang];
+ {
+ //log_debug << "get_dictionary: " << lang << std::endl;
+ Dictionary& dict = dictionaries[lang];
- dict.set_language(get_language_def(lang));
- if(charset != "")
- dict.set_charset(charset);
+ dict.set_language(get_language_def(lang));
+ if(charset != "")
+ dict.set_charset(charset);
- for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
- {
- char** files = PHYSFS_enumerateFiles(p->c_str());
- if(!files)
- {
- log_warning << "Error: enumerateFiles() failed on " << *p << std::endl;
+ for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
+ {
+ std::vector<std::string> files = Unison::VFS::FileSystem::get().ls(*p);
+ for(std::vector<std::string>::iterator iter = files.begin();iter != files.end();++iter)
+ {
+ // check if filename matches requested language
+ std::string fname = *iter;
+ std::string load_from_file = "";
+ if(fname == lang + ".po") {
+ load_from_file = fname;
+ } else {
+ std::string::size_type s = lang.find("_");
+ if(s != std::string::npos) {
+ std::string lang_short = std::string(lang, 0, s);
+ if (fname == lang_short + ".po") {
+ load_from_file = lang_short;
}
- else
- {
- for(const char* const* filename = files;
- *filename != 0; filename++) {
-
- // check if filename matches requested language
- std::string fname = std::string(*filename);
- std::string load_from_file = "";
- if(fname == lang + ".po") {
- load_from_file = fname;
- } else {
- std::string::size_type s = lang.find("_");
- if(s != std::string::npos) {
- std::string lang_short = std::string(lang, 0, s);
- if (fname == lang_short + ".po") {
- load_from_file = lang_short;
- }
- }
- }
-
- // if it matched, load dictionary
- if (load_from_file != "") {
- //log_debug << "Loading dictionary for language \"" << lang << "\" from \"" << filename << "\"" << std::endl;
- std::string pofile = *p + "/" + *filename;
- try {
- IFileStream in(pofile);
- read_po_file(dict, in);
- } catch(std::exception& e) {
- log_warning << "Error: Failure file opening: " << pofile << std::endl;
- log_warning << e.what() << "" << std::endl;
- }
- }
+ }
+ }
- }
- PHYSFS_freeList(files);
- }
+ // if it matched, load dictionary
+ if (load_from_file != "") {
+ //log_debug << "Loading dictionary for language \"" << lang << "\" from \"" << filename << "\"" << std::endl;
+ std::string pofile = *p + "/" + *iter;
+ try {
+ IFileStream in(pofile);
+ read_po_file(dict, in);
+ } catch(std::exception& e) {
+ log_warning << "Error: Failure file opening: " << pofile << std::endl;
+ log_warning << e.what() << "" << std::endl;
+ }
+ }
+ }
+
+#if 0
+ char** files = PHYSFS_enumerateFiles(p->c_str());
+ if(!files)
+ {
+ log_warning << "Error: enumerateFiles() failed on " << *p << std::endl;
+ }
+ else
+ {
+ for(const char* const* filename = files;
+ *filename != 0; filename++) {
+
+ // check if filename matches requested language
+ std::string fname = std::string(*filename);
+ std::string load_from_file = "";
+ if(fname == lang + ".po") {
+ load_from_file = fname;
+ } else {
+ std::string::size_type s = lang.find("_");
+ if(s != std::string::npos) {
+ std::string lang_short = std::string(lang, 0, s);
+ if (fname == lang_short + ".po") {
+ load_from_file = lang_short;
+ }
}
+ }
+
+ // if it matched, load dictionary
+ if (load_from_file != "") {
+ //log_debug << "Loading dictionary for language \"" << lang << "\" from \"" << filename << "\"" << std::endl;
+ std::string pofile = *p + "/" + *filename;
+ try {
+ IFileStream in(pofile);
+ read_po_file(dict, in);
+ } catch(std::exception& e) {
+ log_warning << "Error: Failure file opening: " << pofile << std::endl;
+ log_warning << e.what() << "" << std::endl;
+ }
+ }
- return dict;
+ }
+ PHYSFS_freeList(files);
+ }
+#endif
}
+
+ return dict;
+ }
}
std::set<std::string>
for (SearchPath::iterator p = search_path.begin(); p != search_path.end(); ++p)
{
+ std::vector<std::string> files = Unison::VFS::FileSystem::get().ls(*p);
+ for(std::vector<std::string>::iterator iter = files.begin();iter != files.end();++iter)
+ {
+ if(has_suffix(*iter, ".po")) {
+ std::string filename = *iter;
+ languages.insert(filename.substr(0, filename.length()-3));
+ }
+ }
+#if 0
char** files = PHYSFS_enumerateFiles(p->c_str());
if (!files)
{
}
PHYSFS_freeList(files);
}
+#endif
}
return languages;
}
#include <errno.h>
#include <unistd.h>
#include <SDL.h>
-#include <SDL_image.h>
-#include <physfs.h>
+//#include <SDL_image.h>
+//#include <physfs.h>
+#include <unison/vfs/FileSystem.hpp>
#include "title.hpp"
#include "mainloop.hpp"
{
/** Generating contrib levels list by making use of Level Subset */
std::vector<std::string> level_worlds;
- char** files = PHYSFS_enumerateFiles("levels/");
+ std::vector<std::string> files = Unison::VFS::FileSystem::get().ls("levels/");
+ for(std::vector<std::string>::iterator iter = files.begin();iter != files.end();++iter)
+ {
+ std::string filepath = "levels/" + *iter;
+ if(Unison::VFS::FileSystem::get().is_dir(filepath))
+ level_worlds.push_back(filepath);
+ }
+ /*char** files = PHYSFS_enumerateFiles("levels/");
for(const char* const* filename = files; *filename != 0; ++filename) {
std::string filepath = std::string("levels/") + *filename;
if(PHYSFS_isDirectory(filepath.c_str()))
level_worlds.push_back(filepath);
}
- PHYSFS_freeList(files);
+ PHYSFS_freeList(files);*/
free_contrib_menu();
contrib_menu.reset(new Menu());
stream << "save/" << worlddirname << "_" << slot << ".stsg";
std::string slotfile = stream.str();
- try {
+ if(Unison::VFS::FileSystem::get().exists(slotfile))
+ {
lisp::Parser parser;
const lisp::Lisp* root = parser.parse(slotfile);
throw std::runtime_error("file is not a supertux-savegame.");
savegame->get("title", title);
- } catch(std::exception& ) {
+ }
+ else
+ {
std::ostringstream slottitle;
slottitle << _("Slot") << " " << slot << " - " << _("Free");
return slottitle.str();
--- /dev/null
+# Copyright Timothy Goya 2007.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+PROJECT(UNISON)
+
+INCLUDE_DIRECTORIES(${UNISON_SOURCE_DIR}/include/)
+
+FIND_PACKAGE(SDL REQUIRED)
+INCLUDE_DIRECTORIES(${SDL_INCLUDE_DIR})
+LINK_LIBRARIES(${SDL_LIBRARY})
+
+FIND_PACKAGE(SDL_image REQUIRED)
+INCLUDE_DIRECTORIES(${SDLIMAGE_INCLUDE_DIR})
+LINK_LIBRARIES(${SDLIMAGE_LIBRARY})
+
+FIND_PACKAGE(PhysFS)
+IF(${PHYSFS_FOUND} STREQUAL "NO")
+ SET(PHYSFS_BUILD_SHARED FALSE)
+ ADD_SUBDIRECTORY(physfs-1.1.1)
+ SET(PHYSFS_INCLUDE_DIR physfs-1.1.1)
+ SET(PHYSFS_LIBRARY ${UNISON_BINARY_DIR}/physfs-1.1.1/libphysfs.a)
+ENDIF(${PHYSFS_FOUND} STREQUAL "NO")
+INCLUDE_DIRECTORIES(${PHYSFS_INCLUDE_DIR})
+LINK_LIBRARIES(${PHYSFS_LIBRARY})
+
+IF(CMAKE_COMPILER_IS_GNUCC)
+ ADD_DEFINITIONS(-g -O2 -Wall -Wextra)
+ENDIF(CMAKE_COMPILER_IS_GNUCC)
+
+IF(MSVC)
+ ADD_DEFINITIONS(-D_CRG_SECURE_NO_WARNINGS=1)
+ENDIF(MSVC)
+
+CHECK_INCLUDE_FILE(assert.h HAVE_ASSERT_H)
+IF(HAVE_ASSERT_H)
+ ADD_DEFINITIONS(-DHAVE_ASSERT_H=1)
+ENDIF(HAVE_ASSERT_H)
+
+FILE(GLOB_RECURSE UNISON_SOURCES RELATIVE ${UNISON_SOURCE_DIR} src/*.cpp src/*.c)
+
+ADD_LIBRARY(unison ${UNISON_SOURCES})
+
+IF(${PHYSFS_FOUND} STREQUAL "NO")
+ ADD_DEPENDENCIES(unison physfs-static)
+ SET(PHYSFS_FOUND "YES")
+ENDIF(${PHYSFS_FOUND} STREQUAL "NO")
+
+FIND_PACKAGE(Doxygen)
+IF(DOXYGEN_FOUND)
+ ADD_CUSTOM_TARGET(docs ${DOXYGEN_EXECUTABLE} COMMENT "Building documentation")
+ELSE(DOXYGEN_FOUND)
+ MESSAGE(STATUS "Doxygen not found. You won't be able to build documentation.")
+ENDIF(DOXYGEN_FOUND)
--- /dev/null
+# Doxyfile 1.5.2
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file that
+# follow. The default is UTF-8 which is also the encoding used for all text before
+# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
+# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
+# possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = Unison
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = include/unison/video include/unison/video/backend
+
+# This tag can be used to specify the character encoding of the source files that
+# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
+# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
+# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the output.
+# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
+# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = docs
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
+# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
+# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
+# be found in the default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen will always
+# show the root nodes and its direct children regardless of this setting.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
--- /dev/null
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VFS_FILE_SYSTEM_HPP
+#define UNISON_VFS_FILE_SYSTEM_HPP
+
+#include <string>
+#include <vector>
+
+namespace Unison
+{
+ namespace VFS
+ {
+ class FileSystem
+ {
+ public:
+ static FileSystem &get()
+ {
+ static FileSystem vfs;
+ return vfs;
+ }
+
+ void follow_sym_links(bool follow);
+
+ std::string get_dir_sep();
+ std::string get_base_dir();
+ std::string get_user_dir();
+
+ std::string get_write_dir();
+ void set_write_dir(const std::string &write_dir);
+
+ void mount(const std::string &path, const std::string &mount_point = "/", bool append = false);
+ void umount(const std::string &path);
+ std::vector<std::string> get_search_path();
+ std::string get_mount_point(const std::string &path);
+
+ void mkdir(const std::string &dir);
+ void rm(const std::string &filename);
+ std::vector<std::string> ls(const std::string &path);
+ bool exists(const std::string &filename);
+ bool is_dir(const std::string &filename);
+
+ std::string get_real_dir(const std::string &filename);
+ private:
+ FileSystem();
+ ~FileSystem();
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VFS_SDL_UTILS_HPP
+#define UNISON_VFS_SDL_UTILS_HPP
+
+#include <string>
+
+#include "SDL.h"
+
+namespace Unison
+{
+ namespace VFS
+ {
+ namespace SDL
+ {
+ struct Utils
+ {
+ static SDL_RWops *open_physfs_in(const std::string &filename);
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VFS_STREAM_HPP
+#define UNISON_VFS_STREAM_HPP
+
+#include <iostream>
+
+namespace Unison
+{
+ namespace VFS
+ {
+ class istream : public std::istream
+ {
+ public:
+ istream(const std::string &filename);
+ ~istream();
+ };
+
+ class ostream : public std::ostream
+ {
+ public:
+ ostream(const std::string &filename);
+ ~ostream();
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_BLITTABLE_HPP
+#define UNISON_VIDEO_BLITTABLE_HPP
+
+#include <unison/video/Coord.hpp>
+#include <unison/video/Rect.hpp>
+#include <unison/video/RenderOptions.hpp>
+
+namespace Unison
+{
+ namespace Video
+ {
+ class Surface;
+ class SurfaceSection;
+ class Texture;
+ class TextureSection;
+ class DisplayList;
+ class Blittable
+ {
+ public:
+ virtual ~Blittable();
+
+ /// Does a surface blit
+ /// \param[in] src The source surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the source surface to blit from
+ /// \param[in] options Extra blit options
+ virtual void blit(const Surface &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions()) = 0;
+
+ /// Does a texture blit
+ /// \param[in] src The source texture
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the source texture to blit from
+ /// \param[in] options Extra blit options
+ virtual void blit(const Texture &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions()) = 0;
+ /// Does a surface section blit
+ /// \param[in] section The section to blit
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] options Extra blit options
+ void blit_section(const SurfaceSection §ion, const Point &dst_pos = Point(), const RenderOptions &options = RenderOptions());
+
+ /// Does a texture section blit
+ /// \param[in] section The section to blit
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] options Extra blit options
+ void blit_section(const TextureSection §ion, const Point &dst_pos = Point(), const RenderOptions &options = RenderOptions());
+
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ virtual void fill(const Color &color, const Rect &rect = Rect()) = 0;
+
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ virtual void fill_blend(const Color &color, const Rect &rect = Rect()) = 0;
+
+ /// Draw the requests in the display list
+ /// \param[in] list The display list
+ void draw(const DisplayList &list);
+ };
+
+ /// A section of a blittable
+ class BlittableSection : public Blittable
+ {
+ public:
+ /// The image
+ Blittable ℑ
+
+ /// The clip rectangle
+ Rect clip_rect;
+
+ /// Create a section from an image and a rectangle
+ /// \param[in] image The image
+ /// \param[in] rect The clip rectangle
+ BlittableSection(Blittable &image, const Rect &clip_rect = Rect());
+
+ /// Does a clipped blit to the image
+ /// \param[in] src The source surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the blit source to blit from
+ /// \param[in] options Extra blit options
+ void blit(const Surface &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions());
+
+ /// Does a clipped blit to the image
+ /// \param[in] src The source texture
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the blit source to blit from
+ /// \param[in] options Extra blit options
+ void blit(const Texture &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions());
+
+ /// Fills the camera viewable portion with a color
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill(const Color &color, const Rect &rect = Rect());
+
+ /// Fills the camera viewable portion with a color using alpha blending
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill_blend(const Color &color, const Rect &rect = Rect());
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_BLITTERS_HPP
+#define UNISON_VIDEO_BLITTERS_HPP
+
+#include <unison/video/Coord.hpp>
+#include <unison/video/RenderOptions.hpp>
+
+namespace Unison
+{
+ namespace Video
+ {
+ class Rect;
+ class Surface;
+
+ struct Blitters
+ {
+ static void blit_upper(const Surface &src, Rect src_rect, Surface &dst, Point dst_pos, void (*blit_lower)(const Surface &, const Rect &, Surface &, const Point &));
+
+ static void blit_lower_none(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos);
+
+ static void blit_lower_mask(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos);
+
+ static void blit_lower_alpha(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos);
+
+ static void blit_lower_add(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos);
+
+ static void blit_lower_mod(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos);
+
+ static void blit_blend_none(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos)
+ {
+ blit_upper(src, src_rect, dst, dst_pos, blit_lower_none);
+ }
+
+ static void blit_blend_mask(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos)
+ {
+ blit_upper(src, src_rect, dst, dst_pos, blit_lower_mask);
+ }
+
+ static void blit_blend_alpha(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos)
+ {
+ blit_upper(src, src_rect, dst, dst_pos, blit_lower_alpha);
+ }
+
+ static void blit_blend_add(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos)
+ {
+ blit_upper(src, src_rect, dst, dst_pos, blit_lower_add);
+ }
+
+ static void blit_blend_mod(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos)
+ {
+ blit_upper(src, src_rect, dst, dst_pos, blit_lower_mod);
+ }
+
+ static void blit_blend(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos, BlendMode blend)
+ {
+ switch(blend)
+ {
+ case BLEND_NONE:
+ blit_blend_none(src, src_rect, dst, dst_pos);
+ break;
+ case BLEND_MASK:
+ blit_blend_mask(src, src_rect, dst, dst_pos);
+ break;
+ case BLEND_ALPHA:
+ blit_blend_alpha(src, src_rect, dst, dst_pos);
+ break;
+ case BLEND_ADD:
+ blit_blend_add(src, src_rect, dst, dst_pos);
+ break;
+ case BLEND_MOD:
+ blit_blend_mod(src, src_rect, dst, dst_pos);
+ break;
+ }
+ }
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_COLOR_HPP
+#define UNISON_VIDEO_COLOR_HPP
+
+//#include "SDL_stdinc.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ /// A RGBA color
+ class Color
+ {
+ public:
+ /// The red component (0x00 to 0xff)
+ unsigned char red;
+
+ /// The green component (0x00 to 0xff)
+ unsigned char green;
+
+ /// The blue component (0x00 to 0xff)
+ unsigned char blue;
+
+ /// The alpha component (0x00 to 0xff)
+ unsigned char alpha;
+
+ /// Default constructor (transparent black)
+ Color() :
+ red(),
+ green(),
+ blue(),
+ alpha()
+ {
+ }
+
+ /// Create a color from the given values
+ /// \param[in] red The red component (0x00 to 0xff)
+ /// \param[in] green The red component (0x00 to 0xff)
+ /// \param[in] blue The red component (0x00 to 0xff)
+ /// \param[in] alpha The red component (0x00 to 0xff)
+ Color(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha = 0xff) :
+ red(red),
+ green(green),
+ blue(blue),
+ alpha(alpha)
+ {
+ }
+
+ /// Equality operator
+ /// \param[in] rhs The color to test
+ /// \return Whether the colors are equal
+ bool operator == (const Color &rhs) const
+ {
+ return red == rhs.red && green == rhs.green && blue == rhs.blue && alpha == rhs.alpha;
+ }
+
+ /// Equality operator
+ /// \param[in] rhs The color to test
+ /// \return Whether the colors are not equal
+ bool operator != (const Color &rhs) const
+ {
+ return !(*this == rhs);
+ }
+
+ /// Less than operator
+ /// \param[in] rhs The color to test
+ /// \return Whether the color's grayscale value is less than the tested color
+ bool operator < (const Color &rhs) const
+ {
+ return grayscale() < rhs.grayscale();
+ }
+
+ /// Calculate the grayscale value of the color
+ /// \return The grayscale value (30% red, 59% green, 11% blue)
+ unsigned char grayscale() const
+ {
+ return (red * 30 + green * 59 + blue * 11) / 100;
+ }
+
+ /// Opaque black (red = 0x00, green = 0x00, blue = 0x00)
+ static const Color BLACK;
+
+ /// Opaque red (red = 0xff, green = 0x00, blue = 0x00)
+ static const Color RED;
+
+ /// Opaque green (red = 0x00, green = 0xff, blue = 0x00)
+ static const Color GREEN;
+
+ /// Opaque blue (red = 0x00, green = 0x00, blue = 0xff)
+ static const Color BLUE;
+
+ /// Opaque cyan (red = 0x00, green = 0xff, blue = 0xff)
+ static const Color CYAN;
+
+ /// Opaque magenta (red = 0xff, green = 0x00, blue = 0xff)
+ static const Color MAGENTA;
+
+ /// Opaque yellow (red = 0xff, green = 0xff, blue = 0x00)
+ static const Color YELLOW;
+
+ /// Opaque white (red = 0xff, green = 0xff, blue = 0xff)
+ static const Color WHITE;
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_COORD_HPP
+#define UNISON_VIDEO_COORD_HPP
+
+namespace Unison
+{
+ namespace Video
+ {
+ /// A 2 dimensional quantity in rectangular space
+ template<typename T> class Coord
+ {
+ public:
+ /// The horizontal value
+ T x;
+
+ /// The vertical value
+ T y;
+
+ /// Default constructor (0, 0)
+ Coord()
+ : x(),
+ y()
+ {
+ }
+
+ /// Create a coordinate from the given values
+ /// \param[in] x The horizontal value
+ /// \param[in] y The vertical value
+ Coord(T x, T y)
+ : x(x),
+ y(y)
+ {
+ }
+
+ /// Copy constructor
+ /// \param[in] rhs The source coordinate
+ template<typename V> Coord(const Coord<V> &rhs)
+ : x(rhs.x),
+ y(rhs.y)
+ {
+ }
+
+ /// Assignment operator
+ /// \param[in] rhs The source color
+ template<typename V> Coord<T> &operator =(const Coord<V> &rhs)
+ {
+ x = rhs.x;
+ y = rhs.y;
+ return *this;
+ }
+
+ /// Equality operator
+ /// \param[in] rhs The coordinate to test
+ /// \return Whether the coordinates are equal
+ template<typename V> bool operator ==(const Coord<V> &rhs) const
+ {
+ return x == rhs.x && y == rhs.y;
+ }
+
+ /// Equality operator
+ /// \param[in] rhs The coordinate to test
+ /// \return Whether the coordinates are not equal
+ template<typename V> bool operator !=(const Coord<V> &rhs) const
+ {
+ return !(*this == rhs);
+ }
+
+ /// Add two coordinates and assign to the left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V> Coord<T> &operator +=(const Coord<V> &rhs)
+ {
+ x += rhs.x;
+ y += rhs.y;
+ return *this;
+ }
+
+ /// Subtract two coordinates and assign to the left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V> Coord<T> &operator -=(const Coord<V> &rhs)
+ {
+ x -= rhs.x;
+ y -= rhs.y;
+ return *this;
+ }
+
+ /// Multiply two coordinates and assign to the left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V> Coord<T> &operator *=(const Coord<V> &rhs)
+ {
+ x *= rhs.x;
+ y *= rhs.y;
+ return *this;
+ }
+
+ /// Multiply two coordinates and assign to the left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V> Coord<T> &operator /=(const Coord<V> &rhs)
+ {
+ x /= rhs.x;
+ y /= rhs.y;
+ return *this;
+ }
+
+ /// Add a coordinate with a scalar and assign to the left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V> Coord<T> &operator +=(const V &rhs)
+ {
+ x += rhs;
+ y += rhs;
+ return *this;
+ }
+
+ /// Subtract a coordinate with a scalar and assign to the left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V> Coord<T> &operator -=(const V &rhs)
+ {
+ x -= rhs;
+ y -= rhs;
+ return *this;
+ }
+
+ /// Multiply a coordinate with a scalar and assign to the left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V> Coord<T> &operator *=(const V &rhs)
+ {
+ x *= rhs;
+ y *= rhs;
+ return *this;
+ }
+
+ /// Divide a coordinate with a scalar and assign to the left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V> Coord<T> &operator /=(const V &rhs)
+ {
+ x /= rhs;
+ y /= rhs;
+ return *this;
+ }
+
+ /*T length()
+ {
+ double sq = x*x + y*y;
+ return T(sqrt(sq) + 0.5);
+ }*/
+ };
+
+ /// Add two coordinates
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename T, typename V> const Coord<T> operator +(const Coord<T> &lhs, const Coord<V> &rhs)
+ {
+ return Coord<T>(lhs) += rhs;
+ }
+
+ /// Subtract two coordinates
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename T, typename V> const Coord<T> operator -(const Coord<T> &lhs, const Coord<V> &rhs)
+ {
+ return Coord<T>(lhs) -= rhs;
+ }
+
+ /// Multiply two coordinates
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename T, typename V> const Coord<T> operator *(const Coord<T> &lhs, const Coord<V> &rhs)
+ {
+ return Coord<T>(lhs) *= rhs;
+ }
+
+ /// Divide two coordinates
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename T, typename V> const Coord<T> operator /(const Coord<T> &lhs, const Coord<V> &rhs)
+ {
+ return Coord<T>(lhs) /= rhs;
+ }
+
+ /// Add a coordinate with a scalar
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename T, typename V> const Coord<T> operator +(const Coord<T> &lhs, const V &rhs)
+ {
+ return Coord<T>(lhs) += rhs;
+ }
+
+ /// Subtract a coordinate with a scalar
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename T, typename V> const Coord<T> operator -(const Coord<T> &lhs, const V &rhs)
+ {
+ return Coord<T>(lhs) -= rhs;
+ }
+
+ /// Multiply a coordinate with a scalar
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename T, typename V> const Coord<T> operator *(const Coord<T> &lhs, const V &rhs)
+ {
+ return Coord<T>(lhs) *= rhs;
+ }
+
+ /// Divide a coordinate with a scalar
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename T, typename V> const Coord<T> operator /(const Coord<T> &lhs, const V &rhs)
+ {
+ return Coord<T>(lhs) /= rhs;
+ }
+
+ /// Add a coordinate with a scalar
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V, typename T> const Coord<T> operator +(const V &lhs, const Coord<T> &rhs)
+ {
+ return Coord<T>(rhs) += lhs;
+ }
+
+ /// Subtract a coordinate with a scalar
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V, typename T> const Coord<T> operator -(const V &lhs, const Coord<T> &rhs)
+ {
+ return Coord<T>(rhs) -= lhs;
+ }
+
+ /// Multiply a coordinate with a scalar
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V, typename T> const Coord<T> operator *(const V &lhs, const Coord<T> &rhs)
+ {
+ return Coord<T>(rhs) *= lhs;
+ }
+
+ /// Divide a coordinate with a scalar
+ /// \param[in] lhs The left operand
+ /// \param[in] rhs The right operand
+ /// \return The resultant coordinate
+ template<typename V, typename T> const Coord<T> operator /(const V &lhs, const Coord<T> &rhs)
+ {
+ return Coord<T>(rhs) /= lhs;
+ }
+
+ /// A point in 2-dimensional space
+ typedef Coord<int> Point;
+
+ /// A 2-dimensional area
+ typedef Coord<unsigned int> Area;
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_DISPLAY_LIST_HPP
+#define UNISON_VIDEO_DISPLAY_LIST_HPP
+
+#include <unison/video/Blittable.hpp>
+#include <unison/video/RenderOptions.hpp>
+#include <unison/video/Surface.hpp>
+#include <unison/video/Texture.hpp>
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace Unison
+{
+ namespace Video
+ {
+ class DisplayList : public Blittable
+ {
+ public:
+ DisplayList() :
+ requests()
+ {
+ }
+
+ DisplayList(const std::map<int, DisplayList> &layers) :
+ requests(std::for_each(layers.begin(), layers.end(), Collator()).requests)
+ {
+ std::for_each(requests.begin(), requests.end(), std::mem_fun(&Unison::Video::DisplayList::Request::ref));
+ }
+
+ DisplayList(const DisplayList &rhs) :
+ Blittable(),
+ requests(rhs.requests)
+ {
+ std::for_each(requests.begin(), requests.end(), std::mem_fun(&Unison::Video::DisplayList::Request::ref));
+ }
+
+ ~DisplayList()
+ {
+ std::for_each(requests.begin(), requests.end(), std::mem_fun(&Unison::Video::DisplayList::Request::unref));
+ }
+
+ DisplayList &operator = (const DisplayList &rhs)
+ {
+ std::for_each(rhs.requests.begin(), rhs.requests.end(), std::mem_fun(&Unison::Video::DisplayList::Request::ref));
+ std::for_each(requests.begin(), requests.end(), std::mem_fun(&Unison::Video::DisplayList::Request::unref));
+ requests = rhs.requests;
+ return *this;
+ }
+
+ /// Add a request to do a surface-to-image blit
+ /// \param[in] src The source surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the source surface to blit from
+ /// \param[in] options Extra blit options
+ /// \param[in] layer The drawing layer to sort by
+ void blit(const Surface &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions())
+ {
+ add_request(new SurfaceBlitRequest(src, dst_pos, src_rect, options));
+ }
+
+ /// Add a request to do a texture-to-image blit
+ /// \param[in] src The source texture
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the source texture to blit from
+ /// \param[in] options Extra blit options
+ /// \param[in] layer The drawing layer to sort by
+ void blit(const Texture &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions())
+ {
+ add_request(new TextureBlitRequest(src, dst_pos, src_rect, options));
+ }
+
+ /// Add a request to fill a portion of the image
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ /// \param[in] layer The drawing layer to sort by
+ void fill(const Color &color, const Rect &rect = Rect())
+ {
+ add_request(new FillRequest(color, rect));
+ }
+
+ /// Add a request to blended fill a portion of the image
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ /// \param[in] layer The drawing layer to sort by
+ void fill_blend(const Color &color, const Rect &rect = Rect())
+ {
+ add_request(new BlendedFillRequest(color, rect));
+ }
+
+ /// Draw requests in display list onto blittable
+ /// \param[in] dst The destination blittable
+ void draw(Blittable *dst) const
+ {
+ std::for_each(requests.begin(), requests.end(), std::bind2nd(std::mem_fun(&Request::do_request), dst));
+ }
+
+ void clear()
+ {
+ requests.clear();
+ }
+
+ class Request
+ {
+ public:
+ Request() :
+ refcount(1)
+ {
+ }
+
+ virtual ~Request()
+ {
+ }
+
+ virtual void do_request(Blittable *dst) const = 0;
+
+ void ref()
+ {
+ refcount++;
+ }
+
+ void unref()
+ {
+ assert(refcount > 0);
+ refcount--;
+ if(refcount == 0)
+ {
+ delete this;
+ }
+ }
+ private:
+ int refcount;
+ };
+
+ void add_request(Request *request)
+ {
+ requests.push_back(request);
+ }
+ private:
+ class Collator
+ {
+ public:
+ void operator () (std::pair<int, DisplayList> pair)
+ {
+ requests.insert(requests.end(), pair.second.requests.begin(), pair.second.requests.end());
+ }
+ std::vector<Request *> requests;
+ };
+
+ class SurfaceBlitRequest : public Request
+ {
+ public:
+ SurfaceBlitRequest(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ : src(src),
+ dst_pos(dst_pos),
+ src_rect(src_rect),
+ options(options)
+ {
+ }
+
+ void do_request(Blittable *dst) const
+ {
+ dst->blit(src, dst_pos, src_rect, options);
+ }
+ private:
+ Surface src;
+ Point dst_pos;
+ Rect src_rect;
+ RenderOptions options;
+ };
+
+ class TextureBlitRequest : public Request
+ {
+ public:
+ TextureBlitRequest(const Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ : src(src),
+ dst_pos(dst_pos),
+ src_rect(src_rect),
+ options(options)
+ {
+ }
+
+ void do_request(Blittable *dst) const
+ {
+ dst->blit(src, dst_pos, src_rect, options);
+ }
+ private:
+ Texture src;
+ Point dst_pos;
+ Rect src_rect;
+ RenderOptions options;
+ };
+
+ class FillRequest : public Request
+ {
+ public:
+ FillRequest(const Color &color, const Rect &rect)
+ : color(color),
+ rect(rect)
+ {
+ }
+
+ void do_request(Blittable *dst) const
+ {
+ dst->fill(color, rect);
+ }
+ private:
+ Color color;
+ Rect rect;
+ };
+
+ class BlendedFillRequest : public Request
+ {
+ public:
+ BlendedFillRequest(const Color &color, const Rect &rect)
+ : color(color),
+ rect(rect)
+ {
+ }
+
+ void do_request(Blittable *dst) const
+ {
+ dst->fill_blend(color, rect);
+ }
+ private:
+ Color color;
+ Rect rect;
+ };
+
+ std::vector<Request *> requests;
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_RECT_HPP
+#define UNISON_VIDEO_RECT_HPP
+
+#include <algorithm>
+
+#include <unison/video/Coord.hpp>
+
+#include "SDL.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ /// Represents a rectangular area
+ class Rect
+ {
+ public:
+ /// The position of the rectangle
+ Point pos;
+
+ /// The size of the rectangle
+ Area size;
+
+ /// Default constructor
+ Rect() :
+ pos(),
+ size(),
+ rect()
+ {
+ }
+
+ /// Create a rectangle from the given coordinates
+ /// \param[in] pos The position of the rectangle
+ /// \param[in] size The size of the rectangle
+ Rect(const Point &pos, const Area &size) :
+ pos(pos),
+ size(size),
+ rect()
+ {
+ }
+
+ /// Create a rectangle from the given values
+ /// \param[in] x The x-position of the rectangle
+ /// \param[in] y The y-position of the rectangle
+ /// \param[in] w The width of the rectangle
+ /// \param[in] h The height of the rectangle
+ Rect(int x, int y, int w, int h) :
+ pos(x, y),
+ size(w, h),
+ rect()
+ {
+ }
+
+ /// Equality operator
+ /// \param[in] rhs The rectangle to test
+ /// \return Whether the rectangles are equal
+ bool operator == (const Rect &rhs) const
+ {
+ return pos == rhs.pos && size == rhs.size;
+ }
+
+ /// Equality operator
+ /// \param[in] rhs The rectangle to test
+ /// \return Whether the rectangles are not equal
+ bool operator != (const Rect &rhs) const
+ {
+ return !(*this == rhs);
+ }
+
+ /// Get the left edge of the rectnagle
+ /// \return The location of the left edge
+ int get_left() const
+ {
+ return pos.x;
+ }
+
+ /// Get the top edge of the rectnagle
+ /// \return The location of the top edge
+ int get_top() const
+ {
+ return pos.y;
+ }
+
+ /// Get the right edge of the rectnagle
+ /// \return The location of the right edge
+ int get_right() const
+ {
+ return pos.x + size.x;
+ }
+
+ /// Get the bottom edge of the rectnagle
+ /// \return The location of the bottom edge
+ int get_bottom() const
+ {
+ return pos.y + size.y;
+ }
+
+ /// Calculate the overlap between the rectangles
+ /// \param[in] rhs The rectangle to check
+ /// \return The part of the rectangle that is overlapping
+ Rect get_overlap(const Rect &rhs)
+ {
+ if(*this == Rect())
+ {
+ return rhs;
+ }
+ if(rhs == Rect())
+ {
+ return *this;
+ }
+ Rect overlap;
+ if(get_left() < rhs.get_right())
+ {
+ overlap.pos.x = std::max(get_left(), rhs.get_left());
+ }
+ else
+ {
+ return Rect();
+ }
+ if(rhs.get_left() < get_right())
+ {
+ overlap.size.x = std::min(rhs.get_right(), get_right()) - overlap.pos.x;
+ }
+ else
+ {
+ return Rect();
+ }
+ if(get_top() < rhs.get_bottom())
+ {
+ overlap.pos.y = std::max(get_top(), rhs.get_top());
+ }
+ else
+ {
+ return Rect();
+ }
+ if(rhs.get_top() < get_bottom())
+ {
+ overlap.size.y = std::min(rhs.get_bottom(), get_bottom()) - overlap.pos.y;
+ }
+ else
+ {
+ return Rect();
+ }
+ return overlap;
+ }
+
+ /// Allow rectangles to be treated like SDL_Rect
+ /// \return The equavalent SDL_Rect
+ operator SDL_Rect () const
+ {
+ rect.x = pos.x;
+ rect.y = pos.y;
+ rect.w = size.x;
+ rect.h = size.y;
+ return rect;
+ }
+
+ /// Allow rectangles to be treated like SDL_Rect
+ /// \return The internal SDL_Rect
+ SDL_Rect *operator &() const
+ {
+ if(*this == Rect())
+ {
+ return 0;
+ }
+ else
+ {
+ rect.x = pos.x;
+ rect.y = pos.y;
+ rect.w = size.x;
+ rect.h = size.y;
+ return ▭
+ }
+ }
+ private:
+ mutable SDL_Rect rect;
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_RENDER_OPTIONS_HPP
+#define UNISON_VIDEO_RENDER_OPTIONS_HPP
+
+#include <unison/video/Color.hpp>
+
+namespace Unison
+{
+ namespace Video
+ {
+ /// The color blending modes
+ enum BlendMode
+ {
+ BLEND_NONE,
+ BLEND_MASK,
+ BLEND_ALPHA,
+ BLEND_ADD,
+ BLEND_MOD
+ };
+
+ /// Extra rendering options
+ class RenderOptions
+ {
+ public:
+ /// The additional color value used (alpha is ignored)
+ Color color;
+
+ /// The additional alpha value used
+ unsigned char alpha;
+
+ /// The blend mode used
+ BlendMode blend;
+
+ /// Flip rendering horizontally
+ bool h_flip;
+
+ /// Flip rendering vertically
+ bool v_flip;
+
+ /// Default constructor
+ RenderOptions() :
+ color(Color::WHITE),
+ alpha(0xff),
+ blend(BLEND_ALPHA),
+ h_flip(false),
+ v_flip(false)
+ {
+ }
+
+ /// Create a set of render options with the given data
+ /// \param[in] color The color modulation (alpha ignored)
+ RenderOptions(const Color &color) :
+ color(color),
+ alpha(0xff),
+ blend(BLEND_ALPHA),
+ h_flip(false),
+ v_flip(false)
+ {
+ }
+
+ /// Create a set of render options with the given data
+ /// \param[in] blend The blend mode
+ RenderOptions(BlendMode blend) :
+ color(Color::WHITE),
+ alpha(0xff),
+ blend(blend),
+ h_flip(false),
+ v_flip(false)
+ {
+ }
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_RENDERERS_HPP
+#define UNISON_VIDEO_RENDERERS_HPP
+
+#include <string>
+#include <vector>
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace Backend
+ {
+ class Renderer;
+ }
+ /// Manages renderers
+ class Renderers
+ {
+ public:
+ /// Initialize and retrieve singleton
+ static Renderers &get();
+
+ /// Set the backend renderer to use
+ /// \param[in] name The name of a renderer backend (can be "auto")
+ void set_renderer(const std::string &name);
+
+ /// Get the current backend renderer
+ /// \return The current backend renderer
+ Backend::Renderer &get_renderer();
+
+ /// Add a backend renderer
+ /// \param[in] renderer The backend renderer to add
+ void add_renderer(Backend::Renderer *renderer);
+ private:
+ /// The auto renderer backend
+ Backend::Renderer *auto_renderer;
+
+ /// The current renderer backend
+ Backend::Renderer *renderer;
+
+ /// The known backend renderers
+ std::vector<Backend::Renderer *> renderers;
+
+ /// Default constructor
+ Renderers();
+
+ /// Destructor
+ ~Renderers();
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_SURFACE_HPP
+#define UNISON_VIDEO_SURFACE_HPP
+
+#include <unison/video/Blittable.hpp>
+#include <unison/video/RenderOptions.hpp>
+
+#include <string>
+#include <istream>
+#include <assert.h>
+
+namespace Unison
+{
+ namespace Video
+ {
+ class Color;
+ class Rect;
+ class Texture;
+ /// An image that is optimized for easy manipulation
+ class Surface : public Blittable
+ {
+ public:
+ /// Default constructor
+ Surface();
+
+ /// Create a Surface from an input stream
+ /// \param[in] src The input stream
+ Surface(std::istream &stream);
+
+ /// Create a Surface from an input stream
+ /// \param[in] src The input stream
+ /// \param[in] colorkey The colorkey used by the file
+ Surface(std::istream &stream, const Color &colorkey);
+
+ /// Opens image file indicated by filename
+ /// \param[in] filename The filename of the image file
+ Surface(const std::string &filename);
+
+ /// Opens image file indicated by filename and use the specified color key
+ /// \param[in] filename The filename of the image file
+ /// \param[in] colorkey The colorkey used by the file
+ Surface(const std::string &filename, const Color &colorkey);
+
+ /// Creates Surface of indicated dimensions
+ /// \param[in] size The size of the desired surface
+ Surface(const Area &size);
+
+ /// Copy constructor
+ /// \param[in] rhs The source surface
+ Surface(const Surface &rhs);
+
+ /// Destructor
+ ~Surface();
+
+ /// Assignment operator
+ /// \param[in] rhs The source surface
+ Surface &operator =(const Surface &rhs);
+
+ /// Saves the surface to file
+ /// \param[in] filename The destination filename
+ void save(const std::string &filename) const;
+
+ /// Retrieves the window's size
+ /// \return The size of the surface
+ Area get_size() const
+ {
+ return pixels ? pixels->size : Area();
+ }
+
+ /// Retrieves a pixel at the specified coordinates
+ /// \param[in] pos The position of the pixel to retrieve
+ /// \return The pixel at the specified position
+ Color &get_pixel(const Point &pos)
+ {
+ cow();
+ assert(pixels);
+ return pixels->buffer[pos.y * pixels->size.x + pos.x];
+ }
+
+ /// Retrieves the pixel color at the specified coordinates
+ /// \param[in] x The x position of the pixel to retrieve
+ /// \param[in] y The y position of the pixel to retrieve
+ /// \return The color of the pixel at the specified position
+ Color& get_pixel(int x, int y)
+ {
+ cow();
+ assert(pixels);
+ return pixels->buffer[y * pixels->size.x + x];
+ }
+
+ /// Retrieves the pixel color at the specified coordinates
+ /// \param[in] pos The position of the pixel to retrieve
+ /// \return The color of the pixel at the specified position
+ Color get_pixel(const Point &pos) const
+ {
+ assert(pixels);
+ return pixels->buffer[pos.y * pixels->size.x + pos.x];
+ }
+
+ /// Retrieves the pixel color at the specified coordinates
+ /// \param[in] x The x position of the pixel to retrieve
+ /// \param[in] y The y position of the pixel to retrieve
+ /// \return The color of the pixel at the specified position
+ Color get_pixel(int x, int y) const
+ {
+ assert(pixels);
+ return pixels->buffer[y * pixels->size.x + x];
+ }
+
+ /// Acquire the pixel color buffer
+ /// \return the pixel color buffer
+ Color *get_pixels()
+ {
+ cow();
+ assert(pixels);
+ return pixels->buffer;
+ }
+
+ /// Acquire the pixel color buffer
+ /// \return the pixel color buffer
+ const Color *get_pixels() const
+ {
+ assert(pixels);
+ return pixels->buffer;
+ }
+
+ /// Does a surface-to-surface blit
+ /// \param[in] src The source surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the source surface to blit from
+ /// \param[in] options Extra blit options
+ void blit(const Surface &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions());
+
+ /// Does a texture-to-surface blit
+ /// \param[in] src The source texture
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the source texture to blit from
+ /// \param[in] options Extra blit options
+ void blit(const Texture &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions());
+
+ /// Fills a portion of the image with the given color
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill(const Color &color, const Rect &rect = Rect());
+
+ /// Fills and alpha blend a portion of the image with the given color
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill_blend(const Color &color, const Rect &rect = Rect());
+
+ /// Scale the surface by a factor of (numerator / denominator)
+ /// \param[in] numerator The numerator of the scale factor
+ /// \param[in] denominator The denominator of the scale factor
+ /// \return The scaled surface
+ Surface scale(unsigned int numerator, unsigned int denominator) const;
+
+ /// Flip the surface horizontally
+ /// \return The flipped surface
+ Surface h_flip() const;
+
+ /// Flip the surface vertically
+ /// \return The flipped surface
+ Surface v_flip() const;
+
+ /// Modulate the image with a color
+ /// \return The modulated surface
+ Surface modulate(const Color &color) const;
+
+ /// Modulate the image with an alpha
+ /// \return The modulated surface
+ Surface modulate(unsigned char alpha) const;
+ private:
+ /// Copy on Write
+ void cow();
+
+ class PixelBuffer
+ {
+ public:
+ PixelBuffer(Area size)
+ : buffer(0),
+ size(size),
+ refcount(1)
+ {
+ if(size != Area())
+ {
+ buffer = new Color[size.x * size.y];
+ }
+ }
+
+ ~PixelBuffer()
+ {
+ delete buffer;
+ }
+
+ void ref()
+ {
+ refcount++;
+ }
+
+ void unref()
+ {
+ assert(refcount > 0);
+ refcount--;
+ if(refcount == 0)
+ {
+ delete this;
+ }
+ }
+
+ Color *buffer;
+ Area size;
+ int refcount;
+ };
+
+ /// The pixels
+ PixelBuffer *pixels;
+ };
+
+ /// A section of a surface
+ class SurfaceSection
+ {
+ public:
+ /// The image
+ Surface image;
+
+ /// The clip rectangle
+ Rect clip_rect;
+
+ /// Create a section from an image and a rectangle
+ /// \param[in] image The image
+ /// \param[in] rect The clip rectangle
+ SurfaceSection(const Surface &image = Surface(), const Rect &clip_rect = Rect()) :
+ image(image),
+ clip_rect(clip_rect)
+ {
+ }
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_TEXTURE_HPP
+#define UNISON_VIDEO_TEXTURE_HPP
+
+#include <unison/video/Blittable.hpp>
+#include <unison/video/Surface.hpp>
+#include <unison/video/Coord.hpp>
+
+#include <string>
+#include <vector>
+#include <set>
+
+namespace Unison
+{
+ namespace Video
+ {
+ typedef unsigned int TextureID;
+ static const TextureID INVALID_TEXTURE_ID = ~0;
+ /// An image that is optimized for fast drawing
+ class Texture : public Blittable
+ {
+ public:
+ /// Default constructor
+ Texture();
+
+ /// Opens image file indicated by filename
+ /// \param[in] filename The filename of the image file
+ Texture(const std::string &filename);
+
+ /// Opens image file indicated by filename and use the specified colorkey
+ /// \param[in] filename The filename of the image file
+ /// \param[in] colorkey The colorkey used by the file
+ Texture(const std::string &filename, const Color &colorkey);
+
+ /// Create a texture from the given surface
+ /// \param[in] surface The surface to optimize
+ Texture(const Surface &surface);
+
+ /// Copy constructor
+ /// \param[in] rhs The source texture
+ Texture(const Texture &rhs);
+
+ /// Destructor
+ ~Texture();
+
+ /// Assignment operator
+ /// \param[in] rhs The source surface
+ Texture &operator =(const Texture &rhs);
+
+ /// Retrieves the texture's id
+ /// \return The id of the surface
+ TextureID get_id() const;
+
+ /// Retrieves the texture's size
+ /// \return The size of the surface
+ Area get_size() const;
+
+ /// Does a surface-to-texture blit
+ /// \param[in] src The source surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the source surface to blit from
+ /// \param[in] options Extra blit options
+ void blit(const Surface &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions());
+
+ /// Does a texture-to-texture blit
+ /// \param[in] src The source texture
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the source texture to blit from
+ /// \param[in] options Extra blit options
+ void blit(const Texture &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions());
+
+ /// Fills a portion of the image with the given color
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill(const Color &color, const Rect &rect = Rect());
+
+ /// Fills and alpha blend a portion of the image with the given color
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill_blend(const Color &color, const Rect &rect = Rect());
+
+ static std::vector<Surface> save_textures();
+ static void load_textures(const std::vector<Surface> &surfaces);
+
+ /// Recover previously used but now unused texture IDs
+ static void recover_texture_ids();
+ private:
+ /// Copy on Write
+ void cow();
+
+ /// The texture ID
+ TextureID id;
+
+ /// All the textures in existence
+ static std::set<Texture *> textures;
+ };
+
+ /// A section of a texture
+ class TextureSection
+ {
+ public:
+ /// The image
+ Texture image;
+
+ /// The clip rectangle
+ Rect clip_rect;
+
+ /// Create a section from an image and a rectangle
+ /// \param[in] image The image
+ /// \param[in] rect The clip rectangle
+ TextureSection(const Texture &image = Texture(), const Rect &clip_rect = Rect()) :
+ image(image),
+ clip_rect(clip_rect)
+ {
+ }
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_WINDOW_HPP
+#define UNISON_VIDEO_WINDOW_HPP
+
+#include <unison/video/Blittable.hpp>
+#include <unison/video/DisplayList.hpp>
+#include <unison/video/RenderOptions.hpp>
+#include <unison/video/Surface.hpp>
+#include <unison/video/Texture.hpp>
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace Unison
+{
+ namespace Video
+ {
+ class Color;
+ class Rect;
+ class Texture;
+ class Surface;
+ namespace Backend
+ {
+ class Window;
+ }
+ /// Window management singleton
+ class Window : public Blittable
+ {
+ public:
+ /// Initialize and retrieve singleton
+ static Window &get();
+
+ /// Set the logical size of the window
+ /// \param[in] logical_size The logical size of the window
+ void set_logical_size(const Area &logical_size);
+
+ /// Get the logical size of the window
+ /// \return The logical size of the window
+ Area get_logical_size() const;
+
+ /// Open the window
+ /// \param[in] size The size of the window
+ /// \param[in] fullscreen Whether to open in fullscreen mode
+ void open(const Area &size, bool fullscreen = false);
+
+ /// Take a screenshot of the window
+ /// \param[in] filename The destination filename
+ void take_screenshot(const std::string &filename) const;
+
+ /// Flip request buffers
+ /// \note Should be called only once per frame!
+ void flip();
+
+ /// Redraw requests
+ void redraw();
+
+ /// Set window title
+ void set_title(const std::string &title);
+
+ /// Set window icon
+ void set_icon(const Surface &icon);
+
+ /// Retrieves the window's size
+ /// \return The size of the window
+ Area get_size() const;
+
+ /// Queries whether the window is open
+ /// \return Whether the window is open
+ bool is_open() const;
+
+ /// Queries whether the window is in fullscreen mode
+ /// \return Whether the window is fullscreen
+ bool is_fullscreen() const;
+
+ /// Does a surface-to-window blit
+ /// \param[in] src The source surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the source surface to blit from
+ /// \param[in] options Extra blit options
+ void blit(const Surface &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions())
+ {
+ layers[0].blit(src, dst_pos, src_rect, options);
+ }
+
+ /// Does a texture-to-window blit
+ /// \param[in] src The source texture
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] src_rect The part of the source texture to blit from
+ /// \param[in] options Extra blit options
+ void blit(const Texture &src, const Point &dst_pos = Point(), const Rect &src_rect = Rect(), const RenderOptions &options = RenderOptions())
+ {
+ layers[0].blit(src, dst_pos, src_rect, options);
+ }
+
+ /// Fills a portion of the window with the given color
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill(const Color &color, const Rect &rect = Rect())
+ {
+ layers[0].fill(color, rect);
+ }
+
+ /// Fills and alpha blend a portion of the window with the given color
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill_blend(const Color &color, const Rect &rect = Rect())
+ {
+ layers[0].fill_blend(color, rect);
+ }
+
+ DisplayList &operator [] (int layer)
+ {
+ return layers[layer];
+ }
+ private:
+ /// The logical size of the window
+ Area logical_size;
+
+ /// The title of the window
+ std::string title;
+
+ /// The window icon
+ Surface icon;
+
+ /// The window
+ Backend::Window *window;
+
+ /// Display list currently being drawn
+ DisplayList list;
+
+ /// Layers of pending display lists
+ std::map<int, DisplayList> layers;
+
+ /// Default constructor
+ Window();
+
+ /// Destructor
+ ~Window();
+ };
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_BACKEND_RENDERER_HPP
+#define UNISON_VIDEO_BACKEND_RENDERER_HPP
+
+#include <unison/video/Coord.hpp>
+
+#include <string>
+#include <istream>
+
+namespace Unison
+{
+ namespace Video
+ {
+ class Surface;
+ class Texture;
+ class Window;
+ class Color;
+ class Rect;
+ class RenderOptions;
+
+ namespace Backend
+ {
+ class Texture;
+ class Window;
+ /// Backend-specific renderer interface
+ class Renderer
+ {
+ public:
+ /// Destructor
+ virtual ~Renderer()
+ {
+ }
+
+ /// Initialize the backend
+ virtual void init() = 0;
+
+ /// Cleanup the backend
+ virtual void quit() = 0;
+
+ /// Get the name of the renderer
+ /// \return the name of the renderer
+ virtual std::string get_name() = 0;
+
+ /// Check if the backend is usable
+ /// \return Whether the backend is usable
+ virtual bool is_usable() = 0;
+
+ virtual Surface load_surface(const std::string &filename) = 0;
+ virtual Surface load_surface(const std::string &filename, const Color &colorkey) = 0;
+
+ virtual void save_surface(const Surface &surface, const std::string &filename) = 0;
+
+ /// Does a surface-to-surface blit
+ /// \param[in] src The source surface
+ /// \param[in] src_rect The part of the source surface to blit from
+ /// \param[in] dst The destination surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] options Extra blit options
+ virtual void blit(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options) = 0;
+
+ /// Does a texture-to-surface blit
+ /// \param[in] src The source texture
+ /// \param[in] src_rect The part of the source texture to blit from
+ /// \param[in] dst The destination surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] options Extra blit options
+ virtual void blit(Texture *src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options) = 0;
+
+ /// Fills a portion of a surface with the given color
+ /// \param[in] dst The destination surface
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ virtual void fill(Surface &dst, const Color &color, const Rect &rect) = 0;
+
+ /// Fills with alpha blend a portion of a surface with the given color
+ /// \param[in] dst The destination surface
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ virtual void fill_blend(Surface &dst, const Color &color, const Rect &rect) = 0;
+
+ /// Create a window
+ /// \param[in] size The size of the window
+ /// \param[in] logical_size The logical size of the window
+ /// \param[in] fullscreen Whether to open in fullscreen mode
+ /// \return The created window
+ virtual Window *create_window(const Area &size, const Area &logical_size, bool fullscreen) = 0;
+
+ /// Create a texture for the given surface
+ /// \param[in] surface The surface to convert
+ /// \param[in] name The name of the texture
+ /// \return The texture for the surface
+ virtual Texture *create_texture(const Surface &surface) = 0;
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_BACKEND_TEXTURE_HPP
+#define UNISON_VIDEO_BACKEND_TEXTURE_HPP
+
+#include <unison/video/Blittable.hpp>
+#include <unison/video/Surface.hpp>
+#include <unison/video/Texture.hpp>
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace Backend
+ {
+ class Renderer;
+ /// Backend-specific texture interface
+ class Texture : public Blittable
+ {
+ public:
+ /// Destructor
+ virtual ~Texture();
+
+ /// Get the size of the texture
+ /// \return The texture size
+ Area get_size();
+
+ /// Called when referenced
+ void ref();
+
+ /// Called when a reference goes away
+ void unref();
+
+ /// Get the number of references to the texture
+ /// \return The reference count
+ int get_refcount();
+
+ /// Get the equavalent surface to the texture
+ virtual const Surface get_surface() = 0;
+
+ /// Save the texture, called when the window is about to be created or recreated
+ virtual void save() = 0;
+
+ /// Save all the textures
+ static std::vector<Surface> save_textures();
+
+ /// Load the textures
+ static void load_textures(const std::vector<Surface> &surfaces);
+
+ /// Recover previously used but now unused texture IDs
+ /// \return A map of what IDs changed during recovery
+ static std::map<TextureID, TextureID> recover_texture_ids();
+
+ /// Retrieve the texture ID for the filename
+ /// \param[in] filename The filename of the image file
+ /// \return The texture ID of the texture
+ static TextureID get_texture_id(const std::string &filename);
+
+ /// Retrieve the texture ID for the filename
+ /// \param[in] filename The filename of the image file
+ /// \param[in] colorkey The colorkey used by the file
+ /// \return The texture ID of the texture
+ static TextureID get_texture_id(const std::string &filename, const Color &colorkey);
+
+ /// Retrieve the texture ID for the surface
+ /// \param[in] surface The surface to optimize
+ /// \return The texture ID of the texture
+ static TextureID get_texture_id(const Surface &surface);
+
+ /// Retrieve the texture ID for the texture
+ /// \param[in] texture The texture
+ /// \return The texture ID of the texture
+ static TextureID get_texture_id(Texture *texture);
+
+ /// Retrieve the texture corresponding to the texture ID
+ /// \param[in] id The texture ID
+ /// \return The corresponding texture
+ static Texture *get_texture(TextureID id);
+
+ /// Retrieve the name associated with the texture ID
+ /// \param[in] texture The texture
+ /// \return The texture ID of the texture
+ static std::string get_name(TextureID id);
+ protected:
+ /// Create a texture from the given surface with the given name
+ /// \param[in] surface The surface to optimize
+ Texture(const Surface &surface);
+
+ /// The surface the texture is based from
+ Surface surface;
+
+ /// The size of the texture
+ Area size;
+
+ /// The number of references to the texture
+ int refcount;
+
+ /// All of the textures in existence
+ static std::vector<Texture *> textures;
+
+ /// The subset of all textures that have names
+ static std::map<std::string, TextureID> named_textures;
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_BACKEND_WINDOW_HPP
+#define UNISON_VIDEO_BACKEND_WINDOW_HPP
+
+#include <unison/video/Blittable.hpp>
+#include <unison/video/RenderOptions.hpp>
+#include <unison/video/Coord.hpp>
+#include <unison/video/Rect.hpp>
+
+#include <string>
+#include <vector>
+
+namespace Unison
+{
+ namespace Video
+ {
+ class Color;
+ class Rect;
+ class Texture;
+ class Surface;
+ namespace Backend
+ {
+ /// Backend-specific window interface
+ class Window : public Blittable
+ {
+ public:
+ /// Destructor
+ virtual ~Window()
+ {
+ }
+
+ /// Take a screenshot of the window
+ /// \param[in] filename The destination filename
+ virtual void take_screenshot(const std::string &filename) const = 0;
+
+ /// Flip request buffers
+ /// \note Should be called only once per frame!
+ virtual void flip() = 0;
+
+ /// Set window title
+ virtual void set_title(const std::string &title) = 0;
+
+ /// Set window icon
+ virtual void set_icon(const Surface &icon) = 0;
+
+ /// Retrieves the window's size
+ /// \return The size of the window
+ virtual Area get_size() const = 0;
+
+ /// Queries whether the window is in fullscreen mode
+ /// \return Whether the window is fullscreen
+ virtual bool is_fullscreen() const = 0;
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_SDL_BLITTERS_HPP
+#define UNISON_VIDEO_SDL_BLITTERS_HPP
+
+#include <unison/video/Blitters.hpp>
+#include <unison/video/Coord.hpp>
+#include <unison/video/Surface.hpp>
+
+#include "SDL.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace SDL
+ {
+ struct Blitters
+ {
+ static SDL_Surface *create_sdl_surface_from(Surface &src)
+ {
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(src.get_pixels(), src.get_size().x, src.get_size().y, 32, src.get_size().x * 4, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+#else
+ SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(src.get_pixels().get_pixels(), src.get_size().x, src.get_size().y, 32, src.get_size().x * 4, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
+#endif
+ return surface;
+ }
+
+ static SDL_Surface *create_sdl_surface_from(const Surface &src)
+ {
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(const_cast<Color *>(src.get_pixels()), src.get_size().x, src.get_size().y, 32, src.get_size().x * 4, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+#else
+ SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(const_cast<Color *>(src.get_pixels()).get_pixels(), src.get_size().x, src.get_size().y, 32, src.get_size().x * 4, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
+#endif
+ return surface;
+ }
+
+ static SDL_Surface *optimize(const Surface &src);
+
+ static void blit_upper(SDL_Surface *src, Rect src_rect, SDL_Surface *dst, Point dst_pos, void (*blit_lower)(SDL_Surface *, const Rect &, SDL_Surface *, const Point &));
+
+ static void blit_lower_none(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos);
+
+ static void blit_lower_mask(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos);
+
+ static void blit_lower_alpha(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos);
+
+ static void blit_lower_add(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos);
+
+ static void blit_lower_mod(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos);
+
+ static void blit_blend_none(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos)
+ {
+ blit_upper(src, src_rect, dst, dst_pos, blit_lower_none);
+ }
+
+ static void blit_blend_mask(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos)
+ {
+ blit_upper(src, src_rect, dst, dst_pos, blit_lower_mask);
+ }
+
+ static void blit_blend_alpha(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos)
+ {
+ blit_upper(src, src_rect, dst, dst_pos, blit_lower_alpha);
+ }
+
+ static void blit_blend_add(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos)
+ {
+ blit_upper(src, src_rect, dst, dst_pos, blit_lower_add);
+ }
+
+ static void blit_blend_mod(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos)
+ {
+ blit_upper(src, src_rect, dst, dst_pos, blit_lower_mod);
+ }
+
+ static void blit_blend(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos, BlendMode blend)
+ {
+ switch(blend)
+ {
+ case BLEND_NONE:
+ blit_blend_none(src, src_rect, dst, dst_pos);
+ break;
+ case BLEND_MASK:
+ blit_blend_mask(src, src_rect, dst, dst_pos);
+ break;
+ case BLEND_ALPHA:
+ blit_blend_alpha(src, src_rect, dst, dst_pos);
+ break;
+ case BLEND_ADD:
+ blit_blend_add(src, src_rect, dst, dst_pos);
+ break;
+ case BLEND_MOD:
+ blit_blend_mod(src, src_rect, dst, dst_pos);
+ break;
+ }
+ }
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+/*
+ * CHANGELOG.
+ */
+
+04032007 - Added a "make dist" target for packing up source code releases.
+ Reverted Unix recursive mutex code. There were some portability
+ issues I didn't anticipate. Upped version to 1.1.1!
+04022007 - Added wxWidgets-based test program (incomplete). Filled in and
+ corrected some Doxygen comments.
+04012007 - Added PHYSFS_isInit() and PHYSFS_symbolicLinksPermitted() functions.
+03312007 - Added a quick'n'dirty unpack utility to the extras directory. Moved
+ DIR archiver to start of the list, so we don't have to have every
+ other archiver fail to open a directory as a file before mounting
+ it. Fixed typos in makeos2.cmd and the Doxygen comments. Added
+ symlink support to windows.c for use on Vista-based systems.
+03282007 - Logic bug in MVL/HOG/GRP archivers: only enumerated files when
+ looking in a directory other than the root, instead of enumerating
+ only for the root (thanks, Chris!). Minor fix for compilers that
+ don't like the BAIL_* macros with an empty argument
+ (thanks, Chris!)
+03262007 - Tons of Unicode work in windows.c ... should now use UCS-2 on
+ NT/XP/Vista/etc versions of the OS, and fallback to "ANSI" versions
+ for 95/98/ME, tapdancing around the system codepage if it has to.
+ Since the Unicode entry points are dynamically loaded, it won't
+ have issues with missing symbols on Win9x, nor does it need to be
+ built separately with #define UNICODE (although it will work the
+ same with or without this define, as it doesn't use TCHARs or
+ the non-[WA] versions of APIs. Other minor Windows cleanups and
+ corrections.
+03252007 - Improved dynamic loader and initial Unicode work in windows.c ...
+03242007 - Replaced BeOS semaphores with BLockers for the mutex implementation.
+ It's much simpler, it has "benaphores" built in behind the scenes
+ for faster performance, and it's recursive...also, we were
+ previously setting the PhysicsFS error state if BeOS mutex grabbing
+ failed (a big no no!), and that's now fixed. Good wins all around.
+03222007 - Replaced some Malloc and all the alloca() calls with
+ __PHYSFS_smallAlloc(), which will stack allocate small (128 or
+ less bytes) blocks and Malloc the rest...naturally these now have
+ to be paired with __PHYSFS_smallFree() calls, so you can't be as
+ lazy as a basic alloca() would let you be. The benefit is both less
+ malloc pressure for those temporary allocations and better stack
+ overflow safety (so if some jerk tries to push a 78 megabyte string
+ through the library as a filename, we won't try to strcpy it to
+ the stack). Hopefully some internal interfaces can now get
+ refactored to stop generating heap pointers and let the caller use
+ smallAlloc to further reduce malloc pressure.
+03212007 - Replaced LONGLONGLITERAL with __PHYSFS_UI64/__PHYSFS_SI64 ...
+03202007 - Removed platform/skeleton.c (it was out of date), added
+ platform/macosx.c (To further Macify the code and get the #ifdefs
+ out of unix.c), and refactored the platform layer to try and
+ make the unix/posix/macosx/beos sources try to find a split that
+ works. Moved the platform allocators to physfs.c, since all but
+ Mac OS X were using malloc()...there's now an interface for the
+ platform to supply a custom allocator if they don't want the malloc
+ version. Removed __PHYSFS_platformTimeslice(), as it's no longer
+ being used. Replaced manual management of pthread mutexes with
+ PTHREAD_MUTEX_RECURSIVE attribute...let's see what platforms
+ throw up on that. Handled documentation comment FIXME in physfs.h.
+03192007 - Fixed two switched strings in CMakeLists.txt ... patch to compile
+ with latest Windows Platform SDK. Explicitly check for NULL in
+ PHYSFS_init() when we can't go on without a real string here.
+ Removed ANSI-C workaround for missing lstat() nonsense in posix.c
+ (POSIX != ANSI, time to give up here). Try to use /proc/self/exe
+ to find the base dir on Unix, so we can do without argv[0] on
+ systems with a Linux-like /proc filesystem.
+03162007 - Changed PHYSFS_file from a typedef to a #define (in case it would
+ cause an aggressive compiler to think you're passing the wrong type
+ to a function) and added Doxygen comments to explain it.
+03152007 - Bunch of work on Unicode...added case-folding stricmp, removed
+ platform-specific stricmp implementations, changed appropriate
+ calls to an ASCII-only stricmp that ignores locale. Fixed case on
+ UTF-8 API entry points.
+03142007 - Dropped classic Mac OS support. It's just too hard to find a working
+ Mac OS 9 install and reasonable development tools, so it's not
+ worth it. If you still target OS 8 or 9, please use PhysicsFS 1.0.
+03112007 - Removed zlib_license_change.txt ... it's in Subversion and the 1.0
+ branch for history's sake. Added shared and static build options
+ to CMakeLists.txt, and the expected "make install" target.
+ Renamed some FILENAME files to FILENAME.txt, removed physfs.rc.
+ Now compiles everything whether we need it or not, removing whole
+ files with #ifdefs...this will make it easier to "embed" this
+ library in other projects or use a different build system: just
+ push everything through the compiler with preprocessor defines for
+ the parts you want/need...platform modules are determined
+ automatically without the build system needing to intervene, so you
+ just have to #define the archivers, etc that you want.
+ Updated makeos2.cmd for newer Innotek toolchain (thanks, Dave!)
+03082007 - Fixed a comment in physfs.h. Renamed win32.c to windows.c.
+ Cleaned up whitespace/formatting in pocketpc.c. Updated PocketPC
+ code to expect UTF-8 strings from the higher level. Changed
+ PHYSFS_SUPPORTS_LZMA to PHYSFS_SUPPORTS_7Z. Killed some #ifdefs
+ in physfs.c. Moved to CMake...so long, autotools! Killed MIX
+ archiver, too.
+11052006 - More 7zip archiver work (thanks, Dennis!). Initial Unicode work.
+ Minor BeOS realpath tweak.
+09272006 - Reworked 7zip archiver (thanks, Dennis!).
+09232006 - Fixed typo in doxygen comment.
+04112006 - Added LZMA archiver...7zip support (thanks, Dennis!).
+03232006 - Added -fvisibility for gcc4 (http://gcc.gnu.org/wiki/Visibility)
+01012006 - Cleaned up overflow checks in platform memory allocators (thanks to
+ Nicolas Lebedenco for pointing out the original issue with
+ long long literals). Added physfs.rc (thanks, Dennis!). Changed my
+ email address. Removed acconfig.h.
+11282005 - Corrected docs on PHYSFS_setWriteDir().
+10122005 - Fixed locateInStringList() in physfs.c (thanks, Matze!). Patched
+ archivers/wad.c to compile.
+09192005 - Make unix mutexes recursive above pthread layer...fixes deadlock on
+ MacOS X, for now.
+09182005 - API BREAKAGE: PHYSFS_enumerateFilesCallback() now passes the
+ original directory name back to the app in the callback. This
+ API was only in 1.1.0, and wasn't promised to be stable at this
+ point. Please update your apps! Cleaned out a FIXME in file
+ enumeration that would confuse the library under certain
+ circumstances.
+09092005 - Some tweaks to PHYSFS_Allocator. Apparently configure.in doesn't
+ work like I thought for version bumps, so it thinks 1.1.0 isn't
+ binary compatible with 1.0...fixed, I think.
+09062005 - Happy September. Changed the allocation abstraction to use
+ PHYSFS_uint64 instead of size_t, so we don't have to include
+ system headers inside physfs.h. Minor MingW fixes (but it's still
+ broken, I think).
+08202005 - Fixed bug in verifyPath() that was breaking PHYSFS_setSaneConfig()
+ and other corner cases.
+07242005 - Patched to compile on BeOS.
+07232005 - Fixed bug in zip archiver (thanks, Jörg Walter!).
+ More minor OS/2 tweaks. Updated zlib to 1.2.3, which properly
+ includes the security fix. Fixed "make dist" to handle .svn dirs
+ and other file changes. Removed "debian" directory. Allow a mount
+ point of NULL to be "/", per the documentation. Fixed warning in
+ physfs.c. Assert definition fix. Updated CWProjects.sit.
+ Upped version to 1.1.0 ... first release of 1.1 dev branch!
+07212005 - Patched to compile on OS/2 again.
+07132005 - Updated zlib to 1.2.2, and patched it for this security hole:
+ http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-2096
+06122005 - Added support for mingw to Unix build process (thanks, Matze!).
+03162005 - Added missing translation and Portuguese support (thanks, Danny!).
+ MPW support and several MacOS Classic fixes (thanks, Chris!).
+ Changed CWProjects from SITX to SIT format, so OS9 users can
+ unpack it.
+03132005 - More mount work, added PHYSFS_getMountPoint() and more cleanups.
+ Replaced all the C runtime allocations with PhysFS allocation hooks.
+ Added pocketpc.c to EXTRA_DIST. Added allocation hooks to some
+ platform drivers. Updated Mac Classic build.
+03122005 - Added evil GOTO_*_MACRO_* macros. Fixed unix.c to compile again on
+ MacOS X. Added PHYSFS_mount() (thanks, Philip!). Cleaned up the
+ INSTALL and CREDITS files a little. Split off start of
+ verifySecurity() into a path sanitizer and changed entry points to
+ sanitize input paths into a stack-allocated buffer before further
+ processing. This removes the need for a malloc() for almost all
+ file system operations, and generally cleaned things up. Added a
+ "mount" command to test_physfs. Other general cleanups.
+02152005 - Minor comment fix in platform/pocketpc.c
+01052005 - Fixed HOG archiver file lookup (thanks, Chris!)
+12162004 - Fixed some documentation/header comment typos (thanks, Gaetan!)
+10302004 - Fixed a strcpy that should have been a strcat. (thanks, Tolga!)
+ Build system respects external CFLAGS now. (thanks, Adam!)
+ Fixed infinite loop in new enumeration code. (thanks, Adam!)
+10062004 - Removed profiling code from physfs.c.
+09292004 - Every API that can return a list of strings can now use a
+ callback mechanism if the application wants to do it's own
+ allocation or handling on a per-item basis. The guts of those
+ APIs that create string lists now use the callbacks themselves to
+ build the lists, too. The callback functionality goes all the way
+ down to the archivers and platform drivers where appropriate, which
+ cleans things up and simplifies some internal tasks very nicely.
+ Got rid of all the annoying forward declarations in all the
+ archivers and moved their PHYSFS_Archiver data to the end of the
+ file, since this was annoying me and I was getting sick of updating
+ function signatures in two places when the internal API changed.
+ Removed the code/data for LinkedStringLists...it isn't used anymore
+ now that the callback code is in place.
+09262004 - Did the same thing to FileHandles than I did to DirHandles, but
+ this triggered massive tweaking in physfs.c. A lot of code got
+ little cleanups, which was nice. Less malloc pressure, too, since
+ opening a file used to allocate a ton of crap and mush it
+ together...now it's basically down to one structure and the
+ instance data in whatever archiver. Minor varname tweak in win32.c
+ and pocketpc.c. Changed PHYSFS_file to PHYSFS_File to match the
+ rest of the API's naming scheme (but put a typedef for source
+ compatibility).
+09252004 - Cleaned up archiver interface to not deal with DirHandles anymore,
+ which simplifies things, removes some responsibility and code
+ duplication from the archivers, and trims some malloc pressure.
+ Ripped up the allocation hook code a little. We'll try to screw
+ with memory locking later, since it makes everything ugly and
+ complex. Oh well.
+09232004 - Started adding allocation hooks.
+09222004 - Happy September. Added Spanish translation back in.
+04092004 - Added MIX support for legacy Westwood titles (Thanks, Sebastian!).
+ Made bootstrap script MacOSX-friendly. Moved byteorder defines into
+ physfs_internal.h ...
+01152003 - Added Portuguese (Brazil) translation (Thanks, Danny!)
+
+
+--- This is where the 1.1 development branch starts. ---
+
+12292003 - Updated CodeWarrior projects from CW6 to CW7, and made a bunch of
+ patches to get the Mac Classic target building again. Removed
+ zlib114 from CVS repository. Updated OS/2 build batch file.
+ Added Z_PREFIX define to Unix builds that use internal zlib.
+ Patched up some (outdated?) Visual C project files for zlib121.
+ Patched Doxyfile and physfs.h for newer Doxygen. Fixed OS/2
+ build script. Tweaked Project Builder files to at least compile.
+ Added some last minute BeOS and Cygwin build fixes. Updated
+ Visual Studio projects and tweaked some Makefile.am crap. Made
+ changes so Visual Studio files would pack with DOS endlines and...
+ Upped version to 1.0.0 (woohoo!).
+12222003 - Fixed a search-and-replace mistake in win32.c that preventing
+ compiling on Windows. (thanks, Brian!) Converted VC6 .dsp to use
+ zlib121; made Z_PREFIX=1 enabled by default to avoid link clashes;
+ put zlib files in separate logical folder in .dsp project; updated
+ zlib121/zconf.h to address remaining symbols that were still
+ causing link warnings.
+12182003 - WAD archiver now puts maps into subdirectories, making them
+ accessible to the application. (Thanks, Travis!) RPM spec and
+ Makefile.am* now package zlib_license_change.txt (Thanks, Edward!)
+12142003 - Added Doom WAD support (Thanks, Travis!)
+12082003 - Fixed some win32.c deficiencies that Robby Dermody pointed
+ out (thanks!)
+12072003 - Upgraded internal zlib to 1.2.1 (thanks, Adam!) Other
+ Unix build fixes.
+11112003 - Patches to make OS/2 support compile again.
+11092003 - Added __PHYSFS_platformStrnicmp(), and made qpak.c case-insensitive.
+09122003 - Happy September. Actually released current tree as 0.1.9.
+08262003 - Added MiNT support to build process and fixed cross-compiling
+ (thanks Patrice Mandin!)
+08092003 - Some Windows build fixes (thanks, Brian Hook!)
+07232003 - Upped version to 0.1.9.
+07202003 - Switched to zlib license (see new LICENSE text in root of source
+ tree, and zlib_license_switch.txt for details). Had to remove
+ archivers/qpak.c, the Ruby bindings from the extras directory, and
+ the Russian and Spanish translations, since those contributors
+ couldn't be contacted. If they show up, we'll readd them to the
+ project, otherwise we'll eventually replace their work...everyone
+ else signed on for the change. Committed a patch to convert all
+ tabs to spaces (Thanks, James!). Added patch to zip.c to fix
+ crash (thanks, dillo!). Reimplmented qpak.c, by welding together
+ bits of grp.c and zip.c. Ed contacted me, so I could readd his
+ contributions post-license change...I'm going to keep the new
+ qpak.c, but I've readded his Ruby bindings and Russian translations.
+06112003 - Patches to globbing.c to handle corner cases (thanks, Bradley!).
+06102003 - Added globbing.c to "extras" directory.
+05232003 - Rewrote MacOSX/Darwin CD-ROM detection code to use IOKit, which is
+ much much more accurate than the previous code. Updated
+ configure.in and Makefile.am.newautomake for some MacOSX stuff.
+05222003 - Fixed win32 crash if PHYSFS_init() is called with a NULL.
+05182003 - PocketPC fixes (thanks, David Hedbor!)
+05162003 - Compiler warning cleanup in HOG and MVL archivers (Thanks, Bradley!)
+04082003 - Minor changes to extras/abs-file.h (Thanks, Adam!)
+03302003 - Fixed seeking in uncompressed ZIP entries, and handle a
+ misbehaviour in Java's JAR creation tools. Thanks to "Tree" for
+ pointing these bugs out. Added HOG and MVL archive support for
+ Descent I and II (Thanks, Bradley Bell!). Added example code to
+ do case-insensitive file searches ("extras/ignorecase.*").
+03192003 - Fixed problem in PHYSFS_mkdir() when dirs to be created already
+ exist. Fixed problem where PHYSFS_mkdir() incorrectly tripped an
+ alarm in __PHYSFS_verifySecurity().
+03122003 - Attempt at cleaning up some type correctness for VC++6. Made QPAK
+ archiver case-insensitive (since Quake2 has problems without it).
+01302003 - Added buffering API to OS/2 build's exported symbol list. Updated
+ CWProjects.sit and made several fixes to get physfs building on
+ MacOS Classic again.
+01282003 - Fixed seeking in buffered files opened for read.
+01072003 - .NET assembly and C# wrapper by Gregory S. Read in the extras dir.
+01042003 - Added a hack for dealing with OSX bundles and newer PBProjects
+ (thanks, Eric Wing!). Added some missing files to "make dist".
+ Fixed minor Doxygen typo in PHYSFS_flush() docs. Upped version to
+ 0.1.8.
+12172002 - Added Apple Project Builder support files (thanks, Eric Wing!).
+12112002 - Added Ruby bindings to extras directory (thanks, Ed Sinjiashvili!).
+ Patched win32.c to compile with Mingw32 (thanks, Niels Wagenaar!).
+12032002 - Adam updated his extras/abs-file.h for the new buffering API.
+12022002 - German translation added, compliments of Michael Renner.
+12012002 - Minor fix to configure.in: reported --enable-debug's default
+ setting incorrectly. Added buffering to the API: you can now
+ buffer a file with PHYSFS_setBuffer(), and flush the buffer to
+ disk with PHYSFS_flush(). PhysicsFS file handles are unbuffered
+ by default (as they were before this API addition), so this does
+ not break the API. Other fixes for bugs I stumbled upon during
+ this work are in CVS, too.
+11292002 - Minor fix for strange PATH strings in unix.c (thanks, Alexander!)
+11222002 - Initial PocketPC port by Corona688.
+10222002 - Fixed segfault in test_physfs.c when user hits CTRL-D (and
+ readline() thus returns NULL)...now gracefully exits, as it should.
+10142002 - Added check for AMD's x86-64 ("Hammer") architecture when
+ determining platform byte order.
+10112002 - Fixed "setsaneconfig" command in test_physfs.c ...
+09232002 - Happy September. Updated VC++6 project files, fixed some
+ VC++ compile nags (more work to be done in zip.c).
+08302002 - Cleaned tab stops out of zip.c, and fixed a possible infinite loop
+ in zip_find_entry().
+08292002 - Fixed a mistake in makeos2.cmd, and updated the INSTALL docs.
+ Added physfs.spec.in to EXTRA_DIST in Makefile.am*
+08292002 - Added a physfs/stdio wrapper header to the "extras" dir,
+ compliments of Adam D. Moss (file is "abs-file.h").
+08282002 - Cleanups in grp.c so that Visual C++ doesn't complain anymore.
+ zip.c now works correctly when PhysicsFS is disallowing symlinks.
+ A few minor optimizations in zip.c, with a few more to come later.
+ Added VS.NET project files to CVS.
+08222002 - Fixed ZIP_exists() to work with directories. Now breaks out of
+ __PHYSFS_verifySecurity() early if a path element is missing
+ (since all the others will be, too)...this check is only done
+ if symlinks are disabled, but we might as well save easy cycles
+ where we can.
+08212002 - Did a couple tedious-for-small-rewards cleanups, optimizations,
+ corrections and streamlinings I've been meaning to do. Touched a
+ lot of code. One of the side results is that ZIP_isDirectory()
+ got fixed.
+08192002 - Generalized sorting routines, moved them into physfs.c and removed
+ the multiple copies from the various archivers. Adding profiling
+ code (currently only for sort routines)...enable it with
+ --enable-profiling in the configure script. Fixed incorrect
+ behaviours in configure.in.
+08172002 - Patched configure.in to work around buggy autoconfs.
+08162002 - Fixed QPAK archiver, since I broke it (sorry!). Also fixed a
+ qpak memory leak.
+08092002 - Added Quake PAK archiver (qpak.c) by Ed Sinjiashvili. Thanks!
+ Made (successful?) attempt to fix pthread-to-ui64 cast problem.
+ Check for OS/2 in configure.in, in case anyone gets autoconf and
+ such to work right on their OS/2 box.
+08012002 - Patched win32.c to compile.
+07302002 - Minor error handling fix (thanks, Alexander!)
+07292002 - Found some memory leaks, thanks to Valgrind (which rules, btw).
+ Added Russian translations (koi8-r, cp1251, cp866, and iso-8859-5)
+ by Ed Sinjiashvili. Added Spanish translation by Pedro J. Pérez.
+ Debian package support in CVS, thanks to Colin Bayer. French
+ translation by Stéphane Peter.
+07282002 - macclassic.c now returns human readable error messages instead of
+ ERR_OS_ERROR. Closing files on MacOS no longer fails if the volume
+ info can't be flushed. Minor error message tweak in os2.c. All
+ possible human-readable literal strings (including all those OS/2
+ and MacOS error messages) have moved to constants in
+ physfs_internal.h...this allows the library to be translated to
+ other spoken languages fairly easily.
+07272002 - Patched the OS/2 code to be useful...works pretty well, now. Added
+ makeos2.cmd for building (not an ideal solution, but oh well).
+ Initialized some variables in zip.c to prevent compiler whining.
+07262002 - Fixed a typo in documentation. Archivers with matching file
+ extensions are now given first shot at opening an archive, but if
+ they fail, the other archivers are tried. More fixes to zip.c's
+ ZIP_enumerateFiles(). Wrote an OS/2 platform driver based on API
+ specs and a heavy pounding of Google Groups...as I don't have an
+ OS/2 compiler at the moment, it probably doesn't even compile. :)
+07252002 - configure.in and unix.c now deal with platforms that lack a
+ functional pthread library. Edward Rudd sent in a patch to the RPM
+ specfile to have the build system set the correct version.
+ Clean ups in grp.c, beos.cpp and macclassic.c.
+07242002 - Rewrote ZIP_enumerate(). Hopefully it sucks less this time.
+ unix.c and configure.in now have the infrastructure to disable
+ the CD-ROM detection code, and use a stub that successfully (and
+ unconditionally) reports no detected discs. Currently this is
+ used on AtheOS (which doesn't have CD-ROM support at the moment
+ anyhow), but it will be useful to get the library up on odd,
+ Unix-like systems that don't use either getmntinfo() or getmntent().
+07232002 - Cleaned up the cut-and-pastes in the various file enumeration
+ routines and moved it into __PHYSFS_addToLinkedStringList().
+ Tons more ZIP file enhancing. I'm fairly certain it's robust and
+ fast in every reasonable respect, now. GRP archiver now caches
+ the file table...it was generally overhauled like the ZIP driver.
+ Added "ls" as an alias of "enumerate" in test_physfs.
+ I lied about zip.c's robustness; disabled the enumeration code.
+07212002 - More FreeBSD build system patches. Added some new autoconf spew to
+ .cvsignore. bootstrap now copies the appropriate Makefile.am
+ instead of rename()ing it.
+07192002 - Cleaned up configure.in and unix.c so that we check by available
+ header to determine the appropriate CD-ROM detection code...this
+ should make this more future-proof (and probably get it building
+ out of the box on other BSD platforms.)
+07172002 - Fixed seeking backwards in ZIP_seek(). Changed the error message
+ ERR_TOO_MANY_SYMLINKS to ERR_SYMLINK_LOOP. Patches to build system
+ and unix.c for FreeBSD compatibility. Added physfs.spec to
+ "make dist" archives (thanks, Edward Rudd!).
+07152002 - Symlinks in ZIP archives are detected correctly now, I think.
+07142002 - Use GetVolumeInformation() instead of GetDiskFreeSpace() in
+ win32.c's mediaInDrive() function. This allows Windows NT 3.x to
+ correctly detect CD-ROM drives. Library now appears to be fully
+ functional on WinNT 3.51...need to try NT 3.1 still. :)
+ Patches to new ZIP code; cleaned up bugs in symlink reading code,
+ but we incorrectly identify some entries as symlinks, which doesn't
+ fly...for now, symlink code is commented out, so symlinks look
+ like regular files (and reading from a symlink entry gives you
+ the link as file data).
+07122002 - Rewrote the ZIP archiver to no longer use Gilles Vollant's unzip.c
+ code. Losing that abstraction should make the ZIP archiver
+ significantly more efficient, and halved the amount of code used.
+ Plus, being a control freak, I like my coding style more than
+ Gilles's. :) There are still bugs to shake out, but this is good
+ progress.
+07112002 - configure.in updated to make it happier on newer autoconfs
+ (thanks again, Alexander!). FIXME cleanups.
+07102002 - Added a byteorder-friendly convenience API, so you can read/write
+ data and convert to the native byteorder without too much effort.
+ Upped version to 0.1.7.
+ Build system corrections for BeOS and Cygwin (thanks, Alexander!).
+ Added RPM specfile for PhysicsFS (thanks, Edward Rudd!).
+06292002 - Fixed incorrect error message when opening a file for read without
+ defining a search path. LOTS of win32 updates and fixes; lots of
+ things that were broken work now, and we are slowly becoming
+ more compatible with legacy win32 systems. Builds on Cygwin again.
+ All platform drivers (except beos.cpp) had a buffer overflow when
+ detecting mounted CD-ROM drives...it only occurs when a drive is
+ detected, and it probably won't result in your box getting rooted,
+ but upgrade soon anyhow. Readded the .cvsignore files from the old
+ build system.
+06282002 - Reworked build system _AGAIN_.
+06222002 - Alexander Pipelka spotted a bug in the file open routines in
+ posix.c; patched.
+06152002 - Autoconf build system will now generate shared libraries on BeOS,
+ and (supposedly) Cygwin.
+06142002 - Rewrote autoconf build system. It now works around the MacOS X bug
+ that prevented shared libraries from building.
+06112002 - Updated CodeWarrior projects and added them to CVS. _Finally_
+ officially released 0.1.6.
+06102002 - Major overhauls to platform/win32.c ... should work on all Windows
+ platforms, including 95/98/ME and NT/2K/XP flavors. Someone should
+ see if this builds on WinCE! :) You no longer need the latest
+ platform SDK to build it, either; the questionable DLL is accessed
+ with LoadLibrary() at runtime now, and handled if not present. This
+ now builds correctly on a freshly installed Visual Studio 6.0, and
+ the DLL it builds works everywhere. Plus, a bunch of other bugs
+ and incorrect behaviours were squashed. Visual Studio 6.0 project
+ file added to CVS.
+06082002 - Fixes to __PHYSFS_platformEnumerateFiles() in win32.c: cleaned up
+ memory leak, handles paths more robustly, and prevents possible
+ skipped file entries. Removed AC_C_CONST and AC_TYPE_SIZE_T checks
+ from configure.in (not needed, and they broke BeOS build). Clean
+ out the docs/ directory when doing a "make dist". Fixed crashbug
+ when calling PHYSFS_deinit() more than once in a row. Tried to get
+ MacOS X to build a shared library, gave up; I'm doing something
+ wrong in my Makefile.am, I think. On MacOS X, running ./configure
+ --enable-static --disable-shared works, though. Hopefully someone
+ will fix this soon. In unix.c, the Darwin version of
+ __PHYSFS_platformDetectAvailableCDs() was free()ing a static
+ buffer; fixed.
+06072002 - Manpages! Finally installed Doxygen and scratched together a
+ Doxyfile. After some revision to physfs.h, we've got a rather
+ nice API reference.
+06062002 - Fixed __PHYSFS_platformSeek() in archivers/posix.c. Implemented the
+ getLastModTime method in archivers/zip.c (returns legitimate info)
+ and archivers/grp.c (returns lastmodtime of GRPfile itself in the
+ physical filesystem). Put a 64-bit _llseek() version of the seek()
+ and tell() methods in platform/posix.c, but you need to hack (or
+ rather, fix) configure.in to enable it. From Greg on win32.c: Fixed
+ file enumerator function (needed a wildcard '*' specification), CD
+ enumeration only reports CDs that have media, getLastModTime() has
+ been implemented.
+06012002 - Added -Wall to debug builds. Removed ANSI stdio calls from
+ platform/posix.c, and replaced them with actual POSIX calls (that
+ is, fopen() became open(), fseek() became lseek(), etc...)
+05272002 - Added some explicit casts when calling malloc() in platform/posix.c
+05252002 - Added John Hall's file modification time patch, and added a
+ getlastmodtime command to test_physfs. Corrected error reporting
+ for missing files a little bit. Changed build system to only try
+ building beos.cpp if on a BeOS system (since we need a C++ compiler
+ available to do so). Implemented getLastModTime in macclassic.c.
+05242002 - Upped version to 0.1.6 (not officially released yet).
+05232002 - Fixed the build system to always package the complete source, not
+ just what we built for a given system, when doing a "make dist".
+ Updated INSTALL. Wrote BeOS platform code (platform/beos.cpp).
+ Split unix.c into unix.c and posix.c. Linux and BeOS both share
+ posix.c, although I don't think it's completely POSIX compliant at
+ this point (not that it matters much).
+05212002 - Cleaned up some FIXMEs.
+05202002 - Added .cvsignore files.
+05162002 - Edward Rudd also caught an embarrassing screwup by me in
+ unix.c: the open-for-append call was using "wb+" instead of
+ "ab" when calling fopen(). Doh!
+05152002 - configure script now deals with systems that have a readline
+ lib, but require it to be linked with curses. Thanks to Edward
+ Rudd for the patch.
+05102002 - A trimmed-down zlib 1.1.4 is now included in the source distro, for
+ use by win32, MacOS, and Unix systems that don't have it installed
+ on the system. Autoconf support! Initial attempt at this. Lots of
+ stuff may be very broken.
+05082002 - From Greg: More win32 work. Library is now 95% functional on win32.
+ Only known win32 problem is that the CD drives are reported whether
+ they contain a disc or not).
+05062002 - From Greg: Win32 boxes without the latest Platform SDK can now
+ #define DISABLE_NT_SUPPORT. Other fixes.
+04242002 - Updated win32 info in INSTALL to discuss Platform SDK issues.
+04202002 - Added a (very) quick and (very) dirty http server to the
+ extras directory (public domain), as another example of using
+ the library.
+04192002 - Corrected some win32 info in INSTALL. Changed Makefile to
+ package releases as .tar.gz instead of .tar.bz2.
+04122002 - Some win32 cleanups and fixes across several files. Upped
+ version to 0.1.5.
+04082002 - Fixed problem when calling __PHYSFS_setError before PHYSFS_init.
+04062002 - Added MacOS info, etc to INSTALL. Patched unix.c and
+ test_physfs.c to compile on Darwin again.
+04052002 - Added byte ordering API. Byte ordering fixes in grp.c, and some
+ cleanups in unzip.c. Mac work is more or less complete.
+04042002 - Mac work continues. Almost complete, now. test_physfs now has
+ tests for write, append, and filelength, and most of the
+ commands can tolerate a quoted argument (although this is
+ hacky, it's good enough for these purposes). Upped test_physfs
+ version to 0.1.1. Added a malloc-failure check in the Unix
+ CD-ROM detection code.
+04032002 - PHYSFS_init always makes sure the calling thread initializes its
+ error state. Win32 codebase is updated with mutex implementation
+ (thanks, Greg!).
+04022002 - Mac work continues. Found a bug where we put a double dir
+ separator in if we had to resort to the fallback userdir (if
+ __PHYSFS_platformGetUserDir() returned NULL to calculateUserDir().
+ Made note of potential infinite recursion in platform driver docs.
+04012002 - (_NOT_ an April Fool's Joke:) Started working on MacOS Classic
+ port. Added skeleton.c to platform directory. Minor patches to
+ get things compiling on Mac (notably, DirInfo conflicts with
+ a type exposed by MacOS's namespace-polluting API, and some
+ typecasting issues). Found a call to ferror() I had missed in
+ unzip.c.
+03302002 - Mutexes! PhysicsFS should be thread safe now, so long as you
+ don't try to do something like close a file at the same time as
+ you are reading from it in another thread. All reasonable race
+ conditions should now be gone, but the new code will need some
+ eyeballing before we install it on life support systems or anything.
+ The mutex abstraction is implemented in unix.c, win32.c will be
+ updated shortly.
+03292002 - Fixed a potential problem in ZIP_realpath() and some byte order
+ issues in zip.c. Converted unzip.c to use physfs file i/o
+ abstractions. Converted CHANGELOG to list latest entries first.
+03242002 - Added __PHYSFS_platformInit() and __PHYSFS_platformDeinit(). Win32
+ improvements by Gregory S. Read. Added PHYSFS_[us]int(8|16|32)
+ types...this breaks binary compatibility with previous PhysicsFS
+ releases! Added platform specific i/o functions, so we don't have
+ to rely on stdio anymore. Updated TODO with my comments on the
+ physfs mailing list. 1.0, here we come! Removed race condition from
+ grp.c and converted to file i/o abstraction layer calls from stdio.
+ Tons of other fixes and enhancements.
+03202002 - Patched platform/win32.c to compile.
+03152002 - PHYSFS_setSaneConfig() now handles failure to set the write dir
+ better. Patched makefile to link the test program. Changed all the
+ "write" functions to get data from a "const" buffer. Added an
+ "extras" dir, which currently contains PhysFS->SDL_RWops glue code.
+03052002 - Made unix.c's timeslice implementation more portable, and added a
+ Darwin-specific means to detect CDs (thanks to Patrick Stein).
+ Minor cleanup in win32.c (changed "for (; condition ;)" into
+ "while (condition)" ...)
+11142001 - Removed a redundant error check in platform/win32.c
+10092001 - Syntax fixes in dir.c, a FIXME in grp.c, and a "cat" command in
+ the test program. Apparently I had accidentally removed a rather
+ crucial line from dir.c a few revisions ago, and no one noticed. :(
+ Fixed. The win32 userdir will default to the base dir, now.
+09252001 - Changed API: PHYSFS_setSaneConfig() takes an organization name, and
+ sets up less directories. Be warned. Fixes from David Hedbor:
+ make setSaneConfig() set write directory correctly if it had to
+ create the directory, and make sure that the writing functions
+ get used in dir.c when a file is opened for writing/appending.
+ Updated CREDITS.
+09142001 - David Hedbor submitted a patch to handle a case where the
+ current working directory has been deleted out from under the
+ process (both in platform/unix.c and physfs.c itself). Thanks,
+ David! Added a CREDITS file. Changed the format of the author field
+ in PHYSFS_ArchiveInfo to put the email address between "<>" instead
+ of "()" chars. Updated TODO. make install now deletes previous
+ revisions of the library. Changed version to 0.1.4.
+09012001 - Happy September. Moved the Visual C project files and the zlib
+ source to a separate download. Look for it at
+ http://icculus.org/physfs/downloads/physfs-win32-support.zip ...
+ Updated the INSTALL doc for Win32 building. Rewrote win32.c's
+ __PHYSFS_platformRealPath() to not rely on Visual C's runtime lib,
+ which was the last Cygwin incompatibility (although the Makefile
+ needs to be updated to build a DLL under Cygwin). Tinkered with the
+ Makefile a little, but it needs more work. Started working on a
+ MacOS version. All I have is CodeWarrior 4, which is way out of
+ date, and (for what is supposed to be an ultra-user-friendly
+ environment) is completely uninituitive to me. Still, managed to
+ get most everything compiling, which improved the quality of the
+ code somewhat). Haven't tried to compile the zipfile support, and
+ I still can't link the library. Dunno what the hell I'm supposed
+ to do there. Isn't Unix supposed to be hard compared to this?
+08312001 - Built PhysicsFS on Mandrake 8.0 for the PowerPC. Compiles clean,
+ but there's at least one byte-ordering issue in zip.c that needs
+ to be fixed.
+08292001 - win32.c calculates the base dir with GetModuleFileName() first, now,
+ and falls back to SearchPath() if there were problems. Changed an
+ occurence of _MAX_PATH to MAX_PATH, so both CygWin and Visual C can
+ handle it.
+08282001 - win32.c now checks HOMEDRIVE, HOMEPATH, and HOME when calculating
+ the userdir. Added include files that make it a little closer to
+ compiling under Cygwin. Added a TODO file. Fixed unix.c's
+ __PHYSFS_platformCalcBaseDir() so that it actually works. Fixed
+ Makefile so that it links the test program properly.
+ Changed version to 0.1.3.
+08232001 - Fixed a potential free()ing of a NULL pointer in
+ __PHYSFS_platformEnumerateFiles() in platform/unix.c. Added
+ platform/win32.c. Other cleanups to get this compiling with
+ Visual C and CygWin. Added BAIL_MACRO for times when we were doing
+ BAIL_IF_MACRO(1, ...). Abstracted mkdir() in the platform drivers.
+ Added GRP setting output to showcfg in the Makefile. Updated INSTALL
+ with license info and Win32 build instructions. Dependency on the
+ readline library in test_physfs.c is now optional.
+ Changed version to 0.1.2.
+08072001 - Changed version to 0.1.1.
+08062001 - Added CD-ROM detection code to the unix platform driver.
+08012001 - Added a safety memset in error setting, fixed URLs and email addr.
+07282001 - Initial release.
+
+--ryan. (icculus@icculus.org)
+
+/* end of CHANGELOG ... */
+
--- /dev/null
+# PhysicsFS; a portable, flexible file i/o abstraction.
+# Copyright (C) 2007 Ryan C. Gordon.
+#
+# Please see the file LICENSE.txt in the source's root directory.
+
+CMAKE_MINIMUM_REQUIRED(VERSION 2.4)
+
+PROJECT(PhysicsFS)
+SET(PHYSFS_VERSION 1.1.1)
+SET(PHYSFS_SOVERSION 1)
+
+# I hate that they define "WIN32" ... we're about to move to Win64...I hope!
+IF(WIN32 AND NOT WINDOWS)
+ SET(WINDOWS TRUE)
+ENDIF(WIN32 AND NOT WINDOWS)
+
+# Bleh, let's do it for "APPLE" too.
+IF(APPLE AND NOT MACOSX)
+ SET(MACOSX TRUE)
+ENDIF(APPLE AND NOT MACOSX)
+
+INCLUDE(CheckIncludeFile)
+INCLUDE(CheckLibraryExists)
+INCLUDE(CheckCSourceCompiles)
+
+INCLUDE_DIRECTORIES(.)
+#INCLUDE_DIRECTORIES(platform)
+#INCLUDE_DIRECTORIES(archivers)
+
+IF(MACOSX)
+ # Fallback to older OS X on PowerPC to support wider range of systems...
+ IF(CMAKE_OSX_ARCHITECTURES MATCHES ppc)
+ ADD_DEFINITIONS(-DMAC_OS_X_VERSION_MIN_REQUIRED=1020)
+ SET(OTHER_LDFLAGS ${OTHER_LDFLAGS} " -mmacosx-version-min=10.2")
+ ENDIF(CMAKE_OSX_ARCHITECTURES MATCHES ppc)
+
+ # Need these everywhere...
+ ADD_DEFINITIONS(-fno-common)
+ SET(OTHER_LDFLAGS ${OTHER_LDFLAGS} " -framework Carbon -framework IOKit")
+ENDIF(MACOSX)
+
+# Add some gcc-specific command lines.
+IF(CMAKE_COMPILER_IS_GNUCC)
+ # Always build with debug symbols...you can strip it later.
+ ADD_DEFINITIONS(-g -pipe -Werror -fsigned-char)
+
+ # Stupid BeOS generates warnings in the system headers.
+ IF(NOT BEOS)
+ ADD_DEFINITIONS(-Wall)
+ ENDIF(NOT BEOS)
+
+ CHECK_C_SOURCE_COMPILES("
+ #if ((defined(__GNUC__)) && (__GNUC__ >= 4))
+ int main(int argc, char **argv) { int is_gcc4 = 1; return 0; }
+ #else
+ #error This is not gcc4.
+ #endif
+ " PHYSFS_IS_GCC4)
+
+ IF(PHYSFS_IS_GCC4)
+ ADD_DEFINITIONS(-fvisibility=hidden)
+ ENDIF(PHYSFS_IS_GCC4)
+ENDIF(CMAKE_COMPILER_IS_GNUCC)
+
+IF(MSVC)
+ # VS.NET 8.0 got really really anal about strcpy, etc, which even if we
+ # cleaned up our code, zlib, etc still use...so disable the warning.
+ ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS=1)
+ENDIF(MSVC)
+
+# Basic chunks of source code ...
+
+SET(ZLIB_SRCS
+ zlib123/adler32.c
+ zlib123/compress.c
+ zlib123/crc32.c
+ zlib123/deflate.c
+ zlib123/gzio.c
+ zlib123/infback.c
+ zlib123/inffast.c
+ zlib123/inflate.c
+ zlib123/inftrees.c
+ zlib123/trees.c
+ zlib123/uncompr.c
+ zlib123/zutil.c
+)
+
+SET(LZMA_SRCS
+ lzma/7zBuffer.c
+ lzma/7zCrc.c
+ lzma/7zDecode.c
+ lzma/7zExtract.c
+ lzma/7zHeader.c
+ lzma/7zIn.c
+ lzma/7zItem.c
+ lzma/7zMethodID.c
+ lzma/LzmaDecode.c
+ lzma/LzmaStateDecode.c
+)
+
+IF(BEOS)
+ # We add this explicitly, since we don't want CMake to think this
+ # is a C++ project unless we're on BeOS.
+ SET(PHYSFS_BEOS_SRCS platform/beos.cpp)
+ SET(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} be root)
+ENDIF(BEOS)
+
+# Almost everything is "compiled" here, but things that don't apply to the
+# build are #ifdef'd out. This is to make it easy to embed PhysicsFS into
+# another project or bring up a new build system: just compile all the source
+# code and #define the things you want.
+SET(PHYSFS_SRCS
+ physfs.c
+ physfs_byteorder.c
+ physfs_unicode.c
+ platform/os2.c
+ platform/pocketpc.c
+ platform/posix.c
+ platform/unix.c
+ platform/macosx.c
+ platform/windows.c
+ archivers/dir.c
+ archivers/grp.c
+ archivers/hog.c
+ archivers/lzma.c
+ archivers/mvl.c
+ archivers/qpak.c
+ archivers/wad.c
+ archivers/zip.c
+ ${PHYSFS_BEOS_SRCS}
+)
+
+
+# platform layers ...
+
+IF(UNIX)
+ IF(BEOS)
+ SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE)
+ SET(PHYSFS_HAVE_THREAD_SUPPORT TRUE)
+ SET(HAVE_PTHREAD_H TRUE)
+ ELSE(BEOS)
+ # !!! FIXME
+ # AC_DEFINE([PHYSFS_HAVE_LLSEEK], 1, [define if we have llseek])
+ CHECK_INCLUDE_FILE(sys/ucred.h HAVE_UCRED_H)
+ IF(HAVE_UCRED_H)
+ ADD_DEFINITIONS(-DPHYSFS_HAVE_SYS_UCRED_H=1)
+ SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE)
+ ENDIF(HAVE_UCRED_H)
+
+ CHECK_INCLUDE_FILE(mntent.h HAVE_MNTENT_H)
+ IF(HAVE_MNTENT_H)
+ ADD_DEFINITIONS(-DPHYSFS_HAVE_MNTENT_H=1)
+ SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE)
+ ENDIF(HAVE_MNTENT_H)
+
+ CHECK_INCLUDE_FILE(pthread.h HAVE_PTHREAD_H)
+ IF(HAVE_PTHREAD_H)
+ SET(PHYSFS_HAVE_THREAD_SUPPORT TRUE)
+ ELSE(HAVE_PTHREAD_H)
+ ADD_DEFINITIONS(-DPHYSFS_NO_PTHREADS_SUPPORT=1)
+ ENDIF(HAVE_PTHREAD_H)
+ ENDIF(BEOS)
+ENDIF(UNIX)
+
+IF(WINDOWS)
+ SET(PHYSFS_HAVE_CDROM_SUPPORT TRUE)
+ SET(PHYSFS_HAVE_THREAD_SUPPORT TRUE)
+ENDIF(WINDOWS)
+
+IF(NOT PHYSFS_HAVE_CDROM_SUPPORT)
+ ADD_DEFINITIONS(-DPHYSFS_NO_CDROM_SUPPORT=1)
+ MESSAGE(WARNING " ***")
+ MESSAGE(WARNING " *** There is no CD-ROM support in this build!")
+ MESSAGE(WARNING " *** PhysicsFS will just pretend there are no discs.")
+ MESSAGE(WARNING " *** This may be fine, depending on how PhysicsFS is used,")
+ MESSAGE(WARNING " *** but is this what you REALLY wanted?")
+ MESSAGE(WARNING " *** (Maybe fix CMakeLists.txt, or write a platform driver?)")
+ MESSAGE(WARNING " ***")
+ENDIF(NOT PHYSFS_HAVE_CDROM_SUPPORT)
+
+IF(PHYSFS_HAVE_THREAD_SUPPORT)
+ ADD_DEFINITIONS(-D_REENTRANT -D_THREAD_SAFE)
+ELSE(PHYSFS_HAVE_THREAD_SUPPORT)
+ MESSAGE(WARNING " ***")
+ MESSAGE(WARNING " *** There is no thread support in this build!")
+ MESSAGE(WARNING " *** PhysicsFS will NOT be reentrant!")
+ MESSAGE(WARNING " *** This may be fine, depending on how PhysicsFS is used,")
+ MESSAGE(WARNING " *** but is this what you REALLY wanted?")
+ MESSAGE(WARNING " *** (Maybe fix CMakeLists.txt, or write a platform driver?)")
+ MESSAGE(WARNING " ***")
+ENDIF(PHYSFS_HAVE_THREAD_SUPPORT)
+
+CHECK_INCLUDE_FILE(assert.h HAVE_ASSERT_H)
+IF(HAVE_ASSERT_H)
+ ADD_DEFINITIONS(-DHAVE_ASSERT_H=1)
+ENDIF(HAVE_ASSERT_H)
+
+
+
+# Archivers ...
+
+OPTION(PHYSFS_ARCHIVE_ZIP "Enable ZIP support" TRUE)
+IF(PHYSFS_ARCHIVE_ZIP)
+ ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_ZIP=1)
+ SET(PHYSFS_NEED_ZLIB TRUE)
+ENDIF(PHYSFS_ARCHIVE_ZIP)
+
+OPTION(PHYSFS_ARCHIVE_7Z "Enable 7zip support" TRUE)
+IF(PHYSFS_ARCHIVE_7Z)
+ ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_7Z=1)
+ # !!! FIXME: rename to 7z.c?
+ SET(PHYSFS_SRCS ${PHYSFS_SRCS} ${LZMA_SRCS})
+ INCLUDE_DIRECTORIES(lzma)
+ ADD_DEFINITIONS(-D_LZMA_IN_CB=1)
+ ADD_DEFINITIONS(-D_LZMA_PROB32=1)
+ ADD_DEFINITIONS(-D_LZMA_SYSTEM_SIZE_T=1)
+ ADD_DEFINITIONS(-D_SZ_ONE_DIRECTORY=1)
+ENDIF(PHYSFS_ARCHIVE_7Z)
+
+OPTION(PHYSFS_ARCHIVE_GRP "Enable Build Engine GRP support" TRUE)
+IF(PHYSFS_ARCHIVE_GRP)
+ ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_GRP=1)
+ENDIF(PHYSFS_ARCHIVE_GRP)
+
+OPTION(PHYSFS_ARCHIVE_WAD "Enable Doom WAD support" TRUE)
+IF(PHYSFS_ARCHIVE_WAD)
+ ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_WAD=1)
+ENDIF(PHYSFS_ARCHIVE_WAD)
+
+OPTION(PHYSFS_ARCHIVE_HOG "Enable Descent I/II HOG support" TRUE)
+IF(PHYSFS_ARCHIVE_HOG)
+ ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_HOG=1)
+ENDIF(PHYSFS_ARCHIVE_HOG)
+
+OPTION(PHYSFS_ARCHIVE_MVL "Enable Descent I/II MVL support" TRUE)
+IF(PHYSFS_ARCHIVE_MVL)
+ ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_MVL=1)
+ENDIF(PHYSFS_ARCHIVE_MVL)
+
+OPTION(PHYSFS_ARCHIVE_QPAK "Enable Quake I/II QPAK support" TRUE)
+IF(PHYSFS_ARCHIVE_QPAK)
+ ADD_DEFINITIONS(-DPHYSFS_SUPPORTS_QPAK=1)
+ENDIF(PHYSFS_ARCHIVE_QPAK)
+
+
+# See if some archiver required zlib, and see about using system version.
+
+IF(PHYSFS_NEED_ZLIB)
+ CHECK_INCLUDE_FILE(zlib.h HAVE_ZLIB_H)
+ IF(HAVE_ZLIB_H)
+ CHECK_LIBRARY_EXISTS("z" "inflate" "" HAVE_LIBZ)
+ IF(HAVE_LIBZ)
+ SET(HAVE_SYSTEM_ZLIB TRUE)
+ ENDIF(HAVE_LIBZ)
+ ENDIF(HAVE_ZLIB_H)
+
+ IF(HAVE_SYSTEM_ZLIB)
+ OPTION(PHYSFS_INTERNAL_ZLIB "Link own zlib instead of system library" FALSE)
+ ELSE(HAVE_SYSTEM_ZLIB)
+ SET(PHYSFS_INTERNAL_ZLIB TRUE)
+ ENDIF(HAVE_SYSTEM_ZLIB)
+
+ IF(PHYSFS_INTERNAL_ZLIB)
+ INCLUDE_DIRECTORIES(zlib123)
+ ADD_DEFINITIONS(-DZ_PREFIX=1)
+ SET(PHYSFS_SRCS ${PHYSFS_SRCS} ${ZLIB_SRCS})
+ ELSE(PHYSFS_INTERNAL_ZLIB)
+ SET(OPTIONAL_LIBRARY_LIBS ${OPTIONAL_LIBRARY_LIBS} z)
+ ENDIF(PHYSFS_INTERNAL_ZLIB)
+ENDIF(PHYSFS_NEED_ZLIB)
+
+OPTION(PHYSFS_BUILD_STATIC "Build static library" TRUE)
+IF(PHYSFS_BUILD_STATIC)
+ ADD_LIBRARY(physfs-static STATIC ${PHYSFS_SRCS})
+ SET_TARGET_PROPERTIES(physfs-static PROPERTIES OUTPUT_NAME "physfs")
+ SET(PHYSFS_LIB_TARGET physfs-static)
+ SET(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";physfs-static")
+ENDIF(PHYSFS_BUILD_STATIC)
+
+OPTION(PHYSFS_BUILD_SHARED "Build shared library" TRUE)
+IF(PHYSFS_BUILD_SHARED)
+ ADD_LIBRARY(physfs SHARED ${PHYSFS_SRCS})
+ SET_TARGET_PROPERTIES(physfs PROPERTIES VERSION ${PHYSFS_VERSION})
+ SET_TARGET_PROPERTIES(physfs PROPERTIES SOVERSION ${PHYSFS_SOVERSION})
+ TARGET_LINK_LIBRARIES(physfs ${OPTIONAL_LIBRARY_LIBS} ${OTHER_LDFLAGS})
+ SET(PHYSFS_LIB_TARGET physfs)
+ SET(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";physfs")
+ENDIF(PHYSFS_BUILD_SHARED)
+
+IF(NOT PHYSFS_BUILD_SHARED AND NOT PHYSFS_BUILD_STATIC)
+ MESSAGE(FATAL "Both shared and static libraries are disabled!")
+ENDIF(NOT PHYSFS_BUILD_SHARED AND NOT PHYSFS_BUILD_STATIC)
+
+# CMake FAQ says I need this...
+IF(PHYSFS_BUILD_SHARED AND PHYSFS_BUILD_STATIC)
+ SET_TARGET_PROPERTIES(physfs PROPERTIES CLEAN_DIRECT_OUTPUT 1)
+ SET_TARGET_PROPERTIES(physfs-static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
+ENDIF(PHYSFS_BUILD_SHARED AND PHYSFS_BUILD_STATIC)
+
+OPTION(PHYSFS_BUILD_TEST "Build stdio test program." TRUE)
+MARK_AS_ADVANCED(PHYSFS_BUILD_TEST)
+IF(PHYSFS_BUILD_TEST)
+ CHECK_INCLUDE_FILE(readline/readline.h HAVE_READLINE_H)
+ CHECK_INCLUDE_FILE(readline/history.h HAVE_HISTORY_H)
+ IF(HAVE_READLINE_H AND HAVE_HISTORY_H)
+ SET(CMAKE_REQUIRED_LIBRARIES curses)
+ CHECK_LIBRARY_EXISTS("readline" "readline" "" HAVE_LIBREADLINE)
+ CHECK_LIBRARY_EXISTS("readline" "history" "" HAVE_LIBHISTORY)
+ IF(HAVE_LIBREADLINE AND HAVE_LIBHISTORY)
+ SET(HAVE_SYSTEM_READLINE TRUE)
+ SET(TEST_PHYSFS_LIBS ${TEST_PHYSFS_LIBS} " " readline curses)
+ ADD_DEFINITIONS(-DPHYSFS_HAVE_READLINE=1)
+ ENDIF(HAVE_LIBREADLINE AND HAVE_LIBHISTORY)
+ ENDIF(HAVE_READLINE_H AND HAVE_HISTORY_H)
+ ADD_EXECUTABLE(test_physfs test/test_physfs.c)
+ TARGET_LINK_LIBRARIES(test_physfs ${PHYSFS_LIB_TARGET} ${TEST_PHYSFS_LIBS} ${OTHER_LDFLAGS})
+ SET(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";test_physfs")
+ENDIF(PHYSFS_BUILD_TEST)
+
+OPTION(PHYSFS_BUILD_WX_TEST "Build wxWidgets test program." TRUE)
+MARK_AS_ADVANCED(PHYSFS_BUILD_WX_TEST)
+IF(PHYSFS_BUILD_WX_TEST)
+ SET(wxWidgets_USE_LIBS base core adv)
+ SET(wxWidgets_INCLUDE_DIRS_NO_SYSTEM 1)
+ FIND_PACKAGE(wxWidgets)
+ IF(wxWidgets_FOUND)
+ INCLUDE(${wxWidgets_USE_FILE})
+ ADD_EXECUTABLE(wxtest_physfs test/wxtest_physfs.cpp)
+ SET_SOURCE_FILES_PROPERTIES(test/wxtest_physfs.cpp COMPILE_FLAGS ${wxWidgets_CXX_FLAGS})
+ TARGET_LINK_LIBRARIES(wxtest_physfs ${PHYSFS_LIB_TARGET} ${wxWidgets_LIBRARIES} ${OTHER_LDFLAGS})
+ SET(PHYSFS_INSTALL_TARGETS ${PHYSFS_INSTALL_TARGETS} ";wxtest_physfs")
+ ELSE(wxWidgets_FOUND)
+ MESSAGE(STATUS "wxWidgets not found. Disabling wx test app.")
+ SET(PHYSFS_BUILD_WX_TEST FALSE)
+ ENDIF(wxWidgets_FOUND)
+ENDIF(PHYSFS_BUILD_WX_TEST)
+
+INSTALL(TARGETS ${PHYSFS_INSTALL_TARGETS}
+ RUNTIME DESTINATION bin
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib)
+INSTALL(FILES physfs.h DESTINATION include)
+
+FIND_PACKAGE(Doxygen)
+IF(DOXYGEN_FOUND)
+ ADD_CUSTOM_TARGET(docs ${DOXYGEN_EXECUTABLE} COMMENT "Building documentation")
+ELSE(DOXYGEN_FOUND)
+ MESSAGE(STATUS "Doxygen not found. You won't be able to build documentation.")
+ENDIF(DOXYGEN_FOUND)
+
+IF(UNIX)
+ ADD_CUSTOM_TARGET(dist ./extras/makedist.sh ${PHYSFS_VERSION} COMMENT "Building source tarball")
+ENDIF(UNIX)
+
+MACRO(MESSAGE_BOOL_OPTION _NAME _VALUE)
+ IF(${_VALUE})
+ MESSAGE(STATUS " ${_NAME}: enabled")
+ ELSE(${_VALUE})
+ MESSAGE(STATUS " ${_NAME}: disabled")
+ ENDIF(${_VALUE})
+ENDMACRO(MESSAGE_BOOL_OPTION)
+
+MESSAGE(STATUS "PhysicsFS will build with the following options:")
+MESSAGE_BOOL_OPTION("ZIP support" PHYSFS_ARCHIVE_ZIP)
+MESSAGE_BOOL_OPTION("7zip support" PHYSFS_ARCHIVE_7Z)
+MESSAGE_BOOL_OPTION("GRP support" PHYSFS_ARCHIVE_GRP)
+MESSAGE_BOOL_OPTION("WAD support" PHYSFS_ARCHIVE_WAD)
+MESSAGE_BOOL_OPTION("HOG support" PHYSFS_ARCHIVE_HOG)
+MESSAGE_BOOL_OPTION("MVL support" PHYSFS_ARCHIVE_MVL)
+MESSAGE_BOOL_OPTION("QPAK support" PHYSFS_ARCHIVE_QPAK)
+MESSAGE_BOOL_OPTION("CD-ROM drive support" PHYSFS_HAVE_CDROM_SUPPORT)
+MESSAGE_BOOL_OPTION("Thread safety" PHYSFS_HAVE_THREAD_SUPPORT)
+MESSAGE_BOOL_OPTION("Build own zlib" PHYSFS_INTERNAL_ZLIB)
+MESSAGE_BOOL_OPTION("Build static library" PHYSFS_BUILD_STATIC)
+MESSAGE_BOOL_OPTION("Build shared library" PHYSFS_BUILD_SHARED)
+MESSAGE_BOOL_OPTION("Build wxWidgets test program" PHYSFS_BUILD_WX_TEST)
+MESSAGE_BOOL_OPTION("Build stdio test program" PHYSFS_BUILD_TEST)
+IF(PHYSFS_BUILD_TEST)
+ MESSAGE_BOOL_OPTION(" Use readline in test program" HAVE_SYSTEM_READLINE)
+ENDIF(PHYSFS_BUILD_TEST)
+
+# end of CMakeLists.txt ...
+
--- /dev/null
+Maintainer and general codemonkey:
+ Ryan C. Gordon
+
+Tons of win32 help:
+ Adam Gates
+
+More win32 hacking:
+ Gregory S. Read
+
+Fixes for missing current working directories,
+PHYSFS_setSaneConfig() improvements,
+other bugfixes:
+ David Hedbor
+
+Darwin support:
+ Patrick Stein
+
+configure fixes,
+RPM specfile:
+ Edward Rudd
+
+GetLastModTime API,
+other stuff:
+ John R. Hall
+
+Various support, fixes and suggestions:
+ Alexander Pipelka
+
+Russian translation,
+Ruby bindings,
+QPAK archiver:
+ Ed Sinjiashvili
+
+French translation:
+ Stéphane Peter
+
+Debian package support:
+ Colin Bayer
+
+"abs-file.h" in "extras" dir:
+ Adam D. Moss
+
+WinCE port and other Win32 patches:
+ Corona688
+
+German translation:
+ Michael Renner
+
+Apple Project Builder support,
+Mac OS X improvements:
+ Eric Wing
+
+HOG archiver,
+MVL archiver:
+ Bradley Bell
+
+MIX archiver:
+ Sebastian Steinhauer
+
+Bug fixes:
+ Tolga Dalman
+
+Initial PHYSFS_mount() work:
+ Philip D. Bober
+
+Brazillian Portuguese translation:
+ Danny Angelo Carminati Grein
+
+Spanish translation:
+ Pedro J. Pérez
+
+MacOS Classic fixes,
+MPW support,
+bug fixes:
+ Chris Taylor
+
+Mingw support,
+General bug fixes:
+ Matze Braun
+
+Bug fixes:
+ Jörg Walter
+
+Windows .rc file,
+7zip/lzma archiver:
+ Dennis Schridde
+
+OS/2 updates:
+ Dave Yeo
+
+Other stuff:
+ Your name here! Patches go to icculus@icculus.org ...
+
+/* end of CREDITS.txt ... */
+
--- /dev/null
+# Doxyfile 1.3.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = physfs
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 1.1.0
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = docs
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
+# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese,
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
+# only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = physfs.h
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output dir.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimised for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assigments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED = DOXYGEN_SHOULD_IGNORE_THIS=1 \
+ __EXPORT__=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse the
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS = NO
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = NO
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = NO
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similiar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes that
+# lay further from the root node will be omitted. Note that setting this option to
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that a graph may be further truncated if the graph's image dimensions are
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT).
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
--- /dev/null
+
+The latest PhysicsFS information and releases can be found at:
+ http://icculus.org/physfs/
+
+Building is (ahem) very easy.
+
+
+ALL PLATFORMS:
+
+Please understand your rights and mine: read the text file LICENSE.txt in the
+ root of the source tree. If you can't abide by it, delete this source tree
+ now. The license is extremely liberal, even to closed-source, commercial
+ applications.
+
+If you've got Doxygen (http://www.doxygen.org/) installed, you can run it
+ without any command line arguments in the root of the source tree to generate
+ the API reference (or build the "docs" target from your build system). This
+ is optional. You can browse the API docs online here:
+
+ http://icculus.org/physfs/docs/
+
+
+
+
+UNIX:
+
+You will need CMake (http://www.cmake.org/) 2.4 or later installed.
+
+Run "cmake ." in the root of the source directory to generate Makefiles.
+ You can then run "ccmake ." and customize the build, but the defaults are
+ probably okay. You can have CMake generate KDevelop project files if you
+ prefer these.
+
+Run "make". PhysicsFS will now build.
+
+As root, run "make install".
+ If you get sick of the library, run "xargs rm < install_manifest.txt" as root
+ and it will remove all traces of the library from the system paths.
+
+Primary Unix development is done with GNU/Linux, but PhysicsFS is known to
+ work out of the box with several flavors of Unix. It it doesn't work, patches
+ to get it running can be sent to icculus@icculus.org.
+
+
+
+BeOS:
+
+Use the "Unix" instructions, above. The CMake port to BeOS is fairly new at
+ the time of this writing, but it works. You can get a build of CMake from
+ bebits.com or build it yourself from source from cmake.org.
+
+
+
+Windows:
+
+If building with CygWin, mingw32 or something else that uses the GNU
+ toolchain, follow the Unix instructions, above.
+
+If you want to use Visual Studio, nmake, or the Platform SDK, you will need
+ CMake (http://www.cmake.org/) 2.4 or later installed. Point CMake at the
+ CMakeLists.txt file in the root of the source directory and hit the
+ "Configure" button. After telling it what type of compiler you are targeting
+ (Borland, Visual Studio, etc), CMake will process for while and then give you
+ a list of options you can change (what archivers you want to support, etc).
+ If you aren't sure, the defaults are probably fine. Hit the "Configure"
+ button again, then "OK" once configuration has completed with options that
+ match your liking. Now project files for your favorite programming
+ environment will be generated for you in the directory you specified.
+ Go there and use them to build PhysicsFS.
+
+PhysicsFS will only link directly against system libraries that have existed
+ since Windows 95 and Windows NT 3.51. If there's a newer API we want to use,
+ we try to dynamically load it at runtime and fallback to a reasonable
+ behaviour when we can't find it...this is used for Unicode support and
+ locating user-specific directories, etc.
+
+PhysicsFS has not been tested on 64-bit Windows, but probably works. There is
+ no 16-bit Windows support at all. Reports of success and problems can go to
+ Ryan at icculus@icculus.org ...
+
+If someone is willing to maintain prebuilt PhysicsFS DLLs, I'd like to hear
+from you; send an email to icculus@icculus.org ...
+
+
+
+PocketPC/WindowsCE:
+
+Code exists for PocketPC support, and there are shipping titles that used
+ PhysicsFS 1.0 on PocketPC...but it isn't tested in 2.0, and is probably
+ broken with the new build system. Please send patches.
+
+
+
+MAC OS 8/9:
+
+Classic Mac OS support has been dropped in PhysicsFS 2.0. Apple hasn't updated
+ pre-OSX versions in almost a decade at this point, none of the hardware
+ they've shipped will boot it for almost as many years, and finding
+ developer tools for it is becoming almost impossible. As the switch to Intel
+ hardware has removed the "Classic" emulation environment, it was time to
+ remove support from PhysicsFS. That being said, the PhysicsFS 1.0 branch can
+ still target back to Mac OS 8.5, so you can use that if you need support for
+ this legacy OS. We still very much support Mac OS X, though: see below.
+
+
+
+MAC OS X:
+
+You will need CMake (http://www.cmake.org/) 2.4 or later installed.
+
+You can either generate a Unix makefile with CMake, or generate an Xcode
+ project, whichever makes you more comfortable.
+
+PowerPC and Intel Macs should both be supported.
+
+If someone is willing to maintain prebuilt PhysicsFS Shared Libraries for
+ Mac OS X, I'd like to hear from you; send an email to icculus@icculus.org.
+
+
+
+OS/2:
+
+You need Innotek GCC and libc installed (or kLIBC). I tried this on a stock
+ Warp 4 install, no fixpaks. You need to install link386.exe (Selective
+ Install, "link object modules" option). Once klibc and GCC are installed
+ correctly, unpack the source to PhysicsFS and run the script
+ file "makeos2.cmd". I know this isn't ideal, but I wanted to have this build
+ without users having to hunt down a "make" program.
+
+Someone please port CMake to OS/2. Ideally I'd like to be able to target
+ Innotek GCC and OpenWatcom with CMake.
+
+If someone is willing to maintain prebuilt PhysicsFS Shared Libraries for
+ OS/2, I'd like to hear from you; send an email to icculus@icculus.org.
+
+
+
+OTHER PLATFORMS:
+
+Many Unix-like platforms might "just work" with CMake. Some of these platforms
+ are known to have worked at one time, but have not been heavily tested, if
+ tested at all. PhysicsFS is, as far as we know, 64-bit and byteorder clean,
+ and is known to compile on several compilers across many platforms. To
+ implement a new platform or archiver, please read the heavily-commented
+ physfs_internal.h and look in the platform/ and archiver/ directories for
+ examples.
+
+--ryan. (icculus@icculus.org)
+
--- /dev/null
+
+ Copyright (c) 2003 Ryan C. Gordon and others.
+
+ This software is provided 'as-is', without any express or implied warranty.
+ In no event will the authors be held liable for any damages arising from
+ the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software in a
+ product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Ryan C. Gordon <icculus@icculus.org>
+
+
+(Please note that versions of PhysicsFS prior to 0.1.9 are licensed under
+the GNU Lesser General Public License, which restricts you significantly more.
+For your own safety, please make sure you've got 0.1.9 or later if you plan
+to use physfs in a commercial or closed-source project.)
+
--- /dev/null
+Stuff that needs to be done and wishlist:
+
+These are in no particular order.
+Some might be dupes, some might be done already.
+
+UNICODE:
+- OS/2: Codepages. No full Unicode in the filesystem, but we can probably make
+ a conversion effort.
+
+
+Stuff:
+- Other archivers: perhaps tar(.gz|.bz2), RPM, ARJ, etc. These are less
+ important, since streaming archives aren't of much value to games (which
+ is why zipfiles are king: random access), but it could have uses for, say,
+ an installer/updater.
+- Reduce malloc() pressure all over the place. We fragment memory like mad.
+- profile string list interpolation.
+- We have two different ways to find dir entries in zip.c.
+- Do symlinks in zip archiver work when they point to dirs?
+- Enable more warnings?
+- Use __cdecl in physfs.h?
+- Look for FIXMEs (many marked with "!!!" in comments).
+- Find some way to relax or remove the security model for external tools.
+- OSX shouldn't use ~/.app for userdir.
+- fscanf and fprintf support in extras dir.
+- Why do we call it openArchive and dirClose?
+- Sanity check byte order at runtime.
+- Memory locking?
+- Find a better name than dvoid and fvoid.
+- Can windows.c and pocketpc.c get merged?
+- There's so much cut-and-paste between archivers...can this be reduced?
+- General code audit.
+- Multiple write dirs with mount points?
+- Deprecate PHYSFS_setSaneConfig and move it to extras?
+- Why is physfsrwops.c cut-and-pasted into the ruby bindings?
+- Replace code from SDL...
+- Should file enumeration return an error or set error state?
+- Need "getmountpoint" command in test_physfs.c ...
+- Look for calloc() calls that aren't going through the allocation hooks.
+- Write up a simple HOWTO on embedding physicsfs in another project.
+- Archivers need abstracted i/o to read from memory or files (archives in archives?)
+- Probably other stuff. Requests and recommendations are welcome.
+
+// end of TODO.txt ...
+
--- /dev/null
+/*
+ * Standard directory I/O support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "physfs.h"
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+static PHYSFS_sint64 DIR_read(fvoid *opaque, void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ PHYSFS_sint64 retval;
+ retval = __PHYSFS_platformRead(opaque, buffer, objSize, objCount);
+ return(retval);
+} /* DIR_read */
+
+
+static PHYSFS_sint64 DIR_write(fvoid *opaque, const void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ PHYSFS_sint64 retval;
+ retval = __PHYSFS_platformWrite(opaque, buffer, objSize, objCount);
+ return(retval);
+} /* DIR_write */
+
+
+static int DIR_eof(fvoid *opaque)
+{
+ return(__PHYSFS_platformEOF(opaque));
+} /* DIR_eof */
+
+
+static PHYSFS_sint64 DIR_tell(fvoid *opaque)
+{
+ return(__PHYSFS_platformTell(opaque));
+} /* DIR_tell */
+
+
+static int DIR_seek(fvoid *opaque, PHYSFS_uint64 offset)
+{
+ return(__PHYSFS_platformSeek(opaque, offset));
+} /* DIR_seek */
+
+
+static PHYSFS_sint64 DIR_fileLength(fvoid *opaque)
+{
+ return(__PHYSFS_platformFileLength(opaque));
+} /* DIR_fileLength */
+
+
+static int DIR_fileClose(fvoid *opaque)
+{
+ /*
+ * we manually flush the buffer, since that's the place a close will
+ * most likely fail, but that will leave the file handle in an undefined
+ * state if it fails. Flush failures we can recover from.
+ */
+ BAIL_IF_MACRO(!__PHYSFS_platformFlush(opaque), NULL, 0);
+ BAIL_IF_MACRO(!__PHYSFS_platformClose(opaque), NULL, 0);
+ return(1);
+} /* DIR_fileClose */
+
+
+static int DIR_isArchive(const char *filename, int forWriting)
+{
+ /* directories ARE archives in this driver... */
+ return(__PHYSFS_platformIsDirectory(filename));
+} /* DIR_isArchive */
+
+
+static void *DIR_openArchive(const char *name, int forWriting)
+{
+ const char *dirsep = PHYSFS_getDirSeparator();
+ char *retval = NULL;
+ size_t namelen = strlen(name);
+ size_t seplen = strlen(dirsep);
+
+ /* !!! FIXME: when is this not called right before openArchive? */
+ BAIL_IF_MACRO(!DIR_isArchive(name, forWriting),
+ ERR_UNSUPPORTED_ARCHIVE, 0);
+
+ retval = allocator.Malloc(namelen + seplen + 1);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ /* make sure there's a dir separator at the end of the string */
+ strcpy(retval, name);
+ if (strcmp((name + namelen) - seplen, dirsep) != 0)
+ strcat(retval, dirsep);
+
+ return(retval);
+} /* DIR_openArchive */
+
+
+static void DIR_enumerateFiles(dvoid *opaque, const char *dname,
+ int omitSymLinks, PHYSFS_EnumFilesCallback cb,
+ const char *origdir, void *callbackdata)
+{
+ char *d = __PHYSFS_platformCvtToDependent((char *)opaque, dname, NULL);
+ if (d != NULL)
+ {
+ __PHYSFS_platformEnumerateFiles(d, omitSymLinks, cb,
+ origdir, callbackdata);
+ allocator.Free(d);
+ } /* if */
+} /* DIR_enumerateFiles */
+
+
+static int DIR_exists(dvoid *opaque, const char *name)
+{
+ char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
+ int retval;
+
+ BAIL_IF_MACRO(f == NULL, NULL, 0);
+ retval = __PHYSFS_platformExists(f);
+ allocator.Free(f);
+ return(retval);
+} /* DIR_exists */
+
+
+static int DIR_isDirectory(dvoid *opaque, const char *name, int *fileExists)
+{
+ char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
+ int retval = 0;
+
+ BAIL_IF_MACRO(d == NULL, NULL, 0);
+ *fileExists = __PHYSFS_platformExists(d);
+ if (*fileExists)
+ retval = __PHYSFS_platformIsDirectory(d);
+ allocator.Free(d);
+ return(retval);
+} /* DIR_isDirectory */
+
+
+static int DIR_isSymLink(dvoid *opaque, const char *name, int *fileExists)
+{
+ char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
+ int retval = 0;
+
+ BAIL_IF_MACRO(f == NULL, NULL, 0);
+ *fileExists = __PHYSFS_platformExists(f);
+ if (*fileExists)
+ retval = __PHYSFS_platformIsSymLink(f);
+ allocator.Free(f);
+ return(retval);
+} /* DIR_isSymLink */
+
+
+static PHYSFS_sint64 DIR_getLastModTime(dvoid *opaque,
+ const char *name,
+ int *fileExists)
+{
+ char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
+ PHYSFS_sint64 retval = -1;
+
+ BAIL_IF_MACRO(d == NULL, NULL, 0);
+ *fileExists = __PHYSFS_platformExists(d);
+ if (*fileExists)
+ retval = __PHYSFS_platformGetLastModTime(d);
+ allocator.Free(d);
+ return(retval);
+} /* DIR_getLastModTime */
+
+
+static fvoid *doOpen(dvoid *opaque, const char *name,
+ void *(*openFunc)(const char *filename),
+ int *fileExists)
+{
+ char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
+ void *rc = NULL;
+
+ BAIL_IF_MACRO(f == NULL, NULL, NULL);
+
+ if (fileExists != NULL)
+ {
+ *fileExists = __PHYSFS_platformExists(f);
+ if (!(*fileExists))
+ {
+ allocator.Free(f);
+ return(NULL);
+ } /* if */
+ } /* if */
+
+ rc = openFunc(f);
+ allocator.Free(f);
+
+ return((fvoid *) rc);
+} /* doOpen */
+
+
+static fvoid *DIR_openRead(dvoid *opaque, const char *fnm, int *exist)
+{
+ return(doOpen(opaque, fnm, __PHYSFS_platformOpenRead, exist));
+} /* DIR_openRead */
+
+
+static fvoid *DIR_openWrite(dvoid *opaque, const char *filename)
+{
+ return(doOpen(opaque, filename, __PHYSFS_platformOpenWrite, NULL));
+} /* DIR_openWrite */
+
+
+static fvoid *DIR_openAppend(dvoid *opaque, const char *filename)
+{
+ return(doOpen(opaque, filename, __PHYSFS_platformOpenAppend, NULL));
+} /* DIR_openAppend */
+
+
+static int DIR_remove(dvoid *opaque, const char *name)
+{
+ char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
+ int retval;
+
+ BAIL_IF_MACRO(f == NULL, NULL, 0);
+ retval = __PHYSFS_platformDelete(f);
+ allocator.Free(f);
+ return(retval);
+} /* DIR_remove */
+
+
+static int DIR_mkdir(dvoid *opaque, const char *name)
+{
+ char *f = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL);
+ int retval;
+
+ BAIL_IF_MACRO(f == NULL, NULL, 0);
+ retval = __PHYSFS_platformMkDir(f);
+ allocator.Free(f);
+ return(retval);
+} /* DIR_mkdir */
+
+
+static void DIR_dirClose(dvoid *opaque)
+{
+ allocator.Free(opaque);
+} /* DIR_dirClose */
+
+
+
+const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_DIR =
+{
+ "",
+ DIR_ARCHIVE_DESCRIPTION,
+ "Ryan C. Gordon <icculus@icculus.org>",
+ "http://icculus.org/physfs/",
+};
+
+
+
+const PHYSFS_Archiver __PHYSFS_Archiver_DIR =
+{
+ &__PHYSFS_ArchiveInfo_DIR,
+ DIR_isArchive, /* isArchive() method */
+ DIR_openArchive, /* openArchive() method */
+ DIR_enumerateFiles, /* enumerateFiles() method */
+ DIR_exists, /* exists() method */
+ DIR_isDirectory, /* isDirectory() method */
+ DIR_isSymLink, /* isSymLink() method */
+ DIR_getLastModTime, /* getLastModTime() method */
+ DIR_openRead, /* openRead() method */
+ DIR_openWrite, /* openWrite() method */
+ DIR_openAppend, /* openAppend() method */
+ DIR_remove, /* remove() method */
+ DIR_mkdir, /* mkdir() method */
+ DIR_dirClose, /* dirClose() method */
+ DIR_read, /* read() method */
+ DIR_write, /* write() method */
+ DIR_eof, /* eof() method */
+ DIR_tell, /* tell() method */
+ DIR_seek, /* seek() method */
+ DIR_fileLength, /* fileLength() method */
+ DIR_fileClose /* fileClose() method */
+};
+
+/* end of dir.c ... */
+
--- /dev/null
+/*
+ * GRP support routines for PhysicsFS.
+ *
+ * This driver handles BUILD engine archives ("groupfiles"). This format
+ * (but not this driver) was put together by Ken Silverman.
+ *
+ * The format is simple enough. In Ken's words:
+ *
+ * What's the .GRP file format?
+ *
+ * The ".grp" file format is just a collection of a lot of files stored
+ * into 1 big one. I tried to make the format as simple as possible: The
+ * first 12 bytes contains my name, "KenSilverman". The next 4 bytes is
+ * the number of files that were compacted into the group file. Then for
+ * each file, there is a 16 byte structure, where the first 12 bytes are
+ * the filename, and the last 4 bytes are the file's size. The rest of
+ * the group file is just the raw data packed one after the other in the
+ * same order as the list of files.
+ *
+ * (That info is from http://www.advsys.net/ken/build.htm ...)
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#if (defined PHYSFS_SUPPORTS_GRP)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "physfs.h"
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+typedef struct
+{
+ char name[13];
+ PHYSFS_uint32 startPos;
+ PHYSFS_uint32 size;
+} GRPentry;
+
+typedef struct
+{
+ char *filename;
+ PHYSFS_sint64 last_mod_time;
+ PHYSFS_uint32 entryCount;
+ GRPentry *entries;
+} GRPinfo;
+
+typedef struct
+{
+ void *handle;
+ GRPentry *entry;
+ PHYSFS_uint32 curPos;
+} GRPfileinfo;
+
+
+static void GRP_dirClose(dvoid *opaque)
+{
+ GRPinfo *info = ((GRPinfo *) opaque);
+ allocator.Free(info->filename);
+ allocator.Free(info->entries);
+ allocator.Free(info);
+} /* GRP_dirClose */
+
+
+static PHYSFS_sint64 GRP_read(fvoid *opaque, void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ GRPfileinfo *finfo = (GRPfileinfo *) opaque;
+ GRPentry *entry = finfo->entry;
+ PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
+ PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
+ PHYSFS_sint64 rc;
+
+ if (objsLeft < objCount)
+ objCount = objsLeft;
+
+ rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
+ if (rc > 0)
+ finfo->curPos += (PHYSFS_uint32) (rc * objSize);
+
+ return(rc);
+} /* GRP_read */
+
+
+static PHYSFS_sint64 GRP_write(fvoid *opaque, const void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
+} /* GRP_write */
+
+
+static int GRP_eof(fvoid *opaque)
+{
+ GRPfileinfo *finfo = (GRPfileinfo *) opaque;
+ GRPentry *entry = finfo->entry;
+ return(finfo->curPos >= entry->size);
+} /* GRP_eof */
+
+
+static PHYSFS_sint64 GRP_tell(fvoid *opaque)
+{
+ return(((GRPfileinfo *) opaque)->curPos);
+} /* GRP_tell */
+
+
+static int GRP_seek(fvoid *opaque, PHYSFS_uint64 offset)
+{
+ GRPfileinfo *finfo = (GRPfileinfo *) opaque;
+ GRPentry *entry = finfo->entry;
+ int rc;
+
+ BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
+ rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
+ if (rc)
+ finfo->curPos = (PHYSFS_uint32) offset;
+
+ return(rc);
+} /* GRP_seek */
+
+
+static PHYSFS_sint64 GRP_fileLength(fvoid *opaque)
+{
+ GRPfileinfo *finfo = (GRPfileinfo *) opaque;
+ return((PHYSFS_sint64) finfo->entry->size);
+} /* GRP_fileLength */
+
+
+static int GRP_fileClose(fvoid *opaque)
+{
+ GRPfileinfo *finfo = (GRPfileinfo *) opaque;
+ BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
+ allocator.Free(finfo);
+ return(1);
+} /* GRP_fileClose */
+
+
+static int grp_open(const char *filename, int forWriting,
+ void **fh, PHYSFS_uint32 *count)
+{
+ PHYSFS_uint8 buf[12];
+
+ *fh = NULL;
+ BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
+
+ *fh = __PHYSFS_platformOpenRead(filename);
+ BAIL_IF_MACRO(*fh == NULL, NULL, 0);
+
+ if (__PHYSFS_platformRead(*fh, buf, 12, 1) != 1)
+ goto openGrp_failed;
+
+ if (memcmp(buf, "KenSilverman", 12) != 0)
+ {
+ __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
+ goto openGrp_failed;
+ } /* if */
+
+ if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
+ goto openGrp_failed;
+
+ *count = PHYSFS_swapULE32(*count);
+
+ return(1);
+
+openGrp_failed:
+ if (*fh != NULL)
+ __PHYSFS_platformClose(*fh);
+
+ *count = -1;
+ *fh = NULL;
+ return(0);
+} /* grp_open */
+
+
+static int GRP_isArchive(const char *filename, int forWriting)
+{
+ void *fh;
+ PHYSFS_uint32 fileCount;
+ int retval = grp_open(filename, forWriting, &fh, &fileCount);
+
+ if (fh != NULL)
+ __PHYSFS_platformClose(fh);
+
+ return(retval);
+} /* GRP_isArchive */
+
+
+static int grp_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ GRPentry *a = (GRPentry *) _a;
+ return(strcmp(a[one].name, a[two].name));
+} /* grp_entry_cmp */
+
+
+static void grp_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ GRPentry tmp;
+ GRPentry *first = &(((GRPentry *) _a)[one]);
+ GRPentry *second = &(((GRPentry *) _a)[two]);
+ memcpy(&tmp, first, sizeof (GRPentry));
+ memcpy(first, second, sizeof (GRPentry));
+ memcpy(second, &tmp, sizeof (GRPentry));
+} /* grp_entry_swap */
+
+
+static int grp_load_entries(const char *name, int forWriting, GRPinfo *info)
+{
+ void *fh = NULL;
+ PHYSFS_uint32 fileCount;
+ PHYSFS_uint32 location = 16; /* sizeof sig. */
+ GRPentry *entry;
+ char *ptr;
+
+ BAIL_IF_MACRO(!grp_open(name, forWriting, &fh, &fileCount), NULL, 0);
+ info->entryCount = fileCount;
+ info->entries = (GRPentry *) allocator.Malloc(sizeof(GRPentry)*fileCount);
+ if (info->entries == NULL)
+ {
+ __PHYSFS_platformClose(fh);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
+ } /* if */
+
+ location += (16 * fileCount);
+
+ for (entry = info->entries; fileCount > 0; fileCount--, entry++)
+ {
+ if (__PHYSFS_platformRead(fh, &entry->name, 12, 1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ entry->name[12] = '\0'; /* name isn't null-terminated in file. */
+ if ((ptr = strchr(entry->name, ' ')) != NULL)
+ *ptr = '\0'; /* trim extra spaces. */
+
+ if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ entry->size = PHYSFS_swapULE32(entry->size);
+ entry->startPos = location;
+ location += entry->size;
+ } /* for */
+
+ __PHYSFS_platformClose(fh);
+
+ __PHYSFS_sort(info->entries, info->entryCount,
+ grp_entry_cmp, grp_entry_swap);
+ return(1);
+} /* grp_load_entries */
+
+
+static void *GRP_openArchive(const char *name, int forWriting)
+{
+ PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
+ GRPinfo *info = (GRPinfo *) allocator.Malloc(sizeof (GRPinfo));
+
+ BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
+
+ memset(info, '\0', sizeof (GRPinfo));
+ info->filename = (char *) allocator.Malloc(strlen(name) + 1);
+ GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, GRP_openArchive_failed);
+
+ if (!grp_load_entries(name, forWriting, info))
+ goto GRP_openArchive_failed;
+
+ strcpy(info->filename, name);
+ info->last_mod_time = modtime;
+
+ return(info);
+
+GRP_openArchive_failed:
+ if (info != NULL)
+ {
+ if (info->filename != NULL)
+ allocator.Free(info->filename);
+ if (info->entries != NULL)
+ allocator.Free(info->entries);
+ allocator.Free(info);
+ } /* if */
+
+ return(NULL);
+} /* GRP_openArchive */
+
+
+static void GRP_enumerateFiles(dvoid *opaque, const char *dname,
+ int omitSymLinks, PHYSFS_EnumFilesCallback cb,
+ const char *origdir, void *callbackdata)
+{
+ /* no directories in GRP files. */
+ if (*dname == '\0')
+ {
+ GRPinfo *info = (GRPinfo *) opaque;
+ GRPentry *entry = info->entries;
+ PHYSFS_uint32 max = info->entryCount;
+ PHYSFS_uint32 i;
+
+ for (i = 0; i < max; i++, entry++)
+ cb(callbackdata, origdir, entry->name);
+ } /* if */
+} /* GRP_enumerateFiles */
+
+
+static GRPentry *grp_find_entry(GRPinfo *info, const char *name)
+{
+ char *ptr = strchr(name, '.');
+ GRPentry *a = info->entries;
+ PHYSFS_sint32 lo = 0;
+ PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
+ PHYSFS_sint32 middle;
+ int rc;
+
+ /*
+ * Rule out filenames to avoid unneeded processing...no dirs,
+ * big filenames, or extensions > 3 chars.
+ */
+ BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
+ BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
+ BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
+
+ while (lo <= hi)
+ {
+ middle = lo + ((hi - lo) / 2);
+ rc = strcmp(name, a[middle].name);
+ if (rc == 0) /* found it! */
+ return(&a[middle]);
+ else if (rc > 0)
+ lo = middle + 1;
+ else
+ hi = middle - 1;
+ } /* while */
+
+ BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
+} /* grp_find_entry */
+
+
+static int GRP_exists(dvoid *opaque, const char *name)
+{
+ return(grp_find_entry((GRPinfo *) opaque, name) != NULL);
+} /* GRP_exists */
+
+
+static int GRP_isDirectory(dvoid *opaque, const char *name, int *fileExists)
+{
+ *fileExists = GRP_exists(opaque, name);
+ return(0); /* never directories in a groupfile. */
+} /* GRP_isDirectory */
+
+
+static int GRP_isSymLink(dvoid *opaque, const char *name, int *fileExists)
+{
+ *fileExists = GRP_exists(opaque, name);
+ return(0); /* never symlinks in a groupfile. */
+} /* GRP_isSymLink */
+
+
+static PHYSFS_sint64 GRP_getLastModTime(dvoid *opaque,
+ const char *name,
+ int *fileExists)
+{
+ GRPinfo *info = (GRPinfo *) opaque;
+ PHYSFS_sint64 retval = -1;
+
+ *fileExists = (grp_find_entry(info, name) != NULL);
+ if (*fileExists) /* use time of GRP itself in the physical filesystem. */
+ retval = info->last_mod_time;
+
+ return(retval);
+} /* GRP_getLastModTime */
+
+
+static fvoid *GRP_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+{
+ GRPinfo *info = (GRPinfo *) opaque;
+ GRPfileinfo *finfo;
+ GRPentry *entry;
+
+ entry = grp_find_entry(info, fnm);
+ *fileExists = (entry != NULL);
+ BAIL_IF_MACRO(entry == NULL, NULL, NULL);
+
+ finfo = (GRPfileinfo *) allocator.Malloc(sizeof (GRPfileinfo));
+ BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ finfo->handle = __PHYSFS_platformOpenRead(info->filename);
+ if ( (finfo->handle == NULL) ||
+ (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
+ {
+ allocator.Free(finfo);
+ return(NULL);
+ } /* if */
+
+ finfo->curPos = 0;
+ finfo->entry = entry;
+ return(finfo);
+} /* GRP_openRead */
+
+
+static fvoid *GRP_openWrite(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* GRP_openWrite */
+
+
+static fvoid *GRP_openAppend(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* GRP_openAppend */
+
+
+static int GRP_remove(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* GRP_remove */
+
+
+static int GRP_mkdir(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* GRP_mkdir */
+
+
+const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP =
+{
+ "GRP",
+ GRP_ARCHIVE_DESCRIPTION,
+ "Ryan C. Gordon <icculus@icculus.org>",
+ "http://icculus.org/physfs/",
+};
+
+
+const PHYSFS_Archiver __PHYSFS_Archiver_GRP =
+{
+ &__PHYSFS_ArchiveInfo_GRP,
+ GRP_isArchive, /* isArchive() method */
+ GRP_openArchive, /* openArchive() method */
+ GRP_enumerateFiles, /* enumerateFiles() method */
+ GRP_exists, /* exists() method */
+ GRP_isDirectory, /* isDirectory() method */
+ GRP_isSymLink, /* isSymLink() method */
+ GRP_getLastModTime, /* getLastModTime() method */
+ GRP_openRead, /* openRead() method */
+ GRP_openWrite, /* openWrite() method */
+ GRP_openAppend, /* openAppend() method */
+ GRP_remove, /* remove() method */
+ GRP_mkdir, /* mkdir() method */
+ GRP_dirClose, /* dirClose() method */
+ GRP_read, /* read() method */
+ GRP_write, /* write() method */
+ GRP_eof, /* eof() method */
+ GRP_tell, /* tell() method */
+ GRP_seek, /* seek() method */
+ GRP_fileLength, /* fileLength() method */
+ GRP_fileClose /* fileClose() method */
+};
+
+#endif /* defined PHYSFS_SUPPORTS_GRP */
+
+/* end of grp.c ... */
+
--- /dev/null
+/*
+ * HOG support routines for PhysicsFS.
+ *
+ * This driver handles Descent I/II HOG archives.
+ *
+ * The format is very simple:
+ *
+ * The file always starts with the 3-byte signature "DHF" (Descent
+ * HOG file). After that the files of a HOG are just attached after
+ * another, divided by a 17 bytes header, which specifies the name
+ * and length (in bytes) of the forthcoming file! So you just read
+ * the header with its information of how big the following file is,
+ * and then skip exact that number of bytes to get to the next file
+ * in that HOG.
+ *
+ * char sig[3] = {'D', 'H', 'F'}; // "DHF"=Descent HOG File
+ *
+ * struct {
+ * char file_name[13]; // Filename, padded to 13 bytes with 0s
+ * int file_size; // filesize in bytes
+ * char data[file_size]; // The file data
+ * } FILE_STRUCT; // Repeated until the end of the file.
+ *
+ * (That info is from http://www.descent2.com/ddn/specs/hog/)
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Bradley Bell.
+ * Based on grp.c by Ryan C. Gordon.
+ */
+
+#if (defined PHYSFS_SUPPORTS_HOG)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "physfs.h"
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+/*
+ * One HOGentry is kept for each file in an open HOG archive.
+ */
+typedef struct
+{
+ char name[13];
+ PHYSFS_uint32 startPos;
+ PHYSFS_uint32 size;
+} HOGentry;
+
+/*
+ * One HOGinfo is kept for each open HOG archive.
+ */
+typedef struct
+{
+ char *filename;
+ PHYSFS_sint64 last_mod_time;
+ PHYSFS_uint32 entryCount;
+ HOGentry *entries;
+} HOGinfo;
+
+/*
+ * One HOGfileinfo is kept for each open file in a HOG archive.
+ */
+typedef struct
+{
+ void *handle;
+ HOGentry *entry;
+ PHYSFS_uint32 curPos;
+} HOGfileinfo;
+
+
+static void HOG_dirClose(dvoid *opaque)
+{
+ HOGinfo *info = ((HOGinfo *) opaque);
+ allocator.Free(info->filename);
+ allocator.Free(info->entries);
+ allocator.Free(info);
+} /* HOG_dirClose */
+
+
+static PHYSFS_sint64 HOG_read(fvoid *opaque, void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ HOGfileinfo *finfo = (HOGfileinfo *) opaque;
+ HOGentry *entry = finfo->entry;
+ PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
+ PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
+ PHYSFS_sint64 rc;
+
+ if (objsLeft < objCount)
+ objCount = objsLeft;
+
+ rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
+ if (rc > 0)
+ finfo->curPos += (PHYSFS_uint32) (rc * objSize);
+
+ return(rc);
+} /* HOG_read */
+
+
+static PHYSFS_sint64 HOG_write(fvoid *opaque, const void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
+} /* HOG_write */
+
+
+static int HOG_eof(fvoid *opaque)
+{
+ HOGfileinfo *finfo = (HOGfileinfo *) opaque;
+ HOGentry *entry = finfo->entry;
+ return(finfo->curPos >= entry->size);
+} /* HOG_eof */
+
+
+static PHYSFS_sint64 HOG_tell(fvoid *opaque)
+{
+ return(((HOGfileinfo *) opaque)->curPos);
+} /* HOG_tell */
+
+
+static int HOG_seek(fvoid *opaque, PHYSFS_uint64 offset)
+{
+ HOGfileinfo *finfo = (HOGfileinfo *) opaque;
+ HOGentry *entry = finfo->entry;
+ int rc;
+
+ BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
+ rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
+ if (rc)
+ finfo->curPos = (PHYSFS_uint32) offset;
+
+ return(rc);
+} /* HOG_seek */
+
+
+static PHYSFS_sint64 HOG_fileLength(fvoid *opaque)
+{
+ HOGfileinfo *finfo = (HOGfileinfo *) opaque;
+ return((PHYSFS_sint64) finfo->entry->size);
+} /* HOG_fileLength */
+
+
+static int HOG_fileClose(fvoid *opaque)
+{
+ HOGfileinfo *finfo = (HOGfileinfo *) opaque;
+ BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
+ allocator.Free(finfo);
+ return(1);
+} /* HOG_fileClose */
+
+
+static int hog_open(const char *filename, int forWriting,
+ void **fh, PHYSFS_uint32 *count)
+{
+ PHYSFS_uint8 buf[13];
+ PHYSFS_uint32 size;
+ PHYSFS_sint64 pos;
+
+ *count = 0;
+
+ *fh = NULL;
+ BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
+
+ *fh = __PHYSFS_platformOpenRead(filename);
+ BAIL_IF_MACRO(*fh == NULL, NULL, 0);
+
+ if (__PHYSFS_platformRead(*fh, buf, 3, 1) != 1)
+ goto openHog_failed;
+
+ if (memcmp(buf, "DHF", 3) != 0)
+ {
+ __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
+ goto openHog_failed;
+ } /* if */
+
+ while (1)
+ {
+ if (__PHYSFS_platformRead(*fh, buf, 13, 1) != 1)
+ break; /* eof here is ok */
+
+ if (__PHYSFS_platformRead(*fh, &size, 4, 1) != 1)
+ goto openHog_failed;
+
+ size = PHYSFS_swapULE32(size);
+
+ (*count)++;
+
+ /* Skip over entry... */
+ pos = __PHYSFS_platformTell(*fh);
+ if (pos == -1)
+ goto openHog_failed;
+ if (!__PHYSFS_platformSeek(*fh, pos + size))
+ goto openHog_failed;
+ } /* while */
+
+ /* Rewind to start of entries... */
+ if (!__PHYSFS_platformSeek(*fh, 3))
+ goto openHog_failed;
+
+ return(1);
+
+openHog_failed:
+ if (*fh != NULL)
+ __PHYSFS_platformClose(*fh);
+
+ *count = -1;
+ *fh = NULL;
+ return(0);
+} /* hog_open */
+
+
+static int HOG_isArchive(const char *filename, int forWriting)
+{
+ void *fh;
+ PHYSFS_uint32 fileCount;
+ int retval = hog_open(filename, forWriting, &fh, &fileCount);
+
+ if (fh != NULL)
+ __PHYSFS_platformClose(fh);
+
+ return(retval);
+} /* HOG_isArchive */
+
+
+static int hog_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ HOGentry *a = (HOGentry *) _a;
+ return(__PHYSFS_stricmpASCII(a[one].name, a[two].name));
+} /* hog_entry_cmp */
+
+
+static void hog_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ HOGentry tmp;
+ HOGentry *first = &(((HOGentry *) _a)[one]);
+ HOGentry *second = &(((HOGentry *) _a)[two]);
+ memcpy(&tmp, first, sizeof (HOGentry));
+ memcpy(first, second, sizeof (HOGentry));
+ memcpy(second, &tmp, sizeof (HOGentry));
+} /* hog_entry_swap */
+
+
+static int hog_load_entries(const char *name, int forWriting, HOGinfo *info)
+{
+ void *fh = NULL;
+ PHYSFS_uint32 fileCount;
+ HOGentry *entry;
+
+ BAIL_IF_MACRO(!hog_open(name, forWriting, &fh, &fileCount), NULL, 0);
+ info->entryCount = fileCount;
+ info->entries = (HOGentry *) allocator.Malloc(sizeof(HOGentry)*fileCount);
+ if (info->entries == NULL)
+ {
+ __PHYSFS_platformClose(fh);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
+ } /* if */
+
+ for (entry = info->entries; fileCount > 0; fileCount--, entry++)
+ {
+ if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ entry->size = PHYSFS_swapULE32(entry->size);
+ entry->startPos = (unsigned int) __PHYSFS_platformTell(fh);
+ if (entry->startPos == -1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ }
+
+ /* Skip over entry */
+ if (!__PHYSFS_platformSeek(fh, entry->startPos + entry->size))
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ }
+ } /* for */
+
+ __PHYSFS_platformClose(fh);
+
+ __PHYSFS_sort(info->entries, info->entryCount,
+ hog_entry_cmp, hog_entry_swap);
+ return(1);
+} /* hog_load_entries */
+
+
+static void *HOG_openArchive(const char *name, int forWriting)
+{
+ PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
+ HOGinfo *info = (HOGinfo *) allocator.Malloc(sizeof (HOGinfo));
+
+ BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
+ memset(info, '\0', sizeof (HOGinfo));
+ info->filename = (char *) allocator.Malloc(strlen(name) + 1);
+ GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, HOG_openArchive_failed);
+
+ if (!hog_load_entries(name, forWriting, info))
+ goto HOG_openArchive_failed;
+
+ strcpy(info->filename, name);
+ info->last_mod_time = modtime;
+
+ return(info);
+
+HOG_openArchive_failed:
+ if (info != NULL)
+ {
+ if (info->filename != NULL)
+ allocator.Free(info->filename);
+ if (info->entries != NULL)
+ allocator.Free(info->entries);
+ allocator.Free(info);
+ } /* if */
+
+ return(NULL);
+} /* HOG_openArchive */
+
+
+static void HOG_enumerateFiles(dvoid *opaque, const char *dname,
+ int omitSymLinks, PHYSFS_EnumFilesCallback cb,
+ const char *origdir, void *callbackdata)
+{
+ /* no directories in HOG files. */
+ if (*dname == '\0')
+ {
+ HOGinfo *info = (HOGinfo *) opaque;
+ HOGentry *entry = info->entries;
+ PHYSFS_uint32 max = info->entryCount;
+ PHYSFS_uint32 i;
+
+ for (i = 0; i < max; i++, entry++)
+ cb(callbackdata, origdir, entry->name);
+ } /* if */
+} /* HOG_enumerateFiles */
+
+
+static HOGentry *hog_find_entry(HOGinfo *info, const char *name)
+{
+ char *ptr = strchr(name, '.');
+ HOGentry *a = info->entries;
+ PHYSFS_sint32 lo = 0;
+ PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
+ PHYSFS_sint32 middle;
+ int rc;
+
+ /*
+ * Rule out filenames to avoid unneeded processing...no dirs,
+ * big filenames, or extensions > 3 chars.
+ */
+ BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
+ BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
+ BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
+
+ while (lo <= hi)
+ {
+ middle = lo + ((hi - lo) / 2);
+ rc = __PHYSFS_stricmpASCII(name, a[middle].name);
+ if (rc == 0) /* found it! */
+ return(&a[middle]);
+ else if (rc > 0)
+ lo = middle + 1;
+ else
+ hi = middle - 1;
+ } /* while */
+
+ BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
+} /* hog_find_entry */
+
+
+static int HOG_exists(dvoid *opaque, const char *name)
+{
+ return(hog_find_entry(((HOGinfo *) opaque), name) != NULL);
+} /* HOG_exists */
+
+
+static int HOG_isDirectory(dvoid *opaque, const char *name, int *fileExists)
+{
+ *fileExists = HOG_exists(opaque, name);
+ return(0); /* never directories in a groupfile. */
+} /* HOG_isDirectory */
+
+
+static int HOG_isSymLink(dvoid *opaque, const char *name, int *fileExists)
+{
+ *fileExists = HOG_exists(opaque, name);
+ return(0); /* never symlinks in a groupfile. */
+} /* HOG_isSymLink */
+
+
+static PHYSFS_sint64 HOG_getLastModTime(dvoid *opaque,
+ const char *name,
+ int *fileExists)
+{
+ HOGinfo *info = ((HOGinfo *) opaque);
+ PHYSFS_sint64 retval = -1;
+
+ *fileExists = (hog_find_entry(info, name) != NULL);
+ if (*fileExists) /* use time of HOG itself in the physical filesystem. */
+ retval = info->last_mod_time;
+
+ return(retval);
+} /* HOG_getLastModTime */
+
+
+static fvoid *HOG_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+{
+ HOGinfo *info = ((HOGinfo *) opaque);
+ HOGfileinfo *finfo;
+ HOGentry *entry;
+
+ entry = hog_find_entry(info, fnm);
+ *fileExists = (entry != NULL);
+ BAIL_IF_MACRO(entry == NULL, NULL, NULL);
+
+ finfo = (HOGfileinfo *) allocator.Malloc(sizeof (HOGfileinfo));
+ BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ finfo->handle = __PHYSFS_platformOpenRead(info->filename);
+ if ( (finfo->handle == NULL) ||
+ (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
+ {
+ allocator.Free(finfo);
+ return(NULL);
+ } /* if */
+
+ finfo->curPos = 0;
+ finfo->entry = entry;
+ return(finfo);
+} /* HOG_openRead */
+
+
+static fvoid *HOG_openWrite(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* HOG_openWrite */
+
+
+static fvoid *HOG_openAppend(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* HOG_openAppend */
+
+
+static int HOG_remove(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* HOG_remove */
+
+
+static int HOG_mkdir(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* HOG_mkdir */
+
+
+const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG =
+{
+ "HOG",
+ HOG_ARCHIVE_DESCRIPTION,
+ "Bradley Bell <btb@icculus.org>",
+ "http://icculus.org/physfs/",
+};
+
+
+const PHYSFS_Archiver __PHYSFS_Archiver_HOG =
+{
+ &__PHYSFS_ArchiveInfo_HOG,
+ HOG_isArchive, /* isArchive() method */
+ HOG_openArchive, /* openArchive() method */
+ HOG_enumerateFiles, /* enumerateFiles() method */
+ HOG_exists, /* exists() method */
+ HOG_isDirectory, /* isDirectory() method */
+ HOG_isSymLink, /* isSymLink() method */
+ HOG_getLastModTime, /* getLastModTime() method */
+ HOG_openRead, /* openRead() method */
+ HOG_openWrite, /* openWrite() method */
+ HOG_openAppend, /* openAppend() method */
+ HOG_remove, /* remove() method */
+ HOG_mkdir, /* mkdir() method */
+ HOG_dirClose, /* dirClose() method */
+ HOG_read, /* read() method */
+ HOG_write, /* write() method */
+ HOG_eof, /* eof() method */
+ HOG_tell, /* tell() method */
+ HOG_seek, /* seek() method */
+ HOG_fileLength, /* fileLength() method */
+ HOG_fileClose /* fileClose() method */
+};
+
+#endif /* defined PHYSFS_SUPPORTS_HOG */
+
+/* end of hog.c ... */
+
--- /dev/null
+/*
+ * LZMA support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file is written by Dennis Schridde, with some peeking at "7zMain.c"
+ * by Igor Pavlov.
+ */
+
+#if (defined PHYSFS_SUPPORTS_7Z)
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "physfs.h"
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+#ifndef _LZMA_IN_CB
+#define _LZMA_IN_CB
+/* Use callback for input data */
+#endif
+
+/* #define _LZMA_OUT_READ */
+/* Use read function for output data */
+
+#ifndef _LZMA_PROB32
+#define _LZMA_PROB32
+/* It can increase speed on some 32-bit CPUs,
+ but memory usage will be doubled in that case */
+#endif
+
+#ifndef _LZMA_SYSTEM_SIZE_T
+#define _LZMA_SYSTEM_SIZE_T
+/* Use system's size_t. You can use it to enable 64-bit sizes supporting */
+#endif
+
+#include "7zIn.h"
+#include "7zCrc.h"
+#include "7zExtract.h"
+
+
+/* 7z internal from 7zIn.c */
+int TestSignatureCandidate(Byte *testBytes);
+
+
+typedef struct _CFileInStream
+{
+ ISzInStream InStream;
+ void *File;
+} CFileInStream;
+
+/*
+ * In LZMA the archive is splited in blocks, those are called folders
+ * Set by LZMA_read()
+*/
+typedef struct _LZMAfolder
+{
+ PHYSFS_uint8 *cache; /* Cached folder */
+ PHYSFS_uint32 size; /* Size of folder */
+ PHYSFS_uint32 index; /* Index of folder in archive */
+ PHYSFS_uint32 references; /* Number of files using this block */
+} LZMAfolder;
+
+/*
+ * Set by LZMA_openArchive(), except folder which gets it's values
+ * in LZMA_read()
+ */
+typedef struct _LZMAarchive
+{
+ struct _LZMAentry *firstEntry; /* Used for cleanup on shutdown */
+ struct _LZMAentry *lastEntry;
+ LZMAfolder *folder; /* Array of folders */
+ CArchiveDatabaseEx db; /* For 7z: Database */
+ CFileInStream stream; /* For 7z: Input file incl. read and seek callbacks */
+} LZMAarchive;
+
+/* Set by LZMA_openRead(), except offset which is set by LZMA_read() */
+typedef struct _LZMAentry
+{
+ struct _LZMAentry *next; /* Used for cleanup on shutdown */
+ struct _LZMAentry *previous;
+ LZMAarchive *archive; /* Link to corresponding archive */
+ CFileItem *file; /* For 7z: File info, eg. name, size */
+ PHYSFS_uint32 fileIndex; /* Index of file in archive */
+ PHYSFS_uint32 folderIndex; /* Index of folder in archive */
+ size_t offset; /* Offset in folder */
+ PHYSFS_uint64 position; /* Current "virtual" position in file */
+} LZMAentry;
+
+
+/* Memory management implementations to be passed to 7z */
+
+static void *SzAllocPhysicsFS(size_t size)
+{
+ return ((size == 0) ? NULL : allocator.Malloc(size));
+} /* SzAllocPhysicsFS */
+
+
+static void SzFreePhysicsFS(void *address)
+{
+ if (address != NULL)
+ allocator.Free(address);
+} /* SzFreePhysicsFS */
+
+
+/* Filesystem implementations to be passed to 7z */
+
+#ifdef _LZMA_IN_CB
+
+#define kBufferSize (1 << 12)
+static Byte g_Buffer[kBufferSize]; /* !!! FIXME: not thread safe! */
+
+SZ_RESULT SzFileReadImp(void *object, void **buffer, size_t maxReqSize,
+ size_t *processedSize)
+{
+ CFileInStream *s = (CFileInStream *)object;
+ PHYSFS_sint64 processedSizeLoc;
+ if (maxReqSize > kBufferSize)
+ maxReqSize = kBufferSize;
+ processedSizeLoc = __PHYSFS_platformRead(s->File, g_Buffer, 1, maxReqSize);
+ *buffer = g_Buffer;
+ if (processedSize != NULL)
+ *processedSize = (size_t) processedSizeLoc;
+ return SZ_OK;
+} /* SzFileReadImp */
+
+#else
+
+SZ_RESULT SzFileReadImp(void *object, void *buffer, size_t size,
+ size_t *processedSize)
+{
+ CFileInStream *s = (CFileInStream *)object;
+ size_t processedSizeLoc = __PHYSFS_platformRead(s->File, buffer, 1, size);
+ if (processedSize != 0)
+ *processedSize = processedSizeLoc;
+ return SZ_OK;
+} /* SzFileReadImp */
+
+#endif
+
+SZ_RESULT SzFileSeekImp(void *object, CFileSize pos)
+{
+ CFileInStream *s = (CFileInStream *) object;
+ if (__PHYSFS_platformSeek(s->File, (PHYSFS_uint64) pos))
+ return SZ_OK;
+ return SZE_FAIL;
+} /* SzFileSeekImp */
+
+
+/*
+ * Find entry 'name' in 'archive' and report the 'index' back
+ */
+static int lzma_find_entry(LZMAarchive *archive, const char *name,
+ PHYSFS_uint32 *index)
+{
+ for (*index = 0; *index < archive->db.Database.NumFiles; (*index)++)
+ {
+ if (strcmp(archive->db.Database.Files[*index].Name, name) == 0)
+ return 1;
+ } /* for */
+
+ BAIL_MACRO(ERR_NO_SUCH_FILE, 0);
+} /* lzma_find_entry */
+
+
+/*
+ * Report the first file index of a directory
+ */
+static PHYSFS_sint32 lzma_find_start_of_dir(LZMAarchive *archive,
+ const char *path,
+ int stop_on_first_find)
+{
+ PHYSFS_sint32 lo = 0;
+ PHYSFS_sint32 hi = (PHYSFS_sint32) (archive->db.Database.NumFiles - 1);
+ PHYSFS_sint32 middle;
+ PHYSFS_uint32 dlen = strlen(path);
+ PHYSFS_sint32 retval = -1;
+ const char *name;
+ int rc;
+
+ if (*path == '\0') /* root dir? */
+ return(0);
+
+ if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */
+ dlen--;
+
+ while (lo <= hi)
+ {
+ middle = lo + ((hi - lo) / 2);
+ name = archive->db.Database.Files[middle].Name;
+ rc = strncmp(path, name, dlen);
+ if (rc == 0)
+ {
+ char ch = name[dlen];
+ if ('/' < ch) /* make sure this isn't just a substr match. */
+ rc = -1;
+ else if ('/' > ch)
+ rc = 1;
+ else
+ {
+ if (stop_on_first_find) /* Just checking dir's existance? */
+ return(middle);
+
+ if (name[dlen + 1] == '\0') /* Skip initial dir entry. */
+ return(middle + 1);
+
+ /* there might be more entries earlier in the list. */
+ retval = middle;
+ hi = middle - 1;
+ } /* else */
+ } /* if */
+
+ if (rc > 0)
+ lo = middle + 1;
+ else
+ hi = middle - 1;
+ } /* while */
+
+ return(retval);
+} /* lzma_find_start_of_dir */
+
+
+/*
+ * Wrap all 7z calls in this, so the physfs error state is set appropriately.
+ */
+static int lzma_err(SZ_RESULT rc)
+{
+ switch (rc)
+ {
+ case SZ_OK: /* Same as LZMA_RESULT_OK */
+ break;
+ case SZE_DATA_ERROR: /* Same as LZMA_RESULT_DATA_ERROR */
+ __PHYSFS_setError(ERR_DATA_ERROR);
+ break;
+ case SZE_OUTOFMEMORY:
+ __PHYSFS_setError(ERR_OUT_OF_MEMORY);
+ break;
+ case SZE_CRC_ERROR:
+ __PHYSFS_setError(ERR_CORRUPTED);
+ break;
+ case SZE_NOTIMPL:
+ __PHYSFS_setError(ERR_NOT_IMPLEMENTED);
+ break;
+ case SZE_FAIL:
+ __PHYSFS_setError(ERR_UNKNOWN_ERROR); /* !!! FIXME: right? */
+ break;
+ case SZE_ARCHIVE_ERROR:
+ __PHYSFS_setError(ERR_CORRUPTED); /* !!! FIXME: right? */
+ break;
+ default:
+ __PHYSFS_setError(ERR_UNKNOWN_ERROR);
+ } /* switch */
+
+ return(rc);
+} /* lzma_err */
+
+
+static PHYSFS_sint64 LZMA_read(fvoid *opaque, void *outBuffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ LZMAentry *entry = (LZMAentry *) opaque;
+
+ PHYSFS_sint64 wantedSize = objSize*objCount;
+ PHYSFS_sint64 remainingSize = entry->file->Size - entry->position;
+
+ size_t fileSize;
+ ISzAlloc allocImp;
+ ISzAlloc allocTempImp;
+
+ BAIL_IF_MACRO(wantedSize == 0, NULL, 0); /* quick rejection. */
+ BAIL_IF_MACRO(remainingSize == 0, ERR_PAST_EOF, 0);
+
+ if (remainingSize < wantedSize)
+ {
+ wantedSize = remainingSize - (remainingSize % objSize);
+ objCount = (PHYSFS_uint32) (remainingSize / objSize);
+ BAIL_IF_MACRO(objCount == 0, ERR_PAST_EOF, 0); /* quick rejection. */
+ __PHYSFS_setError(ERR_PAST_EOF); /* this is always true here. */
+ } /* if */
+
+ /* Prepare callbacks for 7z */
+ allocImp.Alloc = SzAllocPhysicsFS;
+ allocImp.Free = SzFreePhysicsFS;
+
+ allocTempImp.Alloc = SzAllocPhysicsFS;
+ allocTempImp.Free = SzFreePhysicsFS;
+
+ /* Only decompress the folder if it is not allready cached */
+ if (entry->archive->folder[entry->folderIndex].cache == NULL)
+ {
+ size_t tmpsize = entry->archive->folder[entry->folderIndex].size;
+ int rc = lzma_err(SzExtract(
+ &entry->archive->stream.InStream, /* compressed data */
+ &entry->archive->db,
+ entry->fileIndex,
+ /* Index of cached folder, will be changed by SzExtract */
+ &entry->archive->folder[entry->folderIndex].index,
+ /* Cache for decompressed folder, allocated/freed by SzExtract */
+ &entry->archive->folder[entry->folderIndex].cache,
+ /* Size of cache, will be changed by SzExtract */
+ &tmpsize,
+ /* Offset of this file inside the cache, set by SzExtract */
+ &entry->offset,
+ &fileSize, /* Size of this file */
+ &allocImp,
+ &allocTempImp));
+
+ entry->archive->folder[entry->folderIndex].size = tmpsize;
+ if (rc != SZ_OK)
+ return -1;
+ } /* if */
+
+ /* Copy wanted bytes over from cache to outBuffer */
+/* !!! FIXME: strncpy for non-string data? */
+ strncpy(outBuffer,
+ (void*) (entry->archive->folder[entry->folderIndex].cache +
+ entry->offset + entry->position),
+ (size_t) wantedSize);
+ entry->position += wantedSize;
+ return objCount;
+} /* LZMA_read */
+
+
+static PHYSFS_sint64 LZMA_write(fvoid *opaque, const void *buf,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
+} /* LZMA_write */
+
+
+static int LZMA_eof(fvoid *opaque)
+{
+ LZMAentry *entry = (LZMAentry *) opaque;
+ return (entry->position >= entry->file->Size);
+} /* LZMA_eof */
+
+
+static PHYSFS_sint64 LZMA_tell(fvoid *opaque)
+{
+ LZMAentry *entry = (LZMAentry *) opaque;
+ return (entry->position);
+} /* LZMA_tell */
+
+
+static int LZMA_seek(fvoid *opaque, PHYSFS_uint64 offset)
+{
+ LZMAentry *entry = (LZMAentry *) opaque;
+
+ BAIL_IF_MACRO(offset < 0, ERR_SEEK_OUT_OF_RANGE, 0);
+ BAIL_IF_MACRO(offset > entry->file->Size, ERR_PAST_EOF, 0);
+
+ entry->position = offset;
+ return 1;
+} /* LZMA_seek */
+
+
+static PHYSFS_sint64 LZMA_fileLength(fvoid *opaque)
+{
+ LZMAentry *entry = (LZMAentry *) opaque;
+ return (entry->file->Size);
+} /* LZMA_fileLength */
+
+
+static int LZMA_fileClose(fvoid *opaque)
+{
+ LZMAentry *entry = (LZMAentry *) opaque;
+
+ /* Fix archive */
+ if (entry == entry->archive->firstEntry)
+ entry->archive->firstEntry = entry->next;
+ if (entry == entry->archive->lastEntry)
+ entry->archive->lastEntry = entry->previous;
+
+ /* Fix neighbours */
+ if (entry->previous != NULL)
+ entry->previous->next = entry->next;
+ if (entry->next != NULL)
+ entry->next->previous = entry->previous;
+
+ entry->archive->folder[entry->folderIndex].references--;
+ if (entry->archive->folder[entry->folderIndex].references == 0)
+ {
+ allocator.Free(entry->archive->folder[entry->folderIndex].cache);
+ entry->archive->folder[entry->folderIndex].cache = NULL;
+ }
+
+ allocator.Free(entry);
+ entry = NULL;
+
+ return(1);
+} /* LZMA_fileClose */
+
+
+static int LZMA_isArchive(const char *filename, int forWriting)
+{
+ PHYSFS_uint8 sig[k7zSignatureSize];
+ PHYSFS_uint8 res;
+ void *in;
+
+ BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
+
+ in = __PHYSFS_platformOpenRead(filename);
+ BAIL_IF_MACRO(in == NULL, NULL, 0);
+
+ if (__PHYSFS_platformRead(in, sig, k7zSignatureSize, 1) != 1)
+ BAIL_MACRO(NULL, 0);
+
+ /* Test whether sig is the 7z signature */
+ res = TestSignatureCandidate(sig);
+
+ __PHYSFS_platformClose(in);
+
+ return res;
+} /* LZMA_isArchive */
+
+
+static void *LZMA_openArchive(const char *name, int forWriting)
+{
+ PHYSFS_uint64 len;
+ LZMAarchive *archive = NULL;
+ ISzAlloc allocImp;
+ ISzAlloc allocTempImp;
+
+ BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
+ BAIL_IF_MACRO(!LZMA_isArchive(name,forWriting), ERR_UNSUPPORTED_ARCHIVE, 0);
+
+ archive = (LZMAarchive *) allocator.Malloc(sizeof (LZMAarchive));
+ BAIL_IF_MACRO(archive == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ archive->firstEntry = NULL;
+ archive->lastEntry = NULL;
+
+ if ((archive->stream.File = __PHYSFS_platformOpenRead(name)) == NULL)
+ {
+ allocator.Free(archive);
+ return NULL;
+ } /* if */
+
+ /* Prepare structs for 7z */
+ archive->stream.InStream.Read = SzFileReadImp;
+ archive->stream.InStream.Seek = SzFileSeekImp;
+
+ allocImp.Alloc = SzAllocPhysicsFS;
+ allocImp.Free = SzFreePhysicsFS;
+
+ allocTempImp.Alloc = SzAllocPhysicsFS;
+ allocTempImp.Free = SzFreePhysicsFS;
+
+ InitCrcTable();
+ SzArDbExInit(&archive->db);
+ if (lzma_err(SzArchiveOpen(&archive->stream.InStream, &archive->db,
+ &allocImp, &allocTempImp)) != SZ_OK)
+ {
+ __PHYSFS_platformClose(archive->stream.File);
+ allocator.Free(archive);
+ return NULL;
+ } /* if */
+
+ len = archive->db.Database.NumFolders * sizeof (LZMAfolder);
+ archive->folder = (LZMAfolder *) allocator.Malloc(len);
+ BAIL_IF_MACRO(archive->folder == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ /*
+ * Init with 0 so we know when a folder is already cached
+ * Values will be set by LZMA_read()
+ */
+ memset(archive->folder, 0, (size_t) len);
+
+ return(archive);
+} /* LZMA_openArchive */
+
+
+/*
+ * Moved to seperate function so we can use alloca then immediately throw
+ * away the allocated stack space...
+ */
+static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
+ const char *odir, const char *str, PHYSFS_sint32 ln)
+{
+ char *newstr = __PHYSFS_smallAlloc(ln + 1);
+ if (newstr == NULL)
+ return;
+
+ memcpy(newstr, str, ln);
+ newstr[ln] = '\0';
+ cb(callbackdata, odir, newstr);
+ __PHYSFS_smallFree(newstr);
+} /* doEnumCallback */
+
+
+static void LZMA_enumerateFiles(dvoid *opaque, const char *dname,
+ int omitSymLinks, PHYSFS_EnumFilesCallback cb,
+ const char *origdir, void *callbackdata)
+{
+ LZMAarchive *archive = (LZMAarchive *) opaque;
+ PHYSFS_sint32 dlen;
+ PHYSFS_sint32 dlen_inc;
+ PHYSFS_sint32 max;
+ PHYSFS_sint32 i;
+
+ i = lzma_find_start_of_dir(archive, dname, 0);
+ if (i == -1) /* no such directory. */
+ return;
+
+ dlen = strlen(dname);
+ if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */
+ dlen--;
+
+ dlen_inc = ((dlen > 0) ? 1 : 0) + dlen;
+ max = (PHYSFS_sint32) archive->db.Database.NumFiles;
+ while (i < max)
+ {
+ char *add;
+ char *ptr;
+ PHYSFS_sint32 ln;
+ char *e = archive->db.Database.Files[i].Name;
+ if ((dlen) && ((strncmp(e, dname, dlen)) || (e[dlen] != '/')))
+ break; /* past end of this dir; we're done. */
+
+ add = e + dlen_inc;
+ ptr = strchr(add, '/');
+ ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add));
+ doEnumCallback(cb, callbackdata, origdir, add, ln);
+ ln += dlen_inc; /* point past entry to children... */
+
+ /* increment counter and skip children of subdirs... */
+ while ((++i < max) && (ptr != NULL))
+ {
+ char *e_new = archive->db.Database.Files[i].Name;
+ if ((strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/'))
+ break;
+ } /* while */
+ } /* while */
+} /* LZMA_enumerateFiles */
+
+
+static int LZMA_exists(dvoid *opaque, const char *name)
+{
+ LZMAarchive *archive = (LZMAarchive *) opaque;
+ PHYSFS_uint32 index = 0;
+ return(lzma_find_entry(archive, name, &index));
+} /* LZMA_exists */
+
+
+static PHYSFS_sint64 LZMA_getLastModTime(dvoid *opaque,
+ const char *name,
+ int *fileExists)
+{
+ /* !!! FIXME: Lacking support in the LZMA C SDK. */
+ BAIL_MACRO(ERR_NOT_IMPLEMENTED, -1);
+} /* LZMA_getLastModTime */
+
+
+static int LZMA_isDirectory(dvoid *opaque, const char *name, int *fileExists)
+{
+ LZMAarchive *archive = (LZMAarchive *) opaque;
+ PHYSFS_uint32 index = 0;
+
+ *fileExists = lzma_find_entry(archive, name, &index);
+
+ return(archive->db.Database.Files[index].IsDirectory);
+} /* LZMA_isDirectory */
+
+
+static int LZMA_isSymLink(dvoid *opaque, const char *name, int *fileExists)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* LZMA_isSymLink */
+
+
+static fvoid *LZMA_openRead(dvoid *opaque, const char *name, int *fileExists)
+{
+ LZMAarchive *archive = (LZMAarchive *) opaque;
+ LZMAentry *entry = NULL;
+ PHYSFS_uint32 fileIndex = 0;
+ PHYSFS_uint32 folderIndex = 0;
+
+ *fileExists = lzma_find_entry(archive, name, &fileIndex);
+ BAIL_IF_MACRO(!*fileExists, ERR_NO_SUCH_FILE, NULL);
+
+ folderIndex = archive->db.FileIndexToFolderIndexMap[fileIndex];
+ BAIL_IF_MACRO(folderIndex == (PHYSFS_uint32)-1, ERR_UNKNOWN_ERROR, NULL);
+
+ entry = (LZMAentry *) allocator.Malloc(sizeof (LZMAentry));
+ BAIL_IF_MACRO(entry == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ entry->fileIndex = fileIndex;
+ entry->folderIndex = folderIndex;
+ entry->archive = archive;
+ entry->file = archive->db.Database.Files + entry->fileIndex;
+ entry->offset = 0; /* Offset will be set by LZMA_read() */
+ entry->position = 0;
+
+ archive->folder[folderIndex].references++;
+
+ entry->next = NULL;
+ entry->previous = entry->archive->lastEntry;
+ if (entry->previous != NULL)
+ entry->previous->next = entry;
+ entry->archive->lastEntry = entry;
+ if (entry->archive->firstEntry == NULL)
+ entry->archive->firstEntry = entry;
+
+ return(entry);
+} /* LZMA_openRead */
+
+
+static fvoid *LZMA_openWrite(dvoid *opaque, const char *filename)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* LZMA_openWrite */
+
+
+static fvoid *LZMA_openAppend(dvoid *opaque, const char *filename)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* LZMA_openAppend */
+
+
+static void LZMA_dirClose(dvoid *opaque)
+{
+ LZMAarchive *archive = (LZMAarchive *) opaque;
+ LZMAentry *entry = archive->firstEntry;
+ LZMAentry *tmpEntry = entry;
+
+ while (entry != NULL)
+ {
+ tmpEntry = entry->next;
+ LZMA_fileClose(entry);
+ entry = tmpEntry;
+ } /* while */
+
+ SzArDbExFree(&archive->db, SzFreePhysicsFS);
+ __PHYSFS_platformClose(archive->stream.File);
+
+ /* Free the cache which might have been allocated by LZMA_read() */
+ allocator.Free(archive->folder);
+ allocator.Free(archive);
+} /* LZMA_dirClose */
+
+
+static int LZMA_remove(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* LZMA_remove */
+
+
+static int LZMA_mkdir(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* LZMA_mkdir */
+
+
+const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA =
+{
+ "7Z",
+ LZMA_ARCHIVE_DESCRIPTION,
+ "Dennis Schridde <devurandom@gmx.net>",
+ "http://icculus.org/physfs/",
+};
+
+
+const PHYSFS_Archiver __PHYSFS_Archiver_LZMA =
+{
+ &__PHYSFS_ArchiveInfo_LZMA,
+ LZMA_isArchive, /* isArchive() method */
+ LZMA_openArchive, /* openArchive() method */
+ LZMA_enumerateFiles, /* enumerateFiles() method */
+ LZMA_exists, /* exists() method */
+ LZMA_isDirectory, /* isDirectory() method */
+ LZMA_isSymLink, /* isSymLink() method */
+ LZMA_getLastModTime, /* getLastModTime() method */
+ LZMA_openRead, /* openRead() method */
+ LZMA_openWrite, /* openWrite() method */
+ LZMA_openAppend, /* openAppend() method */
+ LZMA_remove, /* remove() method */
+ LZMA_mkdir, /* mkdir() method */
+ LZMA_dirClose, /* dirClose() method */
+ LZMA_read, /* read() method */
+ LZMA_write, /* write() method */
+ LZMA_eof, /* eof() method */
+ LZMA_tell, /* tell() method */
+ LZMA_seek, /* seek() method */
+ LZMA_fileLength, /* fileLength() method */
+ LZMA_fileClose /* fileClose() method */
+};
+
+#endif /* defined PHYSFS_SUPPORTS_7Z */
+
+/* end of lzma.c ... */
+
--- /dev/null
+/*
+ * MVL support routines for PhysicsFS.
+ *
+ * This driver handles Descent II Movielib archives.
+ *
+ * The file format of MVL is quite easy...
+ *
+ * //MVL File format - Written by Heiko Herrmann
+ * char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library
+ *
+ * int num_files; // the number of files in this MVL
+ *
+ * struct {
+ * char file_name[13]; // Filename, padded to 13 bytes with 0s
+ * int file_size; // filesize in bytes
+ * }DIR_STRUCT[num_files];
+ *
+ * struct {
+ * char data[file_size]; // The file data
+ * }FILE_STRUCT[num_files];
+ *
+ * (That info is from http://www.descent2.com/ddn/specs/mvl/)
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Bradley Bell.
+ * Based on grp.c by Ryan C. Gordon.
+ */
+
+#if (defined PHYSFS_SUPPORTS_MVL)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "physfs.h"
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+typedef struct
+{
+ char name[13];
+ PHYSFS_uint32 startPos;
+ PHYSFS_uint32 size;
+} MVLentry;
+
+typedef struct
+{
+ char *filename;
+ PHYSFS_sint64 last_mod_time;
+ PHYSFS_uint32 entryCount;
+ MVLentry *entries;
+} MVLinfo;
+
+typedef struct
+{
+ void *handle;
+ MVLentry *entry;
+ PHYSFS_uint32 curPos;
+} MVLfileinfo;
+
+
+static void MVL_dirClose(dvoid *opaque)
+{
+ MVLinfo *info = ((MVLinfo *) opaque);
+ allocator.Free(info->filename);
+ allocator.Free(info->entries);
+ allocator.Free(info);
+} /* MVL_dirClose */
+
+
+static PHYSFS_sint64 MVL_read(fvoid *opaque, void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ MVLfileinfo *finfo = (MVLfileinfo *) opaque;
+ MVLentry *entry = finfo->entry;
+ PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
+ PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
+ PHYSFS_sint64 rc;
+
+ if (objsLeft < objCount)
+ objCount = objsLeft;
+
+ rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
+ if (rc > 0)
+ finfo->curPos += (PHYSFS_uint32) (rc * objSize);
+
+ return(rc);
+} /* MVL_read */
+
+
+static PHYSFS_sint64 MVL_write(fvoid *opaque, const void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
+} /* MVL_write */
+
+
+static int MVL_eof(fvoid *opaque)
+{
+ MVLfileinfo *finfo = (MVLfileinfo *) opaque;
+ MVLentry *entry = finfo->entry;
+ return(finfo->curPos >= entry->size);
+} /* MVL_eof */
+
+
+static PHYSFS_sint64 MVL_tell(fvoid *opaque)
+{
+ return(((MVLfileinfo *) opaque)->curPos);
+} /* MVL_tell */
+
+
+static int MVL_seek(fvoid *opaque, PHYSFS_uint64 offset)
+{
+ MVLfileinfo *finfo = (MVLfileinfo *) opaque;
+ MVLentry *entry = finfo->entry;
+ int rc;
+
+ BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
+ rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
+ if (rc)
+ finfo->curPos = (PHYSFS_uint32) offset;
+
+ return(rc);
+} /* MVL_seek */
+
+
+static PHYSFS_sint64 MVL_fileLength(fvoid *opaque)
+{
+ MVLfileinfo *finfo = (MVLfileinfo *) opaque;
+ return((PHYSFS_sint64) finfo->entry->size);
+} /* MVL_fileLength */
+
+
+static int MVL_fileClose(fvoid *opaque)
+{
+ MVLfileinfo *finfo = (MVLfileinfo *) opaque;
+ BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
+ allocator.Free(finfo);
+ return(1);
+} /* MVL_fileClose */
+
+
+static int mvl_open(const char *filename, int forWriting,
+ void **fh, PHYSFS_uint32 *count)
+{
+ PHYSFS_uint8 buf[4];
+
+ *fh = NULL;
+ BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
+
+ *fh = __PHYSFS_platformOpenRead(filename);
+ BAIL_IF_MACRO(*fh == NULL, NULL, 0);
+
+ if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1)
+ goto openMvl_failed;
+
+ if (memcmp(buf, "DMVL", 4) != 0)
+ {
+ __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
+ goto openMvl_failed;
+ } /* if */
+
+ if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
+ goto openMvl_failed;
+
+ *count = PHYSFS_swapULE32(*count);
+
+ return(1);
+
+openMvl_failed:
+ if (*fh != NULL)
+ __PHYSFS_platformClose(*fh);
+
+ *count = -1;
+ *fh = NULL;
+ return(0);
+} /* mvl_open */
+
+
+static int MVL_isArchive(const char *filename, int forWriting)
+{
+ void *fh;
+ PHYSFS_uint32 fileCount;
+ int retval = mvl_open(filename, forWriting, &fh, &fileCount);
+
+ if (fh != NULL)
+ __PHYSFS_platformClose(fh);
+
+ return(retval);
+} /* MVL_isArchive */
+
+
+static int mvl_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ MVLentry *a = (MVLentry *) _a;
+ return(strcmp(a[one].name, a[two].name));
+} /* mvl_entry_cmp */
+
+
+static void mvl_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ MVLentry tmp;
+ MVLentry *first = &(((MVLentry *) _a)[one]);
+ MVLentry *second = &(((MVLentry *) _a)[two]);
+ memcpy(&tmp, first, sizeof (MVLentry));
+ memcpy(first, second, sizeof (MVLentry));
+ memcpy(second, &tmp, sizeof (MVLentry));
+} /* mvl_entry_swap */
+
+
+static int mvl_load_entries(const char *name, int forWriting, MVLinfo *info)
+{
+ void *fh = NULL;
+ PHYSFS_uint32 fileCount;
+ PHYSFS_uint32 location = 8; /* sizeof sig. */
+ MVLentry *entry;
+
+ BAIL_IF_MACRO(!mvl_open(name, forWriting, &fh, &fileCount), NULL, 0);
+ info->entryCount = fileCount;
+ info->entries = (MVLentry *) allocator.Malloc(sizeof(MVLentry)*fileCount);
+ if (info->entries == NULL)
+ {
+ __PHYSFS_platformClose(fh);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
+ } /* if */
+
+ location += (17 * fileCount);
+
+ for (entry = info->entries; fileCount > 0; fileCount--, entry++)
+ {
+ if (__PHYSFS_platformRead(fh, &entry->name, 13, 1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ entry->size = PHYSFS_swapULE32(entry->size);
+ entry->startPos = location;
+ location += entry->size;
+ } /* for */
+
+ __PHYSFS_platformClose(fh);
+
+ __PHYSFS_sort(info->entries, info->entryCount,
+ mvl_entry_cmp, mvl_entry_swap);
+ return(1);
+} /* mvl_load_entries */
+
+
+static void *MVL_openArchive(const char *name, int forWriting)
+{
+ PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
+ MVLinfo *info = (MVLinfo *) allocator.Malloc(sizeof (MVLinfo));
+
+ BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
+ memset(info, '\0', sizeof (MVLinfo));
+
+ info->filename = (char *) allocator.Malloc(strlen(name) + 1);
+ GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, MVL_openArchive_failed);
+ if (!mvl_load_entries(name, forWriting, info))
+ goto MVL_openArchive_failed;
+
+ strcpy(info->filename, name);
+ info->last_mod_time = modtime;
+ return(info);
+
+MVL_openArchive_failed:
+ if (info != NULL)
+ {
+ if (info->filename != NULL)
+ allocator.Free(info->filename);
+ if (info->entries != NULL)
+ allocator.Free(info->entries);
+ allocator.Free(info);
+ } /* if */
+
+ return(NULL);
+} /* MVL_openArchive */
+
+
+static void MVL_enumerateFiles(dvoid *opaque, const char *dname,
+ int omitSymLinks, PHYSFS_EnumFilesCallback cb,
+ const char *origdir, void *callbackdata)
+{
+ /* no directories in MVL files. */
+ if (*dname == '\0')
+ {
+ MVLinfo *info = ((MVLinfo *) opaque);
+ MVLentry *entry = info->entries;
+ PHYSFS_uint32 max = info->entryCount;
+ PHYSFS_uint32 i;
+
+ for (i = 0; i < max; i++, entry++)
+ cb(callbackdata, origdir, entry->name);
+ } /* if */
+} /* MVL_enumerateFiles */
+
+
+static MVLentry *mvl_find_entry(MVLinfo *info, const char *name)
+{
+ char *ptr = strchr(name, '.');
+ MVLentry *a = info->entries;
+ PHYSFS_sint32 lo = 0;
+ PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
+ PHYSFS_sint32 middle;
+ int rc;
+
+ /*
+ * Rule out filenames to avoid unneeded processing...no dirs,
+ * big filenames, or extensions > 3 chars.
+ */
+ BAIL_IF_MACRO((ptr) && (strlen(ptr) > 4), ERR_NO_SUCH_FILE, NULL);
+ BAIL_IF_MACRO(strlen(name) > 12, ERR_NO_SUCH_FILE, NULL);
+ BAIL_IF_MACRO(strchr(name, '/') != NULL, ERR_NO_SUCH_FILE, NULL);
+
+ while (lo <= hi)
+ {
+ middle = lo + ((hi - lo) / 2);
+ rc = __PHYSFS_stricmpASCII(name, a[middle].name);
+ if (rc == 0) /* found it! */
+ return(&a[middle]);
+ else if (rc > 0)
+ lo = middle + 1;
+ else
+ hi = middle - 1;
+ } /* while */
+
+ BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
+} /* mvl_find_entry */
+
+
+static int MVL_exists(dvoid *opaque, const char *name)
+{
+ return(mvl_find_entry(((MVLinfo *) opaque), name) != NULL);
+} /* MVL_exists */
+
+
+static int MVL_isDirectory(dvoid *opaque, const char *name, int *fileExists)
+{
+ *fileExists = MVL_exists(opaque, name);
+ return(0); /* never directories in a groupfile. */
+} /* MVL_isDirectory */
+
+
+static int MVL_isSymLink(dvoid *opaque, const char *name, int *fileExists)
+{
+ *fileExists = MVL_exists(opaque, name);
+ return(0); /* never symlinks in a groupfile. */
+} /* MVL_isSymLink */
+
+
+static PHYSFS_sint64 MVL_getLastModTime(dvoid *opaque,
+ const char *name,
+ int *fileExists)
+{
+ MVLinfo *info = ((MVLinfo *) opaque);
+ PHYSFS_sint64 retval = -1;
+
+ *fileExists = (mvl_find_entry(info, name) != NULL);
+ if (*fileExists) /* use time of MVL itself in the physical filesystem. */
+ retval = info->last_mod_time;
+
+ return(retval);
+} /* MVL_getLastModTime */
+
+
+static fvoid *MVL_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+{
+ MVLinfo *info = ((MVLinfo *) opaque);
+ MVLfileinfo *finfo;
+ MVLentry *entry;
+
+ entry = mvl_find_entry(info, fnm);
+ *fileExists = (entry != NULL);
+ BAIL_IF_MACRO(entry == NULL, NULL, NULL);
+
+ finfo = (MVLfileinfo *) allocator.Malloc(sizeof (MVLfileinfo));
+ BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ finfo->handle = __PHYSFS_platformOpenRead(info->filename);
+ if ( (finfo->handle == NULL) ||
+ (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
+ {
+ allocator.Free(finfo);
+ return(NULL);
+ } /* if */
+
+ finfo->curPos = 0;
+ finfo->entry = entry;
+ return(finfo);
+} /* MVL_openRead */
+
+
+static fvoid *MVL_openWrite(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* MVL_openWrite */
+
+
+static fvoid *MVL_openAppend(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* MVL_openAppend */
+
+
+static int MVL_remove(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* MVL_remove */
+
+
+static int MVL_mkdir(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* MVL_mkdir */
+
+
+const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL =
+{
+ "MVL",
+ MVL_ARCHIVE_DESCRIPTION,
+ "Bradley Bell <btb@icculus.org>",
+ "http://icculus.org/physfs/",
+};
+
+
+const PHYSFS_Archiver __PHYSFS_Archiver_MVL =
+{
+ &__PHYSFS_ArchiveInfo_MVL,
+ MVL_isArchive, /* isArchive() method */
+ MVL_openArchive, /* openArchive() method */
+ MVL_enumerateFiles, /* enumerateFiles() method */
+ MVL_exists, /* exists() method */
+ MVL_isDirectory, /* isDirectory() method */
+ MVL_isSymLink, /* isSymLink() method */
+ MVL_getLastModTime, /* getLastModTime() method */
+ MVL_openRead, /* openRead() method */
+ MVL_openWrite, /* openWrite() method */
+ MVL_openAppend, /* openAppend() method */
+ MVL_remove, /* remove() method */
+ MVL_mkdir, /* mkdir() method */
+ MVL_dirClose, /* dirClose() method */
+ MVL_read, /* read() method */
+ MVL_write, /* write() method */
+ MVL_eof, /* eof() method */
+ MVL_tell, /* tell() method */
+ MVL_seek, /* seek() method */
+ MVL_fileLength, /* fileLength() method */
+ MVL_fileClose /* fileClose() method */
+};
+
+#endif /* defined PHYSFS_SUPPORTS_MVL */
+
+/* end of mvl.c ... */
+
--- /dev/null
+/*
+ * QPAK support routines for PhysicsFS.
+ *
+ * This archiver handles the archive format utilized by Quake 1 and 2.
+ * Quake3-based games use the PkZip/Info-Zip format (which our zip.c
+ * archiver handles).
+ *
+ * ========================================================================
+ *
+ * This format info (in more detail) comes from:
+ * http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/pak.txt
+ *
+ * Quake PAK Format
+ *
+ * Header
+ * (4 bytes) signature = 'PACK'
+ * (4 bytes) directory offset
+ * (4 bytes) directory length
+ *
+ * Directory
+ * (56 bytes) file name
+ * (4 bytes) file position
+ * (4 bytes) file length
+ *
+ * ========================================================================
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#if (defined PHYSFS_SUPPORTS_QPAK)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "physfs.h"
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+#if 1 /* Make this case insensitive? */
+#define QPAK_strcmp(x, y) __PHYSFS_stricmpASCII(x, y)
+#define QPAK_strncmp(x, y, z) __PHYSFS_strnicmpASCII(x, y, z)
+#else
+#define QPAK_strcmp(x, y) strcmp(x, y)
+#define QPAK_strncmp(x, y, z) strncmp(x, y, z)
+#endif
+
+
+typedef struct
+{
+ char name[56];
+ PHYSFS_uint32 startPos;
+ PHYSFS_uint32 size;
+} QPAKentry;
+
+typedef struct
+{
+ char *filename;
+ PHYSFS_sint64 last_mod_time;
+ PHYSFS_uint32 entryCount;
+ QPAKentry *entries;
+} QPAKinfo;
+
+typedef struct
+{
+ void *handle;
+ QPAKentry *entry;
+ PHYSFS_uint32 curPos;
+} QPAKfileinfo;
+
+/* Magic numbers... */
+#define QPAK_SIG 0x4b434150 /* "PACK" in ASCII. */
+
+
+static void QPAK_dirClose(dvoid *opaque)
+{
+ QPAKinfo *info = ((QPAKinfo *) opaque);
+ allocator.Free(info->filename);
+ allocator.Free(info->entries);
+ allocator.Free(info);
+} /* QPAK_dirClose */
+
+
+static PHYSFS_sint64 QPAK_read(fvoid *opaque, void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
+ QPAKentry *entry = finfo->entry;
+ PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
+ PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
+ PHYSFS_sint64 rc;
+
+ if (objsLeft < objCount)
+ objCount = objsLeft;
+
+ rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
+ if (rc > 0)
+ finfo->curPos += (PHYSFS_uint32) (rc * objSize);
+
+ return(rc);
+} /* QPAK_read */
+
+
+static PHYSFS_sint64 QPAK_write(fvoid *opaque, const void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
+} /* QPAK_write */
+
+
+static int QPAK_eof(fvoid *opaque)
+{
+ QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
+ QPAKentry *entry = finfo->entry;
+ return(finfo->curPos >= entry->size);
+} /* QPAK_eof */
+
+
+static PHYSFS_sint64 QPAK_tell(fvoid *opaque)
+{
+ return(((QPAKfileinfo *) opaque)->curPos);
+} /* QPAK_tell */
+
+
+static int QPAK_seek(fvoid *opaque, PHYSFS_uint64 offset)
+{
+ QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
+ QPAKentry *entry = finfo->entry;
+ int rc;
+
+ BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
+ rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
+ if (rc)
+ finfo->curPos = (PHYSFS_uint32) offset;
+
+ return(rc);
+} /* QPAK_seek */
+
+
+static PHYSFS_sint64 QPAK_fileLength(fvoid *opaque)
+{
+ QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
+ return((PHYSFS_sint64) finfo->entry->size);
+} /* QPAK_fileLength */
+
+
+static int QPAK_fileClose(fvoid *opaque)
+{
+ QPAKfileinfo *finfo = (QPAKfileinfo *) opaque;
+ BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
+ allocator.Free(finfo);
+ return(1);
+} /* QPAK_fileClose */
+
+
+static int qpak_open(const char *filename, int forWriting,
+ void **fh, PHYSFS_uint32 *count)
+{
+ PHYSFS_uint32 buf;
+
+ *fh = NULL;
+ BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
+
+ *fh = __PHYSFS_platformOpenRead(filename);
+ BAIL_IF_MACRO(*fh == NULL, NULL, 0);
+
+ if (__PHYSFS_platformRead(*fh, &buf, sizeof (PHYSFS_uint32), 1) != 1)
+ goto openQpak_failed;
+
+ buf = PHYSFS_swapULE32(buf);
+ GOTO_IF_MACRO(buf != QPAK_SIG, ERR_UNSUPPORTED_ARCHIVE, openQpak_failed);
+
+ if (__PHYSFS_platformRead(*fh, &buf, sizeof (PHYSFS_uint32), 1) != 1)
+ goto openQpak_failed;
+
+ buf = PHYSFS_swapULE32(buf); /* directory table offset. */
+
+ if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
+ goto openQpak_failed;
+
+ *count = PHYSFS_swapULE32(*count);
+
+ /* corrupted archive? */
+ GOTO_IF_MACRO((*count % 64) != 0, ERR_CORRUPTED, openQpak_failed);
+
+ if (!__PHYSFS_platformSeek(*fh, buf))
+ goto openQpak_failed;
+
+ *count /= 64;
+ return(1);
+
+openQpak_failed:
+ if (*fh != NULL)
+ __PHYSFS_platformClose(*fh);
+
+ *count = -1;
+ *fh = NULL;
+ return(0);
+} /* qpak_open */
+
+
+static int QPAK_isArchive(const char *filename, int forWriting)
+{
+ void *fh;
+ PHYSFS_uint32 fileCount;
+ int retval = qpak_open(filename, forWriting, &fh, &fileCount);
+
+ if (fh != NULL)
+ __PHYSFS_platformClose(fh);
+
+ return(retval);
+} /* QPAK_isArchive */
+
+
+static int qpak_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ QPAKentry *a = (QPAKentry *) _a;
+ return(QPAK_strcmp(a[one].name, a[two].name));
+} /* qpak_entry_cmp */
+
+
+static void qpak_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ QPAKentry tmp;
+ QPAKentry *first = &(((QPAKentry *) _a)[one]);
+ QPAKentry *second = &(((QPAKentry *) _a)[two]);
+ memcpy(&tmp, first, sizeof (QPAKentry));
+ memcpy(first, second, sizeof (QPAKentry));
+ memcpy(second, &tmp, sizeof (QPAKentry));
+} /* qpak_entry_swap */
+
+
+static int qpak_load_entries(const char *name, int forWriting, QPAKinfo *info)
+{
+ void *fh = NULL;
+ PHYSFS_uint32 fileCount;
+ QPAKentry *entry;
+
+ BAIL_IF_MACRO(!qpak_open(name, forWriting, &fh, &fileCount), NULL, 0);
+ info->entryCount = fileCount;
+ info->entries = (QPAKentry*) allocator.Malloc(sizeof(QPAKentry)*fileCount);
+ if (info->entries == NULL)
+ {
+ __PHYSFS_platformClose(fh);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
+ } /* if */
+
+ for (entry = info->entries; fileCount > 0; fileCount--, entry++)
+ {
+ PHYSFS_uint32 loc;
+
+ if (__PHYSFS_platformRead(fh,&entry->name,sizeof(entry->name),1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ if (__PHYSFS_platformRead(fh,&loc,sizeof(loc),1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ if (__PHYSFS_platformRead(fh,&entry->size,sizeof(entry->size),1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ entry->size = PHYSFS_swapULE32(entry->size);
+ entry->startPos = PHYSFS_swapULE32(loc);
+ } /* for */
+
+ __PHYSFS_platformClose(fh);
+
+ __PHYSFS_sort(info->entries, info->entryCount,
+ qpak_entry_cmp, qpak_entry_swap);
+ return(1);
+} /* qpak_load_entries */
+
+
+static void *QPAK_openArchive(const char *name, int forWriting)
+{
+ QPAKinfo *info = (QPAKinfo *) allocator.Malloc(sizeof (QPAKinfo));
+ PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
+
+ BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
+ memset(info, '\0', sizeof (QPAKinfo));
+
+ info->filename = (char *) allocator.Malloc(strlen(name) + 1);
+ if (info->filename == NULL)
+ {
+ __PHYSFS_setError(ERR_OUT_OF_MEMORY);
+ goto QPAK_openArchive_failed;
+ } /* if */
+
+ if (!qpak_load_entries(name, forWriting, info))
+ goto QPAK_openArchive_failed;
+
+ strcpy(info->filename, name);
+ info->last_mod_time = modtime;
+ return(info);
+
+QPAK_openArchive_failed:
+ if (info != NULL)
+ {
+ if (info->filename != NULL)
+ allocator.Free(info->filename);
+ if (info->entries != NULL)
+ allocator.Free(info->entries);
+ allocator.Free(info);
+ } /* if */
+
+ return(NULL);
+} /* QPAK_openArchive */
+
+
+static PHYSFS_sint32 qpak_find_start_of_dir(QPAKinfo *info, const char *path,
+ int stop_on_first_find)
+{
+ PHYSFS_sint32 lo = 0;
+ PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
+ PHYSFS_sint32 middle;
+ PHYSFS_uint32 dlen = strlen(path);
+ PHYSFS_sint32 retval = -1;
+ const char *name;
+ int rc;
+
+ if (*path == '\0') /* root dir? */
+ return(0);
+
+ if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */
+ dlen--;
+
+ while (lo <= hi)
+ {
+ middle = lo + ((hi - lo) / 2);
+ name = info->entries[middle].name;
+ rc = QPAK_strncmp(path, name, dlen);
+ if (rc == 0)
+ {
+ char ch = name[dlen];
+ if (ch < '/') /* make sure this isn't just a substr match. */
+ rc = -1;
+ else if (ch > '/')
+ rc = 1;
+ else
+ {
+ if (stop_on_first_find) /* Just checking dir's existance? */
+ return(middle);
+
+ if (name[dlen + 1] == '\0') /* Skip initial dir entry. */
+ return(middle + 1);
+
+ /* there might be more entries earlier in the list. */
+ retval = middle;
+ hi = middle - 1;
+ } /* else */
+ } /* if */
+
+ if (rc > 0)
+ lo = middle + 1;
+ else
+ hi = middle - 1;
+ } /* while */
+
+ return(retval);
+} /* qpak_find_start_of_dir */
+
+
+/*
+ * Moved to seperate function so we can use alloca then immediately throw
+ * away the allocated stack space...
+ */
+static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
+ const char *odir, const char *str, PHYSFS_sint32 ln)
+{
+ char *newstr = __PHYSFS_smallAlloc(ln + 1);
+ if (newstr == NULL)
+ return;
+
+ memcpy(newstr, str, ln);
+ newstr[ln] = '\0';
+ cb(callbackdata, odir, newstr);
+ __PHYSFS_smallFree(newstr);
+} /* doEnumCallback */
+
+
+static void QPAK_enumerateFiles(dvoid *opaque, const char *dname,
+ int omitSymLinks, PHYSFS_EnumFilesCallback cb,
+ const char *origdir, void *callbackdata)
+{
+ QPAKinfo *info = ((QPAKinfo *) opaque);
+ PHYSFS_sint32 dlen, dlen_inc, max, i;
+
+ i = qpak_find_start_of_dir(info, dname, 0);
+ if (i == -1) /* no such directory. */
+ return;
+
+ dlen = strlen(dname);
+ if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */
+ dlen--;
+
+ dlen_inc = ((dlen > 0) ? 1 : 0) + dlen;
+ max = (PHYSFS_sint32) info->entryCount;
+ while (i < max)
+ {
+ char *add;
+ char *ptr;
+ PHYSFS_sint32 ln;
+ char *e = info->entries[i].name;
+ if ((dlen) && ((QPAK_strncmp(e, dname, dlen)) || (e[dlen] != '/')))
+ break; /* past end of this dir; we're done. */
+
+ add = e + dlen_inc;
+ ptr = strchr(add, '/');
+ ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add));
+ doEnumCallback(cb, callbackdata, origdir, add, ln);
+ ln += dlen_inc; /* point past entry to children... */
+
+ /* increment counter and skip children of subdirs... */
+ while ((++i < max) && (ptr != NULL))
+ {
+ char *e_new = info->entries[i].name;
+ if ((QPAK_strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/'))
+ break;
+ } /* while */
+ } /* while */
+} /* QPAK_enumerateFiles */
+
+
+/*
+ * This will find the QPAKentry associated with a path in platform-independent
+ * notation. Directories don't have QPAKentries associated with them, but
+ * (*isDir) will be set to non-zero if a dir was hit.
+ */
+static QPAKentry *qpak_find_entry(QPAKinfo *info, const char *path, int *isDir)
+{
+ QPAKentry *a = info->entries;
+ PHYSFS_sint32 pathlen = strlen(path);
+ PHYSFS_sint32 lo = 0;
+ PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
+ PHYSFS_sint32 middle;
+ const char *thispath = NULL;
+ int rc;
+
+ while (lo <= hi)
+ {
+ middle = lo + ((hi - lo) / 2);
+ thispath = a[middle].name;
+ rc = QPAK_strncmp(path, thispath, pathlen);
+
+ if (rc > 0)
+ lo = middle + 1;
+
+ else if (rc < 0)
+ hi = middle - 1;
+
+ else /* substring match...might be dir or entry or nothing. */
+ {
+ if (isDir != NULL)
+ {
+ *isDir = (thispath[pathlen] == '/');
+ if (*isDir)
+ return(NULL);
+ } /* if */
+
+ if (thispath[pathlen] == '\0') /* found entry? */
+ return(&a[middle]);
+ else
+ hi = middle - 1; /* adjust search params, try again. */
+ } /* if */
+ } /* while */
+
+ if (isDir != NULL)
+ *isDir = 0;
+
+ BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
+} /* qpak_find_entry */
+
+
+static int QPAK_exists(dvoid *opaque, const char *name)
+{
+ int isDir;
+ QPAKinfo *info = (QPAKinfo *) opaque;
+ QPAKentry *entry = qpak_find_entry(info, name, &isDir);
+ return((entry != NULL) || (isDir));
+} /* QPAK_exists */
+
+
+static int QPAK_isDirectory(dvoid *opaque, const char *name, int *fileExists)
+{
+ QPAKinfo *info = (QPAKinfo *) opaque;
+ int isDir;
+ QPAKentry *entry = qpak_find_entry(info, name, &isDir);
+
+ *fileExists = ((isDir) || (entry != NULL));
+ if (isDir)
+ return(1); /* definitely a dir. */
+
+ BAIL_MACRO(ERR_NO_SUCH_FILE, 0);
+} /* QPAK_isDirectory */
+
+
+static int QPAK_isSymLink(dvoid *opaque, const char *name, int *fileExists)
+{
+ *fileExists = QPAK_exists(opaque, name);
+ return(0); /* never symlinks in a quake pak. */
+} /* QPAK_isSymLink */
+
+
+static PHYSFS_sint64 QPAK_getLastModTime(dvoid *opaque,
+ const char *name,
+ int *fileExists)
+{
+ int isDir;
+ QPAKinfo *info = ((QPAKinfo *) opaque);
+ PHYSFS_sint64 retval = -1;
+ QPAKentry *entry = qpak_find_entry(info, name, &isDir);
+
+ *fileExists = ((isDir) || (entry != NULL));
+ if (*fileExists) /* use time of QPAK itself in the physical filesystem. */
+ retval = info->last_mod_time;
+
+ return(retval);
+} /* QPAK_getLastModTime */
+
+
+static fvoid *QPAK_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+{
+ QPAKinfo *info = ((QPAKinfo *) opaque);
+ QPAKfileinfo *finfo;
+ QPAKentry *entry;
+ int isDir;
+
+ entry = qpak_find_entry(info, fnm, &isDir);
+ *fileExists = ((entry != NULL) || (isDir));
+ BAIL_IF_MACRO(isDir, ERR_NOT_A_FILE, NULL);
+ BAIL_IF_MACRO(entry == NULL, ERR_NO_SUCH_FILE, NULL);
+
+ finfo = (QPAKfileinfo *) allocator.Malloc(sizeof (QPAKfileinfo));
+ BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ finfo->handle = __PHYSFS_platformOpenRead(info->filename);
+ if ( (finfo->handle == NULL) ||
+ (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
+ {
+ allocator.Free(finfo);
+ return(NULL);
+ } /* if */
+
+ finfo->curPos = 0;
+ finfo->entry = entry;
+ return(finfo);
+} /* QPAK_openRead */
+
+
+static fvoid *QPAK_openWrite(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* QPAK_openWrite */
+
+
+static fvoid *QPAK_openAppend(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* QPAK_openAppend */
+
+
+static int QPAK_remove(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* QPAK_remove */
+
+
+static int QPAK_mkdir(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* QPAK_mkdir */
+
+
+const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK =
+{
+ "PAK",
+ QPAK_ARCHIVE_DESCRIPTION,
+ "Ryan C. Gordon <icculus@icculus.org>",
+ "http://icculus.org/physfs/",
+};
+
+
+const PHYSFS_Archiver __PHYSFS_Archiver_QPAK =
+{
+ &__PHYSFS_ArchiveInfo_QPAK,
+ QPAK_isArchive, /* isArchive() method */
+ QPAK_openArchive, /* openArchive() method */
+ QPAK_enumerateFiles, /* enumerateFiles() method */
+ QPAK_exists, /* exists() method */
+ QPAK_isDirectory, /* isDirectory() method */
+ QPAK_isSymLink, /* isSymLink() method */
+ QPAK_getLastModTime, /* getLastModTime() method */
+ QPAK_openRead, /* openRead() method */
+ QPAK_openWrite, /* openWrite() method */
+ QPAK_openAppend, /* openAppend() method */
+ QPAK_remove, /* remove() method */
+ QPAK_mkdir, /* mkdir() method */
+ QPAK_dirClose, /* dirClose() method */
+ QPAK_read, /* read() method */
+ QPAK_write, /* write() method */
+ QPAK_eof, /* eof() method */
+ QPAK_tell, /* tell() method */
+ QPAK_seek, /* seek() method */
+ QPAK_fileLength, /* fileLength() method */
+ QPAK_fileClose /* fileClose() method */
+};
+
+#endif /* defined PHYSFS_SUPPORTS_QPAK */
+
+/* end of qpak.c ... */
+
--- /dev/null
+/*
+ * WAD support routines for PhysicsFS.
+ *
+ * This driver handles DOOM engine archives ("wads").
+ * This format (but not this driver) was designed by id Software for use
+ * with the DOOM engine.
+ * The specs of the format are from the unofficial doom specs v1.666
+ * found here: http://www.gamers.org/dhs/helpdocs/dmsp1666.html
+ * The format of the archive: (from the specs)
+ *
+ * A WAD file has three parts:
+ * (1) a twelve-byte header
+ * (2) one or more "lumps"
+ * (3) a directory or "info table" that contains the names, offsets, and
+ * sizes of all the lumps in the WAD
+ *
+ * The header consists of three four-byte parts:
+ * (a) an ASCII string which must be either "IWAD" or "PWAD"
+ * (b) a 4-byte (long) integer which is the number of lumps in the wad
+ * (c) a long integer which is the file offset to the start of
+ * the directory
+ *
+ * The directory has one 16-byte entry for every lump. Each entry consists
+ * of three parts:
+ *
+ * (a) a long integer, the file offset to the start of the lump
+ * (b) a long integer, the size of the lump in bytes
+ * (c) an 8-byte ASCII string, the name of the lump, padded with zeros.
+ * For example, the "DEMO1" entry in hexadecimal would be
+ * (44 45 4D 4F 31 00 00 00)
+ *
+ * Note that there is no way to tell if an opened WAD archive is a
+ * IWAD or PWAD with this archiver.
+ * I couldn't think of a way to provide that information, without being too
+ * hacky.
+ * I don't think it's really that important though.
+ *
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Travis Wells, based on the GRP archiver by
+ * Ryan C. Gordon.
+ */
+
+#if (defined PHYSFS_SUPPORTS_WAD)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "physfs.h"
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+typedef struct
+{
+ char name[18];
+ PHYSFS_uint32 startPos;
+ PHYSFS_uint32 size;
+} WADentry;
+
+typedef struct
+{
+ char *filename;
+ PHYSFS_sint64 last_mod_time;
+ PHYSFS_uint32 entryCount;
+ PHYSFS_uint32 entryOffset;
+ WADentry *entries;
+} WADinfo;
+
+typedef struct
+{
+ void *handle;
+ WADentry *entry;
+ PHYSFS_uint32 curPos;
+} WADfileinfo;
+
+
+static void WAD_dirClose(dvoid *opaque)
+{
+ WADinfo *info = ((WADinfo *) opaque);
+ allocator.Free(info->filename);
+ allocator.Free(info->entries);
+ allocator.Free(info);
+} /* WAD_dirClose */
+
+
+static PHYSFS_sint64 WAD_read(fvoid *opaque, void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ WADfileinfo *finfo = (WADfileinfo *) opaque;
+ WADentry *entry = finfo->entry;
+ PHYSFS_uint32 bytesLeft = entry->size - finfo->curPos;
+ PHYSFS_uint32 objsLeft = (bytesLeft / objSize);
+ PHYSFS_sint64 rc;
+
+ if (objsLeft < objCount)
+ objCount = objsLeft;
+
+ rc = __PHYSFS_platformRead(finfo->handle, buffer, objSize, objCount);
+ if (rc > 0)
+ finfo->curPos += (PHYSFS_uint32) (rc * objSize);
+
+ return(rc);
+} /* WAD_read */
+
+
+static PHYSFS_sint64 WAD_write(fvoid *opaque, const void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
+} /* WAD_write */
+
+
+static int WAD_eof(fvoid *opaque)
+{
+ WADfileinfo *finfo = (WADfileinfo *) opaque;
+ WADentry *entry = finfo->entry;
+ return(finfo->curPos >= entry->size);
+} /* WAD_eof */
+
+
+static PHYSFS_sint64 WAD_tell(fvoid *opaque)
+{
+ return(((WADfileinfo *) opaque)->curPos);
+} /* WAD_tell */
+
+
+static int WAD_seek(fvoid *opaque, PHYSFS_uint64 offset)
+{
+ WADfileinfo *finfo = (WADfileinfo *) opaque;
+ WADentry *entry = finfo->entry;
+ int rc;
+
+ BAIL_IF_MACRO(offset < 0, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(offset >= entry->size, ERR_PAST_EOF, 0);
+ rc = __PHYSFS_platformSeek(finfo->handle, entry->startPos + offset);
+ if (rc)
+ finfo->curPos = (PHYSFS_uint32) offset;
+
+ return(rc);
+} /* WAD_seek */
+
+
+static PHYSFS_sint64 WAD_fileLength(fvoid *opaque)
+{
+ WADfileinfo *finfo = (WADfileinfo *) opaque;
+ return((PHYSFS_sint64) finfo->entry->size);
+} /* WAD_fileLength */
+
+
+static int WAD_fileClose(fvoid *opaque)
+{
+ WADfileinfo *finfo = (WADfileinfo *) opaque;
+ BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
+ allocator.Free(finfo);
+ return(1);
+} /* WAD_fileClose */
+
+
+static int wad_open(const char *filename, int forWriting,
+ void **fh, PHYSFS_uint32 *count,PHYSFS_uint32 *offset)
+{
+ PHYSFS_uint8 buf[4];
+
+ *fh = NULL;
+ BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, 0);
+
+ *fh = __PHYSFS_platformOpenRead(filename);
+ BAIL_IF_MACRO(*fh == NULL, NULL, 0);
+
+ if (__PHYSFS_platformRead(*fh, buf, 4, 1) != 1)
+ goto openWad_failed;
+
+ if (memcmp(buf, "IWAD", 4) != 0 && memcmp(buf, "PWAD", 4) != 0)
+ {
+ __PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
+ goto openWad_failed;
+ } /* if */
+
+ if (__PHYSFS_platformRead(*fh, count, sizeof (PHYSFS_uint32), 1) != 1)
+ goto openWad_failed;
+
+ *count = PHYSFS_swapULE32(*count);
+
+ if (__PHYSFS_platformRead(*fh, offset, sizeof (PHYSFS_uint32), 1) != 1)
+ goto openWad_failed;
+
+ *offset = PHYSFS_swapULE32(*offset);
+
+ return(1);
+
+openWad_failed:
+ if (*fh != NULL)
+ __PHYSFS_platformClose(*fh);
+
+ *count = -1;
+ *fh = NULL;
+ return(0);
+} /* wad_open */
+
+
+static int WAD_isArchive(const char *filename, int forWriting)
+{
+ void *fh;
+ PHYSFS_uint32 fileCount,offset;
+ int retval = wad_open(filename, forWriting, &fh, &fileCount,&offset);
+
+ if (fh != NULL)
+ __PHYSFS_platformClose(fh);
+
+ return(retval);
+} /* WAD_isArchive */
+
+
+static int wad_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ WADentry *a = (WADentry *) _a;
+ return(strcmp(a[one].name, a[two].name));
+} /* wad_entry_cmp */
+
+
+static void wad_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ WADentry tmp;
+ WADentry *first = &(((WADentry *) _a)[one]);
+ WADentry *second = &(((WADentry *) _a)[two]);
+ memcpy(&tmp, first, sizeof (WADentry));
+ memcpy(first, second, sizeof (WADentry));
+ memcpy(second, &tmp, sizeof (WADentry));
+} /* wad_entry_swap */
+
+
+static int wad_load_entries(const char *name, int forWriting, WADinfo *info)
+{
+ void *fh = NULL;
+ PHYSFS_uint32 fileCount;
+ PHYSFS_uint32 directoryOffset;
+ WADentry *entry;
+ char lastDirectory[9];
+
+ lastDirectory[8] = 0; /* Make sure lastDirectory stays null-terminated. */
+
+ BAIL_IF_MACRO(!wad_open(name, forWriting, &fh, &fileCount,&directoryOffset), NULL, 0);
+ info->entryCount = fileCount;
+ info->entries = (WADentry *) allocator.Malloc(sizeof(WADentry)*fileCount);
+ if (info->entries == NULL)
+ {
+ __PHYSFS_platformClose(fh);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
+ } /* if */
+
+ __PHYSFS_platformSeek(fh,directoryOffset);
+
+ for (entry = info->entries; fileCount > 0; fileCount--, entry++)
+ {
+ if (__PHYSFS_platformRead(fh, &entry->startPos, 4, 1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ if (__PHYSFS_platformRead(fh, &entry->size, 4, 1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ if (__PHYSFS_platformRead(fh, &entry->name, 8, 1) != 1)
+ {
+ __PHYSFS_platformClose(fh);
+ return(0);
+ } /* if */
+
+ entry->name[8] = '\0'; /* name might not be null-terminated in file. */
+ entry->size = PHYSFS_swapULE32(entry->size);
+ entry->startPos = PHYSFS_swapULE32(entry->startPos);
+ } /* for */
+
+ __PHYSFS_platformClose(fh);
+
+ __PHYSFS_sort(info->entries, info->entryCount,
+ wad_entry_cmp, wad_entry_swap);
+ return(1);
+} /* wad_load_entries */
+
+
+static void *WAD_openArchive(const char *name, int forWriting)
+{
+ PHYSFS_sint64 modtime = __PHYSFS_platformGetLastModTime(name);
+ WADinfo *info = (WADinfo *) allocator.Malloc(sizeof (WADinfo));
+
+ BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, NULL);
+ memset(info, '\0', sizeof (WADinfo));
+
+ info->filename = (char *) allocator.Malloc(strlen(name) + 1);
+ GOTO_IF_MACRO(!info->filename, ERR_OUT_OF_MEMORY, WAD_openArchive_failed);
+
+ if (!wad_load_entries(name, forWriting, info))
+ goto WAD_openArchive_failed;
+
+ strcpy(info->filename, name);
+ info->last_mod_time = modtime;
+ return(info);
+
+WAD_openArchive_failed:
+ if (info != NULL)
+ {
+ if (info->filename != NULL)
+ allocator.Free(info->filename);
+ if (info->entries != NULL)
+ allocator.Free(info->entries);
+ allocator.Free(info);
+ } /* if */
+
+ return(NULL);
+} /* WAD_openArchive */
+
+
+static void WAD_enumerateFiles(dvoid *opaque, const char *dname,
+ int omitSymLinks, PHYSFS_EnumFilesCallback cb,
+ const char *origdir, void *callbackdata)
+{
+ WADinfo *info = ((WADinfo *) opaque);
+ WADentry *entry = info->entries;
+ PHYSFS_uint32 max = info->entryCount;
+ PHYSFS_uint32 i;
+ const char *name;
+ char *sep;
+
+ if (*dname == '\0') /* root directory enumeration? */
+ {
+ for (i = 0; i < max; i++, entry++)
+ {
+ name = entry->name;
+ if (strchr(name, '/') == NULL)
+ cb(callbackdata, origdir, name);
+ } /* for */
+ } /* if */
+ else
+ {
+ for (i = 0; i < max; i++, entry++)
+ {
+ name = entry->name;
+ sep = strchr(name, '/');
+ if (sep != NULL)
+ {
+ if (strncmp(dname, name, (sep - name)) == 0)
+ cb(callbackdata, origdir, sep + 1);
+ } /* if */
+ } /* for */
+ } /* else */
+} /* WAD_enumerateFiles */
+
+
+static WADentry *wad_find_entry(WADinfo *info, const char *name)
+{
+ WADentry *a = info->entries;
+ PHYSFS_sint32 lo = 0;
+ PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
+ PHYSFS_sint32 middle;
+ int rc;
+
+ while (lo <= hi)
+ {
+ middle = lo + ((hi - lo) / 2);
+ rc = strcmp(name, a[middle].name);
+ if (rc == 0) /* found it! */
+ return(&a[middle]);
+ else if (rc > 0)
+ lo = middle + 1;
+ else
+ hi = middle - 1;
+ } /* while */
+
+ BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
+} /* wad_find_entry */
+
+
+static int WAD_exists(dvoid *opaque, const char *name)
+{
+ return(wad_find_entry(((WADinfo *) opaque), name) != NULL);
+} /* WAD_exists */
+
+
+static int WAD_isDirectory(dvoid *opaque, const char *name, int *fileExists)
+{
+ WADentry *entry = wad_find_entry(((WADinfo *) opaque), name);
+ if (entry != NULL)
+ {
+ char *n;
+
+ *fileExists = 1;
+
+ /* Can't be a directory if it's a subdirectory. */
+ if (strchr(entry->name, '/') != NULL)
+ return(0);
+
+ /* Check if it matches "MAP??" or "E?M?" ... */
+ n = entry->name;
+ if ((n[0] == 'E' && n[2] == 'M') ||
+ (n[0] == 'M' && n[1] == 'A' && n[2] == 'P' && n[6] == 0))
+ {
+ return(1);
+ } /* if */
+ return(0);
+ } /* if */
+ else
+ {
+ *fileExists = 0;
+ return(0);
+ } /* else */
+} /* WAD_isDirectory */
+
+
+static int WAD_isSymLink(dvoid *opaque, const char *name, int *fileExists)
+{
+ *fileExists = WAD_exists(opaque, name);
+ return(0); /* never symlinks in a wad. */
+} /* WAD_isSymLink */
+
+
+static PHYSFS_sint64 WAD_getLastModTime(dvoid *opaque,
+ const char *name,
+ int *fileExists)
+{
+ WADinfo *info = ((WADinfo *) opaque);
+ PHYSFS_sint64 retval = -1;
+
+ *fileExists = (wad_find_entry(info, name) != NULL);
+ if (*fileExists) /* use time of WAD itself in the physical filesystem. */
+ retval = info->last_mod_time;
+
+ return(retval);
+} /* WAD_getLastModTime */
+
+
+static fvoid *WAD_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+{
+ WADinfo *info = ((WADinfo *) opaque);
+ WADfileinfo *finfo;
+ WADentry *entry;
+
+ entry = wad_find_entry(info, fnm);
+ *fileExists = (entry != NULL);
+ BAIL_IF_MACRO(entry == NULL, NULL, NULL);
+
+ finfo = (WADfileinfo *) allocator.Malloc(sizeof (WADfileinfo));
+ BAIL_IF_MACRO(finfo == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ finfo->handle = __PHYSFS_platformOpenRead(info->filename);
+ if ( (finfo->handle == NULL) ||
+ (!__PHYSFS_platformSeek(finfo->handle, entry->startPos)) )
+ {
+ allocator.Free(finfo);
+ return(NULL);
+ } /* if */
+
+ finfo->curPos = 0;
+ finfo->entry = entry;
+ return(finfo);
+} /* WAD_openRead */
+
+
+static fvoid *WAD_openWrite(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* WAD_openWrite */
+
+
+static fvoid *WAD_openAppend(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* WAD_openAppend */
+
+
+static int WAD_remove(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* WAD_remove */
+
+
+static int WAD_mkdir(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* WAD_mkdir */
+
+
+const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD =
+{
+ "WAD",
+ WAD_ARCHIVE_DESCRIPTION,
+ "Travis Wells <traviswells@mchsi.com>",
+ "http://www.3dmm2.com/doom/",
+};
+
+
+const PHYSFS_Archiver __PHYSFS_Archiver_WAD =
+{
+ &__PHYSFS_ArchiveInfo_WAD,
+ WAD_isArchive, /* isArchive() method */
+ WAD_openArchive, /* openArchive() method */
+ WAD_enumerateFiles, /* enumerateFiles() method */
+ WAD_exists, /* exists() method */
+ WAD_isDirectory, /* isDirectory() method */
+ WAD_isSymLink, /* isSymLink() method */
+ WAD_getLastModTime, /* getLastModTime() method */
+ WAD_openRead, /* openRead() method */
+ WAD_openWrite, /* openWrite() method */
+ WAD_openAppend, /* openAppend() method */
+ WAD_remove, /* remove() method */
+ WAD_mkdir, /* mkdir() method */
+ WAD_dirClose, /* dirClose() method */
+ WAD_read, /* read() method */
+ WAD_write, /* write() method */
+ WAD_eof, /* eof() method */
+ WAD_tell, /* tell() method */
+ WAD_seek, /* seek() method */
+ WAD_fileLength, /* fileLength() method */
+ WAD_fileClose /* fileClose() method */
+};
+
+#endif /* defined PHYSFS_SUPPORTS_WAD */
+
+/* end of wad.c ... */
+
--- /dev/null
+/*
+ * ZIP support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon, with some peeking at "unzip.c"
+ * by Gilles Vollant.
+ */
+
+#if (defined PHYSFS_SUPPORTS_ZIP)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+#include <errno.h>
+#include <time.h>
+#endif
+#include "physfs.h"
+#include "zlib.h"
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+/*
+ * A buffer of ZIP_READBUFSIZE is allocated for each compressed file opened,
+ * and is freed when you close the file; compressed data is read into
+ * this buffer, and then is decompressed into the buffer passed to
+ * PHYSFS_read().
+ *
+ * Uncompressed entries in a zipfile do not allocate this buffer; they just
+ * read data directly into the buffer passed to PHYSFS_read().
+ *
+ * Depending on your speed and memory requirements, you should tweak this
+ * value.
+ */
+#define ZIP_READBUFSIZE (16 * 1024)
+
+
+/*
+ * Entries are "unresolved" until they are first opened. At that time,
+ * local file headers parsed/validated, data offsets will be updated to look
+ * at the actual file data instead of the header, and symlinks will be
+ * followed and optimized. This means that we don't seek and read around the
+ * archive until forced to do so, and after the first time, we had to do
+ * less reading and parsing, which is very CD-ROM friendly.
+ */
+typedef enum
+{
+ ZIP_UNRESOLVED_FILE,
+ ZIP_UNRESOLVED_SYMLINK,
+ ZIP_RESOLVING,
+ ZIP_RESOLVED,
+ ZIP_BROKEN_FILE,
+ ZIP_BROKEN_SYMLINK
+} ZipResolveType;
+
+
+/*
+ * One ZIPentry is kept for each file in an open ZIP archive.
+ */
+typedef struct _ZIPentry
+{
+ char *name; /* Name of file in archive */
+ struct _ZIPentry *symlink; /* NULL or file we symlink to */
+ ZipResolveType resolved; /* Have we resolved file/symlink? */
+ PHYSFS_uint32 offset; /* offset of data in archive */
+ PHYSFS_uint16 version; /* version made by */
+ PHYSFS_uint16 version_needed; /* version needed to extract */
+ PHYSFS_uint16 compression_method; /* compression method */
+ PHYSFS_uint32 crc; /* crc-32 */
+ PHYSFS_uint32 compressed_size; /* compressed size */
+ PHYSFS_uint32 uncompressed_size; /* uncompressed size */
+ PHYSFS_sint64 last_mod_time; /* last file mod time */
+} ZIPentry;
+
+/*
+ * One ZIPinfo is kept for each open ZIP archive.
+ */
+typedef struct
+{
+ char *archiveName; /* path to ZIP in platform-dependent notation. */
+ PHYSFS_uint16 entryCount; /* Number of files in ZIP. */
+ ZIPentry *entries; /* info on all files in ZIP. */
+} ZIPinfo;
+
+/*
+ * One ZIPfileinfo is kept for each open file in a ZIP archive.
+ */
+typedef struct
+{
+ ZIPentry *entry; /* Info on file. */
+ void *handle; /* physical file handle. */
+ PHYSFS_uint32 compressed_position; /* offset in compressed data. */
+ PHYSFS_uint32 uncompressed_position; /* tell() position. */
+ PHYSFS_uint8 *buffer; /* decompression buffer. */
+ z_stream stream; /* zlib stream state. */
+} ZIPfileinfo;
+
+
+/* Magic numbers... */
+#define ZIP_LOCAL_FILE_SIG 0x04034b50
+#define ZIP_CENTRAL_DIR_SIG 0x02014b50
+#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50
+
+/* compression methods... */
+#define COMPMETH_NONE 0
+/* ...and others... */
+
+
+#define UNIX_FILETYPE_MASK 0170000
+#define UNIX_FILETYPE_SYMLINK 0120000
+
+
+/*
+ * Bridge physfs allocation functions to zlib's format...
+ */
+static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size)
+{
+ return(((PHYSFS_Allocator *) opaque)->Malloc(items * size));
+} /* zlibPhysfsAlloc */
+
+/*
+ * Bridge physfs allocation functions to zlib's format...
+ */
+static void zlibPhysfsFree(voidpf opaque, voidpf address)
+{
+ ((PHYSFS_Allocator *) opaque)->Free(address);
+} /* zlibPhysfsFree */
+
+
+/*
+ * Construct a new z_stream to a sane state.
+ */
+static void initializeZStream(z_stream *pstr)
+{
+ memset(pstr, '\0', sizeof (z_stream));
+ pstr->zalloc = zlibPhysfsAlloc;
+ pstr->zfree = zlibPhysfsFree;
+ pstr->opaque = &allocator;
+} /* initializeZStream */
+
+
+static const char *zlib_error_string(int rc)
+{
+ switch (rc)
+ {
+ case Z_OK: return(NULL); /* not an error. */
+ case Z_STREAM_END: return(NULL); /* not an error. */
+#ifndef _WIN32_WCE
+ case Z_ERRNO: return(strerror(errno));
+#endif
+ case Z_NEED_DICT: return(ERR_NEED_DICT);
+ case Z_DATA_ERROR: return(ERR_DATA_ERROR);
+ case Z_MEM_ERROR: return(ERR_MEMORY_ERROR);
+ case Z_BUF_ERROR: return(ERR_BUFFER_ERROR);
+ case Z_VERSION_ERROR: return(ERR_VERSION_ERROR);
+ default: return(ERR_UNKNOWN_ERROR);
+ } /* switch */
+
+ return(NULL);
+} /* zlib_error_string */
+
+
+/*
+ * Wrap all zlib calls in this, so the physfs error state is set appropriately.
+ */
+static int zlib_err(int rc)
+{
+ const char *str = zlib_error_string(rc);
+ if (str != NULL)
+ __PHYSFS_setError(str);
+ return(rc);
+} /* zlib_err */
+
+
+/*
+ * Read an unsigned 32-bit int and swap to native byte order.
+ */
+static int readui32(void *in, PHYSFS_uint32 *val)
+{
+ PHYSFS_uint32 v;
+ BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapULE32(v);
+ return(1);
+} /* readui32 */
+
+
+/*
+ * Read an unsigned 16-bit int and swap to native byte order.
+ */
+static int readui16(void *in, PHYSFS_uint16 *val)
+{
+ PHYSFS_uint16 v;
+ BAIL_IF_MACRO(__PHYSFS_platformRead(in, &v, sizeof (v), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapULE16(v);
+ return(1);
+} /* readui16 */
+
+
+static PHYSFS_sint64 ZIP_read(fvoid *opaque, void *buf,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ ZIPfileinfo *finfo = (ZIPfileinfo *) opaque;
+ ZIPentry *entry = finfo->entry;
+ PHYSFS_sint64 retval = 0;
+ PHYSFS_sint64 maxread = ((PHYSFS_sint64) objSize) * objCount;
+ PHYSFS_sint64 avail = entry->uncompressed_size -
+ finfo->uncompressed_position;
+
+ BAIL_IF_MACRO(maxread == 0, NULL, 0); /* quick rejection. */
+
+ if (avail < maxread)
+ {
+ maxread = avail - (avail % objSize);
+ objCount = (PHYSFS_uint32) (maxread / objSize);
+ BAIL_IF_MACRO(objCount == 0, ERR_PAST_EOF, 0); /* quick rejection. */
+ __PHYSFS_setError(ERR_PAST_EOF); /* this is always true here. */
+ } /* if */
+
+ if (entry->compression_method == COMPMETH_NONE)
+ {
+ retval = __PHYSFS_platformRead(finfo->handle, buf, objSize, objCount);
+ } /* if */
+
+ else
+ {
+ finfo->stream.next_out = buf;
+ finfo->stream.avail_out = objSize * objCount;
+
+ while (retval < maxread)
+ {
+ PHYSFS_uint32 before = finfo->stream.total_out;
+ int rc;
+
+ if (finfo->stream.avail_in == 0)
+ {
+ PHYSFS_sint64 br;
+
+ br = entry->compressed_size - finfo->compressed_position;
+ if (br > 0)
+ {
+ if (br > ZIP_READBUFSIZE)
+ br = ZIP_READBUFSIZE;
+
+ br = __PHYSFS_platformRead(finfo->handle,
+ finfo->buffer,
+ 1, (PHYSFS_uint32) br);
+ if (br <= 0)
+ break;
+
+ finfo->compressed_position += (PHYSFS_uint32) br;
+ finfo->stream.next_in = finfo->buffer;
+ finfo->stream.avail_in = (PHYSFS_uint32) br;
+ } /* if */
+ } /* if */
+
+ rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH));
+ retval += (finfo->stream.total_out - before);
+
+ if (rc != Z_OK)
+ break;
+ } /* while */
+
+ retval /= objSize;
+ } /* else */
+
+ if (retval > 0)
+ finfo->uncompressed_position += (PHYSFS_uint32) (retval * objSize);
+
+ return(retval);
+} /* ZIP_read */
+
+
+static PHYSFS_sint64 ZIP_write(fvoid *opaque, const void *buf,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, -1);
+} /* ZIP_write */
+
+
+static int ZIP_eof(fvoid *opaque)
+{
+ ZIPfileinfo *finfo = (ZIPfileinfo *) opaque;
+ return(finfo->uncompressed_position >= finfo->entry->uncompressed_size);
+} /* ZIP_eof */
+
+
+static PHYSFS_sint64 ZIP_tell(fvoid *opaque)
+{
+ return(((ZIPfileinfo *) opaque)->uncompressed_position);
+} /* ZIP_tell */
+
+
+static int ZIP_seek(fvoid *opaque, PHYSFS_uint64 offset)
+{
+ ZIPfileinfo *finfo = (ZIPfileinfo *) opaque;
+ ZIPentry *entry = finfo->entry;
+ void *in = finfo->handle;
+
+ BAIL_IF_MACRO(offset > entry->uncompressed_size, ERR_PAST_EOF, 0);
+
+ if (entry->compression_method == COMPMETH_NONE)
+ {
+ PHYSFS_sint64 newpos = offset + entry->offset;
+ BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, newpos), NULL, 0);
+ finfo->uncompressed_position = (PHYSFS_uint32) offset;
+ } /* if */
+
+ else
+ {
+ /*
+ * If seeking backwards, we need to redecode the file
+ * from the start and throw away the compressed bits until we hit
+ * the offset we need. If seeking forward, we still need to
+ * decode, but we don't rewind first.
+ */
+ if (offset < finfo->uncompressed_position)
+ {
+ /* we do a copy so state is sane if inflateInit2() fails. */
+ z_stream str;
+ initializeZStream(&str);
+ if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK)
+ return(0);
+
+ if (!__PHYSFS_platformSeek(in, entry->offset))
+ return(0);
+
+ inflateEnd(&finfo->stream);
+ memcpy(&finfo->stream, &str, sizeof (z_stream));
+ finfo->uncompressed_position = finfo->compressed_position = 0;
+ } /* if */
+
+ while (finfo->uncompressed_position != offset)
+ {
+ PHYSFS_uint8 buf[512];
+ PHYSFS_uint32 maxread;
+
+ maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position);
+ if (maxread > sizeof (buf))
+ maxread = sizeof (buf);
+
+ if (ZIP_read(finfo, buf, maxread, 1) != 1)
+ return(0);
+ } /* while */
+ } /* else */
+
+ return(1);
+} /* ZIP_seek */
+
+
+static PHYSFS_sint64 ZIP_fileLength(fvoid *opaque)
+{
+ ZIPfileinfo *finfo = (ZIPfileinfo *) opaque;
+ return(finfo->entry->uncompressed_size);
+} /* ZIP_fileLength */
+
+
+static int ZIP_fileClose(fvoid *opaque)
+{
+ ZIPfileinfo *finfo = (ZIPfileinfo *) opaque;
+ BAIL_IF_MACRO(!__PHYSFS_platformClose(finfo->handle), NULL, 0);
+
+ if (finfo->entry->compression_method != COMPMETH_NONE)
+ inflateEnd(&finfo->stream);
+
+ if (finfo->buffer != NULL)
+ allocator.Free(finfo->buffer);
+
+ allocator.Free(finfo);
+ return(1);
+} /* ZIP_fileClose */
+
+
+static PHYSFS_sint64 zip_find_end_of_central_dir(void *in, PHYSFS_sint64 *len)
+{
+ PHYSFS_uint8 buf[256];
+ PHYSFS_sint32 i = 0;
+ PHYSFS_sint64 filelen;
+ PHYSFS_sint64 filepos;
+ PHYSFS_sint32 maxread;
+ PHYSFS_sint32 totalread = 0;
+ int found = 0;
+ PHYSFS_uint32 extra = 0;
+
+ filelen = __PHYSFS_platformFileLength(in);
+ BAIL_IF_MACRO(filelen == -1, NULL, 0); /* !!! FIXME: unlocalized string */
+ BAIL_IF_MACRO(filelen > 0xFFFFFFFF, "ZIP bigger than 2 gigs?!", 0);
+
+ /*
+ * Jump to the end of the file and start reading backwards.
+ * The last thing in the file is the zipfile comment, which is variable
+ * length, and the field that specifies its size is before it in the
+ * file (argh!)...this means that we need to scan backwards until we
+ * hit the end-of-central-dir signature. We can then sanity check that
+ * the comment was as big as it should be to make sure we're in the
+ * right place. The comment length field is 16 bits, so we can stop
+ * searching for that signature after a little more than 64k at most,
+ * and call it a corrupted zipfile.
+ */
+
+ if (sizeof (buf) < filelen)
+ {
+ filepos = filelen - sizeof (buf);
+ maxread = sizeof (buf);
+ } /* if */
+ else
+ {
+ filepos = 0;
+ maxread = (PHYSFS_uint32) filelen;
+ } /* else */
+
+ while ((totalread < filelen) && (totalread < 65557))
+ {
+ BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, filepos), NULL, -1);
+
+ /* make sure we catch a signature between buffers. */
+ if (totalread != 0)
+ {
+ if (__PHYSFS_platformRead(in, buf, maxread - 4, 1) != 1)
+ return(-1);
+ *((PHYSFS_uint32 *) (&buf[maxread - 4])) = extra;
+ totalread += maxread - 4;
+ } /* if */
+ else
+ {
+ if (__PHYSFS_platformRead(in, buf, maxread, 1) != 1)
+ return(-1);
+ totalread += maxread;
+ } /* else */
+
+ extra = *((PHYSFS_uint32 *) (&buf[0]));
+
+ for (i = maxread - 4; i > 0; i--)
+ {
+ if ((buf[i + 0] == 0x50) &&
+ (buf[i + 1] == 0x4B) &&
+ (buf[i + 2] == 0x05) &&
+ (buf[i + 3] == 0x06) )
+ {
+ found = 1; /* that's the signature! */
+ break;
+ } /* if */
+ } /* for */
+
+ if (found)
+ break;
+
+ filepos -= (maxread - 4);
+ } /* while */
+
+ BAIL_IF_MACRO(!found, ERR_NOT_AN_ARCHIVE, -1);
+
+ if (len != NULL)
+ *len = filelen;
+
+ return(filepos + i);
+} /* zip_find_end_of_central_dir */
+
+
+static int ZIP_isArchive(const char *filename, int forWriting)
+{
+ PHYSFS_uint32 sig;
+ int retval = 0;
+ void *in;
+
+ in = __PHYSFS_platformOpenRead(filename);
+ BAIL_IF_MACRO(in == NULL, NULL, 0);
+
+ /*
+ * The first thing in a zip file might be the signature of the
+ * first local file record, so it makes for a quick determination.
+ */
+ if (readui32(in, &sig))
+ {
+ retval = (sig == ZIP_LOCAL_FILE_SIG);
+ if (!retval)
+ {
+ /*
+ * No sig...might be a ZIP with data at the start
+ * (a self-extracting executable, etc), so we'll have to do
+ * it the hard way...
+ */
+ retval = (zip_find_end_of_central_dir(in, NULL) != -1);
+ } /* if */
+ } /* if */
+
+ __PHYSFS_platformClose(in);
+ return(retval);
+} /* ZIP_isArchive */
+
+
+static void zip_free_entries(ZIPentry *entries, PHYSFS_uint32 max)
+{
+ PHYSFS_uint32 i;
+ for (i = 0; i < max; i++)
+ {
+ ZIPentry *entry = &entries[i];
+ if (entry->name != NULL)
+ allocator.Free(entry->name);
+ } /* for */
+
+ allocator.Free(entries);
+} /* zip_free_entries */
+
+
+/*
+ * This will find the ZIPentry associated with a path in platform-independent
+ * notation. Directories don't have ZIPentries associated with them, but
+ * (*isDir) will be set to non-zero if a dir was hit.
+ */
+static ZIPentry *zip_find_entry(ZIPinfo *info, const char *path, int *isDir)
+{
+ ZIPentry *a = info->entries;
+ PHYSFS_sint32 pathlen = strlen(path);
+ PHYSFS_sint32 lo = 0;
+ PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
+ PHYSFS_sint32 middle;
+ const char *thispath = NULL;
+ int rc;
+
+ while (lo <= hi)
+ {
+ middle = lo + ((hi - lo) / 2);
+ thispath = a[middle].name;
+ rc = strncmp(path, thispath, pathlen);
+
+ if (rc > 0)
+ lo = middle + 1;
+
+ else if (rc < 0)
+ hi = middle - 1;
+
+ else /* substring match...might be dir or entry or nothing. */
+ {
+ if (isDir != NULL)
+ {
+ *isDir = (thispath[pathlen] == '/');
+ if (*isDir)
+ return(NULL);
+ } /* if */
+
+ if (thispath[pathlen] == '\0') /* found entry? */
+ return(&a[middle]);
+ else
+ hi = middle - 1; /* adjust search params, try again. */
+ } /* if */
+ } /* while */
+
+ if (isDir != NULL)
+ *isDir = 0;
+
+ BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
+} /* zip_find_entry */
+
+
+/* Convert paths from old, buggy DOS zippers... */
+static void zip_convert_dos_path(ZIPentry *entry, char *path)
+{
+ PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entry->version >> 8) & 0xFF);
+ if (hosttype == 0) /* FS_FAT_ */
+ {
+ while (*path)
+ {
+ if (*path == '\\')
+ *path = '/';
+ path++;
+ } /* while */
+ } /* if */
+} /* zip_convert_dos_path */
+
+
+static void zip_expand_symlink_path(char *path)
+{
+ char *ptr = path;
+ char *prevptr = path;
+
+ while (1)
+ {
+ ptr = strchr(ptr, '/');
+ if (ptr == NULL)
+ break;
+
+ if (*(ptr + 1) == '.')
+ {
+ if (*(ptr + 2) == '/')
+ {
+ /* current dir in middle of string: ditch it. */
+ memmove(ptr, ptr + 2, strlen(ptr + 2) + 1);
+ } /* else if */
+
+ else if (*(ptr + 2) == '\0')
+ {
+ /* current dir at end of string: ditch it. */
+ *ptr = '\0';
+ } /* else if */
+
+ else if (*(ptr + 2) == '.')
+ {
+ if (*(ptr + 3) == '/')
+ {
+ /* parent dir in middle: move back one, if possible. */
+ memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1);
+ ptr = prevptr;
+ while (prevptr != path)
+ {
+ prevptr--;
+ if (*prevptr == '/')
+ {
+ prevptr++;
+ break;
+ } /* if */
+ } /* while */
+ } /* if */
+
+ if (*(ptr + 3) == '\0')
+ {
+ /* parent dir at end: move back one, if possible. */
+ *prevptr = '\0';
+ } /* if */
+ } /* if */
+ } /* if */
+ else
+ {
+ prevptr = ptr;
+ } /* else */
+ } /* while */
+} /* zip_expand_symlink_path */
+
+/* (forward reference: zip_follow_symlink and zip_resolve call each other.) */
+static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry);
+
+/*
+ * Look for the entry named by (path). If it exists, resolve it, and return
+ * a pointer to that entry. If it's another symlink, keep resolving until you
+ * hit a real file and then return a pointer to the final non-symlink entry.
+ * If there's a problem, return NULL. (path) is always free()'d by this
+ * function.
+ */
+static ZIPentry *zip_follow_symlink(void *in, ZIPinfo *info, char *path)
+{
+ ZIPentry *entry;
+
+ zip_expand_symlink_path(path);
+ entry = zip_find_entry(info, path, NULL);
+ if (entry != NULL)
+ {
+ if (!zip_resolve(in, info, entry)) /* recursive! */
+ entry = NULL;
+ else
+ {
+ if (entry->symlink != NULL)
+ entry = entry->symlink;
+ } /* else */
+ } /* if */
+
+ allocator.Free(path);
+ return(entry);
+} /* zip_follow_symlink */
+
+
+static int zip_resolve_symlink(void *in, ZIPinfo *info, ZIPentry *entry)
+{
+ char *path;
+ PHYSFS_uint32 size = entry->uncompressed_size;
+ int rc = 0;
+
+ /*
+ * We've already parsed the local file header of the symlink at this
+ * point. Now we need to read the actual link from the file data and
+ * follow it.
+ */
+
+ BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0);
+
+ path = (char *) allocator.Malloc(size + 1);
+ BAIL_IF_MACRO(path == NULL, ERR_OUT_OF_MEMORY, 0);
+
+ if (entry->compression_method == COMPMETH_NONE)
+ rc = (__PHYSFS_platformRead(in, path, size, 1) == 1);
+
+ else /* symlink target path is compressed... */
+ {
+ z_stream stream;
+ PHYSFS_uint32 complen = entry->compressed_size;
+ PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen);
+ if (compressed != NULL)
+ {
+ if (__PHYSFS_platformRead(in, compressed, complen, 1) == 1)
+ {
+ initializeZStream(&stream);
+ stream.next_in = compressed;
+ stream.avail_in = complen;
+ stream.next_out = (unsigned char *) path;
+ stream.avail_out = size;
+ if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK)
+ {
+ rc = zlib_err(inflate(&stream, Z_FINISH));
+ inflateEnd(&stream);
+
+ /* both are acceptable outcomes... */
+ rc = ((rc == Z_OK) || (rc == Z_STREAM_END));
+ } /* if */
+ } /* if */
+ __PHYSFS_smallFree(compressed);
+ } /* if */
+ } /* else */
+
+ if (!rc)
+ allocator.Free(path);
+ else
+ {
+ path[entry->uncompressed_size] = '\0'; /* null-terminate it. */
+ zip_convert_dos_path(entry, path);
+ entry->symlink = zip_follow_symlink(in, info, path);
+ } /* else */
+
+ return(entry->symlink != NULL);
+} /* zip_resolve_symlink */
+
+
+/*
+ * Parse the local file header of an entry, and update entry->offset.
+ */
+static int zip_parse_local(void *in, ZIPentry *entry)
+{
+ PHYSFS_uint32 ui32;
+ PHYSFS_uint16 ui16;
+ PHYSFS_uint16 fnamelen;
+ PHYSFS_uint16 extralen;
+
+ /*
+ * crc and (un)compressed_size are always zero if this is a "JAR"
+ * archive created with Sun's Java tools, apparently. We only
+ * consider this archive corrupted if those entries don't match and
+ * aren't zero. That seems to work well.
+ */
+
+ BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, entry->offset), NULL, 0);
+ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+ BAIL_IF_MACRO(ui32 != ZIP_LOCAL_FILE_SIG, ERR_CORRUPTED, 0);
+ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+ BAIL_IF_MACRO(ui16 != entry->version_needed, ERR_CORRUPTED, 0);
+ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits. */
+ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+ BAIL_IF_MACRO(ui16 != entry->compression_method, ERR_CORRUPTED, 0);
+ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0); /* date/time */
+ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+ BAIL_IF_MACRO(ui32 && (ui32 != entry->crc), ERR_CORRUPTED, 0);
+ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+ BAIL_IF_MACRO(ui32 && (ui32 != entry->compressed_size), ERR_CORRUPTED, 0);
+ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+ BAIL_IF_MACRO(ui32 && (ui32 != entry->uncompressed_size),ERR_CORRUPTED,0);
+ BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0);
+ BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0);
+
+ entry->offset += fnamelen + extralen + 30;
+ return(1);
+} /* zip_parse_local */
+
+
+static int zip_resolve(void *in, ZIPinfo *info, ZIPentry *entry)
+{
+ int retval = 1;
+ ZipResolveType resolve_type = entry->resolved;
+
+ /* Don't bother if we've failed to resolve this entry before. */
+ BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_FILE, ERR_CORRUPTED, 0);
+ BAIL_IF_MACRO(resolve_type == ZIP_BROKEN_SYMLINK, ERR_CORRUPTED, 0);
+
+ /* uhoh...infinite symlink loop! */
+ BAIL_IF_MACRO(resolve_type == ZIP_RESOLVING, ERR_SYMLINK_LOOP, 0);
+
+ /*
+ * We fix up the offset to point to the actual data on the
+ * first open, since we don't want to seek across the whole file on
+ * archive open (can be SLOW on large, CD-stored files), but we
+ * need to check the local file header...not just for corruption,
+ * but since it stores offset info the central directory does not.
+ */
+ if (resolve_type != ZIP_RESOLVED)
+ {
+ entry->resolved = ZIP_RESOLVING;
+
+ retval = zip_parse_local(in, entry);
+ if (retval)
+ {
+ /*
+ * If it's a symlink, find the original file. This will cause
+ * resolution of other entries (other symlinks and, eventually,
+ * the real file) if all goes well.
+ */
+ if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
+ retval = zip_resolve_symlink(in, info, entry);
+ } /* if */
+
+ if (resolve_type == ZIP_UNRESOLVED_SYMLINK)
+ entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK);
+ else if (resolve_type == ZIP_UNRESOLVED_FILE)
+ entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE);
+ } /* if */
+
+ return(retval);
+} /* zip_resolve */
+
+
+static int zip_version_does_symlinks(PHYSFS_uint32 version)
+{
+ int retval = 0;
+ PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF);
+
+ switch (hosttype)
+ {
+ /*
+ * These are the platforms that can NOT build an archive with
+ * symlinks, according to the Info-ZIP project.
+ */
+ case 0: /* FS_FAT_ */
+ case 1: /* AMIGA_ */
+ case 2: /* VMS_ */
+ case 4: /* VM_CSM_ */
+ case 6: /* FS_HPFS_ */
+ case 11: /* FS_NTFS_ */
+ case 14: /* FS_VFAT_ */
+ case 13: /* ACORN_ */
+ case 15: /* MVS_ */
+ case 18: /* THEOS_ */
+ break; /* do nothing. */
+
+ default: /* assume the rest to be unix-like. */
+ retval = 1;
+ break;
+ } /* switch */
+
+ return(retval);
+} /* zip_version_does_symlinks */
+
+
+static int zip_entry_is_symlink(ZIPentry *entry)
+{
+ return((entry->resolved == ZIP_UNRESOLVED_SYMLINK) ||
+ (entry->resolved == ZIP_BROKEN_SYMLINK) ||
+ (entry->symlink));
+} /* zip_entry_is_symlink */
+
+
+static int zip_has_symlink_attr(ZIPentry *entry, PHYSFS_uint32 extern_attr)
+{
+ PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF);
+
+ return (
+ (zip_version_does_symlinks(entry->version)) &&
+ (entry->uncompressed_size > 0) &&
+ ((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK)
+ );
+} /* zip_has_symlink_attr */
+
+
+static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime)
+{
+#ifdef _WIN32_WCE
+ /* We have no struct tm and no mktime right now.
+ FIXME: This should probably be fixed at some point.
+ */
+ return -1;
+#else
+ PHYSFS_uint32 dosdate;
+ struct tm unixtime;
+ memset(&unixtime, '\0', sizeof (unixtime));
+
+ dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF);
+ dostime &= 0xFFFF;
+
+ /* dissect date */
+ unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80;
+ unixtime.tm_mon = ((dosdate >> 5) & 0x0F) - 1;
+ unixtime.tm_mday = ((dosdate ) & 0x1F);
+
+ /* dissect time */
+ unixtime.tm_hour = ((dostime >> 11) & 0x1F);
+ unixtime.tm_min = ((dostime >> 5) & 0x3F);
+ unixtime.tm_sec = ((dostime << 1) & 0x3E);
+
+ /* let mktime calculate daylight savings time. */
+ unixtime.tm_isdst = -1;
+
+ return((PHYSFS_sint64) mktime(&unixtime));
+#endif
+} /* zip_dos_time_to_physfs_time */
+
+
+static int zip_load_entry(void *in, ZIPentry *entry, PHYSFS_uint32 ofs_fixup)
+{
+ PHYSFS_uint16 fnamelen, extralen, commentlen;
+ PHYSFS_uint32 external_attr;
+ PHYSFS_uint16 ui16;
+ PHYSFS_uint32 ui32;
+ PHYSFS_sint64 si64;
+
+ /* sanity check with central directory signature... */
+ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+ BAIL_IF_MACRO(ui32 != ZIP_CENTRAL_DIR_SIG, ERR_CORRUPTED, 0);
+
+ /* Get the pertinent parts of the record... */
+ BAIL_IF_MACRO(!readui16(in, &entry->version), NULL, 0);
+ BAIL_IF_MACRO(!readui16(in, &entry->version_needed), NULL, 0);
+ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* general bits */
+ BAIL_IF_MACRO(!readui16(in, &entry->compression_method), NULL, 0);
+ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+ entry->last_mod_time = zip_dos_time_to_physfs_time(ui32);
+ BAIL_IF_MACRO(!readui32(in, &entry->crc), NULL, 0);
+ BAIL_IF_MACRO(!readui32(in, &entry->compressed_size), NULL, 0);
+ BAIL_IF_MACRO(!readui32(in, &entry->uncompressed_size), NULL, 0);
+ BAIL_IF_MACRO(!readui16(in, &fnamelen), NULL, 0);
+ BAIL_IF_MACRO(!readui16(in, &extralen), NULL, 0);
+ BAIL_IF_MACRO(!readui16(in, &commentlen), NULL, 0);
+ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* disk number start */
+ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0); /* internal file attribs */
+ BAIL_IF_MACRO(!readui32(in, &external_attr), NULL, 0);
+ BAIL_IF_MACRO(!readui32(in, &entry->offset), NULL, 0);
+ entry->offset += ofs_fixup;
+
+ entry->symlink = NULL; /* will be resolved later, if necessary. */
+ entry->resolved = (zip_has_symlink_attr(entry, external_attr)) ?
+ ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE;
+
+ entry->name = (char *) allocator.Malloc(fnamelen + 1);
+ BAIL_IF_MACRO(entry->name == NULL, ERR_OUT_OF_MEMORY, 0);
+ if (__PHYSFS_platformRead(in, entry->name, fnamelen, 1) != 1)
+ goto zip_load_entry_puked;
+
+ entry->name[fnamelen] = '\0'; /* null-terminate the filename. */
+ zip_convert_dos_path(entry, entry->name);
+
+ si64 = __PHYSFS_platformTell(in);
+ if (si64 == -1)
+ goto zip_load_entry_puked;
+
+ /* seek to the start of the next entry in the central directory... */
+ if (!__PHYSFS_platformSeek(in, si64 + extralen + commentlen))
+ goto zip_load_entry_puked;
+
+ return(1); /* success. */
+
+zip_load_entry_puked:
+ allocator.Free(entry->name);
+ return(0); /* failure. */
+} /* zip_load_entry */
+
+
+static int zip_entry_cmp(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ ZIPentry *a = (ZIPentry *) _a;
+ return(strcmp(a[one].name, a[two].name));
+} /* zip_entry_cmp */
+
+
+static void zip_entry_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two)
+{
+ ZIPentry tmp;
+ ZIPentry *first = &(((ZIPentry *) _a)[one]);
+ ZIPentry *second = &(((ZIPentry *) _a)[two]);
+ memcpy(&tmp, first, sizeof (ZIPentry));
+ memcpy(first, second, sizeof (ZIPentry));
+ memcpy(second, &tmp, sizeof (ZIPentry));
+} /* zip_entry_swap */
+
+
+static int zip_load_entries(void *in, ZIPinfo *info,
+ PHYSFS_uint32 data_ofs, PHYSFS_uint32 central_ofs)
+{
+ PHYSFS_uint32 max = info->entryCount;
+ PHYSFS_uint32 i;
+
+ BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, central_ofs), NULL, 0);
+
+ info->entries = (ZIPentry *) allocator.Malloc(sizeof (ZIPentry) * max);
+ BAIL_IF_MACRO(info->entries == NULL, ERR_OUT_OF_MEMORY, 0);
+
+ for (i = 0; i < max; i++)
+ {
+ if (!zip_load_entry(in, &info->entries[i], data_ofs))
+ {
+ zip_free_entries(info->entries, i);
+ return(0);
+ } /* if */
+ } /* for */
+
+ __PHYSFS_sort(info->entries, max, zip_entry_cmp, zip_entry_swap);
+ return(1);
+} /* zip_load_entries */
+
+
+static int zip_parse_end_of_central_dir(void *in, ZIPinfo *info,
+ PHYSFS_uint32 *data_start,
+ PHYSFS_uint32 *central_dir_ofs)
+{
+ PHYSFS_uint32 ui32;
+ PHYSFS_uint16 ui16;
+ PHYSFS_sint64 len;
+ PHYSFS_sint64 pos;
+
+ /* find the end-of-central-dir record, and seek to it. */
+ pos = zip_find_end_of_central_dir(in, &len);
+ BAIL_IF_MACRO(pos == -1, NULL, 0);
+ BAIL_IF_MACRO(!__PHYSFS_platformSeek(in, pos), NULL, 0);
+
+ /* check signature again, just in case. */
+ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+ BAIL_IF_MACRO(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, ERR_NOT_AN_ARCHIVE, 0);
+
+ /* number of this disk */
+ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+ BAIL_IF_MACRO(ui16 != 0, ERR_UNSUPPORTED_ARCHIVE, 0);
+
+ /* number of the disk with the start of the central directory */
+ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+ BAIL_IF_MACRO(ui16 != 0, ERR_UNSUPPORTED_ARCHIVE, 0);
+
+ /* total number of entries in the central dir on this disk */
+ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+
+ /* total number of entries in the central dir */
+ BAIL_IF_MACRO(!readui16(in, &info->entryCount), NULL, 0);
+ BAIL_IF_MACRO(ui16 != info->entryCount, ERR_UNSUPPORTED_ARCHIVE, 0);
+
+ /* size of the central directory */
+ BAIL_IF_MACRO(!readui32(in, &ui32), NULL, 0);
+
+ /* offset of central directory */
+ BAIL_IF_MACRO(!readui32(in, central_dir_ofs), NULL, 0);
+ BAIL_IF_MACRO(pos < *central_dir_ofs + ui32, ERR_UNSUPPORTED_ARCHIVE, 0);
+
+ /*
+ * For self-extracting archives, etc, there's crapola in the file
+ * before the zipfile records; we calculate how much data there is
+ * prepended by determining how far the central directory offset is
+ * from where it is supposed to be (start of end-of-central-dir minus
+ * sizeof central dir)...the difference in bytes is how much arbitrary
+ * data is at the start of the physical file.
+ */
+ *data_start = (PHYSFS_uint32) (pos - (*central_dir_ofs + ui32));
+
+ /* Now that we know the difference, fix up the central dir offset... */
+ *central_dir_ofs += *data_start;
+
+ /* zipfile comment length */
+ BAIL_IF_MACRO(!readui16(in, &ui16), NULL, 0);
+
+ /*
+ * Make sure that the comment length matches to the end of file...
+ * If it doesn't, we're either in the wrong part of the file, or the
+ * file is corrupted, but we give up either way.
+ */
+ BAIL_IF_MACRO((pos + 22 + ui16) != len, ERR_UNSUPPORTED_ARCHIVE, 0);
+
+ return(1); /* made it. */
+} /* zip_parse_end_of_central_dir */
+
+
+static ZIPinfo *zip_create_zipinfo(const char *name)
+{
+ char *ptr;
+ ZIPinfo *info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo));
+ BAIL_IF_MACRO(info == NULL, ERR_OUT_OF_MEMORY, 0);
+ memset(info, '\0', sizeof (ZIPinfo));
+
+ ptr = (char *) allocator.Malloc(strlen(name) + 1);
+ if (ptr == NULL)
+ {
+ allocator.Free(info);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* if */
+
+ info->archiveName = ptr;
+ strcpy(info->archiveName, name);
+ return(info);
+} /* zip_create_zipinfo */
+
+
+static void *ZIP_openArchive(const char *name, int forWriting)
+{
+ void *in = NULL;
+ ZIPinfo *info = NULL;
+ PHYSFS_uint32 data_start;
+ PHYSFS_uint32 cent_dir_ofs;
+
+ BAIL_IF_MACRO(forWriting, ERR_ARC_IS_READ_ONLY, NULL);
+
+ if ((in = __PHYSFS_platformOpenRead(name)) == NULL)
+ goto zip_openarchive_failed;
+
+ if ((info = zip_create_zipinfo(name)) == NULL)
+ goto zip_openarchive_failed;
+
+ if (!zip_parse_end_of_central_dir(in, info, &data_start, ¢_dir_ofs))
+ goto zip_openarchive_failed;
+
+ if (!zip_load_entries(in, info, data_start, cent_dir_ofs))
+ goto zip_openarchive_failed;
+
+ __PHYSFS_platformClose(in);
+ return(info);
+
+zip_openarchive_failed:
+ if (info != NULL)
+ {
+ if (info->archiveName != NULL)
+ allocator.Free(info->archiveName);
+ allocator.Free(info);
+ } /* if */
+
+ if (in != NULL)
+ __PHYSFS_platformClose(in);
+
+ return(NULL);
+} /* ZIP_openArchive */
+
+
+static PHYSFS_sint32 zip_find_start_of_dir(ZIPinfo *info, const char *path,
+ int stop_on_first_find)
+{
+ PHYSFS_sint32 lo = 0;
+ PHYSFS_sint32 hi = (PHYSFS_sint32) (info->entryCount - 1);
+ PHYSFS_sint32 middle;
+ PHYSFS_uint32 dlen = strlen(path);
+ PHYSFS_sint32 retval = -1;
+ const char *name;
+ int rc;
+
+ if (*path == '\0') /* root dir? */
+ return(0);
+
+ if ((dlen > 0) && (path[dlen - 1] == '/')) /* ignore trailing slash. */
+ dlen--;
+
+ while (lo <= hi)
+ {
+ middle = lo + ((hi - lo) / 2);
+ name = info->entries[middle].name;
+ rc = strncmp(path, name, dlen);
+ if (rc == 0)
+ {
+ char ch = name[dlen];
+ if ('/' < ch) /* make sure this isn't just a substr match. */
+ rc = -1;
+ else if ('/' > ch)
+ rc = 1;
+ else
+ {
+ if (stop_on_first_find) /* Just checking dir's existance? */
+ return(middle);
+
+ if (name[dlen + 1] == '\0') /* Skip initial dir entry. */
+ return(middle + 1);
+
+ /* there might be more entries earlier in the list. */
+ retval = middle;
+ hi = middle - 1;
+ } /* else */
+ } /* if */
+
+ if (rc > 0)
+ lo = middle + 1;
+ else
+ hi = middle - 1;
+ } /* while */
+
+ return(retval);
+} /* zip_find_start_of_dir */
+
+
+/*
+ * Moved to seperate function so we can use alloca then immediately throw
+ * away the allocated stack space...
+ */
+static void doEnumCallback(PHYSFS_EnumFilesCallback cb, void *callbackdata,
+ const char *odir, const char *str, PHYSFS_sint32 ln)
+{
+ char *newstr = __PHYSFS_smallAlloc(ln + 1);
+ if (newstr == NULL)
+ return;
+
+ memcpy(newstr, str, ln);
+ newstr[ln] = '\0';
+ cb(callbackdata, odir, newstr);
+ __PHYSFS_smallFree(newstr);
+} /* doEnumCallback */
+
+
+static void ZIP_enumerateFiles(dvoid *opaque, const char *dname,
+ int omitSymLinks, PHYSFS_EnumFilesCallback cb,
+ const char *origdir, void *callbackdata)
+{
+ ZIPinfo *info = ((ZIPinfo *) opaque);
+ PHYSFS_sint32 dlen, dlen_inc, max, i;
+
+ i = zip_find_start_of_dir(info, dname, 0);
+ if (i == -1) /* no such directory. */
+ return;
+
+ dlen = strlen(dname);
+ if ((dlen > 0) && (dname[dlen - 1] == '/')) /* ignore trailing slash. */
+ dlen--;
+
+ dlen_inc = ((dlen > 0) ? 1 : 0) + dlen;
+ max = (PHYSFS_sint32) info->entryCount;
+ while (i < max)
+ {
+ char *e = info->entries[i].name;
+ if ((dlen) && ((strncmp(e, dname, dlen) != 0) || (e[dlen] != '/')))
+ break; /* past end of this dir; we're done. */
+
+ if ((omitSymLinks) && (zip_entry_is_symlink(&info->entries[i])))
+ i++;
+ else
+ {
+ char *add = e + dlen_inc;
+ char *ptr = strchr(add, '/');
+ PHYSFS_sint32 ln = (PHYSFS_sint32) ((ptr) ? ptr-add : strlen(add));
+ doEnumCallback(cb, callbackdata, origdir, add, ln);
+ ln += dlen_inc; /* point past entry to children... */
+
+ /* increment counter and skip children of subdirs... */
+ while ((++i < max) && (ptr != NULL))
+ {
+ char *e_new = info->entries[i].name;
+ if ((strncmp(e, e_new, ln) != 0) || (e_new[ln] != '/'))
+ break;
+ } /* while */
+ } /* else */
+ } /* while */
+} /* ZIP_enumerateFiles */
+
+
+static int ZIP_exists(dvoid *opaque, const char *name)
+{
+ int isDir;
+ ZIPinfo *info = (ZIPinfo *) opaque;
+ ZIPentry *entry = zip_find_entry(info, name, &isDir);
+ return((entry != NULL) || (isDir));
+} /* ZIP_exists */
+
+
+static PHYSFS_sint64 ZIP_getLastModTime(dvoid *opaque,
+ const char *name,
+ int *fileExists)
+{
+ int isDir;
+ ZIPinfo *info = (ZIPinfo *) opaque;
+ ZIPentry *entry = zip_find_entry(info, name, &isDir);
+
+ *fileExists = ((isDir) || (entry != NULL));
+ if (isDir)
+ return(1); /* Best I can do for a dir... */
+
+ BAIL_IF_MACRO(entry == NULL, NULL, -1);
+ return(entry->last_mod_time);
+} /* ZIP_getLastModTime */
+
+
+static int ZIP_isDirectory(dvoid *opaque, const char *name, int *fileExists)
+{
+ ZIPinfo *info = (ZIPinfo *) opaque;
+ int isDir;
+ ZIPentry *entry = zip_find_entry(info, name, &isDir);
+
+ *fileExists = ((isDir) || (entry != NULL));
+ if (isDir)
+ return(1); /* definitely a dir. */
+
+ /* Follow symlinks. This means we might need to resolve entries. */
+ BAIL_IF_MACRO(entry == NULL, ERR_NO_SUCH_FILE, 0);
+
+ if (entry->resolved == ZIP_UNRESOLVED_SYMLINK) /* gotta resolve it. */
+ {
+ int rc;
+ void *in = __PHYSFS_platformOpenRead(info->archiveName);
+ BAIL_IF_MACRO(in == NULL, NULL, 0);
+ rc = zip_resolve(in, info, entry);
+ __PHYSFS_platformClose(in);
+ if (!rc)
+ return(0);
+ } /* if */
+
+ BAIL_IF_MACRO(entry->resolved == ZIP_BROKEN_SYMLINK, NULL, 0);
+ BAIL_IF_MACRO(entry->symlink == NULL, ERR_NOT_A_DIR, 0);
+
+ return(zip_find_start_of_dir(info, entry->symlink->name, 1) >= 0);
+} /* ZIP_isDirectory */
+
+
+static int ZIP_isSymLink(dvoid *opaque, const char *name, int *fileExists)
+{
+ int isDir;
+ ZIPentry *entry = zip_find_entry((ZIPinfo *) opaque, name, &isDir);
+ *fileExists = ((isDir) || (entry != NULL));
+ BAIL_IF_MACRO(entry == NULL, NULL, 0);
+ return(zip_entry_is_symlink(entry));
+} /* ZIP_isSymLink */
+
+
+static void *zip_get_file_handle(const char *fn, ZIPinfo *inf, ZIPentry *entry)
+{
+ int success;
+ void *retval = __PHYSFS_platformOpenRead(fn);
+ BAIL_IF_MACRO(retval == NULL, NULL, NULL);
+
+ success = zip_resolve(retval, inf, entry);
+ if (success)
+ {
+ PHYSFS_sint64 offset;
+ offset = ((entry->symlink) ? entry->symlink->offset : entry->offset);
+ success = __PHYSFS_platformSeek(retval, offset);
+ } /* if */
+
+ if (!success)
+ {
+ __PHYSFS_platformClose(retval);
+ retval = NULL;
+ } /* if */
+
+ return(retval);
+} /* zip_get_file_handle */
+
+
+static fvoid *ZIP_openRead(dvoid *opaque, const char *fnm, int *fileExists)
+{
+ ZIPinfo *info = (ZIPinfo *) opaque;
+ ZIPentry *entry = zip_find_entry(info, fnm, NULL);
+ ZIPfileinfo *finfo = NULL;
+ void *in;
+
+ *fileExists = (entry != NULL);
+ BAIL_IF_MACRO(entry == NULL, NULL, NULL);
+
+ in = zip_get_file_handle(info->archiveName, info, entry);
+ BAIL_IF_MACRO(in == NULL, NULL, NULL);
+
+ finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo));
+ if (finfo == NULL)
+ {
+ __PHYSFS_platformClose(in);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* if */
+
+ memset(finfo, '\0', sizeof (ZIPfileinfo));
+ finfo->handle = in;
+ finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry);
+ initializeZStream(&finfo->stream);
+ if (finfo->entry->compression_method != COMPMETH_NONE)
+ {
+ if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK)
+ {
+ ZIP_fileClose(finfo);
+ return(NULL);
+ } /* if */
+
+ finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE);
+ if (finfo->buffer == NULL)
+ {
+ ZIP_fileClose(finfo);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* if */
+ } /* if */
+
+ return(finfo);
+} /* ZIP_openRead */
+
+
+static fvoid *ZIP_openWrite(dvoid *opaque, const char *filename)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* ZIP_openWrite */
+
+
+static fvoid *ZIP_openAppend(dvoid *opaque, const char *filename)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, NULL);
+} /* ZIP_openAppend */
+
+
+static void ZIP_dirClose(dvoid *opaque)
+{
+ ZIPinfo *zi = (ZIPinfo *) (opaque);
+ zip_free_entries(zi->entries, zi->entryCount);
+ allocator.Free(zi->archiveName);
+ allocator.Free(zi);
+} /* ZIP_dirClose */
+
+
+static int ZIP_remove(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* ZIP_remove */
+
+
+static int ZIP_mkdir(dvoid *opaque, const char *name)
+{
+ BAIL_MACRO(ERR_NOT_SUPPORTED, 0);
+} /* ZIP_mkdir */
+
+
+const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP =
+{
+ "ZIP",
+ ZIP_ARCHIVE_DESCRIPTION,
+ "Ryan C. Gordon <icculus@icculus.org>",
+ "http://icculus.org/physfs/",
+};
+
+
+const PHYSFS_Archiver __PHYSFS_Archiver_ZIP =
+{
+ &__PHYSFS_ArchiveInfo_ZIP,
+ ZIP_isArchive, /* isArchive() method */
+ ZIP_openArchive, /* openArchive() method */
+ ZIP_enumerateFiles, /* enumerateFiles() method */
+ ZIP_exists, /* exists() method */
+ ZIP_isDirectory, /* isDirectory() method */
+ ZIP_isSymLink, /* isSymLink() method */
+ ZIP_getLastModTime, /* getLastModTime() method */
+ ZIP_openRead, /* openRead() method */
+ ZIP_openWrite, /* openWrite() method */
+ ZIP_openAppend, /* openAppend() method */
+ ZIP_remove, /* remove() method */
+ ZIP_mkdir, /* mkdir() method */
+ ZIP_dirClose, /* dirClose() method */
+ ZIP_read, /* read() method */
+ ZIP_write, /* write() method */
+ ZIP_eof, /* eof() method */
+ ZIP_tell, /* tell() method */
+ ZIP_seek, /* seek() method */
+ ZIP_fileLength, /* fileLength() method */
+ ZIP_fileClose /* fileClose() method */
+};
+
+#endif /* defined PHYSFS_SUPPORTS_ZIP */
+
+/* end of zip.c ... */
+
--- /dev/null
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+//
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+//
+[assembly: AssemblyTitle("PhysFS.NET")]
+[assembly: AssemblyDescription("PhysFS Bindings for .NET")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PhysFS.NET")]
+[assembly: AssemblyCopyright("(c)2003 Gregory S. Read")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+//
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("1.0.*")]
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the
+// Microsoft .NET Framework documentation for more information on assembly signing.
+//
+// Use the attributes below to control which key is used for signing.
+//
+// Notes:
+// (*) If no key is specified, the assembly is not signed.
+// (*) KeyName refers to a key that has been installed in the Crypto Service
+// Provider (CSP) on your machine. KeyFile refers to a file which contains
+// a key.
+// (*) If the KeyFile and the KeyName values are both specified, the
+// following processing occurs:
+// (1) If the KeyName can be found in the CSP, that key is used.
+// (2) If the KeyName does not exist and the KeyFile does exist, the key
+// in the KeyFile is installed into the CSP and used.
+// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
+// When specifying the KeyFile, the location of the KeyFile should be
+// relative to the project output directory which is
+// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
+// located in the project directory, you would specify the AssemblyKeyFile
+// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
+// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+// documentation for more information on this.
+//
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
+[assembly: AssemblyKeyName("")]
--- /dev/null
+<VisualStudioProject>
+ <CSHARP
+ ProjectType = "Local"
+ ProductVersion = "7.0.9466"
+ SchemaVersion = "1.0"
+ ProjectGuid = "{C6205A43-3D4D-41E6-872C-96CD7BE55230}"
+ >
+ <Build>
+ <Settings
+ ApplicationIcon = ""
+ AssemblyKeyContainerName = ""
+ AssemblyName = "PhysFS.NET"
+ AssemblyOriginatorKeyFile = ""
+ DefaultClientScript = "JScript"
+ DefaultHTMLPageLayout = "Grid"
+ DefaultTargetSchema = "IE50"
+ DelaySign = "false"
+ OutputType = "Library"
+ RootNamespace = "PhysFS.NET"
+ StartupObject = ""
+ >
+ <Config
+ Name = "Debug"
+ AllowUnsafeBlocks = "true"
+ BaseAddress = "285212672"
+ CheckForOverflowUnderflow = "false"
+ ConfigurationOverrideFile = ""
+ DefineConstants = "DEBUG;TRACE"
+ DocumentationFile = ""
+ DebugSymbols = "true"
+ FileAlignment = "4096"
+ IncrementalBuild = "true"
+ Optimize = "false"
+ OutputPath = "bin\Debug\"
+ RegisterForComInterop = "false"
+ RemoveIntegerChecks = "false"
+ TreatWarningsAsErrors = "false"
+ WarningLevel = "4"
+ />
+ <Config
+ Name = "Release"
+ AllowUnsafeBlocks = "true"
+ BaseAddress = "285212672"
+ CheckForOverflowUnderflow = "false"
+ ConfigurationOverrideFile = ""
+ DefineConstants = "TRACE"
+ DocumentationFile = ""
+ DebugSymbols = "false"
+ FileAlignment = "4096"
+ IncrementalBuild = "false"
+ Optimize = "true"
+ OutputPath = "bin\Release\"
+ RegisterForComInterop = "false"
+ RemoveIntegerChecks = "false"
+ TreatWarningsAsErrors = "false"
+ WarningLevel = "4"
+ />
+ </Settings>
+ <References>
+ <Reference
+ Name = "System"
+ AssemblyName = "System"
+ HintPath = "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.dll"
+ />
+ <Reference
+ Name = "System.Data"
+ AssemblyName = "System.Data"
+ HintPath = "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Data.dll"
+ />
+ <Reference
+ Name = "System.XML"
+ AssemblyName = "System.Xml"
+ HintPath = "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.XML.dll"
+ />
+ <Reference
+ Name = "System.Drawing"
+ AssemblyName = "System.Drawing"
+ HintPath = "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Drawing.dll"
+ />
+ <Reference
+ Name = "System.Windows.Forms"
+ AssemblyName = "System.Windows.Forms"
+ HintPath = "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Windows.Forms.dll"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File
+ RelPath = "AssemblyInfo.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "PhysFS.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "PhysFS_DLL.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "PhysFSFileStream.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ </Include>
+ </Files>
+ </CSHARP>
+</VisualStudioProject>
+
--- /dev/null
+Microsoft Visual Studio Solution File, Format Version 7.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhysFS.NET", "PhysFS.NET.csproj", "{C6205A43-3D4D-41E6-872C-96CD7BE55230}"
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ ConfigName.0 = Debug
+ ConfigName.1 = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {C6205A43-3D4D-41E6-872C-96CD7BE55230}.Debug.ActiveCfg = Debug|.NET
+ {C6205A43-3D4D-41E6-872C-96CD7BE55230}.Debug.Build.0 = Debug|.NET
+ {C6205A43-3D4D-41E6-872C-96CD7BE55230}.Release.ActiveCfg = Release|.NET
+ {C6205A43-3D4D-41E6-872C-96CD7BE55230}.Release.Build.0 = Release|.NET
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
--- /dev/null
+/* PhysFS.cs - (c)2003 Gregory S. Read
+ * Provides access to PhysFS API calls not specific to file handle access.
+ */
+using System;
+
+namespace PhysFS_NET
+{
+ public class PhysFS
+ {
+ /* Initialize
+ * Inits the PhysFS API. This normally does not need to be called unless
+ * the API has been manually deinitialized since the PhysFS_DLL class
+ * initializes just before the first call is made into the DLL.
+ * Parameters
+ * none
+ * Returns
+ * none
+ * Exceptions
+ * PhysFSException - An error occured in the PhysFS API
+ */
+ public static void Initialize()
+ {
+ // Initialize the physfs library, raise an exception if error
+ if(PhysFS_DLL.PHYSFS_init("") == 0)
+ throw new PhysFSException();
+ }
+
+ /* Deinitialize
+ * Deinits the PhysFS API. It is recommended that this method be called
+ * by the application before exiting in order to gracefully deallocate
+ * resources and close all filehandles, etc.
+ * Parameters
+ * none
+ * Returns
+ * none
+ * Exceptions
+ * PhysFSException - An error occured in the PhysFS API
+ */
+ public static void Deinitialize()
+ {
+ // Deinit, raise an exception if an error occured
+ if(PhysFS_DLL.PHYSFS_deinit() == 0)
+ throw new PhysFSException();
+ }
+
+ /* BaseDir
+ * Gets the base directory configured for PhysFS. See the PhysFS API
+ * documentation for more information.
+ * Parameters
+ * none
+ * Returns
+ * A string value representing the Base Directory
+ * Exceptions
+ * none
+ */
+ public static string BaseDir
+ {
+ get
+ {
+ // Return the current base directory
+ return PhysFS_DLL.PHYSFS_getBaseDir();
+ }
+ }
+
+ /* WriteDir
+ * Gets or sets the write directory configured for PhysFS. See the PhysFS API
+ * documentation for more information.
+ * Parameters
+ * set - Path to set the WriteDir property to
+ * Returns
+ * A string value representing the Write Directory
+ * Exceptions
+ * PhysFSException - An error occured in the PhysFS API when
+ * settings the write directory.
+ */
+ public static string WriteDir
+ {
+ get
+ {
+ // Return the current write directory
+ return PhysFS_DLL.PHYSFS_getWriteDir();
+ }
+ set
+ {
+ // Set the write directory and raise an exception if an error occured
+ if(PhysFS_DLL.PHYSFS_setWriteDir(value) == 0)
+ throw new PhysFSException();
+ }
+ }
+
+ /* UserDir
+ * Gets or sets the write directory configured for PhysFS. See the PhysFS API
+ * documentation for more information.
+ * Parameters
+ * set - Path to set the WriteDir property to
+ * Returns
+ * A string value representing the Write Directory
+ * Exceptions
+ * PhysFSException - An error occured in the PhysFS API when
+ * settings the write directory.
+ */
+ public static string UserDir
+ {
+ get
+ {
+ // Return the current user directory
+ return PhysFS_DLL.PHYSFS_getUserDir();
+ }
+ }
+ public static void AddToSearchPath(string NewDir, bool Append)
+ {
+ if(PhysFS_DLL.PHYSFS_addToSearchPath(NewDir, Append?1:0) == 0)
+ throw new PhysFSException();
+ }
+ public static void RemoveFromSearchPath(string OldDir)
+ {
+ if(PhysFS_DLL.PHYSFS_removeFromSearchPath(OldDir) == 0)
+ throw new PhysFSException();
+ }
+ public unsafe static string[] GetSearchPath()
+ {
+ byte** p; // Searchpath list from PhysFS dll
+ string[] pathlist; // List converted to an array
+
+ // Get the CDROM drive listing
+ p = PhysFS_DLL.PHYSFS_getSearchPath();
+ // Convert the C-style array to a .NET style array
+ pathlist = PhysFS_DLL.BytePPToArray(p);
+ // Free the original list since we're done with it
+ PhysFS_DLL.PHYSFS_freeList(p);
+
+ return pathlist;
+ }
+ public unsafe static string[] GetCDROMDrives()
+ {
+ byte** p; // CDROM list from PhysFS dll
+ string[] cdromlist; // List converted to an array
+
+ // Get the CDROM drive listing
+ p = PhysFS_DLL.PHYSFS_getCdRomDirs();
+ // Convert the C-style array to a .NET style array
+ cdromlist = PhysFS_DLL.BytePPToArray(p);
+ // Free the original list since we're done with it
+ PhysFS_DLL.PHYSFS_freeList(p);
+
+ return cdromlist;
+ }
+ public static void MkDir(string Dirname)
+ {
+ if(PhysFS_DLL.PHYSFS_mkdir(Dirname) == 0)
+ throw new PhysFSException();
+ }
+ public static void Delete(string Filename)
+ {
+ if(PhysFS_DLL.PHYSFS_delete(Filename) == 0)
+ throw new PhysFSException();
+ }
+ public static string GetRealDir(string Filename)
+ {
+ string RetValue;
+
+ RetValue = PhysFS_DLL.PHYSFS_getRealDir(Filename);
+ if(RetValue == null)
+ throw new PhysFSException("File not found in search path.");
+
+ // Return the real file path of the specified filename
+ return RetValue;
+ }
+ public unsafe static string[] EnumerateFiles(string Dirname)
+ {
+ byte** p; // File list from PhysFS dll
+ string[] filelist; // List converted to an array
+
+ // Get the CDROM drive listing
+ p = PhysFS_DLL.PHYSFS_enumerateFiles(Dirname);
+ // Convert the C-style array to a .NET style array
+ filelist = PhysFS_DLL.BytePPToArray(p);
+ // Free the original list since we're done with it
+ PhysFS_DLL.PHYSFS_freeList(p);
+
+ return filelist;
+ }
+ public static bool IsDirectory(string Filename)
+ {
+ // Return true if non-zero, otherwise return false
+ return (PhysFS_DLL.PHYSFS_isDirectory(Filename) == 0)?false:true;
+ }
+ }
+}
--- /dev/null
+/* PhysFSFileStream.cs - (c)2003 Gregory S. Read */
+using System;
+using System.Collections;
+using System.IO;
+
+namespace PhysFS_NET
+{
+ public enum PhysFSFileMode {Read, Write, Append};
+
+ // Our exception class we'll use for throwing all PhysFS API related exception
+ public class PhysFSException : IOException
+ {
+ public PhysFSException(string Message) : base(Message) {}
+ public PhysFSException() : base(PhysFS_DLL.PHYSFS_getLastError()) {}
+ }
+
+ public unsafe class PhysFSFileStream : Stream
+ {
+ // ***Public properties***
+ public override bool CanRead
+ {
+ get
+ {
+ // Reading is supported
+ return true;
+ }
+ }
+
+ public override bool CanSeek
+ {
+ get
+ {
+ // Seek is supported
+ return true;
+ }
+ }
+
+ public override bool CanWrite
+ {
+ get
+ {
+ // Writing is supported
+ return true;
+ }
+ }
+
+ public override long Length
+ {
+ get
+ {
+ long TempLength;
+ TempLength = PhysFS_DLL.PHYSFS_fileLength(pHandle);
+
+ // If call returned an error, throw an exception
+ if(TempLength == -1)
+ throw new PhysFSException();
+
+ return TempLength;
+ }
+ }
+
+ public override long Position
+ {
+ get
+ {
+ long TempPosition;
+ TempPosition = PhysFS_DLL.PHYSFS_tell(pHandle);
+
+ // If call returned an error, throw an exception
+ if(TempPosition == -1)
+ throw new PhysFSException();
+
+ return TempPosition;
+ }
+ set
+ {
+ // Seek from beginning of file using the position value
+ Seek(value, SeekOrigin.Begin);
+ }
+ }
+
+ // ***Public methods***
+ public PhysFSFileStream(string FileName, PhysFSFileMode FileMode, ulong BufferSize)
+ {
+ // Open the specified file with the appropriate file access
+ switch(FileMode)
+ {
+ case PhysFSFileMode.Read:
+ pHandle = PhysFS_DLL.PHYSFS_openRead(FileName);
+ break;
+ case PhysFSFileMode.Write:
+ pHandle = PhysFS_DLL.PHYSFS_openWrite(FileName);
+ break;
+ case PhysFSFileMode.Append:
+ pHandle = PhysFS_DLL.PHYSFS_openAppend(FileName);
+ break;
+ default:
+ throw new PhysFSException("Invalid FileMode specified");
+ }
+
+ // If handle is null, an error occured, so raise an exception
+ //!!! Does object get created if exception is thrown?
+ if(pHandle == null)
+ throw new PhysFSException();
+
+ // Set buffer size, raise an exception if an error occured
+ if(PhysFS_DLL.PHYSFS_setBuffer(pHandle, BufferSize) == 0)
+ throw new PhysFSException();
+ }
+
+ // This constructor sets the buffer size to 0 if not specified
+ public PhysFSFileStream(string FileName, PhysFSFileMode FileMode) : this(FileName, FileMode, 0) {}
+
+ ~PhysFSFileStream()
+ {
+ // Don't close the handle if they've specifically closed it already
+ if(!Closed)
+ Close();
+ }
+
+ public override void Flush()
+ {
+ if(PhysFS_DLL.PHYSFS_flush(pHandle) == 0)
+ throw new PhysFSException();
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ long RetValue;
+
+ fixed(byte *pbytes = &buffer[offset])
+ {
+ // Read into our allocated pointer
+ RetValue = PhysFS_DLL.PHYSFS_read(pHandle, pbytes, sizeof(byte), (uint)count);
+ }
+
+ if(RetValue == -1)
+ throw new PhysFSException();
+
+ // Return number of bytes read
+ // Note: This cast should be safe since we are only reading 'count' items, which
+ // is of type 'int'.
+ return (int)RetValue;
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ long RetValue;
+
+ fixed(byte* pbytes = &buffer[offset])
+ {
+ // Write buffer
+ RetValue = PhysFS_DLL.PHYSFS_write(pHandle, pbytes, sizeof(byte), (uint)count);
+ }
+
+ if(RetValue == -1)
+ throw new PhysFSException();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ // Only seeking from beginning is supported by PhysFS API
+ if(origin != SeekOrigin.Begin)
+ throw new PhysFSException("Only seek origin of \"Begin\" is supported");
+
+ // Seek to specified offset, raise an exception if error occured
+ if(PhysFS_DLL.PHYSFS_seek(pHandle, (ulong)offset) == 0)
+ throw new PhysFSException();
+
+ // Since we always seek from beginning, the offset is always
+ // the absolute position.
+ return offset;
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException("SetLength method not supported in PhysFSFileStream objects.");
+ }
+
+ public override void Close()
+ {
+ // Close the handle
+ if(PhysFS_DLL.PHYSFS_close(pHandle) == 0)
+ throw new PhysFSException();
+
+ // File has been closed. Rock.
+ Closed = true;
+ }
+
+ // ***Private variables***
+ private void *pHandle;
+ private bool Closed = false;
+ }
+}
\ No newline at end of file
--- /dev/null
+/* PhysFS_DLL - (c)2003 Gregory S. Read
+ * Internal class that provides direct access to the PhysFS DLL. It is
+ * not accessible outside of the PhysFS.NET assembly.
+ */
+using System.Collections;
+using System.Runtime.InteropServices;
+
+namespace PhysFS_NET
+{
+ internal class PhysFS_DLL
+ {
+ /* Static constructor
+ * Initializes the PhysFS API before any method is called in this class. This
+ * relieves the user from having to explicitly initialize the API.
+ * Parameters
+ * none
+ * Returns
+ * none
+ * Exceptions
+ * PhysFSException - An error occured in the PhysFS API
+ */
+ static PhysFS_DLL()
+ {
+ if(PHYSFS_init("") == 0)
+ throw new PhysFSException();
+ }
+
+ /* BytePPToArray
+ * Converts a C-style string array into a .NET managed string array
+ * Parameters
+ * C-style string array pointer returned from PhysFS
+ * Returns
+ * .NET managed string array
+ * Exceptions
+ * none
+ */
+ public unsafe static string[] BytePPToArray(byte **bytearray)
+ {
+ byte** ptr;
+ byte* c;
+ string tempstr;
+ ArrayList MyArrayList = new ArrayList();
+ string[] RetArray;
+
+ for(ptr = bytearray; *ptr != null; ptr++)
+ {
+ tempstr = "";
+ for(c = *ptr; *c != 0; c++)
+ {
+ tempstr += (char)*c;
+ }
+
+ // Add string to our list
+ MyArrayList.Add(tempstr);
+ }
+
+ // Return a normal array of the list
+ RetArray = new string[MyArrayList.Count];
+ MyArrayList.CopyTo(RetArray, 0);
+ return RetArray;
+ }
+
+ // Name of DLL to import
+ private const string PHYSFS_DLLNAME = "physfs.dll";
+
+ // DLL import declarations
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_init(string argv0);
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_deinit();
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe void PHYSFS_freeList(void *listVar);
+ [DllImport(PHYSFS_DLLNAME)] public static extern string PHYSFS_getLastError();
+ [DllImport(PHYSFS_DLLNAME)] public static extern string PHYSFS_getDirSeparator();
+ [DllImport(PHYSFS_DLLNAME)] public static extern void PHYSFS_permitSymbolicLinks(int allow);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe byte** PHYSFS_getCdRomDirs();
+ [DllImport(PHYSFS_DLLNAME)] public static extern string PHYSFS_getBaseDir();
+ [DllImport(PHYSFS_DLLNAME)] public static extern string PHYSFS_getUserDir();
+ [DllImport(PHYSFS_DLLNAME)] public static extern string PHYSFS_getWriteDir();
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_setWriteDir(string newDir);
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_addToSearchPath(string newDir, int appendToPath);
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_removeFromSearchPath(string oldDir);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe byte** PHYSFS_getSearchPath();
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_setSaneConfig(string organization,
+ string appName,
+ string archiveExt,
+ int includeCdRoms,
+ int archivesFirst);
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_mkdir(string dirName);
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_delete(string filename);
+ [DllImport(PHYSFS_DLLNAME)] public static extern string PHYSFS_getRealDir(string filename);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe byte** PHYSFS_enumerateFiles(string dir);
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_exists(string fname);
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_isDirectory(string fname);
+ [DllImport(PHYSFS_DLLNAME)] public static extern int PHYSFS_isSymbolicLink(string fname);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe void* PHYSFS_openWrite(string filename);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe void* PHYSFS_openAppend(string filename);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe void* PHYSFS_openRead(string filename);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe int PHYSFS_close(void* handle);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe long PHYSFS_getLastModTime(string filename);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe long PHYSFS_read(void* handle,
+ void *buffer,
+ uint objSize,
+ uint objCount);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe long PHYSFS_write(void* handle,
+ void *buffer,
+ uint objSize,
+ uint objCount);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe int PHYSFS_eof(void* handle);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe long PHYSFS_tell(void* handle);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe int PHYSFS_seek(void* handle, ulong pos);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe long PHYSFS_fileLength(void* handle);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe int PHYSFS_setBuffer(void* handle, ulong bufsize);
+ [DllImport(PHYSFS_DLLNAME)] public static extern unsafe int PHYSFS_flush(void* handle);
+ }
+}
--- /dev/null
+PhysFS.NET is a library that encapsulates the PhysFS API into a .NET assembly.
+
+There are two class objects that are exposed in the assembly:
+ PhysFS.cs
+ This class exposes any non-filehandle specific functionality contained in
+ the PhysFS library.
+ PhysFSFileStream.cs
+ A System.IO.Stream derived class which provides file access via the
+ PhysFS API. Usage of this object is identical to a standard stream
+ object.
\ No newline at end of file
--- /dev/null
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+//
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+//
+[assembly: AssemblyTitle("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+//
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("1.0.*")]
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the
+// Microsoft .NET Framework documentation for more information on assembly signing.
+//
+// Use the attributes below to control which key is used for signing.
+//
+// Notes:
+// (*) If no key is specified, the assembly is not signed.
+// (*) KeyName refers to a key that has been installed in the Crypto Service
+// Provider (CSP) on your machine. KeyFile refers to a file which contains
+// a key.
+// (*) If the KeyFile and the KeyName values are both specified, the
+// following processing occurs:
+// (1) If the KeyName can be found in the CSP, that key is used.
+// (2) If the KeyName does not exist and the KeyFile does exist, the key
+// in the KeyFile is installed into the CSP and used.
+// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
+// When specifying the KeyFile, the location of the KeyFile should be
+// relative to the project output directory which is
+// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
+// located in the project directory, you would specify the AssemblyKeyFile
+// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
+// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+// documentation for more information on this.
+//
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
+[assembly: AssemblyKeyName("")]
--- /dev/null
+<VisualStudioProject>
+ <CSHARP
+ ProjectType = "Local"
+ ProductVersion = "7.0.9466"
+ SchemaVersion = "1.0"
+ ProjectGuid = "{9C1ECC6B-16C7-42B3-BF7C-8BA8D81BA980}"
+ >
+ <Build>
+ <Settings
+ ApplicationIcon = "App.ico"
+ AssemblyKeyContainerName = ""
+ AssemblyName = "TestApp"
+ AssemblyOriginatorKeyFile = ""
+ DefaultClientScript = "JScript"
+ DefaultHTMLPageLayout = "Grid"
+ DefaultTargetSchema = "IE50"
+ DelaySign = "false"
+ OutputType = "WinExe"
+ RootNamespace = "TestApp"
+ StartupObject = ""
+ >
+ <Config
+ Name = "Debug"
+ AllowUnsafeBlocks = "false"
+ BaseAddress = "285212672"
+ CheckForOverflowUnderflow = "false"
+ ConfigurationOverrideFile = ""
+ DefineConstants = "DEBUG;TRACE"
+ DocumentationFile = ""
+ DebugSymbols = "true"
+ FileAlignment = "4096"
+ IncrementalBuild = "true"
+ Optimize = "false"
+ OutputPath = "bin\Debug\"
+ RegisterForComInterop = "false"
+ RemoveIntegerChecks = "false"
+ TreatWarningsAsErrors = "false"
+ WarningLevel = "4"
+ />
+ <Config
+ Name = "Release"
+ AllowUnsafeBlocks = "false"
+ BaseAddress = "285212672"
+ CheckForOverflowUnderflow = "false"
+ ConfigurationOverrideFile = ""
+ DefineConstants = "TRACE"
+ DocumentationFile = ""
+ DebugSymbols = "false"
+ FileAlignment = "4096"
+ IncrementalBuild = "false"
+ Optimize = "true"
+ OutputPath = "bin\Release\"
+ RegisterForComInterop = "false"
+ RemoveIntegerChecks = "false"
+ TreatWarningsAsErrors = "false"
+ WarningLevel = "4"
+ />
+ </Settings>
+ <References>
+ <Reference
+ Name = "System"
+ AssemblyName = "System"
+ HintPath = "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.dll"
+ />
+ <Reference
+ Name = "System.Data"
+ AssemblyName = "System.Data"
+ HintPath = "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Data.dll"
+ />
+ <Reference
+ Name = "System.Drawing"
+ AssemblyName = "System.Drawing"
+ HintPath = "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Drawing.dll"
+ />
+ <Reference
+ Name = "System.Windows.Forms"
+ AssemblyName = "System.Windows.Forms"
+ HintPath = "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Windows.Forms.dll"
+ />
+ <Reference
+ Name = "System.XML"
+ AssemblyName = "System.Xml"
+ HintPath = "C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.XML.dll"
+ />
+ <Reference
+ Name = "PhysFS.NET"
+ Project = "{C6205A43-3D4D-41E6-872C-96CD7BE55230}"
+ Package = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"
+ />
+ </References>
+ </Build>
+ <Files>
+ <Include>
+ <File
+ RelPath = "App.ico"
+ BuildAction = "Content"
+ />
+ <File
+ RelPath = "AssemblyInfo.cs"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "TestAppForm.cs"
+ SubType = "Form"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "TestAppForm.resx"
+ DependentUpon = "TestAppForm.cs"
+ BuildAction = "EmbeddedResource"
+ />
+ </Include>
+ </Files>
+ </CSHARP>
+</VisualStudioProject>
+
--- /dev/null
+Microsoft Visual Studio Solution File, Format Version 7.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "TestApp.csproj", "{9C1ECC6B-16C7-42B3-BF7C-8BA8D81BA980}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhysFS.NET", "..\PhysFS.NET.csproj", "{C6205A43-3D4D-41E6-872C-96CD7BE55230}"
+EndProject
+Global
+ GlobalSection(SolutionConfiguration) = preSolution
+ ConfigName.0 = Debug
+ ConfigName.1 = Release
+ EndGlobalSection
+ GlobalSection(ProjectDependencies) = postSolution
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {9C1ECC6B-16C7-42B3-BF7C-8BA8D81BA980}.Debug.ActiveCfg = Debug|.NET
+ {9C1ECC6B-16C7-42B3-BF7C-8BA8D81BA980}.Debug.Build.0 = Debug|.NET
+ {9C1ECC6B-16C7-42B3-BF7C-8BA8D81BA980}.Release.ActiveCfg = Release|.NET
+ {9C1ECC6B-16C7-42B3-BF7C-8BA8D81BA980}.Release.Build.0 = Release|.NET
+ {C6205A43-3D4D-41E6-872C-96CD7BE55230}.Debug.ActiveCfg = Debug|.NET
+ {C6205A43-3D4D-41E6-872C-96CD7BE55230}.Debug.Build.0 = Debug|.NET
+ {C6205A43-3D4D-41E6-872C-96CD7BE55230}.Release.ActiveCfg = Release|.NET
+ {C6205A43-3D4D-41E6-872C-96CD7BE55230}.Release.Build.0 = Release|.NET
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
--- /dev/null
+using System;
+using System.Drawing;
+using System.Collections;
+using System.ComponentModel;
+using System.Windows.Forms;
+using System.Data;
+using System.IO;
+using PhysFS_NET;
+
+namespace TestApp
+{
+ /// <summary>
+ /// Summary description for Form1.
+ /// </summary>
+ public class TestAppForm : System.Windows.Forms.Form
+ {
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Button RefreshCDsButton;
+ private System.Windows.Forms.ListBox CDDrivesList;
+ private System.Windows.Forms.ListBox SearchPathList;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TextBox EnumFilesPath;
+ private System.Windows.Forms.ListBox EnumList;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.TextBox NewSearchPathText;
+ private System.Windows.Forms.Button AddSearchPathButton;
+ private System.Windows.Forms.Button RemovePathButton;
+ private System.Windows.Forms.Button RefreshEnumList;
+ private System.Windows.Forms.Button RefreshSearchPathButton;
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.Container components = null;
+
+ public TestAppForm()
+ {
+ //
+ // Required for Windows Form Designer support
+ //
+ InitializeComponent();
+
+ //
+ // TODO: Add any constructor code after InitializeComponent call
+ //
+ }
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if (components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ #region Windows Form Designer generated code
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent()
+ {
+ this.label2 = new System.Windows.Forms.Label();
+ this.RefreshCDsButton = new System.Windows.Forms.Button();
+ this.CDDrivesList = new System.Windows.Forms.ListBox();
+ this.SearchPathList = new System.Windows.Forms.ListBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.EnumFilesPath = new System.Windows.Forms.TextBox();
+ this.EnumList = new System.Windows.Forms.ListBox();
+ this.label3 = new System.Windows.Forms.Label();
+ this.RefreshEnumList = new System.Windows.Forms.Button();
+ this.NewSearchPathText = new System.Windows.Forms.TextBox();
+ this.AddSearchPathButton = new System.Windows.Forms.Button();
+ this.RemovePathButton = new System.Windows.Forms.Button();
+ this.RefreshSearchPathButton = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // label2
+ //
+ this.label2.Location = new System.Drawing.Point(8, 8);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(136, 16);
+ this.label2.TabIndex = 2;
+ this.label2.Text = "Available CD-ROM Drives";
+ //
+ // RefreshCDsButton
+ //
+ this.RefreshCDsButton.Location = new System.Drawing.Point(8, 152);
+ this.RefreshCDsButton.Name = "RefreshCDsButton";
+ this.RefreshCDsButton.Size = new System.Drawing.Size(72, 24);
+ this.RefreshCDsButton.TabIndex = 4;
+ this.RefreshCDsButton.Text = "Refresh";
+ this.RefreshCDsButton.Click += new System.EventHandler(this.RefreshCDsButton_Click);
+ //
+ // CDDrivesList
+ //
+ this.CDDrivesList.Location = new System.Drawing.Point(8, 24);
+ this.CDDrivesList.Name = "CDDrivesList";
+ this.CDDrivesList.Size = new System.Drawing.Size(136, 121);
+ this.CDDrivesList.TabIndex = 7;
+ //
+ // SearchPathList
+ //
+ this.SearchPathList.Location = new System.Drawing.Point(152, 24);
+ this.SearchPathList.Name = "SearchPathList";
+ this.SearchPathList.Size = new System.Drawing.Size(248, 95);
+ this.SearchPathList.TabIndex = 8;
+ //
+ // label1
+ //
+ this.label1.Location = new System.Drawing.Point(152, 8);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(136, 16);
+ this.label1.TabIndex = 10;
+ this.label1.Text = "Search Path";
+ //
+ // EnumFilesPath
+ //
+ this.EnumFilesPath.Location = new System.Drawing.Point(408, 128);
+ this.EnumFilesPath.Name = "EnumFilesPath";
+ this.EnumFilesPath.Size = new System.Drawing.Size(208, 20);
+ this.EnumFilesPath.TabIndex = 11;
+ this.EnumFilesPath.Text = "";
+ //
+ // EnumList
+ //
+ this.EnumList.Location = new System.Drawing.Point(408, 24);
+ this.EnumList.Name = "EnumList";
+ this.EnumList.Size = new System.Drawing.Size(208, 95);
+ this.EnumList.TabIndex = 12;
+ //
+ // label3
+ //
+ this.label3.Location = new System.Drawing.Point(408, 8);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(136, 16);
+ this.label3.TabIndex = 13;
+ this.label3.Text = "Enumerate Files";
+ //
+ // RefreshEnumList
+ //
+ this.RefreshEnumList.Location = new System.Drawing.Point(544, 152);
+ this.RefreshEnumList.Name = "RefreshEnumList";
+ this.RefreshEnumList.Size = new System.Drawing.Size(72, 24);
+ this.RefreshEnumList.TabIndex = 14;
+ this.RefreshEnumList.Text = "Refresh";
+ this.RefreshEnumList.Click += new System.EventHandler(this.RefreshEnumList_Click);
+ //
+ // NewSearchPathText
+ //
+ this.NewSearchPathText.Location = new System.Drawing.Point(152, 128);
+ this.NewSearchPathText.Name = "NewSearchPathText";
+ this.NewSearchPathText.Size = new System.Drawing.Size(248, 20);
+ this.NewSearchPathText.TabIndex = 15;
+ this.NewSearchPathText.Text = "";
+ //
+ // AddSearchPathButton
+ //
+ this.AddSearchPathButton.Location = new System.Drawing.Point(152, 152);
+ this.AddSearchPathButton.Name = "AddSearchPathButton";
+ this.AddSearchPathButton.Size = new System.Drawing.Size(72, 24);
+ this.AddSearchPathButton.TabIndex = 9;
+ this.AddSearchPathButton.Text = "Add Path";
+ this.AddSearchPathButton.Click += new System.EventHandler(this.AddSearchPathButton_Click);
+ //
+ // RemovePathButton
+ //
+ this.RemovePathButton.Location = new System.Drawing.Point(232, 152);
+ this.RemovePathButton.Name = "RemovePathButton";
+ this.RemovePathButton.Size = new System.Drawing.Size(88, 24);
+ this.RemovePathButton.TabIndex = 16;
+ this.RemovePathButton.Text = "Remove Path";
+ this.RemovePathButton.Click += new System.EventHandler(this.RemovePathButton_Click);
+ //
+ // RefreshSearchPathButton
+ //
+ this.RefreshSearchPathButton.Location = new System.Drawing.Point(328, 152);
+ this.RefreshSearchPathButton.Name = "RefreshSearchPathButton";
+ this.RefreshSearchPathButton.Size = new System.Drawing.Size(72, 24);
+ this.RefreshSearchPathButton.TabIndex = 17;
+ this.RefreshSearchPathButton.Text = "Refresh";
+ this.RefreshSearchPathButton.Click += new System.EventHandler(this.RefreshSearchPathButton_Click);
+ //
+ // TestAppForm
+ //
+ this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
+ this.ClientSize = new System.Drawing.Size(624, 309);
+ this.Controls.AddRange(new System.Windows.Forms.Control[] {
+ this.RefreshSearchPathButton,
+ this.RemovePathButton,
+ this.NewSearchPathText,
+ this.RefreshEnumList,
+ this.label3,
+ this.EnumList,
+ this.EnumFilesPath,
+ this.label1,
+ this.SearchPathList,
+ this.CDDrivesList,
+ this.RefreshCDsButton,
+ this.label2,
+ this.AddSearchPathButton});
+ this.Name = "TestAppForm";
+ this.Text = "PhysFS Test Application";
+ this.Load += new System.EventHandler(this.TestAppForm_Load);
+ this.ResumeLayout(false);
+
+ }
+ #endregion
+
+ /// <summary>
+ /// The main entry point for the application.
+ /// </summary>
+ [STAThread]
+ static void Main()
+ {
+ Application.Run(new TestAppForm());
+ }
+
+ private void TestAppForm_Load(object sender, System.EventArgs e)
+ {
+
+ }
+
+ private void RefreshCDsButton_Click(object sender, System.EventArgs e)
+ {
+ // Clear ths listbox if it contains any items
+ CDDrivesList.Items.Clear();
+ // Add the items to the list
+ CDDrivesList.Items.AddRange(PhysFS.GetCDROMDrives());
+ }
+
+ private void RefreshSearchPathButton_Click(object sender, System.EventArgs e)
+ {
+ // Clear ths listbox if it contains any items
+ SearchPathList.Items.Clear();
+ // Add the items to the list
+ SearchPathList.Items.AddRange(PhysFS.GetSearchPath());
+ }
+
+ private void AddSearchPathButton_Click(object sender, System.EventArgs e)
+ {
+ // Add search path
+ PhysFS.AddToSearchPath(NewSearchPathText.Text, false);
+ // Clear ths listbox if it contains any items
+ SearchPathList.Items.Clear();
+ // Add the items to the list
+ SearchPathList.Items.AddRange(PhysFS.GetSearchPath());
+ }
+
+ private void RemovePathButton_Click(object sender, System.EventArgs e)
+ {
+ if(SearchPathList.SelectedItem != null)
+ {
+ PhysFS.RemoveFromSearchPath(SearchPathList.SelectedItem.ToString());
+ // Clear ths listbox if it contains any items
+ SearchPathList.Items.Clear();
+ // Add the items to the list
+ SearchPathList.Items.AddRange(PhysFS.GetSearchPath());
+ }
+ }
+
+ private void RefreshEnumList_Click(object sender, System.EventArgs e)
+ {
+ EnumList.Items.Clear();
+ EnumList.Items.AddRange(PhysFS.EnumerateFiles(EnumFilesPath.Text));
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 1.3
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">1.3</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1">this is my long string</data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ [base64 mime encoded serialized .NET Framework object]
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ [base64 mime encoded string representing a byte array form of the .NET Framework object]
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>1.3</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="$this.Name">
+ <value>TestAppForm</value>
+ </data>
+</root>
\ No newline at end of file
--- /dev/null
+/*
+ * stdio/physfs abstraction layer 2003-04-02
+ *
+ * Adam D. Moss <adam@gimp.org> <aspirin@icculus.org>
+ *
+ * These wrapper macros and functions are designed to allow a program
+ * to perform file I/O with identical semantics and syntax regardless
+ * of whether PhysicsFS is being used or not.
+ */
+#ifndef _ABS_FILE_H
+#define _ABS_FILE_H
+/*
+PLEASE NOTE: This license applies to abs-file.h ONLY (to make it clear that
+you may embed this wrapper code within commercial software); PhysicsFS itself
+is (at the time of writing) released under a different license with
+additional restrictions.
+
+Copyright (C) 2002-2003 Adam D. Moss (the "Author"). All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is fur-
+nished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
+NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
+NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the Author of the
+Software shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in this Software without prior written authorization
+from the Author.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * API:
+ *
+ * Macro/function use like stdio equivalent...
+ * -------------- ----------------------------
+ * MY_FILETYPE FILE
+ * MY_OPEN_FOR_READ fopen(..., "rb")
+ * MY_READ fread(...)
+ * MY_CLOSE fclose(...)
+ * MY_GETC fgetc(...)
+ * MY_GETS fgets(...)
+ * MY_ATEOF feof(...)
+ * MY_TELL ftell(...)
+ * MY_SEEK fseek(..., SEEK_SET)
+ * MY_REWIND rewind(...)
+ * MY_SETBUFFER (not a standard for stdio, does nothing there)
+ */
+
+/*
+ * Important DEFINEs:
+ * It is important to define these consistantly across the various
+ * compilation modules of your program if you wish to exchange file
+ * handles between them.
+ *
+ * USE_PHYSFS: Define USE_PHYSFS if PhysicsFS is being used; note that if
+ * you do intend to use PhysicsFS then you will still need to initialize
+ * PhysicsFS yourself and set up its search-paths.
+ *
+ * Optional DEFINEs:
+ *
+ * PHYSFS_DEFAULT_READ_BUFFER <bytes>: If set then abs-file.h sets the
+ * PhysicsFS buffer size to this value whenever you open a file. You
+ * may over-ride this on a per-filehandle basis by using the
+ * MY_SETBUFFER() macro (which simply does nothing when not using
+ * PhysicsFS). If you have not defined this value explicitly then
+ * abs-file.h will default to the same default buffer size as used by
+ * stdio if it can be determined, or 8192 bytes otherwise.
+ */
+#ifndef PHYSFS_DEFAULT_READ_BUFFER
+#ifdef BUFSIZ
+#define PHYSFS_DEFAULT_READ_BUFFER BUFSIZ
+#else
+#define PHYSFS_DEFAULT_READ_BUFFER 8192
+#endif
+#endif
+
+#ifdef USE_PHYSFS
+
+#include <physfs.h>
+#define MY_FILETYPE PHYSFS_File
+#define MY_SETBUFFER(fp,size) PHYSFS_setBuffer(fp,size)
+#define MY_READ(p,s,n,fp) PHYSFS_read(fp,p,s,n)
+#if PHYSFS_DEFAULT_READ_BUFFER
+static MY_FILETYPE* MY_OPEN_FOR_READ(const char *const filename)
+{
+ MY_FILETYPE *const file = PHYSFS_openRead(filename);
+ if (file) {
+ MY_SETBUFFER(file, PHYSFS_DEFAULT_READ_BUFFER);
+ }
+ return file;
+}
+#else
+#define MY_OPEN_FOR_READ(fn) PHYSFS_openRead(fn)
+#endif
+static int MY_GETC(MY_FILETYPE *const fp) {
+ unsigned char c;
+ /*if (PHYSFS_eof(fp)) {
+ return EOF;
+ }
+ MY_READ(&c, 1, 1, fp);*/
+ if (MY_READ(&c, 1, 1, fp) != 1) {
+ return EOF;
+ }
+ return c;
+}
+static char * MY_GETS(char * const str, const int size,
+ MY_FILETYPE *const fp) {
+ int i = 0;
+ int c;
+ do {
+ if (i == size-1) {
+ break;
+ }
+ c = MY_GETC(fp);
+ if (c == EOF) {
+ break;
+ }
+ str[i++] = c;
+ } while (c != '\0' &&
+ c != -1 &&
+ c != '\n');
+ str[i] = '\0';
+ if (i == 0) {
+ return NULL;
+ }
+ return str;
+}
+#define MY_CLOSE(fp) PHYSFS_close(fp)
+#define MY_ATEOF(fp) PHYSFS_eof(fp)
+#define MY_TELL(fp) PHYSFS_tell(fp)
+#define MY_SEEK(fp,o) PHYSFS_seek(fp,o)
+#define MY_REWIND(fp) MY_SEEK(fp,0)
+
+#else
+
+#define MY_FILETYPE FILE
+#define MY_READ(p,s,n,fp) fread(p,s,n,fp)
+#define MY_OPEN_FOR_READ(n) fopen(n, "rb")
+#define MY_GETC(fp) fgetc(fp)
+#define MY_GETS(str,size,fp) fgets(str,size,fp)
+#define MY_CLOSE(fp) fclose(fp)
+#define MY_ATEOF(fp) feof(fp)
+#define MY_TELL(fp) ftell(fp)
+#define MY_SEEK(fp,o) fseek(fp,o, SEEK_SET)
+#define MY_REWIND(fp) rewind(fp)
+/*static void MY_SETBUFFER(const MY_FILETYPE *const file, const int num) { }*/
+#define MY_SETBUFFER(fp,size)
+#endif
+
+#endif
--- /dev/null
+# CaseFolding-4.1.0.txt
+# Date: 2005-03-26, 00:24:43 GMT [MD]
+#
+# Unicode Character Database
+# Copyright (c) 1991-2005 Unicode, Inc.
+# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For documentation, see UCD.html
+#
+# Case Folding Properties
+#
+# This file is a supplement to the UnicodeData file.
+# It provides a case folding mapping generated from the Unicode Character Database.
+# If all characters are mapped according to the full mapping below, then
+# case differences (according to UnicodeData.txt and SpecialCasing.txt)
+# are eliminated.
+#
+# The data supports both implementations that require simple case foldings
+# (where string lengths don't change), and implementations that allow full case folding
+# (where string lengths may grow). Note that where they can be supported, the
+# full case foldings are superior: for example, they allow "MASSE" and "Maße" to match.
+#
+# All code points not listed in this file map to themselves.
+#
+# NOTE: case folding does not preserve normalization formats!
+#
+# For information on case folding, see
+# UTR #21 Case Mappings, at http://www.unicode.org/unicode/reports/tr21/
+#
+# ================================================================================
+# Format
+# ================================================================================
+# The entries in this file are in the following machine-readable format:
+#
+# <code>; <status>; <mapping>; # <name>
+#
+# The status field is:
+# C: common case folding, common mappings shared by both simple and full mappings.
+# F: full case folding, mappings that cause strings to grow in length. Multiple characters are separated by spaces.
+# S: simple case folding, mappings to single characters where different from F.
+# T: special case for uppercase I and dotted uppercase I
+# - For non-Turkic languages, this mapping is normally not used.
+# - For Turkic languages (tr, az), this mapping can be used instead of the normal mapping for these characters.
+# Note that the Turkic mappings do not maintain canonical equivalence without additional processing.
+# See the discussions of case mapping in the Unicode Standard for more information.
+#
+# Usage:
+# A. To do a simple case folding, use the mappings with status C + S.
+# B. To do a full case folding, use the mappings with status C + F.
+#
+# The mappings with status T can be used or omitted depending on the desired case-folding
+# behavior. (The default option is to exclude them.)
+#
+# =================================================================
+
+0041; C; 0061; # LATIN CAPITAL LETTER A
+0042; C; 0062; # LATIN CAPITAL LETTER B
+0043; C; 0063; # LATIN CAPITAL LETTER C
+0044; C; 0064; # LATIN CAPITAL LETTER D
+0045; C; 0065; # LATIN CAPITAL LETTER E
+0046; C; 0066; # LATIN CAPITAL LETTER F
+0047; C; 0067; # LATIN CAPITAL LETTER G
+0048; C; 0068; # LATIN CAPITAL LETTER H
+0049; C; 0069; # LATIN CAPITAL LETTER I
+0049; T; 0131; # LATIN CAPITAL LETTER I
+004A; C; 006A; # LATIN CAPITAL LETTER J
+004B; C; 006B; # LATIN CAPITAL LETTER K
+004C; C; 006C; # LATIN CAPITAL LETTER L
+004D; C; 006D; # LATIN CAPITAL LETTER M
+004E; C; 006E; # LATIN CAPITAL LETTER N
+004F; C; 006F; # LATIN CAPITAL LETTER O
+0050; C; 0070; # LATIN CAPITAL LETTER P
+0051; C; 0071; # LATIN CAPITAL LETTER Q
+0052; C; 0072; # LATIN CAPITAL LETTER R
+0053; C; 0073; # LATIN CAPITAL LETTER S
+0054; C; 0074; # LATIN CAPITAL LETTER T
+0055; C; 0075; # LATIN CAPITAL LETTER U
+0056; C; 0076; # LATIN CAPITAL LETTER V
+0057; C; 0077; # LATIN CAPITAL LETTER W
+0058; C; 0078; # LATIN CAPITAL LETTER X
+0059; C; 0079; # LATIN CAPITAL LETTER Y
+005A; C; 007A; # LATIN CAPITAL LETTER Z
+00B5; C; 03BC; # MICRO SIGN
+00C0; C; 00E0; # LATIN CAPITAL LETTER A WITH GRAVE
+00C1; C; 00E1; # LATIN CAPITAL LETTER A WITH ACUTE
+00C2; C; 00E2; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+00C3; C; 00E3; # LATIN CAPITAL LETTER A WITH TILDE
+00C4; C; 00E4; # LATIN CAPITAL LETTER A WITH DIAERESIS
+00C5; C; 00E5; # LATIN CAPITAL LETTER A WITH RING ABOVE
+00C6; C; 00E6; # LATIN CAPITAL LETTER AE
+00C7; C; 00E7; # LATIN CAPITAL LETTER C WITH CEDILLA
+00C8; C; 00E8; # LATIN CAPITAL LETTER E WITH GRAVE
+00C9; C; 00E9; # LATIN CAPITAL LETTER E WITH ACUTE
+00CA; C; 00EA; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+00CB; C; 00EB; # LATIN CAPITAL LETTER E WITH DIAERESIS
+00CC; C; 00EC; # LATIN CAPITAL LETTER I WITH GRAVE
+00CD; C; 00ED; # LATIN CAPITAL LETTER I WITH ACUTE
+00CE; C; 00EE; # LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+00CF; C; 00EF; # LATIN CAPITAL LETTER I WITH DIAERESIS
+00D0; C; 00F0; # LATIN CAPITAL LETTER ETH
+00D1; C; 00F1; # LATIN CAPITAL LETTER N WITH TILDE
+00D2; C; 00F2; # LATIN CAPITAL LETTER O WITH GRAVE
+00D3; C; 00F3; # LATIN CAPITAL LETTER O WITH ACUTE
+00D4; C; 00F4; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+00D5; C; 00F5; # LATIN CAPITAL LETTER O WITH TILDE
+00D6; C; 00F6; # LATIN CAPITAL LETTER O WITH DIAERESIS
+00D8; C; 00F8; # LATIN CAPITAL LETTER O WITH STROKE
+00D9; C; 00F9; # LATIN CAPITAL LETTER U WITH GRAVE
+00DA; C; 00FA; # LATIN CAPITAL LETTER U WITH ACUTE
+00DB; C; 00FB; # LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+00DC; C; 00FC; # LATIN CAPITAL LETTER U WITH DIAERESIS
+00DD; C; 00FD; # LATIN CAPITAL LETTER Y WITH ACUTE
+00DE; C; 00FE; # LATIN CAPITAL LETTER THORN
+00DF; F; 0073 0073; # LATIN SMALL LETTER SHARP S
+0100; C; 0101; # LATIN CAPITAL LETTER A WITH MACRON
+0102; C; 0103; # LATIN CAPITAL LETTER A WITH BREVE
+0104; C; 0105; # LATIN CAPITAL LETTER A WITH OGONEK
+0106; C; 0107; # LATIN CAPITAL LETTER C WITH ACUTE
+0108; C; 0109; # LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+010A; C; 010B; # LATIN CAPITAL LETTER C WITH DOT ABOVE
+010C; C; 010D; # LATIN CAPITAL LETTER C WITH CARON
+010E; C; 010F; # LATIN CAPITAL LETTER D WITH CARON
+0110; C; 0111; # LATIN CAPITAL LETTER D WITH STROKE
+0112; C; 0113; # LATIN CAPITAL LETTER E WITH MACRON
+0114; C; 0115; # LATIN CAPITAL LETTER E WITH BREVE
+0116; C; 0117; # LATIN CAPITAL LETTER E WITH DOT ABOVE
+0118; C; 0119; # LATIN CAPITAL LETTER E WITH OGONEK
+011A; C; 011B; # LATIN CAPITAL LETTER E WITH CARON
+011C; C; 011D; # LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+011E; C; 011F; # LATIN CAPITAL LETTER G WITH BREVE
+0120; C; 0121; # LATIN CAPITAL LETTER G WITH DOT ABOVE
+0122; C; 0123; # LATIN CAPITAL LETTER G WITH CEDILLA
+0124; C; 0125; # LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+0126; C; 0127; # LATIN CAPITAL LETTER H WITH STROKE
+0128; C; 0129; # LATIN CAPITAL LETTER I WITH TILDE
+012A; C; 012B; # LATIN CAPITAL LETTER I WITH MACRON
+012C; C; 012D; # LATIN CAPITAL LETTER I WITH BREVE
+012E; C; 012F; # LATIN CAPITAL LETTER I WITH OGONEK
+0130; F; 0069 0307; # LATIN CAPITAL LETTER I WITH DOT ABOVE
+0130; T; 0069; # LATIN CAPITAL LETTER I WITH DOT ABOVE
+0132; C; 0133; # LATIN CAPITAL LIGATURE IJ
+0134; C; 0135; # LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+0136; C; 0137; # LATIN CAPITAL LETTER K WITH CEDILLA
+0139; C; 013A; # LATIN CAPITAL LETTER L WITH ACUTE
+013B; C; 013C; # LATIN CAPITAL LETTER L WITH CEDILLA
+013D; C; 013E; # LATIN CAPITAL LETTER L WITH CARON
+013F; C; 0140; # LATIN CAPITAL LETTER L WITH MIDDLE DOT
+0141; C; 0142; # LATIN CAPITAL LETTER L WITH STROKE
+0143; C; 0144; # LATIN CAPITAL LETTER N WITH ACUTE
+0145; C; 0146; # LATIN CAPITAL LETTER N WITH CEDILLA
+0147; C; 0148; # LATIN CAPITAL LETTER N WITH CARON
+0149; F; 02BC 006E; # LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+014A; C; 014B; # LATIN CAPITAL LETTER ENG
+014C; C; 014D; # LATIN CAPITAL LETTER O WITH MACRON
+014E; C; 014F; # LATIN CAPITAL LETTER O WITH BREVE
+0150; C; 0151; # LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0152; C; 0153; # LATIN CAPITAL LIGATURE OE
+0154; C; 0155; # LATIN CAPITAL LETTER R WITH ACUTE
+0156; C; 0157; # LATIN CAPITAL LETTER R WITH CEDILLA
+0158; C; 0159; # LATIN CAPITAL LETTER R WITH CARON
+015A; C; 015B; # LATIN CAPITAL LETTER S WITH ACUTE
+015C; C; 015D; # LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+015E; C; 015F; # LATIN CAPITAL LETTER S WITH CEDILLA
+0160; C; 0161; # LATIN CAPITAL LETTER S WITH CARON
+0162; C; 0163; # LATIN CAPITAL LETTER T WITH CEDILLA
+0164; C; 0165; # LATIN CAPITAL LETTER T WITH CARON
+0166; C; 0167; # LATIN CAPITAL LETTER T WITH STROKE
+0168; C; 0169; # LATIN CAPITAL LETTER U WITH TILDE
+016A; C; 016B; # LATIN CAPITAL LETTER U WITH MACRON
+016C; C; 016D; # LATIN CAPITAL LETTER U WITH BREVE
+016E; C; 016F; # LATIN CAPITAL LETTER U WITH RING ABOVE
+0170; C; 0171; # LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0172; C; 0173; # LATIN CAPITAL LETTER U WITH OGONEK
+0174; C; 0175; # LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+0176; C; 0177; # LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0178; C; 00FF; # LATIN CAPITAL LETTER Y WITH DIAERESIS
+0179; C; 017A; # LATIN CAPITAL LETTER Z WITH ACUTE
+017B; C; 017C; # LATIN CAPITAL LETTER Z WITH DOT ABOVE
+017D; C; 017E; # LATIN CAPITAL LETTER Z WITH CARON
+017F; C; 0073; # LATIN SMALL LETTER LONG S
+0181; C; 0253; # LATIN CAPITAL LETTER B WITH HOOK
+0182; C; 0183; # LATIN CAPITAL LETTER B WITH TOPBAR
+0184; C; 0185; # LATIN CAPITAL LETTER TONE SIX
+0186; C; 0254; # LATIN CAPITAL LETTER OPEN O
+0187; C; 0188; # LATIN CAPITAL LETTER C WITH HOOK
+0189; C; 0256; # LATIN CAPITAL LETTER AFRICAN D
+018A; C; 0257; # LATIN CAPITAL LETTER D WITH HOOK
+018B; C; 018C; # LATIN CAPITAL LETTER D WITH TOPBAR
+018E; C; 01DD; # LATIN CAPITAL LETTER REVERSED E
+018F; C; 0259; # LATIN CAPITAL LETTER SCHWA
+0190; C; 025B; # LATIN CAPITAL LETTER OPEN E
+0191; C; 0192; # LATIN CAPITAL LETTER F WITH HOOK
+0193; C; 0260; # LATIN CAPITAL LETTER G WITH HOOK
+0194; C; 0263; # LATIN CAPITAL LETTER GAMMA
+0196; C; 0269; # LATIN CAPITAL LETTER IOTA
+0197; C; 0268; # LATIN CAPITAL LETTER I WITH STROKE
+0198; C; 0199; # LATIN CAPITAL LETTER K WITH HOOK
+019C; C; 026F; # LATIN CAPITAL LETTER TURNED M
+019D; C; 0272; # LATIN CAPITAL LETTER N WITH LEFT HOOK
+019F; C; 0275; # LATIN CAPITAL LETTER O WITH MIDDLE TILDE
+01A0; C; 01A1; # LATIN CAPITAL LETTER O WITH HORN
+01A2; C; 01A3; # LATIN CAPITAL LETTER OI
+01A4; C; 01A5; # LATIN CAPITAL LETTER P WITH HOOK
+01A6; C; 0280; # LATIN LETTER YR
+01A7; C; 01A8; # LATIN CAPITAL LETTER TONE TWO
+01A9; C; 0283; # LATIN CAPITAL LETTER ESH
+01AC; C; 01AD; # LATIN CAPITAL LETTER T WITH HOOK
+01AE; C; 0288; # LATIN CAPITAL LETTER T WITH RETROFLEX HOOK
+01AF; C; 01B0; # LATIN CAPITAL LETTER U WITH HORN
+01B1; C; 028A; # LATIN CAPITAL LETTER UPSILON
+01B2; C; 028B; # LATIN CAPITAL LETTER V WITH HOOK
+01B3; C; 01B4; # LATIN CAPITAL LETTER Y WITH HOOK
+01B5; C; 01B6; # LATIN CAPITAL LETTER Z WITH STROKE
+01B7; C; 0292; # LATIN CAPITAL LETTER EZH
+01B8; C; 01B9; # LATIN CAPITAL LETTER EZH REVERSED
+01BC; C; 01BD; # LATIN CAPITAL LETTER TONE FIVE
+01C4; C; 01C6; # LATIN CAPITAL LETTER DZ WITH CARON
+01C5; C; 01C6; # LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON
+01C7; C; 01C9; # LATIN CAPITAL LETTER LJ
+01C8; C; 01C9; # LATIN CAPITAL LETTER L WITH SMALL LETTER J
+01CA; C; 01CC; # LATIN CAPITAL LETTER NJ
+01CB; C; 01CC; # LATIN CAPITAL LETTER N WITH SMALL LETTER J
+01CD; C; 01CE; # LATIN CAPITAL LETTER A WITH CARON
+01CF; C; 01D0; # LATIN CAPITAL LETTER I WITH CARON
+01D1; C; 01D2; # LATIN CAPITAL LETTER O WITH CARON
+01D3; C; 01D4; # LATIN CAPITAL LETTER U WITH CARON
+01D5; C; 01D6; # LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON
+01D7; C; 01D8; # LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE
+01D9; C; 01DA; # LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON
+01DB; C; 01DC; # LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE
+01DE; C; 01DF; # LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON
+01E0; C; 01E1; # LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON
+01E2; C; 01E3; # LATIN CAPITAL LETTER AE WITH MACRON
+01E4; C; 01E5; # LATIN CAPITAL LETTER G WITH STROKE
+01E6; C; 01E7; # LATIN CAPITAL LETTER G WITH CARON
+01E8; C; 01E9; # LATIN CAPITAL LETTER K WITH CARON
+01EA; C; 01EB; # LATIN CAPITAL LETTER O WITH OGONEK
+01EC; C; 01ED; # LATIN CAPITAL LETTER O WITH OGONEK AND MACRON
+01EE; C; 01EF; # LATIN CAPITAL LETTER EZH WITH CARON
+01F0; F; 006A 030C; # LATIN SMALL LETTER J WITH CARON
+01F1; C; 01F3; # LATIN CAPITAL LETTER DZ
+01F2; C; 01F3; # LATIN CAPITAL LETTER D WITH SMALL LETTER Z
+01F4; C; 01F5; # LATIN CAPITAL LETTER G WITH ACUTE
+01F6; C; 0195; # LATIN CAPITAL LETTER HWAIR
+01F7; C; 01BF; # LATIN CAPITAL LETTER WYNN
+01F8; C; 01F9; # LATIN CAPITAL LETTER N WITH GRAVE
+01FA; C; 01FB; # LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+01FC; C; 01FD; # LATIN CAPITAL LETTER AE WITH ACUTE
+01FE; C; 01FF; # LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+0200; C; 0201; # LATIN CAPITAL LETTER A WITH DOUBLE GRAVE
+0202; C; 0203; # LATIN CAPITAL LETTER A WITH INVERTED BREVE
+0204; C; 0205; # LATIN CAPITAL LETTER E WITH DOUBLE GRAVE
+0206; C; 0207; # LATIN CAPITAL LETTER E WITH INVERTED BREVE
+0208; C; 0209; # LATIN CAPITAL LETTER I WITH DOUBLE GRAVE
+020A; C; 020B; # LATIN CAPITAL LETTER I WITH INVERTED BREVE
+020C; C; 020D; # LATIN CAPITAL LETTER O WITH DOUBLE GRAVE
+020E; C; 020F; # LATIN CAPITAL LETTER O WITH INVERTED BREVE
+0210; C; 0211; # LATIN CAPITAL LETTER R WITH DOUBLE GRAVE
+0212; C; 0213; # LATIN CAPITAL LETTER R WITH INVERTED BREVE
+0214; C; 0215; # LATIN CAPITAL LETTER U WITH DOUBLE GRAVE
+0216; C; 0217; # LATIN CAPITAL LETTER U WITH INVERTED BREVE
+0218; C; 0219; # LATIN CAPITAL LETTER S WITH COMMA BELOW
+021A; C; 021B; # LATIN CAPITAL LETTER T WITH COMMA BELOW
+021C; C; 021D; # LATIN CAPITAL LETTER YOGH
+021E; C; 021F; # LATIN CAPITAL LETTER H WITH CARON
+0220; C; 019E; # LATIN CAPITAL LETTER N WITH LONG RIGHT LEG
+0222; C; 0223; # LATIN CAPITAL LETTER OU
+0224; C; 0225; # LATIN CAPITAL LETTER Z WITH HOOK
+0226; C; 0227; # LATIN CAPITAL LETTER A WITH DOT ABOVE
+0228; C; 0229; # LATIN CAPITAL LETTER E WITH CEDILLA
+022A; C; 022B; # LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON
+022C; C; 022D; # LATIN CAPITAL LETTER O WITH TILDE AND MACRON
+022E; C; 022F; # LATIN CAPITAL LETTER O WITH DOT ABOVE
+0230; C; 0231; # LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON
+0232; C; 0233; # LATIN CAPITAL LETTER Y WITH MACRON
+023B; C; 023C; # LATIN CAPITAL LETTER C WITH STROKE
+023D; C; 019A; # LATIN CAPITAL LETTER L WITH BAR
+0241; C; 0294; # LATIN CAPITAL LETTER GLOTTAL STOP
+0345; C; 03B9; # COMBINING GREEK YPOGEGRAMMENI
+0386; C; 03AC; # GREEK CAPITAL LETTER ALPHA WITH TONOS
+0388; C; 03AD; # GREEK CAPITAL LETTER EPSILON WITH TONOS
+0389; C; 03AE; # GREEK CAPITAL LETTER ETA WITH TONOS
+038A; C; 03AF; # GREEK CAPITAL LETTER IOTA WITH TONOS
+038C; C; 03CC; # GREEK CAPITAL LETTER OMICRON WITH TONOS
+038E; C; 03CD; # GREEK CAPITAL LETTER UPSILON WITH TONOS
+038F; C; 03CE; # GREEK CAPITAL LETTER OMEGA WITH TONOS
+0390; F; 03B9 0308 0301; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+0391; C; 03B1; # GREEK CAPITAL LETTER ALPHA
+0392; C; 03B2; # GREEK CAPITAL LETTER BETA
+0393; C; 03B3; # GREEK CAPITAL LETTER GAMMA
+0394; C; 03B4; # GREEK CAPITAL LETTER DELTA
+0395; C; 03B5; # GREEK CAPITAL LETTER EPSILON
+0396; C; 03B6; # GREEK CAPITAL LETTER ZETA
+0397; C; 03B7; # GREEK CAPITAL LETTER ETA
+0398; C; 03B8; # GREEK CAPITAL LETTER THETA
+0399; C; 03B9; # GREEK CAPITAL LETTER IOTA
+039A; C; 03BA; # GREEK CAPITAL LETTER KAPPA
+039B; C; 03BB; # GREEK CAPITAL LETTER LAMDA
+039C; C; 03BC; # GREEK CAPITAL LETTER MU
+039D; C; 03BD; # GREEK CAPITAL LETTER NU
+039E; C; 03BE; # GREEK CAPITAL LETTER XI
+039F; C; 03BF; # GREEK CAPITAL LETTER OMICRON
+03A0; C; 03C0; # GREEK CAPITAL LETTER PI
+03A1; C; 03C1; # GREEK CAPITAL LETTER RHO
+03A3; C; 03C3; # GREEK CAPITAL LETTER SIGMA
+03A4; C; 03C4; # GREEK CAPITAL LETTER TAU
+03A5; C; 03C5; # GREEK CAPITAL LETTER UPSILON
+03A6; C; 03C6; # GREEK CAPITAL LETTER PHI
+03A7; C; 03C7; # GREEK CAPITAL LETTER CHI
+03A8; C; 03C8; # GREEK CAPITAL LETTER PSI
+03A9; C; 03C9; # GREEK CAPITAL LETTER OMEGA
+03AA; C; 03CA; # GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+03AB; C; 03CB; # GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+03B0; F; 03C5 0308 0301; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03C2; C; 03C3; # GREEK SMALL LETTER FINAL SIGMA
+03D0; C; 03B2; # GREEK BETA SYMBOL
+03D1; C; 03B8; # GREEK THETA SYMBOL
+03D5; C; 03C6; # GREEK PHI SYMBOL
+03D6; C; 03C0; # GREEK PI SYMBOL
+03D8; C; 03D9; # GREEK LETTER ARCHAIC KOPPA
+03DA; C; 03DB; # GREEK LETTER STIGMA
+03DC; C; 03DD; # GREEK LETTER DIGAMMA
+03DE; C; 03DF; # GREEK LETTER KOPPA
+03E0; C; 03E1; # GREEK LETTER SAMPI
+03E2; C; 03E3; # COPTIC CAPITAL LETTER SHEI
+03E4; C; 03E5; # COPTIC CAPITAL LETTER FEI
+03E6; C; 03E7; # COPTIC CAPITAL LETTER KHEI
+03E8; C; 03E9; # COPTIC CAPITAL LETTER HORI
+03EA; C; 03EB; # COPTIC CAPITAL LETTER GANGIA
+03EC; C; 03ED; # COPTIC CAPITAL LETTER SHIMA
+03EE; C; 03EF; # COPTIC CAPITAL LETTER DEI
+03F0; C; 03BA; # GREEK KAPPA SYMBOL
+03F1; C; 03C1; # GREEK RHO SYMBOL
+03F4; C; 03B8; # GREEK CAPITAL THETA SYMBOL
+03F5; C; 03B5; # GREEK LUNATE EPSILON SYMBOL
+03F7; C; 03F8; # GREEK CAPITAL LETTER SHO
+03F9; C; 03F2; # GREEK CAPITAL LUNATE SIGMA SYMBOL
+03FA; C; 03FB; # GREEK CAPITAL LETTER SAN
+0400; C; 0450; # CYRILLIC CAPITAL LETTER IE WITH GRAVE
+0401; C; 0451; # CYRILLIC CAPITAL LETTER IO
+0402; C; 0452; # CYRILLIC CAPITAL LETTER DJE
+0403; C; 0453; # CYRILLIC CAPITAL LETTER GJE
+0404; C; 0454; # CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0405; C; 0455; # CYRILLIC CAPITAL LETTER DZE
+0406; C; 0456; # CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0407; C; 0457; # CYRILLIC CAPITAL LETTER YI
+0408; C; 0458; # CYRILLIC CAPITAL LETTER JE
+0409; C; 0459; # CYRILLIC CAPITAL LETTER LJE
+040A; C; 045A; # CYRILLIC CAPITAL LETTER NJE
+040B; C; 045B; # CYRILLIC CAPITAL LETTER TSHE
+040C; C; 045C; # CYRILLIC CAPITAL LETTER KJE
+040D; C; 045D; # CYRILLIC CAPITAL LETTER I WITH GRAVE
+040E; C; 045E; # CYRILLIC CAPITAL LETTER SHORT U
+040F; C; 045F; # CYRILLIC CAPITAL LETTER DZHE
+0410; C; 0430; # CYRILLIC CAPITAL LETTER A
+0411; C; 0431; # CYRILLIC CAPITAL LETTER BE
+0412; C; 0432; # CYRILLIC CAPITAL LETTER VE
+0413; C; 0433; # CYRILLIC CAPITAL LETTER GHE
+0414; C; 0434; # CYRILLIC CAPITAL LETTER DE
+0415; C; 0435; # CYRILLIC CAPITAL LETTER IE
+0416; C; 0436; # CYRILLIC CAPITAL LETTER ZHE
+0417; C; 0437; # CYRILLIC CAPITAL LETTER ZE
+0418; C; 0438; # CYRILLIC CAPITAL LETTER I
+0419; C; 0439; # CYRILLIC CAPITAL LETTER SHORT I
+041A; C; 043A; # CYRILLIC CAPITAL LETTER KA
+041B; C; 043B; # CYRILLIC CAPITAL LETTER EL
+041C; C; 043C; # CYRILLIC CAPITAL LETTER EM
+041D; C; 043D; # CYRILLIC CAPITAL LETTER EN
+041E; C; 043E; # CYRILLIC CAPITAL LETTER O
+041F; C; 043F; # CYRILLIC CAPITAL LETTER PE
+0420; C; 0440; # CYRILLIC CAPITAL LETTER ER
+0421; C; 0441; # CYRILLIC CAPITAL LETTER ES
+0422; C; 0442; # CYRILLIC CAPITAL LETTER TE
+0423; C; 0443; # CYRILLIC CAPITAL LETTER U
+0424; C; 0444; # CYRILLIC CAPITAL LETTER EF
+0425; C; 0445; # CYRILLIC CAPITAL LETTER HA
+0426; C; 0446; # CYRILLIC CAPITAL LETTER TSE
+0427; C; 0447; # CYRILLIC CAPITAL LETTER CHE
+0428; C; 0448; # CYRILLIC CAPITAL LETTER SHA
+0429; C; 0449; # CYRILLIC CAPITAL LETTER SHCHA
+042A; C; 044A; # CYRILLIC CAPITAL LETTER HARD SIGN
+042B; C; 044B; # CYRILLIC CAPITAL LETTER YERU
+042C; C; 044C; # CYRILLIC CAPITAL LETTER SOFT SIGN
+042D; C; 044D; # CYRILLIC CAPITAL LETTER E
+042E; C; 044E; # CYRILLIC CAPITAL LETTER YU
+042F; C; 044F; # CYRILLIC CAPITAL LETTER YA
+0460; C; 0461; # CYRILLIC CAPITAL LETTER OMEGA
+0462; C; 0463; # CYRILLIC CAPITAL LETTER YAT
+0464; C; 0465; # CYRILLIC CAPITAL LETTER IOTIFIED E
+0466; C; 0467; # CYRILLIC CAPITAL LETTER LITTLE YUS
+0468; C; 0469; # CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS
+046A; C; 046B; # CYRILLIC CAPITAL LETTER BIG YUS
+046C; C; 046D; # CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS
+046E; C; 046F; # CYRILLIC CAPITAL LETTER KSI
+0470; C; 0471; # CYRILLIC CAPITAL LETTER PSI
+0472; C; 0473; # CYRILLIC CAPITAL LETTER FITA
+0474; C; 0475; # CYRILLIC CAPITAL LETTER IZHITSA
+0476; C; 0477; # CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT
+0478; C; 0479; # CYRILLIC CAPITAL LETTER UK
+047A; C; 047B; # CYRILLIC CAPITAL LETTER ROUND OMEGA
+047C; C; 047D; # CYRILLIC CAPITAL LETTER OMEGA WITH TITLO
+047E; C; 047F; # CYRILLIC CAPITAL LETTER OT
+0480; C; 0481; # CYRILLIC CAPITAL LETTER KOPPA
+048A; C; 048B; # CYRILLIC CAPITAL LETTER SHORT I WITH TAIL
+048C; C; 048D; # CYRILLIC CAPITAL LETTER SEMISOFT SIGN
+048E; C; 048F; # CYRILLIC CAPITAL LETTER ER WITH TICK
+0490; C; 0491; # CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0492; C; 0493; # CYRILLIC CAPITAL LETTER GHE WITH STROKE
+0494; C; 0495; # CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK
+0496; C; 0497; # CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER
+0498; C; 0499; # CYRILLIC CAPITAL LETTER ZE WITH DESCENDER
+049A; C; 049B; # CYRILLIC CAPITAL LETTER KA WITH DESCENDER
+049C; C; 049D; # CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE
+049E; C; 049F; # CYRILLIC CAPITAL LETTER KA WITH STROKE
+04A0; C; 04A1; # CYRILLIC CAPITAL LETTER BASHKIR KA
+04A2; C; 04A3; # CYRILLIC CAPITAL LETTER EN WITH DESCENDER
+04A4; C; 04A5; # CYRILLIC CAPITAL LIGATURE EN GHE
+04A6; C; 04A7; # CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK
+04A8; C; 04A9; # CYRILLIC CAPITAL LETTER ABKHASIAN HA
+04AA; C; 04AB; # CYRILLIC CAPITAL LETTER ES WITH DESCENDER
+04AC; C; 04AD; # CYRILLIC CAPITAL LETTER TE WITH DESCENDER
+04AE; C; 04AF; # CYRILLIC CAPITAL LETTER STRAIGHT U
+04B0; C; 04B1; # CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE
+04B2; C; 04B3; # CYRILLIC CAPITAL LETTER HA WITH DESCENDER
+04B4; C; 04B5; # CYRILLIC CAPITAL LIGATURE TE TSE
+04B6; C; 04B7; # CYRILLIC CAPITAL LETTER CHE WITH DESCENDER
+04B8; C; 04B9; # CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE
+04BA; C; 04BB; # CYRILLIC CAPITAL LETTER SHHA
+04BC; C; 04BD; # CYRILLIC CAPITAL LETTER ABKHASIAN CHE
+04BE; C; 04BF; # CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER
+04C1; C; 04C2; # CYRILLIC CAPITAL LETTER ZHE WITH BREVE
+04C3; C; 04C4; # CYRILLIC CAPITAL LETTER KA WITH HOOK
+04C5; C; 04C6; # CYRILLIC CAPITAL LETTER EL WITH TAIL
+04C7; C; 04C8; # CYRILLIC CAPITAL LETTER EN WITH HOOK
+04C9; C; 04CA; # CYRILLIC CAPITAL LETTER EN WITH TAIL
+04CB; C; 04CC; # CYRILLIC CAPITAL LETTER KHAKASSIAN CHE
+04CD; C; 04CE; # CYRILLIC CAPITAL LETTER EM WITH TAIL
+04D0; C; 04D1; # CYRILLIC CAPITAL LETTER A WITH BREVE
+04D2; C; 04D3; # CYRILLIC CAPITAL LETTER A WITH DIAERESIS
+04D4; C; 04D5; # CYRILLIC CAPITAL LIGATURE A IE
+04D6; C; 04D7; # CYRILLIC CAPITAL LETTER IE WITH BREVE
+04D8; C; 04D9; # CYRILLIC CAPITAL LETTER SCHWA
+04DA; C; 04DB; # CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS
+04DC; C; 04DD; # CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS
+04DE; C; 04DF; # CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS
+04E0; C; 04E1; # CYRILLIC CAPITAL LETTER ABKHASIAN DZE
+04E2; C; 04E3; # CYRILLIC CAPITAL LETTER I WITH MACRON
+04E4; C; 04E5; # CYRILLIC CAPITAL LETTER I WITH DIAERESIS
+04E6; C; 04E7; # CYRILLIC CAPITAL LETTER O WITH DIAERESIS
+04E8; C; 04E9; # CYRILLIC CAPITAL LETTER BARRED O
+04EA; C; 04EB; # CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS
+04EC; C; 04ED; # CYRILLIC CAPITAL LETTER E WITH DIAERESIS
+04EE; C; 04EF; # CYRILLIC CAPITAL LETTER U WITH MACRON
+04F0; C; 04F1; # CYRILLIC CAPITAL LETTER U WITH DIAERESIS
+04F2; C; 04F3; # CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE
+04F4; C; 04F5; # CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS
+04F6; C; 04F7; # CYRILLIC CAPITAL LETTER GHE WITH DESCENDER
+04F8; C; 04F9; # CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS
+0500; C; 0501; # CYRILLIC CAPITAL LETTER KOMI DE
+0502; C; 0503; # CYRILLIC CAPITAL LETTER KOMI DJE
+0504; C; 0505; # CYRILLIC CAPITAL LETTER KOMI ZJE
+0506; C; 0507; # CYRILLIC CAPITAL LETTER KOMI DZJE
+0508; C; 0509; # CYRILLIC CAPITAL LETTER KOMI LJE
+050A; C; 050B; # CYRILLIC CAPITAL LETTER KOMI NJE
+050C; C; 050D; # CYRILLIC CAPITAL LETTER KOMI SJE
+050E; C; 050F; # CYRILLIC CAPITAL LETTER KOMI TJE
+0531; C; 0561; # ARMENIAN CAPITAL LETTER AYB
+0532; C; 0562; # ARMENIAN CAPITAL LETTER BEN
+0533; C; 0563; # ARMENIAN CAPITAL LETTER GIM
+0534; C; 0564; # ARMENIAN CAPITAL LETTER DA
+0535; C; 0565; # ARMENIAN CAPITAL LETTER ECH
+0536; C; 0566; # ARMENIAN CAPITAL LETTER ZA
+0537; C; 0567; # ARMENIAN CAPITAL LETTER EH
+0538; C; 0568; # ARMENIAN CAPITAL LETTER ET
+0539; C; 0569; # ARMENIAN CAPITAL LETTER TO
+053A; C; 056A; # ARMENIAN CAPITAL LETTER ZHE
+053B; C; 056B; # ARMENIAN CAPITAL LETTER INI
+053C; C; 056C; # ARMENIAN CAPITAL LETTER LIWN
+053D; C; 056D; # ARMENIAN CAPITAL LETTER XEH
+053E; C; 056E; # ARMENIAN CAPITAL LETTER CA
+053F; C; 056F; # ARMENIAN CAPITAL LETTER KEN
+0540; C; 0570; # ARMENIAN CAPITAL LETTER HO
+0541; C; 0571; # ARMENIAN CAPITAL LETTER JA
+0542; C; 0572; # ARMENIAN CAPITAL LETTER GHAD
+0543; C; 0573; # ARMENIAN CAPITAL LETTER CHEH
+0544; C; 0574; # ARMENIAN CAPITAL LETTER MEN
+0545; C; 0575; # ARMENIAN CAPITAL LETTER YI
+0546; C; 0576; # ARMENIAN CAPITAL LETTER NOW
+0547; C; 0577; # ARMENIAN CAPITAL LETTER SHA
+0548; C; 0578; # ARMENIAN CAPITAL LETTER VO
+0549; C; 0579; # ARMENIAN CAPITAL LETTER CHA
+054A; C; 057A; # ARMENIAN CAPITAL LETTER PEH
+054B; C; 057B; # ARMENIAN CAPITAL LETTER JHEH
+054C; C; 057C; # ARMENIAN CAPITAL LETTER RA
+054D; C; 057D; # ARMENIAN CAPITAL LETTER SEH
+054E; C; 057E; # ARMENIAN CAPITAL LETTER VEW
+054F; C; 057F; # ARMENIAN CAPITAL LETTER TIWN
+0550; C; 0580; # ARMENIAN CAPITAL LETTER REH
+0551; C; 0581; # ARMENIAN CAPITAL LETTER CO
+0552; C; 0582; # ARMENIAN CAPITAL LETTER YIWN
+0553; C; 0583; # ARMENIAN CAPITAL LETTER PIWR
+0554; C; 0584; # ARMENIAN CAPITAL LETTER KEH
+0555; C; 0585; # ARMENIAN CAPITAL LETTER OH
+0556; C; 0586; # ARMENIAN CAPITAL LETTER FEH
+0587; F; 0565 0582; # ARMENIAN SMALL LIGATURE ECH YIWN
+10A0; C; 2D00; # GEORGIAN CAPITAL LETTER AN
+10A1; C; 2D01; # GEORGIAN CAPITAL LETTER BAN
+10A2; C; 2D02; # GEORGIAN CAPITAL LETTER GAN
+10A3; C; 2D03; # GEORGIAN CAPITAL LETTER DON
+10A4; C; 2D04; # GEORGIAN CAPITAL LETTER EN
+10A5; C; 2D05; # GEORGIAN CAPITAL LETTER VIN
+10A6; C; 2D06; # GEORGIAN CAPITAL LETTER ZEN
+10A7; C; 2D07; # GEORGIAN CAPITAL LETTER TAN
+10A8; C; 2D08; # GEORGIAN CAPITAL LETTER IN
+10A9; C; 2D09; # GEORGIAN CAPITAL LETTER KAN
+10AA; C; 2D0A; # GEORGIAN CAPITAL LETTER LAS
+10AB; C; 2D0B; # GEORGIAN CAPITAL LETTER MAN
+10AC; C; 2D0C; # GEORGIAN CAPITAL LETTER NAR
+10AD; C; 2D0D; # GEORGIAN CAPITAL LETTER ON
+10AE; C; 2D0E; # GEORGIAN CAPITAL LETTER PAR
+10AF; C; 2D0F; # GEORGIAN CAPITAL LETTER ZHAR
+10B0; C; 2D10; # GEORGIAN CAPITAL LETTER RAE
+10B1; C; 2D11; # GEORGIAN CAPITAL LETTER SAN
+10B2; C; 2D12; # GEORGIAN CAPITAL LETTER TAR
+10B3; C; 2D13; # GEORGIAN CAPITAL LETTER UN
+10B4; C; 2D14; # GEORGIAN CAPITAL LETTER PHAR
+10B5; C; 2D15; # GEORGIAN CAPITAL LETTER KHAR
+10B6; C; 2D16; # GEORGIAN CAPITAL LETTER GHAN
+10B7; C; 2D17; # GEORGIAN CAPITAL LETTER QAR
+10B8; C; 2D18; # GEORGIAN CAPITAL LETTER SHIN
+10B9; C; 2D19; # GEORGIAN CAPITAL LETTER CHIN
+10BA; C; 2D1A; # GEORGIAN CAPITAL LETTER CAN
+10BB; C; 2D1B; # GEORGIAN CAPITAL LETTER JIL
+10BC; C; 2D1C; # GEORGIAN CAPITAL LETTER CIL
+10BD; C; 2D1D; # GEORGIAN CAPITAL LETTER CHAR
+10BE; C; 2D1E; # GEORGIAN CAPITAL LETTER XAN
+10BF; C; 2D1F; # GEORGIAN CAPITAL LETTER JHAN
+10C0; C; 2D20; # GEORGIAN CAPITAL LETTER HAE
+10C1; C; 2D21; # GEORGIAN CAPITAL LETTER HE
+10C2; C; 2D22; # GEORGIAN CAPITAL LETTER HIE
+10C3; C; 2D23; # GEORGIAN CAPITAL LETTER WE
+10C4; C; 2D24; # GEORGIAN CAPITAL LETTER HAR
+10C5; C; 2D25; # GEORGIAN CAPITAL LETTER HOE
+1E00; C; 1E01; # LATIN CAPITAL LETTER A WITH RING BELOW
+1E02; C; 1E03; # LATIN CAPITAL LETTER B WITH DOT ABOVE
+1E04; C; 1E05; # LATIN CAPITAL LETTER B WITH DOT BELOW
+1E06; C; 1E07; # LATIN CAPITAL LETTER B WITH LINE BELOW
+1E08; C; 1E09; # LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE
+1E0A; C; 1E0B; # LATIN CAPITAL LETTER D WITH DOT ABOVE
+1E0C; C; 1E0D; # LATIN CAPITAL LETTER D WITH DOT BELOW
+1E0E; C; 1E0F; # LATIN CAPITAL LETTER D WITH LINE BELOW
+1E10; C; 1E11; # LATIN CAPITAL LETTER D WITH CEDILLA
+1E12; C; 1E13; # LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW
+1E14; C; 1E15; # LATIN CAPITAL LETTER E WITH MACRON AND GRAVE
+1E16; C; 1E17; # LATIN CAPITAL LETTER E WITH MACRON AND ACUTE
+1E18; C; 1E19; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW
+1E1A; C; 1E1B; # LATIN CAPITAL LETTER E WITH TILDE BELOW
+1E1C; C; 1E1D; # LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE
+1E1E; C; 1E1F; # LATIN CAPITAL LETTER F WITH DOT ABOVE
+1E20; C; 1E21; # LATIN CAPITAL LETTER G WITH MACRON
+1E22; C; 1E23; # LATIN CAPITAL LETTER H WITH DOT ABOVE
+1E24; C; 1E25; # LATIN CAPITAL LETTER H WITH DOT BELOW
+1E26; C; 1E27; # LATIN CAPITAL LETTER H WITH DIAERESIS
+1E28; C; 1E29; # LATIN CAPITAL LETTER H WITH CEDILLA
+1E2A; C; 1E2B; # LATIN CAPITAL LETTER H WITH BREVE BELOW
+1E2C; C; 1E2D; # LATIN CAPITAL LETTER I WITH TILDE BELOW
+1E2E; C; 1E2F; # LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE
+1E30; C; 1E31; # LATIN CAPITAL LETTER K WITH ACUTE
+1E32; C; 1E33; # LATIN CAPITAL LETTER K WITH DOT BELOW
+1E34; C; 1E35; # LATIN CAPITAL LETTER K WITH LINE BELOW
+1E36; C; 1E37; # LATIN CAPITAL LETTER L WITH DOT BELOW
+1E38; C; 1E39; # LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON
+1E3A; C; 1E3B; # LATIN CAPITAL LETTER L WITH LINE BELOW
+1E3C; C; 1E3D; # LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW
+1E3E; C; 1E3F; # LATIN CAPITAL LETTER M WITH ACUTE
+1E40; C; 1E41; # LATIN CAPITAL LETTER M WITH DOT ABOVE
+1E42; C; 1E43; # LATIN CAPITAL LETTER M WITH DOT BELOW
+1E44; C; 1E45; # LATIN CAPITAL LETTER N WITH DOT ABOVE
+1E46; C; 1E47; # LATIN CAPITAL LETTER N WITH DOT BELOW
+1E48; C; 1E49; # LATIN CAPITAL LETTER N WITH LINE BELOW
+1E4A; C; 1E4B; # LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW
+1E4C; C; 1E4D; # LATIN CAPITAL LETTER O WITH TILDE AND ACUTE
+1E4E; C; 1E4F; # LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS
+1E50; C; 1E51; # LATIN CAPITAL LETTER O WITH MACRON AND GRAVE
+1E52; C; 1E53; # LATIN CAPITAL LETTER O WITH MACRON AND ACUTE
+1E54; C; 1E55; # LATIN CAPITAL LETTER P WITH ACUTE
+1E56; C; 1E57; # LATIN CAPITAL LETTER P WITH DOT ABOVE
+1E58; C; 1E59; # LATIN CAPITAL LETTER R WITH DOT ABOVE
+1E5A; C; 1E5B; # LATIN CAPITAL LETTER R WITH DOT BELOW
+1E5C; C; 1E5D; # LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON
+1E5E; C; 1E5F; # LATIN CAPITAL LETTER R WITH LINE BELOW
+1E60; C; 1E61; # LATIN CAPITAL LETTER S WITH DOT ABOVE
+1E62; C; 1E63; # LATIN CAPITAL LETTER S WITH DOT BELOW
+1E64; C; 1E65; # LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE
+1E66; C; 1E67; # LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE
+1E68; C; 1E69; # LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE
+1E6A; C; 1E6B; # LATIN CAPITAL LETTER T WITH DOT ABOVE
+1E6C; C; 1E6D; # LATIN CAPITAL LETTER T WITH DOT BELOW
+1E6E; C; 1E6F; # LATIN CAPITAL LETTER T WITH LINE BELOW
+1E70; C; 1E71; # LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW
+1E72; C; 1E73; # LATIN CAPITAL LETTER U WITH DIAERESIS BELOW
+1E74; C; 1E75; # LATIN CAPITAL LETTER U WITH TILDE BELOW
+1E76; C; 1E77; # LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW
+1E78; C; 1E79; # LATIN CAPITAL LETTER U WITH TILDE AND ACUTE
+1E7A; C; 1E7B; # LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS
+1E7C; C; 1E7D; # LATIN CAPITAL LETTER V WITH TILDE
+1E7E; C; 1E7F; # LATIN CAPITAL LETTER V WITH DOT BELOW
+1E80; C; 1E81; # LATIN CAPITAL LETTER W WITH GRAVE
+1E82; C; 1E83; # LATIN CAPITAL LETTER W WITH ACUTE
+1E84; C; 1E85; # LATIN CAPITAL LETTER W WITH DIAERESIS
+1E86; C; 1E87; # LATIN CAPITAL LETTER W WITH DOT ABOVE
+1E88; C; 1E89; # LATIN CAPITAL LETTER W WITH DOT BELOW
+1E8A; C; 1E8B; # LATIN CAPITAL LETTER X WITH DOT ABOVE
+1E8C; C; 1E8D; # LATIN CAPITAL LETTER X WITH DIAERESIS
+1E8E; C; 1E8F; # LATIN CAPITAL LETTER Y WITH DOT ABOVE
+1E90; C; 1E91; # LATIN CAPITAL LETTER Z WITH CIRCUMFLEX
+1E92; C; 1E93; # LATIN CAPITAL LETTER Z WITH DOT BELOW
+1E94; C; 1E95; # LATIN CAPITAL LETTER Z WITH LINE BELOW
+1E96; F; 0068 0331; # LATIN SMALL LETTER H WITH LINE BELOW
+1E97; F; 0074 0308; # LATIN SMALL LETTER T WITH DIAERESIS
+1E98; F; 0077 030A; # LATIN SMALL LETTER W WITH RING ABOVE
+1E99; F; 0079 030A; # LATIN SMALL LETTER Y WITH RING ABOVE
+1E9A; F; 0061 02BE; # LATIN SMALL LETTER A WITH RIGHT HALF RING
+1E9B; C; 1E61; # LATIN SMALL LETTER LONG S WITH DOT ABOVE
+1EA0; C; 1EA1; # LATIN CAPITAL LETTER A WITH DOT BELOW
+1EA2; C; 1EA3; # LATIN CAPITAL LETTER A WITH HOOK ABOVE
+1EA4; C; 1EA5; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE
+1EA6; C; 1EA7; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE
+1EA8; C; 1EA9; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+1EAA; C; 1EAB; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE
+1EAC; C; 1EAD; # LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+1EAE; C; 1EAF; # LATIN CAPITAL LETTER A WITH BREVE AND ACUTE
+1EB0; C; 1EB1; # LATIN CAPITAL LETTER A WITH BREVE AND GRAVE
+1EB2; C; 1EB3; # LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE
+1EB4; C; 1EB5; # LATIN CAPITAL LETTER A WITH BREVE AND TILDE
+1EB6; C; 1EB7; # LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW
+1EB8; C; 1EB9; # LATIN CAPITAL LETTER E WITH DOT BELOW
+1EBA; C; 1EBB; # LATIN CAPITAL LETTER E WITH HOOK ABOVE
+1EBC; C; 1EBD; # LATIN CAPITAL LETTER E WITH TILDE
+1EBE; C; 1EBF; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE
+1EC0; C; 1EC1; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE
+1EC2; C; 1EC3; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+1EC4; C; 1EC5; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE
+1EC6; C; 1EC7; # LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+1EC8; C; 1EC9; # LATIN CAPITAL LETTER I WITH HOOK ABOVE
+1ECA; C; 1ECB; # LATIN CAPITAL LETTER I WITH DOT BELOW
+1ECC; C; 1ECD; # LATIN CAPITAL LETTER O WITH DOT BELOW
+1ECE; C; 1ECF; # LATIN CAPITAL LETTER O WITH HOOK ABOVE
+1ED0; C; 1ED1; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE
+1ED2; C; 1ED3; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE
+1ED4; C; 1ED5; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+1ED6; C; 1ED7; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE
+1ED8; C; 1ED9; # LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+1EDA; C; 1EDB; # LATIN CAPITAL LETTER O WITH HORN AND ACUTE
+1EDC; C; 1EDD; # LATIN CAPITAL LETTER O WITH HORN AND GRAVE
+1EDE; C; 1EDF; # LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE
+1EE0; C; 1EE1; # LATIN CAPITAL LETTER O WITH HORN AND TILDE
+1EE2; C; 1EE3; # LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW
+1EE4; C; 1EE5; # LATIN CAPITAL LETTER U WITH DOT BELOW
+1EE6; C; 1EE7; # LATIN CAPITAL LETTER U WITH HOOK ABOVE
+1EE8; C; 1EE9; # LATIN CAPITAL LETTER U WITH HORN AND ACUTE
+1EEA; C; 1EEB; # LATIN CAPITAL LETTER U WITH HORN AND GRAVE
+1EEC; C; 1EED; # LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE
+1EEE; C; 1EEF; # LATIN CAPITAL LETTER U WITH HORN AND TILDE
+1EF0; C; 1EF1; # LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW
+1EF2; C; 1EF3; # LATIN CAPITAL LETTER Y WITH GRAVE
+1EF4; C; 1EF5; # LATIN CAPITAL LETTER Y WITH DOT BELOW
+1EF6; C; 1EF7; # LATIN CAPITAL LETTER Y WITH HOOK ABOVE
+1EF8; C; 1EF9; # LATIN CAPITAL LETTER Y WITH TILDE
+1F08; C; 1F00; # GREEK CAPITAL LETTER ALPHA WITH PSILI
+1F09; C; 1F01; # GREEK CAPITAL LETTER ALPHA WITH DASIA
+1F0A; C; 1F02; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA
+1F0B; C; 1F03; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA
+1F0C; C; 1F04; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA
+1F0D; C; 1F05; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA
+1F0E; C; 1F06; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI
+1F0F; C; 1F07; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI
+1F18; C; 1F10; # GREEK CAPITAL LETTER EPSILON WITH PSILI
+1F19; C; 1F11; # GREEK CAPITAL LETTER EPSILON WITH DASIA
+1F1A; C; 1F12; # GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA
+1F1B; C; 1F13; # GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA
+1F1C; C; 1F14; # GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA
+1F1D; C; 1F15; # GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+1F28; C; 1F20; # GREEK CAPITAL LETTER ETA WITH PSILI
+1F29; C; 1F21; # GREEK CAPITAL LETTER ETA WITH DASIA
+1F2A; C; 1F22; # GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA
+1F2B; C; 1F23; # GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA
+1F2C; C; 1F24; # GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA
+1F2D; C; 1F25; # GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA
+1F2E; C; 1F26; # GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI
+1F2F; C; 1F27; # GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI
+1F38; C; 1F30; # GREEK CAPITAL LETTER IOTA WITH PSILI
+1F39; C; 1F31; # GREEK CAPITAL LETTER IOTA WITH DASIA
+1F3A; C; 1F32; # GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA
+1F3B; C; 1F33; # GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA
+1F3C; C; 1F34; # GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA
+1F3D; C; 1F35; # GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA
+1F3E; C; 1F36; # GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI
+1F3F; C; 1F37; # GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI
+1F48; C; 1F40; # GREEK CAPITAL LETTER OMICRON WITH PSILI
+1F49; C; 1F41; # GREEK CAPITAL LETTER OMICRON WITH DASIA
+1F4A; C; 1F42; # GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA
+1F4B; C; 1F43; # GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA
+1F4C; C; 1F44; # GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA
+1F4D; C; 1F45; # GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+1F50; F; 03C5 0313; # GREEK SMALL LETTER UPSILON WITH PSILI
+1F52; F; 03C5 0313 0300; # GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA
+1F54; F; 03C5 0313 0301; # GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA
+1F56; F; 03C5 0313 0342; # GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI
+1F59; C; 1F51; # GREEK CAPITAL LETTER UPSILON WITH DASIA
+1F5B; C; 1F53; # GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+1F5D; C; 1F55; # GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+1F5F; C; 1F57; # GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI
+1F68; C; 1F60; # GREEK CAPITAL LETTER OMEGA WITH PSILI
+1F69; C; 1F61; # GREEK CAPITAL LETTER OMEGA WITH DASIA
+1F6A; C; 1F62; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA
+1F6B; C; 1F63; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA
+1F6C; C; 1F64; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA
+1F6D; C; 1F65; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA
+1F6E; C; 1F66; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI
+1F6F; C; 1F67; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI
+1F80; F; 1F00 03B9; # GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI
+1F81; F; 1F01 03B9; # GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI
+1F82; F; 1F02 03B9; # GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+1F83; F; 1F03 03B9; # GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+1F84; F; 1F04 03B9; # GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+1F85; F; 1F05 03B9; # GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+1F86; F; 1F06 03B9; # GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+1F87; F; 1F07 03B9; # GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1F88; F; 1F00 03B9; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI
+1F88; S; 1F80; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI
+1F89; F; 1F01 03B9; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI
+1F89; S; 1F81; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI
+1F8A; F; 1F02 03B9; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1F8A; S; 1F82; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1F8B; F; 1F03 03B9; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1F8B; S; 1F83; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1F8C; F; 1F04 03B9; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1F8C; S; 1F84; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1F8D; F; 1F05 03B9; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1F8D; S; 1F85; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1F8E; F; 1F06 03B9; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1F8E; S; 1F86; # GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1F8F; F; 1F07 03B9; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1F8F; S; 1F87; # GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1F90; F; 1F20 03B9; # GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI
+1F91; F; 1F21 03B9; # GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI
+1F92; F; 1F22 03B9; # GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+1F93; F; 1F23 03B9; # GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+1F94; F; 1F24 03B9; # GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+1F95; F; 1F25 03B9; # GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+1F96; F; 1F26 03B9; # GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+1F97; F; 1F27 03B9; # GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1F98; F; 1F20 03B9; # GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI
+1F98; S; 1F90; # GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI
+1F99; F; 1F21 03B9; # GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI
+1F99; S; 1F91; # GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI
+1F9A; F; 1F22 03B9; # GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1F9A; S; 1F92; # GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1F9B; F; 1F23 03B9; # GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1F9B; S; 1F93; # GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1F9C; F; 1F24 03B9; # GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1F9C; S; 1F94; # GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1F9D; F; 1F25 03B9; # GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1F9D; S; 1F95; # GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1F9E; F; 1F26 03B9; # GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1F9E; S; 1F96; # GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1F9F; F; 1F27 03B9; # GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1F9F; S; 1F97; # GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1FA0; F; 1F60 03B9; # GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI
+1FA1; F; 1F61 03B9; # GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI
+1FA2; F; 1F62 03B9; # GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI
+1FA3; F; 1F63 03B9; # GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI
+1FA4; F; 1F64 03B9; # GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI
+1FA5; F; 1F65 03B9; # GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI
+1FA6; F; 1F66 03B9; # GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI
+1FA7; F; 1F67 03B9; # GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI
+1FA8; F; 1F60 03B9; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI
+1FA8; S; 1FA0; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI
+1FA9; F; 1F61 03B9; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI
+1FA9; S; 1FA1; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI
+1FAA; F; 1F62 03B9; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1FAA; S; 1FA2; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+1FAB; F; 1F63 03B9; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1FAB; S; 1FA3; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+1FAC; F; 1F64 03B9; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1FAC; S; 1FA4; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+1FAD; F; 1F65 03B9; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1FAD; S; 1FA5; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+1FAE; F; 1F66 03B9; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1FAE; S; 1FA6; # GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+1FAF; F; 1F67 03B9; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1FAF; S; 1FA7; # GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+1FB2; F; 1F70 03B9; # GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI
+1FB3; F; 03B1 03B9; # GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI
+1FB4; F; 03AC 03B9; # GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI
+1FB6; F; 03B1 0342; # GREEK SMALL LETTER ALPHA WITH PERISPOMENI
+1FB7; F; 03B1 0342 03B9; # GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FB8; C; 1FB0; # GREEK CAPITAL LETTER ALPHA WITH VRACHY
+1FB9; C; 1FB1; # GREEK CAPITAL LETTER ALPHA WITH MACRON
+1FBA; C; 1F70; # GREEK CAPITAL LETTER ALPHA WITH VARIA
+1FBB; C; 1F71; # GREEK CAPITAL LETTER ALPHA WITH OXIA
+1FBC; F; 03B1 03B9; # GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FBC; S; 1FB3; # GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+1FBE; C; 03B9; # GREEK PROSGEGRAMMENI
+1FC2; F; 1F74 03B9; # GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI
+1FC3; F; 03B7 03B9; # GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI
+1FC4; F; 03AE 03B9; # GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI
+1FC6; F; 03B7 0342; # GREEK SMALL LETTER ETA WITH PERISPOMENI
+1FC7; F; 03B7 0342 03B9; # GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FC8; C; 1F72; # GREEK CAPITAL LETTER EPSILON WITH VARIA
+1FC9; C; 1F73; # GREEK CAPITAL LETTER EPSILON WITH OXIA
+1FCA; C; 1F74; # GREEK CAPITAL LETTER ETA WITH VARIA
+1FCB; C; 1F75; # GREEK CAPITAL LETTER ETA WITH OXIA
+1FCC; F; 03B7 03B9; # GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FCC; S; 1FC3; # GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+1FD2; F; 03B9 0308 0300; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA
+1FD3; F; 03B9 0308 0301; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+1FD6; F; 03B9 0342; # GREEK SMALL LETTER IOTA WITH PERISPOMENI
+1FD7; F; 03B9 0308 0342; # GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI
+1FD8; C; 1FD0; # GREEK CAPITAL LETTER IOTA WITH VRACHY
+1FD9; C; 1FD1; # GREEK CAPITAL LETTER IOTA WITH MACRON
+1FDA; C; 1F76; # GREEK CAPITAL LETTER IOTA WITH VARIA
+1FDB; C; 1F77; # GREEK CAPITAL LETTER IOTA WITH OXIA
+1FE2; F; 03C5 0308 0300; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA
+1FE3; F; 03C5 0308 0301; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
+1FE4; F; 03C1 0313; # GREEK SMALL LETTER RHO WITH PSILI
+1FE6; F; 03C5 0342; # GREEK SMALL LETTER UPSILON WITH PERISPOMENI
+1FE7; F; 03C5 0308 0342; # GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI
+1FE8; C; 1FE0; # GREEK CAPITAL LETTER UPSILON WITH VRACHY
+1FE9; C; 1FE1; # GREEK CAPITAL LETTER UPSILON WITH MACRON
+1FEA; C; 1F7A; # GREEK CAPITAL LETTER UPSILON WITH VARIA
+1FEB; C; 1F7B; # GREEK CAPITAL LETTER UPSILON WITH OXIA
+1FEC; C; 1FE5; # GREEK CAPITAL LETTER RHO WITH DASIA
+1FF2; F; 1F7C 03B9; # GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI
+1FF3; F; 03C9 03B9; # GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI
+1FF4; F; 03CE 03B9; # GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI
+1FF6; F; 03C9 0342; # GREEK SMALL LETTER OMEGA WITH PERISPOMENI
+1FF7; F; 03C9 0342 03B9; # GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI
+1FF8; C; 1F78; # GREEK CAPITAL LETTER OMICRON WITH VARIA
+1FF9; C; 1F79; # GREEK CAPITAL LETTER OMICRON WITH OXIA
+1FFA; C; 1F7C; # GREEK CAPITAL LETTER OMEGA WITH VARIA
+1FFB; C; 1F7D; # GREEK CAPITAL LETTER OMEGA WITH OXIA
+1FFC; F; 03C9 03B9; # GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+1FFC; S; 1FF3; # GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+2126; C; 03C9; # OHM SIGN
+212A; C; 006B; # KELVIN SIGN
+212B; C; 00E5; # ANGSTROM SIGN
+2160; C; 2170; # ROMAN NUMERAL ONE
+2161; C; 2171; # ROMAN NUMERAL TWO
+2162; C; 2172; # ROMAN NUMERAL THREE
+2163; C; 2173; # ROMAN NUMERAL FOUR
+2164; C; 2174; # ROMAN NUMERAL FIVE
+2165; C; 2175; # ROMAN NUMERAL SIX
+2166; C; 2176; # ROMAN NUMERAL SEVEN
+2167; C; 2177; # ROMAN NUMERAL EIGHT
+2168; C; 2178; # ROMAN NUMERAL NINE
+2169; C; 2179; # ROMAN NUMERAL TEN
+216A; C; 217A; # ROMAN NUMERAL ELEVEN
+216B; C; 217B; # ROMAN NUMERAL TWELVE
+216C; C; 217C; # ROMAN NUMERAL FIFTY
+216D; C; 217D; # ROMAN NUMERAL ONE HUNDRED
+216E; C; 217E; # ROMAN NUMERAL FIVE HUNDRED
+216F; C; 217F; # ROMAN NUMERAL ONE THOUSAND
+24B6; C; 24D0; # CIRCLED LATIN CAPITAL LETTER A
+24B7; C; 24D1; # CIRCLED LATIN CAPITAL LETTER B
+24B8; C; 24D2; # CIRCLED LATIN CAPITAL LETTER C
+24B9; C; 24D3; # CIRCLED LATIN CAPITAL LETTER D
+24BA; C; 24D4; # CIRCLED LATIN CAPITAL LETTER E
+24BB; C; 24D5; # CIRCLED LATIN CAPITAL LETTER F
+24BC; C; 24D6; # CIRCLED LATIN CAPITAL LETTER G
+24BD; C; 24D7; # CIRCLED LATIN CAPITAL LETTER H
+24BE; C; 24D8; # CIRCLED LATIN CAPITAL LETTER I
+24BF; C; 24D9; # CIRCLED LATIN CAPITAL LETTER J
+24C0; C; 24DA; # CIRCLED LATIN CAPITAL LETTER K
+24C1; C; 24DB; # CIRCLED LATIN CAPITAL LETTER L
+24C2; C; 24DC; # CIRCLED LATIN CAPITAL LETTER M
+24C3; C; 24DD; # CIRCLED LATIN CAPITAL LETTER N
+24C4; C; 24DE; # CIRCLED LATIN CAPITAL LETTER O
+24C5; C; 24DF; # CIRCLED LATIN CAPITAL LETTER P
+24C6; C; 24E0; # CIRCLED LATIN CAPITAL LETTER Q
+24C7; C; 24E1; # CIRCLED LATIN CAPITAL LETTER R
+24C8; C; 24E2; # CIRCLED LATIN CAPITAL LETTER S
+24C9; C; 24E3; # CIRCLED LATIN CAPITAL LETTER T
+24CA; C; 24E4; # CIRCLED LATIN CAPITAL LETTER U
+24CB; C; 24E5; # CIRCLED LATIN CAPITAL LETTER V
+24CC; C; 24E6; # CIRCLED LATIN CAPITAL LETTER W
+24CD; C; 24E7; # CIRCLED LATIN CAPITAL LETTER X
+24CE; C; 24E8; # CIRCLED LATIN CAPITAL LETTER Y
+24CF; C; 24E9; # CIRCLED LATIN CAPITAL LETTER Z
+2C00; C; 2C30; # GLAGOLITIC CAPITAL LETTER AZU
+2C01; C; 2C31; # GLAGOLITIC CAPITAL LETTER BUKY
+2C02; C; 2C32; # GLAGOLITIC CAPITAL LETTER VEDE
+2C03; C; 2C33; # GLAGOLITIC CAPITAL LETTER GLAGOLI
+2C04; C; 2C34; # GLAGOLITIC CAPITAL LETTER DOBRO
+2C05; C; 2C35; # GLAGOLITIC CAPITAL LETTER YESTU
+2C06; C; 2C36; # GLAGOLITIC CAPITAL LETTER ZHIVETE
+2C07; C; 2C37; # GLAGOLITIC CAPITAL LETTER DZELO
+2C08; C; 2C38; # GLAGOLITIC CAPITAL LETTER ZEMLJA
+2C09; C; 2C39; # GLAGOLITIC CAPITAL LETTER IZHE
+2C0A; C; 2C3A; # GLAGOLITIC CAPITAL LETTER INITIAL IZHE
+2C0B; C; 2C3B; # GLAGOLITIC CAPITAL LETTER I
+2C0C; C; 2C3C; # GLAGOLITIC CAPITAL LETTER DJERVI
+2C0D; C; 2C3D; # GLAGOLITIC CAPITAL LETTER KAKO
+2C0E; C; 2C3E; # GLAGOLITIC CAPITAL LETTER LJUDIJE
+2C0F; C; 2C3F; # GLAGOLITIC CAPITAL LETTER MYSLITE
+2C10; C; 2C40; # GLAGOLITIC CAPITAL LETTER NASHI
+2C11; C; 2C41; # GLAGOLITIC CAPITAL LETTER ONU
+2C12; C; 2C42; # GLAGOLITIC CAPITAL LETTER POKOJI
+2C13; C; 2C43; # GLAGOLITIC CAPITAL LETTER RITSI
+2C14; C; 2C44; # GLAGOLITIC CAPITAL LETTER SLOVO
+2C15; C; 2C45; # GLAGOLITIC CAPITAL LETTER TVRIDO
+2C16; C; 2C46; # GLAGOLITIC CAPITAL LETTER UKU
+2C17; C; 2C47; # GLAGOLITIC CAPITAL LETTER FRITU
+2C18; C; 2C48; # GLAGOLITIC CAPITAL LETTER HERU
+2C19; C; 2C49; # GLAGOLITIC CAPITAL LETTER OTU
+2C1A; C; 2C4A; # GLAGOLITIC CAPITAL LETTER PE
+2C1B; C; 2C4B; # GLAGOLITIC CAPITAL LETTER SHTA
+2C1C; C; 2C4C; # GLAGOLITIC CAPITAL LETTER TSI
+2C1D; C; 2C4D; # GLAGOLITIC CAPITAL LETTER CHRIVI
+2C1E; C; 2C4E; # GLAGOLITIC CAPITAL LETTER SHA
+2C1F; C; 2C4F; # GLAGOLITIC CAPITAL LETTER YERU
+2C20; C; 2C50; # GLAGOLITIC CAPITAL LETTER YERI
+2C21; C; 2C51; # GLAGOLITIC CAPITAL LETTER YATI
+2C22; C; 2C52; # GLAGOLITIC CAPITAL LETTER SPIDERY HA
+2C23; C; 2C53; # GLAGOLITIC CAPITAL LETTER YU
+2C24; C; 2C54; # GLAGOLITIC CAPITAL LETTER SMALL YUS
+2C25; C; 2C55; # GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL
+2C26; C; 2C56; # GLAGOLITIC CAPITAL LETTER YO
+2C27; C; 2C57; # GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS
+2C28; C; 2C58; # GLAGOLITIC CAPITAL LETTER BIG YUS
+2C29; C; 2C59; # GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS
+2C2A; C; 2C5A; # GLAGOLITIC CAPITAL LETTER FITA
+2C2B; C; 2C5B; # GLAGOLITIC CAPITAL LETTER IZHITSA
+2C2C; C; 2C5C; # GLAGOLITIC CAPITAL LETTER SHTAPIC
+2C2D; C; 2C5D; # GLAGOLITIC CAPITAL LETTER TROKUTASTI A
+2C2E; C; 2C5E; # GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE
+2C80; C; 2C81; # COPTIC CAPITAL LETTER ALFA
+2C82; C; 2C83; # COPTIC CAPITAL LETTER VIDA
+2C84; C; 2C85; # COPTIC CAPITAL LETTER GAMMA
+2C86; C; 2C87; # COPTIC CAPITAL LETTER DALDA
+2C88; C; 2C89; # COPTIC CAPITAL LETTER EIE
+2C8A; C; 2C8B; # COPTIC CAPITAL LETTER SOU
+2C8C; C; 2C8D; # COPTIC CAPITAL LETTER ZATA
+2C8E; C; 2C8F; # COPTIC CAPITAL LETTER HATE
+2C90; C; 2C91; # COPTIC CAPITAL LETTER THETHE
+2C92; C; 2C93; # COPTIC CAPITAL LETTER IAUDA
+2C94; C; 2C95; # COPTIC CAPITAL LETTER KAPA
+2C96; C; 2C97; # COPTIC CAPITAL LETTER LAULA
+2C98; C; 2C99; # COPTIC CAPITAL LETTER MI
+2C9A; C; 2C9B; # COPTIC CAPITAL LETTER NI
+2C9C; C; 2C9D; # COPTIC CAPITAL LETTER KSI
+2C9E; C; 2C9F; # COPTIC CAPITAL LETTER O
+2CA0; C; 2CA1; # COPTIC CAPITAL LETTER PI
+2CA2; C; 2CA3; # COPTIC CAPITAL LETTER RO
+2CA4; C; 2CA5; # COPTIC CAPITAL LETTER SIMA
+2CA6; C; 2CA7; # COPTIC CAPITAL LETTER TAU
+2CA8; C; 2CA9; # COPTIC CAPITAL LETTER UA
+2CAA; C; 2CAB; # COPTIC CAPITAL LETTER FI
+2CAC; C; 2CAD; # COPTIC CAPITAL LETTER KHI
+2CAE; C; 2CAF; # COPTIC CAPITAL LETTER PSI
+2CB0; C; 2CB1; # COPTIC CAPITAL LETTER OOU
+2CB2; C; 2CB3; # COPTIC CAPITAL LETTER DIALECT-P ALEF
+2CB4; C; 2CB5; # COPTIC CAPITAL LETTER OLD COPTIC AIN
+2CB6; C; 2CB7; # COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE
+2CB8; C; 2CB9; # COPTIC CAPITAL LETTER DIALECT-P KAPA
+2CBA; C; 2CBB; # COPTIC CAPITAL LETTER DIALECT-P NI
+2CBC; C; 2CBD; # COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI
+2CBE; C; 2CBF; # COPTIC CAPITAL LETTER OLD COPTIC OOU
+2CC0; C; 2CC1; # COPTIC CAPITAL LETTER SAMPI
+2CC2; C; 2CC3; # COPTIC CAPITAL LETTER CROSSED SHEI
+2CC4; C; 2CC5; # COPTIC CAPITAL LETTER OLD COPTIC SHEI
+2CC6; C; 2CC7; # COPTIC CAPITAL LETTER OLD COPTIC ESH
+2CC8; C; 2CC9; # COPTIC CAPITAL LETTER AKHMIMIC KHEI
+2CCA; C; 2CCB; # COPTIC CAPITAL LETTER DIALECT-P HORI
+2CCC; C; 2CCD; # COPTIC CAPITAL LETTER OLD COPTIC HORI
+2CCE; C; 2CCF; # COPTIC CAPITAL LETTER OLD COPTIC HA
+2CD0; C; 2CD1; # COPTIC CAPITAL LETTER L-SHAPED HA
+2CD2; C; 2CD3; # COPTIC CAPITAL LETTER OLD COPTIC HEI
+2CD4; C; 2CD5; # COPTIC CAPITAL LETTER OLD COPTIC HAT
+2CD6; C; 2CD7; # COPTIC CAPITAL LETTER OLD COPTIC GANGIA
+2CD8; C; 2CD9; # COPTIC CAPITAL LETTER OLD COPTIC DJA
+2CDA; C; 2CDB; # COPTIC CAPITAL LETTER OLD COPTIC SHIMA
+2CDC; C; 2CDD; # COPTIC CAPITAL LETTER OLD NUBIAN SHIMA
+2CDE; C; 2CDF; # COPTIC CAPITAL LETTER OLD NUBIAN NGI
+2CE0; C; 2CE1; # COPTIC CAPITAL LETTER OLD NUBIAN NYI
+2CE2; C; 2CE3; # COPTIC CAPITAL LETTER OLD NUBIAN WAU
+FB00; F; 0066 0066; # LATIN SMALL LIGATURE FF
+FB01; F; 0066 0069; # LATIN SMALL LIGATURE FI
+FB02; F; 0066 006C; # LATIN SMALL LIGATURE FL
+FB03; F; 0066 0066 0069; # LATIN SMALL LIGATURE FFI
+FB04; F; 0066 0066 006C; # LATIN SMALL LIGATURE FFL
+FB05; F; 0073 0074; # LATIN SMALL LIGATURE LONG S T
+FB06; F; 0073 0074; # LATIN SMALL LIGATURE ST
+FB13; F; 0574 0576; # ARMENIAN SMALL LIGATURE MEN NOW
+FB14; F; 0574 0565; # ARMENIAN SMALL LIGATURE MEN ECH
+FB15; F; 0574 056B; # ARMENIAN SMALL LIGATURE MEN INI
+FB16; F; 057E 0576; # ARMENIAN SMALL LIGATURE VEW NOW
+FB17; F; 0574 056D; # ARMENIAN SMALL LIGATURE MEN XEH
+FF21; C; FF41; # FULLWIDTH LATIN CAPITAL LETTER A
+FF22; C; FF42; # FULLWIDTH LATIN CAPITAL LETTER B
+FF23; C; FF43; # FULLWIDTH LATIN CAPITAL LETTER C
+FF24; C; FF44; # FULLWIDTH LATIN CAPITAL LETTER D
+FF25; C; FF45; # FULLWIDTH LATIN CAPITAL LETTER E
+FF26; C; FF46; # FULLWIDTH LATIN CAPITAL LETTER F
+FF27; C; FF47; # FULLWIDTH LATIN CAPITAL LETTER G
+FF28; C; FF48; # FULLWIDTH LATIN CAPITAL LETTER H
+FF29; C; FF49; # FULLWIDTH LATIN CAPITAL LETTER I
+FF2A; C; FF4A; # FULLWIDTH LATIN CAPITAL LETTER J
+FF2B; C; FF4B; # FULLWIDTH LATIN CAPITAL LETTER K
+FF2C; C; FF4C; # FULLWIDTH LATIN CAPITAL LETTER L
+FF2D; C; FF4D; # FULLWIDTH LATIN CAPITAL LETTER M
+FF2E; C; FF4E; # FULLWIDTH LATIN CAPITAL LETTER N
+FF2F; C; FF4F; # FULLWIDTH LATIN CAPITAL LETTER O
+FF30; C; FF50; # FULLWIDTH LATIN CAPITAL LETTER P
+FF31; C; FF51; # FULLWIDTH LATIN CAPITAL LETTER Q
+FF32; C; FF52; # FULLWIDTH LATIN CAPITAL LETTER R
+FF33; C; FF53; # FULLWIDTH LATIN CAPITAL LETTER S
+FF34; C; FF54; # FULLWIDTH LATIN CAPITAL LETTER T
+FF35; C; FF55; # FULLWIDTH LATIN CAPITAL LETTER U
+FF36; C; FF56; # FULLWIDTH LATIN CAPITAL LETTER V
+FF37; C; FF57; # FULLWIDTH LATIN CAPITAL LETTER W
+FF38; C; FF58; # FULLWIDTH LATIN CAPITAL LETTER X
+FF39; C; FF59; # FULLWIDTH LATIN CAPITAL LETTER Y
+FF3A; C; FF5A; # FULLWIDTH LATIN CAPITAL LETTER Z
+10400; C; 10428; # DESERET CAPITAL LETTER LONG I
+10401; C; 10429; # DESERET CAPITAL LETTER LONG E
+10402; C; 1042A; # DESERET CAPITAL LETTER LONG A
+10403; C; 1042B; # DESERET CAPITAL LETTER LONG AH
+10404; C; 1042C; # DESERET CAPITAL LETTER LONG O
+10405; C; 1042D; # DESERET CAPITAL LETTER LONG OO
+10406; C; 1042E; # DESERET CAPITAL LETTER SHORT I
+10407; C; 1042F; # DESERET CAPITAL LETTER SHORT E
+10408; C; 10430; # DESERET CAPITAL LETTER SHORT A
+10409; C; 10431; # DESERET CAPITAL LETTER SHORT AH
+1040A; C; 10432; # DESERET CAPITAL LETTER SHORT O
+1040B; C; 10433; # DESERET CAPITAL LETTER SHORT OO
+1040C; C; 10434; # DESERET CAPITAL LETTER AY
+1040D; C; 10435; # DESERET CAPITAL LETTER OW
+1040E; C; 10436; # DESERET CAPITAL LETTER WU
+1040F; C; 10437; # DESERET CAPITAL LETTER YEE
+10410; C; 10438; # DESERET CAPITAL LETTER H
+10411; C; 10439; # DESERET CAPITAL LETTER PEE
+10412; C; 1043A; # DESERET CAPITAL LETTER BEE
+10413; C; 1043B; # DESERET CAPITAL LETTER TEE
+10414; C; 1043C; # DESERET CAPITAL LETTER DEE
+10415; C; 1043D; # DESERET CAPITAL LETTER CHEE
+10416; C; 1043E; # DESERET CAPITAL LETTER JEE
+10417; C; 1043F; # DESERET CAPITAL LETTER KAY
+10418; C; 10440; # DESERET CAPITAL LETTER GAY
+10419; C; 10441; # DESERET CAPITAL LETTER EF
+1041A; C; 10442; # DESERET CAPITAL LETTER VEE
+1041B; C; 10443; # DESERET CAPITAL LETTER ETH
+1041C; C; 10444; # DESERET CAPITAL LETTER THEE
+1041D; C; 10445; # DESERET CAPITAL LETTER ES
+1041E; C; 10446; # DESERET CAPITAL LETTER ZEE
+1041F; C; 10447; # DESERET CAPITAL LETTER ESH
+10420; C; 10448; # DESERET CAPITAL LETTER ZHEE
+10421; C; 10449; # DESERET CAPITAL LETTER ER
+10422; C; 1044A; # DESERET CAPITAL LETTER EL
+10423; C; 1044B; # DESERET CAPITAL LETTER EM
+10424; C; 1044C; # DESERET CAPITAL LETTER EN
+10425; C; 1044D; # DESERET CAPITAL LETTER ENG
+10426; C; 1044E; # DESERET CAPITAL LETTER OI
+10427; C; 1044F; # DESERET CAPITAL LETTER EW
--- /dev/null
+/** \file globbing.c */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "physfs.h"
+#include "globbing.h"
+
+/**
+ * Please see globbing.h for details.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes. This code has
+ * NO WARRANTY.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
+ * Please see LICENSE.txt in the root of the source tree.
+ *
+ * \author Ryan C. Gordon.
+ */
+
+
+static int matchesPattern(const char *fname, const char *wildcard,
+ int caseSensitive)
+{
+ char x, y;
+ const char *fnameptr = fname;
+ const char *wildptr = wildcard;
+
+ while ((*wildptr) && (*fnameptr))
+ {
+ y = *wildptr;
+ if (y == '*')
+ {
+ do
+ {
+ wildptr++; /* skip multiple '*' in a row... */
+ } while (*wildptr == '*');
+
+ y = (caseSensitive) ? *wildptr : (char) tolower(*wildptr);
+
+ while (1)
+ {
+ x = (caseSensitive) ? *fnameptr : (char) tolower(*fnameptr);
+ if ((!x) || (x == y))
+ break;
+ else
+ fnameptr++;
+ } /* while */
+ } /* if */
+
+ else if (y == '?')
+ {
+ wildptr++;
+ fnameptr++;
+ } /* else if */
+
+ else
+ {
+ if (caseSensitive)
+ x = *fnameptr;
+ else
+ {
+ x = tolower(*fnameptr);
+ y = tolower(y);
+ } /* if */
+
+ wildptr++;
+ fnameptr++;
+
+ if (x != y)
+ return(0);
+ } /* else */
+ } /* while */
+
+ while (*wildptr == '*')
+ wildptr++;
+
+ return(*fnameptr == *wildptr);
+} /* matchesPattern */
+
+
+char **PHYSFSEXT_enumerateFilesWildcard(const char *dir, const char *wildcard,
+ int caseSensitive)
+{
+ char **rc = PHYSFS_enumerateFiles(dir);
+ char **i = rc;
+ char **j;
+
+ while (*i != NULL)
+ {
+ if (matchesPattern(*i, wildcard, caseSensitive))
+ i++;
+ else
+ {
+ /* FIXME: This counts on physfs's allocation method not changing! */
+ free(*i);
+ for (j = i; *j != NULL; j++)
+ j[0] = j[1];
+ } /* else */
+ } /* for */
+
+ return(rc);
+} /* PHYSFSEXT_enumerateFilesWildcard */
+
+
+#ifdef TEST_PHYSFSEXT_ENUMERATEFILESWILDCARD
+int main(int argc, char **argv)
+{
+ int rc;
+ char **flist;
+ char **i;
+
+ if (argc != 3)
+ {
+ printf("USAGE: %s <pattern> <caseSen>\n"
+ " where <caseSen> is 1 or 0.\n", argv[0]);
+ return(1);
+ } /* if */
+
+ if (!PHYSFS_init(argv[0]))
+ {
+ fprintf(stderr, "PHYSFS_init(): %s\n", PHYSFS_getLastError());
+ return(1);
+ } /* if */
+
+ if (!PHYSFS_addToSearchPath(".", 1))
+ {
+ fprintf(stderr, "PHYSFS_addToSearchPath(): %s\n", PHYSFS_getLastError());
+ PHYSFS_deinit();
+ return(1);
+ } /* if */
+
+ flist = PHYSFSEXT_enumerateFilesWildcard("/", argv[1], atoi(argv[2]));
+ rc = 0;
+ for (i = flist; *i; i++)
+ {
+ printf("%s\n", *i);
+ rc++;
+ } /* for */
+ printf("\n total %d files.\n\n", rc);
+
+ PHYSFS_freeList(flist);
+ PHYSFS_deinit();
+
+ return(0);
+} /* main */
+#endif
+
+/* end of globbing.c ... */
+
--- /dev/null
+/** \file globbing.h */
+
+/**
+ * \mainpage PhysicsFS globbing
+ *
+ * This is an extension to PhysicsFS to let you search for files with basic
+ * wildcard matching, regardless of what sort of filesystem or archive they
+ * reside in. It does this by enumerating directories as needed and manually
+ * locating matching entries.
+ *
+ * Usage: Set up PhysicsFS as you normally would, then use
+ * PHYSFSEXT_enumerateFilesPattern() when enumerating files. This is just
+ * like PHYSFS_enumerateFiles(), but it returns a subset that matches your
+ * wildcard pattern. You must call PHYSFS_freeList() on the results, just
+ * like you would with PHYSFS_enumerateFiles().
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes. This code has
+ * NO WARRANTY.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
+ * Please see LICENSE.txt in the root of the source tree.
+ *
+ * \author Ryan C. Gordon.
+ */
+
+
+/**
+ * \fn char **PHYSFS_enumerateFilesWildcard(const char *dir, const char *wildcard, int caseSensitive)
+ * \brief Get a file listing of a search path's directory.
+ *
+ * Matching directories are interpolated. That is, if "C:\mydir" is in the
+ * search path and contains a directory "savegames" that contains "x.sav",
+ * "y.Sav", and "z.txt", and there is also a "C:\userdir" in the search path
+ * that has a "savegames" subdirectory with "w.sav", then the following code:
+ *
+ * \code
+ * char **rc = PHYSFS_enumerateFilesWildcard("savegames", "*.sav", 0);
+ * char **i;
+ *
+ * for (i = rc; *i != NULL; i++)
+ * printf(" * We've got [%s].\n", *i);
+ *
+ * PHYSFS_freeList(rc);
+ * \endcode
+ *
+ * ...will print:
+ *
+ * \verbatim
+ * We've got [x.sav].
+ * We've got [y.Sav].
+ * We've got [w.sav].\endverbatim
+ *
+ * Feel free to sort the list however you like. We only promise there will
+ * be no duplicates, but not what order the final list will come back in.
+ *
+ * Wildcard strings can use the '*' and '?' characters, currently.
+ * Matches can be case-insensitive if you pass a zero for argument 3.
+ *
+ * Don't forget to call PHYSFS_freeList() with the return value from this
+ * function when you are done with it.
+ *
+ * \param dir directory in platform-independent notation to enumerate.
+ * \return Null-terminated array of null-terminated strings.
+ */
+__EXPORT__ char **PHYSFSEXT_enumerateFilesWildcard(const char *dir,
+ const char *wildcard,
+ int caseSensitive);
+
+/* end of globbing.h ... */
+
--- /dev/null
+/** \file ignorecase.c */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "physfs.h"
+#include "ignorecase.h"
+
+/**
+ * Please see ignorecase.h for details.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes. This code has
+ * NO WARRANTY.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
+ * Please see LICENSE.txt in the root of the source tree.
+ *
+ * \author Ryan C. Gordon.
+ */
+
+/* I'm not screwing around with stricmp vs. strcasecmp... */
+/* !!! FIXME: this will NOT work with UTF-8 strings in physfs2.0 */
+static int caseInsensitiveStringCompare(const char *x, const char *y)
+{
+ int ux, uy;
+ do
+ {
+ ux = toupper((int) *x);
+ uy = toupper((int) *y);
+ if (ux != uy)
+ return((ux > uy) ? 1 : -1);
+ x++;
+ y++;
+ } while ((ux) && (uy));
+
+ return(0);
+} /* caseInsensitiveStringCompare */
+
+
+static int locateOneElement(char *buf)
+{
+ char *ptr;
+ char **rc;
+ char **i;
+
+ if (PHYSFS_exists(buf))
+ return(1); /* quick rejection: exists in current case. */
+
+ ptr = strrchr(buf, '/'); /* find entry at end of path. */
+ if (ptr == NULL)
+ {
+ rc = PHYSFS_enumerateFiles("/");
+ ptr = buf;
+ } /* if */
+ else
+ {
+ *ptr = '\0';
+ rc = PHYSFS_enumerateFiles(buf);
+ *ptr = '/';
+ ptr++; /* point past dirsep to entry itself. */
+ } /* else */
+
+ for (i = rc; *i != NULL; i++)
+ {
+ if (caseInsensitiveStringCompare(*i, ptr) == 0)
+ {
+ strcpy(ptr, *i); /* found a match. Overwrite with this case. */
+ PHYSFS_freeList(rc);
+ return(1);
+ } /* if */
+ } /* for */
+
+ /* no match at all... */
+ PHYSFS_freeList(rc);
+ return(0);
+} /* locateOneElement */
+
+
+int PHYSFSEXT_locateCorrectCase(char *buf)
+{
+ int rc;
+ char *ptr;
+ char *prevptr;
+
+ while (*buf == '/') /* skip any '/' at start of string... */
+ buf++;
+
+ ptr = prevptr = buf;
+ if (*ptr == '\0')
+ return(0); /* Uh...I guess that's success. */
+
+ while (ptr = strchr(ptr + 1, '/'))
+ {
+ *ptr = '\0'; /* block this path section off */
+ rc = locateOneElement(buf);
+ *ptr = '/'; /* restore path separator */
+ if (!rc)
+ return(-2); /* missing element in path. */
+ } /* while */
+
+ /* check final element... */
+ return(locateOneElement(buf) ? 0 : -1);
+} /* PHYSFSEXT_locateCorrectCase */
+
+
+#ifdef TEST_PHYSFSEXT_LOCATECORRECTCASE
+int main(int argc, char **argv)
+{
+ int rc;
+ char buf[128];
+ PHYSFS_File *f;
+
+ if (!PHYSFS_init(argv[0]))
+ {
+ fprintf(stderr, "PHYSFS_init(): %s\n", PHYSFS_getLastError());
+ return(1);
+ } /* if */
+
+ if (!PHYSFS_addToSearchPath(".", 1))
+ {
+ fprintf(stderr, "PHYSFS_addToSearchPath(): %s\n", PHYSFS_getLastError());
+ PHYSFS_deinit();
+ return(1);
+ } /* if */
+
+ if (!PHYSFS_setWriteDir("."))
+ {
+ fprintf(stderr, "PHYSFS_setWriteDir(): %s\n", PHYSFS_getLastError());
+ PHYSFS_deinit();
+ return(1);
+ } /* if */
+
+ if (!PHYSFS_mkdir("/a/b/c"))
+ {
+ fprintf(stderr, "PHYSFS_mkdir(): %s\n", PHYSFS_getLastError());
+ PHYSFS_deinit();
+ return(1);
+ } /* if */
+
+ if (!PHYSFS_mkdir("/a/b/C"))
+ {
+ fprintf(stderr, "PHYSFS_mkdir(): %s\n", PHYSFS_getLastError());
+ PHYSFS_deinit();
+ return(1);
+ } /* if */
+
+ f = PHYSFS_openWrite("/a/b/c/x.txt");
+ PHYSFS_close(f);
+ if (f == NULL)
+ {
+ fprintf(stderr, "PHYSFS_openWrite(): %s\n", PHYSFS_getLastError());
+ PHYSFS_deinit();
+ return(1);
+ } /* if */
+
+ f = PHYSFS_openWrite("/a/b/C/X.txt");
+ PHYSFS_close(f);
+ if (f == NULL)
+ {
+ fprintf(stderr, "PHYSFS_openWrite(): %s\n", PHYSFS_getLastError());
+ PHYSFS_deinit();
+ return(1);
+ } /* if */
+
+ strcpy(buf, "/a/b/c/x.txt");
+ rc = PHYSFSEXT_locateCorrectCase(buf);
+ if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0))
+ printf("test 1 failed\n");
+
+ strcpy(buf, "/a/B/c/x.txt");
+ rc = PHYSFSEXT_locateCorrectCase(buf);
+ if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0))
+ printf("test 2 failed\n");
+
+ strcpy(buf, "/a/b/C/x.txt");
+ rc = PHYSFSEXT_locateCorrectCase(buf);
+ if ((rc != 0) || (strcmp(buf, "/a/b/C/X.txt") != 0))
+ printf("test 3 failed\n");
+
+ strcpy(buf, "/a/b/c/X.txt");
+ rc = PHYSFSEXT_locateCorrectCase(buf);
+ if ((rc != 0) || (strcmp(buf, "/a/b/c/x.txt") != 0))
+ printf("test 4 failed\n");
+
+ strcpy(buf, "/a/b/c/z.txt");
+ rc = PHYSFSEXT_locateCorrectCase(buf);
+ if ((rc != -1) || (strcmp(buf, "/a/b/c/z.txt") != 0))
+ printf("test 5 failed\n");
+
+ strcpy(buf, "/A/B/Z/z.txt");
+ rc = PHYSFSEXT_locateCorrectCase(buf);
+ if ((rc != -2) || (strcmp(buf, "/a/b/Z/z.txt") != 0))
+ printf("test 6 failed\n");
+
+ printf("Testing completed.\n");
+ printf(" If no errors were reported, you're good to go.\n");
+
+ PHYSFS_delete("/a/b/c/x.txt");
+ PHYSFS_delete("/a/b/C/X.txt");
+ PHYSFS_delete("/a/b/c");
+ PHYSFS_delete("/a/b/C");
+ PHYSFS_delete("/a/b");
+ PHYSFS_delete("/a");
+ PHYSFS_deinit();
+ return(0);
+} /* main */
+#endif
+
+/* end of ignorecase.c ... */
+
--- /dev/null
+/** \file ignorecase.h */
+
+/**
+ * \mainpage PhysicsFS ignorecase
+ *
+ * This is an extension to PhysicsFS to let you handle files in a
+ * case-insensitive manner, regardless of what sort of filesystem or
+ * archive they reside in. It does this by enumerating directories as
+ * needed and manually locating matching entries.
+ *
+ * Please note that this brings with it some caveats:
+ * - On filesystems that are case-insensitive to start with, such as those
+ * used on Windows or MacOS, you are adding extra overhead.
+ * - On filesystems that are case-sensitive, you might select the wrong dir
+ * or file (which brings security considerations and potential bugs). This
+ * code favours exact case matches, but you will lose access to otherwise
+ * duplicate filenames, or you might go down a wrong directory tree, etc.
+ * In practive, this is rarely a problem, but you need to be aware of it.
+ * - This doesn't do _anything_ with the write directory; you're on your
+ * own for opening the right files for writing. You can sort of get around
+ * this by adding your write directory to the search path, but then the
+ * interpolated directory tree can screw you up even more.
+ *
+ * This code should be considered an aid for legacy code. New development
+ * shouldn't do dumbass things that require this aid in the first place. :)
+ *
+ * Usage: Set up PhysicsFS as you normally would, then use
+ * PHYSFSEXT_locateCorrectCase() to get a "correct" pathname to pass to
+ * functions like PHYSFS_openRead(), etc.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes. This code has
+ * NO WARRANTY.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
+ * Please see LICENSE.txt in the root of the source tree.
+ *
+ * \author Ryan C. Gordon.
+ */
+
+
+/**
+ * \fn int PHYSFSEXT_locateCorrectCase(char *buf)
+ * \brief Find an existing filename with matching case.
+ *
+ * This function will look for a path/filename that matches the passed in
+ * buffer. Each element of the buffer's path is checked for a
+ * case-insensitive match. The buffer must specify a null-terminated string
+ * in platform-independent notation.
+ *
+ * Please note results may be skewed differently depending on whether symlinks
+ * are enabled or not.
+ *
+ * Each element of the buffer is overwritten with the actual case of an
+ * existing match. If there is no match, the search aborts and reports an
+ * error. Exact matches are favored over case-insensitive matches.
+ *
+ * THIS IS RISKY. Please do not use this function for anything but crappy
+ * legacy code.
+ *
+ * \param buf Buffer with null-terminated string of path/file to locate.
+ * This buffer will be modified by this function.
+ * \return zero if match was found, -1 if the final element (the file itself)
+ * is missing, -2 if one of the parent directories is missing.
+ */
+int PHYSFSEXT_locateCorrectCase(char *buf);
+
+/* end of ignorecase.h ... */
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use warnings;
+use strict;
+
+print <<__EOF__;
+/*
+ * This file is part of PhysicsFS (http://icculus.org/physfs/)
+ *
+ * This data generated by physfs/extras/makecasefoldhashtable.pl ...
+ * Do not manually edit this file!
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ */
+
+#ifndef __PHYSICSFS_INTERNAL__
+#error Do not include this header from your applications.
+#endif
+
+__EOF__
+
+
+my @foldPairs;
+
+for (my $i = 0; $i < 256; $i++) {
+ $foldPairs[$i] = '';
+}
+
+open(FH,'<','casefolding.txt') or die("failed to open casefolding.txt: $!\n");
+while (<FH>) {
+ chomp;
+ # strip comments from textfile...
+ s/\#.*\Z//;
+
+ # strip whitespace...
+ s/\A\s+//;
+ s/\s+\Z//;
+
+ next if not /\A([a-fA-F0-9]+)\;\s*(.)\;\s*(.+)\;/;
+ my ($code, $status, $mapping) = ($1, $2, $3);
+ my $hexxed = hex($code);
+ my $hashed = (($hexxed ^ ($hexxed >> 8)) & 0xFF);
+ #print("// code '$code' status '$status' mapping '$mapping'\n");
+ #print("// hexxed '$hexxed' hashed '$hashed'\n");
+
+ if (($status eq 'C') or ($status eq 'F')) {
+ my ($map1, $map2, $map3) = ('0000', '0000', '0000');
+ $map1 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//;
+ $map2 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//;
+ $map3 = $1 if $mapping =~ s/\A([a-fA-F0-9]+)(\s*|\Z)//;
+ die("mapping space too small for '$code'\n") if ($mapping ne '');
+ $foldPairs[$hashed] .= " { 0x$code, 0x$map1, 0x$map2, 0x$map3 },\n";
+ }
+}
+close(FH);
+
+for (my $i = 0; $i < 256; $i++) {
+ $foldPairs[$i] =~ s/,\n\Z//;
+ my $str = $foldPairs[$i];
+ next if $str eq '';
+ my $num = '000' . $i;
+ $num =~ s/\A.*?(\d\d\d)\Z/$1/;
+ my $sym = "case_fold_${num}";
+ print("static const CaseFoldMapping ${sym}[] = {\n$str\n};\n\n");
+}
+
+print("\nstatic const CaseFoldHashBucket case_fold_hash[256] = {\n");
+
+for (my $i = 0; $i < 256; $i++) {
+ my $str = $foldPairs[$i];
+ if ($str eq '') {
+ print(" { 0, NULL },\n");
+ } else {
+ my $num = '000' . $i;
+ $num =~ s/\A.*?(\d\d\d)\Z/$1/;
+ my $sym = "case_fold_${num}";
+ print(" { __PHYSFS_ARRAYLEN($sym), $sym },\n");
+ }
+}
+print("};\n\n");
+
+exit 0;
+
+# end of makecashfoldhashtable.pl ...
+
--- /dev/null
+#!/bin/sh
+
+# This shell script is roughly equivalent to what "make dist" did in the
+# autotools build system and is called from a custom CMake target.
+
+# !!! FIXME: This code sort of sucks. Consider using CPack instead...
+
+if [ ! -f ./CMakeLists.txt ]; then
+ echo "you are in the wrong place."
+ exit 1
+fi
+
+if [ -z "$1" ]; then
+ echo "Wrong arguments."
+ exit 2
+fi
+
+set -e
+
+VERSION="$1"
+BASENAME="physfs-$VERSION"
+TARBALL="$BASENAME.tar.gz"
+TMPCPDIR="../9sdkujy75jv932-physfstmp-$VERSION"
+CPDIR="$TMPCPDIR/$BASENAME"
+
+echo "Packing PhysicsFS $VERSION source tarball..."
+echo " + Setting up scratch dir..."
+rm -rf $TMPCPDIR
+mkdir $TMPCPDIR
+mkdir $CPDIR
+
+echo " + Making copy of source tree in scratch dir..."
+cp -R . $CPDIR/
+echo " + Deleting cruft..."
+pushd $CPDIR >/dev/null
+rm -rf `svn propget svn:ignore .`
+rm -rf `svn status |grep '?' |sed -s 's/\?//'`
+popd >/dev/null
+rm -rf `find $CPDIR -type d -name '.svn'`
+echo " + Deleting Subversion metadata..."
+rm -rf `find $CPDIR -type d -name '.svn'`
+echo " + Fixing up permissions..."
+chmod -R a+rw $CPDIR
+chmod a+x `find $CPDIR -type d`
+echo " + Building final tarball..."
+rm -f $TARBALL
+tar -czf $TARBALL -C $TMPCPDIR $BASENAME
+echo " + Cleaning up..."
+rm -rf $TMPCPDIR
+echo " + All done! Packed to '$TARBALL' ..."
+set +e
+
+exit 0
+
--- /dev/null
+# $Id: installer.rb 585 2003-07-21 03:46:50Z icculus $
+
+require 'rbconfig'
+require 'find'
+require 'ftools'
+
+include Config
+
+module Slimb
+ class Installer
+ def initialize target_dir = "", &user_skip
+ @user_skip = user_skip or proc {|f| false}
+
+ @version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
+ @libdir = File.join(CONFIG["libdir"], "ruby", @version)
+ @sitedir = CONFIG["sitedir"] || File.join(@libdir, "site_ruby")
+ @dest = File.join @sitedir, target_dir
+
+ File::makedirs @dest
+ File::chmod 0755, @dest, true
+ end
+
+ def skip? file
+ @user_skip[file] or
+ file[0] == ?. or file[-1] == ?~ or file[-1] == ?#
+ end
+
+ def install_dir dir
+ File::makedirs(File.join(@dest, dir))
+ File::chmod(0755, File.join(@dest, dir), true)
+ Dir.foreach(dir) {|file|
+ next if skip? file
+
+ if File.ftype(File.join(dir, file)) == "directory"
+ install_dir File.join(dir, file)
+ else
+ install_file File.join(dir, file)
+ end
+ }
+ end
+
+ def install_file file
+ if file =~ /\.so$/
+ install_so file
+ else
+ File::install file, File.join(@dest, file), 0644, true
+ end
+ end
+
+ def install_so file
+ File::install file, File.join(CONFIG["sitearchdir"], file), 0644, true
+ end
+
+ def uninstall_so file
+ file = File.join(CONFIG["sitearchdir"], file)
+ File::safe_unlink file
+ end
+
+ def install something
+ case something
+ when Array
+ something.each {|x|
+ install x if x.is_a? String
+ }
+ when String
+ if File.ftype(something) == "directory"
+ install_dir something
+ else
+ install_file something
+ end
+ end
+ end
+
+ def uninstall what = "*"
+ case what
+ when Array
+ files = what.map {|x| File.join(@dest, x)}
+ when String
+ files = Dir[File.join(@dest, what)]
+ end
+
+ files.each {|x|
+ # FIXME: recursive uninstall is a must
+ next if FileTest.directory? x
+ File::safe_unlink x
+ }
+ end
+
+ def run files, argv
+ if !argv.grep(/--uninstall/).empty?
+ uninstall files
+ else
+ install files
+ end
+ end
+ end
+end
+
+# self-installation
+if $0 == __FILE__
+ $stderr.puts "Installing slimb installer..."
+ Slimb::Installer.new("slimb").install File.basename(__FILE__)
+end
--- /dev/null
+require 'mkmf'
+
+$CFLAGS += `sdl-config --cflags`.chomp
+$LDFLAGS += `sdl-config --libs`.chomp
+
+have_library "physfs", "PHYSFS_init"
+have_library "SDL", "SDL_AllocRW"
+
+create_makefile "physfs_so"
--- /dev/null
+#!/usr/local/bin/ruby
+
+if __FILE__ == $0
+ require 'slimb/installer'
+ files = ["physfs.rb", "physfs_so.so"]
+ installer = Slimb::Installer.new.run files, ARGV
+end
--- /dev/null
+#!/bin/sh
+ruby extconf.rb
+make
+cd ..
+ruby installer.rb
+cd physfs
+ruby install.rb
+cd test
+ruby test_physfs.rb
\ No newline at end of file
--- /dev/null
+#
+# PhysicsFS - ruby interface
+#
+# Author: Ed Sinjiashvili (slimb@vlinkmail.com)
+# License: LGPL
+#
+
+require 'physfs_so'
+
+module PhysicsFS
+
+ class Version
+ def initialize major, minor, patch
+ @major = major
+ @minor = minor
+ @patch = patch
+ end
+
+ attr_reader :major, :minor, :patch
+
+ def to_s
+ "#@major.#@minor.#@patch"
+ end
+ end
+
+ class ArchiveInfo
+ def initialize ext, desc, author, url
+ @extension = ext
+ @description = desc
+ @author = author
+ @url = url
+ end
+
+ attr_reader :extension, :description
+ attr_reader :author, :url
+
+ def to_s
+ " * #@extension: #@description\n Written by #@author.\n #@url\n"
+ end
+ end
+
+ #
+ # convenience methods
+ #
+ class << self
+
+ def init argv0 = $0
+ init_internal argv0
+ end
+
+ def append_search_path str
+ add_to_search_path str, 1
+ self
+ end
+
+ def prepend_search_path str
+ add_to_search_path str, 0
+ self
+ end
+
+ alias_method :<<, :append_search_path
+ alias_method :push, :append_search_path
+ alias_method :unshift, :prepend_search_path
+
+ def ls path = ""
+ enumerate path
+ end
+ end
+
+ #
+ # File - PhysicsFS abstract file - can be drawn from various sources
+ #
+ class File
+ def write_str str
+ write str, 1, str.length
+ end
+
+ def cat
+ prev_pos = tell
+ seek 0
+ r = read length, 1
+ seek prev_pos
+ r
+ end
+
+ alias_method :size, :length
+ end
+
+ #
+ # RWops - general stdio like operations on file-like creatures
+ #
+ class RWops
+ SEEK_SET = 0
+ SEEK_CUR = 1
+ SEEK_END = 2
+
+ # tell current position of RWopted entity
+ def tell
+ seek 0, SEEK_CUR
+ end
+
+ # length of RWops abstracted entity
+ def length
+ cur = tell
+ r = seek 0, SEEK_END
+ seek cur, SEEK_SET
+ r
+ end
+
+ alias_method :size, :length
+
+ #
+ # create rwops from PhysicsFS file object
+ #
+ def self.from_physfs file
+ file.to_rwops
+ end
+ end
+end
+
+# physfs.rb ends here #
--- /dev/null
+/*
+ * This code provides a glue layer between PhysicsFS and Simple Directmedia
+ * Layer's (SDL) RWops i/o abstraction.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the GNU Lesser
+ * General Public License: http://www.gnu.org/licenses/lgpl.txt
+ *
+ * SDL falls under the LGPL, too. You can get SDL at http://www.libsdl.org/
+ *
+ * This file was written by Ryan C. Gordon. (icculus@icculus.org).
+ */
+
+#include <stdio.h> /* used for SEEK_SET, SEEK_CUR, SEEK_END ... */
+#include "physfsrwops.h"
+
+static int physfsrwops_seek(SDL_RWops *rw, int offset, int whence)
+{
+ PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
+ int pos = 0;
+
+ if (whence == SEEK_SET)
+ {
+ pos = offset;
+ } /* if */
+
+ else if (whence == SEEK_CUR)
+ {
+ PHYSFS_sint64 current = PHYSFS_tell(handle);
+ if (current == -1)
+ {
+ SDL_SetError("Can't find position in file: %s",
+ PHYSFS_getLastError());
+ return(-1);
+ } /* if */
+
+ pos = (int) current;
+ if ( ((PHYSFS_sint64) pos) != current )
+ {
+ SDL_SetError("Can't fit current file position in an int!");
+ return(-1);
+ } /* if */
+
+ if (offset == 0) /* this is a "tell" call. We're done. */
+ return(pos);
+
+ pos += offset;
+ } /* else if */
+
+ else if (whence == SEEK_END)
+ {
+ PHYSFS_sint64 len = PHYSFS_fileLength(handle);
+ if (len == -1)
+ {
+ SDL_SetError("Can't find end of file: %s", PHYSFS_getLastError());
+ return(-1);
+ } /* if */
+
+ pos = (int) len;
+ if ( ((PHYSFS_sint64) pos) != len )
+ {
+ SDL_SetError("Can't fit end-of-file position in an int!");
+ return(-1);
+ } /* if */
+
+ pos += offset;
+ } /* else if */
+
+ else
+ {
+ SDL_SetError("Invalid 'whence' parameter.");
+ return(-1);
+ } /* else */
+
+ if ( pos < 0 )
+ {
+ SDL_SetError("Attempt to seek past start of file.");
+ return(-1);
+ } /* if */
+
+ if (!PHYSFS_seek(handle, (PHYSFS_uint64) pos))
+ {
+ SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+ return(-1);
+ } /* if */
+
+ return(pos);
+} /* physfsrwops_seek */
+
+
+static int physfsrwops_read(SDL_RWops *rw, void *ptr, int size, int maxnum)
+{
+ PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
+ PHYSFS_sint64 rc = PHYSFS_read(handle, ptr, size, maxnum);
+ if (rc != maxnum)
+ {
+ if (!PHYSFS_eof(handle)) /* not EOF? Must be an error. */
+ SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+ } /* if */
+
+ return((int) rc);
+} /* physfsrwops_read */
+
+
+static int physfsrwops_write(SDL_RWops *rw, const void *ptr, int size, int num)
+{
+ PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
+ PHYSFS_sint64 rc = PHYSFS_write(handle, ptr, size, num);
+ if (rc != num)
+ SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+
+ return((int) rc);
+} /* physfsrwops_write */
+
+
+static int physfsrwops_close(SDL_RWops *rw)
+{
+ PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
+ if (!PHYSFS_close(handle))
+ {
+ SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+ return(-1);
+ } /* if */
+
+ SDL_FreeRW(rw);
+ return(0);
+} /* physfsrwops_close */
+
+
+static SDL_RWops *create_rwops(PHYSFS_File *handle)
+{
+ SDL_RWops *retval = NULL;
+
+ if (handle == NULL)
+ SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+ else
+ {
+ retval = SDL_AllocRW();
+ if (retval != NULL)
+ {
+ retval->seek = physfsrwops_seek;
+ retval->read = physfsrwops_read;
+ retval->write = physfsrwops_write;
+ retval->close = physfsrwops_close;
+ retval->hidden.unknown.data1 = handle;
+ } /* if */
+ } /* else */
+
+ return(retval);
+} /* create_rwops */
+
+
+SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_File *handle)
+{
+ SDL_RWops *retval = NULL;
+ if (handle == NULL)
+ SDL_SetError("NULL pointer passed to PHYSFSRWOPS_makeRWops().");
+ else
+ retval = create_rwops(handle);
+
+ return(retval);
+} /* PHYSFSRWOPS_makeRWops */
+
+
+SDL_RWops *PHYSFSRWOPS_openRead(const char *fname)
+{
+ return(create_rwops(PHYSFS_openRead(fname)));
+} /* PHYSFSRWOPS_openRead */
+
+
+SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname)
+{
+ return(create_rwops(PHYSFS_openWrite(fname)));
+} /* PHYSFSRWOPS_openWrite */
+
+
+SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname)
+{
+ return(create_rwops(PHYSFS_openAppend(fname)));
+} /* PHYSFSRWOPS_openAppend */
+
+
+/* end of physfsrwops.c ... */
+
--- /dev/null
+/*
+ * This code provides a glue layer between PhysicsFS and Simple Directmedia
+ * Layer's (SDL) RWops i/o abstraction.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the GNU Lesser
+ * General Public License: http://www.gnu.org/licenses/lgpl.txt
+ *
+ * SDL falls under the LGPL, too. You can get SDL at http://www.libsdl.org/
+ *
+ * This file was written by Ryan C. Gordon. (icculus@icculus.org).
+ */
+
+#ifndef _INCLUDE_PHYSFSRWOPS_H_
+#define _INCLUDE_PHYSFSRWOPS_H_
+
+#include "physfs.h"
+#include "SDL.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Open a platform-independent filename for reading, and make it accessible
+ * via an SDL_RWops structure. The file will be closed in PhysicsFS when the
+ * RWops is closed. PhysicsFS should be configured to your liking before
+ * opening files through this method.
+ *
+ * @param filename File to open in platform-independent notation.
+ * @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_openRead(const char *fname);
+
+/**
+ * Open a platform-independent filename for writing, and make it accessible
+ * via an SDL_RWops structure. The file will be closed in PhysicsFS when the
+ * RWops is closed. PhysicsFS should be configured to your liking before
+ * opening files through this method.
+ *
+ * @param filename File to open in platform-independent notation.
+ * @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname);
+
+/**
+ * Open a platform-independent filename for appending, and make it accessible
+ * via an SDL_RWops structure. The file will be closed in PhysicsFS when the
+ * RWops is closed. PhysicsFS should be configured to your liking before
+ * opening files through this method.
+ *
+ * @param filename File to open in platform-independent notation.
+ * @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname);
+
+/**
+ * Make a SDL_RWops from an existing PhysicsFS file handle. You should
+ * dispose of any references to the handle after successful creation of
+ * the RWops. The actual PhysicsFS handle will be destroyed when the
+ * RWops is closed.
+ *
+ * @param handle a valid PhysicsFS file handle.
+ * @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_file *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* include-once blocker */
+
+/* end of physfsrwops.h ... */
+
--- /dev/null
+/*
+ * PhysicsFS - ruby interface
+ *
+ * Author:: Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License:: LGPL
+ */
+
+#include "physfs.h"
+#include "ruby.h"
+
+#include "rb_physfs.h"
+#include "rb_physfs_file.h"
+
+VALUE modulePhysfs;
+
+/*
+ * PhysicsFS::init str
+ *
+ * initialize PhysicsFS
+ */
+VALUE physfs_init (VALUE self, VALUE str)
+{
+ int result = PHYSFS_init (STR2CSTR(str));
+
+ if (result)
+ return Qtrue;
+
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::deinit
+ */
+VALUE physfs_deinit (VALUE self)
+{
+ if (PHYSFS_deinit ())
+ return Qtrue;
+
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::version
+ *
+ * return PhysicsFS::Version object
+ */
+VALUE physfs_version (VALUE self)
+{
+ char evalStr[200];
+ PHYSFS_Version ver;
+
+ PHYSFS_getLinkedVersion (&ver);
+
+ sprintf (evalStr, "PhysicsFS::Version.new %d, %d, %d",
+ ver.major, ver.minor, ver.patch);
+ return rb_eval_string (evalStr);
+}
+
+/*
+ * PhysicsFS::supported_archives
+ *
+ * return Array of PhysicsFS::ArchiveInfo objects
+ */
+VALUE physfs_supported_archives (VALUE self)
+{
+ const PHYSFS_ArchiveInfo **info = PHYSFS_supportedArchiveTypes();
+ VALUE klass = rb_const_get (modulePhysfs, rb_intern ("ArchiveInfo"));
+ VALUE ary = rb_ary_new ();
+ VALUE params[4];
+
+ while ( *info != 0 )
+ {
+ params[0] = rb_str_new2 ((*info)->extension);
+ params[1] = rb_str_new2 ((*info)->description);
+ params[2] = rb_str_new2 ((*info)->author);
+ params[3] = rb_str_new2 ((*info)->url);
+
+ rb_ary_push (ary, rb_class_new_instance (4, params, klass));
+ info++;
+ }
+
+ return ary;
+}
+
+/*
+ * PhysicsFS::last_error
+ *
+ * return string representation of last PhysicsFS error
+ */
+VALUE physfs_last_error (VALUE self)
+{
+ const char *last_error = PHYSFS_getLastError ();
+
+ if (last_error == 0)
+ last_error = "";
+
+ return rb_str_new2 (last_error);
+}
+
+/*
+ * PhysicsFS::dir_separator
+ *
+ * return platform directory separator
+ */
+VALUE physfs_dir_separator (VALUE self)
+{
+ return rb_str_new2 (PHYSFS_getDirSeparator ());
+}
+
+/*
+ * PhysicsFS::permit_symlinks boolValue
+ *
+ * turn symlinks support on/off
+ */
+VALUE physfs_permit_symlinks (VALUE self, VALUE allow)
+{
+ int p = 1;
+
+ if (allow == Qfalse || allow == Qnil)
+ p = 0;
+
+ PHYSFS_permitSymbolicLinks (p);
+ return Qtrue;
+}
+
+/*
+ * PhysicsFS::cdrom_dirs
+ *
+ * return Array of strings containing available CDs
+ */
+VALUE physfs_cdrom_dirs (VALUE self)
+{
+ char **cds = PHYSFS_getCdRomDirs();
+ char **i;
+ VALUE ary = rb_ary_new ();
+
+ for (i = cds; *i != 0; i++)
+ rb_ary_push (ary, rb_str_new2 (*i));
+
+ PHYSFS_freeList (cds);
+ return ary;
+}
+
+/*
+ * PhysicsFS::base_dir
+ *
+ * return base directory
+ */
+VALUE physfs_base_dir (VALUE self)
+{
+ const char *base_dir = PHYSFS_getBaseDir ();
+ if (base_dir == 0)
+ base_dir = "";
+
+ return rb_str_new2 (base_dir);
+}
+
+/*
+ * PhysicsFS::user_dir
+ *
+ * return user directory
+ */
+VALUE physfs_user_dir (VALUE self)
+{
+ const char *user_dir = PHYSFS_getBaseDir ();
+ if (user_dir == 0)
+ user_dir = "";
+
+ return rb_str_new2 (user_dir);
+}
+
+/*
+ * PhysicsFS::write_dir
+ *
+ * return write directory
+ */
+VALUE physfs_write_dir (VALUE self)
+{
+ const char *write_dir = PHYSFS_getWriteDir ();
+ if (write_dir == 0)
+ return Qnil;
+
+ return rb_str_new2 (write_dir);
+}
+
+/*
+ * PhysicsFS::write_dir= str
+ *
+ * set write directory to *str*
+ */
+VALUE physfs_set_write_dir (VALUE self, VALUE str)
+{
+ int result = PHYSFS_setWriteDir (STR2CSTR(str));
+
+ if (result)
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::add_to_search_path str, append
+ *
+ * if append > 0 - append str to search path, otherwise prepend it
+ */
+VALUE physfs_add_search_path (VALUE self, VALUE str, VALUE append)
+{
+ int result = PHYSFS_addToSearchPath (STR2CSTR(str), FIX2INT(append));
+ if (result)
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::remove_from_search_path str
+ *
+ * removes str from search path
+ */
+VALUE physfs_remove_search_path (VALUE self, VALUE str)
+{
+ int result = PHYSFS_removeFromSearchPath (STR2CSTR(str));
+ if (result)
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::search_path
+ *
+ * return current search_path - as array of strings
+ */
+VALUE physfs_search_path (VALUE self)
+{
+ char **path = PHYSFS_getSearchPath ();
+ char **i;
+ VALUE ary = rb_ary_new ();
+
+ for (i = path ; *i != 0; i++)
+ rb_ary_push (ary, rb_str_new2 (*i));
+
+ PHYSFS_freeList (path);
+ return ary;
+}
+
+//
+VALUE physfs_setSaneConfig(VALUE self, VALUE org, VALUE app, VALUE ext,
+ VALUE includeCdroms, VALUE archivesFirst)
+{
+ int res = PHYSFS_setSaneConfig (STR2CSTR(org), STR2CSTR(app), STR2CSTR(ext),
+ RTEST(includeCdroms), RTEST(archivesFirst));
+ if (res)
+ return Qtrue;
+
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::mkdir newdir
+ *
+ * create new directory
+ */
+VALUE physfs_mkdir (VALUE self, VALUE newdir)
+{
+ int result = PHYSFS_mkdir (STR2CSTR(newdir));
+ if (result)
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::delete name
+ *
+ * delete file with name
+ */
+VALUE physfs_delete (VALUE self, VALUE name)
+{
+ int result = PHYSFS_delete (STR2CSTR(name));
+ if (result)
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::real_dir name
+ *
+ * return real directory (in search path) of a name
+ */
+VALUE physfs_real_dir (VALUE self, VALUE name)
+{
+ const char *path = PHYSFS_getRealDir (STR2CSTR(name));
+ if (path == 0)
+ return Qnil;
+
+ return rb_str_new2 (path);
+}
+
+/*
+ * PhysicsFS::enumerate dir
+ *
+ * list a dir from a search path
+ */
+VALUE physfs_enumerate (VALUE self, VALUE dir)
+{
+ char **files = PHYSFS_enumerateFiles (STR2CSTR(dir));
+ char **i;
+ VALUE ary = rb_ary_new ();
+
+ for (i = files; *i != 0; i++)
+ rb_ary_push (ary, rb_str_new2 (*i));
+
+ PHYSFS_freeList (files);
+ return ary;
+}
+
+/*
+ * PhysicsFS::exists? name
+ *
+ * does a file with name exist?
+ */
+VALUE physfs_exists (VALUE self, VALUE name)
+{
+ int result = PHYSFS_exists (STR2CSTR(name));
+ if (result)
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::is_directory? name
+ *
+ * return true if name is directory
+ */
+VALUE physfs_is_directory (VALUE self, VALUE name)
+{
+ int result = PHYSFS_isDirectory (STR2CSTR(name));
+ if (result)
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::is_symlink? name
+ *
+ * return true if name is symlink
+ */
+VALUE physfs_is_symlink (VALUE self, VALUE name)
+{
+ int result = PHYSFS_isSymbolicLink (STR2CSTR(name));
+ if (result)
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::last_mod_time name
+ *
+ * return last modification time of a file
+ */
+VALUE physfs_last_mod_time (VALUE self, VALUE name)
+{
+ int result = PHYSFS_getLastModTime (STR2CSTR(name));
+
+ return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::open_read name
+ *
+ * return +PhysicsFS::File+ ready for reading
+ */
+VALUE physfs_open_read (VALUE self, VALUE name)
+{
+ PHYSFS_File *file = PHYSFS_openRead (STR2CSTR(name));
+ return physfs_file_new (file);
+}
+
+/*
+ * PhysicsFS::open_write name
+ *
+ * return PhysicsFS::File ready for writing
+ */
+VALUE physfs_open_write (VALUE self, VALUE name)
+{
+ PHYSFS_File *file = PHYSFS_openWrite (STR2CSTR(name));
+ return physfs_file_new (file);
+}
+
+/*
+ * PhysicsFS::open_append name
+ *
+ * return PhysicsFS::File ready for appending
+ */
+VALUE physfs_open_append (VALUE self, VALUE name)
+{
+ PHYSFS_File *file = PHYSFS_openAppend (STR2CSTR(name));
+ return physfs_file_new (file);
+}
+
+void Init_physfs_so (void)
+{
+ modulePhysfs = rb_define_module ("PhysicsFS");
+
+ rb_define_singleton_method (modulePhysfs, "init_internal", physfs_init, 1);
+ rb_define_singleton_method (modulePhysfs, "deinit", physfs_deinit, 0);
+ rb_define_singleton_method (modulePhysfs, "version", physfs_version, 0);
+ rb_define_singleton_method (modulePhysfs, "supported_archives",
+ physfs_supported_archives, 0);
+ rb_define_singleton_method (modulePhysfs, "last_error",
+ physfs_last_error, 0);
+ rb_define_singleton_method (modulePhysfs, "dir_separator",
+ physfs_dir_separator, 0);
+ rb_define_singleton_method (modulePhysfs, "permit_symlinks",
+ physfs_permit_symlinks, 1);
+ rb_define_singleton_method (modulePhysfs, "cdrom_dirs",
+ physfs_cdrom_dirs, 0);
+ rb_define_singleton_method (modulePhysfs, "base_dir", physfs_base_dir, 0);
+ rb_define_singleton_method (modulePhysfs, "user_dir", physfs_user_dir, 0);
+
+ rb_define_singleton_method (modulePhysfs, "write_dir", physfs_write_dir, 0);
+ rb_define_singleton_method (modulePhysfs, "write_dir=",
+ physfs_set_write_dir, 1);
+
+ rb_define_singleton_method (modulePhysfs, "add_to_search_path",
+ physfs_add_search_path, 2);
+ rb_define_singleton_method (modulePhysfs, "remove_from_search_path",
+ physfs_remove_search_path, 1);
+ rb_define_singleton_method (modulePhysfs, "search_path",
+ physfs_search_path, 0);
+
+ rb_define_singleton_method (modulePhysfs, "set_sane_config",
+ physfs_setSaneConfig, 5);
+
+ rb_define_singleton_method (modulePhysfs, "mkdir", physfs_mkdir, 1);
+ rb_define_singleton_method (modulePhysfs, "delete", physfs_delete, 1);
+ rb_define_singleton_method (modulePhysfs, "real_dir",
+ physfs_real_dir, 1);
+ rb_define_singleton_method (modulePhysfs, "enumerate", physfs_enumerate, 1);
+ rb_define_singleton_method (modulePhysfs, "exists?", physfs_exists, 1);
+ rb_define_singleton_method (modulePhysfs, "is_directory?",
+ physfs_is_directory, 1);
+ rb_define_singleton_method (modulePhysfs, "is_symlink?",
+ physfs_is_symlink, 1);
+ rb_define_singleton_method (modulePhysfs, "last_mod_time",
+ physfs_last_mod_time, 1);
+
+ rb_define_singleton_method (modulePhysfs, "open_read",
+ physfs_open_read, 1);
+ rb_define_singleton_method (modulePhysfs, "open_write",
+ physfs_open_write, 1);
+ rb_define_singleton_method (modulePhysfs, "open_append",
+ physfs_open_append, 1);
+
+ init_physfs_file ();
+ init_sdl_rwops ();
+}
+
+/*
+// Local Variables:
+// mode: C
+// c-indentation-style: "stroustrup"
+// indent-tabs-mode: nil
+// End:
+*/
--- /dev/null
+/*
+ * PhysicsFS - ruby interface
+ *
+ * Author:: Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License:: LGPL
+ */
+
+#ifndef __RB__PHYSFS__H__
+#define __RB__PHYSFS__H__
+
+extern VALUE modulePhysfs;
+
+#endif
--- /dev/null
+/*
+ * PhysicsFS File abstraction - ruby interface
+ *
+ * Author:: Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License:: LGPL
+ */
+
+#include "physfs.h"
+#include "ruby.h"
+
+#include "rb_physfs.h"
+#include "rb_physfs_file.h"
+#include "physfsrwops.h"
+
+VALUE classPhysfsFile;
+
+/*
+ * construct new PhysicsFS::File object
+ */
+VALUE physfs_file_new (PHYSFS_File *file)
+{
+ if (file == 0)
+ return Qnil;
+
+ return Data_Wrap_Struct (classPhysfsFile, 0, 0, file);
+}
+
+/*
+ * PhysicsFS::File#close
+ *
+ * Close the file. It's illegal to use the object after its closure.
+ */
+VALUE physfs_file_close (VALUE self)
+{
+ int result;
+ PHYSFS_File *file;
+ Data_Get_Struct (self, PHYSFS_File, file);
+
+ if (file == 0)
+ return Qfalse;
+
+ result = PHYSFS_close (file);
+ DATA_PTR(self) = 0;
+
+ if (result)
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::File#read obj_size, num_objects
+ *
+ * Read *objCount* objects which are *objSize* each.
+ * return String instance containing raw data or nil if failure.
+ * #length of string will reflect real number of objects read.
+ */
+VALUE physfs_file_read (VALUE self, VALUE objSize, VALUE objCount)
+{
+ int objRead;
+ void *buffer;
+ VALUE result;
+ PHYSFS_File *file;
+
+ Data_Get_Struct (self, PHYSFS_File, file);
+ if (file == 0)
+ return Qnil; //wasted file - no read possible
+
+ buffer = malloc (FIX2UINT(objSize) * FIX2UINT(objCount));
+ if (buffer == 0)
+ return Qnil;
+
+ objRead = PHYSFS_read (file, buffer, FIX2UINT(objSize), FIX2UINT(objCount));
+ if (objRead == -1)
+ {
+ free (buffer);
+ return Qnil;
+ }
+
+ result = rb_str_new (buffer, objRead * FIX2UINT(objSize));
+ free (buffer);
+ return result;
+}
+
+/*
+ * PhysicsFS::File#write buffer, obj_size, num_objects
+ *
+ * return nil on failure or number of objects written.
+ */
+VALUE physfs_file_write (VALUE self, VALUE buf, VALUE objSize, VALUE objCount)
+{
+ int result;
+ PHYSFS_File *file;
+
+ Data_Get_Struct (self, PHYSFS_File, file);
+ if (file == 0)
+ return Qnil;
+
+ result = PHYSFS_write (file, STR2CSTR(buf),
+ FIX2UINT(objSize), FIX2UINT(objCount));
+ if (result == -1)
+ return Qnil;
+
+ return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::File#eof?
+ */
+VALUE physfs_file_eof (VALUE self)
+{
+ int result;
+ PHYSFS_File *file;
+
+ Data_Get_Struct (self, PHYSFS_File, file);
+ if (file == 0)
+ return Qnil;
+
+ result = PHYSFS_eof (file);
+
+ if (result)
+ return Qtrue;
+
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::File#tell
+ *
+ * tells current position in file
+ */
+VALUE physfs_file_tell (VALUE self)
+{
+ int result;
+ PHYSFS_File *file;
+
+ Data_Get_Struct (self, PHYSFS_File, file);
+ if (file == 0)
+ return Qnil;
+
+ result = PHYSFS_tell (file);
+
+ if (result == -1)
+ return Qnil;
+
+ return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::File#seek pos
+ *
+ * seek to pos in file
+ */
+VALUE physfs_file_seek (VALUE self, VALUE pos)
+{
+ int result;
+ PHYSFS_File *file;
+
+ Data_Get_Struct (self, PHYSFS_File, file);
+ if (file == 0)
+ return Qnil;
+
+ result = PHYSFS_seek (file, FIX2LONG(pos));
+
+ if (result)
+ return Qtrue;
+
+ return Qfalse;
+}
+
+/*
+ * PhysicsFS::File#length
+ */
+VALUE physfs_file_length (VALUE self)
+{
+ int result;
+ PHYSFS_File *file;
+
+ Data_Get_Struct (self, PHYSFS_File, file);
+ if (file == 0)
+ return Qnil;
+
+ result = PHYSFS_fileLength (file);
+
+ if (result == -1)
+ return Qnil;
+
+ return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::File#to_rwops
+ *
+ * File object is converted to RWops object.
+ * File object becomes unusable after that - every operation
+ * should be done through new-born RWops object.
+ */
+VALUE physfs_file_to_rwops (VALUE self)
+{
+ PHYSFS_File *file;
+ SDL_RWops *rwops;
+
+ Data_Get_Struct (self, PHYSFS_File, file);
+ if (file == 0)
+ return Qnil;
+
+ rwops = PHYSFSRWOPS_makeRWops (file);
+ if (rwops == 0)
+ return Qnil;
+
+ DATA_PTR(self) = 0; // oh, gosh, we've sacrificed ourselves!
+ return sdl_rwops_new (rwops);
+}
+
+void init_physfs_file (void)
+{
+ classPhysfsFile = rb_define_class_under (modulePhysfs, "File", rb_cObject);
+
+ rb_define_method (classPhysfsFile, "close", physfs_file_close, 0);
+ rb_define_method (classPhysfsFile, "eof?", physfs_file_eof, 0);
+ rb_define_method (classPhysfsFile, "tell", physfs_file_tell, 0);
+ rb_define_method (classPhysfsFile, "seek", physfs_file_seek, 1);
+ rb_define_method (classPhysfsFile, "length", physfs_file_length, 0);
+ rb_define_method (classPhysfsFile, "read", physfs_file_read, 2);
+ rb_define_method (classPhysfsFile, "write", physfs_file_write, 3);
+ rb_define_method (classPhysfsFile, "to_rwops", physfs_file_to_rwops, 0);
+}
--- /dev/null
+/*
+ * PhysicsFS File abstraction - ruby interface
+ *
+ * Author:: Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License:: LGPL
+ */
+
+#ifndef __RB__PHYSFS__FILE__H__
+#define __RB__PHYSFS__FILE__H__
+
+extern VALUE classPhysfsFile;
+
+VALUE physfs_file_new (PHYSFS_file *file);
+VALUE physfs_file_close (VALUE self);
+VALUE physfs_file_read (VALUE self, VALUE objSize, VALUE objCount);
+VALUE physfs_file_write (VALUE self, VALUE buf, VALUE objSize, VALUE objCount);
+VALUE physfs_file_eof (VALUE self);
+VALUE physfs_file_tell (VALUE self);
+VALUE physfs_file_seek (VALUE self, VALUE pos);
+VALUE physfs_file_length (VALUE self);
+
+void init_physfs_file (void);
+
+#endif
--- /dev/null
+/*
+ * SDL_RWops - ruby interface
+ *
+ * Author:: Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License:: LGPL
+ */
+
+#include "SDL_rwops.h"
+#include "ruby.h"
+
+#include "rb_physfs.h"
+#include "rb_sdl_rwops.h"
+
+VALUE classRWops;
+
+/*
+ * RWops constructor
+ */
+VALUE sdl_rwops_new (SDL_RWops *ops)
+{
+ VALUE result;
+
+ if (ops == 0)
+ return Qnil;
+
+ result = Data_Wrap_Struct (classRWops, 0, SDL_FreeRW, ops);
+ return result;
+}
+
+/*
+ * PhysicsFS::RWops::from_file name, mode
+ *
+ * create RWops object from file
+ */
+VALUE sdl_rwops_from_file (VALUE self, VALUE name, VALUE mode)
+{
+ SDL_RWops *ops = SDL_RWFromFile(STR2CSTR(name), STR2CSTR(mode));
+ return sdl_rwops_new (ops);
+}
+
+/*
+ * PhysicsFS::RWops::from_memory string
+ *
+ * create RWops object from memory
+ */
+VALUE sdl_rwops_from_mem (VALUE self, VALUE str)
+{
+ int len = RSTRING(str)->len;
+ void *mem = STR2CSTR(str);
+ SDL_RWops *ops = SDL_RWFromMem(mem, len);
+
+ return sdl_rwops_new (ops);
+}
+
+/*
+ * PhysicsFS::RWops#seek offset, whence
+ *
+ * position RWops object
+ */
+VALUE sdl_rwops_seek (VALUE self, VALUE offset, VALUE whence)
+{
+ int result;
+ SDL_RWops *ops;
+
+ Data_Get_Struct (self, SDL_RWops, ops);
+ if (ops == 0)
+ return Qnil;
+
+ result = SDL_RWseek(ops, FIX2INT(offset), FIX2INT(whence));
+ return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::RWops#close
+ *
+ * close RWops. No use of the object is possible after that.
+ */
+VALUE sdl_rwops_close (VALUE self)
+{
+ int result;
+ SDL_RWops *ops;
+
+ Data_Get_Struct (self, SDL_RWops, ops);
+ if (ops == 0)
+ return Qnil;
+
+ result = SDL_RWclose (ops);
+ DATA_PTR(self) = 0;
+
+ return INT2FIX(result);
+}
+
+/*
+ * PhysicsFS::RWops#read
+ *
+ * read from RWops object objCount objSize'd entities.
+ * return string containing raw data or nil
+ */
+VALUE sdl_rwops_read (VALUE self, VALUE objSize, VALUE objCount)
+{
+ int objRead;
+ void *buffer;
+ VALUE result;
+ SDL_RWops *ops;
+
+ Data_Get_Struct (self, SDL_RWops, ops);
+ if (ops == 0)
+ return Qnil;
+
+ buffer = malloc (FIX2UINT(objSize) * FIX2UINT(objCount));
+ if (buffer == 0)
+ return Qnil;
+
+ objRead = SDL_RWread (ops, buffer, FIX2UINT(objSize), FIX2UINT(objCount));
+ if (objRead == -1)
+ {
+ free (buffer);
+ return Qnil;
+ }
+
+ result = rb_str_new (buffer, objRead * FIX2UINT(objSize));
+ free (buffer);
+ return result;
+}
+
+/*
+ * PhysicsFS::RWops#write buffer, size, n
+ *
+ * write raw string containing n objects size length each.
+ * return number of objects written or nil
+ */
+VALUE sdl_rwops_write (VALUE self, VALUE buffer, VALUE size, VALUE n)
+{
+ int result;
+ SDL_RWops *ops;
+
+ Data_Get_Struct (self, SDL_RWops, ops);
+ if (ops == 0)
+ return Qnil;
+
+ result = SDL_RWwrite (ops, STR2CSTR(buffer), FIX2INT(size), FIX2INT(n));
+
+ if (result == -1)
+ return Qnil;
+
+ return INT2FIX(result);
+}
+
+void init_sdl_rwops (void)
+{
+ classRWops = rb_define_class_under (modulePhysfs, "RWops", rb_cObject);
+
+ rb_define_method (classRWops, "seek", sdl_rwops_seek, 2);
+ rb_define_method (classRWops, "read", sdl_rwops_read, 2);
+ rb_define_method (classRWops, "write", sdl_rwops_write, 3);
+ rb_define_method (classRWops, "close", sdl_rwops_close, 0);
+
+ rb_define_singleton_method (classRWops, "from_file",
+ sdl_rwops_from_file, 2);
+ rb_define_singleton_method (classRWops, "from_memory",
+ sdl_rwops_from_mem, 1);
+}
--- /dev/null
+/*
+ * SDL_RWops - ruby interface
+ *
+ * Author:: Ed Sinjiashvili (slimb@vlinkmail.com)
+ * License:: LGPL
+ */
+
+#ifndef __RB__SDL__RWOPS__H__
+#define __RB__SDL__RWOPS__H__
+
+extern VALUE classRWops;
+
+VALUE sdl_rwops_new (SDL_RWops *ops);
+void init_sdl_rwops (void);
+
+#endif
--- /dev/null
+#
+# PhysicsFS test program - mimics real physfs_test
+#
+require 'readline'
+require 'physfs'
+
+def die msg
+ puts "#{msg} - reason: #{PhysicsFS.last_error}"
+end
+
+#
+# parse line to command and args
+#
+def parse line
+ return false if line.nil?
+
+ if line.strip =~ /^(.*?) (?: (?:\s+(.*)) | $)/x
+ run $1, $2
+ else
+ false
+ end
+end
+
+#
+# parse command args
+#
+def parse_args args
+ args.strip!
+
+ dquoted = /^ " (.*?) "/x
+ squoted = /^ ' (.*?) '/x
+ unquoted = /^([^\s\'\"]+)/
+
+ regexps = [dquoted, squoted, unquoted]
+
+ result = []
+ while args != ""
+ regexps.each do |r|
+ if args =~ r
+ result << $1
+ args.sub! r, ""
+ args.sub!(/\s+/, "")
+ break
+ end
+ end
+ end
+ result
+end
+
+def usage cmd, prefix = "usage: "
+ print prefix
+ args = Commands::HELP[cmd]
+ if args
+ print cmd
+ args.scan(/\w+/).each {|x|
+ print " <#{x}>"
+ }
+ puts
+ else
+ puts %|#{cmd} (no arguments)|
+ end
+end
+
+# commands go below
+module Commands
+ HELP = {
+ "init" => "argv0",
+ "addarchive" => "archiveLocation append",
+ "removearchive" => "archiveLocation",
+ "enumerate" => "dirToEnumerate",
+ "ls" => "dirToEnumerate",
+ "setwritedir" => "newWriteDir",
+ "permitsymlinks" => "1or0",
+ "setsaneconfig" => "org appName arcExt includeCdRoms archivesFirst",
+ "mkdir" => "dirToMk",
+ "delete" => "dirToDelete",
+ "getrealdir" => "fileToFind",
+ "exists" => "fileToCheck",
+ "isdir" => "fileToCheck",
+ "issymlink" => "fileToCheck",
+ "cat" => "fileToCat",
+ "filelength" => "fileToCheck",
+ "append" => "fileToAppend",
+ "write" => "fileToCreateOrTrash",
+ "getlastmodtime" => "fileToExamine"
+ }
+
+ def quit_cmd
+ exit
+ end
+
+ alias q_cmd quit_cmd
+
+ def help_cmd
+ commands = ::Commands.instance_methods.grep(/_cmd$/).sort
+ puts "Commands:"
+ commands.each do |c|
+ usage c.sub("_cmd", ""), " - "
+ end
+
+ true
+ end
+
+ def e val
+ if val
+ puts "Successful."
+ else
+ puts "Failure. reason: #{PhysicsFS.last_error}"
+ end
+ true
+ end
+
+ def init_cmd arg
+ e PhysicsFS.init(arg)
+ end
+
+ def deinit_cmd
+ e PhysicsFS.deinit
+ end
+
+ def addarchive_cmd archive, append
+ e PhysicsFS.add_to_search_path(archive, append)
+ end
+
+ def removearchive_cmd archive
+ e PhysicsFS.remove_from_search_path archive
+ end
+
+ def enumerate_cmd path
+ entries = PhysicsFS.enumerate(path)
+ entries.each {|x|
+ puts x
+ }
+ true
+ end
+
+ alias ls_cmd enumerate_cmd
+
+ def getlasterror_cmd
+ puts "Last error is [#{PhysicsFS.last_error}]"
+ true
+ end
+
+ def getdirsep_cmd
+ puts "Directory separator is [#{PhysicsFS.dir_separator}]"
+ true
+ end
+
+ def getcdromdirs_cmd
+ dirs = PhysicsFS.cdrom_dirs
+ dirs.each {|x|
+ puts x
+ }
+ puts " total [#{dirs.length}] drives."
+ true
+ end
+
+ def getsearchpath_cmd
+ spath = PhysicsFS.search_path
+ spath.each {|x|
+ puts x
+ }
+ puts "total [#{spath.length}] directories."
+ true
+ end
+
+ def getbasedir_cmd
+ dir = PhysicsFS.base_dir
+ puts dir if dir
+ true
+ end
+
+ def getuserdir_cmd
+ puts PhysicsFS.user_dir
+ true
+ end
+
+ def getwritedir_cmd
+ dir = PhysicsFS.write_dir
+ if dir
+ puts "Write directory is [#{dir}]."
+ else
+ puts "No write directory defined."
+ end
+ true
+ end
+
+ def setwritedir_cmd dir
+ e(PhysicsFS.write_dir = dir)
+ end
+
+ def permitsymlinks_cmd val
+ if val.to_i == 1
+ PhysicsFS.permit_symlinks true
+ puts "Symlinks are now permitted"
+ else
+ PhysicsFS.permit_symlinks false
+ puts "Symlinks are now forbidden"
+ end
+ true
+ end
+
+ def setsaneconfig_cmd org, appname, ext, includeCdroms, archivesFirst
+ includeCdroms = includeCdroms.to_i == 1
+ archiveFirst = archivesFirst == 1
+ e PhysicsFS.set_sane_config(org, appname, ext, includeCdroms, archivesFirst)
+ end
+
+ def mkdir_cmd dir
+ e PhysicsFS.mkdir(dir)
+ end
+
+ def delete_cmd dir
+ e PhysicsFS.delete(dir)
+ end
+
+ def getrealdir_cmd file
+ dir = PhysicsFS.real_dir file
+ if dir
+ puts "Found at [#{dir}]"
+ else
+ puts "Not found."
+ end
+ true
+ end
+
+ def exists_cmd file
+ if PhysicsFS.exists? file
+ puts "File exists"
+ else
+ puts "File does not exist"
+ end
+ true
+ end
+
+ def isdir_cmd file
+ if PhysicsFS.is_directory? file
+ puts "File is a directory"
+ else
+ puts "File is NOT a directory"
+ end
+ true
+ end
+
+ def issymlink_cmd file
+ if PhysicsFS.is_symlink? file
+ puts "File is a symlink"
+ else
+ puts "File is NOT a symlink"
+ end
+ true
+ end
+
+ def cat_cmd filename
+ file = PhysicsFS.open_read filename
+ if file.nil?
+ puts "failed to open. reason: #{PhysicsFS.last_error}"
+ return true
+ end
+
+ puts file.cat
+ true
+ end
+
+ def filelength_cmd filename
+ file = PhysicsFS.open_read filename
+ if file.nil?
+ puts "failed to open. reason: #{PhysicsFS.last_error}"
+ return true
+ end
+
+ puts file.length
+ file.close
+ true
+ end
+
+ WRITE_STR = "Rubyfied PhysicsFS works just fine.\n\n"
+
+ def append_cmd filename
+ file = PhysicsFS.open_append filename
+ if file.nil?
+ puts "failed to open. reason: #{PhysicsFS.last_error}"
+ return true
+ end
+
+ file.write WRITE_STR, 1, WRITE_STR.length
+ file.close
+ true
+ end
+
+ def write_cmd filename
+ file = PhysicsFS.open_write filename
+ if file.nil?
+ puts "failed to open. reason: #{PhysicsFS.last_error}"
+ return true
+ end
+
+ file.write_str WRITE_STR
+ file.close
+ true
+ end
+
+ def getlastmodtime_cmd filename
+ t = PhysicsFS.last_mod_time filename
+ if t == -1
+ puts "failed to determin. reason: #{PhysicsFS.last_error}"
+ else
+ puts "Last modified: #{Time.at(t)}"
+ end
+ true
+ end
+end
+
+include Commands
+
+def run command, args
+ if args
+ args = parse_args args
+ else
+ args = []
+ end
+
+ begin
+ cmd = method "#{command}_cmd"
+ if args.length == cmd.arity
+ return cmd.call *args
+ else
+ usage command
+ true
+ end
+ rescue NameError
+ puts 'Unknown command. Enter "help" for instructions.'
+ true
+ end
+end
+
+if __FILE__ == $0
+
+ PhysicsFS.init($0) or die "PhysicsFS init failed"
+
+ puts "PhysicsFS version: #{PhysicsFS.version}"
+ puts
+
+ puts "Supported archives: "
+ puts PhysicsFS.supported_archives
+ puts
+
+ puts 'Enter commands. Enter "help" for instructions.'
+
+ loop {
+ line = Readline::readline "physfs_rb> ", true
+ break unless parse line
+ }
+end
+
+
+
+
--- /dev/null
+/*
+ * This is a quick and dirty HTTP server that uses PhysicsFS to retrieve
+ * files. It is not robust at all, probably buggy, and definitely poorly
+ * designed. It's just meant to show that it can be done.
+ *
+ * Basically, you compile this code, and run it:
+ * ./physfshttpd archive1.zip archive2.zip /path/to/a/real/dir etc...
+ *
+ * The files are appended in order to the PhysicsFS search path, and when
+ * a client request comes it, it looks for the file in said search path.
+ *
+ * My goal was to make this work in less than 300 lines of C, so again, it's
+ * not to be used for any serious purpose. Patches to make this application
+ * suck less will be readily and gratefully accepted.
+ *
+ * Command line I used to build this on Linux:
+ * gcc -Wall -Werror -g -o bin/physfshttpd extras/physfshttpd.c -lphysfs
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes. This code has
+ * NO WARRANTY.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
+ * Please see LICENSE.txt in the root of the source tree.
+ *
+ * This file was written by Ryan C. Gordon. (icculus@icculus.org).
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifndef LACKING_SIGNALS
+#include <signal.h>
+#endif
+
+#ifndef LACKING_PROTOENT
+#include <netdb.h>
+#endif
+
+#include "physfs.h"
+
+
+#define DEFAULT_PORTNUM 6667
+
+typedef struct
+{
+ int sock;
+ struct sockaddr *addr;
+ socklen_t addrlen;
+} http_args;
+
+
+static char *txt404 =
+"HTTP/1.0 404 Not Found\n"
+"Connection: close\n"
+"Content-type: text/html\n"
+"\n"
+"<html><head><title>404 Not Found</title></head>\n"
+"<body>Can't find that.</body></html>\n\n";
+
+
+static void feed_file_http(const char *ipstr, int sock, const char *fname)
+{
+ PHYSFS_File *in = PHYSFS_openRead(fname);
+ char buffer[1024];
+ printf("%s: requested [%s].\n", ipstr, fname);
+ if (in == NULL)
+ {
+ printf("%s: Can't open [%s]: %s.\n",
+ ipstr, fname, PHYSFS_getLastError());
+ write(sock, txt404, strlen(txt404)); /* !!! FIXME: Check retval */
+ } /* if */
+ else
+ {
+ do
+ {
+ PHYSFS_sint64 br = PHYSFS_read(in, buffer, 1, sizeof (buffer));
+ if (br == -1)
+ {
+ printf("%s: Read error: %s.\n", ipstr, PHYSFS_getLastError());
+ break;
+ } /* if */
+
+ write(sock, buffer, (int) br); /* !!! FIXME: CHECK THIS RETVAL! */
+ } while (!PHYSFS_eof(in));
+
+ PHYSFS_close(in);
+ } /* else */
+} /* feed_file_http */
+
+
+static void *do_http(void *_args)
+{
+ http_args *args = (http_args *) _args;
+ char ipstr[128];
+ char buffer[512];
+ char *ptr;
+ strncpy(ipstr, inet_ntoa(((struct sockaddr_in *) args->addr)->sin_addr),
+ sizeof (ipstr));
+ ipstr[sizeof (ipstr) - 1] = '\0';
+
+ printf("%s: connected.\n", ipstr);
+ read(args->sock, buffer, sizeof (buffer));
+ buffer[sizeof (buffer) - 1] = '\0';
+ ptr = strchr(buffer, '\n');
+ if (!ptr)
+ printf("%s: potentially bogus request.\n", ipstr);
+ else
+ {
+ *ptr = '\0';
+ ptr = strchr(buffer, '\r');
+ if (ptr != NULL)
+ *ptr = '\0';
+
+ if ((toupper(buffer[0]) == 'G') &&
+ (toupper(buffer[1]) == 'E') &&
+ (toupper(buffer[2]) == 'T') &&
+ (toupper(buffer[3]) == ' ') &&
+ (toupper(buffer[4]) == '/'))
+ {
+ ptr = strchr(buffer + 5, ' ');
+ if (ptr != NULL)
+ *ptr = '\0';
+ feed_file_http(ipstr, args->sock, buffer + 4);
+ } /* if */
+ } /* else */
+
+ /* !!! FIXME: Time the transfer. */
+ printf("%s: closing connection.\n", ipstr);
+ close(args->sock);
+ free(args->addr);
+ free(args);
+ return(NULL);
+} /* do_http */
+
+
+static void serve_http_request(int sock, struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ http_args *args = (http_args *) malloc(sizeof (http_args));
+ if (args == NULL)
+ {
+ printf("out of memory.\n");
+ return;
+ } // if
+ args->addr = (struct sockaddr *) malloc(addrlen);
+ if (args->addr == NULL)
+ {
+ free(args);
+ printf("out of memory.\n");
+ return;
+ } // if
+
+ args->sock = sock;
+ args->addrlen = addrlen;
+ memcpy(args->addr, addr, addrlen);
+
+ /* !!! FIXME: optionally spin a thread... */
+ do_http((void *) args);
+} /* server_http_request */
+
+
+static int create_listen_socket(short portnum)
+{
+ int retval = -1;
+ int protocol = 0; /* pray this is right. */
+
+#ifndef LACKING_PROTOENT
+ struct protoent *prot;
+ setprotoent(0);
+ prot = getprotobyname("tcp");
+ if (prot != NULL)
+ protocol = prot->p_proto;
+#endif
+
+ retval = socket(PF_INET, SOCK_STREAM, protocol);
+ if (retval >= 0)
+ {
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(portnum);
+ addr.sin_addr.s_addr = INADDR_ANY;
+ if ((bind(retval, &addr, (socklen_t) sizeof (addr)) == -1) ||
+ (listen(retval, 5) == -1))
+ {
+ close(retval);
+ retval = -1;
+ } /* if */
+ } /* if */
+
+ return(retval);
+} /* create_listen_socket */
+
+
+static int listensocket = -1;
+
+void at_exit_cleanup(void)
+{
+ /*
+ * !!! FIXME: If thread support, signal threads to terminate and
+ * !!! FIXME: wait for them to clean up.
+ */
+
+ if (listensocket >= 0)
+ close(listensocket);
+
+ if (!PHYSFS_deinit())
+ printf("PHYSFS_deinit() failed: %s\n", PHYSFS_getLastError());
+} /* at_exit_cleanup */
+
+
+int main(int argc, char **argv)
+{
+ int i;
+ int portnum = DEFAULT_PORTNUM;
+
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+#ifndef LACKING_SIGNALS
+ /* I'm not sure if this qualifies as a cheap trick... */
+ signal(SIGTERM, exit);
+ signal(SIGINT, exit);
+ signal(SIGFPE, exit);
+ signal(SIGSEGV, exit);
+ signal(SIGPIPE, exit);
+ signal(SIGILL, exit);
+#endif
+
+ if (argc == 1)
+ {
+ printf("USAGE: %s <archive1> [archive2 [... archiveN]]\n", argv[0]);
+ return(42);
+ } /* if */
+
+ if (!PHYSFS_init(argv[0]))
+ {
+ printf("PHYSFS_init() failed: %s\n", PHYSFS_getLastError());
+ return(42);
+ } /* if */
+
+ /* normally, this is bad practice, but oh well. */
+ atexit(at_exit_cleanup);
+
+ for (i = 1; i < argc; i++)
+ {
+ if (!PHYSFS_addToSearchPath(argv[i], 1))
+ printf(" WARNING: failed to add [%s] to search path.\n", argv[i]);
+ } /* else */
+
+ listensocket = create_listen_socket(portnum);
+ if (listensocket < 0)
+ {
+ printf("listen socket failed to create.\n");
+ return(42);
+ } /* if */
+
+ while (1) /* infinite loop for now. */
+ {
+ struct sockaddr addr;
+ socklen_t len;
+ int s = accept(listensocket, &addr, &len);
+ if (s < 0)
+ {
+ printf("accept() failed: %s\n", strerror(errno));
+ close(listensocket);
+ return(42);
+ } /* if */
+
+ serve_http_request(s, &addr, len);
+ } /* while */
+
+ return(0);
+} /* main */
+
+/* end of physfshttpd.c ... */
+
--- /dev/null
+/*
+ * This code provides a glue layer between PhysicsFS and Simple Directmedia
+ * Layer's (SDL) RWops i/o abstraction.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes. This code has
+ * NO WARRANTY.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
+ * Please see LICENSE.txt in the root of the source tree.
+ *
+ * SDL falls under the LGPL license. You can get SDL at http://www.libsdl.org/
+ *
+ * This file was written by Ryan C. Gordon. (icculus@icculus.org).
+ */
+
+#include <stdio.h> /* used for SEEK_SET, SEEK_CUR, SEEK_END ... */
+#include "physfsrwops.h"
+
+static int physfsrwops_seek(SDL_RWops *rw, int offset, int whence)
+{
+ PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
+ int pos = 0;
+
+ if (whence == SEEK_SET)
+ {
+ pos = offset;
+ } /* if */
+
+ else if (whence == SEEK_CUR)
+ {
+ PHYSFS_sint64 current = PHYSFS_tell(handle);
+ if (current == -1)
+ {
+ SDL_SetError("Can't find position in file: %s",
+ PHYSFS_getLastError());
+ return(-1);
+ } /* if */
+
+ pos = (int) current;
+ if ( ((PHYSFS_sint64) pos) != current )
+ {
+ SDL_SetError("Can't fit current file position in an int!");
+ return(-1);
+ } /* if */
+
+ if (offset == 0) /* this is a "tell" call. We're done. */
+ return(pos);
+
+ pos += offset;
+ } /* else if */
+
+ else if (whence == SEEK_END)
+ {
+ PHYSFS_sint64 len = PHYSFS_fileLength(handle);
+ if (len == -1)
+ {
+ SDL_SetError("Can't find end of file: %s", PHYSFS_getLastError());
+ return(-1);
+ } /* if */
+
+ pos = (int) len;
+ if ( ((PHYSFS_sint64) pos) != len )
+ {
+ SDL_SetError("Can't fit end-of-file position in an int!");
+ return(-1);
+ } /* if */
+
+ pos += offset;
+ } /* else if */
+
+ else
+ {
+ SDL_SetError("Invalid 'whence' parameter.");
+ return(-1);
+ } /* else */
+
+ if ( pos < 0 )
+ {
+ SDL_SetError("Attempt to seek past start of file.");
+ return(-1);
+ } /* if */
+
+ if (!PHYSFS_seek(handle, (PHYSFS_uint64) pos))
+ {
+ SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+ return(-1);
+ } /* if */
+
+ return(pos);
+} /* physfsrwops_seek */
+
+
+static int physfsrwops_read(SDL_RWops *rw, void *ptr, int size, int maxnum)
+{
+ PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
+ PHYSFS_sint64 rc = PHYSFS_read(handle, ptr, size, maxnum);
+ if (rc != maxnum)
+ {
+ if (!PHYSFS_eof(handle)) /* not EOF? Must be an error. */
+ SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+ } /* if */
+
+ return((int) rc);
+} /* physfsrwops_read */
+
+
+static int physfsrwops_write(SDL_RWops *rw, const void *ptr, int size, int num)
+{
+ PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
+ PHYSFS_sint64 rc = PHYSFS_write(handle, ptr, size, num);
+ if (rc != num)
+ SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+
+ return((int) rc);
+} /* physfsrwops_write */
+
+
+static int physfsrwops_close(SDL_RWops *rw)
+{
+ PHYSFS_File *handle = (PHYSFS_File *) rw->hidden.unknown.data1;
+ if (!PHYSFS_close(handle))
+ {
+ SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+ return(-1);
+ } /* if */
+
+ SDL_FreeRW(rw);
+ return(0);
+} /* physfsrwops_close */
+
+
+static SDL_RWops *create_rwops(PHYSFS_File *handle)
+{
+ SDL_RWops *retval = NULL;
+
+ if (handle == NULL)
+ SDL_SetError("PhysicsFS error: %s", PHYSFS_getLastError());
+ else
+ {
+ retval = SDL_AllocRW();
+ if (retval != NULL)
+ {
+ retval->seek = physfsrwops_seek;
+ retval->read = physfsrwops_read;
+ retval->write = physfsrwops_write;
+ retval->close = physfsrwops_close;
+ retval->hidden.unknown.data1 = handle;
+ } /* if */
+ } /* else */
+
+ return(retval);
+} /* create_rwops */
+
+
+SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_File *handle)
+{
+ SDL_RWops *retval = NULL;
+ if (handle == NULL)
+ SDL_SetError("NULL pointer passed to PHYSFSRWOPS_makeRWops().");
+ else
+ retval = create_rwops(handle);
+
+ return(retval);
+} /* PHYSFSRWOPS_makeRWops */
+
+
+SDL_RWops *PHYSFSRWOPS_openRead(const char *fname)
+{
+ return(create_rwops(PHYSFS_openRead(fname)));
+} /* PHYSFSRWOPS_openRead */
+
+
+SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname)
+{
+ return(create_rwops(PHYSFS_openWrite(fname)));
+} /* PHYSFSRWOPS_openWrite */
+
+
+SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname)
+{
+ return(create_rwops(PHYSFS_openAppend(fname)));
+} /* PHYSFSRWOPS_openAppend */
+
+
+/* end of physfsrwops.c ... */
+
--- /dev/null
+/*
+ * This code provides a glue layer between PhysicsFS and Simple Directmedia
+ * Layer's (SDL) RWops i/o abstraction.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes. This code has
+ * NO WARRANTY.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
+ * Please see LICENSE.txt in the root of the source tree.
+ *
+ * SDL falls under the LGPL license. You can get SDL at http://www.libsdl.org/
+ *
+ * This file was written by Ryan C. Gordon. (icculus@icculus.org).
+ */
+
+#ifndef _INCLUDE_PHYSFSRWOPS_H_
+#define _INCLUDE_PHYSFSRWOPS_H_
+
+#include "physfs.h"
+#include "SDL.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Open a platform-independent filename for reading, and make it accessible
+ * via an SDL_RWops structure. The file will be closed in PhysicsFS when the
+ * RWops is closed. PhysicsFS should be configured to your liking before
+ * opening files through this method.
+ *
+ * @param filename File to open in platform-independent notation.
+ * @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_openRead(const char *fname);
+
+/**
+ * Open a platform-independent filename for writing, and make it accessible
+ * via an SDL_RWops structure. The file will be closed in PhysicsFS when the
+ * RWops is closed. PhysicsFS should be configured to your liking before
+ * opening files through this method.
+ *
+ * @param filename File to open in platform-independent notation.
+ * @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_openWrite(const char *fname);
+
+/**
+ * Open a platform-independent filename for appending, and make it accessible
+ * via an SDL_RWops structure. The file will be closed in PhysicsFS when the
+ * RWops is closed. PhysicsFS should be configured to your liking before
+ * opening files through this method.
+ *
+ * @param filename File to open in platform-independent notation.
+ * @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_openAppend(const char *fname);
+
+/**
+ * Make a SDL_RWops from an existing PhysicsFS file handle. You should
+ * dispose of any references to the handle after successful creation of
+ * the RWops. The actual PhysicsFS handle will be destroyed when the
+ * RWops is closed.
+ *
+ * @param handle a valid PhysicsFS file handle.
+ * @return A valid SDL_RWops structure on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ SDL_RWops *PHYSFSRWOPS_makeRWops(PHYSFS_File *handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* include-once blocker */
+
+/* end of physfsrwops.h ... */
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "physfs.h"
+
+
+static int failure = 0;
+
+static void modTimeToStr(PHYSFS_sint64 modtime, char *modstr, size_t strsize)
+{
+ const char *str = "unknown modtime";
+ if (modtime != -1)
+ {
+ time_t t = (time_t) modtime;
+ str = ctime(&t);
+ } /* if */
+
+ strncpy(modstr, str, strsize);
+ modstr[strsize-1] = '\0';
+ strsize = strlen(modstr);
+ while ((modstr[strsize-1] == '\n') || (modstr[strsize-1] == '\r'))
+ modstr[--strsize] = '\0';
+} /* modTimeToStr */
+
+
+static void fail(const char *what, const char *why)
+{
+ if (why == NULL)
+ why = PHYSFS_getLastError();
+ fprintf(stderr, "%s failed: %s\n", what, why);
+ failure = 1;
+} /* fail */
+
+
+static void dumpFile(const char *fname)
+{
+ const int origfailure = failure;
+ PHYSFS_File *out = NULL;
+ PHYSFS_File *in = NULL;
+
+ failure = 0;
+
+ if ((in = PHYSFS_openRead(fname)) == NULL)
+ fail("\nPHYSFS_openRead", NULL);
+ else if ((out = PHYSFS_openWrite(fname)) == NULL)
+ fail("\nPHYSFS_openWrite", NULL);
+ else
+ {
+ char modstr[64];
+ PHYSFS_sint64 size = PHYSFS_fileLength(in);
+
+ printf("(");
+ if (size == -1)
+ printf("?");
+ else
+ printf("%lld", (long long) size);
+ printf(" bytes");
+
+ modTimeToStr(PHYSFS_getLastModTime(fname), modstr, sizeof (modstr));
+ printf(", %s)\n", modstr);
+
+ while ( (!failure) && (!PHYSFS_eof(in)) )
+ {
+ static char buf[64 * 1024];
+ PHYSFS_sint64 br = PHYSFS_read(in, buf, 1, sizeof (buf));
+ if (br == -1)
+ fail("PHYSFS_read", NULL);
+ else
+ {
+ PHYSFS_sint64 bw = PHYSFS_write(out, buf, 1, br);
+ if (bw != br)
+ fail("PHYSFS_write", NULL);
+ else
+ size -= bw;
+ } /* else */
+ } /* while */
+
+ if ((!failure) && (size != 0))
+ fail("PHYSFS_eof", "BUG! eof != PHYSFS_fileLength bytes!");
+ } /* else */
+
+ if (in != NULL)
+ PHYSFS_close(in);
+
+ if (out != NULL)
+ {
+ if (!PHYSFS_close(out))
+ fail("PHYSFS_close", NULL);
+ } /* if */
+
+ if (failure)
+ PHYSFS_delete(fname);
+ else
+ failure = origfailure;
+} /* dumpFile */
+
+
+static void unpackCallback(void *_depth, const char *origdir, const char *str)
+{
+ int depth = *((int *) _depth);
+ const int len = strlen(origdir) + strlen(str) + 2;
+ char *fname = (char *) malloc(len);
+ if (fname == NULL)
+ fail("malloc", "Out of memory!");
+ else
+ {
+ if (strcmp(origdir, "/") == 0)
+ origdir = "";
+
+ snprintf(fname, len, "%s/%s", origdir, str);
+
+ printf("%s ", fname);
+ if (PHYSFS_isDirectory(fname))
+ {
+ depth++;
+ printf("(directory)\n");
+ if (!PHYSFS_mkdir(fname))
+ fail("PHYSFS_mkdir", NULL);
+ else
+ PHYSFS_enumerateFilesCallback(fname, unpackCallback, &depth);
+ } /* if */
+
+ else if (PHYSFS_isSymbolicLink(fname))
+ {
+ printf("(symlink)\n");
+ /* !!! FIXME: ? if (!symlink(fname, */
+ } /* else if */
+
+ else /* ...file. */
+ {
+ dumpFile(fname);
+ } /* else */
+
+ free(fname);
+ } /* else */
+} /* unpackCallback */
+
+
+int main(int argc, char **argv)
+{
+ int zero = 0;
+
+ if (argc != 3)
+ {
+ fprintf(stderr, "USAGE: %s <archive> <unpackDirectory>\n", argv[0]);
+ return 1;
+ } /* if */
+
+ if (!PHYSFS_init(argv[0]))
+ {
+ fprintf(stderr, "PHYSFS_init() failed: %s\n", PHYSFS_getLastError());
+ return 2;
+ } /* if */
+
+ if (!PHYSFS_setWriteDir(argv[2]))
+ {
+ fprintf(stderr, "PHYSFS_setWriteDir('%s') failed: %s\n",
+ argv[2], PHYSFS_getLastError());
+ return 3;
+ } /* if */
+
+ if (!PHYSFS_mount(argv[1], NULL, 1))
+ {
+ fprintf(stderr, "PHYSFS_mount('%s') failed: %s\n",
+ argv[1], PHYSFS_getLastError());
+ return 4;
+ } /* if */
+
+ PHYSFS_permitSymbolicLinks(1);
+ PHYSFS_enumerateFilesCallback("/", unpackCallback, &zero);
+ PHYSFS_deinit();
+ if (failure)
+ return 5;
+
+ return 0;
+} /* main */
+
+/* end of physfsunpack.c ... */
+
--- /dev/null
+/*
+ * This code shows how to read a zipfile included in an app's binary.
+ *
+ * License: this code is public domain. I make no warranty that it is useful,
+ * correct, harmless, or environmentally safe.
+ *
+ * This particular file may be used however you like, including copying it
+ * verbatim into a closed-source project, exploiting it commercially, and
+ * removing any trace of my name from the source (although I hope you won't
+ * do that). I welcome enhancements and corrections to this file, but I do
+ * not require you to send me patches if you make changes. This code has
+ * NO WARRANTY.
+ *
+ * Unless otherwise stated, the rest of PhysicsFS falls under the zlib license.
+ * Please see LICENSE.txt in the root of the source tree.
+ *
+ * This file was written by Ryan C. Gordon. (icculus@icculus.org).
+ */
+
+/*
+ * Compile this program and then attach a .zip file to the end of the
+ * compiled binary.
+ *
+ * On Linux, something like this will build the final binary:
+ * gcc -o selfextract.tmp selfextract.c -lphysfs && \
+ * cat selfextract.tmp myzipfile.zip >> selfextract && \
+ * chmod a+x selfextract && \
+ * rm -f selfextract.tmp
+ *
+ * This may not work on all platforms, and it probably only works with
+ * .zip files, since they are designed to be appended to another file.
+ */
+
+#include <stdio.h>
+#include "physfs.h"
+
+int main(int argc, char **argv)
+{
+ int rc = 0;
+
+ if (!PHYSFS_init(argv[0]))
+ {
+ printf("PHYSFS_init() failed: %s\n", PHYSFS_getLastError());
+ return(42);
+ } /* if */
+
+ rc = PHYSFS_addToSearchPath(argv[0], 0);
+ if (!rc)
+ {
+ printf("Couldn't find self-extract data: %s\n", PHYSFS_getLastError());
+ printf("This might mean you didn't append a zipfile to the binary.\n");
+ return(42);
+ } /* if */
+
+ char **files = PHYSFS_enumerateFiles("/");
+ char **i;
+ for (i = files; *i != NULL; i++)
+ {
+ const char *dirorfile = PHYSFS_isDirectory(*i) ? "Directory" : "File";
+ printf(" * %s '%s' is in root of attached data.\n", dirorfile, *i);
+ } /* for */
+ PHYSFS_freeList(files);
+
+ return(0);
+} /* main */
+
--- /dev/null
+/* 7zAlloc.h */
+
+#ifndef __7Z_ALLOC_H
+#define __7Z_ALLOC_H
+
+#include <stddef.h>
+
+typedef struct _ISzAlloc
+{
+ void *(*Alloc)(size_t size);
+ void (*Free)(void *address); /* address can be 0 */
+} ISzAlloc;
+
+void *SzAlloc(size_t size);
+void SzFree(void *address);
+
+void *SzAllocTemp(size_t size);
+void SzFreeTemp(void *address);
+
+#endif
--- /dev/null
+/* 7zBuffer.c */
+
+#include "7zBuffer.h"
+#include "7zAlloc.h"
+
+void SzByteBufferInit(CSzByteBuffer *buffer)
+{
+ buffer->Capacity = 0;
+ buffer->Items = 0;
+}
+
+int SzByteBufferCreate(CSzByteBuffer *buffer, size_t newCapacity, void * (*allocFunc)(size_t size))
+{
+ buffer->Capacity = newCapacity;
+ if (newCapacity == 0)
+ {
+ buffer->Items = 0;
+ return 1;
+ }
+ buffer->Items = (Byte *)allocFunc(newCapacity);
+ return (buffer->Items != 0);
+}
+
+void SzByteBufferFree(CSzByteBuffer *buffer, void (*freeFunc)(void *))
+{
+ freeFunc(buffer->Items);
+ buffer->Items = 0;
+ buffer->Capacity = 0;
+}
--- /dev/null
+/* 7zBuffer.h */
+
+#ifndef __7Z_BUFFER_H
+#define __7Z_BUFFER_H
+
+#include <stddef.h>
+#include "7zTypes.h"
+
+typedef struct _CSzByteBuffer
+{
+ size_t Capacity;
+ Byte *Items;
+}CSzByteBuffer;
+
+void SzByteBufferInit(CSzByteBuffer *buffer);
+int SzByteBufferCreate(CSzByteBuffer *buffer, size_t newCapacity, void * (*allocFunc)(size_t size));
+void SzByteBufferFree(CSzByteBuffer *buffer, void (*freeFunc)(void *));
+
+#endif
--- /dev/null
+/* 7zCrc.c */
+
+#include "7zCrc.h"
+
+#define kCrcPoly 0xEDB88320
+
+UInt32 g_CrcTable[256];
+
+void InitCrcTable()
+{
+ UInt32 i;
+ for (i = 0; i < 256; i++)
+ {
+ UInt32 r = i;
+ int j;
+ for (j = 0; j < 8; j++)
+ if (r & 1)
+ r = (r >> 1) ^ kCrcPoly;
+ else
+ r >>= 1;
+ g_CrcTable[i] = r;
+ }
+}
+
+void CrcInit(UInt32 *crc) { *crc = 0xFFFFFFFF; }
+UInt32 CrcGetDigest(UInt32 *crc) { return *crc ^ 0xFFFFFFFF; }
+
+void CrcUpdateByte(UInt32 *crc, Byte b)
+{
+ *crc = g_CrcTable[((Byte)(*crc)) ^ b] ^ (*crc >> 8);
+}
+
+void CrcUpdateUInt16(UInt32 *crc, UInt16 v)
+{
+ CrcUpdateByte(crc, (Byte)v);
+ CrcUpdateByte(crc, (Byte)(v >> 8));
+}
+
+void CrcUpdateUInt32(UInt32 *crc, UInt32 v)
+{
+ int i;
+ for (i = 0; i < 4; i++)
+ CrcUpdateByte(crc, (Byte)(v >> (8 * i)));
+}
+
+void CrcUpdateUInt64(UInt32 *crc, UInt64 v)
+{
+ int i;
+ for (i = 0; i < 8; i++)
+ {
+ CrcUpdateByte(crc, (Byte)(v));
+ v >>= 8;
+ }
+}
+
+void CrcUpdate(UInt32 *crc, const void *data, size_t size)
+{
+ UInt32 v = *crc;
+ const Byte *p = (const Byte *)data;
+ for (; size > 0 ; size--, p++)
+ v = g_CrcTable[((Byte)(v)) ^ *p] ^ (v >> 8);
+ *crc = v;
+}
+
+UInt32 CrcCalculateDigest(const void *data, size_t size)
+{
+ UInt32 crc;
+ CrcInit(&crc);
+ CrcUpdate(&crc, data, size);
+ return CrcGetDigest(&crc);
+}
+
+int CrcVerifyDigest(UInt32 digest, const void *data, size_t size)
+{
+ return (CrcCalculateDigest(data, size) == digest);
+}
--- /dev/null
+/* 7zCrc.h */
+
+#ifndef __7Z_CRC_H
+#define __7Z_CRC_H
+
+#include <stddef.h>
+
+#include "7zTypes.h"
+
+extern UInt32 g_CrcTable[256];
+void InitCrcTable();
+
+void CrcInit(UInt32 *crc);
+UInt32 CrcGetDigest(UInt32 *crc);
+void CrcUpdateByte(UInt32 *crc, Byte v);
+void CrcUpdateUInt16(UInt32 *crc, UInt16 v);
+void CrcUpdateUInt32(UInt32 *crc, UInt32 v);
+void CrcUpdateUInt64(UInt32 *crc, UInt64 v);
+void CrcUpdate(UInt32 *crc, const void *data, size_t size);
+
+UInt32 CrcCalculateDigest(const void *data, size_t size);
+int CrcVerifyDigest(UInt32 digest, const void *data, size_t size);
+
+#endif
--- /dev/null
+/* 7zDecode.c */
+
+#include "7zDecode.h"
+#ifdef _SZ_ONE_DIRECTORY
+#include "LzmaDecode.h"
+#else
+#include "../../Compress/LZMA_C/LzmaDecode.h"
+#endif
+
+CMethodID k_Copy = { { 0x0 }, 1 };
+CMethodID k_LZMA = { { 0x3, 0x1, 0x1 }, 3 };
+
+#ifdef _LZMA_IN_CB
+
+typedef struct _CLzmaInCallbackImp
+{
+ ILzmaInCallback InCallback;
+ ISzInStream *InStream;
+ size_t Size;
+} CLzmaInCallbackImp;
+
+int LzmaReadImp(void *object, const unsigned char **buffer, SizeT *size)
+{
+ CLzmaInCallbackImp *cb = (CLzmaInCallbackImp *)object;
+ size_t processedSize;
+ SZ_RESULT res;
+ *size = 0;
+ res = cb->InStream->Read((void *)cb->InStream, (void **)buffer, cb->Size, &processedSize);
+ *size = (SizeT)processedSize;
+ if (processedSize > cb->Size)
+ return (int)SZE_FAIL;
+ cb->Size -= processedSize;
+ if (res == SZ_OK)
+ return 0;
+ return (int)res;
+}
+
+#endif
+
+SZ_RESULT SzDecode(const CFileSize *packSizes, const CFolder *folder,
+ #ifdef _LZMA_IN_CB
+ ISzInStream *inStream,
+ #else
+ const Byte *inBuffer,
+ #endif
+ Byte *outBuffer, size_t outSize,
+ size_t *outSizeProcessed, ISzAlloc *allocMain)
+{
+ UInt32 si;
+ size_t inSize = 0;
+ CCoderInfo *coder;
+ if (folder->NumPackStreams != 1)
+ return SZE_NOTIMPL;
+ if (folder->NumCoders != 1)
+ return SZE_NOTIMPL;
+ coder = folder->Coders;
+ *outSizeProcessed = 0;
+
+ for (si = 0; si < folder->NumPackStreams; si++)
+ inSize += (size_t)packSizes[si];
+
+ if (AreMethodsEqual(&coder->MethodID, &k_Copy))
+ {
+ size_t i;
+ if (inSize != outSize)
+ return SZE_DATA_ERROR;
+ #ifdef _LZMA_IN_CB
+ for (i = 0; i < inSize;)
+ {
+ size_t j;
+ void *inBuffer;
+ size_t bufferSize;
+ RINOK(inStream->Read((void *)inStream, (void **)&inBuffer, inSize - i, &bufferSize));
+ if (bufferSize == 0)
+ return SZE_DATA_ERROR;
+ if (bufferSize > inSize - i)
+ return SZE_FAIL;
+ *outSizeProcessed += bufferSize;
+ for (j = 0; j < bufferSize && i < inSize; j++, i++)
+ outBuffer[i] = ((Byte*)inBuffer)[j];
+ }
+ #else
+ for (i = 0; i < inSize; i++)
+ outBuffer[i] = inBuffer[i];
+ *outSizeProcessed = inSize;
+ #endif
+ return SZ_OK;
+ }
+
+ if (AreMethodsEqual(&coder->MethodID, &k_LZMA))
+ {
+ #ifdef _LZMA_IN_CB
+ CLzmaInCallbackImp lzmaCallback;
+ #else
+ SizeT inProcessed;
+ #endif
+
+ CLzmaDecoderState state; /* it's about 24-80 bytes structure, if int is 32-bit */
+ int result;
+ SizeT outSizeProcessedLoc;
+
+ #ifdef _LZMA_IN_CB
+ lzmaCallback.Size = inSize;
+ lzmaCallback.InStream = inStream;
+ lzmaCallback.InCallback.Read = LzmaReadImp;
+ #endif
+
+ if (LzmaDecodeProperties(&state.Properties, coder->Properties.Items,
+ coder->Properties.Capacity) != LZMA_RESULT_OK)
+ return SZE_FAIL;
+
+ state.Probs = (CProb *)allocMain->Alloc(LzmaGetNumProbs(&state.Properties) * sizeof(CProb));
+ if (state.Probs == 0)
+ return SZE_OUTOFMEMORY;
+
+ #ifdef _LZMA_OUT_READ
+ if (state.Properties.DictionarySize == 0)
+ state.Dictionary = 0;
+ else
+ {
+ state.Dictionary = (unsigned char *)allocMain->Alloc(state.Properties.DictionarySize);
+ if (state.Dictionary == 0)
+ {
+ allocMain->Free(state.Probs);
+ return SZE_OUTOFMEMORY;
+ }
+ }
+ LzmaDecoderInit(&state);
+ #endif
+
+ result = LzmaDecode(&state,
+ #ifdef _LZMA_IN_CB
+ &lzmaCallback.InCallback,
+ #else
+ inBuffer, (SizeT)inSize, &inProcessed,
+ #endif
+ outBuffer, (SizeT)outSize, &outSizeProcessedLoc);
+ *outSizeProcessed = (size_t)outSizeProcessedLoc;
+ allocMain->Free(state.Probs);
+ #ifdef _LZMA_OUT_READ
+ allocMain->Free(state.Dictionary);
+ #endif
+ if (result == LZMA_RESULT_DATA_ERROR)
+ return SZE_DATA_ERROR;
+ if (result != LZMA_RESULT_OK)
+ return SZE_FAIL;
+ return SZ_OK;
+ }
+ return SZE_NOTIMPL;
+}
--- /dev/null
+/* 7zDecode.h */
+
+#ifndef __7Z_DECODE_H
+#define __7Z_DECODE_H
+
+#include "7zItem.h"
+#include "7zAlloc.h"
+#ifdef _LZMA_IN_CB
+#include "7zIn.h"
+#endif
+
+SZ_RESULT SzDecode(const CFileSize *packSizes, const CFolder *folder,
+ #ifdef _LZMA_IN_CB
+ ISzInStream *stream,
+ #else
+ const Byte *inBuffer,
+ #endif
+ Byte *outBuffer, size_t outSize,
+ size_t *outSizeProcessed, ISzAlloc *allocMain);
+
+#endif
--- /dev/null
+/* 7zExtract.c */
+
+#include "7zExtract.h"
+#include "7zDecode.h"
+#include "7zCrc.h"
+
+SZ_RESULT SzExtract(
+ ISzInStream *inStream,
+ CArchiveDatabaseEx *db,
+ UInt32 fileIndex,
+ UInt32 *blockIndex,
+ Byte **outBuffer,
+ size_t *outBufferSize,
+ size_t *offset,
+ size_t *outSizeProcessed,
+ ISzAlloc *allocMain,
+ ISzAlloc *allocTemp)
+{
+ UInt32 folderIndex = db->FileIndexToFolderIndexMap[fileIndex];
+ SZ_RESULT res = SZ_OK;
+ *offset = 0;
+ *outSizeProcessed = 0;
+ if (folderIndex == (UInt32)-1)
+ {
+ allocMain->Free(*outBuffer);
+ *blockIndex = folderIndex;
+ *outBuffer = 0;
+ *outBufferSize = 0;
+ return SZ_OK;
+ }
+
+ if (*outBuffer == 0 || *blockIndex != folderIndex)
+ {
+ CFolder *folder = db->Database.Folders + folderIndex;
+ CFileSize unPackSize = SzFolderGetUnPackSize(folder);
+ #ifndef _LZMA_IN_CB
+ CFileSize packSize = SzArDbGetFolderFullPackSize(db, folderIndex);
+ Byte *inBuffer = 0;
+ size_t processedSize;
+ #endif
+ *blockIndex = folderIndex;
+ allocMain->Free(*outBuffer);
+ *outBuffer = 0;
+
+ RINOK(inStream->Seek(inStream, SzArDbGetFolderStreamPos(db, folderIndex, 0)));
+
+ #ifndef _LZMA_IN_CB
+ if (packSize != 0)
+ {
+ inBuffer = (Byte *)allocTemp->Alloc((size_t)packSize);
+ if (inBuffer == 0)
+ return SZE_OUTOFMEMORY;
+ }
+ res = inStream->Read(inStream, inBuffer, (size_t)packSize, &processedSize);
+ if (res == SZ_OK && processedSize != (size_t)packSize)
+ res = SZE_FAIL;
+ #endif
+ if (res == SZ_OK)
+ {
+ *outBufferSize = (size_t)unPackSize;
+ if (unPackSize != 0)
+ {
+ *outBuffer = (Byte *)allocMain->Alloc((size_t)unPackSize);
+ if (*outBuffer == 0)
+ res = SZE_OUTOFMEMORY;
+ }
+ if (res == SZ_OK)
+ {
+ size_t outRealSize;
+ res = SzDecode(db->Database.PackSizes +
+ db->FolderStartPackStreamIndex[folderIndex], folder,
+ #ifdef _LZMA_IN_CB
+ inStream,
+ #else
+ inBuffer,
+ #endif
+ *outBuffer, (size_t)unPackSize, &outRealSize, allocTemp);
+ if (res == SZ_OK)
+ {
+ if (outRealSize == (size_t)unPackSize)
+ {
+ if (folder->UnPackCRCDefined)
+ {
+ if (!CrcVerifyDigest(folder->UnPackCRC, *outBuffer, (size_t)unPackSize))
+ res = SZE_FAIL;
+ }
+ }
+ else
+ res = SZE_FAIL;
+ }
+ }
+ }
+ #ifndef _LZMA_IN_CB
+ allocTemp->Free(inBuffer);
+ #endif
+ }
+ if (res == SZ_OK)
+ {
+ UInt32 i;
+ CFileItem *fileItem = db->Database.Files + fileIndex;
+ *offset = 0;
+ for(i = db->FolderStartFileIndex[folderIndex]; i < fileIndex; i++)
+ *offset += (UInt32)db->Database.Files[i].Size;
+ *outSizeProcessed = (size_t)fileItem->Size;
+ if (*offset + *outSizeProcessed > *outBufferSize)
+ return SZE_FAIL;
+ {
+ if (fileItem->IsFileCRCDefined)
+ {
+ if (!CrcVerifyDigest(fileItem->FileCRC, *outBuffer + *offset, *outSizeProcessed))
+ res = SZE_FAIL;
+ }
+ }
+ }
+ return res;
+}
--- /dev/null
+/* 7zExtract.h */
+
+#ifndef __7Z_EXTRACT_H
+#define __7Z_EXTRACT_H
+
+#include "7zIn.h"
+
+/*
+ SzExtract extracts file from archive
+
+ *outBuffer must be 0 before first call for each new archive.
+
+ Extracting cache:
+ If you need to decompress more than one file, you can send
+ these values from previous call:
+ *blockIndex,
+ *outBuffer,
+ *outBufferSize
+ You can consider "*outBuffer" as cache of solid block. If your archive is solid,
+ it will increase decompression speed.
+
+ If you use external function, you can declare these 3 cache variables
+ (blockIndex, outBuffer, outBufferSize) as static in that external function.
+
+ Free *outBuffer and set *outBuffer to 0, if you want to flush cache.
+*/
+
+SZ_RESULT SzExtract(
+ ISzInStream *inStream,
+ CArchiveDatabaseEx *db,
+ UInt32 fileIndex, /* index of file */
+ UInt32 *blockIndex, /* index of solid block */
+ Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */
+ size_t *outBufferSize, /* buffer size for output buffer */
+ size_t *offset, /* offset of stream for required file in *outBuffer */
+ size_t *outSizeProcessed, /* size of file in *outBuffer */
+ ISzAlloc *allocMain,
+ ISzAlloc *allocTemp);
+
+#endif
--- /dev/null
+/* 7zHeader.c */
+
+#include "7zHeader.h"
+
+Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C};
--- /dev/null
+/* 7zHeader.h */
+
+#ifndef __7Z_HEADER_H
+#define __7Z_HEADER_H
+
+#include "7zTypes.h"
+
+#define k7zSignatureSize 6
+extern Byte k7zSignature[k7zSignatureSize];
+
+#define k7zMajorVersion 0
+
+#define k7zStartHeaderSize 0x20
+
+enum EIdEnum
+{
+ k7zIdEnd,
+
+ k7zIdHeader,
+
+ k7zIdArchiveProperties,
+
+ k7zIdAdditionalStreamsInfo,
+ k7zIdMainStreamsInfo,
+ k7zIdFilesInfo,
+
+ k7zIdPackInfo,
+ k7zIdUnPackInfo,
+ k7zIdSubStreamsInfo,
+
+ k7zIdSize,
+ k7zIdCRC,
+
+ k7zIdFolder,
+
+ k7zIdCodersUnPackSize,
+ k7zIdNumUnPackStream,
+
+ k7zIdEmptyStream,
+ k7zIdEmptyFile,
+ k7zIdAnti,
+
+ k7zIdName,
+ k7zIdCreationTime,
+ k7zIdLastAccessTime,
+ k7zIdLastWriteTime,
+ k7zIdWinAttributes,
+ k7zIdComment,
+
+ k7zIdEncodedHeader,
+
+ k7zIdStartPos
+};
+
+#endif
--- /dev/null
+/* 7zIn.c */
+
+#include "7zIn.h"
+#include "7zCrc.h"
+#include "7zDecode.h"
+
+#define RINOM(x) { if((x) == 0) return SZE_OUTOFMEMORY; }
+
+void SzArDbExInit(CArchiveDatabaseEx *db)
+{
+ SzArchiveDatabaseInit(&db->Database);
+ db->FolderStartPackStreamIndex = 0;
+ db->PackStreamStartPositions = 0;
+ db->FolderStartFileIndex = 0;
+ db->FileIndexToFolderIndexMap = 0;
+}
+
+void SzArDbExFree(CArchiveDatabaseEx *db, void (*freeFunc)(void *))
+{
+ freeFunc(db->FolderStartPackStreamIndex);
+ freeFunc(db->PackStreamStartPositions);
+ freeFunc(db->FolderStartFileIndex);
+ freeFunc(db->FileIndexToFolderIndexMap);
+ SzArchiveDatabaseFree(&db->Database, freeFunc);
+ SzArDbExInit(db);
+}
+
+/*
+CFileSize GetFolderPackStreamSize(int folderIndex, int streamIndex) const
+{
+ return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex];
+}
+
+CFileSize GetFilePackSize(int fileIndex) const
+{
+ int folderIndex = FileIndexToFolderIndexMap[fileIndex];
+ if (folderIndex >= 0)
+ {
+ const CFolder &folderInfo = Folders[folderIndex];
+ if (FolderStartFileIndex[folderIndex] == fileIndex)
+ return GetFolderFullPackSize(folderIndex);
+ }
+ return 0;
+}
+*/
+
+#define MY_ALLOC(T, p, size, allocFunc) { if ((size) == 0) p = 0; else \
+ if ((p = (T *)allocFunc((size) * sizeof(T))) == 0) return SZE_OUTOFMEMORY; }
+
+SZ_RESULT SzArDbExFill(CArchiveDatabaseEx *db, void * (*allocFunc)(size_t size))
+{
+ UInt32 startPos = 0;
+ CFileSize startPosSize = 0;
+ UInt32 i;
+ UInt32 folderIndex = 0;
+ UInt32 indexInFolder = 0;
+ MY_ALLOC(UInt32, db->FolderStartPackStreamIndex, db->Database.NumFolders, allocFunc);
+ for(i = 0; i < db->Database.NumFolders; i++)
+ {
+ db->FolderStartPackStreamIndex[i] = startPos;
+ startPos += db->Database.Folders[i].NumPackStreams;
+ }
+
+ MY_ALLOC(CFileSize, db->PackStreamStartPositions, db->Database.NumPackStreams, allocFunc);
+
+ for(i = 0; i < db->Database.NumPackStreams; i++)
+ {
+ db->PackStreamStartPositions[i] = startPosSize;
+ startPosSize += db->Database.PackSizes[i];
+ }
+
+ MY_ALLOC(UInt32, db->FolderStartFileIndex, db->Database.NumFolders, allocFunc);
+ MY_ALLOC(UInt32, db->FileIndexToFolderIndexMap, db->Database.NumFiles, allocFunc);
+
+ for (i = 0; i < db->Database.NumFiles; i++)
+ {
+ CFileItem *file = db->Database.Files + i;
+ int emptyStream = !file->HasStream;
+ if (emptyStream && indexInFolder == 0)
+ {
+ db->FileIndexToFolderIndexMap[i] = (UInt32)-1;
+ continue;
+ }
+ if (indexInFolder == 0)
+ {
+ /*
+ v3.13 incorrectly worked with empty folders
+ v4.07: Loop for skipping empty folders
+ */
+ while(1)
+ {
+ if (folderIndex >= db->Database.NumFolders)
+ return SZE_ARCHIVE_ERROR;
+ db->FolderStartFileIndex[folderIndex] = i;
+ if (db->Database.Folders[folderIndex].NumUnPackStreams != 0)
+ break;
+ folderIndex++;
+ }
+ }
+ db->FileIndexToFolderIndexMap[i] = folderIndex;
+ if (emptyStream)
+ continue;
+ indexInFolder++;
+ if (indexInFolder >= db->Database.Folders[folderIndex].NumUnPackStreams)
+ {
+ folderIndex++;
+ indexInFolder = 0;
+ }
+ }
+ return SZ_OK;
+}
+
+
+CFileSize SzArDbGetFolderStreamPos(CArchiveDatabaseEx *db, UInt32 folderIndex, UInt32 indexInFolder)
+{
+ return db->ArchiveInfo.DataStartPosition +
+ db->PackStreamStartPositions[db->FolderStartPackStreamIndex[folderIndex] + indexInFolder];
+}
+
+CFileSize SzArDbGetFolderFullPackSize(CArchiveDatabaseEx *db, UInt32 folderIndex)
+{
+ UInt32 packStreamIndex = db->FolderStartPackStreamIndex[folderIndex];
+ CFolder *folder = db->Database.Folders + folderIndex;
+ CFileSize size = 0;
+ UInt32 i;
+ for (i = 0; i < folder->NumPackStreams; i++)
+ size += db->Database.PackSizes[packStreamIndex + i];
+ return size;
+}
+
+
+/*
+SZ_RESULT SzReadTime(const CObjectVector<CSzByteBuffer> &dataVector,
+ CObjectVector<CFileItem> &files, UInt64 type)
+{
+ CBoolVector boolVector;
+ RINOK(ReadBoolVector2(files.Size(), boolVector))
+
+ CStreamSwitch streamSwitch;
+ RINOK(streamSwitch.Set(this, &dataVector));
+
+ for(int i = 0; i < files.Size(); i++)
+ {
+ CFileItem &file = files[i];
+ CArchiveFileTime fileTime;
+ bool defined = boolVector[i];
+ if (defined)
+ {
+ UInt32 low, high;
+ RINOK(SzReadUInt32(low));
+ RINOK(SzReadUInt32(high));
+ fileTime.dwLowDateTime = low;
+ fileTime.dwHighDateTime = high;
+ }
+ switch(type)
+ {
+ case k7zIdCreationTime:
+ file.IsCreationTimeDefined = defined;
+ if (defined)
+ file.CreationTime = fileTime;
+ break;
+ case k7zIdLastWriteTime:
+ file.IsLastWriteTimeDefined = defined;
+ if (defined)
+ file.LastWriteTime = fileTime;
+ break;
+ case k7zIdLastAccessTime:
+ file.IsLastAccessTimeDefined = defined;
+ if (defined)
+ file.LastAccessTime = fileTime;
+ break;
+ }
+ }
+ return SZ_OK;
+}
+*/
+
+SZ_RESULT SafeReadDirect(ISzInStream *inStream, Byte *data, size_t size)
+{
+ #ifdef _LZMA_IN_CB
+ while (size > 0)
+ {
+ void *inBuffer; // Dennis Schridde: Make this compile with -Wall -Werror. Was Byte* before.
+ size_t processedSize;
+ RINOK(inStream->Read(inStream, (void **)&inBuffer, size, &processedSize));
+ if (processedSize == 0 || processedSize > size)
+ return SZE_FAIL;
+ size -= processedSize;
+ do
+ {
+ *(data++) = *((Byte*)inBuffer);
+ inBuffer = ((Byte*) inBuffer) + 1;
+ }
+ while (--processedSize != 0);
+ }
+ #else
+ size_t processedSize;
+ RINOK(inStream->Read(inStream, data, size, &processedSize));
+ if (processedSize != size)
+ return SZE_FAIL;
+ #endif
+ return SZ_OK;
+}
+
+SZ_RESULT SafeReadDirectByte(ISzInStream *inStream, Byte *data)
+{
+ return SafeReadDirect(inStream, data, 1);
+}
+
+SZ_RESULT SafeReadDirectUInt32(ISzInStream *inStream, UInt32 *value)
+{
+ int i;
+ *value = 0;
+ for (i = 0; i < 4; i++)
+ {
+ Byte b;
+ RINOK(SafeReadDirectByte(inStream, &b));
+ *value |= ((UInt32)b << (8 * i));
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SafeReadDirectUInt64(ISzInStream *inStream, UInt64 *value)
+{
+ int i;
+ *value = 0;
+ for (i = 0; i < 8; i++)
+ {
+ Byte b;
+ RINOK(SafeReadDirectByte(inStream, &b));
+ *value |= ((UInt32)b << (8 * i));
+ }
+ return SZ_OK;
+}
+
+int TestSignatureCandidate(Byte *testBytes)
+{
+ size_t i;
+ for (i = 0; i < k7zSignatureSize; i++)
+ if (testBytes[i] != k7zSignature[i])
+ return 0;
+ return 1;
+}
+
+typedef struct _CSzState
+{
+ Byte *Data;
+ size_t Size;
+}CSzData;
+
+SZ_RESULT SzReadByte(CSzData *sd, Byte *b)
+{
+ if (sd->Size == 0)
+ return SZE_ARCHIVE_ERROR;
+ sd->Size--;
+ *b = *sd->Data++;
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadBytes(CSzData *sd, Byte *data, size_t size)
+{
+ size_t i;
+ for (i = 0; i < size; i++)
+ {
+ RINOK(SzReadByte(sd, data + i));
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadUInt32(CSzData *sd, UInt32 *value)
+{
+ int i;
+ *value = 0;
+ for (i = 0; i < 4; i++)
+ {
+ Byte b;
+ RINOK(SzReadByte(sd, &b));
+ *value |= ((UInt32)(b) << (8 * i));
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadNumber(CSzData *sd, UInt64 *value)
+{
+ Byte firstByte;
+ Byte mask = 0x80;
+ int i;
+ RINOK(SzReadByte(sd, &firstByte));
+ *value = 0;
+ for (i = 0; i < 8; i++)
+ {
+ Byte b;
+ if ((firstByte & mask) == 0)
+ {
+ UInt64 highPart = firstByte & (mask - 1);
+ *value += (highPart << (8 * i));
+ return SZ_OK;
+ }
+ RINOK(SzReadByte(sd, &b));
+ *value |= ((UInt64)b << (8 * i));
+ mask >>= 1;
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadSize(CSzData *sd, CFileSize *value)
+{
+ UInt64 value64;
+ RINOK(SzReadNumber(sd, &value64));
+ *value = (CFileSize)value64;
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadNumber32(CSzData *sd, UInt32 *value)
+{
+ UInt64 value64;
+ RINOK(SzReadNumber(sd, &value64));
+ if (value64 >= 0x80000000)
+ return SZE_NOTIMPL;
+ if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 2)))
+ return SZE_NOTIMPL;
+ *value = (UInt32)value64;
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadID(CSzData *sd, UInt64 *value)
+{
+ return SzReadNumber(sd, value);
+}
+
+SZ_RESULT SzSkeepDataSize(CSzData *sd, UInt64 size)
+{
+ if (size > sd->Size)
+ return SZE_ARCHIVE_ERROR;
+ sd->Size -= (size_t)size;
+ sd->Data += (size_t)size;
+ return SZ_OK;
+}
+
+SZ_RESULT SzSkeepData(CSzData *sd)
+{
+ UInt64 size;
+ RINOK(SzReadNumber(sd, &size));
+ return SzSkeepDataSize(sd, size);
+}
+
+SZ_RESULT SzReadArchiveProperties(CSzData *sd)
+{
+ while(1)
+ {
+ UInt64 type;
+ RINOK(SzReadID(sd, &type));
+ if (type == k7zIdEnd)
+ break;
+ SzSkeepData(sd);
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SzWaitAttribute(CSzData *sd, UInt64 attribute)
+{
+ while(1)
+ {
+ UInt64 type;
+ RINOK(SzReadID(sd, &type));
+ if (type == attribute)
+ return SZ_OK;
+ if (type == k7zIdEnd)
+ return SZE_ARCHIVE_ERROR;
+ RINOK(SzSkeepData(sd));
+ }
+}
+
+SZ_RESULT SzReadBoolVector(CSzData *sd, size_t numItems, Byte **v, void * (*allocFunc)(size_t size))
+{
+ Byte b = 0;
+ Byte mask = 0;
+ size_t i;
+ MY_ALLOC(Byte, *v, numItems, allocFunc);
+ for(i = 0; i < numItems; i++)
+ {
+ if (mask == 0)
+ {
+ RINOK(SzReadByte(sd, &b));
+ mask = 0x80;
+ }
+ (*v)[i] = (Byte)(((b & mask) != 0) ? 1 : 0);
+ mask >>= 1;
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadBoolVector2(CSzData *sd, size_t numItems, Byte **v, void * (*allocFunc)(size_t size))
+{
+ Byte allAreDefined;
+ size_t i;
+ RINOK(SzReadByte(sd, &allAreDefined));
+ if (allAreDefined == 0)
+ return SzReadBoolVector(sd, numItems, v, allocFunc);
+ MY_ALLOC(Byte, *v, numItems, allocFunc);
+ for(i = 0; i < numItems; i++)
+ (*v)[i] = 1;
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadHashDigests(
+ CSzData *sd,
+ size_t numItems,
+ Byte **digestsDefined,
+ UInt32 **digests,
+ void * (*allocFunc)(size_t size))
+{
+ size_t i;
+ RINOK(SzReadBoolVector2(sd, numItems, digestsDefined, allocFunc));
+ MY_ALLOC(UInt32, *digests, numItems, allocFunc);
+ for(i = 0; i < numItems; i++)
+ if ((*digestsDefined)[i])
+ {
+ RINOK(SzReadUInt32(sd, (*digests) + i));
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadPackInfo(
+ CSzData *sd,
+ CFileSize *dataOffset,
+ UInt32 *numPackStreams,
+ CFileSize **packSizes,
+ Byte **packCRCsDefined,
+ UInt32 **packCRCs,
+ void * (*allocFunc)(size_t size))
+{
+ UInt32 i;
+ RINOK(SzReadSize(sd, dataOffset));
+ RINOK(SzReadNumber32(sd, numPackStreams));
+
+ RINOK(SzWaitAttribute(sd, k7zIdSize));
+
+ MY_ALLOC(CFileSize, *packSizes, (size_t)*numPackStreams, allocFunc);
+
+ for(i = 0; i < *numPackStreams; i++)
+ {
+ RINOK(SzReadSize(sd, (*packSizes) + i));
+ }
+
+ while(1)
+ {
+ UInt64 type;
+ RINOK(SzReadID(sd, &type));
+ if (type == k7zIdEnd)
+ break;
+ if (type == k7zIdCRC)
+ {
+ RINOK(SzReadHashDigests(sd, (size_t)*numPackStreams, packCRCsDefined, packCRCs, allocFunc));
+ continue;
+ }
+ RINOK(SzSkeepData(sd));
+ }
+ if (*packCRCsDefined == 0)
+ {
+ MY_ALLOC(Byte, *packCRCsDefined, (size_t)*numPackStreams, allocFunc);
+ MY_ALLOC(UInt32, *packCRCs, (size_t)*numPackStreams, allocFunc);
+ for(i = 0; i < *numPackStreams; i++)
+ {
+ (*packCRCsDefined)[i] = 0;
+ (*packCRCs)[i] = 0;
+ }
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadSwitch(CSzData *sd)
+{
+ Byte external;
+ RINOK(SzReadByte(sd, &external));
+ return (external == 0) ? SZ_OK: SZE_ARCHIVE_ERROR;
+}
+
+SZ_RESULT SzGetNextFolderItem(CSzData *sd, CFolder *folder, void * (*allocFunc)(size_t size))
+{
+ UInt32 numCoders;
+ UInt32 numBindPairs;
+ UInt32 numPackedStreams;
+ UInt32 i;
+ UInt32 numInStreams = 0;
+ UInt32 numOutStreams = 0;
+ RINOK(SzReadNumber32(sd, &numCoders));
+ folder->NumCoders = numCoders;
+
+ MY_ALLOC(CCoderInfo, folder->Coders, (size_t)numCoders, allocFunc);
+
+ for (i = 0; i < numCoders; i++)
+ SzCoderInfoInit(folder->Coders + i);
+
+ for (i = 0; i < numCoders; i++)
+ {
+ Byte mainByte;
+ CCoderInfo *coder = folder->Coders + i;
+ {
+ RINOK(SzReadByte(sd, &mainByte));
+ coder->MethodID.IDSize = (Byte)(mainByte & 0xF);
+ RINOK(SzReadBytes(sd, coder->MethodID.ID, coder->MethodID.IDSize));
+ if ((mainByte & 0x10) != 0)
+ {
+ RINOK(SzReadNumber32(sd, &coder->NumInStreams));
+ RINOK(SzReadNumber32(sd, &coder->NumOutStreams));
+ }
+ else
+ {
+ coder->NumInStreams = 1;
+ coder->NumOutStreams = 1;
+ }
+ if ((mainByte & 0x20) != 0)
+ {
+ UInt64 propertiesSize = 0;
+ RINOK(SzReadNumber(sd, &propertiesSize));
+ if (!SzByteBufferCreate(&coder->Properties, (size_t)propertiesSize, allocFunc))
+ return SZE_OUTOFMEMORY;
+ RINOK(SzReadBytes(sd, coder->Properties.Items, (size_t)propertiesSize));
+ }
+ }
+ while ((mainByte & 0x80) != 0)
+ {
+ RINOK(SzReadByte(sd, &mainByte));
+ RINOK(SzSkeepDataSize(sd, (mainByte & 0xF)));
+ if ((mainByte & 0x10) != 0)
+ {
+ UInt32 n;
+ RINOK(SzReadNumber32(sd, &n));
+ RINOK(SzReadNumber32(sd, &n));
+ }
+ if ((mainByte & 0x20) != 0)
+ {
+ UInt64 propertiesSize = 0;
+ RINOK(SzReadNumber(sd, &propertiesSize));
+ RINOK(SzSkeepDataSize(sd, propertiesSize));
+ }
+ }
+ numInStreams += (UInt32)coder->NumInStreams;
+ numOutStreams += (UInt32)coder->NumOutStreams;
+ }
+
+ numBindPairs = numOutStreams - 1;
+ folder->NumBindPairs = numBindPairs;
+
+
+ MY_ALLOC(CBindPair, folder->BindPairs, (size_t)numBindPairs, allocFunc);
+
+ for (i = 0; i < numBindPairs; i++)
+ {
+ CBindPair *bindPair = folder->BindPairs + i;;
+ RINOK(SzReadNumber32(sd, &bindPair->InIndex));
+ RINOK(SzReadNumber32(sd, &bindPair->OutIndex));
+ }
+
+ numPackedStreams = numInStreams - (UInt32)numBindPairs;
+
+ folder->NumPackStreams = numPackedStreams;
+ MY_ALLOC(UInt32, folder->PackStreams, (size_t)numPackedStreams, allocFunc);
+
+ if (numPackedStreams == 1)
+ {
+ UInt32 j;
+ UInt32 pi = 0;
+ for (j = 0; j < numInStreams; j++)
+ if (SzFolderFindBindPairForInStream(folder, j) < 0)
+ {
+ folder->PackStreams[pi++] = j;
+ break;
+ }
+ }
+ else
+ for(i = 0; i < numPackedStreams; i++)
+ {
+ RINOK(SzReadNumber32(sd, folder->PackStreams + i));
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadUnPackInfo(
+ CSzData *sd,
+ UInt32 *numFolders,
+ CFolder **folders, /* for allocFunc */
+ void * (*allocFunc)(size_t size),
+ ISzAlloc *allocTemp)
+{
+ UInt32 i;
+ RINOK(SzWaitAttribute(sd, k7zIdFolder));
+ RINOK(SzReadNumber32(sd, numFolders));
+ {
+ RINOK(SzReadSwitch(sd));
+
+ MY_ALLOC(CFolder, *folders, (size_t)*numFolders, allocFunc);
+
+ for(i = 0; i < *numFolders; i++)
+ SzFolderInit((*folders) + i);
+
+ for(i = 0; i < *numFolders; i++)
+ {
+ RINOK(SzGetNextFolderItem(sd, (*folders) + i, allocFunc));
+ }
+ }
+
+ RINOK(SzWaitAttribute(sd, k7zIdCodersUnPackSize));
+
+ for(i = 0; i < *numFolders; i++)
+ {
+ UInt32 j;
+ CFolder *folder = (*folders) + i;
+ UInt32 numOutStreams = SzFolderGetNumOutStreams(folder);
+
+ MY_ALLOC(CFileSize, folder->UnPackSizes, (size_t)numOutStreams, allocFunc);
+
+ for(j = 0; j < numOutStreams; j++)
+ {
+ RINOK(SzReadSize(sd, folder->UnPackSizes + j));
+ }
+ }
+
+ while(1)
+ {
+ UInt64 type;
+ RINOK(SzReadID(sd, &type));
+ if (type == k7zIdEnd)
+ return SZ_OK;
+ if (type == k7zIdCRC)
+ {
+ SZ_RESULT res;
+ Byte *crcsDefined = 0;
+ UInt32 *crcs = 0;
+ res = SzReadHashDigests(sd, *numFolders, &crcsDefined, &crcs, allocTemp->Alloc);
+ if (res == SZ_OK)
+ {
+ for(i = 0; i < *numFolders; i++)
+ {
+ CFolder *folder = (*folders) + i;
+ folder->UnPackCRCDefined = crcsDefined[i];
+ folder->UnPackCRC = crcs[i];
+ }
+ }
+ allocTemp->Free(crcs);
+ allocTemp->Free(crcsDefined);
+ RINOK(res);
+ continue;
+ }
+ RINOK(SzSkeepData(sd));
+ }
+}
+
+SZ_RESULT SzReadSubStreamsInfo(
+ CSzData *sd,
+ UInt32 numFolders,
+ CFolder *folders,
+ UInt32 *numUnPackStreams,
+ CFileSize **unPackSizes,
+ Byte **digestsDefined,
+ UInt32 **digests,
+ ISzAlloc *allocTemp)
+{
+ UInt64 type = 0;
+ UInt32 i;
+ UInt32 si = 0;
+ UInt32 numDigests = 0;
+
+ for(i = 0; i < numFolders; i++)
+ folders[i].NumUnPackStreams = 1;
+ *numUnPackStreams = numFolders;
+
+ while(1)
+ {
+ RINOK(SzReadID(sd, &type));
+ if (type == k7zIdNumUnPackStream)
+ {
+ *numUnPackStreams = 0;
+ for(i = 0; i < numFolders; i++)
+ {
+ UInt32 numStreams;
+ RINOK(SzReadNumber32(sd, &numStreams));
+ folders[i].NumUnPackStreams = numStreams;
+ *numUnPackStreams += numStreams;
+ }
+ continue;
+ }
+ if (type == k7zIdCRC || type == k7zIdSize)
+ break;
+ if (type == k7zIdEnd)
+ break;
+ RINOK(SzSkeepData(sd));
+ }
+
+ if (*numUnPackStreams == 0)
+ {
+ *unPackSizes = 0;
+ *digestsDefined = 0;
+ *digests = 0;
+ }
+ else
+ {
+ *unPackSizes = (CFileSize *)allocTemp->Alloc((size_t)*numUnPackStreams * sizeof(CFileSize));
+ RINOM(*unPackSizes);
+ *digestsDefined = (Byte *)allocTemp->Alloc((size_t)*numUnPackStreams * sizeof(Byte));
+ RINOM(*digestsDefined);
+ *digests = (UInt32 *)allocTemp->Alloc((size_t)*numUnPackStreams * sizeof(UInt32));
+ RINOM(*digests);
+ }
+
+ for(i = 0; i < numFolders; i++)
+ {
+ /*
+ v3.13 incorrectly worked with empty folders
+ v4.07: we check that folder is empty
+ */
+ CFileSize sum = 0;
+ UInt32 j;
+ UInt32 numSubstreams = folders[i].NumUnPackStreams;
+ if (numSubstreams == 0)
+ continue;
+ if (type == k7zIdSize)
+ for (j = 1; j < numSubstreams; j++)
+ {
+ CFileSize size;
+ RINOK(SzReadSize(sd, &size));
+ (*unPackSizes)[si++] = size;
+ sum += size;
+ }
+ (*unPackSizes)[si++] = SzFolderGetUnPackSize(folders + i) - sum;
+ }
+ if (type == k7zIdSize)
+ {
+ RINOK(SzReadID(sd, &type));
+ }
+
+ for(i = 0; i < *numUnPackStreams; i++)
+ {
+ (*digestsDefined)[i] = 0;
+ (*digests)[i] = 0;
+ }
+
+
+ for(i = 0; i < numFolders; i++)
+ {
+ UInt32 numSubstreams = folders[i].NumUnPackStreams;
+ if (numSubstreams != 1 || !folders[i].UnPackCRCDefined)
+ numDigests += numSubstreams;
+ }
+
+
+ si = 0;
+ while(1)
+ {
+ if (type == k7zIdCRC)
+ {
+ int digestIndex = 0;
+ Byte *digestsDefined2 = 0;
+ UInt32 *digests2 = 0;
+ SZ_RESULT res = SzReadHashDigests(sd, numDigests, &digestsDefined2, &digests2, allocTemp->Alloc);
+ if (res == SZ_OK)
+ {
+ for (i = 0; i < numFolders; i++)
+ {
+ CFolder *folder = folders + i;
+ UInt32 numSubstreams = folder->NumUnPackStreams;
+ if (numSubstreams == 1 && folder->UnPackCRCDefined)
+ {
+ (*digestsDefined)[si] = 1;
+ (*digests)[si] = folder->UnPackCRC;
+ si++;
+ }
+ else
+ {
+ UInt32 j;
+ for (j = 0; j < numSubstreams; j++, digestIndex++)
+ {
+ (*digestsDefined)[si] = digestsDefined2[digestIndex];
+ (*digests)[si] = digests2[digestIndex];
+ si++;
+ }
+ }
+ }
+ }
+ allocTemp->Free(digestsDefined2);
+ allocTemp->Free(digests2);
+ RINOK(res);
+ }
+ else if (type == k7zIdEnd)
+ return SZ_OK;
+ else
+ {
+ RINOK(SzSkeepData(sd));
+ }
+ RINOK(SzReadID(sd, &type));
+ }
+}
+
+
+SZ_RESULT SzReadStreamsInfo(
+ CSzData *sd,
+ CFileSize *dataOffset,
+ CArchiveDatabase *db,
+ UInt32 *numUnPackStreams,
+ CFileSize **unPackSizes, /* allocTemp */
+ Byte **digestsDefined, /* allocTemp */
+ UInt32 **digests, /* allocTemp */
+ void * (*allocFunc)(size_t size),
+ ISzAlloc *allocTemp)
+{
+ while(1)
+ {
+ UInt64 type;
+ RINOK(SzReadID(sd, &type));
+ if ((UInt64)(int)type != type)
+ return SZE_FAIL;
+ switch((int)type)
+ {
+ case k7zIdEnd:
+ return SZ_OK;
+ case k7zIdPackInfo:
+ {
+ RINOK(SzReadPackInfo(sd, dataOffset, &db->NumPackStreams,
+ &db->PackSizes, &db->PackCRCsDefined, &db->PackCRCs, allocFunc));
+ break;
+ }
+ case k7zIdUnPackInfo:
+ {
+ RINOK(SzReadUnPackInfo(sd, &db->NumFolders, &db->Folders, allocFunc, allocTemp));
+ break;
+ }
+ case k7zIdSubStreamsInfo:
+ {
+ RINOK(SzReadSubStreamsInfo(sd, db->NumFolders, db->Folders,
+ numUnPackStreams, unPackSizes, digestsDefined, digests, allocTemp));
+ break;
+ }
+ default:
+ return SZE_FAIL;
+ }
+ }
+}
+
+Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+SZ_RESULT SzReadFileNames(CSzData *sd, UInt32 numFiles, CFileItem *files,
+ void * (*allocFunc)(size_t size))
+{
+ UInt32 i;
+ for(i = 0; i < numFiles; i++)
+ {
+ UInt32 len = 0;
+ UInt32 pos = 0;
+ CFileItem *file = files + i;
+ while(pos + 2 <= sd->Size)
+ {
+ int numAdds;
+ UInt32 value = (UInt32)(sd->Data[pos] | (((UInt32)sd->Data[pos + 1]) << 8));
+ pos += 2;
+ len++;
+ if (value == 0)
+ break;
+ if (value < 0x80)
+ continue;
+ if (value >= 0xD800 && value < 0xE000)
+ {
+ UInt32 c2;
+ if (value >= 0xDC00)
+ return SZE_ARCHIVE_ERROR;
+ if (pos + 2 > sd->Size)
+ return SZE_ARCHIVE_ERROR;
+ c2 = (UInt32)(sd->Data[pos] | (((UInt32)sd->Data[pos + 1]) << 8));
+ pos += 2;
+ if (c2 < 0xDC00 || c2 >= 0xE000)
+ return SZE_ARCHIVE_ERROR;
+ value = ((value - 0xD800) << 10) | (c2 - 0xDC00);
+ }
+ for (numAdds = 1; numAdds < 5; numAdds++)
+ if (value < (((UInt32)1) << (numAdds * 5 + 6)))
+ break;
+ len += numAdds;
+ }
+
+ MY_ALLOC(char, file->Name, (size_t)len, allocFunc);
+
+ len = 0;
+ while(2 <= sd->Size)
+ {
+ int numAdds;
+ UInt32 value = (UInt32)(sd->Data[0] | (((UInt32)sd->Data[1]) << 8));
+ SzSkeepDataSize(sd, 2);
+ if (value < 0x80)
+ {
+ file->Name[len++] = (char)value;
+ if (value == 0)
+ break;
+ continue;
+ }
+ if (value >= 0xD800 && value < 0xE000)
+ {
+ UInt32 c2 = (UInt32)(sd->Data[0] | (((UInt32)sd->Data[1]) << 8));
+ SzSkeepDataSize(sd, 2);
+ value = ((value - 0xD800) << 10) | (c2 - 0xDC00);
+ }
+ for (numAdds = 1; numAdds < 5; numAdds++)
+ if (value < (((UInt32)1) << (numAdds * 5 + 6)))
+ break;
+ file->Name[len++] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds)));
+ do
+ {
+ numAdds--;
+ file->Name[len++] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F));
+ }
+ while(numAdds > 0);
+
+ len += numAdds;
+ }
+ }
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadHeader2(
+ CSzData *sd,
+ CArchiveDatabaseEx *db, /* allocMain */
+ CFileSize **unPackSizes, /* allocTemp */
+ Byte **digestsDefined, /* allocTemp */
+ UInt32 **digests, /* allocTemp */
+ Byte **emptyStreamVector, /* allocTemp */
+ Byte **emptyFileVector, /* allocTemp */
+ ISzAlloc *allocMain,
+ ISzAlloc *allocTemp)
+{
+ UInt64 type;
+ UInt32 numUnPackStreams = 0;
+ UInt32 numFiles = 0;
+ CFileItem *files = 0;
+ UInt32 numEmptyStreams = 0;
+ UInt32 i;
+
+ RINOK(SzReadID(sd, &type));
+
+ if (type == k7zIdArchiveProperties)
+ {
+ RINOK(SzReadArchiveProperties(sd));
+ RINOK(SzReadID(sd, &type));
+ }
+
+
+ if (type == k7zIdMainStreamsInfo)
+ {
+ RINOK(SzReadStreamsInfo(sd,
+ &db->ArchiveInfo.DataStartPosition,
+ &db->Database,
+ &numUnPackStreams,
+ unPackSizes,
+ digestsDefined,
+ digests, allocMain->Alloc, allocTemp));
+ db->ArchiveInfo.DataStartPosition += db->ArchiveInfo.StartPositionAfterHeader;
+ RINOK(SzReadID(sd, &type));
+ }
+
+ if (type == k7zIdEnd)
+ return SZ_OK;
+ if (type != k7zIdFilesInfo)
+ return SZE_ARCHIVE_ERROR;
+
+ RINOK(SzReadNumber32(sd, &numFiles));
+ db->Database.NumFiles = numFiles;
+
+ MY_ALLOC(CFileItem, files, (size_t)numFiles, allocMain->Alloc);
+
+ db->Database.Files = files;
+ for(i = 0; i < numFiles; i++)
+ SzFileInit(files + i);
+
+ while(1)
+ {
+ UInt64 type;
+ UInt64 size;
+ RINOK(SzReadID(sd, &type));
+ if (type == k7zIdEnd)
+ break;
+ RINOK(SzReadNumber(sd, &size));
+
+ if ((UInt64)(int)type != type)
+ {
+ RINOK(SzSkeepDataSize(sd, size));
+ }
+ else
+ switch((int)type)
+ {
+ case k7zIdName:
+ {
+ RINOK(SzReadSwitch(sd));
+ RINOK(SzReadFileNames(sd, numFiles, files, allocMain->Alloc))
+ break;
+ }
+ case k7zIdEmptyStream:
+ {
+ RINOK(SzReadBoolVector(sd, numFiles, emptyStreamVector, allocTemp->Alloc));
+ numEmptyStreams = 0;
+ for (i = 0; i < numFiles; i++)
+ if ((*emptyStreamVector)[i])
+ numEmptyStreams++;
+ break;
+ }
+ case k7zIdEmptyFile:
+ {
+ RINOK(SzReadBoolVector(sd, numEmptyStreams, emptyFileVector, allocTemp->Alloc));
+ break;
+ }
+ default:
+ {
+ RINOK(SzSkeepDataSize(sd, size));
+ }
+ }
+ }
+
+ {
+ UInt32 emptyFileIndex = 0;
+ UInt32 sizeIndex = 0;
+ for(i = 0; i < numFiles; i++)
+ {
+ CFileItem *file = files + i;
+ file->IsAnti = 0;
+ if (*emptyStreamVector == 0)
+ file->HasStream = 1;
+ else
+ file->HasStream = (Byte)((*emptyStreamVector)[i] ? 0 : 1);
+ if(file->HasStream)
+ {
+ file->IsDirectory = 0;
+ file->Size = (*unPackSizes)[sizeIndex];
+ file->FileCRC = (*digests)[sizeIndex];
+ file->IsFileCRCDefined = (Byte)(*digestsDefined)[sizeIndex];
+ sizeIndex++;
+ }
+ else
+ {
+ if (*emptyFileVector == 0)
+ file->IsDirectory = 1;
+ else
+ file->IsDirectory = (Byte)((*emptyFileVector)[emptyFileIndex] ? 0 : 1);
+ emptyFileIndex++;
+ file->Size = 0;
+ file->IsFileCRCDefined = 0;
+ }
+ }
+ }
+ return SzArDbExFill(db, allocMain->Alloc);
+}
+
+SZ_RESULT SzReadHeader(
+ CSzData *sd,
+ CArchiveDatabaseEx *db,
+ ISzAlloc *allocMain,
+ ISzAlloc *allocTemp)
+{
+ CFileSize *unPackSizes = 0;
+ Byte *digestsDefined = 0;
+ UInt32 *digests = 0;
+ Byte *emptyStreamVector = 0;
+ Byte *emptyFileVector = 0;
+ SZ_RESULT res = SzReadHeader2(sd, db,
+ &unPackSizes, &digestsDefined, &digests,
+ &emptyStreamVector, &emptyFileVector,
+ allocMain, allocTemp);
+ allocTemp->Free(unPackSizes);
+ allocTemp->Free(digestsDefined);
+ allocTemp->Free(digests);
+ allocTemp->Free(emptyStreamVector);
+ allocTemp->Free(emptyFileVector);
+ return res;
+}
+
+SZ_RESULT SzReadAndDecodePackedStreams2(
+ ISzInStream *inStream,
+ CSzData *sd,
+ CSzByteBuffer *outBuffer,
+ CFileSize baseOffset,
+ CArchiveDatabase *db,
+ CFileSize **unPackSizes,
+ Byte **digestsDefined,
+ UInt32 **digests,
+ #ifndef _LZMA_IN_CB
+ Byte **inBuffer,
+ #endif
+ ISzAlloc *allocTemp)
+{
+
+ UInt32 numUnPackStreams = 0;
+ CFileSize dataStartPos;
+ CFolder *folder;
+ #ifndef _LZMA_IN_CB
+ CFileSize packSize = 0;
+ UInt32 i = 0;
+ #endif
+ CFileSize unPackSize;
+ size_t outRealSize;
+ SZ_RESULT res;
+
+ RINOK(SzReadStreamsInfo(sd, &dataStartPos, db,
+ &numUnPackStreams, unPackSizes, digestsDefined, digests,
+ allocTemp->Alloc, allocTemp));
+
+ dataStartPos += baseOffset;
+ if (db->NumFolders != 1)
+ return SZE_ARCHIVE_ERROR;
+
+ folder = db->Folders;
+ unPackSize = SzFolderGetUnPackSize(folder);
+
+ RINOK(inStream->Seek(inStream, dataStartPos));
+
+ #ifndef _LZMA_IN_CB
+ for (i = 0; i < db->NumPackStreams; i++)
+ packSize += db->PackSizes[i];
+
+ MY_ALLOC(Byte, *inBuffer, (size_t)packSize, allocTemp->Alloc);
+
+ RINOK(SafeReadDirect(inStream, *inBuffer, (size_t)packSize));
+ #endif
+
+ if (!SzByteBufferCreate(outBuffer, (size_t)unPackSize, allocTemp->Alloc))
+ return SZE_OUTOFMEMORY;
+
+ res = SzDecode(db->PackSizes, folder,
+ #ifdef _LZMA_IN_CB
+ inStream,
+ #else
+ *inBuffer,
+ #endif
+ outBuffer->Items, (size_t)unPackSize,
+ &outRealSize, allocTemp);
+ RINOK(res)
+ if (outRealSize != (UInt32)unPackSize)
+ return SZE_FAIL;
+ if (folder->UnPackCRCDefined)
+ if (!CrcVerifyDigest(folder->UnPackCRC, outBuffer->Items, (size_t)unPackSize))
+ return SZE_FAIL;
+ return SZ_OK;
+}
+
+SZ_RESULT SzReadAndDecodePackedStreams(
+ ISzInStream *inStream,
+ CSzData *sd,
+ CSzByteBuffer *outBuffer,
+ CFileSize baseOffset,
+ ISzAlloc *allocTemp)
+{
+ CArchiveDatabase db;
+ CFileSize *unPackSizes = 0;
+ Byte *digestsDefined = 0;
+ UInt32 *digests = 0;
+ #ifndef _LZMA_IN_CB
+ Byte *inBuffer = 0;
+ #endif
+ SZ_RESULT res;
+ SzArchiveDatabaseInit(&db);
+ res = SzReadAndDecodePackedStreams2(inStream, sd, outBuffer, baseOffset,
+ &db, &unPackSizes, &digestsDefined, &digests,
+ #ifndef _LZMA_IN_CB
+ &inBuffer,
+ #endif
+ allocTemp);
+ SzArchiveDatabaseFree(&db, allocTemp->Free);
+ allocTemp->Free(unPackSizes);
+ allocTemp->Free(digestsDefined);
+ allocTemp->Free(digests);
+ #ifndef _LZMA_IN_CB
+ allocTemp->Free(inBuffer);
+ #endif
+ return res;
+}
+
+SZ_RESULT SzArchiveOpen2(
+ ISzInStream *inStream,
+ CArchiveDatabaseEx *db,
+ ISzAlloc *allocMain,
+ ISzAlloc *allocTemp)
+{
+ Byte signature[k7zSignatureSize];
+ Byte version;
+ UInt32 crcFromArchive;
+ UInt64 nextHeaderOffset;
+ UInt64 nextHeaderSize;
+ UInt32 nextHeaderCRC;
+ UInt32 crc;
+ CFileSize pos = 0;
+ CSzByteBuffer buffer;
+ CSzData sd;
+ SZ_RESULT res;
+
+ RINOK(SafeReadDirect(inStream, signature, k7zSignatureSize));
+
+ if (!TestSignatureCandidate(signature))
+ return SZE_ARCHIVE_ERROR;
+
+ /*
+ db.Clear();
+ db.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition;
+ */
+ RINOK(SafeReadDirectByte(inStream, &version));
+ if (version != k7zMajorVersion)
+ return SZE_ARCHIVE_ERROR;
+ RINOK(SafeReadDirectByte(inStream, &version));
+
+ RINOK(SafeReadDirectUInt32(inStream, &crcFromArchive));
+
+ CrcInit(&crc);
+ RINOK(SafeReadDirectUInt64(inStream, &nextHeaderOffset));
+ CrcUpdateUInt64(&crc, nextHeaderOffset);
+ RINOK(SafeReadDirectUInt64(inStream, &nextHeaderSize));
+ CrcUpdateUInt64(&crc, nextHeaderSize);
+ RINOK(SafeReadDirectUInt32(inStream, &nextHeaderCRC));
+ CrcUpdateUInt32(&crc, nextHeaderCRC);
+
+ pos = k7zStartHeaderSize;
+ db->ArchiveInfo.StartPositionAfterHeader = pos;
+
+ if (CrcGetDigest(&crc) != crcFromArchive)
+ return SZE_ARCHIVE_ERROR;
+
+ if (nextHeaderSize == 0)
+ return SZ_OK;
+
+ RINOK(inStream->Seek(inStream, (CFileSize)(pos + nextHeaderOffset)));
+
+ if (!SzByteBufferCreate(&buffer, (size_t)nextHeaderSize, allocTemp->Alloc))
+ return SZE_OUTOFMEMORY;
+
+ res = SafeReadDirect(inStream, buffer.Items, (size_t)nextHeaderSize);
+ if (res == SZ_OK)
+ {
+ if (CrcVerifyDigest(nextHeaderCRC, buffer.Items, (UInt32)nextHeaderSize))
+ {
+ while (1)
+ {
+ UInt64 type;
+ sd.Data = buffer.Items;
+ sd.Size = buffer.Capacity;
+ res = SzReadID(&sd, &type);
+ if (res != SZ_OK)
+ break;
+ if (type == k7zIdHeader)
+ {
+ res = SzReadHeader(&sd, db, allocMain, allocTemp);
+ break;
+ }
+ if (type != k7zIdEncodedHeader)
+ {
+ res = SZE_ARCHIVE_ERROR;
+ break;
+ }
+ {
+ CSzByteBuffer outBuffer;
+ res = SzReadAndDecodePackedStreams(inStream, &sd, &outBuffer,
+ db->ArchiveInfo.StartPositionAfterHeader,
+ allocTemp);
+ if (res != SZ_OK)
+ {
+ SzByteBufferFree(&outBuffer, allocTemp->Free);
+ break;
+ }
+ SzByteBufferFree(&buffer, allocTemp->Free);
+ buffer.Items = outBuffer.Items;
+ buffer.Capacity = outBuffer.Capacity;
+ }
+ }
+ }
+ }
+ SzByteBufferFree(&buffer, allocTemp->Free);
+ return res;
+}
+
+SZ_RESULT SzArchiveOpen(
+ ISzInStream *inStream,
+ CArchiveDatabaseEx *db,
+ ISzAlloc *allocMain,
+ ISzAlloc *allocTemp)
+{
+ SZ_RESULT res = SzArchiveOpen2(inStream, db, allocMain, allocTemp);
+ if (res != SZ_OK)
+ SzArDbExFree(db, allocMain->Free);
+ return res;
+}
--- /dev/null
+/* 7zIn.h */
+
+#ifndef __7Z_IN_H
+#define __7Z_IN_H
+
+#include "7zHeader.h"
+#include "7zItem.h"
+#include "7zAlloc.h"
+
+typedef struct _CInArchiveInfo
+{
+ CFileSize StartPositionAfterHeader;
+ CFileSize DataStartPosition;
+}CInArchiveInfo;
+
+typedef struct _CArchiveDatabaseEx
+{
+ CArchiveDatabase Database;
+ CInArchiveInfo ArchiveInfo;
+ UInt32 *FolderStartPackStreamIndex;
+ CFileSize *PackStreamStartPositions;
+ UInt32 *FolderStartFileIndex;
+ UInt32 *FileIndexToFolderIndexMap;
+}CArchiveDatabaseEx;
+
+void SzArDbExInit(CArchiveDatabaseEx *db);
+void SzArDbExFree(CArchiveDatabaseEx *db, void (*freeFunc)(void *));
+CFileSize SzArDbGetFolderStreamPos(CArchiveDatabaseEx *db, UInt32 folderIndex, UInt32 indexInFolder);
+CFileSize SzArDbGetFolderFullPackSize(CArchiveDatabaseEx *db, UInt32 folderIndex);
+
+typedef struct _ISzInStream
+{
+ #ifdef _LZMA_IN_CB
+ SZ_RESULT (*Read)(
+ void *object, /* pointer to ISzInStream itself */
+ void **buffer, /* out: pointer to buffer with data */
+ size_t maxRequiredSize, /* max required size to read */
+ size_t *processedSize); /* real processed size.
+ processedSize can be less than maxRequiredSize.
+ If processedSize == 0, then there are no more
+ bytes in stream. */
+ #else
+ SZ_RESULT (*Read)(void *object, void *buffer, size_t size, size_t *processedSize);
+ #endif
+ SZ_RESULT (*Seek)(void *object, CFileSize pos);
+} ISzInStream;
+
+
+int SzArchiveOpen(
+ ISzInStream *inStream,
+ CArchiveDatabaseEx *db,
+ ISzAlloc *allocMain,
+ ISzAlloc *allocTemp);
+
+#endif
--- /dev/null
+/* 7zItem.c */
+
+#include "7zItem.h"
+#include "7zAlloc.h"
+
+void SzCoderInfoInit(CCoderInfo *coder)
+{
+ SzByteBufferInit(&coder->Properties);
+}
+
+void SzCoderInfoFree(CCoderInfo *coder, void (*freeFunc)(void *p))
+{
+ SzByteBufferFree(&coder->Properties, freeFunc);
+ SzCoderInfoInit(coder);
+}
+
+void SzFolderInit(CFolder *folder)
+{
+ folder->NumCoders = 0;
+ folder->Coders = 0;
+ folder->NumBindPairs = 0;
+ folder->BindPairs = 0;
+ folder->NumPackStreams = 0;
+ folder->PackStreams = 0;
+ folder->UnPackSizes = 0;
+ folder->UnPackCRCDefined = 0;
+ folder->UnPackCRC = 0;
+ folder->NumUnPackStreams = 0;
+}
+
+void SzFolderFree(CFolder *folder, void (*freeFunc)(void *p))
+{
+ UInt32 i;
+ for (i = 0; i < folder->NumCoders; i++)
+ SzCoderInfoFree(&folder->Coders[i], freeFunc);
+ freeFunc(folder->Coders);
+ freeFunc(folder->BindPairs);
+ freeFunc(folder->PackStreams);
+ freeFunc(folder->UnPackSizes);
+ SzFolderInit(folder);
+}
+
+UInt32 SzFolderGetNumOutStreams(CFolder *folder)
+{
+ UInt32 result = 0;
+ UInt32 i;
+ for (i = 0; i < folder->NumCoders; i++)
+ result += folder->Coders[i].NumOutStreams;
+ return result;
+}
+
+int SzFolderFindBindPairForInStream(CFolder *folder, UInt32 inStreamIndex)
+{
+ UInt32 i;
+ for(i = 0; i < folder->NumBindPairs; i++)
+ if (folder->BindPairs[i].InIndex == inStreamIndex)
+ return i;
+ return -1;
+}
+
+
+int SzFolderFindBindPairForOutStream(CFolder *folder, UInt32 outStreamIndex)
+{
+ UInt32 i;
+ for(i = 0; i < folder->NumBindPairs; i++)
+ if (folder->BindPairs[i].OutIndex == outStreamIndex)
+ return i;
+ return -1;
+}
+
+CFileSize SzFolderGetUnPackSize(CFolder *folder)
+{
+ int i = (int)SzFolderGetNumOutStreams(folder);
+ if (i == 0)
+ return 0;
+ for (i--; i >= 0; i--)
+ if (SzFolderFindBindPairForOutStream(folder, i) < 0)
+ return folder->UnPackSizes[i];
+ /* throw 1; */
+ return 0;
+}
+
+/*
+int FindPackStreamArrayIndex(int inStreamIndex) const
+{
+ for(int i = 0; i < PackStreams.Size(); i++)
+ if (PackStreams[i] == inStreamIndex)
+ return i;
+ return -1;
+}
+*/
+
+void SzFileInit(CFileItem *fileItem)
+{
+ fileItem->IsFileCRCDefined = 0;
+ fileItem->HasStream = 1;
+ fileItem->IsDirectory = 0;
+ fileItem->IsAnti = 0;
+ fileItem->Name = 0;
+}
+
+void SzFileFree(CFileItem *fileItem, void (*freeFunc)(void *p))
+{
+ freeFunc(fileItem->Name);
+ SzFileInit(fileItem);
+}
+
+void SzArchiveDatabaseInit(CArchiveDatabase *db)
+{
+ db->NumPackStreams = 0;
+ db->PackSizes = 0;
+ db->PackCRCsDefined = 0;
+ db->PackCRCs = 0;
+ db->NumFolders = 0;
+ db->Folders = 0;
+ db->NumFiles = 0;
+ db->Files = 0;
+}
+
+void SzArchiveDatabaseFree(CArchiveDatabase *db, void (*freeFunc)(void *))
+{
+ UInt32 i;
+ for (i = 0; i < db->NumFolders; i++)
+ SzFolderFree(&db->Folders[i], freeFunc);
+ for (i = 0; i < db->NumFiles; i++)
+ SzFileFree(&db->Files[i], freeFunc);
+ freeFunc(db->PackSizes);
+ freeFunc(db->PackCRCsDefined);
+ freeFunc(db->PackCRCs);
+ freeFunc(db->Folders);
+ freeFunc(db->Files);
+ SzArchiveDatabaseInit(db);
+}
--- /dev/null
+/* 7zItem.h */
+
+#ifndef __7Z_ITEM_H
+#define __7Z_ITEM_H
+
+#include "7zMethodID.h"
+#include "7zHeader.h"
+#include "7zBuffer.h"
+
+typedef struct _CCoderInfo
+{
+ UInt32 NumInStreams;
+ UInt32 NumOutStreams;
+ CMethodID MethodID;
+ CSzByteBuffer Properties;
+}CCoderInfo;
+
+void SzCoderInfoInit(CCoderInfo *coder);
+void SzCoderInfoFree(CCoderInfo *coder, void (*freeFunc)(void *p));
+
+typedef struct _CBindPair
+{
+ UInt32 InIndex;
+ UInt32 OutIndex;
+}CBindPair;
+
+typedef struct _CFolder
+{
+ UInt32 NumCoders;
+ CCoderInfo *Coders;
+ UInt32 NumBindPairs;
+ CBindPair *BindPairs;
+ UInt32 NumPackStreams;
+ UInt32 *PackStreams;
+ CFileSize *UnPackSizes;
+ int UnPackCRCDefined;
+ UInt32 UnPackCRC;
+
+ UInt32 NumUnPackStreams;
+}CFolder;
+
+void SzFolderInit(CFolder *folder);
+CFileSize SzFolderGetUnPackSize(CFolder *folder);
+int SzFolderFindBindPairForInStream(CFolder *folder, UInt32 inStreamIndex);
+UInt32 SzFolderGetNumOutStreams(CFolder *folder);
+CFileSize SzFolderGetUnPackSize(CFolder *folder);
+
+/* #define CArchiveFileTime UInt64 */
+
+typedef struct _CFileItem
+{
+ /*
+ CArchiveFileTime LastWriteTime;
+ CFileSize StartPos;
+ UInt32 Attributes;
+ */
+ CFileSize Size;
+ UInt32 FileCRC;
+ char *Name;
+
+ Byte IsFileCRCDefined;
+ Byte HasStream;
+ Byte IsDirectory;
+ Byte IsAnti;
+ /*
+ int AreAttributesDefined;
+ int IsLastWriteTimeDefined;
+ int IsStartPosDefined;
+ */
+}CFileItem;
+
+void SzFileInit(CFileItem *fileItem);
+
+typedef struct _CArchiveDatabase
+{
+ UInt32 NumPackStreams;
+ CFileSize *PackSizes;
+ Byte *PackCRCsDefined;
+ UInt32 *PackCRCs;
+ UInt32 NumFolders;
+ CFolder *Folders;
+ UInt32 NumFiles;
+ CFileItem *Files;
+}CArchiveDatabase;
+
+void SzArchiveDatabaseInit(CArchiveDatabase *db);
+void SzArchiveDatabaseFree(CArchiveDatabase *db, void (*freeFunc)(void *));
+
+
+#endif
--- /dev/null
+/* 7zMethodID.c */
+
+#include "7zMethodID.h"
+
+int AreMethodsEqual(CMethodID *a1, CMethodID *a2)
+{
+ int i;
+ if (a1->IDSize != a2->IDSize)
+ return 0;
+ for (i = 0; i < a1->IDSize; i++)
+ if (a1->ID[i] != a2->ID[i])
+ return 0;
+ return 1;
+}
--- /dev/null
+/* 7zMethodID.h */
+
+#ifndef __7Z_METHOD_ID_H
+#define __7Z_METHOD_ID_H
+
+#include "7zTypes.h"
+
+#define kMethodIDSize 15
+
+typedef struct _CMethodID
+{
+ Byte ID[kMethodIDSize];
+ Byte IDSize;
+} CMethodID;
+
+int AreMethodsEqual(CMethodID *a1, CMethodID *a2);
+
+#endif
--- /dev/null
+/* 7zTypes.h */
+
+#ifndef __COMMON_TYPES_H
+#define __COMMON_TYPES_H
+
+#ifndef _7ZIP_BYTE_DEFINED
+#define _7ZIP_BYTE_DEFINED
+typedef unsigned char Byte;
+#endif
+
+#ifndef _7ZIP_UINT16_DEFINED
+#define _7ZIP_UINT16_DEFINED
+typedef unsigned short UInt16;
+#endif
+
+#ifndef _7ZIP_UINT32_DEFINED
+#define _7ZIP_UINT32_DEFINED
+#ifdef _LZMA_UINT32_IS_ULONG
+typedef unsigned long UInt32;
+#else
+typedef unsigned int UInt32;
+#endif
+#endif
+
+/* #define _SZ_NO_INT_64 */
+/* define it your compiler doesn't support long long int */
+
+#ifndef _7ZIP_UINT64_DEFINED
+#define _7ZIP_UINT64_DEFINED
+#ifdef _SZ_NO_INT_64
+typedef unsigned long UInt64;
+#else
+#ifdef _MSC_VER
+typedef unsigned __int64 UInt64;
+#else
+typedef unsigned long long int UInt64;
+#endif
+#endif
+#endif
+
+
+/* #define _SZ_FILE_SIZE_64 */
+/* Use _SZ_FILE_SIZE_64 if you need support for files larger than 4 GB*/
+
+#ifndef CFileSize
+#ifdef _SZ_FILE_SIZE_64
+typedef UInt64 CFileSize;
+#else
+typedef UInt32 CFileSize;
+#endif
+#endif
+
+#define SZ_RESULT int
+
+#define SZ_OK (0)
+#define SZE_DATA_ERROR (1)
+#define SZE_OUTOFMEMORY (2)
+#define SZE_CRC_ERROR (3)
+
+#define SZE_NOTIMPL (4)
+#define SZE_FAIL (5)
+
+#define SZE_ARCHIVE_ERROR (6)
+
+#define RINOK(x) { int __result_ = (x); if(__result_ != 0) return __result_; }
+
+#endif
--- /dev/null
+(These are the licensing details for this directory, taken from lzma.txt in
+ the original source distribution. The basic gist is you can do what you want
+ with this code, including sell it in a closed-source app...changes to LZMA
+ itself must be released as source code, which in the case of PhysicsFS, you
+ can just point people to our source code repository unless you make further
+ changes yourself. --ryan.)
+
+
+LZMA SDK 4.43
+-------------
+
+LZMA SDK Copyright (C) 1999-2006 Igor Pavlov
+
+LZMA SDK provides the documentation, samples, header files, libraries,
+and tools you need to develop applications that use LZMA compression.
+
+LZMA is default and general compression method of 7z format
+in 7-Zip compression program (www.7-zip.org). LZMA provides high
+compression ratio and very fast decompression.
+
+LZMA is an improved version of famous LZ77 compression algorithm.
+It was improved in way of maximum increasing of compression ratio,
+keeping high decompression speed and low memory requirements for
+decompressing.
+
+
+
+LICENSE
+-------
+
+LZMA SDK is available under any of the following licenses:
+
+1) GNU Lesser General Public License (GNU LGPL)
+2) Common Public License (CPL)
+3) Simplified license for unmodified code (read SPECIAL EXCEPTION)
+4) Proprietary license
+
+It means that you can select one of these four options and follow rules of that license.
+
+
+1,2) GNU LGPL and CPL licenses are pretty similar and both these
+licenses are classified as
+ - "Free software licenses" at http://www.gnu.org/
+ - "OSI-approved" at http://www.opensource.org/
+
+
+3) SPECIAL EXCEPTION
+
+Igor Pavlov, as the author of this code, expressly permits you
+to statically or dynamically link your code (or bind by name)
+to the files from LZMA SDK without subjecting your linked
+code to the terms of the CPL or GNU LGPL.
+Any modifications or additions to files from LZMA SDK, however,
+are subject to the GNU LGPL or CPL terms.
+
+SPECIAL EXCEPTION allows you to use LZMA SDK in applications with closed code,
+while you keep LZMA SDK code unmodified.
+
+
+SPECIAL EXCEPTION #2: Igor Pavlov, as the author of this code, expressly permits
+you to use this code under the same terms and conditions contained in the License
+Agreement you have for any previous version of LZMA SDK developed by Igor Pavlov.
+
+SPECIAL EXCEPTION #2 allows owners of proprietary licenses to use latest version
+of LZMA SDK as update for previous versions.
+
+
+SPECIAL EXCEPTION #3: Igor Pavlov, as the author of this code, expressly permits
+you to use code of the following files:
+BranchTypes.h, LzmaTypes.h, LzmaTest.c, LzmaStateTest.c, LzmaAlone.cpp,
+LzmaAlone.cs, LzmaAlone.java
+as public domain code.
+
+
+4) Proprietary license
+
+LZMA SDK also can be available under a proprietary license which
+can include:
+
+1) Right to modify code without subjecting modified code to the
+terms of the CPL or GNU LGPL
+2) Technical support for code
+
+To request such proprietary license or any additional consultations,
+send email message from that page:
+http://www.7-zip.org/support.html
+
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+You should have received a copy of the Common Public License
+along with this library.
--- /dev/null
+/*
+ LzmaDecode.c
+ LZMA Decoder (optimized for Speed version)
+
+ LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01)
+ http://www.7-zip.org/
+
+ LZMA SDK is licensed under two licenses:
+ 1) GNU Lesser General Public License (GNU LGPL)
+ 2) Common Public License (CPL)
+ It means that you can select one of these two licenses and
+ follow rules of that license.
+
+ SPECIAL EXCEPTION:
+ Igor Pavlov, as the author of this Code, expressly permits you to
+ statically or dynamically link your Code (or bind by name) to the
+ interfaces of this file without subjecting your linked Code to the
+ terms of the CPL or GNU LGPL. Any modifications or additions
+ to this file, however, are subject to the LGPL or CPL terms.
+*/
+
+#include "LzmaDecode.h"
+
+#define kNumTopBits 24
+#define kTopValue ((UInt32)1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+
+#define RC_READ_BYTE (*Buffer++)
+
+#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \
+ { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }}
+
+#ifdef _LZMA_IN_CB
+
+#define RC_TEST { if (Buffer == BufferLim) \
+ { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \
+ BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }}
+
+#define RC_INIT Buffer = BufferLim = 0; RC_INIT2
+
+#else
+
+#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; }
+
+#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2
+
+#endif
+
+#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; }
+
+#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound)
+#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits;
+#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits;
+
+#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \
+ { UpdateBit0(p); mi <<= 1; A0; } else \
+ { UpdateBit1(p); mi = (mi + mi) + 1; A1; }
+
+#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)
+
+#define RangeDecoderBitTreeDecode(probs, numLevels, res) \
+ { int i = numLevels; res = 1; \
+ do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \
+ res -= (1 << numLevels); }
+
+
+#define kNumPosBitsMax 4
+#define kNumPosStatesMax (1 << kNumPosBitsMax)
+
+#define kLenNumLowBits 3
+#define kLenNumLowSymbols (1 << kLenNumLowBits)
+#define kLenNumMidBits 3
+#define kLenNumMidSymbols (1 << kLenNumMidBits)
+#define kLenNumHighBits 8
+#define kLenNumHighSymbols (1 << kLenNumHighBits)
+
+#define LenChoice 0
+#define LenChoice2 (LenChoice + 1)
+#define LenLow (LenChoice2 + 1)
+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
+#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
+
+
+#define kNumStates 12
+#define kNumLitStates 7
+
+#define kStartPosModelIndex 4
+#define kEndPosModelIndex 14
+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
+
+#define kNumPosSlotBits 6
+#define kNumLenToPosStates 4
+
+#define kNumAlignBits 4
+#define kAlignTableSize (1 << kNumAlignBits)
+
+#define kMatchMinLen 2
+
+#define IsMatch 0
+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
+#define IsRepG0 (IsRep + kNumStates)
+#define IsRepG1 (IsRepG0 + kNumStates)
+#define IsRepG2 (IsRepG1 + kNumStates)
+#define IsRep0Long (IsRepG2 + kNumStates)
+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
+#define LenCoder (Align + kAlignTableSize)
+#define RepLenCoder (LenCoder + kNumLenProbs)
+#define Literal (RepLenCoder + kNumLenProbs)
+
+#if Literal != LZMA_BASE_SIZE
+StopCompilingDueBUG
+#endif
+
+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size)
+{
+ unsigned char prop0;
+ if (size < LZMA_PROPERTIES_SIZE)
+ return LZMA_RESULT_DATA_ERROR;
+ prop0 = propsData[0];
+ if (prop0 >= (9 * 5 * 5))
+ return LZMA_RESULT_DATA_ERROR;
+ {
+ for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5));
+ for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9);
+ propsRes->lc = prop0;
+ /*
+ unsigned char remainder = (unsigned char)(prop0 / 9);
+ propsRes->lc = prop0 % 9;
+ propsRes->pb = remainder / 5;
+ propsRes->lp = remainder % 5;
+ */
+ }
+
+ #ifdef _LZMA_OUT_READ
+ {
+ int i;
+ propsRes->DictionarySize = 0;
+ for (i = 0; i < 4; i++)
+ propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8);
+ if (propsRes->DictionarySize == 0)
+ propsRes->DictionarySize = 1;
+ }
+ #endif
+ return LZMA_RESULT_OK;
+}
+
+#define kLzmaStreamWasFinishedId (-1)
+
+int LzmaDecode(CLzmaDecoderState *vs,
+ #ifdef _LZMA_IN_CB
+ ILzmaInCallback *InCallback,
+ #else
+ const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
+ #endif
+ unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed)
+{
+ CProb *p = vs->Probs;
+ SizeT nowPos = 0;
+ Byte previousByte = 0;
+ UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1;
+ UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1;
+ int lc = vs->Properties.lc;
+
+ #ifdef _LZMA_OUT_READ
+
+ UInt32 Range = vs->Range;
+ UInt32 Code = vs->Code;
+ #ifdef _LZMA_IN_CB
+ const Byte *Buffer = vs->Buffer;
+ const Byte *BufferLim = vs->BufferLim;
+ #else
+ const Byte *Buffer = inStream;
+ const Byte *BufferLim = inStream + inSize;
+ #endif
+ int state = vs->State;
+ UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
+ int len = vs->RemainLen;
+ UInt32 globalPos = vs->GlobalPos;
+ UInt32 distanceLimit = vs->DistanceLimit;
+
+ Byte *dictionary = vs->Dictionary;
+ UInt32 dictionarySize = vs->Properties.DictionarySize;
+ UInt32 dictionaryPos = vs->DictionaryPos;
+
+ Byte tempDictionary[4];
+
+ #ifndef _LZMA_IN_CB
+ *inSizeProcessed = 0;
+ #endif
+ *outSizeProcessed = 0;
+ if (len == kLzmaStreamWasFinishedId)
+ return LZMA_RESULT_OK;
+
+ if (dictionarySize == 0)
+ {
+ dictionary = tempDictionary;
+ dictionarySize = 1;
+ tempDictionary[0] = vs->TempDictionary[0];
+ }
+
+ if (len == kLzmaNeedInitId)
+ {
+ {
+ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
+ UInt32 i;
+ for (i = 0; i < numProbs; i++)
+ p[i] = kBitModelTotal >> 1;
+ rep0 = rep1 = rep2 = rep3 = 1;
+ state = 0;
+ globalPos = 0;
+ distanceLimit = 0;
+ dictionaryPos = 0;
+ dictionary[dictionarySize - 1] = 0;
+ #ifdef _LZMA_IN_CB
+ RC_INIT;
+ #else
+ RC_INIT(inStream, inSize);
+ #endif
+ }
+ len = 0;
+ }
+ while(len != 0 && nowPos < outSize)
+ {
+ UInt32 pos = dictionaryPos - rep0;
+ if (pos >= dictionarySize)
+ pos += dictionarySize;
+ outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
+ if (++dictionaryPos == dictionarySize)
+ dictionaryPos = 0;
+ len--;
+ }
+ if (dictionaryPos == 0)
+ previousByte = dictionary[dictionarySize - 1];
+ else
+ previousByte = dictionary[dictionaryPos - 1];
+
+ #else /* if !_LZMA_OUT_READ */
+
+ int state = 0;
+ UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
+ int len = 0;
+ const Byte *Buffer;
+ const Byte *BufferLim;
+ UInt32 Range;
+ UInt32 Code;
+
+ #ifndef _LZMA_IN_CB
+ *inSizeProcessed = 0;
+ #endif
+ *outSizeProcessed = 0;
+
+ {
+ UInt32 i;
+ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
+ for (i = 0; i < numProbs; i++)
+ p[i] = kBitModelTotal >> 1;
+ }
+
+ #ifdef _LZMA_IN_CB
+ RC_INIT;
+ #else
+ RC_INIT(inStream, inSize);
+ #endif
+
+ #endif /* _LZMA_OUT_READ */
+
+ while(nowPos < outSize)
+ {
+ CProb *prob;
+ UInt32 bound;
+ int posState = (int)(
+ (nowPos
+ #ifdef _LZMA_OUT_READ
+ + globalPos
+ #endif
+ )
+ & posStateMask);
+
+ prob = p + IsMatch + (state << kNumPosBitsMax) + posState;
+ IfBit0(prob)
+ {
+ int symbol = 1;
+ UpdateBit0(prob)
+ prob = p + Literal + (LZMA_LIT_SIZE *
+ (((
+ (nowPos
+ #ifdef _LZMA_OUT_READ
+ + globalPos
+ #endif
+ )
+ & literalPosMask) << lc) + (previousByte >> (8 - lc))));
+
+ if (state >= kNumLitStates)
+ {
+ int matchByte;
+ #ifdef _LZMA_OUT_READ
+ UInt32 pos = dictionaryPos - rep0;
+ if (pos >= dictionarySize)
+ pos += dictionarySize;
+ matchByte = dictionary[pos];
+ #else
+ matchByte = outStream[nowPos - rep0];
+ #endif
+ do
+ {
+ int bit;
+ CProb *probLit;
+ matchByte <<= 1;
+ bit = (matchByte & 0x100);
+ probLit = prob + 0x100 + bit + symbol;
+ RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break)
+ }
+ while (symbol < 0x100);
+ }
+ while (symbol < 0x100)
+ {
+ CProb *probLit = prob + symbol;
+ RC_GET_BIT(probLit, symbol)
+ }
+ previousByte = (Byte)symbol;
+
+ outStream[nowPos++] = previousByte;
+ #ifdef _LZMA_OUT_READ
+ if (distanceLimit < dictionarySize)
+ distanceLimit++;
+
+ dictionary[dictionaryPos] = previousByte;
+ if (++dictionaryPos == dictionarySize)
+ dictionaryPos = 0;
+ #endif
+ if (state < 4) state = 0;
+ else if (state < 10) state -= 3;
+ else state -= 6;
+ }
+ else
+ {
+ UpdateBit1(prob);
+ prob = p + IsRep + state;
+ IfBit0(prob)
+ {
+ UpdateBit0(prob);
+ rep3 = rep2;
+ rep2 = rep1;
+ rep1 = rep0;
+ state = state < kNumLitStates ? 0 : 3;
+ prob = p + LenCoder;
+ }
+ else
+ {
+ UpdateBit1(prob);
+ prob = p + IsRepG0 + state;
+ IfBit0(prob)
+ {
+ UpdateBit0(prob);
+ prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState;
+ IfBit0(prob)
+ {
+ #ifdef _LZMA_OUT_READ
+ UInt32 pos;
+ #endif
+ UpdateBit0(prob);
+
+ #ifdef _LZMA_OUT_READ
+ if (distanceLimit == 0)
+ #else
+ if (nowPos == 0)
+ #endif
+ return LZMA_RESULT_DATA_ERROR;
+
+ state = state < kNumLitStates ? 9 : 11;
+ #ifdef _LZMA_OUT_READ
+ pos = dictionaryPos - rep0;
+ if (pos >= dictionarySize)
+ pos += dictionarySize;
+ previousByte = dictionary[pos];
+ dictionary[dictionaryPos] = previousByte;
+ if (++dictionaryPos == dictionarySize)
+ dictionaryPos = 0;
+ #else
+ previousByte = outStream[nowPos - rep0];
+ #endif
+ outStream[nowPos++] = previousByte;
+ #ifdef _LZMA_OUT_READ
+ if (distanceLimit < dictionarySize)
+ distanceLimit++;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ UpdateBit1(prob);
+ }
+ }
+ else
+ {
+ UInt32 distance;
+ UpdateBit1(prob);
+ prob = p + IsRepG1 + state;
+ IfBit0(prob)
+ {
+ UpdateBit0(prob);
+ distance = rep1;
+ }
+ else
+ {
+ UpdateBit1(prob);
+ prob = p + IsRepG2 + state;
+ IfBit0(prob)
+ {
+ UpdateBit0(prob);
+ distance = rep2;
+ }
+ else
+ {
+ UpdateBit1(prob);
+ distance = rep3;
+ rep3 = rep2;
+ }
+ rep2 = rep1;
+ }
+ rep1 = rep0;
+ rep0 = distance;
+ }
+ state = state < kNumLitStates ? 8 : 11;
+ prob = p + RepLenCoder;
+ }
+ {
+ int numBits, offset;
+ CProb *probLen = prob + LenChoice;
+ IfBit0(probLen)
+ {
+ UpdateBit0(probLen);
+ probLen = prob + LenLow + (posState << kLenNumLowBits);
+ offset = 0;
+ numBits = kLenNumLowBits;
+ }
+ else
+ {
+ UpdateBit1(probLen);
+ probLen = prob + LenChoice2;
+ IfBit0(probLen)
+ {
+ UpdateBit0(probLen);
+ probLen = prob + LenMid + (posState << kLenNumMidBits);
+ offset = kLenNumLowSymbols;
+ numBits = kLenNumMidBits;
+ }
+ else
+ {
+ UpdateBit1(probLen);
+ probLen = prob + LenHigh;
+ offset = kLenNumLowSymbols + kLenNumMidSymbols;
+ numBits = kLenNumHighBits;
+ }
+ }
+ RangeDecoderBitTreeDecode(probLen, numBits, len);
+ len += offset;
+ }
+
+ if (state < 4)
+ {
+ int posSlot;
+ state += kNumLitStates;
+ prob = p + PosSlot +
+ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
+ kNumPosSlotBits);
+ RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot);
+ if (posSlot >= kStartPosModelIndex)
+ {
+ int numDirectBits = ((posSlot >> 1) - 1);
+ rep0 = (2 | ((UInt32)posSlot & 1));
+ if (posSlot < kEndPosModelIndex)
+ {
+ rep0 <<= numDirectBits;
+ prob = p + SpecPos + rep0 - posSlot - 1;
+ }
+ else
+ {
+ numDirectBits -= kNumAlignBits;
+ do
+ {
+ RC_NORMALIZE
+ Range >>= 1;
+ rep0 <<= 1;
+ if (Code >= Range)
+ {
+ Code -= Range;
+ rep0 |= 1;
+ }
+ }
+ while (--numDirectBits != 0);
+ prob = p + Align;
+ rep0 <<= kNumAlignBits;
+ numDirectBits = kNumAlignBits;
+ }
+ {
+ int i = 1;
+ int mi = 1;
+ do
+ {
+ CProb *prob3 = prob + mi;
+ RC_GET_BIT2(prob3, mi, ; , rep0 |= i);
+ i <<= 1;
+ }
+ while(--numDirectBits != 0);
+ }
+ }
+ else
+ rep0 = posSlot;
+ if (++rep0 == (UInt32)(0))
+ {
+ /* it's for stream version */
+ len = kLzmaStreamWasFinishedId;
+ break;
+ }
+ }
+
+ len += kMatchMinLen;
+ #ifdef _LZMA_OUT_READ
+ if (rep0 > distanceLimit)
+ #else
+ if (rep0 > nowPos)
+ #endif
+ return LZMA_RESULT_DATA_ERROR;
+
+ #ifdef _LZMA_OUT_READ
+ if (dictionarySize - distanceLimit > (UInt32)len)
+ distanceLimit += len;
+ else
+ distanceLimit = dictionarySize;
+ #endif
+
+ do
+ {
+ #ifdef _LZMA_OUT_READ
+ UInt32 pos = dictionaryPos - rep0;
+ if (pos >= dictionarySize)
+ pos += dictionarySize;
+ previousByte = dictionary[pos];
+ dictionary[dictionaryPos] = previousByte;
+ if (++dictionaryPos == dictionarySize)
+ dictionaryPos = 0;
+ #else
+ previousByte = outStream[nowPos - rep0];
+ #endif
+ len--;
+ outStream[nowPos++] = previousByte;
+ }
+ while(len != 0 && nowPos < outSize);
+ }
+ }
+ RC_NORMALIZE;
+
+ #ifdef _LZMA_OUT_READ
+ vs->Range = Range;
+ vs->Code = Code;
+ vs->DictionaryPos = dictionaryPos;
+ vs->GlobalPos = globalPos + (UInt32)nowPos;
+ vs->DistanceLimit = distanceLimit;
+ vs->Reps[0] = rep0;
+ vs->Reps[1] = rep1;
+ vs->Reps[2] = rep2;
+ vs->Reps[3] = rep3;
+ vs->State = state;
+ vs->RemainLen = len;
+ vs->TempDictionary[0] = tempDictionary[0];
+ #endif
+
+ #ifdef _LZMA_IN_CB
+ vs->Buffer = Buffer;
+ vs->BufferLim = BufferLim;
+ #else
+ *inSizeProcessed = (SizeT)(Buffer - inStream);
+ #endif
+ *outSizeProcessed = nowPos;
+ return LZMA_RESULT_OK;
+}
--- /dev/null
+/*
+ LzmaDecode.h
+ LZMA Decoder interface
+
+ LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01)
+ http://www.7-zip.org/
+
+ LZMA SDK is licensed under two licenses:
+ 1) GNU Lesser General Public License (GNU LGPL)
+ 2) Common Public License (CPL)
+ It means that you can select one of these two licenses and
+ follow rules of that license.
+
+ SPECIAL EXCEPTION:
+ Igor Pavlov, as the author of this code, expressly permits you to
+ statically or dynamically link your code (or bind by name) to the
+ interfaces of this file without subjecting your linked code to the
+ terms of the CPL or GNU LGPL. Any modifications or additions
+ to this file, however, are subject to the LGPL or CPL terms.
+*/
+
+#ifndef __LZMADECODE_H
+#define __LZMADECODE_H
+
+#include "LzmaTypes.h"
+
+/* #define _LZMA_IN_CB */
+/* Use callback for input data */
+
+/* #define _LZMA_OUT_READ */
+/* Use read function for output data */
+
+/* #define _LZMA_PROB32 */
+/* It can increase speed on some 32-bit CPUs,
+ but memory usage will be doubled in that case */
+
+/* #define _LZMA_LOC_OPT */
+/* Enable local speed optimizations inside code */
+
+#ifdef _LZMA_PROB32
+#define CProb UInt32
+#else
+#define CProb UInt16
+#endif
+
+#define LZMA_RESULT_OK 0
+#define LZMA_RESULT_DATA_ERROR 1
+
+#ifdef _LZMA_IN_CB
+typedef struct _ILzmaInCallback
+{
+ int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize);
+} ILzmaInCallback;
+#endif
+
+#define LZMA_BASE_SIZE 1846
+#define LZMA_LIT_SIZE 768
+
+#define LZMA_PROPERTIES_SIZE 5
+
+typedef struct _CLzmaProperties
+{
+ int lc;
+ int lp;
+ int pb;
+ #ifdef _LZMA_OUT_READ
+ UInt32 DictionarySize;
+ #endif
+}CLzmaProperties;
+
+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size);
+
+#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp)))
+
+#define kLzmaNeedInitId (-2)
+
+typedef struct _CLzmaDecoderState
+{
+ CLzmaProperties Properties;
+ CProb *Probs;
+
+ #ifdef _LZMA_IN_CB
+ const unsigned char *Buffer;
+ const unsigned char *BufferLim;
+ #endif
+
+ #ifdef _LZMA_OUT_READ
+ unsigned char *Dictionary;
+ UInt32 Range;
+ UInt32 Code;
+ UInt32 DictionaryPos;
+ UInt32 GlobalPos;
+ UInt32 DistanceLimit;
+ UInt32 Reps[4];
+ int State;
+ int RemainLen;
+ unsigned char TempDictionary[4];
+ #endif
+} CLzmaDecoderState;
+
+#ifdef _LZMA_OUT_READ
+#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; }
+#endif
+
+int LzmaDecode(CLzmaDecoderState *vs,
+ #ifdef _LZMA_IN_CB
+ ILzmaInCallback *inCallback,
+ #else
+ const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
+ #endif
+ unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed);
+
+#endif
--- /dev/null
+/*
+LzmaTypes.h
+
+Types for LZMA Decoder
+
+This file written and distributed to public domain by Igor Pavlov.
+This file is part of LZMA SDK 4.40 (2006-05-01)
+*/
+
+#ifndef __LZMATYPES_H
+#define __LZMATYPES_H
+
+#ifndef _7ZIP_BYTE_DEFINED
+#define _7ZIP_BYTE_DEFINED
+typedef unsigned char Byte;
+#endif
+
+#ifndef _7ZIP_UINT16_DEFINED
+#define _7ZIP_UINT16_DEFINED
+typedef unsigned short UInt16;
+#endif
+
+#ifndef _7ZIP_UINT32_DEFINED
+#define _7ZIP_UINT32_DEFINED
+#ifdef _LZMA_UINT32_IS_ULONG
+typedef unsigned long UInt32;
+#else
+typedef unsigned int UInt32;
+#endif
+#endif
+
+/* #define _LZMA_SYSTEM_SIZE_T */
+/* Use system's size_t. You can use it to enable 64-bit sizes supporting */
+
+#ifndef _7ZIP_SIZET_DEFINED
+#define _7ZIP_SIZET_DEFINED
+#ifdef _LZMA_SYSTEM_SIZE_T
+#include <stddef.h>
+typedef size_t SizeT;
+#else
+typedef UInt32 SizeT;
+#endif
+#endif
+
+#endif
--- /dev/null
+@echo off
+rem this is a simple batch file to build PhysicsFS on OS/2. You need to have
+rem the Innotek libc and GCC (or "kLIBC") installed for this to work:
+rem
+rem http://svn.netlabs.org/libc
+rem
+rem This script (and, indeed, our OS/2 support) could use some tweaking.
+rem Patches go to icculus@icculus.org ...
+
+set PHYSFSLANG=PHYSFS_LANG_ENGLISH
+set DEBUGFLAGS=-D_NDEBUG -O2 -s
+rem set CFLAGS=%DEBUGFLAGS% -Wall -Werror -Zomf -Zmt -Zmtd -I. -Izlib123 -c -D__ST_MT_ERRNO__ -DOS2 -DZ_PREFIX -DPHYSFS_SUPPORTS_ZIP -DPHYSFS_SUPPORTS_7Z -DPHYSFS_SUPPORTS_GRP -DPHYSFS_SUPPORTS_WAD -DPHYSFS_SUPPORTS_QPAK -DPHYSFS_SUPPORTS_HOG -DPHYSFS_SUPPORTS_MVL -DPHYSFS_LANG=%PHYSFSLANG% -DHAVE_ASSERT_H
+set CFLAGS=%DEBUGFLAGS% -Wall -Werror -Zomf -I. -Iz -c -D__ST_MT_ERRNO__ -DOS2 -DZ_PREFIX -DPHYSFS_SUPPORTS_ZIP -DPHYSFS_SUPPORTS_7Z -DPHYSFS_SUPPORTS_GRP -DPHYSFS_SUPPORTS_WAD -DPHYSFS_SUPPORTS_QPAK -DPHYSFS_SUPPORTS_HOG -DPHYSFS_SUPPORTS_MVL -DHAVE_ASSERT_H
+
+rem goto :dolinking
+
+@echo cleaning up any previous build...
+@mkdir bin 2>NUL
+@erase /N bin\*.* 2>NUL
+
+@echo Building export definitions...
+@echo ;don't edit this directly! It is rewritten by makeos2.cmd! > bin\test_physfs.def
+@echo NAME TESTPHYSFS WINDOWCOMPAT >> bin\test_physfs.def
+@echo DESCRIPTION 'PhysicsFS: http://icculus.org/physfs/' >> bin\test_physfs.def
+@echo STACKSIZE 0x10000 >> bin\test_physfs.def
+@echo BASE=0x10000 >> bin\test_physfs.def
+@echo PROTMODE >> bin\test_physfs.def
+
+@echo ;don't edit this directly! It is rewritten by makeos2.cmd! > bin\physfs.def
+@echo LIBRARY 'physfs' INITINSTANCE TERMINSTANCE >> bin\physfs.def
+@echo STACKSIZE 0x10000 >> bin\physfs.def
+@echo CODE LOADONCALL >> bin\physfs.def
+@echo DATA LOADONCALL NONSHARED MULTIPLE >> bin\physfs.def
+@echo DESCRIPTION 'PhysicsFS: http://icculus.org/physfs/' >> bin\physfs.def
+@echo EXPORTS >> bin\physfs.def
+@echo "_PHYSFS_getLinkedVersion" >> bin\physfs.def
+@echo "_PHYSFS_init" >> bin\physfs.def
+@echo "_PHYSFS_deinit" >> bin\physfs.def
+@echo "_PHYSFS_isInit" >> bin\physfs.def
+@echo "_PHYSFS_supportedArchiveTypes" >> bin\physfs.def
+@echo "_PHYSFS_freeList" >> bin\physfs.def
+@echo "_PHYSFS_getLastError" >> bin\physfs.def
+@echo "_PHYSFS_getDirSeparator" >> bin\physfs.def
+@echo "_PHYSFS_permitSymbolicLinks" >> bin\physfs.def
+@echo "_PHYSFS_symbolicLinksPermitted" >> bin\physfs.def
+@echo "_PHYSFS_getCdRomDirs" >> bin\physfs.def
+@echo "_PHYSFS_getBaseDir" >> bin\physfs.def
+@echo "_PHYSFS_getUserDir" >> bin\physfs.def
+@echo "_PHYSFS_getWriteDir" >> bin\physfs.def
+@echo "_PHYSFS_setWriteDir" >> bin\physfs.def
+@echo "_PHYSFS_addToSearchPath" >> bin\physfs.def
+@echo "_PHYSFS_removeFromSearchPath" >> bin\physfs.def
+@echo "_PHYSFS_getSearchPath" >> bin\physfs.def
+@echo "_PHYSFS_setSaneConfig" >> bin\physfs.def
+@echo "_PHYSFS_mkdir" >> bin\physfs.def
+@echo "_PHYSFS_delete" >> bin\physfs.def
+@echo "_PHYSFS_getRealDir" >> bin\physfs.def
+@echo "_PHYSFS_enumerateFiles" >> bin\physfs.def
+@echo "_PHYSFS_exists" >> bin\physfs.def
+@echo "_PHYSFS_isDirectory" >> bin\physfs.def
+@echo "_PHYSFS_isSymbolicLink" >> bin\physfs.def
+@echo "_PHYSFS_openWrite" >> bin\physfs.def
+@echo "_PHYSFS_openAppend" >> bin\physfs.def
+@echo "_PHYSFS_openRead" >> bin\physfs.def
+@echo "_PHYSFS_close" >> bin\physfs.def
+@echo "_PHYSFS_read" >> bin\physfs.def
+@echo "_PHYSFS_write" >> bin\physfs.def
+@echo "_PHYSFS_eof" >> bin\physfs.def
+@echo "_PHYSFS_tell" >> bin\physfs.def
+@echo "_PHYSFS_seek" >> bin\physfs.def
+@echo "_PHYSFS_fileLength" >> bin\physfs.def
+@echo "_PHYSFS_swapSLE16" >> bin\physfs.def
+@echo "_PHYSFS_swapULE16" >> bin\physfs.def
+@echo "_PHYSFS_swapSLE32" >> bin\physfs.def
+@echo "_PHYSFS_swapULE32" >> bin\physfs.def
+@echo "_PHYSFS_swapSLE64" >> bin\physfs.def
+@echo "_PHYSFS_swapULE64" >> bin\physfs.def
+@echo "_PHYSFS_swapSBE16" >> bin\physfs.def
+@echo "_PHYSFS_swapUBE16" >> bin\physfs.def
+@echo "_PHYSFS_swapSBE32" >> bin\physfs.def
+@echo "_PHYSFS_swapUBE32" >> bin\physfs.def
+@echo "_PHYSFS_swapSBE64" >> bin\physfs.def
+@echo "_PHYSFS_swapUBE64" >> bin\physfs.def
+@echo "_PHYSFS_getLastModTime" >> bin\physfs.def
+@echo "_PHYSFS_readSLE16" >> bin\physfs.def
+@echo "_PHYSFS_readULE16" >> bin\physfs.def
+@echo "_PHYSFS_readSLE32" >> bin\physfs.def
+@echo "_PHYSFS_readULE32" >> bin\physfs.def
+@echo "_PHYSFS_readSLE64" >> bin\physfs.def
+@echo "_PHYSFS_readULE64" >> bin\physfs.def
+@echo "_PHYSFS_readSBE16" >> bin\physfs.def
+@echo "_PHYSFS_readUBE16" >> bin\physfs.def
+@echo "_PHYSFS_readSBE32" >> bin\physfs.def
+@echo "_PHYSFS_readUBE32" >> bin\physfs.def
+@echo "_PHYSFS_readSBE64" >> bin\physfs.def
+@echo "_PHYSFS_readUBE64" >> bin\physfs.def
+@echo "_PHYSFS_writeSLE16" >> bin\physfs.def
+@echo "_PHYSFS_writeULE16" >> bin\physfs.def
+@echo "_PHYSFS_writeSLE32" >> bin\physfs.def
+@echo "_PHYSFS_writeULE32" >> bin\physfs.def
+@echo "_PHYSFS_writeSLE64" >> bin\physfs.def
+@echo "_PHYSFS_writeULE64" >> bin\physfs.def
+@echo "_PHYSFS_writeSBE16" >> bin\physfs.def
+@echo "_PHYSFS_writeUBE16" >> bin\physfs.def
+@echo "_PHYSFS_writeSBE32" >> bin\physfs.def
+@echo "_PHYSFS_writeUBE32" >> bin\physfs.def
+@echo "_PHYSFS_writeSBE64" >> bin\physfs.def
+@echo "_PHYSFS_writeUBE64" >> bin\physfs.def
+@echo "_PHYSFS_setBuffer" >> bin\physfs.def
+@echo "_PHYSFS_flush" >> bin\physfs.def
+@echo "_PHYSFS_mount" >> bin\physfs.def
+@echo "_PHYSFS_getMountPoint" >> bin\physfs.def
+@echo "_PHYSFS_setAllocator" >> bin\physfs.def
+@echo "_PHYSFS_getCdRomDirsCallback" >> bin\physfs.def
+@echo "_PHYSFS_getSearchPathCallback" >> bin\physfs.def
+@echo "_PHYSFS_enumerateFilesCallback" >> bin\physfs.def
+@echo "_PHYSFS_utf8ToUcs2" >> bin\physfs.def
+@echo "_PHYSFS_utf8FromUcs2" >> bin\physfs.def
+@echo "_PHYSFS_utf8ToUcs4" >> bin\physfs.def
+@echo "_PHYSFS_utf8FromUcs4" >> bin\physfs.def
+@echo "_PHYSFS_utf8FromLatin1" >> bin\physfs.def
+
+@echo Building export library...
+emximp -o bin/physfs.lib bin/physfs.def
+emximp -o bin/physfs.a bin/physfs.def
+
+@echo Compiling PhysicsFS library...
+@echo on
+gcc %CFLAGS% -o bin/physfs.obj physfs.c
+gcc %CFLAGS% -o bin/physfs_byteorder.obj physfs_byteorder.c
+gcc %CFLAGS% -o bin/physfs_unicode.obj physfs_unicode.c
+gcc %CFLAGS% -o bin/os2.obj platform/os2.c
+gcc %CFLAGS% -o bin/dir.obj archivers/dir.c
+gcc %CFLAGS% -o bin/grp.obj archivers/grp.c
+gcc %CFLAGS% -o bin/wad.obj archivers/wad.c
+gcc %CFLAGS% -o bin/lzma.obj archivers/lzma.c
+gcc %CFLAGS% -o bin/zip.obj archivers/zip.c
+gcc %CFLAGS% -o bin/qpak.obj archivers/qpak.c
+gcc %CFLAGS% -o bin/hog.obj archivers/hog.c
+gcc %CFLAGS% -o bin/mvl.obj archivers/mvl.c
+gcc %CFLAGS% -o bin/adler32.obj zlib123/adler32.c
+gcc %CFLAGS% -o bin/compress.obj zlib123/compress.c
+gcc %CFLAGS% -o bin/crc32.obj zlib123/crc32.c
+gcc %CFLAGS% -o bin/deflate.obj zlib123/deflate.c
+gcc %CFLAGS% -o bin/gzio.obj zlib123/gzio.c
+gcc %CFLAGS% -o bin/infback.obj zlib123/infback.c
+gcc %CFLAGS% -o bin/inffast.obj zlib123/inffast.c
+gcc %CFLAGS% -o bin/inflate.obj zlib123/inflate.c
+gcc %CFLAGS% -o bin/inftrees.obj zlib123/inftrees.c
+gcc %CFLAGS% -o bin/trees.obj zlib123/trees.c
+gcc %CFLAGS% -o bin/uncompr.obj zlib123/uncompr.c
+gcc %CFLAGS% -o bin/zutil.obj zlib123/zutil.c
+gcc %CFLAGS% -o bin/7zBuffer.obj lzma/7zBuffer.c
+gcc %CFLAGS% -o bin/7zCrc.obj lzma/7zCrc.c
+gcc %CFLAGS% -o bin/7zDecode.obj lzma/7zDecode.c
+gcc %CFLAGS% -o bin/7zExtract.obj lzma/7zExtract.c
+gcc %CFLAGS% -o bin/7zHeader.obj lzma/7zHeader.c
+gcc %CFLAGS% -o bin/7zIn.obj lzma/7zIn.c
+gcc %CFLAGS% -o bin/7zItem.obj lzma/7zItem.c
+gcc %CFLAGS% -o bin/7zMethodID.obj lzma/7zMethodID.c
+gcc %CFLAGS% -o bin/LzmaDecode.obj lzma/LzmaDecode.c
+gcc %CFLAGS% -o bin/LzmaStateDecode.obj lzma/LzmaStateDecode.c
+@echo off
+
+:dolinking
+@echo Linking PhysicsFS library...
+gcc %DEBUGFLAGS% -Zdll -Zcrtdll -Zomf -o bin/physfs.dll bin/*.obj bin/physfs.def
+
+rem goto :builddone
+
+@echo Compiling test program...
+gcc %CFLAGS% -o bin/test_physfs.obj test/test_physfs.c
+@echo Linking test program...
+gcc %DEBUGFLAGS% -Zomf -Zcrtdll -o bin/test_physfs.exe bin/test_physfs.obj bin/physfs.lib bin/test_physfs.def
+
+:builddone
+
+@echo "All done!"
+
+rem end of makeos2.cmd ...
+
--- /dev/null
+Performing C SOURCE FILE Test PHYSFS_IS_GCC4 failed with the following output:
+
+Source file was:
+
+ #if ((defined(__GNUC__)) && (__GNUC__ >= 4))
+ int main(int argc, char **argv) { int is_gcc4 = 1; return 0; }
+ #else
+ #error This is not gcc4.
+ #endif
+
+Determining if the include file sys/ucred.h exists failed with the following output:
+
+
+Determining if the include file mntent.h exists failed with the following output:
+
+
+Determining if the include file pthread.h exists failed with the following output:
+
+
+Determining if the include file assert.h exists failed with the following output:
+
+
+Determining if the include file zlib.h exists failed with the following output:
+
+
+Determining if the include file readline/readline.h exists failed with the following output:
+
+
+Determining if the include file readline/history.h exists failed with the following output:
+
+
--- /dev/null
+
+ #if ((defined(__GNUC__)) && (__GNUC__ >= 4))
+ int main(int argc, char **argv) { int is_gcc4 = 1; return 0; }
+ #else
+ #error This is not gcc4.
+ #endif
+
--- /dev/null
+/**
+ * PhysicsFS; a portable, flexible file i/o abstraction.
+ *
+ * Documentation is in physfs.h. It's verbose, honest. :)
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "physfs.h"
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+
+typedef struct __PHYSFS_DIRHANDLE__
+{
+ void *opaque; /* Instance data unique to the archiver. */
+ char *dirName; /* Path to archive in platform-dependent notation. */
+ char *mountPoint; /* Mountpoint in virtual file tree. */
+ const PHYSFS_Archiver *funcs; /* Ptr to archiver info for this handle. */
+ struct __PHYSFS_DIRHANDLE__ *next; /* linked list stuff. */
+} DirHandle;
+
+
+typedef struct __PHYSFS_FILEHANDLE__
+{
+ void *opaque; /* Instance data unique to the archiver for this file. */
+ PHYSFS_uint8 forReading; /* Non-zero if reading, zero if write/append */
+ const DirHandle *dirHandle; /* Archiver instance that created this */
+ const PHYSFS_Archiver *funcs; /* Ptr to archiver info for this handle. */
+ PHYSFS_uint8 *buffer; /* Buffer, if set (NULL otherwise). Don't touch! */
+ PHYSFS_uint32 bufsize; /* Bufsize, if set (0 otherwise). Don't touch! */
+ PHYSFS_uint32 buffill; /* Buffer fill size. Don't touch! */
+ PHYSFS_uint32 bufpos; /* Buffer position. Don't touch! */
+ struct __PHYSFS_FILEHANDLE__ *next; /* linked list stuff. */
+} FileHandle;
+
+
+typedef struct __PHYSFS_ERRMSGTYPE__
+{
+ PHYSFS_uint64 tid;
+ int errorAvailable;
+ char errorString[80];
+ struct __PHYSFS_ERRMSGTYPE__ *next;
+} ErrMsg;
+
+
+/* The various i/o drivers...some of these may not be compiled in. */
+extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP;
+extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP;
+extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA;
+extern const PHYSFS_Archiver __PHYSFS_Archiver_LZMA;
+extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP;
+extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP;
+extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK;
+extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK;
+extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG;
+extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG;
+extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL;
+extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL;
+extern const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD;
+extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD;
+extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR;
+
+
+static const PHYSFS_ArchiveInfo *supported_types[] =
+{
+#if (defined PHYSFS_SUPPORTS_ZIP)
+ &__PHYSFS_ArchiveInfo_ZIP,
+#endif
+#if (defined PHYSFS_SUPPORTS_7Z)
+ &__PHYSFS_ArchiveInfo_LZMA,
+#endif
+#if (defined PHYSFS_SUPPORTS_GRP)
+ &__PHYSFS_ArchiveInfo_GRP,
+#endif
+#if (defined PHYSFS_SUPPORTS_QPAK)
+ &__PHYSFS_ArchiveInfo_QPAK,
+#endif
+#if (defined PHYSFS_SUPPORTS_HOG)
+ &__PHYSFS_ArchiveInfo_HOG,
+#endif
+#if (defined PHYSFS_SUPPORTS_MVL)
+ &__PHYSFS_ArchiveInfo_MVL,
+#endif
+#if (defined PHYSFS_SUPPORTS_WAD)
+ &__PHYSFS_ArchiveInfo_WAD,
+#endif
+ NULL
+};
+
+static const PHYSFS_Archiver *archivers[] =
+{
+ &__PHYSFS_Archiver_DIR,
+#if (defined PHYSFS_SUPPORTS_ZIP)
+ &__PHYSFS_Archiver_ZIP,
+#endif
+#if (defined PHYSFS_SUPPORTS_7Z)
+ &__PHYSFS_Archiver_LZMA,
+#endif
+#if (defined PHYSFS_SUPPORTS_GRP)
+ &__PHYSFS_Archiver_GRP,
+#endif
+#if (defined PHYSFS_SUPPORTS_QPAK)
+ &__PHYSFS_Archiver_QPAK,
+#endif
+#if (defined PHYSFS_SUPPORTS_HOG)
+ &__PHYSFS_Archiver_HOG,
+#endif
+#if (defined PHYSFS_SUPPORTS_MVL)
+ &__PHYSFS_Archiver_MVL,
+#endif
+#if (defined PHYSFS_SUPPORTS_WAD)
+ &__PHYSFS_Archiver_WAD,
+#endif
+ NULL
+};
+
+
+
+/* General PhysicsFS state ... */
+static int initialized = 0;
+static ErrMsg *errorMessages = NULL;
+static DirHandle *searchPath = NULL;
+static DirHandle *writeDir = NULL;
+static FileHandle *openWriteList = NULL;
+static FileHandle *openReadList = NULL;
+static char *baseDir = NULL;
+static char *userDir = NULL;
+static int allowSymLinks = 0;
+
+/* mutexes ... */
+static void *errorLock = NULL; /* protects error message list. */
+static void *stateLock = NULL; /* protects other PhysFS static state. */
+
+/* allocator ... */
+static int externalAllocator = 0;
+PHYSFS_Allocator allocator;
+
+
+/* functions ... */
+
+typedef struct
+{
+ char **list;
+ PHYSFS_uint32 size;
+ const char *errorstr;
+} EnumStringListCallbackData;
+
+static void enumStringListCallback(void *data, const char *str)
+{
+ void *ptr;
+ char *newstr;
+ EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data;
+
+ if (pecd->errorstr)
+ return;
+
+ ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *));
+ newstr = (char *) allocator.Malloc(strlen(str) + 1);
+ if (ptr != NULL)
+ pecd->list = (char **) ptr;
+
+ if ((ptr == NULL) || (newstr == NULL))
+ {
+ pecd->errorstr = ERR_OUT_OF_MEMORY;
+ pecd->list[pecd->size] = NULL;
+ PHYSFS_freeList(pecd->list);
+ return;
+ } /* if */
+
+ strcpy(newstr, str);
+ pecd->list[pecd->size] = newstr;
+ pecd->size++;
+} /* enumStringListCallback */
+
+
+static char **doEnumStringList(void (*func)(PHYSFS_StringCallback, void *))
+{
+ EnumStringListCallbackData ecd;
+ memset(&ecd, '\0', sizeof (ecd));
+ ecd.list = (char **) allocator.Malloc(sizeof (char *));
+ BAIL_IF_MACRO(ecd.list == NULL, ERR_OUT_OF_MEMORY, NULL);
+ func(enumStringListCallback, &ecd);
+ BAIL_IF_MACRO(ecd.errorstr != NULL, ecd.errorstr, NULL);
+ ecd.list[ecd.size] = NULL;
+ return(ecd.list);
+} /* doEnumStringList */
+
+
+static void __PHYSFS_bubble_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi,
+ int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
+ void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
+{
+ PHYSFS_uint32 i;
+ int sorted;
+
+ do
+ {
+ sorted = 1;
+ for (i = lo; i < hi; i++)
+ {
+ if (cmpfn(a, i, i + 1) > 0)
+ {
+ swapfn(a, i, i + 1);
+ sorted = 0;
+ } /* if */
+ } /* for */
+ } while (!sorted);
+} /* __PHYSFS_bubble_sort */
+
+
+static void __PHYSFS_quick_sort(void *a, PHYSFS_uint32 lo, PHYSFS_uint32 hi,
+ int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
+ void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
+{
+ PHYSFS_uint32 i;
+ PHYSFS_uint32 j;
+ PHYSFS_uint32 v;
+
+ if ((hi - lo) <= PHYSFS_QUICKSORT_THRESHOLD)
+ __PHYSFS_bubble_sort(a, lo, hi, cmpfn, swapfn);
+ else
+ {
+ i = (hi + lo) / 2;
+
+ if (cmpfn(a, lo, i) > 0) swapfn(a, lo, i);
+ if (cmpfn(a, lo, hi) > 0) swapfn(a, lo, hi);
+ if (cmpfn(a, i, hi) > 0) swapfn(a, i, hi);
+
+ j = hi - 1;
+ swapfn(a, i, j);
+ i = lo;
+ v = j;
+ while (1)
+ {
+ while(cmpfn(a, ++i, v) < 0) { /* do nothing */ }
+ while(cmpfn(a, --j, v) > 0) { /* do nothing */ }
+ if (j < i)
+ break;
+ swapfn(a, i, j);
+ } /* while */
+ swapfn(a, i, hi-1);
+ __PHYSFS_quick_sort(a, lo, j, cmpfn, swapfn);
+ __PHYSFS_quick_sort(a, i+1, hi, cmpfn, swapfn);
+ } /* else */
+} /* __PHYSFS_quick_sort */
+
+
+void __PHYSFS_sort(void *entries, PHYSFS_uint32 max,
+ int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
+ void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32))
+{
+ /*
+ * Quicksort w/ Bubblesort fallback algorithm inspired by code from here:
+ * http://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html
+ */
+ __PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn);
+} /* __PHYSFS_sort */
+
+
+static ErrMsg *findErrorForCurrentThread(void)
+{
+ ErrMsg *i;
+ PHYSFS_uint64 tid;
+
+ if (errorLock != NULL)
+ __PHYSFS_platformGrabMutex(errorLock);
+
+ if (errorMessages != NULL)
+ {
+ tid = __PHYSFS_platformGetThreadID();
+
+ for (i = errorMessages; i != NULL; i = i->next)
+ {
+ if (i->tid == tid)
+ {
+ if (errorLock != NULL)
+ __PHYSFS_platformReleaseMutex(errorLock);
+ return(i);
+ } /* if */
+ } /* for */
+ } /* if */
+
+ if (errorLock != NULL)
+ __PHYSFS_platformReleaseMutex(errorLock);
+
+ return(NULL); /* no error available. */
+} /* findErrorForCurrentThread */
+
+
+void __PHYSFS_setError(const char *str)
+{
+ ErrMsg *err;
+
+ if (str == NULL)
+ return;
+
+ err = findErrorForCurrentThread();
+
+ if (err == NULL)
+ {
+ err = (ErrMsg *) allocator.Malloc(sizeof (ErrMsg));
+ if (err == NULL)
+ return; /* uhh...? */
+
+ memset((void *) err, '\0', sizeof (ErrMsg));
+ err->tid = __PHYSFS_platformGetThreadID();
+
+ if (errorLock != NULL)
+ __PHYSFS_platformGrabMutex(errorLock);
+
+ err->next = errorMessages;
+ errorMessages = err;
+
+ if (errorLock != NULL)
+ __PHYSFS_platformReleaseMutex(errorLock);
+ } /* if */
+
+ err->errorAvailable = 1;
+ strncpy(err->errorString, str, sizeof (err->errorString));
+ err->errorString[sizeof (err->errorString) - 1] = '\0';
+} /* __PHYSFS_setError */
+
+
+const char *PHYSFS_getLastError(void)
+{
+ ErrMsg *err = findErrorForCurrentThread();
+
+ if ((err == NULL) || (!err->errorAvailable))
+ return(NULL);
+
+ err->errorAvailable = 0;
+ return(err->errorString);
+} /* PHYSFS_getLastError */
+
+
+/* MAKE SURE that errorLock is held before calling this! */
+static void freeErrorMessages(void)
+{
+ ErrMsg *i;
+ ErrMsg *next;
+
+ for (i = errorMessages; i != NULL; i = next)
+ {
+ next = i->next;
+ allocator.Free(i);
+ } /* for */
+
+ errorMessages = NULL;
+} /* freeErrorMessages */
+
+
+void PHYSFS_getLinkedVersion(PHYSFS_Version *ver)
+{
+ if (ver != NULL)
+ {
+ ver->major = PHYSFS_VER_MAJOR;
+ ver->minor = PHYSFS_VER_MINOR;
+ ver->patch = PHYSFS_VER_PATCH;
+ } /* if */
+} /* PHYSFS_getLinkedVersion */
+
+
+static const char *find_filename_extension(const char *fname)
+{
+ const char *retval = strchr(fname, '.');
+ const char *p = retval;
+
+ while (p != NULL)
+ {
+ p = strchr(p + 1, '.');
+ if (p != NULL)
+ retval = p;
+ } /* while */
+
+ if (retval != NULL)
+ retval++; /* skip '.' */
+
+ return(retval);
+} /* find_filename_extension */
+
+
+static DirHandle *tryOpenDir(const PHYSFS_Archiver *funcs,
+ const char *d, int forWriting)
+{
+ DirHandle *retval = NULL;
+ if (funcs->isArchive(d, forWriting))
+ {
+ void *opaque = funcs->openArchive(d, forWriting);
+ if (opaque != NULL)
+ {
+ retval = (DirHandle *) allocator.Malloc(sizeof (DirHandle));
+ if (retval == NULL)
+ funcs->dirClose(opaque);
+ else
+ {
+ memset(retval, '\0', sizeof (DirHandle));
+ retval->mountPoint = NULL;
+ retval->funcs = funcs;
+ retval->opaque = opaque;
+ } /* else */
+ } /* if */
+ } /* if */
+
+ return(retval);
+} /* tryOpenDir */
+
+
+static DirHandle *openDirectory(const char *d, int forWriting)
+{
+ DirHandle *retval = NULL;
+ const PHYSFS_Archiver **i;
+ const char *ext;
+
+ BAIL_IF_MACRO(!__PHYSFS_platformExists(d), ERR_NO_SUCH_FILE, NULL);
+
+ ext = find_filename_extension(d);
+ if (ext != NULL)
+ {
+ /* Look for archivers with matching file extensions first... */
+ for (i = archivers; (*i != NULL) && (retval == NULL); i++)
+ {
+ if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) == 0)
+ retval = tryOpenDir(*i, d, forWriting);
+ } /* for */
+
+ /* failing an exact file extension match, try all the others... */
+ for (i = archivers; (*i != NULL) && (retval == NULL); i++)
+ {
+ if (__PHYSFS_stricmpASCII(ext, (*i)->info->extension) != 0)
+ retval = tryOpenDir(*i, d, forWriting);
+ } /* for */
+ } /* if */
+
+ else /* no extension? Try them all. */
+ {
+ for (i = archivers; (*i != NULL) && (retval == NULL); i++)
+ retval = tryOpenDir(*i, d, forWriting);
+ } /* else */
+
+ BAIL_IF_MACRO(retval == NULL, ERR_UNSUPPORTED_ARCHIVE, NULL);
+ return(retval);
+} /* openDirectory */
+
+
+/*
+ * Make a platform-independent path string sane. Doesn't actually check the
+ * file hierarchy, it just cleans up the string.
+ * (dst) must be a buffer at least as big as (src), as this is where the
+ * cleaned up string is deposited.
+ * If there are illegal bits in the path (".." entries, etc) then we
+ * return zero and (dst) is undefined. Non-zero if the path was sanitized.
+ */
+static int sanitizePlatformIndependentPath(const char *src, char *dst)
+{
+ char *prev;
+ char ch;
+
+ while (*src == '/') /* skip initial '/' chars... */
+ src++;
+
+ prev = dst;
+ do
+ {
+ ch = *(src++);
+
+ if ((ch == ':') || (ch == '\\')) /* illegal chars in a physfs path. */
+ BAIL_MACRO(ERR_INSECURE_FNAME, 0);
+
+ if (ch == '/') /* path separator. */
+ {
+ *dst = '\0'; /* "." and ".." are illegal pathnames. */
+ if ((strcmp(prev, ".") == 0) || (strcmp(prev, "..") == 0))
+ BAIL_MACRO(ERR_INSECURE_FNAME, 0);
+
+ while (*src == '/') /* chop out doubles... */
+ src++;
+
+ if (*src == '\0') /* ends with a pathsep? */
+ break; /* we're done, don't add final pathsep to dst. */
+
+ prev = dst + 1;
+ } /* if */
+
+ *(dst++) = ch;
+ } while (ch != '\0');
+
+ return(1);
+} /* sanitizePlatformIndependentPath */
+
+
+/*
+ * Figure out if (fname) is part of (h)'s mountpoint. (fname) must be an
+ * output from sanitizePlatformIndependentPath(), so that it is in a known
+ * state.
+ *
+ * This only finds legitimate segments of a mountpoint. If the mountpoint is
+ * "/a/b/c" and (fname) is "/a/b/c", "/", or "/a/b/c/d", then the results are
+ * all zero. "/a/b" will succeed, though.
+ */
+static int partOfMountPoint(DirHandle *h, char *fname)
+{
+ /* !!! FIXME: This code feels gross. */
+ int rc;
+ size_t len, mntpntlen;
+
+ if (h->mountPoint == NULL)
+ return(0);
+ else if (*fname == '\0')
+ return(1);
+
+ len = strlen(fname);
+ mntpntlen = strlen(h->mountPoint);
+ if (len > mntpntlen) /* can't be a subset of mountpoint. */
+ return(0);
+
+ /* if true, must be not a match or a complete match, but not a subset. */
+ if ((len + 1) == mntpntlen)
+ return(0);
+
+ rc = strncmp(fname, h->mountPoint, len); /* !!! FIXME: case insensitive? */
+ if (rc != 0)
+ return(0); /* not a match. */
+
+ /* make sure /a/b matches /a/b/ and not /a/bc ... */
+ return(h->mountPoint[len] == '/');
+} /* partOfMountPoint */
+
+
+static DirHandle *createDirHandle(const char *newDir,
+ const char *mountPoint,
+ int forWriting)
+{
+ DirHandle *dirHandle = NULL;
+ char *tmpmntpnt = NULL;
+
+ GOTO_IF_MACRO(!newDir, ERR_INVALID_ARGUMENT, badDirHandle);
+ if (mountPoint != NULL)
+ {
+ const size_t len = strlen(mountPoint) + 1;
+ tmpmntpnt = (char *) __PHYSFS_smallAlloc(len);
+ GOTO_IF_MACRO(!tmpmntpnt, ERR_OUT_OF_MEMORY, badDirHandle);
+ if (!sanitizePlatformIndependentPath(mountPoint, tmpmntpnt))
+ goto badDirHandle;
+ mountPoint = tmpmntpnt; /* sanitized version. */
+ } /* if */
+
+ dirHandle = openDirectory(newDir, forWriting);
+ GOTO_IF_MACRO(!dirHandle, NULL, badDirHandle);
+
+ dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1);
+ GOTO_IF_MACRO(!dirHandle->dirName, ERR_OUT_OF_MEMORY, badDirHandle);
+ strcpy(dirHandle->dirName, newDir);
+
+ if ((mountPoint != NULL) && (*mountPoint != '\0'))
+ {
+ dirHandle->mountPoint = (char *)allocator.Malloc(strlen(mountPoint)+2);
+ GOTO_IF_MACRO(!dirHandle->mountPoint, ERR_OUT_OF_MEMORY, badDirHandle);
+ strcpy(dirHandle->mountPoint, mountPoint);
+ strcat(dirHandle->mountPoint, "/");
+ } /* if */
+
+ __PHYSFS_smallFree(tmpmntpnt);
+ return(dirHandle);
+
+badDirHandle:
+ if (dirHandle != NULL)
+ {
+ dirHandle->funcs->dirClose(dirHandle->opaque);
+ allocator.Free(dirHandle->dirName);
+ allocator.Free(dirHandle->mountPoint);
+ allocator.Free(dirHandle);
+ } /* if */
+
+ __PHYSFS_smallFree(tmpmntpnt);
+ return(NULL);
+} /* createDirHandle */
+
+
+/* MAKE SURE you've got the stateLock held before calling this! */
+static int freeDirHandle(DirHandle *dh, FileHandle *openList)
+{
+ FileHandle *i;
+
+ if (dh == NULL)
+ return(1);
+
+ for (i = openList; i != NULL; i = i->next)
+ BAIL_IF_MACRO(i->dirHandle == dh, ERR_FILES_STILL_OPEN, 0);
+
+ dh->funcs->dirClose(dh->opaque);
+ allocator.Free(dh->dirName);
+ allocator.Free(dh->mountPoint);
+ allocator.Free(dh);
+ return(1);
+} /* freeDirHandle */
+
+
+static char *calculateUserDir(void)
+{
+ char *retval = NULL;
+ const char *str = NULL;
+
+ str = __PHYSFS_platformGetUserDir();
+ if (str != NULL)
+ retval = (char *) str;
+ else
+ {
+ const char *dirsep = PHYSFS_getDirSeparator();
+ const char *uname = __PHYSFS_platformGetUserName();
+
+ str = (uname != NULL) ? uname : "default";
+ retval = (char *) allocator.Malloc(strlen(baseDir) + strlen(str) +
+ strlen(dirsep) + 6);
+
+ if (retval == NULL)
+ __PHYSFS_setError(ERR_OUT_OF_MEMORY);
+ else
+ sprintf(retval, "%susers%s%s", baseDir, dirsep, str);
+
+ allocator.Free((void *) uname);
+ } /* else */
+
+ return(retval);
+} /* calculateUserDir */
+
+
+static int appendDirSep(char **dir)
+{
+ const char *dirsep = PHYSFS_getDirSeparator();
+ char *ptr;
+
+ if (strcmp((*dir + strlen(*dir)) - strlen(dirsep), dirsep) == 0)
+ return(1);
+
+ ptr = (char *) allocator.Realloc(*dir, strlen(*dir) + strlen(dirsep) + 1);
+ if (!ptr)
+ {
+ allocator.Free(*dir);
+ return(0);
+ } /* if */
+
+ strcat(ptr, dirsep);
+ *dir = ptr;
+ return(1);
+} /* appendDirSep */
+
+
+static char *calculateBaseDir(const char *argv0)
+{
+ char *retval = NULL;
+ const char *dirsep = NULL;
+ char *ptr = NULL;
+
+ /* Give the platform layer first shot at this. */
+ retval = __PHYSFS_platformCalcBaseDir(argv0);
+ if (retval != NULL)
+ return(retval);
+
+ /* We need argv0 to go on. */
+ BAIL_IF_MACRO(argv0 == NULL, ERR_ARGV0_IS_NULL, NULL);
+
+ dirsep = PHYSFS_getDirSeparator();
+ if (strlen(dirsep) == 1) /* fast path. */
+ ptr = strrchr(argv0, *dirsep);
+ else
+ {
+ ptr = strstr(argv0, dirsep);
+ if (ptr != NULL)
+ {
+ char *p = ptr;
+ while (p != NULL)
+ {
+ ptr = p;
+ p = strstr(p + 1, dirsep);
+ } /* while */
+ } /* if */
+ } /* else */
+
+ if (ptr != NULL)
+ {
+ size_t size = (size_t) (ptr - argv0);
+ retval = (char *) allocator.Malloc(size + 1);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+ memcpy(retval, argv0, size);
+ retval[size] = '\0';
+ return(retval);
+ } /* if */
+
+ /* argv0 wasn't helpful. */
+ BAIL_MACRO(ERR_INVALID_ARGUMENT, NULL);
+ return(NULL);
+} /* calculateBaseDir */
+
+
+static int initializeMutexes(void)
+{
+ errorLock = __PHYSFS_platformCreateMutex();
+ if (errorLock == NULL)
+ goto initializeMutexes_failed;
+
+ stateLock = __PHYSFS_platformCreateMutex();
+ if (stateLock == NULL)
+ goto initializeMutexes_failed;
+
+ return(1); /* success. */
+
+initializeMutexes_failed:
+ if (errorLock != NULL)
+ __PHYSFS_platformDestroyMutex(errorLock);
+
+ if (stateLock != NULL)
+ __PHYSFS_platformDestroyMutex(stateLock);
+
+ errorLock = stateLock = NULL;
+ return(0); /* failed. */
+} /* initializeMutexes */
+
+
+static void setDefaultAllocator(void);
+
+int PHYSFS_init(const char *argv0)
+{
+ char *ptr;
+
+ BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0);
+
+ if (!externalAllocator)
+ setDefaultAllocator();
+
+ if (allocator.Init != NULL)
+ BAIL_IF_MACRO(!allocator.Init(), NULL, 0);
+
+ BAIL_IF_MACRO(!__PHYSFS_platformInit(), NULL, 0);
+
+ BAIL_IF_MACRO(!initializeMutexes(), NULL, 0);
+
+ baseDir = calculateBaseDir(argv0);
+ BAIL_IF_MACRO(baseDir == NULL, NULL, 0);
+
+ /* !!! FIXME: only call this if we got this from argv0 (unreliable). */
+ ptr = __PHYSFS_platformRealPath(baseDir);
+ allocator.Free(baseDir);
+ BAIL_IF_MACRO(ptr == NULL, NULL, 0);
+ baseDir = ptr;
+
+ BAIL_IF_MACRO(!appendDirSep(&baseDir), NULL, 0);
+
+ userDir = calculateUserDir();
+ if (userDir != NULL)
+ {
+ ptr = __PHYSFS_platformRealPath(userDir);
+ allocator.Free(userDir);
+ userDir = ptr;
+ } /* if */
+
+ if ((userDir == NULL) || (!appendDirSep(&userDir)))
+ {
+ allocator.Free(baseDir);
+ baseDir = NULL;
+ return(0);
+ } /* if */
+
+ initialized = 1;
+
+ /* This makes sure that the error subsystem is initialized. */
+ __PHYSFS_setError(PHYSFS_getLastError());
+
+ return(1);
+} /* PHYSFS_init */
+
+
+/* MAKE SURE you hold stateLock before calling this! */
+static int closeFileHandleList(FileHandle **list)
+{
+ FileHandle *i;
+ FileHandle *next = NULL;
+
+ for (i = *list; i != NULL; i = next)
+ {
+ next = i->next;
+ if (!i->funcs->fileClose(i->opaque))
+ {
+ *list = i;
+ return(0);
+ } /* if */
+
+ allocator.Free(i);
+ } /* for */
+
+ *list = NULL;
+ return(1);
+} /* closeFileHandleList */
+
+
+/* MAKE SURE you hold the stateLock before calling this! */
+static void freeSearchPath(void)
+{
+ DirHandle *i;
+ DirHandle *next = NULL;
+
+ closeFileHandleList(&openReadList);
+
+ if (searchPath != NULL)
+ {
+ for (i = searchPath; i != NULL; i = next)
+ {
+ next = i->next;
+ freeDirHandle(i, openReadList);
+ } /* for */
+ searchPath = NULL;
+ } /* if */
+} /* freeSearchPath */
+
+
+int PHYSFS_deinit(void)
+{
+ BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
+ BAIL_IF_MACRO(!__PHYSFS_platformDeinit(), NULL, 0);
+
+ closeFileHandleList(&openWriteList);
+ BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), ERR_FILES_STILL_OPEN, 0);
+
+ freeSearchPath();
+ freeErrorMessages();
+
+ if (baseDir != NULL)
+ {
+ allocator.Free(baseDir);
+ baseDir = NULL;
+ } /* if */
+
+ if (userDir != NULL)
+ {
+ allocator.Free(userDir);
+ userDir = NULL;
+ } /* if */
+
+ allowSymLinks = 0;
+ initialized = 0;
+
+ __PHYSFS_platformDestroyMutex(errorLock);
+ __PHYSFS_platformDestroyMutex(stateLock);
+
+ if (allocator.Deinit != NULL)
+ allocator.Deinit();
+
+ errorLock = stateLock = NULL;
+ return(1);
+} /* PHYSFS_deinit */
+
+
+int PHYSFS_isInit(void)
+{
+ return(initialized);
+} /* PHYSFS_isInit */
+
+
+const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void)
+{
+ return(supported_types);
+} /* PHYSFS_supportedArchiveTypes */
+
+
+void PHYSFS_freeList(void *list)
+{
+ void **i;
+ for (i = (void **) list; *i != NULL; i++)
+ allocator.Free(*i);
+
+ allocator.Free(list);
+} /* PHYSFS_freeList */
+
+
+const char *PHYSFS_getDirSeparator(void)
+{
+ return(__PHYSFS_platformDirSeparator);
+} /* PHYSFS_getDirSeparator */
+
+
+char **PHYSFS_getCdRomDirs(void)
+{
+ return(doEnumStringList(__PHYSFS_platformDetectAvailableCDs));
+} /* PHYSFS_getCdRomDirs */
+
+
+void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback callback, void *data)
+{
+ __PHYSFS_platformDetectAvailableCDs(callback, data);
+} /* PHYSFS_getCdRomDirsCallback */
+
+
+const char *PHYSFS_getBaseDir(void)
+{
+ return(baseDir); /* this is calculated in PHYSFS_init()... */
+} /* PHYSFS_getBaseDir */
+
+
+const char *PHYSFS_getUserDir(void)
+{
+ return(userDir); /* this is calculated in PHYSFS_init()... */
+} /* PHYSFS_getUserDir */
+
+
+const char *PHYSFS_getWriteDir(void)
+{
+ const char *retval = NULL;
+
+ __PHYSFS_platformGrabMutex(stateLock);
+ if (writeDir != NULL)
+ retval = writeDir->dirName;
+ __PHYSFS_platformReleaseMutex(stateLock);
+
+ return(retval);
+} /* PHYSFS_getWriteDir */
+
+
+int PHYSFS_setWriteDir(const char *newDir)
+{
+ int retval = 1;
+
+ __PHYSFS_platformGrabMutex(stateLock);
+
+ if (writeDir != NULL)
+ {
+ BAIL_IF_MACRO_MUTEX(!freeDirHandle(writeDir, openWriteList), NULL,
+ stateLock, 0);
+ writeDir = NULL;
+ } /* if */
+
+ if (newDir != NULL)
+ {
+ writeDir = createDirHandle(newDir, NULL, 1);
+ retval = (writeDir != NULL);
+ } /* if */
+
+ __PHYSFS_platformReleaseMutex(stateLock);
+
+ return(retval);
+} /* PHYSFS_setWriteDir */
+
+
+int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath)
+{
+ DirHandle *dh;
+ DirHandle *prev = NULL;
+ DirHandle *i;
+
+ BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0);
+
+ if (mountPoint == NULL)
+ mountPoint = "/";
+
+ __PHYSFS_platformGrabMutex(stateLock);
+
+ for (i = searchPath; i != NULL; i = i->next)
+ {
+ /* already in search path? */
+ BAIL_IF_MACRO_MUTEX(strcmp(newDir, i->dirName)==0, NULL, stateLock, 1);
+ prev = i;
+ } /* for */
+
+ dh = createDirHandle(newDir, mountPoint, 0);
+ BAIL_IF_MACRO_MUTEX(dh == NULL, NULL, stateLock, 0);
+
+ if (appendToPath)
+ {
+ if (prev == NULL)
+ searchPath = dh;
+ else
+ prev->next = dh;
+ } /* if */
+ else
+ {
+ dh->next = searchPath;
+ searchPath = dh;
+ } /* else */
+
+ __PHYSFS_platformReleaseMutex(stateLock);
+ return(1);
+} /* PHYSFS_mount */
+
+
+int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
+{
+ return(PHYSFS_mount(newDir, NULL, appendToPath));
+} /* PHYSFS_addToSearchPath */
+
+
+int PHYSFS_removeFromSearchPath(const char *oldDir)
+{
+ DirHandle *i;
+ DirHandle *prev = NULL;
+ DirHandle *next = NULL;
+
+ BAIL_IF_MACRO(oldDir == NULL, ERR_INVALID_ARGUMENT, 0);
+
+ __PHYSFS_platformGrabMutex(stateLock);
+ for (i = searchPath; i != NULL; i = i->next)
+ {
+ if (strcmp(i->dirName, oldDir) == 0)
+ {
+ next = i->next;
+ BAIL_IF_MACRO_MUTEX(!freeDirHandle(i, openReadList), NULL,
+ stateLock, 0);
+
+ if (prev == NULL)
+ searchPath = next;
+ else
+ prev->next = next;
+
+ BAIL_MACRO_MUTEX(NULL, stateLock, 1);
+ } /* if */
+ prev = i;
+ } /* for */
+
+ BAIL_MACRO_MUTEX(ERR_NOT_IN_SEARCH_PATH, stateLock, 0);
+} /* PHYSFS_removeFromSearchPath */
+
+
+char **PHYSFS_getSearchPath(void)
+{
+ return(doEnumStringList(PHYSFS_getSearchPathCallback));
+} /* PHYSFS_getSearchPath */
+
+
+const char *PHYSFS_getMountPoint(const char *dir)
+{
+ DirHandle *i;
+ __PHYSFS_platformGrabMutex(stateLock);
+ for (i = searchPath; i != NULL; i = i->next)
+ {
+ if (strcmp(i->dirName, dir) == 0)
+ {
+ const char *retval = ((i->mountPoint) ? i->mountPoint : "/");
+ __PHYSFS_platformReleaseMutex(stateLock);
+ return(retval);
+ } /* if */
+ } /* for */
+ __PHYSFS_platformReleaseMutex(stateLock);
+
+ BAIL_MACRO(ERR_NOT_IN_SEARCH_PATH, NULL);
+} /* PHYSFS_getMountPoint */
+
+
+void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback callback, void *data)
+{
+ DirHandle *i;
+
+ __PHYSFS_platformGrabMutex(stateLock);
+
+ for (i = searchPath; i != NULL; i = i->next)
+ callback(data, i->dirName);
+
+ __PHYSFS_platformReleaseMutex(stateLock);
+} /* PHYSFS_getSearchPathCallback */
+
+
+/* Split out to avoid stack allocation in a loop. */
+static void setSaneCfgAddPath(const char *i, const size_t l, const char *dirsep,
+ int archivesFirst)
+{
+ const char *d = PHYSFS_getRealDir(i);
+ const size_t allocsize = strlen(d) + strlen(dirsep) + l + 1;
+ char *str = (char *) __PHYSFS_smallAlloc(allocsize);
+ if (str != NULL)
+ {
+ sprintf(str, "%s%s%s", d, dirsep, i);
+ PHYSFS_addToSearchPath(str, archivesFirst == 0);
+ __PHYSFS_smallFree(str);
+ } /* if */
+} /* setSaneCfgAddPath */
+
+
+int PHYSFS_setSaneConfig(const char *organization, const char *appName,
+ const char *archiveExt, int includeCdRoms,
+ int archivesFirst)
+{
+ const char *basedir = PHYSFS_getBaseDir();
+ const char *userdir = PHYSFS_getUserDir();
+ const char *dirsep = PHYSFS_getDirSeparator();
+ PHYSFS_uint64 len = 0;
+ char *str = NULL;
+
+ BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
+
+ /* set write dir... */
+ len = (strlen(userdir) + (strlen(organization) * 2) +
+ (strlen(appName) * 2) + (strlen(dirsep) * 3) + 2);
+
+ str = (char *) __PHYSFS_smallAlloc(len);
+
+ BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0);
+ sprintf(str, "%s.%s%s%s", userdir, organization, dirsep, appName);
+
+ if (!PHYSFS_setWriteDir(str))
+ {
+ int no_write = 0;
+ sprintf(str, ".%s/%s", organization, appName);
+ if ( (PHYSFS_setWriteDir(userdir)) &&
+ (PHYSFS_mkdir(str)) )
+ {
+ sprintf(str, "%s.%s%s%s", userdir, organization, dirsep, appName);
+ if (!PHYSFS_setWriteDir(str))
+ no_write = 1;
+ } /* if */
+ else
+ {
+ no_write = 1;
+ } /* else */
+
+ if (no_write)
+ {
+ PHYSFS_setWriteDir(NULL); /* just in case. */
+ __PHYSFS_smallFree(str);
+ BAIL_MACRO(ERR_CANT_SET_WRITE_DIR, 0);
+ } /* if */
+ } /* if */
+
+ /* Put write dir first in search path... */
+ PHYSFS_addToSearchPath(str, 0);
+ __PHYSFS_smallFree(str);
+
+ /* Put base path on search path... */
+ PHYSFS_addToSearchPath(basedir, 1);
+
+ /* handle CD-ROMs... */
+ if (includeCdRoms)
+ {
+ char **cds = PHYSFS_getCdRomDirs();
+ char **i;
+ for (i = cds; *i != NULL; i++)
+ PHYSFS_addToSearchPath(*i, 1);
+
+ PHYSFS_freeList(cds);
+ } /* if */
+
+ /* Root out archives, and add them to search path... */
+ if (archiveExt != NULL)
+ {
+ char **rc = PHYSFS_enumerateFiles("/");
+ char **i;
+ size_t extlen = strlen(archiveExt);
+ char *ext;
+
+ for (i = rc; *i != NULL; i++)
+ {
+ size_t l = strlen(*i);
+ if ((l > extlen) && ((*i)[l - extlen - 1] == '.'))
+ {
+ ext = (*i) + (l - extlen);
+ if (__PHYSFS_stricmpASCII(ext, archiveExt) == 0)
+ setSaneCfgAddPath(*i, l, dirsep, archivesFirst);
+ } /* if */
+ } /* for */
+
+ PHYSFS_freeList(rc);
+ } /* if */
+
+ return(1);
+} /* PHYSFS_setSaneConfig */
+
+
+void PHYSFS_permitSymbolicLinks(int allow)
+{
+ allowSymLinks = allow;
+} /* PHYSFS_permitSymbolicLinks */
+
+
+int PHYSFS_symbolicLinksPermitted(void)
+{
+ return(allowSymLinks);
+} /* PHYSFS_symbolicLinksPermitted */
+
+
+/* string manipulation in C makes my ass itch. */
+char *__PHYSFS_convertToDependent(const char *prepend,
+ const char *dirName,
+ const char *append)
+{
+ const char *dirsep = __PHYSFS_platformDirSeparator;
+ size_t sepsize = strlen(dirsep);
+ char *str;
+ char *i1;
+ char *i2;
+ size_t allocSize;
+
+ while (*dirName == '/') /* !!! FIXME: pass through sanitize function. */
+ dirName++;
+
+ allocSize = strlen(dirName) + 1;
+ if (prepend != NULL)
+ allocSize += strlen(prepend) + sepsize;
+ if (append != NULL)
+ allocSize += strlen(append) + sepsize;
+
+ /* make sure there's enough space if the dir separator is bigger. */
+ if (sepsize > 1)
+ {
+ str = (char *) dirName;
+ do
+ {
+ str = strchr(str, '/');
+ if (str != NULL)
+ {
+ allocSize += (sepsize - 1);
+ str++;
+ } /* if */
+ } while (str != NULL);
+ } /* if */
+
+ str = (char *) allocator.Malloc(allocSize);
+ BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ if (prepend == NULL)
+ *str = '\0';
+ else
+ {
+ strcpy(str, prepend);
+ strcat(str, dirsep);
+ } /* else */
+
+ for (i1 = (char *) dirName, i2 = str + strlen(str); *i1; i1++, i2++)
+ {
+ if (*i1 == '/')
+ {
+ strcpy(i2, dirsep);
+ i2 += sepsize;
+ } /* if */
+ else
+ {
+ *i2 = *i1;
+ } /* else */
+ } /* for */
+ *i2 = '\0';
+
+ if (append)
+ {
+ strcat(str, dirsep);
+ strcat(str, append);
+ } /* if */
+
+ return(str);
+} /* __PHYSFS_convertToDependent */
+
+
+/*
+ * Verify that (fname) (in platform-independent notation), in relation
+ * to (h) is secure. That means that each element of fname is checked
+ * for symlinks (if they aren't permitted). This also allows for quick
+ * rejection of files that exist outside an archive's mountpoint.
+ *
+ * With some exceptions (like PHYSFS_mkdir(), which builds multiple subdirs
+ * at a time), you should always pass zero for "allowMissing" for efficiency.
+ *
+ * (fname) must point to an output from sanitizePlatformIndependentPath(),
+ * since it will make sure that path names are in the right format for
+ * passing certain checks. It will also do checks for "insecure" pathnames
+ * like ".." which should be done once instead of once per archive. This also
+ * gives us license to treat (fname) as scratch space in this function.
+ *
+ * Returns non-zero if string is safe, zero if there's a security issue.
+ * PHYSFS_getLastError() will specify what was wrong. (*fname) will be
+ * updated to point past any mount point elements so it is prepared to
+ * be used with the archiver directly.
+ */
+static int verifyPath(DirHandle *h, char **_fname, int allowMissing)
+{
+ char *fname = *_fname;
+ int retval = 1;
+ char *start;
+ char *end;
+
+ if (*fname == '\0') /* quick rejection. */
+ return(1);
+
+ /* !!! FIXME: This codeblock sucks. */
+ if (h->mountPoint != NULL) /* NULL mountpoint means "/". */
+ {
+ size_t mntpntlen = strlen(h->mountPoint);
+ size_t len = strlen(fname);
+ assert(mntpntlen > 1); /* root mount points should be NULL. */
+ /* not under the mountpoint, so skip this archive. */
+ BAIL_IF_MACRO(len < mntpntlen-1, ERR_NO_SUCH_PATH, 0);
+ /* !!! FIXME: Case insensitive? */
+ retval = strncmp(h->mountPoint, fname, mntpntlen-1);
+ BAIL_IF_MACRO(retval != 0, ERR_NO_SUCH_PATH, 0);
+ if (len > mntpntlen-1) /* corner case... */
+ BAIL_IF_MACRO(fname[mntpntlen-1] != '/', ERR_NO_SUCH_PATH, 0);
+ fname += mntpntlen-1; /* move to start of actual archive path. */
+ if (*fname == '/')
+ fname++;
+ *_fname = fname; /* skip mountpoint for later use. */
+ retval = 1; /* may be reset, below. */
+ } /* if */
+
+ start = fname;
+ if (!allowSymLinks)
+ {
+ while (1)
+ {
+ int rc = 0;
+ end = strchr(start, '/');
+
+ if (end != NULL) *end = '\0';
+ rc = h->funcs->isSymLink(h->opaque, fname, &retval);
+ if (end != NULL) *end = '/';
+
+ BAIL_IF_MACRO(rc, ERR_SYMLINK_DISALLOWED, 0); /* insecure. */
+
+ /* break out early if path element is missing. */
+ if (!retval)
+ {
+ /*
+ * We need to clear it if it's the last element of the path,
+ * since this might be a non-existant file we're opening
+ * for writing...
+ */
+ if ((end == NULL) || (allowMissing))
+ retval = 1;
+ break;
+ } /* if */
+
+ if (end == NULL)
+ break;
+
+ start = end + 1;
+ } /* while */
+ } /* if */
+
+ return(retval);
+} /* verifyPath */
+
+
+static int doMkdir(const char *_dname, char *dname)
+{
+ DirHandle *h;
+ char *start;
+ char *end;
+ int retval = 0;
+ int exists = 1; /* force existance check on first path element. */
+
+ BAIL_IF_MACRO(!sanitizePlatformIndependentPath(_dname, dname), NULL, 0);
+
+ __PHYSFS_platformGrabMutex(stateLock);
+ BAIL_IF_MACRO_MUTEX(writeDir == NULL, ERR_NO_WRITE_DIR, stateLock, 0);
+ h = writeDir;
+ BAIL_IF_MACRO_MUTEX(!verifyPath(h, &dname, 1), NULL, stateLock, 0);
+
+ start = dname;
+ while (1)
+ {
+ end = strchr(start, '/');
+ if (end != NULL)
+ *end = '\0';
+
+ /* only check for existance if all parent dirs existed, too... */
+ if (exists)
+ retval = h->funcs->isDirectory(h->opaque, dname, &exists);
+
+ if (!exists)
+ retval = h->funcs->mkdir(h->opaque, dname);
+
+ if (!retval)
+ break;
+
+ if (end == NULL)
+ break;
+
+ *end = '/';
+ start = end + 1;
+ } /* while */
+
+ __PHYSFS_platformReleaseMutex(stateLock);
+ return(retval);
+} /* doMkdir */
+
+
+int PHYSFS_mkdir(const char *_dname)
+{
+ int retval = 0;
+ char *dname;
+ size_t len;
+
+ BAIL_IF_MACRO(_dname == NULL, ERR_INVALID_ARGUMENT, 0);
+ len = strlen(_dname) + 1;
+ dname = (char *) __PHYSFS_smallAlloc(len);
+ BAIL_IF_MACRO(dname == NULL, ERR_OUT_OF_MEMORY, 0);
+ retval = doMkdir(_dname, dname);
+ __PHYSFS_smallFree(dname);
+ return(retval);
+} /* PHYSFS_mkdir */
+
+
+static int doDelete(const char *_fname, char *fname)
+{
+ int retval;
+ DirHandle *h;
+ BAIL_IF_MACRO(!sanitizePlatformIndependentPath(_fname, fname), NULL, 0);
+
+ __PHYSFS_platformGrabMutex(stateLock);
+
+ BAIL_IF_MACRO_MUTEX(writeDir == NULL, ERR_NO_WRITE_DIR, stateLock, 0);
+ h = writeDir;
+ BAIL_IF_MACRO_MUTEX(!verifyPath(h, &fname, 0), NULL, stateLock, 0);
+ retval = h->funcs->remove(h->opaque, fname);
+
+ __PHYSFS_platformReleaseMutex(stateLock);
+ return(retval);
+} /* doDelete */
+
+
+int PHYSFS_delete(const char *_fname)
+{
+ int retval;
+ char *fname;
+ size_t len;
+
+ BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0);
+ len = strlen(_fname) + 1;
+ fname = (char *) __PHYSFS_smallAlloc(len);
+ BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0);
+ retval = doDelete(_fname, fname);
+ __PHYSFS_smallFree(fname);
+ return(retval);
+} /* PHYSFS_delete */
+
+
+const char *PHYSFS_getRealDir(const char *_fname)
+{
+ const char *retval = NULL;
+ char *fname = NULL;
+ size_t len;
+
+ BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, NULL);
+ len = strlen(_fname) + 1;
+ fname = __PHYSFS_smallAlloc(len);
+ BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, NULL);
+ if (sanitizePlatformIndependentPath(_fname, fname))
+ {
+ DirHandle *i;
+ __PHYSFS_platformGrabMutex(stateLock);
+ for (i = searchPath; ((i != NULL) && (retval == NULL)); i = i->next)
+ {
+ char *arcfname = fname;
+ if (partOfMountPoint(i, arcfname))
+ retval = i->dirName;
+ else if (verifyPath(i, &arcfname, 0))
+ {
+ if (i->funcs->exists(i->opaque, arcfname))
+ retval = i->dirName;
+ } /* if */
+ } /* for */
+ __PHYSFS_platformReleaseMutex(stateLock);
+ } /* if */
+
+ __PHYSFS_smallFree(fname);
+ return(retval);
+} /* PHYSFS_getRealDir */
+
+
+static int locateInStringList(const char *str,
+ char **list,
+ PHYSFS_uint32 *pos)
+{
+ PHYSFS_uint32 len = *pos;
+ PHYSFS_uint32 half_len;
+ PHYSFS_uint32 lo = 0;
+ PHYSFS_uint32 middle;
+ int cmp;
+
+ while (len > 0)
+ {
+ half_len = len >> 1;
+ middle = lo + half_len;
+ cmp = strcmp(list[middle], str);
+
+ if (cmp == 0) /* it's in the list already. */
+ return(1);
+ else if (cmp > 0)
+ len = half_len;
+ else
+ {
+ lo = middle + 1;
+ len -= half_len + 1;
+ } /* else */
+ } /* while */
+
+ *pos = lo;
+ return(0);
+} /* locateInStringList */
+
+
+static void enumFilesCallback(void *data, const char *origdir, const char *str)
+{
+ PHYSFS_uint32 pos;
+ void *ptr;
+ char *newstr;
+ EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data;
+
+ /*
+ * See if file is in the list already, and if not, insert it in there
+ * alphabetically...
+ */
+ pos = pecd->size;
+ if (locateInStringList(str, pecd->list, &pos))
+ return; /* already in the list. */
+
+ ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *));
+ newstr = (char *) allocator.Malloc(strlen(str) + 1);
+ if (ptr != NULL)
+ pecd->list = (char **) ptr;
+
+ if ((ptr == NULL) || (newstr == NULL))
+ return; /* better luck next time. */
+
+ strcpy(newstr, str);
+
+ if (pos != pecd->size)
+ {
+ memmove(&pecd->list[pos+1], &pecd->list[pos],
+ sizeof (char *) * ((pecd->size) - pos));
+ } /* if */
+
+ pecd->list[pos] = newstr;
+ pecd->size++;
+} /* enumFilesCallback */
+
+
+char **PHYSFS_enumerateFiles(const char *path)
+{
+ EnumStringListCallbackData ecd;
+ memset(&ecd, '\0', sizeof (ecd));
+ ecd.list = (char **) allocator.Malloc(sizeof (char *));
+ BAIL_IF_MACRO(ecd.list == NULL, ERR_OUT_OF_MEMORY, NULL);
+ PHYSFS_enumerateFilesCallback(path, enumFilesCallback, &ecd);
+ ecd.list[ecd.size] = NULL;
+ return(ecd.list);
+} /* PHYSFS_enumerateFiles */
+
+
+/*
+ * Broke out to seperate function so we can use stack allocation gratuitously.
+ */
+static void enumerateFromMountPoint(DirHandle *i, const char *arcfname,
+ PHYSFS_EnumFilesCallback callback,
+ const char *_fname, void *data)
+{
+ const size_t len = strlen(arcfname);
+ char *ptr = NULL;
+ char *end = NULL;
+ const size_t slen = strlen(i->mountPoint) + 1;
+ char *mountPoint = (char *) __PHYSFS_smallAlloc(slen);
+
+ if (mountPoint == NULL)
+ return; /* oh well. */
+
+ strcpy(mountPoint, i->mountPoint);
+ ptr = mountPoint + ((len) ? len + 1 : 0);
+ end = strchr(ptr, '/');
+ assert(end); /* should always find a terminating '/'. */
+ *end = '\0';
+ callback(data, _fname, ptr);
+ __PHYSFS_smallFree(mountPoint);
+} /* enumerateFromMountPoint */
+
+
+/* !!! FIXME: this should report error conditions. */
+void PHYSFS_enumerateFilesCallback(const char *_fname,
+ PHYSFS_EnumFilesCallback callback,
+ void *data)
+{
+ size_t len;
+ char *fname;
+
+ BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, ) /*0*/;
+ BAIL_IF_MACRO(callback == NULL, ERR_INVALID_ARGUMENT, ) /*0*/;
+
+ len = strlen(_fname) + 1;
+ fname = (char *) __PHYSFS_smallAlloc(len);
+ BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, ) /*0*/;
+
+ if (sanitizePlatformIndependentPath(_fname, fname))
+ {
+ DirHandle *i;
+ int noSyms;
+
+ __PHYSFS_platformGrabMutex(stateLock);
+ noSyms = !allowSymLinks;
+ for (i = searchPath; i != NULL; i = i->next)
+ {
+ char *arcfname = fname;
+ if (partOfMountPoint(i, arcfname))
+ enumerateFromMountPoint(i, arcfname, callback, _fname, data);
+
+ else if (verifyPath(i, &arcfname, 0))
+ {
+ i->funcs->enumerateFiles(i->opaque, arcfname, noSyms,
+ callback, _fname, data);
+ } /* else if */
+ } /* for */
+ __PHYSFS_platformReleaseMutex(stateLock);
+ } /* if */
+
+ __PHYSFS_smallFree(fname);
+} /* PHYSFS_enumerateFilesCallback */
+
+
+int PHYSFS_exists(const char *fname)
+{
+ return(PHYSFS_getRealDir(fname) != NULL);
+} /* PHYSFS_exists */
+
+
+PHYSFS_sint64 PHYSFS_getLastModTime(const char *_fname)
+{
+ PHYSFS_sint64 retval = -1;
+ char *fname;
+ size_t len;
+
+ BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, -1);
+ len = strlen(_fname) + 1;
+ fname = (char *) __PHYSFS_smallAlloc(len);
+ BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, -1);
+
+ if (sanitizePlatformIndependentPath(_fname, fname))
+ {
+ if (*fname == '\0') /* eh...punt if it's the root dir. */
+ retval = 1; /* !!! FIXME: Maybe this should be an error? */
+ else
+ {
+ DirHandle *i;
+ int exists = 0;
+ __PHYSFS_platformGrabMutex(stateLock);
+ for (i = searchPath; ((i != NULL) && (!exists)); i = i->next)
+ {
+ char *arcfname = fname;
+ exists = partOfMountPoint(i, arcfname);
+ if (exists)
+ retval = 1; /* !!! FIXME: What's the right value? */
+ else if (verifyPath(i, &arcfname, 0))
+ {
+ retval = i->funcs->getLastModTime(i->opaque, arcfname,
+ &exists);
+ } /* else if */
+ } /* for */
+ __PHYSFS_platformReleaseMutex(stateLock);
+ } /* else */
+ } /* if */
+
+ __PHYSFS_smallFree(fname);
+ return(retval);
+} /* PHYSFS_getLastModTime */
+
+
+int PHYSFS_isDirectory(const char *_fname)
+{
+ int retval = 0;
+ size_t len;
+ char *fname;
+
+ BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0);
+ len = strlen(_fname) + 1;
+ fname = (char *) __PHYSFS_smallAlloc(len);
+ BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0);
+
+ if (!sanitizePlatformIndependentPath(_fname, fname))
+ retval = 0;
+
+ else if (*fname == '\0')
+ retval = 1; /* Root is always a dir. :) */
+
+ else
+ {
+ DirHandle *i;
+ int exists = 0;
+
+ __PHYSFS_platformGrabMutex(stateLock);
+ for (i = searchPath; ((i != NULL) && (!exists)); i = i->next)
+ {
+ char *arcfname = fname;
+ if ((exists = partOfMountPoint(i, arcfname)) != 0)
+ retval = 1;
+ else if (verifyPath(i, &arcfname, 0))
+ retval = i->funcs->isDirectory(i->opaque, arcfname, &exists);
+ } /* for */
+ __PHYSFS_platformReleaseMutex(stateLock);
+ } /* else */
+
+ __PHYSFS_smallFree(fname);
+ return(retval);
+} /* PHYSFS_isDirectory */
+
+
+int PHYSFS_isSymbolicLink(const char *_fname)
+{
+ int retval = 0;
+ size_t len;
+ char *fname;
+
+ BAIL_IF_MACRO(!allowSymLinks, ERR_SYMLINK_DISALLOWED, 0);
+
+ BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0);
+ len = strlen(_fname) + 1;
+ fname = (char *) __PHYSFS_smallAlloc(len);
+ BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0);
+
+ if (!sanitizePlatformIndependentPath(_fname, fname))
+ retval = 0;
+
+ else if (*fname == '\0')
+ retval = 1; /* Root is never a symlink. */
+
+ else
+ {
+ DirHandle *i;
+ int fileExists = 0;
+
+ __PHYSFS_platformGrabMutex(stateLock);
+ for (i = searchPath; ((i != NULL) && (!fileExists)); i = i->next)
+ {
+ char *arcfname = fname;
+ if ((fileExists = partOfMountPoint(i, arcfname)) != 0)
+ retval = 0; /* virtual dir...not a symlink. */
+ else if (verifyPath(i, &arcfname, 0))
+ retval = i->funcs->isSymLink(i->opaque, arcfname, &fileExists);
+ } /* for */
+ __PHYSFS_platformReleaseMutex(stateLock);
+ } /* else */
+
+ __PHYSFS_smallFree(fname);
+ return(retval);
+} /* PHYSFS_isSymbolicLink */
+
+
+static PHYSFS_File *doOpenWrite(const char *_fname, int appending)
+{
+ FileHandle *fh = NULL;
+ size_t len;
+ char *fname;
+
+ BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0);
+ len = strlen(_fname) + 1;
+ fname = (char *) __PHYSFS_smallAlloc(len);
+ BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0);
+
+ if (sanitizePlatformIndependentPath(_fname, fname))
+ {
+ void *opaque = NULL;
+ DirHandle *h = NULL;
+ const PHYSFS_Archiver *f;
+
+ __PHYSFS_platformGrabMutex(stateLock);
+
+ GOTO_IF_MACRO(!writeDir, ERR_NO_WRITE_DIR, doOpenWriteEnd);
+
+ h = writeDir;
+ GOTO_IF_MACRO(!verifyPath(h, &fname, 0), NULL, doOpenWriteEnd);
+
+ f = h->funcs;
+ if (appending)
+ opaque = f->openAppend(h->opaque, fname);
+ else
+ opaque = f->openWrite(h->opaque, fname);
+
+ GOTO_IF_MACRO(opaque == NULL, NULL, doOpenWriteEnd);
+
+ fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
+ if (fh == NULL)
+ {
+ f->fileClose(opaque);
+ GOTO_MACRO(ERR_OUT_OF_MEMORY, doOpenWriteEnd);
+ } /* if */
+ else
+ {
+ memset(fh, '\0', sizeof (FileHandle));
+ fh->opaque = opaque;
+ fh->dirHandle = h;
+ fh->funcs = h->funcs;
+ fh->next = openWriteList;
+ openWriteList = fh;
+ } /* else */
+
+ doOpenWriteEnd:
+ __PHYSFS_platformReleaseMutex(stateLock);
+ } /* if */
+
+ __PHYSFS_smallFree(fname);
+ return((PHYSFS_File *) fh);
+} /* doOpenWrite */
+
+
+PHYSFS_File *PHYSFS_openWrite(const char *filename)
+{
+ return(doOpenWrite(filename, 0));
+} /* PHYSFS_openWrite */
+
+
+PHYSFS_File *PHYSFS_openAppend(const char *filename)
+{
+ return(doOpenWrite(filename, 1));
+} /* PHYSFS_openAppend */
+
+
+PHYSFS_File *PHYSFS_openRead(const char *_fname)
+{
+ FileHandle *fh = NULL;
+ char *fname;
+ size_t len;
+
+ BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, 0);
+ len = strlen(_fname) + 1;
+ fname = (char *) __PHYSFS_smallAlloc(len);
+ BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, 0);
+
+ if (sanitizePlatformIndependentPath(_fname, fname))
+ {
+ int fileExists = 0;
+ DirHandle *i = NULL;
+ fvoid *opaque = NULL;
+
+ __PHYSFS_platformGrabMutex(stateLock);
+
+ GOTO_IF_MACRO(!searchPath, ERR_NO_SUCH_PATH, openReadEnd);
+
+ /* !!! FIXME: Why aren't we using a for loop here? */
+ i = searchPath;
+
+ do
+ {
+ char *arcfname = fname;
+ if (verifyPath(i, &arcfname, 0))
+ {
+ opaque = i->funcs->openRead(i->opaque, arcfname, &fileExists);
+ if (opaque)
+ break;
+ } /* if */
+ i = i->next;
+ } while ((i != NULL) && (!fileExists));
+
+ /* !!! FIXME: may not set an error if openRead didn't fail. */
+ GOTO_IF_MACRO(opaque == NULL, NULL, openReadEnd);
+
+ fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle));
+ if (fh == NULL)
+ {
+ i->funcs->fileClose(opaque);
+ GOTO_MACRO(ERR_OUT_OF_MEMORY, openReadEnd);
+ } /* if */
+
+ memset(fh, '\0', sizeof (FileHandle));
+ fh->opaque = opaque;
+ fh->forReading = 1;
+ fh->dirHandle = i;
+ fh->funcs = i->funcs;
+ fh->next = openReadList;
+ openReadList = fh;
+
+ openReadEnd:
+ __PHYSFS_platformReleaseMutex(stateLock);
+ } /* if */
+
+ __PHYSFS_smallFree(fname);
+ return((PHYSFS_File *) fh);
+} /* PHYSFS_openRead */
+
+
+static int closeHandleInOpenList(FileHandle **list, FileHandle *handle)
+{
+ FileHandle *prev = NULL;
+ FileHandle *i;
+ int rc = 1;
+
+ for (i = *list; i != NULL; i = i->next)
+ {
+ if (i == handle) /* handle is in this list? */
+ {
+ PHYSFS_uint8 *tmp = handle->buffer;
+ rc = PHYSFS_flush((PHYSFS_File *) handle);
+ if (rc)
+ rc = handle->funcs->fileClose(handle->opaque);
+ if (!rc)
+ return(-1);
+
+ if (tmp != NULL) /* free any associated buffer. */
+ allocator.Free(tmp);
+
+ if (prev == NULL)
+ *list = handle->next;
+ else
+ prev->next = handle->next;
+
+ allocator.Free(handle);
+ return(1);
+ } /* if */
+ prev = i;
+ } /* for */
+
+ return(0);
+} /* closeHandleInOpenList */
+
+
+int PHYSFS_close(PHYSFS_File *_handle)
+{
+ FileHandle *handle = (FileHandle *) _handle;
+ int rc;
+
+ __PHYSFS_platformGrabMutex(stateLock);
+
+ /* -1 == close failure. 0 == not found. 1 == success. */
+ rc = closeHandleInOpenList(&openReadList, handle);
+ BAIL_IF_MACRO_MUTEX(rc == -1, NULL, stateLock, 0);
+ if (!rc)
+ {
+ rc = closeHandleInOpenList(&openWriteList, handle);
+ BAIL_IF_MACRO_MUTEX(rc == -1, NULL, stateLock, 0);
+ } /* if */
+
+ __PHYSFS_platformReleaseMutex(stateLock);
+ BAIL_IF_MACRO(!rc, ERR_NOT_A_HANDLE, 0);
+ return(1);
+} /* PHYSFS_close */
+
+
+static PHYSFS_sint64 doBufferedRead(FileHandle *fh, void *buffer,
+ PHYSFS_uint32 objSize,
+ PHYSFS_uint32 objCount)
+{
+ PHYSFS_sint64 retval = 0;
+ PHYSFS_uint32 remainder = 0;
+
+ while (objCount > 0)
+ {
+ PHYSFS_uint32 buffered = fh->buffill - fh->bufpos;
+ PHYSFS_uint64 mustread = (objSize * objCount) - remainder;
+ PHYSFS_uint32 copied;
+
+ if (buffered == 0) /* need to refill buffer? */
+ {
+ PHYSFS_sint64 rc = fh->funcs->read(fh->opaque, fh->buffer,
+ 1, fh->bufsize);
+ if (rc <= 0)
+ {
+ fh->bufpos -= remainder;
+ return(((rc == -1) && (retval == 0)) ? -1 : retval);
+ } /* if */
+
+ buffered = fh->buffill = (PHYSFS_uint32) rc;
+ fh->bufpos = 0;
+ } /* if */
+
+ if (buffered > mustread)
+ buffered = (PHYSFS_uint32) mustread;
+
+ memcpy(buffer, fh->buffer + fh->bufpos, (size_t) buffered);
+ buffer = ((PHYSFS_uint8 *) buffer) + buffered;
+ fh->bufpos += buffered;
+ buffered += remainder; /* take remainder into account. */
+ copied = (buffered / objSize);
+ remainder = (buffered % objSize);
+ retval += copied;
+ objCount -= copied;
+ } /* while */
+
+ return(retval);
+} /* doBufferedRead */
+
+
+PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ FileHandle *fh = (FileHandle *) handle;
+
+ BAIL_IF_MACRO(!fh->forReading, ERR_FILE_ALREADY_OPEN_W, -1);
+ if (fh->buffer != NULL)
+ return(doBufferedRead(fh, buffer, objSize, objCount));
+
+ return(fh->funcs->read(fh->opaque, buffer, objSize, objCount));
+} /* PHYSFS_read */
+
+
+static PHYSFS_sint64 doBufferedWrite(PHYSFS_File *handle, const void *buffer,
+ PHYSFS_uint32 objSize,
+ PHYSFS_uint32 objCount)
+{
+ FileHandle *fh = (FileHandle *) handle;
+
+ /* whole thing fits in the buffer? */
+ if (fh->buffill + (objSize * objCount) < fh->bufsize)
+ {
+ memcpy(fh->buffer + fh->buffill, buffer, objSize * objCount);
+ fh->buffill += (objSize * objCount);
+ return(objCount);
+ } /* if */
+
+ /* would overflow buffer. Flush and then write the new objects, too. */
+ BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, -1);
+ return(fh->funcs->write(fh->opaque, buffer, objSize, objCount));
+} /* doBufferedWrite */
+
+
+PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+{
+ FileHandle *fh = (FileHandle *) handle;
+
+ BAIL_IF_MACRO(fh->forReading, ERR_FILE_ALREADY_OPEN_R, -1);
+ if (fh->buffer != NULL)
+ return(doBufferedWrite(handle, buffer, objSize, objCount));
+
+ return(fh->funcs->write(fh->opaque, buffer, objSize, objCount));
+} /* PHYSFS_write */
+
+
+int PHYSFS_eof(PHYSFS_File *handle)
+{
+ FileHandle *fh = (FileHandle *) handle;
+
+ if (!fh->forReading) /* never EOF on files opened for write/append. */
+ return(0);
+
+ /* eof if buffer is empty and archiver says so. */
+ return((fh->bufpos == fh->buffill) && (fh->funcs->eof(fh->opaque)));
+} /* PHYSFS_eof */
+
+
+PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle)
+{
+ FileHandle *fh = (FileHandle *) handle;
+ PHYSFS_sint64 pos = fh->funcs->tell(fh->opaque);
+ PHYSFS_sint64 retval = fh->forReading ?
+ (pos - fh->buffill) + fh->bufpos :
+ (pos + fh->buffill);
+ return(retval);
+} /* PHYSFS_tell */
+
+
+int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos)
+{
+ FileHandle *fh = (FileHandle *) handle;
+ BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0);
+
+ if (fh->buffer && fh->forReading)
+ {
+ /* avoid throwing away our precious buffer if seeking within it. */
+ PHYSFS_sint64 offset = pos - PHYSFS_tell(handle);
+ if ( /* seeking within the already-buffered range? */
+ ((offset >= 0) && (offset <= fh->buffill - fh->bufpos)) /* fwd */
+ || ((offset < 0) && (-offset <= fh->bufpos)) /* backward */ )
+ {
+ fh->bufpos += (PHYSFS_uint32) offset;
+ return(1); /* successful seek */
+ } /* if */
+ } /* if */
+
+ /* we have to fall back to a 'raw' seek. */
+ fh->buffill = fh->bufpos = 0;
+ return(fh->funcs->seek(fh->opaque, pos));
+} /* PHYSFS_seek */
+
+
+PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle)
+{
+ FileHandle *fh = (FileHandle *) handle;
+ return(fh->funcs->fileLength(fh->opaque));
+} /* PHYSFS_filelength */
+
+
+int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 _bufsize)
+{
+ FileHandle *fh = (FileHandle *) handle;
+ PHYSFS_uint32 bufsize;
+
+ /* !!! FIXME: Unlocalized string. */
+ BAIL_IF_MACRO(_bufsize > 0xFFFFFFFF, "buffer must fit in 32-bits", 0);
+ bufsize = (PHYSFS_uint32) _bufsize;
+
+ BAIL_IF_MACRO(!PHYSFS_flush(handle), NULL, 0);
+
+ /*
+ * For reads, we need to move the file pointer to where it would be
+ * if we weren't buffering, so that the next read will get the
+ * right chunk of stuff from the file. PHYSFS_flush() handles writes.
+ */
+ if ((fh->forReading) && (fh->buffill != fh->bufpos))
+ {
+ PHYSFS_uint64 pos;
+ PHYSFS_sint64 curpos = fh->funcs->tell(fh->opaque);
+ BAIL_IF_MACRO(curpos == -1, NULL, 0);
+ pos = ((curpos - fh->buffill) + fh->bufpos);
+ BAIL_IF_MACRO(!fh->funcs->seek(fh->opaque, pos), NULL, 0);
+ } /* if */
+
+ if (bufsize == 0) /* delete existing buffer. */
+ {
+ if (fh->buffer != NULL)
+ {
+ allocator.Free(fh->buffer);
+ fh->buffer = NULL;
+ } /* if */
+ } /* if */
+
+ else
+ {
+ PHYSFS_uint8 *newbuf;
+ newbuf = (PHYSFS_uint8 *) allocator.Realloc(fh->buffer, bufsize);
+ BAIL_IF_MACRO(newbuf == NULL, ERR_OUT_OF_MEMORY, 0);
+ fh->buffer = newbuf;
+ } /* else */
+
+ fh->bufsize = bufsize;
+ fh->buffill = fh->bufpos = 0;
+ return(1);
+} /* PHYSFS_setBuffer */
+
+
+int PHYSFS_flush(PHYSFS_File *handle)
+{
+ FileHandle *fh = (FileHandle *) handle;
+ PHYSFS_sint64 rc;
+
+ if ((fh->forReading) || (fh->bufpos == fh->buffill))
+ return(1); /* open for read or buffer empty are successful no-ops. */
+
+ /* dump buffer to disk. */
+ rc = fh->funcs->write(fh->opaque, fh->buffer + fh->bufpos,
+ fh->buffill - fh->bufpos, 1);
+ BAIL_IF_MACRO(rc <= 0, NULL, 0);
+ fh->bufpos = fh->buffill = 0;
+ return(1);
+} /* PHYSFS_flush */
+
+
+int PHYSFS_setAllocator(const PHYSFS_Allocator *a)
+{
+ BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0);
+ externalAllocator = (a != NULL);
+ if (externalAllocator)
+ memcpy(&allocator, a, sizeof (PHYSFS_Allocator));
+
+ return(1);
+} /* PHYSFS_setAllocator */
+
+
+static void *mallocAllocatorMalloc(PHYSFS_uint64 s)
+{
+ BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
+ #undef malloc
+ return(malloc((size_t) s));
+} /* mallocAllocatorMalloc */
+
+
+static void *mallocAllocatorRealloc(void *ptr, PHYSFS_uint64 s)
+{
+ BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
+ #undef realloc
+ return(realloc(ptr, (size_t) s));
+} /* mallocAllocatorRealloc */
+
+
+static void mallocAllocatorFree(void *ptr)
+{
+ #undef free
+ free(ptr);
+} /* mallocAllocatorFree */
+
+
+static void setDefaultAllocator(void)
+{
+ assert(!externalAllocator);
+ if (!__PHYSFS_platformSetDefaultAllocator(&allocator))
+ {
+ allocator.Init = NULL;
+ allocator.Deinit = NULL;
+ allocator.Malloc = mallocAllocatorMalloc;
+ allocator.Realloc = mallocAllocatorRealloc;
+ allocator.Free = mallocAllocatorFree;
+ } /* if */
+} /* setDefaultAllocator */
+
+
+void *__PHYSFS_initSmallAlloc(void *ptr, PHYSFS_uint64 len)
+{
+ const char useHeap = ((ptr == NULL) ? 1 : 0);
+ if (useHeap) /* too large for stack allocation or alloca() failed. */
+ ptr = allocator.Malloc(len+1);
+
+ if (ptr != NULL)
+ {
+ char *retval = (char *) ptr;
+ /*printf("%s alloc'd (%d) bytes at (%p).\n",
+ useHeap ? "heap" : "stack", (int) len, ptr);*/
+ *retval = useHeap;
+ return(retval+1);
+ } /* if */
+
+ return(NULL); /* allocation failed. */
+} /* __PHYSFS_initSmallAlloc */
+
+
+void __PHYSFS_smallFree(void *ptr)
+{
+ if (ptr != NULL)
+ {
+ char *block = ((char *) ptr) - 1;
+ const char useHeap = *block;
+ if (useHeap)
+ allocator.Free(block);
+ /*printf("%s free'd (%p).\n", useHeap ? "heap" : "stack", block);*/
+ } /* if */
+} /* __PHYSFS_smallFree */
+
+/* end of physfs.c ... */
+
--- /dev/null
+/** \file physfs.h */
+
+/**
+ * \mainpage PhysicsFS
+ *
+ * The latest version of PhysicsFS can be found at:
+ * http://icculus.org/physfs/
+ *
+ * PhysicsFS; a portable, flexible file i/o abstraction.
+ *
+ * This API gives you access to a system file system in ways superior to the
+ * stdio or system i/o calls. The brief benefits:
+ *
+ * - It's portable.
+ * - It's safe. No file access is permitted outside the specified dirs.
+ * - It's flexible. Archives (.ZIP files) can be used transparently as
+ * directory structures.
+ *
+ * This system is largely inspired by Quake 3's PK3 files and the related
+ * fs_* cvars. If you've ever tinkered with these, then this API will be
+ * familiar to you.
+ *
+ * With PhysicsFS, you have a single writing directory and multiple
+ * directories (the "search path") for reading. You can think of this as a
+ * filesystem within a filesystem. If (on Windows) you were to set the
+ * writing directory to "C:\MyGame\MyWritingDirectory", then no PHYSFS calls
+ * could touch anything above this directory, including the "C:\MyGame" and
+ * "C:\" directories. This prevents an application's internal scripting
+ * language from piddling over c:\\config.sys, for example. If you'd rather
+ * give PHYSFS full access to the system's REAL file system, set the writing
+ * dir to "C:\", but that's generally A Bad Thing for several reasons.
+ *
+ * Drive letters are hidden in PhysicsFS once you set up your initial paths.
+ * The search path creates a single, hierarchical directory structure.
+ * Not only does this lend itself well to general abstraction with archives,
+ * it also gives better support to operating systems like MacOS and Unix.
+ * Generally speaking, you shouldn't ever hardcode a drive letter; not only
+ * does this hurt portability to non-Microsoft OSes, but it limits your win32
+ * users to a single drive, too. Use the PhysicsFS abstraction functions and
+ * allow user-defined configuration options, too. When opening a file, you
+ * specify it like it was on a Unix filesystem: if you want to write to
+ * "C:\MyGame\MyConfigFiles\game.cfg", then you might set the write dir to
+ * "C:\MyGame" and then open "MyConfigFiles/game.cfg". This gives an
+ * abstraction across all platforms. Specifying a file in this way is termed
+ * "platform-independent notation" in this documentation. Specifying a
+ * a filename in a form such as "C:\mydir\myfile" or
+ * "MacOS hard drive:My Directory:My File" is termed "platform-dependent
+ * notation". The only time you use platform-dependent notation is when
+ * setting up your write directory and search path; after that, all file
+ * access into those directories are done with platform-independent notation.
+ *
+ * All files opened for writing are opened in relation to the write directory,
+ * which is the root of the writable filesystem. When opening a file for
+ * reading, PhysicsFS goes through the search path. This is NOT the
+ * same thing as the PATH environment variable. An application using
+ * PhysicsFS specifies directories to be searched which may be actual
+ * directories, or archive files that contain files and subdirectories of
+ * their own. See the end of these docs for currently supported archive
+ * formats.
+ *
+ * Once the search path is defined, you may open files for reading. If you've
+ * got the following search path defined (to use a win32 example again):
+ *
+ * - C:\\mygame
+ * - C:\\mygame\\myuserfiles
+ * - D:\\mygamescdromdatafiles
+ * - C:\\mygame\\installeddatafiles.zip
+ *
+ * Then a call to PHYSFS_openRead("textfiles/myfile.txt") (note the directory
+ * separator, lack of drive letter, and lack of dir separator at the start of
+ * the string; this is platform-independent notation) will check for
+ * C:\\mygame\\textfiles\\myfile.txt, then
+ * C:\\mygame\\myuserfiles\\textfiles\\myfile.txt, then
+ * D:\\mygamescdromdatafiles\\textfiles\\myfile.txt, then, finally, for
+ * textfiles\\myfile.txt inside of C:\\mygame\\installeddatafiles.zip.
+ * Remember that most archive types and platform filesystems store their
+ * filenames in a case-sensitive manner, so you should be careful to specify
+ * it correctly.
+ *
+ * Files opened through PhysicsFS may NOT contain "." or ".." or ":" as dir
+ * elements. Not only are these meaningless on MacOS Classic and/or Unix,
+ * they are a security hole. Also, symbolic links (which can be found in
+ * some archive types and directly in the filesystem on Unix platforms) are
+ * NOT followed until you call PHYSFS_permitSymbolicLinks(). That's left to
+ * your own discretion, as following a symlink can allow for access outside
+ * the write dir and search paths. For portability, there is no mechanism for
+ * creating new symlinks in PhysicsFS.
+ *
+ * The write dir is not included in the search path unless you specifically
+ * add it. While you CAN change the write dir as many times as you like,
+ * you should probably set it once and stick to it. Remember that your
+ * program will not have permission to write in every directory on Unix and
+ * NT systems.
+ *
+ * All files are opened in binary mode; there is no endline conversion for
+ * textfiles. Other than that, PhysicsFS has some convenience functions for
+ * platform-independence. There is a function to tell you the current
+ * platform's dir separator ("\\" on windows, "/" on Unix, ":" on MacOS),
+ * which is needed only to set up your search/write paths. There is a
+ * function to tell you what CD-ROM drives contain accessible discs, and a
+ * function to recommend a good search path, etc.
+ *
+ * A recommended order for the search path is the write dir, then the base dir,
+ * then the cdrom dir, then any archives discovered. Quake 3 does something
+ * like this, but moves the archives to the start of the search path. Build
+ * Engine games, like Duke Nukem 3D and Blood, place the archives last, and
+ * use the base dir for both searching and writing. There is a helper
+ * function (PHYSFS_setSaneConfig()) that puts together a basic configuration
+ * for you, based on a few parameters. Also see the comments on
+ * PHYSFS_getBaseDir(), and PHYSFS_getUserDir() for info on what those
+ * are and how they can help you determine an optimal search path.
+ *
+ * PhysicsFS 2.0 adds the concept of "mounting" archives to arbitrary points
+ * in the search path. If a zipfile contains "maps/level.map" and you mount
+ * that archive at "mods/mymod", then you would have to open
+ * "mods/mymod/maps/level.map" to access the file, even though "mods/mymod"
+ * isn't actually specified in the .zip file. Unlike the Unix mentality of
+ * mounting a filesystem, "mods/mymod" doesn't actually have to exist when
+ * mounting the zipfile. It's a "virtual" directory. The mounting mechanism
+ * allows the developer to seperate archives in the tree and avoid trampling
+ * over files when added new archives, such as including mod support in a
+ * game...keeping external content on a tight leash in this manner can be of
+ * utmost importance to some applications.
+ *
+ * PhysicsFS is mostly thread safe. The error messages returned by
+ * PHYSFS_getLastError are unique by thread, and library-state-setting
+ * functions are mutex'd. For efficiency, individual file accesses are
+ * not locked, so you can not safely read/write/seek/close/etc the same
+ * file from two threads at the same time. Other race conditions are bugs
+ * that should be reported/patched.
+ *
+ * While you CAN use stdio/syscall file access in a program that has PHYSFS_*
+ * calls, doing so is not recommended, and you can not use system
+ * filehandles with PhysicsFS and vice versa.
+ *
+ * Note that archives need not be named as such: if you have a ZIP file and
+ * rename it with a .PKG extension, the file will still be recognized as a
+ * ZIP archive by PhysicsFS; the file's contents are used to determine its
+ * type where possible.
+ *
+ * Currently supported archive types:
+ * - .ZIP (pkZip/WinZip/Info-ZIP compatible)
+ * - .GRP (Build Engine groupfile archives)
+ * - .PAK (Quake I/II archive format)
+ * - .HOG (Descent I/II HOG file archives)
+ * - .MVL (Descent II movielib archives)
+ * - .WAD (DOOM engine archives)
+ *
+ *
+ * String policy for PhysicsFS 2.0 and later:
+ *
+ * PhysicsFS 1.0 could only deal with null-terminated ASCII strings. All high
+ * ASCII chars resulted in undefined behaviour, and there was no Unicode
+ * support at all. PhysicsFS 2.0 supports Unicode without breaking binary
+ * compatibility with the 1.0 API by using UTF-8 encoding of all strings
+ * passed in and out of the library.
+ *
+ * All strings passed through PhysicsFS are in null-terminated UTF-8 format.
+ * This means that if all you care about is English (ASCII characters <= 127)
+ * then you just use regular C strings. If you care about Unicode (and you
+ * should!) then you need to figure out what your platform wants, needs, and
+ * offers. If you are on Windows and build with Unicode support, your TCHAR
+ * strings are two bytes per character (this is called "UCS-2 encoding"). You
+ * should convert them to UTF-8 before handing them to PhysicsFS with
+ * PHYSFS_utf8FromUcs2(). If you're using Unix or Mac OS X, your wchar_t
+ * strings are four bytes per character ("UCS-4 encoding"). Use
+ * PHYSFS_utf8FromUcs4(). Mac OS X can give you UTF-8 directly from a
+ * CFString, and many Unixes generally give you C strings in UTF-8 format
+ * everywhere. If you have a single-byte high ASCII charset, like so-many
+ * European "codepages" you may be out of luck. We'll convert from "Latin1"
+ * to UTF-8 only, and never back to Latin1. If you're above ASCII 127, all
+ * bets are off: move to Unicode or use your platform's facilities. Passing a
+ * C string with high-ASCII data that isn't UTF-8 encoded will NOT do what
+ * you expect!
+ *
+ * Naturally, there's also PHYSFS_utf8ToUcs2() and PHYSFS_utf8ToUcs4() to get
+ * data back into a format you like. Behind the scenes, PhysicsFS will use
+ * Unicode where possible: the UTF-8 strings on Windows will be converted
+ * and used with the multibyte Windows APIs, for example.
+ *
+ * PhysicsFS offers basic encoding conversion support, but not a whole string
+ * library. Get your stuff into whatever format you can work with.
+ *
+ * Some platforms and archivers don't offer full Unicode support behind the
+ * scenes. For example, OS/2 only offers "codepages" and the filesystem
+ * itself doesn't support multibyte encodings. We make an earnest effort to
+ * convert to/from the current locale here, but all bets are off if
+ * you want to hand an arbitrary Japanese character through to these systems.
+ * Modern OSes (Mac OS X, Linux, Windows, PocketPC, etc) should all be fine.
+ * Many game-specific archivers are seriously unprepared for Unicode (the
+ * Descent HOG/MVL and Build Engine GRP archivers, for example, only offer a
+ * DOS 8.3 filename, for example). Nothing can be done for these, but they
+ * tend to be legacy formats for existing content that was all ASCII (and
+ * thus, valid UTF-8) anyhow. Other formats, like .ZIP, don't explicitly
+ * offer Unicode support, but unofficially expect filenames to be UTF-8
+ * encoded, and thus Just Work. Most everything does the right thing without
+ * bothering you, but it's good to be aware of these nuances in case they
+ * don't.
+ *
+ *
+ * Other stuff:
+ *
+ * Please see the file LICENSE.txt in the source's root directory for licensing
+ * and redistribution rights.
+ *
+ * Please see the file CREDITS.txt in the source's root directory for a more or
+ * less complete list of who's responsible for this.
+ *
+ * \author Ryan C. Gordon.
+ */
+
+#ifndef _INCLUDE_PHYSFS_H_
+#define _INCLUDE_PHYSFS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef DOXYGEN_SHOULD_IGNORE_THIS
+#if (defined _MSC_VER)
+#define __EXPORT__ __declspec(dllexport)
+#elif (__GNUC__ >= 3)
+#define __EXPORT__ __attribute__((visibility("default")))
+#else
+#define __EXPORT__
+#endif
+#endif /* DOXYGEN_SHOULD_IGNORE_THIS */
+
+/**
+ * \typedef PHYSFS_uint8
+ * \brief An unsigned, 8-bit integer type.
+ */
+typedef unsigned char PHYSFS_uint8;
+
+/**
+ * \typedef PHYSFS_sint8
+ * \brief A signed, 8-bit integer type.
+ */
+typedef signed char PHYSFS_sint8;
+
+/**
+ * \typedef PHYSFS_uint16
+ * \brief An unsigned, 16-bit integer type.
+ */
+typedef unsigned short PHYSFS_uint16;
+
+/**
+ * \typedef PHYSFS_sint16
+ * \brief A signed, 16-bit integer type.
+ */
+typedef signed short PHYSFS_sint16;
+
+/**
+ * \typedef PHYSFS_uint32
+ * \brief An unsigned, 32-bit integer type.
+ */
+typedef unsigned int PHYSFS_uint32;
+
+/**
+ * \typedef PHYSFS_sint32
+ * \brief A signed, 32-bit integer type.
+ */
+typedef signed int PHYSFS_sint32;
+
+/**
+ * \typedef PHYSFS_uint64
+ * \brief An unsigned, 64-bit integer type.
+ * \warning on platforms without any sort of 64-bit datatype, this is
+ * equivalent to PHYSFS_uint32!
+ */
+
+/**
+ * \typedef PHYSFS_sint64
+ * \brief A signed, 64-bit integer type.
+ * \warning on platforms without any sort of 64-bit datatype, this is
+ * equivalent to PHYSFS_sint32!
+ */
+
+
+#if (defined PHYSFS_NO_64BIT_SUPPORT) /* oh well. */
+typedef PHYSFS_uint32 PHYSFS_uint64;
+typedef PHYSFS_sint32 PHYSFS_sint64;
+#elif (defined _MSC_VER)
+typedef signed __int64 PHYSFS_sint64;
+typedef unsigned __int64 PHYSFS_uint64;
+#else
+typedef unsigned long long PHYSFS_uint64;
+typedef signed long long PHYSFS_sint64;
+#endif
+
+
+#ifndef DOXYGEN_SHOULD_IGNORE_THIS
+/* Make sure the types really have the right sizes */
+#define PHYSFS_COMPILE_TIME_ASSERT(name, x) \
+ typedef int PHYSFS_dummy_ ## name[(x) * 2 - 1]
+
+PHYSFS_COMPILE_TIME_ASSERT(uint8, sizeof(PHYSFS_uint8) == 1);
+PHYSFS_COMPILE_TIME_ASSERT(sint8, sizeof(PHYSFS_sint8) == 1);
+PHYSFS_COMPILE_TIME_ASSERT(uint16, sizeof(PHYSFS_uint16) == 2);
+PHYSFS_COMPILE_TIME_ASSERT(sint16, sizeof(PHYSFS_sint16) == 2);
+PHYSFS_COMPILE_TIME_ASSERT(uint32, sizeof(PHYSFS_uint32) == 4);
+PHYSFS_COMPILE_TIME_ASSERT(sint32, sizeof(PHYSFS_sint32) == 4);
+
+#ifndef PHYSFS_NO_64BIT_SUPPORT
+PHYSFS_COMPILE_TIME_ASSERT(uint64, sizeof(PHYSFS_uint64) == 8);
+PHYSFS_COMPILE_TIME_ASSERT(sint64, sizeof(PHYSFS_sint64) == 8);
+#endif
+
+#undef PHYSFS_COMPILE_TIME_ASSERT
+
+#endif /* DOXYGEN_SHOULD_IGNORE_THIS */
+
+
+/**
+ * \struct PHYSFS_File
+ * \brief A PhysicsFS file handle.
+ *
+ * You get a pointer to one of these when you open a file for reading,
+ * writing, or appending via PhysicsFS.
+ *
+ * As you can see from the lack of meaningful fields, you should treat this
+ * as opaque data. Don't try to manipulate the file handle, just pass the
+ * pointer you got, unmolested, to various PhysicsFS APIs.
+ *
+ * \sa PHYSFS_openRead
+ * \sa PHYSFS_openWrite
+ * \sa PHYSFS_openAppend
+ * \sa PHYSFS_close
+ * \sa PHYSFS_read
+ * \sa PHYSFS_write
+ * \sa PHYSFS_seek
+ * \sa PHYSFS_tell
+ * \sa PHYSFS_eof
+ * \sa PHYSFS_setBuffer
+ * \sa PHYSFS_flush
+ */
+typedef struct
+{
+ void *opaque; /**< That's all you get. Don't touch. */
+} PHYSFS_File;
+
+
+/**
+ * \def PHYSFS_file
+ * \brief 1.0 API compatibility define.
+ *
+ * PHYSFS_file is identical to PHYSFS_File. This #define is here for backwards
+ * compatibility with the 1.0 API, which had an inconsistent capitalization
+ * convention in this case. New code should use PHYSFS_File, as this #define
+ * may go away someday.
+ *
+ * \sa PHYSFS_File
+ */
+#define PHYSFS_file PHYSFS_File
+
+
+/**
+ * \struct PHYSFS_ArchiveInfo
+ * \brief Information on various PhysicsFS-supported archives.
+ *
+ * This structure gives you details on what sort of archives are supported
+ * by this implementation of PhysicsFS. Archives tend to be things like
+ * ZIP files and such.
+ *
+ * \warning Not all binaries are created equal! PhysicsFS can be built with
+ * or without support for various archives. You can check with
+ * PHYSFS_supportedArchiveTypes() to see if your archive type is
+ * supported.
+ *
+ * \sa PHYSFS_supportedArchiveTypes
+ */
+typedef struct
+{
+ const char *extension; /**< Archive file extension: "ZIP", for example. */
+ const char *description; /**< Human-readable archive description. */
+ const char *author; /**< Person who did support for this archive. */
+ const char *url; /**< URL related to this archive */
+} PHYSFS_ArchiveInfo;
+
+
+/**
+ * \struct PHYSFS_Version
+ * \brief Information the version of PhysicsFS in use.
+ *
+ * Represents the library's version as three levels: major revision
+ * (increments with massive changes, additions, and enhancements),
+ * minor revision (increments with backwards-compatible changes to the
+ * major revision), and patchlevel (increments with fixes to the minor
+ * revision).
+ *
+ * \sa PHYSFS_VERSION
+ * \sa PHYSFS_getLinkedVersion
+ */
+typedef struct
+{
+ PHYSFS_uint8 major; /**< major revision */
+ PHYSFS_uint8 minor; /**< minor revision */
+ PHYSFS_uint8 patch; /**< patchlevel */
+} PHYSFS_Version;
+
+#ifndef DOXYGEN_SHOULD_IGNORE_THIS
+#define PHYSFS_VER_MAJOR 1
+#define PHYSFS_VER_MINOR 1
+#define PHYSFS_VER_PATCH 1
+#endif /* DOXYGEN_SHOULD_IGNORE_THIS */
+
+
+/* PhysicsFS state stuff ... */
+
+/**
+ * \def PHYSFS_VERSION(x)
+ * \brief Macro to determine PhysicsFS version program was compiled against.
+ *
+ * This macro fills in a PHYSFS_Version structure with the version of the
+ * library you compiled against. This is determined by what header the
+ * compiler uses. Note that if you dynamically linked the library, you might
+ * have a slightly newer or older version at runtime. That version can be
+ * determined with PHYSFS_getLinkedVersion(), which, unlike PHYSFS_VERSION,
+ * is not a macro.
+ *
+ * \param x A pointer to a PHYSFS_Version struct to initialize.
+ *
+ * \sa PHYSFS_Version
+ * \sa PHYSFS_getLinkedVersion
+ */
+#define PHYSFS_VERSION(x) \
+{ \
+ (x)->major = PHYSFS_VER_MAJOR; \
+ (x)->minor = PHYSFS_VER_MINOR; \
+ (x)->patch = PHYSFS_VER_PATCH; \
+}
+
+
+/**
+ * \fn void PHYSFS_getLinkedVersion(PHYSFS_Version *ver)
+ * \brief Get the version of PhysicsFS that is linked against your program.
+ *
+ * If you are using a shared library (DLL) version of PhysFS, then it is
+ * possible that it will be different than the version you compiled against.
+ *
+ * This is a real function; the macro PHYSFS_VERSION tells you what version
+ * of PhysFS you compiled against:
+ *
+ * \code
+ * PHYSFS_Version compiled;
+ * PHYSFS_Version linked;
+ *
+ * PHYSFS_VERSION(&compiled);
+ * PHYSFS_getLinkedVersion(&linked);
+ * printf("We compiled against PhysFS version %d.%d.%d ...\n",
+ * compiled.major, compiled.minor, compiled.patch);
+ * printf("But we linked against PhysFS version %d.%d.%d.\n",
+ * linked.major, linked.minor, linked.patch);
+ * \endcode
+ *
+ * This function may be called safely at any time, even before PHYSFS_init().
+ *
+ * \sa PHYSFS_VERSION
+ */
+__EXPORT__ void PHYSFS_getLinkedVersion(PHYSFS_Version *ver);
+
+
+/**
+ * \fn int PHYSFS_init(const char *argv0)
+ * \brief Initialize the PhysicsFS library.
+ *
+ * This must be called before any other PhysicsFS function.
+ *
+ * This should be called prior to any attempts to change your process's
+ * current working directory.
+ *
+ * \param argv0 the argv[0] string passed to your program's mainline.
+ * This may be NULL on most platforms (such as ones without a
+ * standard main() function), but you should always try to pass
+ * something in here. Unix-like systems such as Linux _need_ to
+ * pass argv[0] from main() in here.
+ * \return nonzero on success, zero on error. Specifics of the error can be
+ * gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_deinit
+ * \sa PHYSFS_isInit
+ */
+__EXPORT__ int PHYSFS_init(const char *argv0);
+
+
+/**
+ * \fn int PHYSFS_deinit(void)
+ * \brief Deinitialize the PhysicsFS library.
+ *
+ * This closes any files opened via PhysicsFS, blanks the search/write paths,
+ * frees memory, and invalidates all of your file handles.
+ *
+ * Note that this call can FAIL if there's a file open for writing that
+ * refuses to close (for example, the underlying operating system was
+ * buffering writes to network filesystem, and the fileserver has crashed,
+ * or a hard drive has failed, etc). It is usually best to close all write
+ * handles yourself before calling this function, so that you can gracefully
+ * handle a specific failure.
+ *
+ * Once successfully deinitialized, PHYSFS_init() can be called again to
+ * restart the subsystem. All defaults API states are restored at this
+ * point.
+ *
+ * \return nonzero on success, zero on error. Specifics of the error can be
+ * gleaned from PHYSFS_getLastError(). If failure, state of PhysFS is
+ * undefined, and probably badly screwed up.
+ *
+ * \sa PHYSFS_init
+ * \sa PHYSFS_isInit
+ */
+__EXPORT__ int PHYSFS_deinit(void);
+
+
+/**
+ * \fn const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void)
+ * \brief Get a list of supported archive types.
+ *
+ * Get a list of archive types supported by this implementation of PhysicFS.
+ * These are the file formats usable for search path entries. This is for
+ * informational purposes only. Note that the extension listed is merely
+ * convention: if we list "ZIP", you can open a PkZip-compatible archive
+ * with an extension of "XYZ", if you like.
+ *
+ * The returned value is an array of pointers to PHYSFS_ArchiveInfo structures,
+ * with a NULL entry to signify the end of the list:
+ *
+ * \code
+ * PHYSFS_ArchiveInfo **i;
+ *
+ * for (i = PHYSFS_supportedArchiveTypes(); *i != NULL; i++)
+ * {
+ * printf("Supported archive: [%s], which is [%s].\n",
+ * i->extension, i->description);
+ * }
+ * \endcode
+ *
+ * The return values are pointers to static internal memory, and should
+ * be considered READ ONLY, and never freed.
+ *
+ * \return READ ONLY Null-terminated array of READ ONLY structures.
+ */
+__EXPORT__ const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void);
+
+
+/**
+ * \fn void PHYSFS_freeList(void *listVar)
+ * \brief Deallocate resources of lists returned by PhysicsFS.
+ *
+ * Certain PhysicsFS functions return lists of information that are
+ * dynamically allocated. Use this function to free those resources.
+ *
+ * \param listVar List of information specified as freeable by this function.
+ *
+ * \sa PHYSFS_getCdRomDirs
+ * \sa PHYSFS_enumerateFiles
+ * \sa PHYSFS_getSearchPath
+ */
+__EXPORT__ void PHYSFS_freeList(void *listVar);
+
+
+/**
+ * \fn const char *PHYSFS_getLastError(void)
+ * \brief Get human-readable error information.
+ *
+ * Get the last PhysicsFS error message as a human-readable, null-terminated
+ * string. This will be NULL if there's been no error since the last call to
+ * this function. The pointer returned by this call points to an internal
+ * buffer. Each thread has a unique error state associated with it, but each
+ * time a new error message is set, it will overwrite the previous one
+ * associated with that thread. It is safe to call this function at anytime,
+ * even before PHYSFS_init().
+ *
+ * It is not wise to expect a specific string of characters here, since the
+ * error message may be localized into an unfamiliar language. These strings
+ * are meant to be passed on directly to the user.
+ *
+ * \return READ ONLY string of last error message.
+ */
+__EXPORT__ const char *PHYSFS_getLastError(void);
+
+
+/**
+ * \fn const char *PHYSFS_getDirSeparator(void)
+ * \brief Get platform-dependent dir separator string.
+ *
+ * This returns "\\" on win32, "/" on Unix, and ":" on MacOS. It may be more
+ * than one character, depending on the platform, and your code should take
+ * that into account. Note that this is only useful for setting up the
+ * search/write paths, since access into those dirs always use '/'
+ * (platform-independent notation) to separate directories. This is also
+ * handy for getting platform-independent access when using stdio calls.
+ *
+ * \return READ ONLY null-terminated string of platform's dir separator.
+ */
+__EXPORT__ const char *PHYSFS_getDirSeparator(void);
+
+
+/**
+ * \fn void PHYSFS_permitSymbolicLinks(int allow)
+ * \brief Enable or disable following of symbolic links.
+ *
+ * Some physical filesystems and archives contain files that are just pointers
+ * to other files. On the physical filesystem, opening such a link will
+ * (transparently) open the file that is pointed to.
+ *
+ * By default, PhysicsFS will check if a file is really a symlink during open
+ * calls and fail if it is. Otherwise, the link could take you outside the
+ * write and search paths, and compromise security.
+ *
+ * If you want to take that risk, call this function with a non-zero parameter.
+ * Note that this is more for sandboxing a program's scripting language, in
+ * case untrusted scripts try to compromise the system. Generally speaking,
+ * a user could very well have a legitimate reason to set up a symlink, so
+ * unless you feel there's a specific danger in allowing them, you should
+ * permit them.
+ *
+ * Symlinks are only explicitly checked when dealing with filenames
+ * in platform-independent notation. That is, when setting up your
+ * search and write paths, etc, symlinks are never checked for.
+ *
+ * Symbolic link permission can be enabled or disabled at any time after
+ * you've called PHYSFS_init(), and is disabled by default.
+ *
+ * \param allow nonzero to permit symlinks, zero to deny linking.
+ *
+ * \sa PHYSFS_symbolicLinksPermitted
+ */
+__EXPORT__ void PHYSFS_permitSymbolicLinks(int allow);
+
+
+/* !!! FIXME: const this? */
+/**
+ * \fn char **PHYSFS_getCdRomDirs(void)
+ * \brief Get an array of paths to available CD-ROM drives.
+ *
+ * The dirs returned are platform-dependent ("D:\" on Win32, "/cdrom" or
+ * whatnot on Unix). Dirs are only returned if there is a disc ready and
+ * accessible in the drive. So if you've got two drives (D: and E:), and only
+ * E: has a disc in it, then that's all you get. If the user inserts a disc
+ * in D: and you call this function again, you get both drives. If, on a
+ * Unix box, the user unmounts a disc and remounts it elsewhere, the next
+ * call to this function will reflect that change.
+ *
+ * This function refers to "CD-ROM" media, but it really means "inserted disc
+ * media," such as DVD-ROM, HD-DVD, CDRW, and Blu-Ray discs. It looks for
+ * filesystems, and as such won't report an audio CD, unless there's a
+ * mounted filesystem track on it.
+ *
+ * The returned value is an array of strings, with a NULL entry to signify the
+ * end of the list:
+ *
+ * \code
+ * char **cds = PHYSFS_getCdRomDirs();
+ * char **i;
+ *
+ * for (i = cds; *i != NULL; i++)
+ * printf("cdrom dir [%s] is available.\n", *i);
+ *
+ * PHYSFS_freeList(cds);
+ * \endcode
+ *
+ * This call may block while drives spin up. Be forewarned.
+ *
+ * When you are done with the returned information, you may dispose of the
+ * resources by calling PHYSFS_freeList() with the returned pointer.
+ *
+ * \return Null-terminated array of null-terminated strings.
+ *
+ * \sa PHYSFS_getCdRomDirsCallback
+ */
+__EXPORT__ char **PHYSFS_getCdRomDirs(void);
+
+
+/**
+ * \fn const char *PHYSFS_getBaseDir(void)
+ * \brief Get the path where the application resides.
+ *
+ * Helper function.
+ *
+ * Get the "base dir". This is the directory where the application was run
+ * from, which is probably the installation directory, and may or may not
+ * be the process's current working directory.
+ *
+ * You should probably use the base dir in your search path.
+ *
+ * \return READ ONLY string of base dir in platform-dependent notation.
+ *
+ * \sa PHYSFS_getUserDir
+ */
+__EXPORT__ const char *PHYSFS_getBaseDir(void);
+
+
+/**
+ * \fn const char *PHYSFS_getUserDir(void)
+ * \brief Get the path where user's home directory resides.
+ *
+ * Helper function.
+ *
+ * Get the "user dir". This is meant to be a suggestion of where a specific
+ * user of the system can store files. On Unix, this is her home directory.
+ * On systems with no concept of multiple home directories (MacOS, win95),
+ * this will default to something like "C:\mybasedir\users\username"
+ * where "username" will either be the login name, or "default" if the
+ * platform doesn't support multiple users, either.
+ *
+ * You should probably use the user dir as the basis for your write dir, and
+ * also put it near the beginning of your search path.
+ *
+ * \return READ ONLY string of user dir in platform-dependent notation.
+ *
+ * \sa PHYSFS_getBaseDir
+ */
+__EXPORT__ const char *PHYSFS_getUserDir(void);
+
+
+/**
+ * \fn const char *PHYSFS_getWriteDir(void)
+ * \brief Get path where PhysicsFS will allow file writing.
+ *
+ * Get the current write dir. The default write dir is NULL.
+ *
+ * \return READ ONLY string of write dir in platform-dependent notation,
+ * OR NULL IF NO WRITE PATH IS CURRENTLY SET.
+ *
+ * \sa PHYSFS_setWriteDir
+ */
+__EXPORT__ const char *PHYSFS_getWriteDir(void);
+
+
+/**
+ * \fn int PHYSFS_setWriteDir(const char *newDir)
+ * \brief Tell PhysicsFS where it may write files.
+ *
+ * Set a new write dir. This will override the previous setting.
+ *
+ * This call will fail (and fail to change the write dir) if the current
+ * write dir still has files open in it.
+ *
+ * \param newDir The new directory to be the root of the write dir,
+ * specified in platform-dependent notation. Setting to NULL
+ * disables the write dir, so no files can be opened for
+ * writing via PhysicsFS.
+ * \return non-zero on success, zero on failure. All attempts to open a file
+ * for writing via PhysicsFS will fail until this call succeeds.
+ * Specifics of the error can be gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_getWriteDir
+ */
+__EXPORT__ int PHYSFS_setWriteDir(const char *newDir);
+
+
+/**
+ * \fn int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
+ * \brief Add an archive or directory to the search path.
+ *
+ * This is a legacy call in PhysicsFS 2.0, equivalent to:
+ * PHYSFS_mount(newDir, NULL, appendToPath);
+ *
+ * You must use this and not PHYSFS_mount if binary compatibility with
+ * PhysicsFS 1.0 is important (which it may not be for many people).
+ *
+ * \sa PHYSFS_mount
+ * \sa PHYSFS_removeFromSearchPath
+ * \sa PHYSFS_getSearchPath
+ */
+__EXPORT__ int PHYSFS_addToSearchPath(const char *newDir, int appendToPath);
+
+
+/**
+ * \fn int PHYSFS_removeFromSearchPath(const char *oldDir)
+ * \brief Remove a directory or archive from the search path.
+ *
+ * This must be a (case-sensitive) match to a dir or archive already in the
+ * search path, specified in platform-dependent notation.
+ *
+ * This call will fail (and fail to remove from the path) if the element still
+ * has files open in it.
+ *
+ * \param oldDir dir/archive to remove.
+ * \return nonzero on success, zero on failure.
+ * Specifics of the error can be gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_addToSearchPath
+ * \sa PHYSFS_getSearchPath
+ */
+__EXPORT__ int PHYSFS_removeFromSearchPath(const char *oldDir);
+
+
+/**
+ * \fn char **PHYSFS_getSearchPath(void)
+ * \brief Get the current search path.
+ *
+ * The default search path is an empty list.
+ *
+ * The returned value is an array of strings, with a NULL entry to signify the
+ * end of the list:
+ *
+ * \code
+ * char **i;
+ *
+ * for (i = PHYSFS_getSearchPath(); *i != NULL; i++)
+ * printf("[%s] is in the search path.\n", *i);
+ * \endcode
+ *
+ * When you are done with the returned information, you may dispose of the
+ * resources by calling PHYSFS_freeList() with the returned pointer.
+ *
+ * \return Null-terminated array of null-terminated strings. NULL if there
+ * was a problem (read: OUT OF MEMORY).
+ *
+ * \sa PHYSFS_getSearchPathCallback
+ * \sa PHYSFS_addToSearchPath
+ * \sa PHYSFS_removeFromSearchPath
+ */
+__EXPORT__ char **PHYSFS_getSearchPath(void);
+
+
+/**
+ * \fn int PHYSFS_setSaneConfig(const char *organization, const char *appName, const char *archiveExt, int includeCdRoms, int archivesFirst)
+ * \brief Set up sane, default paths.
+ *
+ * Helper function.
+ *
+ * The write dir will be set to "userdir/.organization/appName", which is
+ * created if it doesn't exist.
+ *
+ * The above is sufficient to make sure your program's configuration directory
+ * is separated from other clutter, and platform-independent. The period
+ * before "mygame" even hides the directory on Unix systems.
+ *
+ * The search path will be:
+ *
+ * - The Write Dir (created if it doesn't exist)
+ * - The Base Dir (PHYSFS_getBaseDir())
+ * - All found CD-ROM dirs (optionally)
+ *
+ * These directories are then searched for files ending with the extension
+ * (archiveExt), which, if they are valid and supported archives, will also
+ * be added to the search path. If you specified "PKG" for (archiveExt), and
+ * there's a file named data.PKG in the base dir, it'll be checked. Archives
+ * can either be appended or prepended to the search path in alphabetical
+ * order, regardless of which directories they were found in.
+ *
+ * All of this can be accomplished from the application, but this just does it
+ * all for you. Feel free to add more to the search path manually, too.
+ *
+ * \param organization Name of your company/group/etc to be used as a
+ * dirname, so keep it small, and no-frills.
+ *
+ * \param appName Program-specific name of your program, to separate it
+ * from other programs using PhysicsFS.
+ *
+ * \param archiveExt File extension used by your program to specify an
+ * archive. For example, Quake 3 uses "pk3", even though
+ * they are just zipfiles. Specify NULL to not dig out
+ * archives automatically. Do not specify the '.' char;
+ * If you want to look for ZIP files, specify "ZIP" and
+ * not ".ZIP" ... the archive search is case-insensitive.
+ *
+ * \param includeCdRoms Non-zero to include CD-ROMs in the search path, and
+ * (if (archiveExt) != NULL) search them for archives.
+ * This may cause a significant amount of blocking
+ * while discs are accessed, and if there are no discs
+ * in the drive (or even not mounted on Unix systems),
+ * then they may not be made available anyhow. You may
+ * want to specify zero and handle the disc setup
+ * yourself.
+ *
+ * \param archivesFirst Non-zero to prepend the archives to the search path.
+ * Zero to append them. Ignored if !(archiveExt).
+ *
+ * \return nonzero on success, zero on error. Specifics of the error can be
+ * gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_setSaneConfig(const char *organization,
+ const char *appName,
+ const char *archiveExt,
+ int includeCdRoms,
+ int archivesFirst);
+
+
+/* Directory management stuff ... */
+
+/**
+ * \fn int PHYSFS_mkdir(const char *dirName)
+ * \brief Create a directory.
+ *
+ * This is specified in platform-independent notation in relation to the
+ * write dir. All missing parent directories are also created if they
+ * don't exist.
+ *
+ * So if you've got the write dir set to "C:\mygame\writedir" and call
+ * PHYSFS_mkdir("downloads/maps") then the directories
+ * "C:\mygame\writedir\downloads" and "C:\mygame\writedir\downloads\maps"
+ * will be created if possible. If the creation of "maps" fails after we
+ * have successfully created "downloads", then the function leaves the
+ * created directory behind and reports failure.
+ *
+ * \param dirName New dir to create.
+ * \return nonzero on success, zero on error. Specifics of the error can be
+ * gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_delete
+ */
+__EXPORT__ int PHYSFS_mkdir(const char *dirName);
+
+
+/**
+ * \fn int PHYSFS_delete(const char *filename)
+ * \brief Delete a file or directory.
+ *
+ * (filename) is specified in platform-independent notation in relation to the
+ * write dir.
+ *
+ * A directory must be empty before this call can delete it.
+ *
+ * Deleting a symlink will remove the link, not what it points to, regardless
+ * of whether you "permitSymLinks" or not.
+ *
+ * So if you've got the write dir set to "C:\mygame\writedir" and call
+ * PHYSFS_delete("downloads/maps/level1.map") then the file
+ * "C:\mygame\writedir\downloads\maps\level1.map" is removed from the
+ * physical filesystem, if it exists and the operating system permits the
+ * deletion.
+ *
+ * Note that on Unix systems, deleting a file may be successful, but the
+ * actual file won't be removed until all processes that have an open
+ * filehandle to it (including your program) close their handles.
+ *
+ * Chances are, the bits that make up the file still exist, they are just
+ * made available to be written over at a later point. Don't consider this
+ * a security method or anything. :)
+ *
+ * \param filename Filename to delete.
+ * \return nonzero on success, zero on error. Specifics of the error can be
+ * gleaned from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_delete(const char *filename);
+
+
+/**
+ * \fn const char *PHYSFS_getRealDir(const char *filename)
+ * \brief Figure out where in the search path a file resides.
+ *
+ * The file is specified in platform-independent notation. The returned
+ * filename will be the element of the search path where the file was found,
+ * which may be a directory, or an archive. Even if there are multiple
+ * matches in different parts of the search path, only the first one found
+ * is used, just like when opening a file.
+ *
+ * So, if you look for "maps/level1.map", and C:\\mygame is in your search
+ * path and C:\\mygame\\maps\\level1.map exists, then "C:\mygame" is returned.
+ *
+ * If a any part of a match is a symbolic link, and you've not explicitly
+ * permitted symlinks, then it will be ignored, and the search for a match
+ * will continue.
+ *
+ * If you specify a fake directory that only exists as a mount point, it'll
+ * be associated with the first archive mounted there, even though that
+ * directory isn't necessarily contained in a real archive.
+ *
+ * \param filename file to look for.
+ * \return READ ONLY string of element of search path containing the
+ * the file in question. NULL if not found.
+ */
+__EXPORT__ const char *PHYSFS_getRealDir(const char *filename);
+
+
+/**
+ * \fn char **PHYSFS_enumerateFiles(const char *dir)
+ * \brief Get a file listing of a search path's directory.
+ *
+ * Matching directories are interpolated. That is, if "C:\mydir" is in the
+ * search path and contains a directory "savegames" that contains "x.sav",
+ * "y.sav", and "z.sav", and there is also a "C:\userdir" in the search path
+ * that has a "savegames" subdirectory with "w.sav", then the following code:
+ *
+ * \code
+ * char **rc = PHYSFS_enumerateFiles("savegames");
+ * char **i;
+ *
+ * for (i = rc; *i != NULL; i++)
+ * printf(" * We've got [%s].\n", *i);
+ *
+ * PHYSFS_freeList(rc);
+ * \endcode
+ *
+ * ...will print:
+ *
+ * \verbatim
+ * We've got [x.sav].
+ * We've got [y.sav].
+ * We've got [z.sav].
+ * We've got [w.sav].\endverbatim
+ *
+ * Feel free to sort the list however you like. We only promise there will
+ * be no duplicates, but not what order the final list will come back in.
+ *
+ * Don't forget to call PHYSFS_freeList() with the return value from this
+ * function when you are done with it.
+ *
+ * \param dir directory in platform-independent notation to enumerate.
+ * \return Null-terminated array of null-terminated strings.
+ *
+ * \sa PHYSFS_enumerateFilesCallback
+ */
+__EXPORT__ char **PHYSFS_enumerateFiles(const char *dir);
+
+
+/**
+ * \fn int PHYSFS_exists(const char *fname)
+ * \brief Determine if a file exists in the search path.
+ *
+ * Reports true if there is an entry anywhere in the search path by the
+ * name of (fname).
+ *
+ * Note that entries that are symlinks are ignored if
+ * PHYSFS_permitSymbolicLinks(1) hasn't been called, so you
+ * might end up further down in the search path than expected.
+ *
+ * \param fname filename in platform-independent notation.
+ * \return non-zero if filename exists. zero otherwise.
+ *
+ * \sa PHYSFS_isDirectory
+ * \sa PHYSFS_isSymbolicLink
+ */
+__EXPORT__ int PHYSFS_exists(const char *fname);
+
+
+/**
+ * \fn int PHYSFS_isDirectory(const char *fname)
+ * \brief Determine if a file in the search path is really a directory.
+ *
+ * Determine if the first occurence of (fname) in the search path is
+ * really a directory entry.
+ *
+ * Note that entries that are symlinks are ignored if
+ * PHYSFS_permitSymbolicLinks(1) hasn't been called, so you
+ * might end up further down in the search path than expected.
+ *
+ * \param fname filename in platform-independent notation.
+ * \return non-zero if filename exists and is a directory. zero otherwise.
+ *
+ * \sa PHYSFS_exists
+ * \sa PHYSFS_isSymbolicLink
+ */
+__EXPORT__ int PHYSFS_isDirectory(const char *fname);
+
+
+/**
+ * \fn int PHYSFS_isSymbolicLink(const char *fname)
+ * \brief Determine if a file in the search path is really a symbolic link.
+ *
+ * Determine if the first occurence of (fname) in the search path is
+ * really a symbolic link.
+ *
+ * Note that entries that are symlinks are ignored if
+ * PHYSFS_permitSymbolicLinks(1) hasn't been called, and as such,
+ * this function will always return 0 in that case.
+ *
+ * \param fname filename in platform-independent notation.
+ * \return non-zero if filename exists and is a symlink. zero otherwise.
+ *
+ * \sa PHYSFS_exists
+ * \sa PHYSFS_isDirectory
+ */
+__EXPORT__ int PHYSFS_isSymbolicLink(const char *fname);
+
+
+/**
+ * \fn PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename)
+ * \brief Get the last modification time of a file.
+ *
+ * The modtime is returned as a number of seconds since the epoch
+ * (Jan 1, 1970). The exact derivation and accuracy of this time depends on
+ * the particular archiver. If there is no reasonable way to obtain this
+ * information for a particular archiver, or there was some sort of error,
+ * this function returns (-1).
+ *
+ * \param filename filename to check, in platform-independent notation.
+ * \return last modified time of the file. -1 if it can't be determined.
+ */
+__EXPORT__ PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename);
+
+
+/* i/o stuff... */
+
+/**
+ * \fn PHYSFS_File *PHYSFS_openWrite(const char *filename)
+ * \brief Open a file for writing.
+ *
+ * Open a file for writing, in platform-independent notation and in relation
+ * to the write dir as the root of the writable filesystem. The specified
+ * file is created if it doesn't exist. If it does exist, it is truncated to
+ * zero bytes, and the writing offset is set to the start.
+ *
+ * Note that entries that are symlinks are ignored if
+ * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a
+ * symlink with this function will fail in such a case.
+ *
+ * \param filename File to open.
+ * \return A valid PhysicsFS filehandle on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_openRead
+ * \sa PHYSFS_openAppend
+ * \sa PHYSFS_write
+ * \sa PHYSFS_close
+ */
+__EXPORT__ PHYSFS_File *PHYSFS_openWrite(const char *filename);
+
+
+/**
+ * \fn PHYSFS_File *PHYSFS_openAppend(const char *filename)
+ * \brief Open a file for appending.
+ *
+ * Open a file for writing, in platform-independent notation and in relation
+ * to the write dir as the root of the writable filesystem. The specified
+ * file is created if it doesn't exist. If it does exist, the writing offset
+ * is set to the end of the file, so the first write will be the byte after
+ * the end.
+ *
+ * Note that entries that are symlinks are ignored if
+ * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a
+ * symlink with this function will fail in such a case.
+ *
+ * \param filename File to open.
+ * \return A valid PhysicsFS filehandle on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_openRead
+ * \sa PHYSFS_openWrite
+ * \sa PHYSFS_write
+ * \sa PHYSFS_close
+ */
+__EXPORT__ PHYSFS_File *PHYSFS_openAppend(const char *filename);
+
+
+/**
+ * \fn PHYSFS_File *PHYSFS_openRead(const char *filename)
+ * \brief Open a file for reading.
+ *
+ * Open a file for reading, in platform-independent notation. The search path
+ * is checked one at a time until a matching file is found, in which case an
+ * abstract filehandle is associated with it, and reading may be done.
+ * The reading offset is set to the first byte of the file.
+ *
+ * Note that entries that are symlinks are ignored if
+ * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a
+ * symlink with this function will fail in such a case.
+ *
+ * \param filename File to open.
+ * \return A valid PhysicsFS filehandle on success, NULL on error. Specifics
+ * of the error can be gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_openWrite
+ * \sa PHYSFS_openAppend
+ * \sa PHYSFS_read
+ * \sa PHYSFS_close
+ */
+__EXPORT__ PHYSFS_File *PHYSFS_openRead(const char *filename);
+
+
+/**
+ * \fn int PHYSFS_close(PHYSFS_File *handle)
+ * \brief Close a PhysicsFS filehandle.
+ *
+ * This call is capable of failing if the operating system was buffering
+ * writes to the physical media, and, now forced to write those changes to
+ * physical media, can not store the data for some reason. In such a case,
+ * the filehandle stays open. A well-written program should ALWAYS check the
+ * return value from the close call in addition to every writing call!
+ *
+ * \param handle handle returned from PHYSFS_open*().
+ * \return nonzero on success, zero on error. Specifics of the error can be
+ * gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_openRead
+ * \sa PHYSFS_openWrite
+ * \sa PHYSFS_openAppend
+ */
+__EXPORT__ int PHYSFS_close(PHYSFS_File *handle);
+
+
+/**
+ * \fn PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+ * \brief Read data from a PhysicsFS filehandle
+ *
+ * The file must be opened for reading.
+ *
+ * \param handle handle returned from PHYSFS_openRead().
+ * \param buffer buffer to store read data into.
+ * \param objSize size in bytes of objects being read from (handle).
+ * \param objCount number of (objSize) objects to read from (handle).
+ * \return number of objects read. PHYSFS_getLastError() can shed light on
+ * the reason this might be < (objCount), as can PHYSFS_eof().
+ * -1 if complete failure.
+ *
+ * \sa PHYSFS_eof
+ */
+__EXPORT__ PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle,
+ void *buffer,
+ PHYSFS_uint32 objSize,
+ PHYSFS_uint32 objCount);
+
+/**
+ * \fn PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount)
+ * \brief Write data to a PhysicsFS filehandle
+ *
+ * The file must be opened for writing.
+ *
+ * \param handle retval from PHYSFS_openWrite() or PHYSFS_openAppend().
+ * \param buffer buffer to store read data into.
+ * \param objSize size in bytes of objects being read from (handle).
+ * \param objCount number of (objSize) objects to read from (handle).
+ * \return number of objects written. PHYSFS_getLastError() can shed light on
+ * the reason this might be < (objCount). -1 if complete failure.
+ */
+__EXPORT__ PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle,
+ const void *buffer,
+ PHYSFS_uint32 objSize,
+ PHYSFS_uint32 objCount);
+
+
+/* File position stuff... */
+
+/**
+ * \fn int PHYSFS_eof(PHYSFS_File *handle)
+ * \brief Check for end-of-file state on a PhysicsFS filehandle.
+ *
+ * Determine if the end of file has been reached in a PhysicsFS filehandle.
+ *
+ * \param handle handle returned from PHYSFS_openRead().
+ * \return nonzero if EOF, zero if not.
+ *
+ * \sa PHYSFS_read
+ * \sa PHYSFS_tell
+ */
+__EXPORT__ int PHYSFS_eof(PHYSFS_File *handle);
+
+
+/**
+ * \fn PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle)
+ * \brief Determine current position within a PhysicsFS filehandle.
+ *
+ * \param handle handle returned from PHYSFS_open*().
+ * \return offset in bytes from start of file. -1 if error occurred.
+ * Specifics of the error can be gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_seek
+ */
+__EXPORT__ PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle);
+
+
+/**
+ * \fn int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos)
+ * \brief Seek to a new position within a PhysicsFS filehandle.
+ *
+ * The next read or write will occur at that place. Seeking past the
+ * beginning or end of the file is not allowed, and causes an error.
+ *
+ * \param handle handle returned from PHYSFS_open*().
+ * \param pos number of bytes from start of file to seek to.
+ * \return nonzero on success, zero on error. Specifics of the error can be
+ * gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_tell
+ */
+__EXPORT__ int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos);
+
+
+/**
+ * \fn PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle)
+ * \brief Get total length of a file in bytes.
+ *
+ * Note that if the file size can't be determined (since the archive is
+ * "streamed" or whatnot) than this will report (-1). Also note that if
+ * another process/thread is writing to this file at the same time, then
+ * the information this function supplies could be incorrect before you
+ * get it. Use with caution, or better yet, don't use at all.
+ *
+ * \param handle handle returned from PHYSFS_open*().
+ * \return size in bytes of the file. -1 if can't be determined.
+ *
+ * \sa PHYSFS_tell
+ * \sa PHYSFS_seek
+ */
+__EXPORT__ PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle);
+
+
+/* Buffering stuff... */
+
+/**
+ * \fn int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 bufsize)
+ * \brief Set up buffering for a PhysicsFS file handle.
+ *
+ * Define an i/o buffer for a file handle. A memory block of (bufsize) bytes
+ * will be allocated and associated with (handle).
+ *
+ * For files opened for reading, up to (bufsize) bytes are read from (handle)
+ * and stored in the internal buffer. Calls to PHYSFS_read() will pull
+ * from this buffer until it is empty, and then refill it for more reading.
+ * Note that compressed files, like ZIP archives, will decompress while
+ * buffering, so this can be handy for offsetting CPU-intensive operations.
+ * The buffer isn't filled until you do your next read.
+ *
+ * For files opened for writing, data will be buffered to memory until the
+ * buffer is full or the buffer is flushed. Closing a handle implicitly
+ * causes a flush...check your return values!
+ *
+ * Seeking, etc transparently accounts for buffering.
+ *
+ * You can resize an existing buffer by calling this function more than once
+ * on the same file. Setting the buffer size to zero will free an existing
+ * buffer.
+ *
+ * PhysicsFS file handles are unbuffered by default.
+ *
+ * Please check the return value of this function! Failures can include
+ * not being able to seek backwards in a read-only file when removing the
+ * buffer, not being able to allocate the buffer, and not being able to
+ * flush the buffer to disk, among other unexpected problems.
+ *
+ * \param handle handle returned from PHYSFS_open*().
+ * \param bufsize size, in bytes, of buffer to allocate.
+ * \return nonzero if successful, zero on error.
+ *
+ * \sa PHYSFS_flush
+ * \sa PHYSFS_read
+ * \sa PHYSFS_write
+ * \sa PHYSFS_close
+ */
+__EXPORT__ int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 bufsize);
+
+
+/**
+ * \fn int PHYSFS_flush(PHYSFS_File *handle)
+ * \brief Flush a buffered PhysicsFS file handle.
+ *
+ * For buffered files opened for writing, this will put the current contents
+ * of the buffer to disk and flag the buffer as empty if possible.
+ *
+ * For buffered files opened for reading or unbuffered files, this is a safe
+ * no-op, and will report success.
+ *
+ * \param handle handle returned from PHYSFS_open*().
+ * \return nonzero if successful, zero on error.
+ *
+ * \sa PHYSFS_setBuffer
+ * \sa PHYSFS_close
+ */
+__EXPORT__ int PHYSFS_flush(PHYSFS_File *handle);
+
+
+/* Byteorder stuff... */
+
+/**
+ * \fn PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 val)
+ * \brief Swap littleendian signed 16 to platform's native byte order.
+ *
+ * Take a 16-bit signed value in littleendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ */
+__EXPORT__ PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 val);
+
+
+/**
+ * \fn PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 val)
+ * \brief Swap littleendian unsigned 16 to platform's native byte order.
+ *
+ * Take a 16-bit unsigned value in littleendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ */
+__EXPORT__ PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 val);
+
+/**
+ * \fn PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 val)
+ * \brief Swap littleendian signed 32 to platform's native byte order.
+ *
+ * Take a 32-bit signed value in littleendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ */
+__EXPORT__ PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 val);
+
+
+/**
+ * \fn PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 val)
+ * \brief Swap littleendian unsigned 32 to platform's native byte order.
+ *
+ * Take a 32-bit unsigned value in littleendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ */
+__EXPORT__ PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 val);
+
+/**
+ * \fn PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 val)
+ * \brief Swap littleendian signed 64 to platform's native byte order.
+ *
+ * Take a 64-bit signed value in littleendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ *
+ * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 val);
+
+
+/**
+ * \fn PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 val)
+ * \brief Swap littleendian unsigned 64 to platform's native byte order.
+ *
+ * Take a 64-bit unsigned value in littleendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ *
+ * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 val);
+
+
+/**
+ * \fn PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 val)
+ * \brief Swap bigendian signed 16 to platform's native byte order.
+ *
+ * Take a 16-bit signed value in bigendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ */
+__EXPORT__ PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 val);
+
+
+/**
+ * \fn PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 val)
+ * \brief Swap bigendian unsigned 16 to platform's native byte order.
+ *
+ * Take a 16-bit unsigned value in bigendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ */
+__EXPORT__ PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 val);
+
+/**
+ * \fn PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 val)
+ * \brief Swap bigendian signed 32 to platform's native byte order.
+ *
+ * Take a 32-bit signed value in bigendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ */
+__EXPORT__ PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 val);
+
+
+/**
+ * \fn PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 val)
+ * \brief Swap bigendian unsigned 32 to platform's native byte order.
+ *
+ * Take a 32-bit unsigned value in bigendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ */
+__EXPORT__ PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 val);
+
+
+/**
+ * \fn PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 val)
+ * \brief Swap bigendian signed 64 to platform's native byte order.
+ *
+ * Take a 64-bit signed value in bigendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ *
+ * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 val);
+
+
+/**
+ * \fn PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 val)
+ * \brief Swap bigendian unsigned 64 to platform's native byte order.
+ *
+ * Take a 64-bit unsigned value in bigendian format and convert it to
+ * the platform's native byte order.
+ *
+ * \param val value to convert
+ * \return converted value.
+ *
+ * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 val);
+
+
+/**
+ * \fn int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val)
+ * \brief Read and convert a signed 16-bit littleendian value.
+ *
+ * Convenience function. Read a signed 16-bit littleendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val);
+
+
+/**
+ * \fn int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val)
+ * \brief Read and convert an unsigned 16-bit littleendian value.
+ *
+ * Convenience function. Read an unsigned 16-bit littleendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ *
+ */
+__EXPORT__ int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val);
+
+
+/**
+ * \fn int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val)
+ * \brief Read and convert a signed 16-bit bigendian value.
+ *
+ * Convenience function. Read a signed 16-bit bigendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val);
+
+
+/**
+ * \fn int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val)
+ * \brief Read and convert an unsigned 16-bit bigendian value.
+ *
+ * Convenience function. Read an unsigned 16-bit bigendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ *
+ */
+__EXPORT__ int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val);
+
+
+/**
+ * \fn int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val)
+ * \brief Read and convert a signed 32-bit littleendian value.
+ *
+ * Convenience function. Read a signed 32-bit littleendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val);
+
+
+/**
+ * \fn int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val)
+ * \brief Read and convert an unsigned 32-bit littleendian value.
+ *
+ * Convenience function. Read an unsigned 32-bit littleendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ *
+ */
+__EXPORT__ int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val);
+
+
+/**
+ * \fn int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val)
+ * \brief Read and convert a signed 32-bit bigendian value.
+ *
+ * Convenience function. Read a signed 32-bit bigendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val);
+
+
+/**
+ * \fn int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val)
+ * \brief Read and convert an unsigned 32-bit bigendian value.
+ *
+ * Convenience function. Read an unsigned 32-bit bigendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ *
+ */
+__EXPORT__ int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val);
+
+
+/**
+ * \fn int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val)
+ * \brief Read and convert a signed 64-bit littleendian value.
+ *
+ * Convenience function. Read a signed 64-bit littleendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ *
+ * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val);
+
+
+/**
+ * \fn int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val)
+ * \brief Read and convert an unsigned 64-bit littleendian value.
+ *
+ * Convenience function. Read an unsigned 64-bit littleendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ *
+ * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val);
+
+
+/**
+ * \fn int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val)
+ * \brief Read and convert a signed 64-bit bigendian value.
+ *
+ * Convenience function. Read a signed 64-bit bigendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ *
+ * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val);
+
+
+/**
+ * \fn int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val)
+ * \brief Read and convert an unsigned 64-bit bigendian value.
+ *
+ * Convenience function. Read an unsigned 64-bit bigendian value from a
+ * file and convert it to the platform's native byte order.
+ *
+ * \param file PhysicsFS file handle from which to read.
+ * \param val pointer to where value should be stored.
+ * \return zero on failure, non-zero on success. If successful, (*val) will
+ * store the result. On failure, you can find out what went wrong
+ * from PHYSFS_getLastError().
+ *
+ * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val);
+
+
+/**
+ * \fn int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val)
+ * \brief Convert and write a signed 16-bit littleendian value.
+ *
+ * Convenience function. Convert a signed 16-bit value from the platform's
+ * native byte order to littleendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val);
+
+
+/**
+ * \fn int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val)
+ * \brief Convert and write an unsigned 16-bit littleendian value.
+ *
+ * Convenience function. Convert an unsigned 16-bit value from the platform's
+ * native byte order to littleendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val);
+
+
+/**
+ * \fn int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val)
+ * \brief Convert and write a signed 16-bit bigendian value.
+ *
+ * Convenience function. Convert a signed 16-bit value from the platform's
+ * native byte order to bigendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val);
+
+
+/**
+ * \fn int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val)
+ * \brief Convert and write an unsigned 16-bit bigendian value.
+ *
+ * Convenience function. Convert an unsigned 16-bit value from the platform's
+ * native byte order to bigendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val);
+
+
+/**
+ * \fn int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val)
+ * \brief Convert and write a signed 32-bit littleendian value.
+ *
+ * Convenience function. Convert a signed 32-bit value from the platform's
+ * native byte order to littleendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val);
+
+
+/**
+ * \fn int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val)
+ * \brief Convert and write an unsigned 32-bit littleendian value.
+ *
+ * Convenience function. Convert an unsigned 32-bit value from the platform's
+ * native byte order to littleendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val);
+
+
+/**
+ * \fn int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val)
+ * \brief Convert and write a signed 32-bit bigendian value.
+ *
+ * Convenience function. Convert a signed 32-bit value from the platform's
+ * native byte order to bigendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val);
+
+
+/**
+ * \fn int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val)
+ * \brief Convert and write an unsigned 32-bit bigendian value.
+ *
+ * Convenience function. Convert an unsigned 32-bit value from the platform's
+ * native byte order to bigendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ */
+__EXPORT__ int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val);
+
+
+/**
+ * \fn int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val)
+ * \brief Convert and write a signed 64-bit littleendian value.
+ *
+ * Convenience function. Convert a signed 64-bit value from the platform's
+ * native byte order to littleendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ *
+ * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val);
+
+
+/**
+ * \fn int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val)
+ * \brief Convert and write an unsigned 64-bit littleendian value.
+ *
+ * Convenience function. Convert an unsigned 64-bit value from the platform's
+ * native byte order to littleendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ *
+ * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val);
+
+
+/**
+ * \fn int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val)
+ * \brief Convert and write a signed 64-bit bigending value.
+ *
+ * Convenience function. Convert a signed 64-bit value from the platform's
+ * native byte order to bigendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ *
+ * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val);
+
+
+/**
+ * \fn int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val)
+ * \brief Convert and write an unsigned 64-bit bigendian value.
+ *
+ * Convenience function. Convert an unsigned 64-bit value from the platform's
+ * native byte order to bigendian and write it to a file.
+ *
+ * \param file PhysicsFS file handle to which to write.
+ * \param val Value to convert and write.
+ * \return zero on failure, non-zero on success. On failure, you can
+ * find out what went wrong from PHYSFS_getLastError().
+ *
+ * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without
+ * any sort of 64-bit support.
+ */
+__EXPORT__ int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val);
+
+
+/* Everything above this line is part of the PhysicsFS 1.0 API. */
+
+/**
+ * \fn int PHYSFS_isInit(void)
+ * \brief Determine if the PhysicsFS library is initialized.
+ *
+ * Once PHYSFS_init() returns successfully, this will return non-zero.
+ * Before a successful PHYSFS_init() and after PHYSFS_deinit() returns
+ * successfully, this will return zero. This function is safe to call at
+ * any time.
+ *
+ * \return non-zero if library is initialized, zero if library is not.
+ *
+ * \sa PHYSFS_init
+ * \sa PHYSFS_deinit
+ */
+__EXPORT__ int PHYSFS_isInit(void);
+
+
+/**
+ * \fn int PHYSFS_symbolicLinksPermitted(void)
+ * \brief Determine if the symbolic links are permitted.
+ *
+ * This reports the setting from the last call to PHYSFS_permitSymbolicLinks().
+ * If PHYSFS_permitSymbolicLinks() hasn't been called since the library was
+ * last initialized, symbolic links are implicitly disabled.
+ *
+ * \return non-zero if symlinks are permitted, zero if not.
+ *
+ * \sa PHYSFS_permitSymbolicLinks
+ */
+__EXPORT__ int PHYSFS_symbolicLinksPermitted(void);
+
+
+/**
+ * \struct PHYSFS_Allocator
+ * \brief PhysicsFS allocation function pointers.
+ *
+ * (This is for limited, hardcore use. If you don't immediately see a need
+ * for it, you can probably ignore this forever.)
+ *
+ * You create one of these structures for use with PHYSFS_setAllocator.
+ * Allocators are assumed to be reentrant by the caller; please mutex
+ * accordingly.
+ *
+ * Allocations are always discussed in 64-bits, for future expansion...we're
+ * on the cusp of a 64-bit transition, and we'll probably be allocating 6
+ * gigabytes like it's nothing sooner or later, and I don't want to change
+ * this again at that point. If you're on a 32-bit platform and have to
+ * downcast, it's okay to return NULL if the allocation is greater than
+ * 4 gigabytes, since you'd have to do so anyhow.
+ *
+ * \sa PHYSFS_setAllocator
+ */
+typedef struct
+{
+ int (*Init)(void); /**< Initialize. Can be NULL. Zero on failure. */
+ void (*Deinit)(void); /**< Deinitialize your allocator. Can be NULL. */
+ void *(*Malloc)(PHYSFS_uint64); /**< Allocate like malloc(). */
+ void *(*Realloc)(void *, PHYSFS_uint64); /**< Reallocate like realloc(). */
+ void (*Free)(void *); /**< Free memory from Malloc or Realloc. */
+} PHYSFS_Allocator;
+
+
+/**
+ * \fn int PHYSFS_setAllocator(const PHYSFS_Allocator *allocator)
+ * \brief Hook your own allocation routines into PhysicsFS.
+ *
+ * (This is for limited, hardcore use. If you don't immediately see a need
+ * for it, you can probably ignore this forever.)
+ *
+ * By default, PhysicsFS will use whatever is reasonable for a platform
+ * to manage dynamic memory (usually ANSI C malloc/realloc/calloc/free, but
+ * some platforms might use something else), but in some uncommon cases, the
+ * app might want more control over the library's memory management. This
+ * lets you redirect PhysicsFS to use your own allocation routines instead.
+ * You can only call this function before PHYSFS_init(); if the library is
+ * initialized, it'll reject your efforts to change the allocator mid-stream.
+ * You may call this function after PHYSFS_deinit() if you are willing to
+ * shut down the library and restart it with a new allocator; this is a safe
+ * and supported operation. The allocator remains intact between deinit/init
+ * calls. If you want to return to the platform's default allocator, pass a
+ * NULL in here.
+ *
+ * If you aren't immediately sure what to do with this function, you can
+ * safely ignore it altogether.
+ *
+ * \param allocator Structure containing your allocator's entry points.
+ * \return zero on failure, non-zero on success. This call only fails
+ * when used between PHYSFS_init() and PHYSFS_deinit() calls.
+ */
+__EXPORT__ int PHYSFS_setAllocator(const PHYSFS_Allocator *allocator);
+
+
+/**
+ * \fn int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath)
+ * \brief Add an archive or directory to the search path.
+ *
+ * If this is a duplicate, the entry is not added again, even though the
+ * function succeeds. You may not add the same archive to two different
+ * mountpoints: duplicate checking is done against the archive and not the
+ * mountpoint.
+ *
+ * When you mount an archive, it is added to a virtual file system...all files
+ * in all of the archives are interpolated into a single hierachical file
+ * tree. Two archives mounted at the same place (or an archive with files
+ * overlapping another mountpoint) may have overlapping files: in such a case,
+ * the file earliest in the search path is selected, and the other files are
+ * inaccessible to the application. This allows archives to be used to
+ * override previous revisions; you can use the mounting mechanism to place
+ * archives at a specific point in the file tree and prevent overlap; this
+ * is useful for downloadable mods that might trample over application data
+ * or each other, for example.
+ *
+ * The mountpoint does not need to exist prior to mounting, which is different
+ * than those familiar with the Unix concept of "mounting" may not expect.
+ * As well, more than one archive can be mounted to the same mountpoint, or
+ * mountpoints and archive contents can overlap...the interpolation mechanism
+ * still functions as usual.
+ *
+ * \param newDir directory or archive to add to the path, in
+ * platform-dependent notation.
+ * \param mountPoint Location in the interpolated tree that this archive
+ * will be "mounted", in platform-independent notation.
+ * NULL or "" is equivalent to "/".
+ * \param appendToPath nonzero to append to search path, zero to prepend.
+ * \return nonzero if added to path, zero on failure (bogus archive, dir
+ * missing, etc). Specifics of the error can be
+ * gleaned from PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_removeFromSearchPath
+ * \sa PHYSFS_getSearchPath
+ * \sa PHYSFS_getMountPoint
+ */
+__EXPORT__ int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath);
+
+/**
+ * \fn int PHYSFS_getMountPoint(const char *dir)
+ * \brief Determine a mounted archive's mountpoint.
+ *
+ * You give this function the name of an archive or dir you successfully
+ * added to the search path, and it reports the location in the interpolated
+ * tree where it is mounted. Files mounted with a NULL mountpoint or through
+ * PHYSFS_addToSearchPath() will report "/". The return value is READ ONLY
+ * and valid until the archive is removed from the search path.
+ *
+ * \param dir directory or archive previously added to the path, in
+ * platform-dependent notation. This must match the string
+ * used when adding, even if your string would also reference
+ * the same file with a different string of characters.
+ * \return READ-ONLY string of mount point if added to path, NULL on failure
+ * (bogus archive, etc) Specifics of the error can be gleaned from
+ * PHYSFS_getLastError().
+ *
+ * \sa PHYSFS_removeFromSearchPath
+ * \sa PHYSFS_getSearchPath
+ * \sa PHYSFS_getMountPoint
+ */
+__EXPORT__ const char *PHYSFS_getMountPoint(const char *dir);
+
+
+/**
+ * \typedef PHYSFS_StringCallback
+ * \brief Function signature for callbacks that report strings.
+ *
+ * These are used to report a list of strings to an original caller, one
+ * string per callback. All strings are UTF-8 encoded. Functions should not
+ * try to modify or free the string's memory.
+ *
+ * These callbacks are used, starting in PhysicsFS 1.1, as an alternative to
+ * functions that would return lists that need to be cleaned up with
+ * PHYSFS_freeList(). The callback means that the library doesn't need to
+ * allocate an entire list and all the strings up front.
+ *
+ * Be aware that promises data ordering in the list versions are not
+ * necessarily so in the callback versions. Check the documentation on
+ * specific APIs, but strings may not be sorted as you expect.
+ *
+ * \param data User-defined data pointer, passed through from the API
+ * that eventually called the callback.
+ * \param str The string data about which the callback is meant to inform.
+ *
+ * \sa PHYSFS_getCdRomDirsCallback
+ * \sa PHYSFS_getSearchPathCallback
+ */
+typedef void (*PHYSFS_StringCallback)(void *data, const char *str);
+
+
+/**
+ * \typedef PHYSFS_EnumFilesCallback
+ * \brief Function signature for callbacks that enumerate files.
+ *
+ * These are used to report a list of directory entries to an original caller,
+ * one file/dir/symlink per callback. All strings are UTF-8 encoded.
+ * Functions should not try to modify or free any string's memory.
+ *
+ * These callbacks are used, starting in PhysicsFS 1.1, as an alternative to
+ * functions that would return lists that need to be cleaned up with
+ * PHYSFS_freeList(). The callback means that the library doesn't need to
+ * allocate an entire list and all the strings up front.
+ *
+ * Be aware that promises data ordering in the list versions are not
+ * necessarily so in the callback versions. Check the documentation on
+ * specific APIs, but strings may not be sorted as you expect.
+ *
+ * \param data User-defined data pointer, passed through from the API
+ * that eventually called the callback.
+ * \param origdir A string containing the full path, in platform-independent
+ * notation, of the directory containing this file. In most
+ * cases, this is the directory on which you requested
+ * enumeration, passed in the callback for your convenience.
+ * \param fname The filename that is being enumerated. It may not be in
+ * alphabetical order compared to other callbacks that have
+ * fired, and it will not contain the full path. You can
+ * recreate the fullpath with $origdir/$fname ... The file
+ * can be a subdirectory, a file, a symlink, etc.
+ *
+ * \sa PHYSFS_enumerateFilesCallback
+ */
+typedef void (*PHYSFS_EnumFilesCallback)(void *data, const char *origdir,
+ const char *fname);
+
+
+/**
+ * \fn void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback c, void *d)
+ * \brief Enumerate CD-ROM directories, using an application-defined callback.
+ *
+ * Internally, PHYSFS_getCdRomDirs() just calls this function and then builds
+ * a list before returning to the application, so functionality is identical
+ * except for how the information is represented to the application.
+ *
+ * Unlike PHYSFS_getCdRomDirs(), this function does not return an array.
+ * Rather, it calls a function specified by the application once per
+ * detected disc:
+ *
+ * \code
+ *
+ * static void foundDisc(void *data, const char *cddir)
+ * {
+ * printf("cdrom dir [%s] is available.\n", cddir);
+ * }
+ *
+ * // ...
+ * PHYSFS_getCdRomDirsCallback(foundDisc, NULL);
+ * \endcode
+ *
+ * This call may block while drives spin up. Be forewarned.
+ *
+ * \param c Callback function to notify about detected drives.
+ * \param d Application-defined data passed to callback. Can be NULL.
+ *
+ * \sa PHYSFS_StringCallback
+ * \sa PHYSFS_getCdRomDirs
+ */
+__EXPORT__ void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback c, void *d);
+
+
+/**
+ * \fn void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback c, void *d)
+ * \brief Enumerate the search path, using an application-defined callback.
+ *
+ * Internally, PHYSFS_getSearchPath() just calls this function and then builds
+ * a list before returning to the application, so functionality is identical
+ * except for how the information is represented to the application.
+ *
+ * Unlike PHYSFS_getSearchPath(), this function does not return an array.
+ * Rather, it calls a function specified by the application once per
+ * element of the search path:
+ *
+ * \code
+ *
+ * static void printSearchPath(void *data, const char *pathItem)
+ * {
+ * printf("[%s] is in the search path.\n", pathItem);
+ * }
+ *
+ * // ...
+ * PHYSFS_getSearchPathCallback(printSearchPath, NULL);
+ * \endcode
+ *
+ * Elements of the search path are reported in order search priority, so the
+ * first archive/dir that would be examined when looking for a file is the
+ * first element passed through the callback.
+ *
+ * \param c Callback function to notify about search path elements.
+ * \param d Application-defined data passed to callback. Can be NULL.
+ *
+ * \sa PHYSFS_StringCallback
+ * \sa PHYSFS_getSearchPath
+ */
+__EXPORT__ void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback c, void *d);
+
+
+/**
+ * \fn void PHYSFS_enumerateFilesCallback(const char *dir, PHYSFS_EnumFilesCallback c, void *d)
+ * \brief Get a file listing of a search path's directory, using an application-defined callback.
+ *
+ * Internally, PHYSFS_enumerateFiles() just calls this function and then builds
+ * a list before returning to the application, so functionality is identical
+ * except for how the information is represented to the application.
+ *
+ * Unlike PHYSFS_enumerateFiles(), this function does not return an array.
+ * Rather, it calls a function specified by the application once per
+ * element of the search path:
+ *
+ * \code
+ *
+ * static void printDir(void *data, const char *origdir, const char *fname)
+ * {
+ * printf(" * We've got [%s] in [%s].\n", fname, origdir);
+ * }
+ *
+ * // ...
+ * PHYSFS_enumerateFilesCallback("/some/path", printDir, NULL);
+ * \endcode
+ *
+ * Items sent to the callback are not guaranteed to be in any order whatsoever.
+ * There is no sorting done at this level, and if you need that, you should
+ * probably use PHYSFS_enumerateFiles() instead, which guarantees
+ * alphabetical sorting. This form reports whatever is discovered in each
+ * archive before moving on to the next. Even within one archive, we can't
+ * guarantee what order it will discover data. <em>Any sorting you find in
+ * these callbacks is just pure luck. Do not rely on it.</em>
+ *
+ * \param dir Directory, in platform-independent notation, to enumerate.
+ * \param c Callback function to notify about search path elements.
+ * \param d Application-defined data passed to callback. Can be NULL.
+ *
+ * \sa PHYSFS_EnumFilesCallback
+ * \sa PHYSFS_enumerateFiles
+ */
+__EXPORT__ void PHYSFS_enumerateFilesCallback(const char *dir,
+ PHYSFS_EnumFilesCallback c,
+ void *d);
+
+/**
+ * \fn void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, PHYSFS_uint64 len)
+ * \brief Convert a UCS-4 string to a UTF-8 string.
+ *
+ * UCS-4 strings are 32-bits per character: \c wchar_t on Unix.
+ *
+ * To ensure that the destination buffer is large enough for the conversion,
+ * please allocate a buffer that is the same size as the source buffer. UTF-8
+ * never uses more than 32-bits per character, so while it may shrink a UCS-4
+ * string, it will never expand it.
+ *
+ * Strings that don't fit in the destination buffer will be truncated, but
+ * will always be null-terminated and never have an incomplete UTF-8
+ * sequence at the end.
+ *
+ * \param src Null-terminated source string in UCS-4 format.
+ * \param dst Buffer to store converted UTF-8 string.
+ * \param len Size, in bytes, of destination buffer.
+ */
+__EXPORT__ void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst,
+ PHYSFS_uint64 len);
+
+/**
+ * \fn void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, PHYSFS_uint64 len)
+ * \brief Convert a UTF-8 string to a UCS-4 string.
+ *
+ * UCS-4 strings are 32-bits per character: \c wchar_t on Unix.
+ *
+ * To ensure that the destination buffer is large enough for the conversion,
+ * please allocate a buffer that is four times the size of the source buffer.
+ * UTF-8 uses from one to four bytes per character, but UCS-4 always uses
+ * four, so an entirely low-ASCII string will quadruple in size!
+ *
+ * Strings that don't fit in the destination buffer will be truncated, but
+ * will always be null-terminated and never have an incomplete UCS-4
+ * sequence at the end.
+ *
+ * \param src Null-terminated source string in UTF-8 format.
+ * \param dst Buffer to store converted UCS-4 string.
+ * \param len Size, in bytes, of destination buffer.
+ */
+__EXPORT__ void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst,
+ PHYSFS_uint64 len);
+
+/**
+ * \fn void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len)
+ * \brief Convert a UCS-2 string to a UTF-8 string.
+ *
+ * UCS-2 strings are 16-bits per character: \c TCHAR on Windows, when building
+ * with Unicode support.
+ *
+ * To ensure that the destination buffer is large enough for the conversion,
+ * please allocate a buffer that is double the size of the source buffer.
+ * UTF-8 never uses more than 32-bits per character, so while it may shrink
+ * a UCS-2 string, it may also expand it.
+ *
+ * Strings that don't fit in the destination buffer will be truncated, but
+ * will always be null-terminated and never have an incomplete UTF-8
+ * sequence at the end.
+ *
+ * Please note that UCS-2 is not UTF-16; we do not support the "surrogate"
+ * values at this time.
+ *
+ * \param src Null-terminated source string in UCS-2 format.
+ * \param dst Buffer to store converted UTF-8 string.
+ * \param len Size, in bytes, of destination buffer.
+ */
+__EXPORT__ void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst,
+ PHYSFS_uint64 len);
+
+/**
+ * \fn PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len)
+ * \brief Convert a UTF-8 string to a UCS-2 string.
+ *
+ * UCS-2 strings are 16-bits per character: \c TCHAR on Windows, when building
+ * with Unicode support.
+ *
+ * To ensure that the destination buffer is large enough for the conversion,
+ * please allocate a buffer that is double the size of the source buffer.
+ * UTF-8 uses from one to four bytes per character, but UCS-2 always uses
+ * two, so an entirely low-ASCII string will double in size!
+ *
+ * Strings that don't fit in the destination buffer will be truncated, but
+ * will always be null-terminated and never have an incomplete UCS-2
+ * sequence at the end.
+ *
+ * Please note that UCS-2 is not UTF-16; we do not support the "surrogate"
+ * values at this time.
+ *
+ * \param src Null-terminated source string in UTF-8 format.
+ * \param dst Buffer to store converted UCS-2 string.
+ * \param len Size, in bytes, of destination buffer.
+ */
+__EXPORT__ void PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst,
+ PHYSFS_uint64 len);
+
+/**
+ * \fn void PHYSFS_utf8FromLatin1(const char *src, char *dst, PHYSFS_uint64 len)
+ * \brief Convert a UTF-8 string to a Latin1 string.
+ *
+ * Latin1 strings are 8-bits per character: a popular "high ASCII"
+ * encoding.
+ *
+ * To ensure that the destination buffer is large enough for the conversion,
+ * please allocate a buffer that is double the size of the source buffer.
+ * UTF-8 expands latin1 codepoints over 127 from 1 to 2 bytes, so the string
+ * may grow in some cases.
+ *
+ * Strings that don't fit in the destination buffer will be truncated, but
+ * will always be null-terminated and never have an incomplete UTF-8
+ * sequence at the end.
+ *
+ * Please note that we do not supply a UTF-8 to Latin1 converter, since Latin1
+ * can't express most Unicode codepoints. It's a legacy encoding; you should
+ * be converting away from it at all times.
+ *
+ * \param src Null-terminated source string in Latin1 format.
+ * \param dst Buffer to store converted UTF-8 string.
+ * \param len Size, in bytes, of destination buffer.
+ */
+__EXPORT__ void PHYSFS_utf8FromLatin1(const char *src, char *dst,
+ PHYSFS_uint64 len);
+
+/* Everything above this line is part of the PhysicsFS 2.0 API. */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined _INCLUDE_PHYSFS_H_ */
+
+/* end of physfs.h ... */
+
--- /dev/null
+%define version @VERSION@
+%define release 1
+%define name physfs
+%define prefix /usr
+
+Summary: PhysicsFS file abstraction layer for games
+Name: %{name}
+Version: %{version}
+Release: %{release}
+Prefix: %{prefix}
+Copyright: zlib license
+Group: System Environment/Libraries
+URL: http://www.icculus/physfs/
+Source: physfs-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}
+BuildRequires: doxygen, readline-devel, ncurses-devel
+Requires: readline, ncurses, zlib
+
+%description
+PhysicsFS is a library to provide abstract access to various archives.
+It is intended for use in video games, and the design was somewhat inspired
+by Quake 3's file subsystem. The programmer defines a "write directory" on
+the physical filesystem. No file writing done through the PhysicsFS API can
+leave that write directory, for security. For example, an embedded scripting
+language cannot write outside of this path if it uses PhysFS for all of its
+I/O, which means that untrusted scripts can run more safely. Symbolic links
+can be disabled as well, for added safety. For file reading, the programmer
+lists directories and archives that form a "search path". Once the search
+path is defined, it becomes a single, transparent hierarchical filesystem.
+This makes for easy access to ZIP files in the same way as you access a file
+directly on the disk, and it makes it easy to ship a new archive that will
+override a previous archive on a per-file basis. Finally, PhysicsFS gives
+you platform-abstracted means to determine if CD-ROMs are available, the
+user's home directory, where in the real filesystem your program is running,
+etc.
+
+%package devel
+Summary: Development headers, libraries, and documentation for PhysicsFS
+Group: Development/Libraries
+Requires: %{name} = %{version}
+
+%description devel
+PhysicsFS is a library to provide abstract access to various archives.
+This package contains the development headers, libraries, and documentaion to
+build programs using PhysicsFS.
+
+%prep
+%setup
+export CFLAGS="${RPM_OPT_FLAGS}" CXXFLAGS="${RPM_OPT_FLAGS}";
+./configure --prefix=/usr
+
+%build
+export CFLAGS="${RPM_OPT_FLAGS}" CXXFLAGS="${RPM_OPT_FLAGS}";
+make
+# Make doxygen docs
+doxygen
+
+%install
+[ -d ${RPM_BUILD_ROOT} ] && rm -rf ${RPM_BUILD_ROOT}
+make DESTDIR=${RPM_BUILD_ROOT} install
+
+%clean
+[ -d ${RPM_BUILD_ROOT} ] && rm -rf ${RPM_BUILD_ROOT}
+
+%post -p /sbin/ldconfig
+%postun -p /sbin/ldconfig
+
+%files
+%defattr(-,root,root)
+%doc CHANGELOG.txt CREDITS.txt INSTALL.txt LICENSE.txt TODO.txt
+%{_bindir}/test_physfs
+%{_libdir}/*so.*
+
+%files devel
+%defattr(-,root,root)
+%doc docs/*
+%{_libdir}/*.so
+%{_includedir}/physfs.h
+
+%changelog
+* Sun Mar 11 2007 Ryan C. Gordon <icculus@icculus.org>
+- Updated filenames in documents.
+
+* Thu Dec 18 2002 Edward Rudd <eddie@omegaware.com>
+- added zlib_license_change.txt to documents
+
+* Wed Jul 10 2002 Edward Rudd <eddie@omegaware.com>
+- added doxygen to build requirements
+
+* Wed Jul 10 2002 Edward Rudd <eddie@omegaware.com>
+- updated to release 0.17
+
+* Tue May 15 2002 Edward Rudd <eddie@omegaware.com>
+- updated to latest CVS and modified spec file to use
+ the autoconf/automake support in the latest CVS
+
+* Tue Apr 30 2002 Edward Rudd <eddie@omegaware.com>
+- Initial spec file
+
--- /dev/null
+/**
+ * PhysicsFS; a portable, flexible file i/o abstraction.
+ *
+ * Documentation is in physfs.h. It's verbose, honest. :)
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+/* The macros used to swap values */
+/* Try to use superfast macros on systems that support them */
+#ifdef linux
+#include <asm/byteorder.h>
+#ifdef __arch__swab16
+#define PHYSFS_Swap16 __arch__swab16
+#endif
+#ifdef __arch__swab32
+#define PHYSFS_Swap32 __arch__swab32
+#endif
+#endif /* linux */
+
+#if (defined macintosh) && !(defined __MWERKS__)
+#define __inline__
+#endif
+
+#if (defined _MSC_VER)
+#define __inline__ __inline
+#endif
+
+#ifndef PHYSFS_Swap16
+static __inline__ PHYSFS_uint16 PHYSFS_Swap16(PHYSFS_uint16 D)
+{
+ return((D<<8)|(D>>8));
+}
+#endif
+#ifndef PHYSFS_Swap32
+static __inline__ PHYSFS_uint32 PHYSFS_Swap32(PHYSFS_uint32 D)
+{
+ return((D<<24)|((D<<8)&0x00FF0000)|((D>>8)&0x0000FF00)|(D>>24));
+}
+#endif
+#ifndef PHYSFS_NO_64BIT_SUPPORT
+#ifndef PHYSFS_Swap64
+static __inline__ PHYSFS_uint64 PHYSFS_Swap64(PHYSFS_uint64 val) {
+ PHYSFS_uint32 hi, lo;
+
+ /* Separate into high and low 32-bit values and swap them */
+ lo = (PHYSFS_uint32)(val&0xFFFFFFFF);
+ val >>= 32;
+ hi = (PHYSFS_uint32)(val&0xFFFFFFFF);
+ val = PHYSFS_Swap32(lo);
+ val <<= 32;
+ val |= PHYSFS_Swap32(hi);
+ return(val);
+}
+#endif
+#else
+#ifndef PHYSFS_Swap64
+/* This is mainly to keep compilers from complaining in PHYSFS code.
+ If there is no real 64-bit datatype, then compilers will complain about
+ the fake 64-bit datatype that PHYSFS provides when it compiles user code.
+*/
+#define PHYSFS_Swap64(X) (X)
+#endif
+#endif /* PHYSFS_NO_64BIT_SUPPORT */
+
+
+/* Byteswap item from the specified endianness to the native endianness */
+#if PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN
+PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return(x); }
+PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return(x); }
+PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return(x); }
+PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return(x); }
+PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return(x); }
+PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return(x); }
+
+PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return(PHYSFS_Swap16(x)); }
+PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return(PHYSFS_Swap16(x)); }
+PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return(PHYSFS_Swap32(x)); }
+PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return(PHYSFS_Swap32(x)); }
+PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return(PHYSFS_Swap64(x)); }
+PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return(PHYSFS_Swap64(x)); }
+#else
+PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return(PHYSFS_Swap16(x)); }
+PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return(PHYSFS_Swap16(x)); }
+PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return(PHYSFS_Swap32(x)); }
+PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return(PHYSFS_Swap32(x)); }
+PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return(PHYSFS_Swap64(x)); }
+PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return(PHYSFS_Swap64(x)); }
+
+PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return(x); }
+PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return(x); }
+PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return(x); }
+PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return(x); }
+PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return(x); }
+PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return(x); }
+#endif
+
+
+int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val)
+{
+ PHYSFS_sint16 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapSLE16(in);
+ return(1);
+} /* PHYSFS_readSLE16 */
+
+
+int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val)
+{
+ PHYSFS_uint16 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapULE16(in);
+ return(1);
+} /* PHYSFS_readULE16 */
+
+
+int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val)
+{
+ PHYSFS_sint16 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapSBE16(in);
+ return(1);
+} /* PHYSFS_readSBE16 */
+
+
+int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val)
+{
+ PHYSFS_uint16 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapUBE16(in);
+ return(1);
+} /* PHYSFS_readUBE16 */
+
+
+int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val)
+{
+ PHYSFS_sint32 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapSLE32(in);
+ return(1);
+} /* PHYSFS_readSLE32 */
+
+
+int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val)
+{
+ PHYSFS_uint32 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapULE32(in);
+ return(1);
+} /* PHYSFS_readULE32 */
+
+
+int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val)
+{
+ PHYSFS_sint32 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapSBE32(in);
+ return(1);
+} /* PHYSFS_readSBE32 */
+
+
+int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val)
+{
+ PHYSFS_uint32 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapUBE32(in);
+ return(1);
+} /* PHYSFS_readUBE32 */
+
+
+int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val)
+{
+ PHYSFS_sint64 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapSLE64(in);
+ return(1);
+} /* PHYSFS_readSLE64 */
+
+
+int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val)
+{
+ PHYSFS_uint64 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapULE64(in);
+ return(1);
+} /* PHYSFS_readULE64 */
+
+
+int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val)
+{
+ PHYSFS_sint64 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapSBE64(in);
+ return(1);
+} /* PHYSFS_readSBE64 */
+
+
+int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val)
+{
+ PHYSFS_uint64 in;
+ BAIL_IF_MACRO(val == NULL, ERR_INVALID_ARGUMENT, 0);
+ BAIL_IF_MACRO(PHYSFS_read(file, &in, sizeof (in), 1) != 1, NULL, 0);
+ *val = PHYSFS_swapUBE64(in);
+ return(1);
+} /* PHYSFS_readUBE64 */
+
+
+
+int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val)
+{
+ PHYSFS_sint16 out = PHYSFS_swapSLE16(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeSLE16 */
+
+
+int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val)
+{
+ PHYSFS_uint16 out = PHYSFS_swapULE16(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeULE16 */
+
+
+int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val)
+{
+ PHYSFS_sint16 out = PHYSFS_swapSBE16(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeSBE16 */
+
+
+int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val)
+{
+ PHYSFS_uint16 out = PHYSFS_swapUBE16(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeUBE16 */
+
+
+int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val)
+{
+ PHYSFS_sint32 out = PHYSFS_swapSLE32(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeSLE32 */
+
+
+int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val)
+{
+ PHYSFS_uint32 out = PHYSFS_swapULE32(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeULE32 */
+
+
+int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val)
+{
+ PHYSFS_sint32 out = PHYSFS_swapSBE32(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeSBE32 */
+
+
+int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val)
+{
+ PHYSFS_uint32 out = PHYSFS_swapUBE32(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeUBE32 */
+
+
+int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val)
+{
+ PHYSFS_sint64 out = PHYSFS_swapSLE64(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeSLE64 */
+
+
+int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val)
+{
+ PHYSFS_uint64 out = PHYSFS_swapULE64(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeULE64 */
+
+
+int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val)
+{
+ PHYSFS_sint64 out = PHYSFS_swapSBE64(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeSBE64 */
+
+
+int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val)
+{
+ PHYSFS_uint64 out = PHYSFS_swapUBE64(val);
+ BAIL_IF_MACRO(PHYSFS_write(file, &out, sizeof (out), 1) != 1, NULL, 0);
+ return(1);
+} /* PHYSFS_writeUBE64 */
+
+/* end of physfs_byteorder.c ... */
+
--- /dev/null
+/*
+ * This file is part of PhysicsFS (http://icculus.org/physfs/)
+ *
+ * This data generated by physfs/extras/makecasefoldhashtable.pl ...
+ * Do not manually edit this file!
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ */
+
+#ifndef __PHYSICSFS_INTERNAL__
+#error Do not include this header from your applications.
+#endif
+
+static const CaseFoldMapping case_fold_000[] = {
+ { 0x0202, 0x0203, 0x0000, 0x0000 },
+ { 0x0404, 0x0454, 0x0000, 0x0000 },
+ { 0x1E1E, 0x1E1F, 0x0000, 0x0000 },
+ { 0x2C2C, 0x2C5C, 0x0000, 0x0000 },
+ { 0x10404, 0x1042C, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_001[] = {
+ { 0x0100, 0x0101, 0x0000, 0x0000 },
+ { 0x0405, 0x0455, 0x0000, 0x0000 },
+ { 0x0504, 0x0505, 0x0000, 0x0000 },
+ { 0x2C2D, 0x2C5D, 0x0000, 0x0000 },
+ { 0x10405, 0x1042D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_002[] = {
+ { 0x0200, 0x0201, 0x0000, 0x0000 },
+ { 0x0406, 0x0456, 0x0000, 0x0000 },
+ { 0x1E1C, 0x1E1D, 0x0000, 0x0000 },
+ { 0x1F1D, 0x1F15, 0x0000, 0x0000 },
+ { 0x2C2E, 0x2C5E, 0x0000, 0x0000 },
+ { 0x10406, 0x1042E, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_003[] = {
+ { 0x0102, 0x0103, 0x0000, 0x0000 },
+ { 0x0407, 0x0457, 0x0000, 0x0000 },
+ { 0x0506, 0x0507, 0x0000, 0x0000 },
+ { 0x1F1C, 0x1F14, 0x0000, 0x0000 },
+ { 0x10407, 0x1042F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_004[] = {
+ { 0x0206, 0x0207, 0x0000, 0x0000 },
+ { 0x0400, 0x0450, 0x0000, 0x0000 },
+ { 0x1E1A, 0x1E1B, 0x0000, 0x0000 },
+ { 0x1F1B, 0x1F13, 0x0000, 0x0000 },
+ { 0x2C28, 0x2C58, 0x0000, 0x0000 },
+ { 0x10400, 0x10428, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_005[] = {
+ { 0x0104, 0x0105, 0x0000, 0x0000 },
+ { 0x0401, 0x0451, 0x0000, 0x0000 },
+ { 0x0500, 0x0501, 0x0000, 0x0000 },
+ { 0x1F1A, 0x1F12, 0x0000, 0x0000 },
+ { 0x2C29, 0x2C59, 0x0000, 0x0000 },
+ { 0x10401, 0x10429, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_006[] = {
+ { 0x0204, 0x0205, 0x0000, 0x0000 },
+ { 0x0402, 0x0452, 0x0000, 0x0000 },
+ { 0x1E18, 0x1E19, 0x0000, 0x0000 },
+ { 0x1F19, 0x1F11, 0x0000, 0x0000 },
+ { 0x2C2A, 0x2C5A, 0x0000, 0x0000 },
+ { 0x10402, 0x1042A, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_007[] = {
+ { 0x0106, 0x0107, 0x0000, 0x0000 },
+ { 0x0403, 0x0453, 0x0000, 0x0000 },
+ { 0x0502, 0x0503, 0x0000, 0x0000 },
+ { 0x1F18, 0x1F10, 0x0000, 0x0000 },
+ { 0x2126, 0x03C9, 0x0000, 0x0000 },
+ { 0x2C2B, 0x2C5B, 0x0000, 0x0000 },
+ { 0x10403, 0x1042B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_008[] = {
+ { 0x020A, 0x020B, 0x0000, 0x0000 },
+ { 0x040C, 0x045C, 0x0000, 0x0000 },
+ { 0x1E16, 0x1E17, 0x0000, 0x0000 },
+ { 0x2C24, 0x2C54, 0x0000, 0x0000 },
+ { 0x1040C, 0x10434, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_009[] = {
+ { 0x0108, 0x0109, 0x0000, 0x0000 },
+ { 0x040D, 0x045D, 0x0000, 0x0000 },
+ { 0x050C, 0x050D, 0x0000, 0x0000 },
+ { 0x2C25, 0x2C55, 0x0000, 0x0000 },
+ { 0x1040D, 0x10435, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_010[] = {
+ { 0x0208, 0x0209, 0x0000, 0x0000 },
+ { 0x040E, 0x045E, 0x0000, 0x0000 },
+ { 0x1E14, 0x1E15, 0x0000, 0x0000 },
+ { 0x212B, 0x00E5, 0x0000, 0x0000 },
+ { 0x2C26, 0x2C56, 0x0000, 0x0000 },
+ { 0x1040E, 0x10436, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_011[] = {
+ { 0x010A, 0x010B, 0x0000, 0x0000 },
+ { 0x040F, 0x045F, 0x0000, 0x0000 },
+ { 0x050E, 0x050F, 0x0000, 0x0000 },
+ { 0x212A, 0x006B, 0x0000, 0x0000 },
+ { 0x2C27, 0x2C57, 0x0000, 0x0000 },
+ { 0x1040F, 0x10437, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_012[] = {
+ { 0x020E, 0x020F, 0x0000, 0x0000 },
+ { 0x0408, 0x0458, 0x0000, 0x0000 },
+ { 0x1E12, 0x1E13, 0x0000, 0x0000 },
+ { 0x2C20, 0x2C50, 0x0000, 0x0000 },
+ { 0x10408, 0x10430, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_013[] = {
+ { 0x010C, 0x010D, 0x0000, 0x0000 },
+ { 0x0409, 0x0459, 0x0000, 0x0000 },
+ { 0x0508, 0x0509, 0x0000, 0x0000 },
+ { 0x2C21, 0x2C51, 0x0000, 0x0000 },
+ { 0x10409, 0x10431, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_014[] = {
+ { 0x020C, 0x020D, 0x0000, 0x0000 },
+ { 0x040A, 0x045A, 0x0000, 0x0000 },
+ { 0x1E10, 0x1E11, 0x0000, 0x0000 },
+ { 0x2C22, 0x2C52, 0x0000, 0x0000 },
+ { 0x1040A, 0x10432, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_015[] = {
+ { 0x010E, 0x010F, 0x0000, 0x0000 },
+ { 0x040B, 0x045B, 0x0000, 0x0000 },
+ { 0x050A, 0x050B, 0x0000, 0x0000 },
+ { 0x2C23, 0x2C53, 0x0000, 0x0000 },
+ { 0x1040B, 0x10433, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_016[] = {
+ { 0x0212, 0x0213, 0x0000, 0x0000 },
+ { 0x0414, 0x0434, 0x0000, 0x0000 },
+ { 0x1E0E, 0x1E0F, 0x0000, 0x0000 },
+ { 0x1F0F, 0x1F07, 0x0000, 0x0000 },
+ { 0x10414, 0x1043C, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_017[] = {
+ { 0x0110, 0x0111, 0x0000, 0x0000 },
+ { 0x0415, 0x0435, 0x0000, 0x0000 },
+ { 0x1F0E, 0x1F06, 0x0000, 0x0000 },
+ { 0x10415, 0x1043D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_018[] = {
+ { 0x0210, 0x0211, 0x0000, 0x0000 },
+ { 0x0416, 0x0436, 0x0000, 0x0000 },
+ { 0x1E0C, 0x1E0D, 0x0000, 0x0000 },
+ { 0x1F0D, 0x1F05, 0x0000, 0x0000 },
+ { 0x10416, 0x1043E, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_019[] = {
+ { 0x0112, 0x0113, 0x0000, 0x0000 },
+ { 0x0417, 0x0437, 0x0000, 0x0000 },
+ { 0x1F0C, 0x1F04, 0x0000, 0x0000 },
+ { 0x10417, 0x1043F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_020[] = {
+ { 0x0216, 0x0217, 0x0000, 0x0000 },
+ { 0x0410, 0x0430, 0x0000, 0x0000 },
+ { 0x1E0A, 0x1E0B, 0x0000, 0x0000 },
+ { 0x1F0B, 0x1F03, 0x0000, 0x0000 },
+ { 0x10410, 0x10438, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_021[] = {
+ { 0x0114, 0x0115, 0x0000, 0x0000 },
+ { 0x0411, 0x0431, 0x0000, 0x0000 },
+ { 0x1F0A, 0x1F02, 0x0000, 0x0000 },
+ { 0x10411, 0x10439, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_022[] = {
+ { 0x0214, 0x0215, 0x0000, 0x0000 },
+ { 0x0412, 0x0432, 0x0000, 0x0000 },
+ { 0x1E08, 0x1E09, 0x0000, 0x0000 },
+ { 0x1F09, 0x1F01, 0x0000, 0x0000 },
+ { 0x10412, 0x1043A, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_023[] = {
+ { 0x0116, 0x0117, 0x0000, 0x0000 },
+ { 0x0413, 0x0433, 0x0000, 0x0000 },
+ { 0x1F08, 0x1F00, 0x0000, 0x0000 },
+ { 0x10413, 0x1043B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_024[] = {
+ { 0x021A, 0x021B, 0x0000, 0x0000 },
+ { 0x041C, 0x043C, 0x0000, 0x0000 },
+ { 0x1E06, 0x1E07, 0x0000, 0x0000 },
+ { 0x1041C, 0x10444, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_025[] = {
+ { 0x0118, 0x0119, 0x0000, 0x0000 },
+ { 0x041D, 0x043D, 0x0000, 0x0000 },
+ { 0x1041D, 0x10445, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_026[] = {
+ { 0x0218, 0x0219, 0x0000, 0x0000 },
+ { 0x041E, 0x043E, 0x0000, 0x0000 },
+ { 0x1E04, 0x1E05, 0x0000, 0x0000 },
+ { 0x1041E, 0x10446, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_027[] = {
+ { 0x011A, 0x011B, 0x0000, 0x0000 },
+ { 0x041F, 0x043F, 0x0000, 0x0000 },
+ { 0x1041F, 0x10447, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_028[] = {
+ { 0x021E, 0x021F, 0x0000, 0x0000 },
+ { 0x0418, 0x0438, 0x0000, 0x0000 },
+ { 0x1E02, 0x1E03, 0x0000, 0x0000 },
+ { 0x10418, 0x10440, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_029[] = {
+ { 0x011C, 0x011D, 0x0000, 0x0000 },
+ { 0x0419, 0x0439, 0x0000, 0x0000 },
+ { 0x10419, 0x10441, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_030[] = {
+ { 0x021C, 0x021D, 0x0000, 0x0000 },
+ { 0x041A, 0x043A, 0x0000, 0x0000 },
+ { 0x1E00, 0x1E01, 0x0000, 0x0000 },
+ { 0x1041A, 0x10442, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_031[] = {
+ { 0x011E, 0x011F, 0x0000, 0x0000 },
+ { 0x041B, 0x043B, 0x0000, 0x0000 },
+ { 0x1041B, 0x10443, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_032[] = {
+ { 0x0222, 0x0223, 0x0000, 0x0000 },
+ { 0x0424, 0x0444, 0x0000, 0x0000 },
+ { 0x1E3E, 0x1E3F, 0x0000, 0x0000 },
+ { 0x1F3F, 0x1F37, 0x0000, 0x0000 },
+ { 0x2C0C, 0x2C3C, 0x0000, 0x0000 },
+ { 0x10424, 0x1044C, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_033[] = {
+ { 0x0120, 0x0121, 0x0000, 0x0000 },
+ { 0x0425, 0x0445, 0x0000, 0x0000 },
+ { 0x1F3E, 0x1F36, 0x0000, 0x0000 },
+ { 0x2C0D, 0x2C3D, 0x0000, 0x0000 },
+ { 0x10425, 0x1044D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_034[] = {
+ { 0x0220, 0x019E, 0x0000, 0x0000 },
+ { 0x0426, 0x0446, 0x0000, 0x0000 },
+ { 0x1E3C, 0x1E3D, 0x0000, 0x0000 },
+ { 0x1F3D, 0x1F35, 0x0000, 0x0000 },
+ { 0x2C0E, 0x2C3E, 0x0000, 0x0000 },
+ { 0x10426, 0x1044E, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_035[] = {
+ { 0x0122, 0x0123, 0x0000, 0x0000 },
+ { 0x0427, 0x0447, 0x0000, 0x0000 },
+ { 0x1F3C, 0x1F34, 0x0000, 0x0000 },
+ { 0x2C0F, 0x2C3F, 0x0000, 0x0000 },
+ { 0x10427, 0x1044F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_036[] = {
+ { 0x0226, 0x0227, 0x0000, 0x0000 },
+ { 0x0420, 0x0440, 0x0000, 0x0000 },
+ { 0x1E3A, 0x1E3B, 0x0000, 0x0000 },
+ { 0x1F3B, 0x1F33, 0x0000, 0x0000 },
+ { 0x2C08, 0x2C38, 0x0000, 0x0000 },
+ { 0x10420, 0x10448, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_037[] = {
+ { 0x0124, 0x0125, 0x0000, 0x0000 },
+ { 0x0421, 0x0441, 0x0000, 0x0000 },
+ { 0x1F3A, 0x1F32, 0x0000, 0x0000 },
+ { 0x2C09, 0x2C39, 0x0000, 0x0000 },
+ { 0x10421, 0x10449, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_038[] = {
+ { 0x0224, 0x0225, 0x0000, 0x0000 },
+ { 0x0422, 0x0442, 0x0000, 0x0000 },
+ { 0x1E38, 0x1E39, 0x0000, 0x0000 },
+ { 0x1F39, 0x1F31, 0x0000, 0x0000 },
+ { 0x2C0A, 0x2C3A, 0x0000, 0x0000 },
+ { 0x10422, 0x1044A, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_039[] = {
+ { 0x0126, 0x0127, 0x0000, 0x0000 },
+ { 0x0423, 0x0443, 0x0000, 0x0000 },
+ { 0x1F38, 0x1F30, 0x0000, 0x0000 },
+ { 0x2C0B, 0x2C3B, 0x0000, 0x0000 },
+ { 0x10423, 0x1044B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_040[] = {
+ { 0x022A, 0x022B, 0x0000, 0x0000 },
+ { 0x042C, 0x044C, 0x0000, 0x0000 },
+ { 0x1E36, 0x1E37, 0x0000, 0x0000 },
+ { 0x2C04, 0x2C34, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_041[] = {
+ { 0x0128, 0x0129, 0x0000, 0x0000 },
+ { 0x042D, 0x044D, 0x0000, 0x0000 },
+ { 0x2C05, 0x2C35, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_042[] = {
+ { 0x0228, 0x0229, 0x0000, 0x0000 },
+ { 0x042E, 0x044E, 0x0000, 0x0000 },
+ { 0x1E34, 0x1E35, 0x0000, 0x0000 },
+ { 0x2C06, 0x2C36, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_043[] = {
+ { 0x012A, 0x012B, 0x0000, 0x0000 },
+ { 0x042F, 0x044F, 0x0000, 0x0000 },
+ { 0x2C07, 0x2C37, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_044[] = {
+ { 0x022E, 0x022F, 0x0000, 0x0000 },
+ { 0x0428, 0x0448, 0x0000, 0x0000 },
+ { 0x1E32, 0x1E33, 0x0000, 0x0000 },
+ { 0x2C00, 0x2C30, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_045[] = {
+ { 0x012C, 0x012D, 0x0000, 0x0000 },
+ { 0x0429, 0x0449, 0x0000, 0x0000 },
+ { 0x2C01, 0x2C31, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_046[] = {
+ { 0x022C, 0x022D, 0x0000, 0x0000 },
+ { 0x042A, 0x044A, 0x0000, 0x0000 },
+ { 0x1E30, 0x1E31, 0x0000, 0x0000 },
+ { 0x2C02, 0x2C32, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_047[] = {
+ { 0x012E, 0x012F, 0x0000, 0x0000 },
+ { 0x042B, 0x044B, 0x0000, 0x0000 },
+ { 0x2C03, 0x2C33, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_048[] = {
+ { 0x0232, 0x0233, 0x0000, 0x0000 },
+ { 0x0535, 0x0565, 0x0000, 0x0000 },
+ { 0x1E2E, 0x1E2F, 0x0000, 0x0000 },
+ { 0x1F2F, 0x1F27, 0x0000, 0x0000 },
+ { 0x2C1C, 0x2C4C, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_049[] = {
+ { 0x0130, 0x0069, 0x0307, 0x0000 },
+ { 0x0534, 0x0564, 0x0000, 0x0000 },
+ { 0x1F2E, 0x1F26, 0x0000, 0x0000 },
+ { 0x2C1D, 0x2C4D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_050[] = {
+ { 0x0230, 0x0231, 0x0000, 0x0000 },
+ { 0x0537, 0x0567, 0x0000, 0x0000 },
+ { 0x1E2C, 0x1E2D, 0x0000, 0x0000 },
+ { 0x1F2D, 0x1F25, 0x0000, 0x0000 },
+ { 0x2C1E, 0x2C4E, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_051[] = {
+ { 0x0132, 0x0133, 0x0000, 0x0000 },
+ { 0x0536, 0x0566, 0x0000, 0x0000 },
+ { 0x1F2C, 0x1F24, 0x0000, 0x0000 },
+ { 0x2C1F, 0x2C4F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_052[] = {
+ { 0x0531, 0x0561, 0x0000, 0x0000 },
+ { 0x1E2A, 0x1E2B, 0x0000, 0x0000 },
+ { 0x1F2B, 0x1F23, 0x0000, 0x0000 },
+ { 0x2C18, 0x2C48, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_053[] = {
+ { 0x0134, 0x0135, 0x0000, 0x0000 },
+ { 0x1F2A, 0x1F22, 0x0000, 0x0000 },
+ { 0x2C19, 0x2C49, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_054[] = {
+ { 0x0533, 0x0563, 0x0000, 0x0000 },
+ { 0x1E28, 0x1E29, 0x0000, 0x0000 },
+ { 0x1F29, 0x1F21, 0x0000, 0x0000 },
+ { 0x2C1A, 0x2C4A, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_055[] = {
+ { 0x0136, 0x0137, 0x0000, 0x0000 },
+ { 0x0532, 0x0562, 0x0000, 0x0000 },
+ { 0x1F28, 0x1F20, 0x0000, 0x0000 },
+ { 0x2C1B, 0x2C4B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_056[] = {
+ { 0x0139, 0x013A, 0x0000, 0x0000 },
+ { 0x053D, 0x056D, 0x0000, 0x0000 },
+ { 0x1E26, 0x1E27, 0x0000, 0x0000 },
+ { 0x2C14, 0x2C44, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_057[] = {
+ { 0x023B, 0x023C, 0x0000, 0x0000 },
+ { 0x053C, 0x056C, 0x0000, 0x0000 },
+ { 0x2C15, 0x2C45, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_058[] = {
+ { 0x013B, 0x013C, 0x0000, 0x0000 },
+ { 0x053F, 0x056F, 0x0000, 0x0000 },
+ { 0x1E24, 0x1E25, 0x0000, 0x0000 },
+ { 0x2C16, 0x2C46, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_059[] = {
+ { 0x053E, 0x056E, 0x0000, 0x0000 },
+ { 0x2C17, 0x2C47, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_060[] = {
+ { 0x013D, 0x013E, 0x0000, 0x0000 },
+ { 0x0539, 0x0569, 0x0000, 0x0000 },
+ { 0x1E22, 0x1E23, 0x0000, 0x0000 },
+ { 0x2C10, 0x2C40, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_061[] = {
+ { 0x0538, 0x0568, 0x0000, 0x0000 },
+ { 0x2C11, 0x2C41, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_062[] = {
+ { 0x013F, 0x0140, 0x0000, 0x0000 },
+ { 0x053B, 0x056B, 0x0000, 0x0000 },
+ { 0x1E20, 0x1E21, 0x0000, 0x0000 },
+ { 0x2C12, 0x2C42, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_063[] = {
+ { 0x023D, 0x019A, 0x0000, 0x0000 },
+ { 0x053A, 0x056A, 0x0000, 0x0000 },
+ { 0x2C13, 0x2C43, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_064[] = {
+ { 0x0141, 0x0142, 0x0000, 0x0000 },
+ { 0x0545, 0x0575, 0x0000, 0x0000 },
+ { 0x1E5E, 0x1E5F, 0x0000, 0x0000 },
+ { 0x1F5F, 0x1F57, 0x0000, 0x0000 },
+ { 0x2161, 0x2171, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_065[] = {
+ { 0x0041, 0x0061, 0x0000, 0x0000 },
+ { 0x0544, 0x0574, 0x0000, 0x0000 },
+ { 0x2160, 0x2170, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_066[] = {
+ { 0x0042, 0x0062, 0x0000, 0x0000 },
+ { 0x0143, 0x0144, 0x0000, 0x0000 },
+ { 0x0547, 0x0577, 0x0000, 0x0000 },
+ { 0x1E5C, 0x1E5D, 0x0000, 0x0000 },
+ { 0x1F5D, 0x1F55, 0x0000, 0x0000 },
+ { 0x2163, 0x2173, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_067[] = {
+ { 0x0043, 0x0063, 0x0000, 0x0000 },
+ { 0x0241, 0x0294, 0x0000, 0x0000 },
+ { 0x0546, 0x0576, 0x0000, 0x0000 },
+ { 0x2162, 0x2172, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_068[] = {
+ { 0x0044, 0x0064, 0x0000, 0x0000 },
+ { 0x0145, 0x0146, 0x0000, 0x0000 },
+ { 0x0541, 0x0571, 0x0000, 0x0000 },
+ { 0x1E5A, 0x1E5B, 0x0000, 0x0000 },
+ { 0x1F5B, 0x1F53, 0x0000, 0x0000 },
+ { 0x2165, 0x2175, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_069[] = {
+ { 0x0045, 0x0065, 0x0000, 0x0000 },
+ { 0x0540, 0x0570, 0x0000, 0x0000 },
+ { 0x2164, 0x2174, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_070[] = {
+ { 0x0046, 0x0066, 0x0000, 0x0000 },
+ { 0x0147, 0x0148, 0x0000, 0x0000 },
+ { 0x0345, 0x03B9, 0x0000, 0x0000 },
+ { 0x0543, 0x0573, 0x0000, 0x0000 },
+ { 0x1E58, 0x1E59, 0x0000, 0x0000 },
+ { 0x1F59, 0x1F51, 0x0000, 0x0000 },
+ { 0x2167, 0x2177, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_071[] = {
+ { 0x0047, 0x0067, 0x0000, 0x0000 },
+ { 0x0542, 0x0572, 0x0000, 0x0000 },
+ { 0x2166, 0x2176, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_072[] = {
+ { 0x0048, 0x0068, 0x0000, 0x0000 },
+ { 0x0149, 0x02BC, 0x006E, 0x0000 },
+ { 0x054D, 0x057D, 0x0000, 0x0000 },
+ { 0x1E56, 0x1E57, 0x0000, 0x0000 },
+ { 0x2169, 0x2179, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_073[] = {
+ { 0x0049, 0x0069, 0x0000, 0x0000 },
+ { 0x054C, 0x057C, 0x0000, 0x0000 },
+ { 0x1F56, 0x03C5, 0x0313, 0x0342 },
+ { 0x2168, 0x2178, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_074[] = {
+ { 0x004A, 0x006A, 0x0000, 0x0000 },
+ { 0x054F, 0x057F, 0x0000, 0x0000 },
+ { 0x1E54, 0x1E55, 0x0000, 0x0000 },
+ { 0x216B, 0x217B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_075[] = {
+ { 0x004B, 0x006B, 0x0000, 0x0000 },
+ { 0x014A, 0x014B, 0x0000, 0x0000 },
+ { 0x054E, 0x057E, 0x0000, 0x0000 },
+ { 0x1F54, 0x03C5, 0x0313, 0x0301 },
+ { 0x216A, 0x217A, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_076[] = {
+ { 0x004C, 0x006C, 0x0000, 0x0000 },
+ { 0x0549, 0x0579, 0x0000, 0x0000 },
+ { 0x1E52, 0x1E53, 0x0000, 0x0000 },
+ { 0x216D, 0x217D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_077[] = {
+ { 0x004D, 0x006D, 0x0000, 0x0000 },
+ { 0x014C, 0x014D, 0x0000, 0x0000 },
+ { 0x0548, 0x0578, 0x0000, 0x0000 },
+ { 0x1F52, 0x03C5, 0x0313, 0x0300 },
+ { 0x216C, 0x217C, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_078[] = {
+ { 0x004E, 0x006E, 0x0000, 0x0000 },
+ { 0x054B, 0x057B, 0x0000, 0x0000 },
+ { 0x1E50, 0x1E51, 0x0000, 0x0000 },
+ { 0x216F, 0x217F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_079[] = {
+ { 0x004F, 0x006F, 0x0000, 0x0000 },
+ { 0x014E, 0x014F, 0x0000, 0x0000 },
+ { 0x054A, 0x057A, 0x0000, 0x0000 },
+ { 0x1F50, 0x03C5, 0x0313, 0x0000 },
+ { 0x216E, 0x217E, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_080[] = {
+ { 0x0050, 0x0070, 0x0000, 0x0000 },
+ { 0x0555, 0x0585, 0x0000, 0x0000 },
+ { 0x1E4E, 0x1E4F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_081[] = {
+ { 0x0051, 0x0071, 0x0000, 0x0000 },
+ { 0x0150, 0x0151, 0x0000, 0x0000 },
+ { 0x0554, 0x0584, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_082[] = {
+ { 0x0052, 0x0072, 0x0000, 0x0000 },
+ { 0x1E4C, 0x1E4D, 0x0000, 0x0000 },
+ { 0x1F4D, 0x1F45, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_083[] = {
+ { 0x0053, 0x0073, 0x0000, 0x0000 },
+ { 0x0152, 0x0153, 0x0000, 0x0000 },
+ { 0x0556, 0x0586, 0x0000, 0x0000 },
+ { 0x1F4C, 0x1F44, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_084[] = {
+ { 0x0054, 0x0074, 0x0000, 0x0000 },
+ { 0x0551, 0x0581, 0x0000, 0x0000 },
+ { 0x1E4A, 0x1E4B, 0x0000, 0x0000 },
+ { 0x1F4B, 0x1F43, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_085[] = {
+ { 0x0055, 0x0075, 0x0000, 0x0000 },
+ { 0x0154, 0x0155, 0x0000, 0x0000 },
+ { 0x0550, 0x0580, 0x0000, 0x0000 },
+ { 0x1F4A, 0x1F42, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_086[] = {
+ { 0x0056, 0x0076, 0x0000, 0x0000 },
+ { 0x0553, 0x0583, 0x0000, 0x0000 },
+ { 0x1E48, 0x1E49, 0x0000, 0x0000 },
+ { 0x1F49, 0x1F41, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_087[] = {
+ { 0x0057, 0x0077, 0x0000, 0x0000 },
+ { 0x0156, 0x0157, 0x0000, 0x0000 },
+ { 0x0552, 0x0582, 0x0000, 0x0000 },
+ { 0x1F48, 0x1F40, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_088[] = {
+ { 0x0058, 0x0078, 0x0000, 0x0000 },
+ { 0x1E46, 0x1E47, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_089[] = {
+ { 0x0059, 0x0079, 0x0000, 0x0000 },
+ { 0x0158, 0x0159, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_090[] = {
+ { 0x005A, 0x007A, 0x0000, 0x0000 },
+ { 0x1E44, 0x1E45, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_091[] = {
+ { 0x015A, 0x015B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_092[] = {
+ { 0x1E42, 0x1E43, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_093[] = {
+ { 0x015C, 0x015D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_094[] = {
+ { 0x1E40, 0x1E41, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_095[] = {
+ { 0x015E, 0x015F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_096[] = {
+ { 0x0464, 0x0465, 0x0000, 0x0000 },
+ { 0x1E7E, 0x1E7F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_097[] = {
+ { 0x0160, 0x0161, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_098[] = {
+ { 0x0466, 0x0467, 0x0000, 0x0000 },
+ { 0x1E7C, 0x1E7D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_099[] = {
+ { 0x0162, 0x0163, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_100[] = {
+ { 0x0460, 0x0461, 0x0000, 0x0000 },
+ { 0x1E7A, 0x1E7B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_101[] = {
+ { 0x0164, 0x0165, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_102[] = {
+ { 0x0462, 0x0463, 0x0000, 0x0000 },
+ { 0x1E78, 0x1E79, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_103[] = {
+ { 0x0166, 0x0167, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_104[] = {
+ { 0x046C, 0x046D, 0x0000, 0x0000 },
+ { 0x1E76, 0x1E77, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_105[] = {
+ { 0x0168, 0x0169, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_106[] = {
+ { 0x046E, 0x046F, 0x0000, 0x0000 },
+ { 0x1E74, 0x1E75, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_107[] = {
+ { 0x016A, 0x016B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_108[] = {
+ { 0x0468, 0x0469, 0x0000, 0x0000 },
+ { 0x1E72, 0x1E73, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_109[] = {
+ { 0x016C, 0x016D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_110[] = {
+ { 0x046A, 0x046B, 0x0000, 0x0000 },
+ { 0x1E70, 0x1E71, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_111[] = {
+ { 0x016E, 0x016F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_112[] = {
+ { 0x0474, 0x0475, 0x0000, 0x0000 },
+ { 0x1E6E, 0x1E6F, 0x0000, 0x0000 },
+ { 0x1F6F, 0x1F67, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_113[] = {
+ { 0x0170, 0x0171, 0x0000, 0x0000 },
+ { 0x1F6E, 0x1F66, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_114[] = {
+ { 0x0476, 0x0477, 0x0000, 0x0000 },
+ { 0x1E6C, 0x1E6D, 0x0000, 0x0000 },
+ { 0x1F6D, 0x1F65, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_115[] = {
+ { 0x0172, 0x0173, 0x0000, 0x0000 },
+ { 0x1F6C, 0x1F64, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_116[] = {
+ { 0x0470, 0x0471, 0x0000, 0x0000 },
+ { 0x1E6A, 0x1E6B, 0x0000, 0x0000 },
+ { 0x1F6B, 0x1F63, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_117[] = {
+ { 0x0174, 0x0175, 0x0000, 0x0000 },
+ { 0x1F6A, 0x1F62, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_118[] = {
+ { 0x0472, 0x0473, 0x0000, 0x0000 },
+ { 0x1E68, 0x1E69, 0x0000, 0x0000 },
+ { 0x1F69, 0x1F61, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_119[] = {
+ { 0x0176, 0x0177, 0x0000, 0x0000 },
+ { 0x1F68, 0x1F60, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_120[] = {
+ { 0x0179, 0x017A, 0x0000, 0x0000 },
+ { 0x047C, 0x047D, 0x0000, 0x0000 },
+ { 0x1E66, 0x1E67, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_121[] = {
+ { 0x0178, 0x00FF, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_122[] = {
+ { 0x017B, 0x017C, 0x0000, 0x0000 },
+ { 0x047E, 0x047F, 0x0000, 0x0000 },
+ { 0x1E64, 0x1E65, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_124[] = {
+ { 0x017D, 0x017E, 0x0000, 0x0000 },
+ { 0x0478, 0x0479, 0x0000, 0x0000 },
+ { 0x1E62, 0x1E63, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_126[] = {
+ { 0x017F, 0x0073, 0x0000, 0x0000 },
+ { 0x047A, 0x047B, 0x0000, 0x0000 },
+ { 0x1E60, 0x1E61, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_128[] = {
+ { 0x0181, 0x0253, 0x0000, 0x0000 },
+ { 0x1F9F, 0x1F27, 0x03B9, 0x0000 },
+ { 0x2CAC, 0x2CAD, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_129[] = {
+ { 0x1F9E, 0x1F26, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_130[] = {
+ { 0x0587, 0x0565, 0x0582, 0x0000 },
+ { 0x1F9D, 0x1F25, 0x03B9, 0x0000 },
+ { 0x2CAE, 0x2CAF, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_131[] = {
+ { 0x0182, 0x0183, 0x0000, 0x0000 },
+ { 0x1F9C, 0x1F24, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_132[] = {
+ { 0x0480, 0x0481, 0x0000, 0x0000 },
+ { 0x1E9A, 0x0061, 0x02BE, 0x0000 },
+ { 0x1F9B, 0x1F23, 0x03B9, 0x0000 },
+ { 0x2CA8, 0x2CA9, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_133[] = {
+ { 0x0184, 0x0185, 0x0000, 0x0000 },
+ { 0x0386, 0x03AC, 0x0000, 0x0000 },
+ { 0x1E9B, 0x1E61, 0x0000, 0x0000 },
+ { 0x1F9A, 0x1F22, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_134[] = {
+ { 0x0187, 0x0188, 0x0000, 0x0000 },
+ { 0x1E98, 0x0077, 0x030A, 0x0000 },
+ { 0x1F99, 0x1F21, 0x03B9, 0x0000 },
+ { 0x2CAA, 0x2CAB, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_135[] = {
+ { 0x0186, 0x0254, 0x0000, 0x0000 },
+ { 0x1E99, 0x0079, 0x030A, 0x0000 },
+ { 0x1F98, 0x1F20, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_136[] = {
+ { 0x0189, 0x0256, 0x0000, 0x0000 },
+ { 0x048C, 0x048D, 0x0000, 0x0000 },
+ { 0x1E96, 0x0068, 0x0331, 0x0000 },
+ { 0x1F97, 0x1F27, 0x03B9, 0x0000 },
+ { 0x2CA4, 0x2CA5, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_137[] = {
+ { 0x038A, 0x03AF, 0x0000, 0x0000 },
+ { 0x1E97, 0x0074, 0x0308, 0x0000 },
+ { 0x1F96, 0x1F26, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_138[] = {
+ { 0x018B, 0x018C, 0x0000, 0x0000 },
+ { 0x0389, 0x03AE, 0x0000, 0x0000 },
+ { 0x048E, 0x048F, 0x0000, 0x0000 },
+ { 0x1E94, 0x1E95, 0x0000, 0x0000 },
+ { 0x1F95, 0x1F25, 0x03B9, 0x0000 },
+ { 0x2CA6, 0x2CA7, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_139[] = {
+ { 0x018A, 0x0257, 0x0000, 0x0000 },
+ { 0x0388, 0x03AD, 0x0000, 0x0000 },
+ { 0x1F94, 0x1F24, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_140[] = {
+ { 0x038F, 0x03CE, 0x0000, 0x0000 },
+ { 0x1E92, 0x1E93, 0x0000, 0x0000 },
+ { 0x1F93, 0x1F23, 0x03B9, 0x0000 },
+ { 0x2CA0, 0x2CA1, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_141[] = {
+ { 0x038E, 0x03CD, 0x0000, 0x0000 },
+ { 0x1F92, 0x1F22, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_142[] = {
+ { 0x018F, 0x0259, 0x0000, 0x0000 },
+ { 0x048A, 0x048B, 0x0000, 0x0000 },
+ { 0x1E90, 0x1E91, 0x0000, 0x0000 },
+ { 0x1F91, 0x1F21, 0x03B9, 0x0000 },
+ { 0x2CA2, 0x2CA3, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_143[] = {
+ { 0x018E, 0x01DD, 0x0000, 0x0000 },
+ { 0x038C, 0x03CC, 0x0000, 0x0000 },
+ { 0x1F90, 0x1F20, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_144[] = {
+ { 0x0191, 0x0192, 0x0000, 0x0000 },
+ { 0x0393, 0x03B3, 0x0000, 0x0000 },
+ { 0x0494, 0x0495, 0x0000, 0x0000 },
+ { 0x1E8E, 0x1E8F, 0x0000, 0x0000 },
+ { 0x1F8F, 0x1F07, 0x03B9, 0x0000 },
+ { 0x2CBC, 0x2CBD, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_145[] = {
+ { 0x0190, 0x025B, 0x0000, 0x0000 },
+ { 0x0392, 0x03B2, 0x0000, 0x0000 },
+ { 0x1F8E, 0x1F06, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_146[] = {
+ { 0x0193, 0x0260, 0x0000, 0x0000 },
+ { 0x0391, 0x03B1, 0x0000, 0x0000 },
+ { 0x0496, 0x0497, 0x0000, 0x0000 },
+ { 0x1E8C, 0x1E8D, 0x0000, 0x0000 },
+ { 0x1F8D, 0x1F05, 0x03B9, 0x0000 },
+ { 0x24B6, 0x24D0, 0x0000, 0x0000 },
+ { 0x2CBE, 0x2CBF, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_147[] = {
+ { 0x0390, 0x03B9, 0x0308, 0x0301 },
+ { 0x1F8C, 0x1F04, 0x03B9, 0x0000 },
+ { 0x24B7, 0x24D1, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_148[] = {
+ { 0x0397, 0x03B7, 0x0000, 0x0000 },
+ { 0x0490, 0x0491, 0x0000, 0x0000 },
+ { 0x1E8A, 0x1E8B, 0x0000, 0x0000 },
+ { 0x1F8B, 0x1F03, 0x03B9, 0x0000 },
+ { 0x2CB8, 0x2CB9, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_149[] = {
+ { 0x0194, 0x0263, 0x0000, 0x0000 },
+ { 0x0396, 0x03B6, 0x0000, 0x0000 },
+ { 0x1F8A, 0x1F02, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_150[] = {
+ { 0x0197, 0x0268, 0x0000, 0x0000 },
+ { 0x0395, 0x03B5, 0x0000, 0x0000 },
+ { 0x0492, 0x0493, 0x0000, 0x0000 },
+ { 0x1E88, 0x1E89, 0x0000, 0x0000 },
+ { 0x1F89, 0x1F01, 0x03B9, 0x0000 },
+ { 0x2CBA, 0x2CBB, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_151[] = {
+ { 0x0196, 0x0269, 0x0000, 0x0000 },
+ { 0x0394, 0x03B4, 0x0000, 0x0000 },
+ { 0x1F88, 0x1F00, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_152[] = {
+ { 0x039B, 0x03BB, 0x0000, 0x0000 },
+ { 0x049C, 0x049D, 0x0000, 0x0000 },
+ { 0x1E86, 0x1E87, 0x0000, 0x0000 },
+ { 0x1F87, 0x1F07, 0x03B9, 0x0000 },
+ { 0x24BC, 0x24D6, 0x0000, 0x0000 },
+ { 0x2CB4, 0x2CB5, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_153[] = {
+ { 0x0198, 0x0199, 0x0000, 0x0000 },
+ { 0x039A, 0x03BA, 0x0000, 0x0000 },
+ { 0x1F86, 0x1F06, 0x03B9, 0x0000 },
+ { 0x24BD, 0x24D7, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_154[] = {
+ { 0x0399, 0x03B9, 0x0000, 0x0000 },
+ { 0x049E, 0x049F, 0x0000, 0x0000 },
+ { 0x1E84, 0x1E85, 0x0000, 0x0000 },
+ { 0x1F85, 0x1F05, 0x03B9, 0x0000 },
+ { 0x24BE, 0x24D8, 0x0000, 0x0000 },
+ { 0x2CB6, 0x2CB7, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_155[] = {
+ { 0x0398, 0x03B8, 0x0000, 0x0000 },
+ { 0x1F84, 0x1F04, 0x03B9, 0x0000 },
+ { 0x24BF, 0x24D9, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_156[] = {
+ { 0x019D, 0x0272, 0x0000, 0x0000 },
+ { 0x039F, 0x03BF, 0x0000, 0x0000 },
+ { 0x0498, 0x0499, 0x0000, 0x0000 },
+ { 0x1E82, 0x1E83, 0x0000, 0x0000 },
+ { 0x1F83, 0x1F03, 0x03B9, 0x0000 },
+ { 0x24B8, 0x24D2, 0x0000, 0x0000 },
+ { 0x2CB0, 0x2CB1, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_157[] = {
+ { 0x019C, 0x026F, 0x0000, 0x0000 },
+ { 0x039E, 0x03BE, 0x0000, 0x0000 },
+ { 0x1F82, 0x1F02, 0x03B9, 0x0000 },
+ { 0x24B9, 0x24D3, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_158[] = {
+ { 0x019F, 0x0275, 0x0000, 0x0000 },
+ { 0x039D, 0x03BD, 0x0000, 0x0000 },
+ { 0x049A, 0x049B, 0x0000, 0x0000 },
+ { 0x1E80, 0x1E81, 0x0000, 0x0000 },
+ { 0x1F81, 0x1F01, 0x03B9, 0x0000 },
+ { 0x24BA, 0x24D4, 0x0000, 0x0000 },
+ { 0x2CB2, 0x2CB3, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_159[] = {
+ { 0x039C, 0x03BC, 0x0000, 0x0000 },
+ { 0x1F80, 0x1F00, 0x03B9, 0x0000 },
+ { 0x24BB, 0x24D5, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_160[] = {
+ { 0x03A3, 0x03C3, 0x0000, 0x0000 },
+ { 0x04A4, 0x04A5, 0x0000, 0x0000 },
+ { 0x10B0, 0x2D10, 0x0000, 0x0000 },
+ { 0x1EBE, 0x1EBF, 0x0000, 0x0000 },
+ { 0x2C8C, 0x2C8D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_161[] = {
+ { 0x01A0, 0x01A1, 0x0000, 0x0000 },
+ { 0x10B1, 0x2D11, 0x0000, 0x0000 },
+ { 0x1FBE, 0x03B9, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_162[] = {
+ { 0x03A1, 0x03C1, 0x0000, 0x0000 },
+ { 0x04A6, 0x04A7, 0x0000, 0x0000 },
+ { 0x10B2, 0x2D12, 0x0000, 0x0000 },
+ { 0x1EBC, 0x1EBD, 0x0000, 0x0000 },
+ { 0x2C8E, 0x2C8F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_163[] = {
+ { 0x01A2, 0x01A3, 0x0000, 0x0000 },
+ { 0x03A0, 0x03C0, 0x0000, 0x0000 },
+ { 0x10B3, 0x2D13, 0x0000, 0x0000 },
+ { 0x1FBC, 0x03B1, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_164[] = {
+ { 0x03A7, 0x03C7, 0x0000, 0x0000 },
+ { 0x04A0, 0x04A1, 0x0000, 0x0000 },
+ { 0x10B4, 0x2D14, 0x0000, 0x0000 },
+ { 0x1EBA, 0x1EBB, 0x0000, 0x0000 },
+ { 0x1FBB, 0x1F71, 0x0000, 0x0000 },
+ { 0x2C88, 0x2C89, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_165[] = {
+ { 0x01A4, 0x01A5, 0x0000, 0x0000 },
+ { 0x03A6, 0x03C6, 0x0000, 0x0000 },
+ { 0x10B5, 0x2D15, 0x0000, 0x0000 },
+ { 0x1FBA, 0x1F70, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_166[] = {
+ { 0x01A7, 0x01A8, 0x0000, 0x0000 },
+ { 0x03A5, 0x03C5, 0x0000, 0x0000 },
+ { 0x04A2, 0x04A3, 0x0000, 0x0000 },
+ { 0x10B6, 0x2D16, 0x0000, 0x0000 },
+ { 0x1EB8, 0x1EB9, 0x0000, 0x0000 },
+ { 0x1FB9, 0x1FB1, 0x0000, 0x0000 },
+ { 0x2C8A, 0x2C8B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_167[] = {
+ { 0x01A6, 0x0280, 0x0000, 0x0000 },
+ { 0x03A4, 0x03C4, 0x0000, 0x0000 },
+ { 0x10B7, 0x2D17, 0x0000, 0x0000 },
+ { 0x1FB8, 0x1FB0, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_168[] = {
+ { 0x01A9, 0x0283, 0x0000, 0x0000 },
+ { 0x03AB, 0x03CB, 0x0000, 0x0000 },
+ { 0x04AC, 0x04AD, 0x0000, 0x0000 },
+ { 0x10B8, 0x2D18, 0x0000, 0x0000 },
+ { 0x1EB6, 0x1EB7, 0x0000, 0x0000 },
+ { 0x1FB7, 0x03B1, 0x0342, 0x03B9 },
+ { 0x2C84, 0x2C85, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_169[] = {
+ { 0x03AA, 0x03CA, 0x0000, 0x0000 },
+ { 0x10B9, 0x2D19, 0x0000, 0x0000 },
+ { 0x1FB6, 0x03B1, 0x0342, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_170[] = {
+ { 0x03A9, 0x03C9, 0x0000, 0x0000 },
+ { 0x04AE, 0x04AF, 0x0000, 0x0000 },
+ { 0x10BA, 0x2D1A, 0x0000, 0x0000 },
+ { 0x1EB4, 0x1EB5, 0x0000, 0x0000 },
+ { 0x2C86, 0x2C87, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_171[] = {
+ { 0x03A8, 0x03C8, 0x0000, 0x0000 },
+ { 0x10BB, 0x2D1B, 0x0000, 0x0000 },
+ { 0x1FB4, 0x03AC, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_172[] = {
+ { 0x04A8, 0x04A9, 0x0000, 0x0000 },
+ { 0x10BC, 0x2D1C, 0x0000, 0x0000 },
+ { 0x1EB2, 0x1EB3, 0x0000, 0x0000 },
+ { 0x1FB3, 0x03B1, 0x03B9, 0x0000 },
+ { 0x2C80, 0x2C81, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_173[] = {
+ { 0x01AC, 0x01AD, 0x0000, 0x0000 },
+ { 0x10BD, 0x2D1D, 0x0000, 0x0000 },
+ { 0x1FB2, 0x1F70, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_174[] = {
+ { 0x01AF, 0x01B0, 0x0000, 0x0000 },
+ { 0x04AA, 0x04AB, 0x0000, 0x0000 },
+ { 0x10BE, 0x2D1E, 0x0000, 0x0000 },
+ { 0x1EB0, 0x1EB1, 0x0000, 0x0000 },
+ { 0x2C82, 0x2C83, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_175[] = {
+ { 0x01AE, 0x0288, 0x0000, 0x0000 },
+ { 0x10BF, 0x2D1F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_176[] = {
+ { 0x01B1, 0x028A, 0x0000, 0x0000 },
+ { 0x04B4, 0x04B5, 0x0000, 0x0000 },
+ { 0x10A0, 0x2D00, 0x0000, 0x0000 },
+ { 0x1EAE, 0x1EAF, 0x0000, 0x0000 },
+ { 0x1FAF, 0x1F67, 0x03B9, 0x0000 },
+ { 0x2C9C, 0x2C9D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_177[] = {
+ { 0x10A1, 0x2D01, 0x0000, 0x0000 },
+ { 0x1FAE, 0x1F66, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_178[] = {
+ { 0x01B3, 0x01B4, 0x0000, 0x0000 },
+ { 0x04B6, 0x04B7, 0x0000, 0x0000 },
+ { 0x10A2, 0x2D02, 0x0000, 0x0000 },
+ { 0x1EAC, 0x1EAD, 0x0000, 0x0000 },
+ { 0x1FAD, 0x1F65, 0x03B9, 0x0000 },
+ { 0x2C9E, 0x2C9F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_179[] = {
+ { 0x01B2, 0x028B, 0x0000, 0x0000 },
+ { 0x03B0, 0x03C5, 0x0308, 0x0301 },
+ { 0x10A3, 0x2D03, 0x0000, 0x0000 },
+ { 0x1FAC, 0x1F64, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_180[] = {
+ { 0x01B5, 0x01B6, 0x0000, 0x0000 },
+ { 0x04B0, 0x04B1, 0x0000, 0x0000 },
+ { 0x10A4, 0x2D04, 0x0000, 0x0000 },
+ { 0x1EAA, 0x1EAB, 0x0000, 0x0000 },
+ { 0x1FAB, 0x1F63, 0x03B9, 0x0000 },
+ { 0x2C98, 0x2C99, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_181[] = {
+ { 0x00B5, 0x03BC, 0x0000, 0x0000 },
+ { 0x10A5, 0x2D05, 0x0000, 0x0000 },
+ { 0x1FAA, 0x1F62, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_182[] = {
+ { 0x01B7, 0x0292, 0x0000, 0x0000 },
+ { 0x04B2, 0x04B3, 0x0000, 0x0000 },
+ { 0x10A6, 0x2D06, 0x0000, 0x0000 },
+ { 0x1EA8, 0x1EA9, 0x0000, 0x0000 },
+ { 0x1FA9, 0x1F61, 0x03B9, 0x0000 },
+ { 0x2C9A, 0x2C9B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_183[] = {
+ { 0x10A7, 0x2D07, 0x0000, 0x0000 },
+ { 0x1FA8, 0x1F60, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_184[] = {
+ { 0x04BC, 0x04BD, 0x0000, 0x0000 },
+ { 0x10A8, 0x2D08, 0x0000, 0x0000 },
+ { 0x1EA6, 0x1EA7, 0x0000, 0x0000 },
+ { 0x1FA7, 0x1F67, 0x03B9, 0x0000 },
+ { 0x2C94, 0x2C95, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_185[] = {
+ { 0x01B8, 0x01B9, 0x0000, 0x0000 },
+ { 0x10A9, 0x2D09, 0x0000, 0x0000 },
+ { 0x1FA6, 0x1F66, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_186[] = {
+ { 0x04BE, 0x04BF, 0x0000, 0x0000 },
+ { 0x10AA, 0x2D0A, 0x0000, 0x0000 },
+ { 0x1EA4, 0x1EA5, 0x0000, 0x0000 },
+ { 0x1FA5, 0x1F65, 0x03B9, 0x0000 },
+ { 0x2C96, 0x2C97, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_187[] = {
+ { 0x10AB, 0x2D0B, 0x0000, 0x0000 },
+ { 0x1FA4, 0x1F64, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_188[] = {
+ { 0x04B8, 0x04B9, 0x0000, 0x0000 },
+ { 0x10AC, 0x2D0C, 0x0000, 0x0000 },
+ { 0x1EA2, 0x1EA3, 0x0000, 0x0000 },
+ { 0x1FA3, 0x1F63, 0x03B9, 0x0000 },
+ { 0x2C90, 0x2C91, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_189[] = {
+ { 0x01BC, 0x01BD, 0x0000, 0x0000 },
+ { 0x10AD, 0x2D0D, 0x0000, 0x0000 },
+ { 0x1FA2, 0x1F62, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_190[] = {
+ { 0x04BA, 0x04BB, 0x0000, 0x0000 },
+ { 0x10AE, 0x2D0E, 0x0000, 0x0000 },
+ { 0x1EA0, 0x1EA1, 0x0000, 0x0000 },
+ { 0x1FA1, 0x1F61, 0x03B9, 0x0000 },
+ { 0x2C92, 0x2C93, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_191[] = {
+ { 0x10AF, 0x2D0F, 0x0000, 0x0000 },
+ { 0x1FA0, 0x1F60, 0x03B9, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_192[] = {
+ { 0x00C0, 0x00E0, 0x0000, 0x0000 },
+ { 0x1EDE, 0x1EDF, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_193[] = {
+ { 0x00C1, 0x00E1, 0x0000, 0x0000 },
+ { 0x03C2, 0x03C3, 0x0000, 0x0000 },
+ { 0x04C5, 0x04C6, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_194[] = {
+ { 0x00C2, 0x00E2, 0x0000, 0x0000 },
+ { 0x1EDC, 0x1EDD, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_195[] = {
+ { 0x00C3, 0x00E3, 0x0000, 0x0000 },
+ { 0x04C7, 0x04C8, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_196[] = {
+ { 0x00C4, 0x00E4, 0x0000, 0x0000 },
+ { 0x01C5, 0x01C6, 0x0000, 0x0000 },
+ { 0x1EDA, 0x1EDB, 0x0000, 0x0000 },
+ { 0x1FDB, 0x1F77, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_197[] = {
+ { 0x00C5, 0x00E5, 0x0000, 0x0000 },
+ { 0x01C4, 0x01C6, 0x0000, 0x0000 },
+ { 0x04C1, 0x04C2, 0x0000, 0x0000 },
+ { 0x1FDA, 0x1F76, 0x0000, 0x0000 },
+ { 0xFF3A, 0xFF5A, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_198[] = {
+ { 0x00C6, 0x00E6, 0x0000, 0x0000 },
+ { 0x01C7, 0x01C9, 0x0000, 0x0000 },
+ { 0x1ED8, 0x1ED9, 0x0000, 0x0000 },
+ { 0x1FD9, 0x1FD1, 0x0000, 0x0000 },
+ { 0xFF39, 0xFF59, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_199[] = {
+ { 0x00C7, 0x00E7, 0x0000, 0x0000 },
+ { 0x04C3, 0x04C4, 0x0000, 0x0000 },
+ { 0x1FD8, 0x1FD0, 0x0000, 0x0000 },
+ { 0xFF38, 0xFF58, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_200[] = {
+ { 0x00C8, 0x00E8, 0x0000, 0x0000 },
+ { 0x1ED6, 0x1ED7, 0x0000, 0x0000 },
+ { 0x1FD7, 0x03B9, 0x0308, 0x0342 },
+ { 0xFF37, 0xFF57, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_201[] = {
+ { 0x00C9, 0x00E9, 0x0000, 0x0000 },
+ { 0x01C8, 0x01C9, 0x0000, 0x0000 },
+ { 0x04CD, 0x04CE, 0x0000, 0x0000 },
+ { 0x1FD6, 0x03B9, 0x0342, 0x0000 },
+ { 0xFF36, 0xFF56, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_202[] = {
+ { 0x00CA, 0x00EA, 0x0000, 0x0000 },
+ { 0x01CB, 0x01CC, 0x0000, 0x0000 },
+ { 0x1ED4, 0x1ED5, 0x0000, 0x0000 },
+ { 0xFF35, 0xFF55, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_203[] = {
+ { 0x00CB, 0x00EB, 0x0000, 0x0000 },
+ { 0x01CA, 0x01CC, 0x0000, 0x0000 },
+ { 0xFF34, 0xFF54, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_204[] = {
+ { 0x00CC, 0x00EC, 0x0000, 0x0000 },
+ { 0x01CD, 0x01CE, 0x0000, 0x0000 },
+ { 0x1ED2, 0x1ED3, 0x0000, 0x0000 },
+ { 0x1FD3, 0x03B9, 0x0308, 0x0301 },
+ { 0x2CE0, 0x2CE1, 0x0000, 0x0000 },
+ { 0xFF33, 0xFF53, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_205[] = {
+ { 0x00CD, 0x00ED, 0x0000, 0x0000 },
+ { 0x04C9, 0x04CA, 0x0000, 0x0000 },
+ { 0x1FD2, 0x03B9, 0x0308, 0x0300 },
+ { 0xFF32, 0xFF52, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_206[] = {
+ { 0x00CE, 0x00EE, 0x0000, 0x0000 },
+ { 0x01CF, 0x01D0, 0x0000, 0x0000 },
+ { 0x1ED0, 0x1ED1, 0x0000, 0x0000 },
+ { 0x2CE2, 0x2CE3, 0x0000, 0x0000 },
+ { 0xFF31, 0xFF51, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_207[] = {
+ { 0x00CF, 0x00EF, 0x0000, 0x0000 },
+ { 0x04CB, 0x04CC, 0x0000, 0x0000 },
+ { 0xFF30, 0xFF50, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_208[] = {
+ { 0x00D0, 0x00F0, 0x0000, 0x0000 },
+ { 0x01D1, 0x01D2, 0x0000, 0x0000 },
+ { 0x04D4, 0x04D5, 0x0000, 0x0000 },
+ { 0x10C0, 0x2D20, 0x0000, 0x0000 },
+ { 0x1ECE, 0x1ECF, 0x0000, 0x0000 },
+ { 0xFF2F, 0xFF4F, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_209[] = {
+ { 0x00D1, 0x00F1, 0x0000, 0x0000 },
+ { 0x10C1, 0x2D21, 0x0000, 0x0000 },
+ { 0xFF2E, 0xFF4E, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_210[] = {
+ { 0x00D2, 0x00F2, 0x0000, 0x0000 },
+ { 0x01D3, 0x01D4, 0x0000, 0x0000 },
+ { 0x03D1, 0x03B8, 0x0000, 0x0000 },
+ { 0x04D6, 0x04D7, 0x0000, 0x0000 },
+ { 0x10C2, 0x2D22, 0x0000, 0x0000 },
+ { 0x1ECC, 0x1ECD, 0x0000, 0x0000 },
+ { 0xFF2D, 0xFF4D, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_211[] = {
+ { 0x00D3, 0x00F3, 0x0000, 0x0000 },
+ { 0x03D0, 0x03B2, 0x0000, 0x0000 },
+ { 0x10C3, 0x2D23, 0x0000, 0x0000 },
+ { 0x1FCC, 0x03B7, 0x03B9, 0x0000 },
+ { 0xFF2C, 0xFF4C, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_212[] = {
+ { 0x00D4, 0x00F4, 0x0000, 0x0000 },
+ { 0x01D5, 0x01D6, 0x0000, 0x0000 },
+ { 0x04D0, 0x04D1, 0x0000, 0x0000 },
+ { 0x10C4, 0x2D24, 0x0000, 0x0000 },
+ { 0x1ECA, 0x1ECB, 0x0000, 0x0000 },
+ { 0x1FCB, 0x1F75, 0x0000, 0x0000 },
+ { 0xFF2B, 0xFF4B, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_213[] = {
+ { 0x00D5, 0x00F5, 0x0000, 0x0000 },
+ { 0x03D6, 0x03C0, 0x0000, 0x0000 },
+ { 0x10C5, 0x2D25, 0x0000, 0x0000 },
+ { 0x1FCA, 0x1F74, 0x0000, 0x0000 },
+ { 0xFF2A, 0xFF4A, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_214[] = {
+ { 0x00D6, 0x00F6, 0x0000, 0x0000 },
+ { 0x01D7, 0x01D8, 0x0000, 0x0000 },
+ { 0x03D5, 0x03C6, 0x0000, 0x0000 },
+ { 0x04D2, 0x04D3, 0x0000, 0x0000 },
+ { 0x1EC8, 0x1EC9, 0x0000, 0x0000 },
+ { 0x1FC9, 0x1F73, 0x0000, 0x0000 },
+ { 0xFF29, 0xFF49, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_215[] = {
+ { 0x1FC8, 0x1F72, 0x0000, 0x0000 },
+ { 0xFF28, 0xFF48, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_216[] = {
+ { 0x00D8, 0x00F8, 0x0000, 0x0000 },
+ { 0x01D9, 0x01DA, 0x0000, 0x0000 },
+ { 0x04DC, 0x04DD, 0x0000, 0x0000 },
+ { 0x1EC6, 0x1EC7, 0x0000, 0x0000 },
+ { 0x1FC7, 0x03B7, 0x0342, 0x03B9 },
+ { 0xFF27, 0xFF47, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_217[] = {
+ { 0x00D9, 0x00F9, 0x0000, 0x0000 },
+ { 0x03DA, 0x03DB, 0x0000, 0x0000 },
+ { 0x1FC6, 0x03B7, 0x0342, 0x0000 },
+ { 0xFF26, 0xFF46, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_218[] = {
+ { 0x00DA, 0x00FA, 0x0000, 0x0000 },
+ { 0x01DB, 0x01DC, 0x0000, 0x0000 },
+ { 0x04DE, 0x04DF, 0x0000, 0x0000 },
+ { 0x1EC4, 0x1EC5, 0x0000, 0x0000 },
+ { 0xFF25, 0xFF45, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_219[] = {
+ { 0x00DB, 0x00FB, 0x0000, 0x0000 },
+ { 0x03D8, 0x03D9, 0x0000, 0x0000 },
+ { 0x1FC4, 0x03AE, 0x03B9, 0x0000 },
+ { 0xFF24, 0xFF44, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_220[] = {
+ { 0x00DC, 0x00FC, 0x0000, 0x0000 },
+ { 0x04D8, 0x04D9, 0x0000, 0x0000 },
+ { 0x1EC2, 0x1EC3, 0x0000, 0x0000 },
+ { 0x1FC3, 0x03B7, 0x03B9, 0x0000 },
+ { 0xFF23, 0xFF43, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_221[] = {
+ { 0x00DD, 0x00FD, 0x0000, 0x0000 },
+ { 0x03DE, 0x03DF, 0x0000, 0x0000 },
+ { 0x1FC2, 0x1F74, 0x03B9, 0x0000 },
+ { 0xFF22, 0xFF42, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_222[] = {
+ { 0x00DE, 0x00FE, 0x0000, 0x0000 },
+ { 0x04DA, 0x04DB, 0x0000, 0x0000 },
+ { 0x1EC0, 0x1EC1, 0x0000, 0x0000 },
+ { 0xFF21, 0xFF41, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_223[] = {
+ { 0x00DF, 0x0073, 0x0073, 0x0000 },
+ { 0x01DE, 0x01DF, 0x0000, 0x0000 },
+ { 0x03DC, 0x03DD, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_224[] = {
+ { 0x04E4, 0x04E5, 0x0000, 0x0000 },
+ { 0x24C4, 0x24DE, 0x0000, 0x0000 },
+ { 0x2CCC, 0x2CCD, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_225[] = {
+ { 0x01E0, 0x01E1, 0x0000, 0x0000 },
+ { 0x03E2, 0x03E3, 0x0000, 0x0000 },
+ { 0x24C5, 0x24DF, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_226[] = {
+ { 0x04E6, 0x04E7, 0x0000, 0x0000 },
+ { 0x24C6, 0x24E0, 0x0000, 0x0000 },
+ { 0x2CCE, 0x2CCF, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_227[] = {
+ { 0x01E2, 0x01E3, 0x0000, 0x0000 },
+ { 0x03E0, 0x03E1, 0x0000, 0x0000 },
+ { 0x1FFC, 0x03C9, 0x03B9, 0x0000 },
+ { 0x24C7, 0x24E1, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_228[] = {
+ { 0x04E0, 0x04E1, 0x0000, 0x0000 },
+ { 0x1FFB, 0x1F7D, 0x0000, 0x0000 },
+ { 0x24C0, 0x24DA, 0x0000, 0x0000 },
+ { 0x2CC8, 0x2CC9, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_229[] = {
+ { 0x01E4, 0x01E5, 0x0000, 0x0000 },
+ { 0x03E6, 0x03E7, 0x0000, 0x0000 },
+ { 0x1FFA, 0x1F7C, 0x0000, 0x0000 },
+ { 0x24C1, 0x24DB, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_230[] = {
+ { 0x04E2, 0x04E3, 0x0000, 0x0000 },
+ { 0x1EF8, 0x1EF9, 0x0000, 0x0000 },
+ { 0x1FF9, 0x1F79, 0x0000, 0x0000 },
+ { 0x24C2, 0x24DC, 0x0000, 0x0000 },
+ { 0x2CCA, 0x2CCB, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_231[] = {
+ { 0x01E6, 0x01E7, 0x0000, 0x0000 },
+ { 0x03E4, 0x03E5, 0x0000, 0x0000 },
+ { 0x1FF8, 0x1F78, 0x0000, 0x0000 },
+ { 0x24C3, 0x24DD, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_232[] = {
+ { 0x04EC, 0x04ED, 0x0000, 0x0000 },
+ { 0x1EF6, 0x1EF7, 0x0000, 0x0000 },
+ { 0x1FF7, 0x03C9, 0x0342, 0x03B9 },
+ { 0x24CC, 0x24E6, 0x0000, 0x0000 },
+ { 0x2CC4, 0x2CC5, 0x0000, 0x0000 },
+ { 0xFB13, 0x0574, 0x0576, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_233[] = {
+ { 0x01E8, 0x01E9, 0x0000, 0x0000 },
+ { 0x03EA, 0x03EB, 0x0000, 0x0000 },
+ { 0x1FF6, 0x03C9, 0x0342, 0x0000 },
+ { 0x24CD, 0x24E7, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_234[] = {
+ { 0x04EE, 0x04EF, 0x0000, 0x0000 },
+ { 0x1EF4, 0x1EF5, 0x0000, 0x0000 },
+ { 0x24CE, 0x24E8, 0x0000, 0x0000 },
+ { 0x2CC6, 0x2CC7, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_235[] = {
+ { 0x01EA, 0x01EB, 0x0000, 0x0000 },
+ { 0x03E8, 0x03E9, 0x0000, 0x0000 },
+ { 0x1FF4, 0x03CE, 0x03B9, 0x0000 },
+ { 0x24CF, 0x24E9, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_236[] = {
+ { 0x04E8, 0x04E9, 0x0000, 0x0000 },
+ { 0x1EF2, 0x1EF3, 0x0000, 0x0000 },
+ { 0x1FF3, 0x03C9, 0x03B9, 0x0000 },
+ { 0x24C8, 0x24E2, 0x0000, 0x0000 },
+ { 0x2CC0, 0x2CC1, 0x0000, 0x0000 },
+ { 0xFB17, 0x0574, 0x056D, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_237[] = {
+ { 0x01EC, 0x01ED, 0x0000, 0x0000 },
+ { 0x03EE, 0x03EF, 0x0000, 0x0000 },
+ { 0x1FF2, 0x1F7C, 0x03B9, 0x0000 },
+ { 0x24C9, 0x24E3, 0x0000, 0x0000 },
+ { 0xFB16, 0x057E, 0x0576, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_238[] = {
+ { 0x04EA, 0x04EB, 0x0000, 0x0000 },
+ { 0x1EF0, 0x1EF1, 0x0000, 0x0000 },
+ { 0x24CA, 0x24E4, 0x0000, 0x0000 },
+ { 0x2CC2, 0x2CC3, 0x0000, 0x0000 },
+ { 0xFB15, 0x0574, 0x056B, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_239[] = {
+ { 0x01EE, 0x01EF, 0x0000, 0x0000 },
+ { 0x03EC, 0x03ED, 0x0000, 0x0000 },
+ { 0x24CB, 0x24E5, 0x0000, 0x0000 },
+ { 0xFB14, 0x0574, 0x0565, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_240[] = {
+ { 0x01F1, 0x01F3, 0x0000, 0x0000 },
+ { 0x04F4, 0x04F5, 0x0000, 0x0000 },
+ { 0x1EEE, 0x1EEF, 0x0000, 0x0000 },
+ { 0x2CDC, 0x2CDD, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_241[] = {
+ { 0x01F0, 0x006A, 0x030C, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_242[] = {
+ { 0x03F1, 0x03C1, 0x0000, 0x0000 },
+ { 0x04F6, 0x04F7, 0x0000, 0x0000 },
+ { 0x1EEC, 0x1EED, 0x0000, 0x0000 },
+ { 0x2CDE, 0x2CDF, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_243[] = {
+ { 0x01F2, 0x01F3, 0x0000, 0x0000 },
+ { 0x03F0, 0x03BA, 0x0000, 0x0000 },
+ { 0x1FEC, 0x1FE5, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_244[] = {
+ { 0x03F7, 0x03F8, 0x0000, 0x0000 },
+ { 0x04F0, 0x04F1, 0x0000, 0x0000 },
+ { 0x1EEA, 0x1EEB, 0x0000, 0x0000 },
+ { 0x1FEB, 0x1F7B, 0x0000, 0x0000 },
+ { 0x2CD8, 0x2CD9, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_245[] = {
+ { 0x01F4, 0x01F5, 0x0000, 0x0000 },
+ { 0x1FEA, 0x1F7A, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_246[] = {
+ { 0x01F7, 0x01BF, 0x0000, 0x0000 },
+ { 0x03F5, 0x03B5, 0x0000, 0x0000 },
+ { 0x04F2, 0x04F3, 0x0000, 0x0000 },
+ { 0x1EE8, 0x1EE9, 0x0000, 0x0000 },
+ { 0x1FE9, 0x1FE1, 0x0000, 0x0000 },
+ { 0x2CDA, 0x2CDB, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_247[] = {
+ { 0x01F6, 0x0195, 0x0000, 0x0000 },
+ { 0x03F4, 0x03B8, 0x0000, 0x0000 },
+ { 0x1FE8, 0x1FE0, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_248[] = {
+ { 0x1EE6, 0x1EE7, 0x0000, 0x0000 },
+ { 0x1FE7, 0x03C5, 0x0308, 0x0342 },
+ { 0x2CD4, 0x2CD5, 0x0000, 0x0000 },
+ { 0xFB03, 0x0066, 0x0066, 0x0069 }
+};
+
+static const CaseFoldMapping case_fold_249[] = {
+ { 0x01F8, 0x01F9, 0x0000, 0x0000 },
+ { 0x03FA, 0x03FB, 0x0000, 0x0000 },
+ { 0x1FE6, 0x03C5, 0x0342, 0x0000 },
+ { 0xFB02, 0x0066, 0x006C, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_250[] = {
+ { 0x03F9, 0x03F2, 0x0000, 0x0000 },
+ { 0x1EE4, 0x1EE5, 0x0000, 0x0000 },
+ { 0x2CD6, 0x2CD7, 0x0000, 0x0000 },
+ { 0xFB01, 0x0066, 0x0069, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_251[] = {
+ { 0x01FA, 0x01FB, 0x0000, 0x0000 },
+ { 0x1FE4, 0x03C1, 0x0313, 0x0000 },
+ { 0xFB00, 0x0066, 0x0066, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_252[] = {
+ { 0x04F8, 0x04F9, 0x0000, 0x0000 },
+ { 0x1EE2, 0x1EE3, 0x0000, 0x0000 },
+ { 0x1FE3, 0x03C5, 0x0308, 0x0301 },
+ { 0x2CD0, 0x2CD1, 0x0000, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_253[] = {
+ { 0x01FC, 0x01FD, 0x0000, 0x0000 },
+ { 0x1FE2, 0x03C5, 0x0308, 0x0300 },
+ { 0xFB06, 0x0073, 0x0074, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_254[] = {
+ { 0x1EE0, 0x1EE1, 0x0000, 0x0000 },
+ { 0x2CD2, 0x2CD3, 0x0000, 0x0000 },
+ { 0xFB05, 0x0073, 0x0074, 0x0000 }
+};
+
+static const CaseFoldMapping case_fold_255[] = {
+ { 0x01FE, 0x01FF, 0x0000, 0x0000 },
+ { 0xFB04, 0x0066, 0x0066, 0x006C }
+};
+
+
+static const CaseFoldHashBucket case_fold_hash[256] = {
+ { __PHYSFS_ARRAYLEN(case_fold_000), case_fold_000 },
+ { __PHYSFS_ARRAYLEN(case_fold_001), case_fold_001 },
+ { __PHYSFS_ARRAYLEN(case_fold_002), case_fold_002 },
+ { __PHYSFS_ARRAYLEN(case_fold_003), case_fold_003 },
+ { __PHYSFS_ARRAYLEN(case_fold_004), case_fold_004 },
+ { __PHYSFS_ARRAYLEN(case_fold_005), case_fold_005 },
+ { __PHYSFS_ARRAYLEN(case_fold_006), case_fold_006 },
+ { __PHYSFS_ARRAYLEN(case_fold_007), case_fold_007 },
+ { __PHYSFS_ARRAYLEN(case_fold_008), case_fold_008 },
+ { __PHYSFS_ARRAYLEN(case_fold_009), case_fold_009 },
+ { __PHYSFS_ARRAYLEN(case_fold_010), case_fold_010 },
+ { __PHYSFS_ARRAYLEN(case_fold_011), case_fold_011 },
+ { __PHYSFS_ARRAYLEN(case_fold_012), case_fold_012 },
+ { __PHYSFS_ARRAYLEN(case_fold_013), case_fold_013 },
+ { __PHYSFS_ARRAYLEN(case_fold_014), case_fold_014 },
+ { __PHYSFS_ARRAYLEN(case_fold_015), case_fold_015 },
+ { __PHYSFS_ARRAYLEN(case_fold_016), case_fold_016 },
+ { __PHYSFS_ARRAYLEN(case_fold_017), case_fold_017 },
+ { __PHYSFS_ARRAYLEN(case_fold_018), case_fold_018 },
+ { __PHYSFS_ARRAYLEN(case_fold_019), case_fold_019 },
+ { __PHYSFS_ARRAYLEN(case_fold_020), case_fold_020 },
+ { __PHYSFS_ARRAYLEN(case_fold_021), case_fold_021 },
+ { __PHYSFS_ARRAYLEN(case_fold_022), case_fold_022 },
+ { __PHYSFS_ARRAYLEN(case_fold_023), case_fold_023 },
+ { __PHYSFS_ARRAYLEN(case_fold_024), case_fold_024 },
+ { __PHYSFS_ARRAYLEN(case_fold_025), case_fold_025 },
+ { __PHYSFS_ARRAYLEN(case_fold_026), case_fold_026 },
+ { __PHYSFS_ARRAYLEN(case_fold_027), case_fold_027 },
+ { __PHYSFS_ARRAYLEN(case_fold_028), case_fold_028 },
+ { __PHYSFS_ARRAYLEN(case_fold_029), case_fold_029 },
+ { __PHYSFS_ARRAYLEN(case_fold_030), case_fold_030 },
+ { __PHYSFS_ARRAYLEN(case_fold_031), case_fold_031 },
+ { __PHYSFS_ARRAYLEN(case_fold_032), case_fold_032 },
+ { __PHYSFS_ARRAYLEN(case_fold_033), case_fold_033 },
+ { __PHYSFS_ARRAYLEN(case_fold_034), case_fold_034 },
+ { __PHYSFS_ARRAYLEN(case_fold_035), case_fold_035 },
+ { __PHYSFS_ARRAYLEN(case_fold_036), case_fold_036 },
+ { __PHYSFS_ARRAYLEN(case_fold_037), case_fold_037 },
+ { __PHYSFS_ARRAYLEN(case_fold_038), case_fold_038 },
+ { __PHYSFS_ARRAYLEN(case_fold_039), case_fold_039 },
+ { __PHYSFS_ARRAYLEN(case_fold_040), case_fold_040 },
+ { __PHYSFS_ARRAYLEN(case_fold_041), case_fold_041 },
+ { __PHYSFS_ARRAYLEN(case_fold_042), case_fold_042 },
+ { __PHYSFS_ARRAYLEN(case_fold_043), case_fold_043 },
+ { __PHYSFS_ARRAYLEN(case_fold_044), case_fold_044 },
+ { __PHYSFS_ARRAYLEN(case_fold_045), case_fold_045 },
+ { __PHYSFS_ARRAYLEN(case_fold_046), case_fold_046 },
+ { __PHYSFS_ARRAYLEN(case_fold_047), case_fold_047 },
+ { __PHYSFS_ARRAYLEN(case_fold_048), case_fold_048 },
+ { __PHYSFS_ARRAYLEN(case_fold_049), case_fold_049 },
+ { __PHYSFS_ARRAYLEN(case_fold_050), case_fold_050 },
+ { __PHYSFS_ARRAYLEN(case_fold_051), case_fold_051 },
+ { __PHYSFS_ARRAYLEN(case_fold_052), case_fold_052 },
+ { __PHYSFS_ARRAYLEN(case_fold_053), case_fold_053 },
+ { __PHYSFS_ARRAYLEN(case_fold_054), case_fold_054 },
+ { __PHYSFS_ARRAYLEN(case_fold_055), case_fold_055 },
+ { __PHYSFS_ARRAYLEN(case_fold_056), case_fold_056 },
+ { __PHYSFS_ARRAYLEN(case_fold_057), case_fold_057 },
+ { __PHYSFS_ARRAYLEN(case_fold_058), case_fold_058 },
+ { __PHYSFS_ARRAYLEN(case_fold_059), case_fold_059 },
+ { __PHYSFS_ARRAYLEN(case_fold_060), case_fold_060 },
+ { __PHYSFS_ARRAYLEN(case_fold_061), case_fold_061 },
+ { __PHYSFS_ARRAYLEN(case_fold_062), case_fold_062 },
+ { __PHYSFS_ARRAYLEN(case_fold_063), case_fold_063 },
+ { __PHYSFS_ARRAYLEN(case_fold_064), case_fold_064 },
+ { __PHYSFS_ARRAYLEN(case_fold_065), case_fold_065 },
+ { __PHYSFS_ARRAYLEN(case_fold_066), case_fold_066 },
+ { __PHYSFS_ARRAYLEN(case_fold_067), case_fold_067 },
+ { __PHYSFS_ARRAYLEN(case_fold_068), case_fold_068 },
+ { __PHYSFS_ARRAYLEN(case_fold_069), case_fold_069 },
+ { __PHYSFS_ARRAYLEN(case_fold_070), case_fold_070 },
+ { __PHYSFS_ARRAYLEN(case_fold_071), case_fold_071 },
+ { __PHYSFS_ARRAYLEN(case_fold_072), case_fold_072 },
+ { __PHYSFS_ARRAYLEN(case_fold_073), case_fold_073 },
+ { __PHYSFS_ARRAYLEN(case_fold_074), case_fold_074 },
+ { __PHYSFS_ARRAYLEN(case_fold_075), case_fold_075 },
+ { __PHYSFS_ARRAYLEN(case_fold_076), case_fold_076 },
+ { __PHYSFS_ARRAYLEN(case_fold_077), case_fold_077 },
+ { __PHYSFS_ARRAYLEN(case_fold_078), case_fold_078 },
+ { __PHYSFS_ARRAYLEN(case_fold_079), case_fold_079 },
+ { __PHYSFS_ARRAYLEN(case_fold_080), case_fold_080 },
+ { __PHYSFS_ARRAYLEN(case_fold_081), case_fold_081 },
+ { __PHYSFS_ARRAYLEN(case_fold_082), case_fold_082 },
+ { __PHYSFS_ARRAYLEN(case_fold_083), case_fold_083 },
+ { __PHYSFS_ARRAYLEN(case_fold_084), case_fold_084 },
+ { __PHYSFS_ARRAYLEN(case_fold_085), case_fold_085 },
+ { __PHYSFS_ARRAYLEN(case_fold_086), case_fold_086 },
+ { __PHYSFS_ARRAYLEN(case_fold_087), case_fold_087 },
+ { __PHYSFS_ARRAYLEN(case_fold_088), case_fold_088 },
+ { __PHYSFS_ARRAYLEN(case_fold_089), case_fold_089 },
+ { __PHYSFS_ARRAYLEN(case_fold_090), case_fold_090 },
+ { __PHYSFS_ARRAYLEN(case_fold_091), case_fold_091 },
+ { __PHYSFS_ARRAYLEN(case_fold_092), case_fold_092 },
+ { __PHYSFS_ARRAYLEN(case_fold_093), case_fold_093 },
+ { __PHYSFS_ARRAYLEN(case_fold_094), case_fold_094 },
+ { __PHYSFS_ARRAYLEN(case_fold_095), case_fold_095 },
+ { __PHYSFS_ARRAYLEN(case_fold_096), case_fold_096 },
+ { __PHYSFS_ARRAYLEN(case_fold_097), case_fold_097 },
+ { __PHYSFS_ARRAYLEN(case_fold_098), case_fold_098 },
+ { __PHYSFS_ARRAYLEN(case_fold_099), case_fold_099 },
+ { __PHYSFS_ARRAYLEN(case_fold_100), case_fold_100 },
+ { __PHYSFS_ARRAYLEN(case_fold_101), case_fold_101 },
+ { __PHYSFS_ARRAYLEN(case_fold_102), case_fold_102 },
+ { __PHYSFS_ARRAYLEN(case_fold_103), case_fold_103 },
+ { __PHYSFS_ARRAYLEN(case_fold_104), case_fold_104 },
+ { __PHYSFS_ARRAYLEN(case_fold_105), case_fold_105 },
+ { __PHYSFS_ARRAYLEN(case_fold_106), case_fold_106 },
+ { __PHYSFS_ARRAYLEN(case_fold_107), case_fold_107 },
+ { __PHYSFS_ARRAYLEN(case_fold_108), case_fold_108 },
+ { __PHYSFS_ARRAYLEN(case_fold_109), case_fold_109 },
+ { __PHYSFS_ARRAYLEN(case_fold_110), case_fold_110 },
+ { __PHYSFS_ARRAYLEN(case_fold_111), case_fold_111 },
+ { __PHYSFS_ARRAYLEN(case_fold_112), case_fold_112 },
+ { __PHYSFS_ARRAYLEN(case_fold_113), case_fold_113 },
+ { __PHYSFS_ARRAYLEN(case_fold_114), case_fold_114 },
+ { __PHYSFS_ARRAYLEN(case_fold_115), case_fold_115 },
+ { __PHYSFS_ARRAYLEN(case_fold_116), case_fold_116 },
+ { __PHYSFS_ARRAYLEN(case_fold_117), case_fold_117 },
+ { __PHYSFS_ARRAYLEN(case_fold_118), case_fold_118 },
+ { __PHYSFS_ARRAYLEN(case_fold_119), case_fold_119 },
+ { __PHYSFS_ARRAYLEN(case_fold_120), case_fold_120 },
+ { __PHYSFS_ARRAYLEN(case_fold_121), case_fold_121 },
+ { __PHYSFS_ARRAYLEN(case_fold_122), case_fold_122 },
+ { 0, NULL },
+ { __PHYSFS_ARRAYLEN(case_fold_124), case_fold_124 },
+ { 0, NULL },
+ { __PHYSFS_ARRAYLEN(case_fold_126), case_fold_126 },
+ { 0, NULL },
+ { __PHYSFS_ARRAYLEN(case_fold_128), case_fold_128 },
+ { __PHYSFS_ARRAYLEN(case_fold_129), case_fold_129 },
+ { __PHYSFS_ARRAYLEN(case_fold_130), case_fold_130 },
+ { __PHYSFS_ARRAYLEN(case_fold_131), case_fold_131 },
+ { __PHYSFS_ARRAYLEN(case_fold_132), case_fold_132 },
+ { __PHYSFS_ARRAYLEN(case_fold_133), case_fold_133 },
+ { __PHYSFS_ARRAYLEN(case_fold_134), case_fold_134 },
+ { __PHYSFS_ARRAYLEN(case_fold_135), case_fold_135 },
+ { __PHYSFS_ARRAYLEN(case_fold_136), case_fold_136 },
+ { __PHYSFS_ARRAYLEN(case_fold_137), case_fold_137 },
+ { __PHYSFS_ARRAYLEN(case_fold_138), case_fold_138 },
+ { __PHYSFS_ARRAYLEN(case_fold_139), case_fold_139 },
+ { __PHYSFS_ARRAYLEN(case_fold_140), case_fold_140 },
+ { __PHYSFS_ARRAYLEN(case_fold_141), case_fold_141 },
+ { __PHYSFS_ARRAYLEN(case_fold_142), case_fold_142 },
+ { __PHYSFS_ARRAYLEN(case_fold_143), case_fold_143 },
+ { __PHYSFS_ARRAYLEN(case_fold_144), case_fold_144 },
+ { __PHYSFS_ARRAYLEN(case_fold_145), case_fold_145 },
+ { __PHYSFS_ARRAYLEN(case_fold_146), case_fold_146 },
+ { __PHYSFS_ARRAYLEN(case_fold_147), case_fold_147 },
+ { __PHYSFS_ARRAYLEN(case_fold_148), case_fold_148 },
+ { __PHYSFS_ARRAYLEN(case_fold_149), case_fold_149 },
+ { __PHYSFS_ARRAYLEN(case_fold_150), case_fold_150 },
+ { __PHYSFS_ARRAYLEN(case_fold_151), case_fold_151 },
+ { __PHYSFS_ARRAYLEN(case_fold_152), case_fold_152 },
+ { __PHYSFS_ARRAYLEN(case_fold_153), case_fold_153 },
+ { __PHYSFS_ARRAYLEN(case_fold_154), case_fold_154 },
+ { __PHYSFS_ARRAYLEN(case_fold_155), case_fold_155 },
+ { __PHYSFS_ARRAYLEN(case_fold_156), case_fold_156 },
+ { __PHYSFS_ARRAYLEN(case_fold_157), case_fold_157 },
+ { __PHYSFS_ARRAYLEN(case_fold_158), case_fold_158 },
+ { __PHYSFS_ARRAYLEN(case_fold_159), case_fold_159 },
+ { __PHYSFS_ARRAYLEN(case_fold_160), case_fold_160 },
+ { __PHYSFS_ARRAYLEN(case_fold_161), case_fold_161 },
+ { __PHYSFS_ARRAYLEN(case_fold_162), case_fold_162 },
+ { __PHYSFS_ARRAYLEN(case_fold_163), case_fold_163 },
+ { __PHYSFS_ARRAYLEN(case_fold_164), case_fold_164 },
+ { __PHYSFS_ARRAYLEN(case_fold_165), case_fold_165 },
+ { __PHYSFS_ARRAYLEN(case_fold_166), case_fold_166 },
+ { __PHYSFS_ARRAYLEN(case_fold_167), case_fold_167 },
+ { __PHYSFS_ARRAYLEN(case_fold_168), case_fold_168 },
+ { __PHYSFS_ARRAYLEN(case_fold_169), case_fold_169 },
+ { __PHYSFS_ARRAYLEN(case_fold_170), case_fold_170 },
+ { __PHYSFS_ARRAYLEN(case_fold_171), case_fold_171 },
+ { __PHYSFS_ARRAYLEN(case_fold_172), case_fold_172 },
+ { __PHYSFS_ARRAYLEN(case_fold_173), case_fold_173 },
+ { __PHYSFS_ARRAYLEN(case_fold_174), case_fold_174 },
+ { __PHYSFS_ARRAYLEN(case_fold_175), case_fold_175 },
+ { __PHYSFS_ARRAYLEN(case_fold_176), case_fold_176 },
+ { __PHYSFS_ARRAYLEN(case_fold_177), case_fold_177 },
+ { __PHYSFS_ARRAYLEN(case_fold_178), case_fold_178 },
+ { __PHYSFS_ARRAYLEN(case_fold_179), case_fold_179 },
+ { __PHYSFS_ARRAYLEN(case_fold_180), case_fold_180 },
+ { __PHYSFS_ARRAYLEN(case_fold_181), case_fold_181 },
+ { __PHYSFS_ARRAYLEN(case_fold_182), case_fold_182 },
+ { __PHYSFS_ARRAYLEN(case_fold_183), case_fold_183 },
+ { __PHYSFS_ARRAYLEN(case_fold_184), case_fold_184 },
+ { __PHYSFS_ARRAYLEN(case_fold_185), case_fold_185 },
+ { __PHYSFS_ARRAYLEN(case_fold_186), case_fold_186 },
+ { __PHYSFS_ARRAYLEN(case_fold_187), case_fold_187 },
+ { __PHYSFS_ARRAYLEN(case_fold_188), case_fold_188 },
+ { __PHYSFS_ARRAYLEN(case_fold_189), case_fold_189 },
+ { __PHYSFS_ARRAYLEN(case_fold_190), case_fold_190 },
+ { __PHYSFS_ARRAYLEN(case_fold_191), case_fold_191 },
+ { __PHYSFS_ARRAYLEN(case_fold_192), case_fold_192 },
+ { __PHYSFS_ARRAYLEN(case_fold_193), case_fold_193 },
+ { __PHYSFS_ARRAYLEN(case_fold_194), case_fold_194 },
+ { __PHYSFS_ARRAYLEN(case_fold_195), case_fold_195 },
+ { __PHYSFS_ARRAYLEN(case_fold_196), case_fold_196 },
+ { __PHYSFS_ARRAYLEN(case_fold_197), case_fold_197 },
+ { __PHYSFS_ARRAYLEN(case_fold_198), case_fold_198 },
+ { __PHYSFS_ARRAYLEN(case_fold_199), case_fold_199 },
+ { __PHYSFS_ARRAYLEN(case_fold_200), case_fold_200 },
+ { __PHYSFS_ARRAYLEN(case_fold_201), case_fold_201 },
+ { __PHYSFS_ARRAYLEN(case_fold_202), case_fold_202 },
+ { __PHYSFS_ARRAYLEN(case_fold_203), case_fold_203 },
+ { __PHYSFS_ARRAYLEN(case_fold_204), case_fold_204 },
+ { __PHYSFS_ARRAYLEN(case_fold_205), case_fold_205 },
+ { __PHYSFS_ARRAYLEN(case_fold_206), case_fold_206 },
+ { __PHYSFS_ARRAYLEN(case_fold_207), case_fold_207 },
+ { __PHYSFS_ARRAYLEN(case_fold_208), case_fold_208 },
+ { __PHYSFS_ARRAYLEN(case_fold_209), case_fold_209 },
+ { __PHYSFS_ARRAYLEN(case_fold_210), case_fold_210 },
+ { __PHYSFS_ARRAYLEN(case_fold_211), case_fold_211 },
+ { __PHYSFS_ARRAYLEN(case_fold_212), case_fold_212 },
+ { __PHYSFS_ARRAYLEN(case_fold_213), case_fold_213 },
+ { __PHYSFS_ARRAYLEN(case_fold_214), case_fold_214 },
+ { __PHYSFS_ARRAYLEN(case_fold_215), case_fold_215 },
+ { __PHYSFS_ARRAYLEN(case_fold_216), case_fold_216 },
+ { __PHYSFS_ARRAYLEN(case_fold_217), case_fold_217 },
+ { __PHYSFS_ARRAYLEN(case_fold_218), case_fold_218 },
+ { __PHYSFS_ARRAYLEN(case_fold_219), case_fold_219 },
+ { __PHYSFS_ARRAYLEN(case_fold_220), case_fold_220 },
+ { __PHYSFS_ARRAYLEN(case_fold_221), case_fold_221 },
+ { __PHYSFS_ARRAYLEN(case_fold_222), case_fold_222 },
+ { __PHYSFS_ARRAYLEN(case_fold_223), case_fold_223 },
+ { __PHYSFS_ARRAYLEN(case_fold_224), case_fold_224 },
+ { __PHYSFS_ARRAYLEN(case_fold_225), case_fold_225 },
+ { __PHYSFS_ARRAYLEN(case_fold_226), case_fold_226 },
+ { __PHYSFS_ARRAYLEN(case_fold_227), case_fold_227 },
+ { __PHYSFS_ARRAYLEN(case_fold_228), case_fold_228 },
+ { __PHYSFS_ARRAYLEN(case_fold_229), case_fold_229 },
+ { __PHYSFS_ARRAYLEN(case_fold_230), case_fold_230 },
+ { __PHYSFS_ARRAYLEN(case_fold_231), case_fold_231 },
+ { __PHYSFS_ARRAYLEN(case_fold_232), case_fold_232 },
+ { __PHYSFS_ARRAYLEN(case_fold_233), case_fold_233 },
+ { __PHYSFS_ARRAYLEN(case_fold_234), case_fold_234 },
+ { __PHYSFS_ARRAYLEN(case_fold_235), case_fold_235 },
+ { __PHYSFS_ARRAYLEN(case_fold_236), case_fold_236 },
+ { __PHYSFS_ARRAYLEN(case_fold_237), case_fold_237 },
+ { __PHYSFS_ARRAYLEN(case_fold_238), case_fold_238 },
+ { __PHYSFS_ARRAYLEN(case_fold_239), case_fold_239 },
+ { __PHYSFS_ARRAYLEN(case_fold_240), case_fold_240 },
+ { __PHYSFS_ARRAYLEN(case_fold_241), case_fold_241 },
+ { __PHYSFS_ARRAYLEN(case_fold_242), case_fold_242 },
+ { __PHYSFS_ARRAYLEN(case_fold_243), case_fold_243 },
+ { __PHYSFS_ARRAYLEN(case_fold_244), case_fold_244 },
+ { __PHYSFS_ARRAYLEN(case_fold_245), case_fold_245 },
+ { __PHYSFS_ARRAYLEN(case_fold_246), case_fold_246 },
+ { __PHYSFS_ARRAYLEN(case_fold_247), case_fold_247 },
+ { __PHYSFS_ARRAYLEN(case_fold_248), case_fold_248 },
+ { __PHYSFS_ARRAYLEN(case_fold_249), case_fold_249 },
+ { __PHYSFS_ARRAYLEN(case_fold_250), case_fold_250 },
+ { __PHYSFS_ARRAYLEN(case_fold_251), case_fold_251 },
+ { __PHYSFS_ARRAYLEN(case_fold_252), case_fold_252 },
+ { __PHYSFS_ARRAYLEN(case_fold_253), case_fold_253 },
+ { __PHYSFS_ARRAYLEN(case_fold_254), case_fold_254 },
+ { __PHYSFS_ARRAYLEN(case_fold_255), case_fold_255 },
+};
+
--- /dev/null
+/*
+ * Internal function/structure declaration. Do NOT include in your
+ * application.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#ifndef _INCLUDE_PHYSFS_INTERNAL_H_
+#define _INCLUDE_PHYSFS_INTERNAL_H_
+
+#ifndef __PHYSICSFS_INTERNAL__
+#error Do not include this header from your applications.
+#endif
+
+#include "physfs.h"
+
+#include <stdlib.h> /* make sure NULL is defined... */
+
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#elif (!defined assert)
+#define assert(x)
+#endif
+
+/* !!! FIXME: remove this when revamping stack allocation code... */
+#ifdef _MSC_VER
+#include <malloc.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Interface for small allocations. If you need a little scratch space for
+ * a throwaway buffer or string, use this. It will make small allocations
+ * on the stack if possible, and use allocator.Malloc() if they are too
+ * large. This helps reduce malloc pressure.
+ * There are some rules, though:
+ * NEVER return a pointer from this, as stack-allocated buffers go away
+ * when your function returns.
+ * NEVER allocate in a loop, as stack-allocated pointers will pile up. Call
+ * a function that uses smallAlloc from your loop, so the allocation can
+ * free each time.
+ * NEVER call smallAlloc with any complex expression (it's a macro that WILL
+ * have side effects...it references the argument multiple times). Use a
+ * variable or a literal.
+ * NEVER free a pointer from this with anything but smallFree. It will not
+ * be a valid pointer to the allocator, regardless of where the memory came
+ * from.
+ * NEVER realloc a pointer from this.
+ * NEVER forget to use smallFree: it may not be a pointer from the stack.
+ * NEVER forget to check for NULL...allocation can fail here, of course!
+ */
+#define __PHYSFS_SMALLALLOCTHRESHOLD 128
+void *__PHYSFS_initSmallAlloc(void *ptr, PHYSFS_uint64 len);
+
+#define __PHYSFS_smallAlloc(bytes) ( \
+ __PHYSFS_initSmallAlloc((((bytes) < __PHYSFS_SMALLALLOCTHRESHOLD) ? \
+ alloca((size_t)((bytes)+1)) : NULL), (bytes)) \
+)
+
+void __PHYSFS_smallFree(void *ptr);
+
+
+/* Use the allocation hooks. */
+#define malloc(x) Do not use malloc() directly.
+#define realloc(x, y) Do not use realloc() directly.
+#define free(x) Do not use free() directly.
+/* !!! FIXME: add alloca check here. */
+
+/* The LANG section. */
+/* please send questions/translations to Ryan: icculus@icculus.org. */
+
+#if (!defined PHYSFS_LANG)
+# define PHYSFS_LANG PHYSFS_LANG_ENGLISH
+#endif
+
+#define PHYSFS_LANG_ENGLISH 1 /* English by Ryan C. Gordon */
+#define PHYSFS_LANG_RUSSIAN_KOI8_R 2 /* Russian by Ed Sinjiashvili */
+#define PHYSFS_LANG_RUSSIAN_CP1251 3 /* Russian by Ed Sinjiashvili */
+#define PHYSFS_LANG_RUSSIAN_CP866 4 /* Russian by Ed Sinjiashvili */
+#define PHYSFS_LANG_RUSSIAN_ISO_8859_5 5 /* Russian by Ed Sinjiashvili */
+#define PHYSFS_LANG_SPANISH 6 /* Spanish by Pedro J. Pérez */
+#define PHYSFS_LANG_FRENCH 7 /* French by Stéphane Peter */
+#define PHYSFS_LANG_GERMAN 8 /* German by Michael Renner */
+#define PHYSFS_LANG_PORTUGUESE_BR 9 /* pt-br by Danny Angelo Carminati Grein */
+
+#if (PHYSFS_LANG == PHYSFS_LANG_ENGLISH)
+ #define DIR_ARCHIVE_DESCRIPTION "Non-archive, direct filesystem I/O"
+ #define GRP_ARCHIVE_DESCRIPTION "Build engine Groupfile format"
+ #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format"
+ #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format"
+ #define QPAK_ARCHIVE_DESCRIPTION "Quake I/II format"
+ #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip compatible"
+ #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format"
+ #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format"
+
+ #define ERR_IS_INITIALIZED "Already initialized"
+ #define ERR_NOT_INITIALIZED "Not initialized"
+ #define ERR_INVALID_ARGUMENT "Invalid argument"
+ #define ERR_FILES_STILL_OPEN "Files still open"
+ #define ERR_NO_DIR_CREATE "Failed to create directories"
+ #define ERR_OUT_OF_MEMORY "Out of memory"
+ #define ERR_NOT_IN_SEARCH_PATH "No such entry in search path"
+ #define ERR_NOT_SUPPORTED "Operation not supported"
+ #define ERR_UNSUPPORTED_ARCHIVE "Archive type unsupported"
+ #define ERR_NOT_A_HANDLE "Not a file handle"
+ #define ERR_INSECURE_FNAME "Insecure filename"
+ #define ERR_SYMLINK_DISALLOWED "Symbolic links are disabled"
+ #define ERR_NO_WRITE_DIR "Write directory is not set"
+ #define ERR_NO_SUCH_FILE "File not found"
+ #define ERR_NO_SUCH_PATH "Path not found"
+ #define ERR_NO_SUCH_VOLUME "Volume not found"
+ #define ERR_PAST_EOF "Past end of file"
+ #define ERR_ARC_IS_READ_ONLY "Archive is read-only"
+ #define ERR_IO_ERROR "I/O error"
+ #define ERR_CANT_SET_WRITE_DIR "Can't set write directory"
+ #define ERR_SYMLINK_LOOP "Infinite symbolic link loop"
+ #define ERR_COMPRESSION "(De)compression error"
+ #define ERR_NOT_IMPLEMENTED "Not implemented"
+ #define ERR_OS_ERROR "Operating system reported error"
+ #define ERR_FILE_EXISTS "File already exists"
+ #define ERR_NOT_A_FILE "Not a file"
+ #define ERR_NOT_A_DIR "Not a directory"
+ #define ERR_NOT_AN_ARCHIVE "Not an archive"
+ #define ERR_CORRUPTED "Corrupted archive"
+ #define ERR_SEEK_OUT_OF_RANGE "Seek out of range"
+ #define ERR_BAD_FILENAME "Bad filename"
+ #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS made a bad system call"
+ #define ERR_ARGV0_IS_NULL "argv0 is NULL"
+ #define ERR_NEED_DICT "need dictionary"
+ #define ERR_DATA_ERROR "data error"
+ #define ERR_MEMORY_ERROR "memory error"
+ #define ERR_BUFFER_ERROR "buffer error"
+ #define ERR_VERSION_ERROR "version error"
+ #define ERR_UNKNOWN_ERROR "unknown error"
+ #define ERR_SEARCHPATH_TRUNC "Search path was truncated"
+ #define ERR_GETMODFN_TRUNC "GetModuleFileName() was truncated"
+ #define ERR_GETMODFN_NO_DIR "GetModuleFileName() had no dir"
+ #define ERR_DISK_FULL "Disk is full"
+ #define ERR_DIRECTORY_FULL "Directory full"
+ #define ERR_MACOS_GENERIC "MacOS reported error (%d)"
+ #define ERR_OS2_GENERIC "OS/2 reported error (%d)"
+ #define ERR_VOL_LOCKED_HW "Volume is locked through hardware"
+ #define ERR_VOL_LOCKED_SW "Volume is locked through software"
+ #define ERR_FILE_LOCKED "File is locked"
+ #define ERR_FILE_OR_DIR_BUSY "File/directory is busy"
+ #define ERR_FILE_ALREADY_OPEN_W "File already open for writing"
+ #define ERR_FILE_ALREADY_OPEN_R "File already open for reading"
+ #define ERR_INVALID_REFNUM "Invalid reference number"
+ #define ERR_GETTING_FILE_POS "Error getting file position"
+ #define ERR_VOLUME_OFFLINE "Volume is offline"
+ #define ERR_PERMISSION_DENIED "Permission denied"
+ #define ERR_VOL_ALREADY_ONLINE "Volume already online"
+ #define ERR_NO_SUCH_DRIVE "No such drive"
+ #define ERR_NOT_MAC_DISK "Not a Macintosh disk"
+ #define ERR_VOL_EXTERNAL_FS "Volume belongs to an external filesystem"
+ #define ERR_PROBLEM_RENAME "Problem during rename"
+ #define ERR_BAD_MASTER_BLOCK "Bad master directory block"
+ #define ERR_CANT_MOVE_FORBIDDEN "Attempt to move forbidden"
+ #define ERR_WRONG_VOL_TYPE "Wrong volume type"
+ #define ERR_SERVER_VOL_LOST "Server volume has been disconnected"
+ #define ERR_FILE_ID_NOT_FOUND "File ID not found"
+ #define ERR_FILE_ID_EXISTS "File ID already exists"
+ #define ERR_SERVER_NO_RESPOND "Server not responding"
+ #define ERR_USER_AUTH_FAILED "User authentication failed"
+ #define ERR_PWORD_EXPIRED "Password has expired on server"
+ #define ERR_ACCESS_DENIED "Access denied"
+ #define ERR_NOT_A_DOS_DISK "Not a DOS disk"
+ #define ERR_SHARING_VIOLATION "Sharing violation"
+ #define ERR_CANNOT_MAKE "Cannot make"
+ #define ERR_DEV_IN_USE "Device already in use"
+ #define ERR_OPEN_FAILED "Open failed"
+ #define ERR_PIPE_BUSY "Pipe is busy"
+ #define ERR_SHARING_BUF_EXCEEDED "Sharing buffer exceeded"
+ #define ERR_TOO_MANY_HANDLES "Too many open handles"
+ #define ERR_SEEK_ERROR "Seek error"
+ #define ERR_DEL_CWD "Trying to delete current working directory"
+ #define ERR_WRITE_PROTECT_ERROR "Write protect error"
+ #define ERR_WRITE_FAULT "Write fault"
+ #define ERR_LOCK_VIOLATION "Lock violation"
+ #define ERR_GEN_FAILURE "General failure"
+ #define ERR_UNCERTAIN_MEDIA "Uncertain media"
+ #define ERR_PROT_VIOLATION "Protection violation"
+ #define ERR_BROKEN_PIPE "Broken pipe"
+
+#elif (PHYSFS_LANG == PHYSFS_LANG_GERMAN)
+ #define DIR_ARCHIVE_DESCRIPTION "Kein Archiv, direkte Ein/Ausgabe in das Dateisystem"
+ #define GRP_ARCHIVE_DESCRIPTION "Build engine Groupfile format"
+ #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format"
+ #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format"
+ #define QPAK_ARCHIVE_DESCRIPTION "Quake I/II format"
+ #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip kompatibel"
+ #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */
+ #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */
+
+ #define ERR_IS_INITIALIZED "Bereits initialisiert"
+ #define ERR_NOT_INITIALIZED "Nicht initialisiert"
+ #define ERR_INVALID_ARGUMENT "Ungültiges Argument"
+ #define ERR_FILES_STILL_OPEN "Dateien noch immer geöffnet"
+ #define ERR_NO_DIR_CREATE "Fehler beim Erzeugen der Verzeichnisse"
+ #define ERR_OUT_OF_MEMORY "Kein Speicher mehr frei"
+ #define ERR_NOT_IN_SEARCH_PATH "Eintrag nicht im Suchpfad enthalten"
+ #define ERR_NOT_SUPPORTED "Befehl nicht unterstützt"
+ #define ERR_UNSUPPORTED_ARCHIVE "Archiv-Typ nicht unterstützt"
+ #define ERR_NOT_A_HANDLE "Ist kein Dateideskriptor"
+ #define ERR_INSECURE_FNAME "Unsicherer Dateiname"
+ #define ERR_SYMLINK_DISALLOWED "Symbolische Verweise deaktiviert"
+ #define ERR_NO_WRITE_DIR "Schreibverzeichnis ist nicht gesetzt"
+ #define ERR_NO_SUCH_FILE "Datei nicht gefunden"
+ #define ERR_NO_SUCH_PATH "Pfad nicht gefunden"
+ #define ERR_NO_SUCH_VOLUME "Datencontainer nicht gefunden"
+ #define ERR_PAST_EOF "Hinter dem Ende der Datei"
+ #define ERR_ARC_IS_READ_ONLY "Archiv ist schreibgeschützt"
+ #define ERR_IO_ERROR "Ein/Ausgabe Fehler"
+ #define ERR_CANT_SET_WRITE_DIR "Kann Schreibverzeichnis nicht setzen"
+ #define ERR_SYMLINK_LOOP "Endlosschleife durch symbolische Verweise"
+ #define ERR_COMPRESSION "(De)Kompressionsfehler"
+ #define ERR_NOT_IMPLEMENTED "Nicht implementiert"
+ #define ERR_OS_ERROR "Betriebssystem meldete Fehler"
+ #define ERR_FILE_EXISTS "Datei existiert bereits"
+ #define ERR_NOT_A_FILE "Ist keine Datei"
+ #define ERR_NOT_A_DIR "Ist kein Verzeichnis"
+ #define ERR_NOT_AN_ARCHIVE "Ist kein Archiv"
+ #define ERR_CORRUPTED "Beschädigtes Archiv"
+ #define ERR_SEEK_OUT_OF_RANGE "Suche war ausserhalb der Reichweite"
+ #define ERR_BAD_FILENAME "Unzulässiger Dateiname"
+ #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS verursachte einen ungültigen Systemaufruf"
+ #define ERR_ARGV0_IS_NULL "argv0 ist NULL"
+ #define ERR_NEED_DICT "brauche Wörterbuch"
+ #define ERR_DATA_ERROR "Datenfehler"
+ #define ERR_MEMORY_ERROR "Speicherfehler"
+ #define ERR_BUFFER_ERROR "Bufferfehler"
+ #define ERR_VERSION_ERROR "Versionskonflikt"
+ #define ERR_UNKNOWN_ERROR "Unbekannter Fehler"
+ #define ERR_SEARCHPATH_TRUNC "Suchpfad war abgeschnitten"
+ #define ERR_GETMODFN_TRUNC "GetModuleFileName() war abgeschnitten"
+ #define ERR_GETMODFN_NO_DIR "GetModuleFileName() bekam kein Verzeichnis"
+ #define ERR_DISK_FULL "Laufwerk ist voll"
+ #define ERR_DIRECTORY_FULL "Verzeichnis ist voll"
+ #define ERR_MACOS_GENERIC "MacOS meldete Fehler (%d)"
+ #define ERR_OS2_GENERIC "OS/2 meldete Fehler (%d)"
+ #define ERR_VOL_LOCKED_HW "Datencontainer ist durch Hardware gesperrt"
+ #define ERR_VOL_LOCKED_SW "Datencontainer ist durch Software gesperrt"
+ #define ERR_FILE_LOCKED "Datei ist gesperrt"
+ #define ERR_FILE_OR_DIR_BUSY "Datei/Verzeichnis ist beschäftigt"
+ #define ERR_FILE_ALREADY_OPEN_W "Datei schon im Schreibmodus geöffnet"
+ #define ERR_FILE_ALREADY_OPEN_R "Datei schon im Lesemodus geöffnet"
+ #define ERR_INVALID_REFNUM "Ungültige Referenznummer"
+ #define ERR_GETTING_FILE_POS "Fehler beim Finden der Dateiposition"
+ #define ERR_VOLUME_OFFLINE "Datencontainer ist offline"
+ #define ERR_PERMISSION_DENIED "Zugriff verweigert"
+ #define ERR_VOL_ALREADY_ONLINE "Datencontainer ist bereits online"
+ #define ERR_NO_SUCH_DRIVE "Laufwerk nicht vorhanden"
+ #define ERR_NOT_MAC_DISK "Ist kein Macintosh Laufwerk"
+ #define ERR_VOL_EXTERNAL_FS "Datencontainer liegt auf einem externen Dateisystem"
+ #define ERR_PROBLEM_RENAME "Fehler beim Umbenennen"
+ #define ERR_BAD_MASTER_BLOCK "Beschädigter Hauptverzeichnisblock"
+ #define ERR_CANT_MOVE_FORBIDDEN "Verschieben nicht erlaubt"
+ #define ERR_WRONG_VOL_TYPE "Falscher Datencontainer-Typ"
+ #define ERR_SERVER_VOL_LOST "Datencontainer am Server wurde getrennt"
+ #define ERR_FILE_ID_NOT_FOUND "Dateikennung nicht gefunden"
+ #define ERR_FILE_ID_EXISTS "Dateikennung existiert bereits"
+ #define ERR_SERVER_NO_RESPOND "Server antwortet nicht"
+ #define ERR_USER_AUTH_FAILED "Benutzerauthentifizierung fehlgeschlagen"
+ #define ERR_PWORD_EXPIRED "Passwort am Server ist abgelaufen"
+ #define ERR_ACCESS_DENIED "Zugriff verweigert"
+ #define ERR_NOT_A_DOS_DISK "Ist kein DOS-Laufwerk"
+ #define ERR_SHARING_VIOLATION "Zugriffsverletzung"
+ #define ERR_CANNOT_MAKE "Kann nicht erzeugen"
+ #define ERR_DEV_IN_USE "Gerät wird bereits benutzt"
+ #define ERR_OPEN_FAILED "Öffnen fehlgeschlagen"
+ #define ERR_PIPE_BUSY "Pipeverbindung ist belegt"
+ #define ERR_SHARING_BUF_EXCEEDED "Zugriffsbuffer überschritten"
+ #define ERR_TOO_MANY_HANDLES "Zu viele offene Dateien"
+ #define ERR_SEEK_ERROR "Fehler beim Suchen"
+ #define ERR_DEL_CWD "Aktuelles Arbeitsverzeichnis darf nicht gelöscht werden"
+ #define ERR_WRITE_PROTECT_ERROR "Schreibschutzfehler"
+ #define ERR_WRITE_FAULT "Schreibfehler"
+ #define ERR_LOCK_VIOLATION "Sperrverletzung"
+ #define ERR_GEN_FAILURE "Allgemeiner Fehler"
+ #define ERR_UNCERTAIN_MEDIA "Unsicheres Medium"
+ #define ERR_PROT_VIOLATION "Schutzverletzung"
+ #define ERR_BROKEN_PIPE "Pipeverbindung unterbrochen"
+
+#elif (PHYSFS_LANG == PHYSFS_LANG_RUSSIAN_KOI8_R)
+ #define DIR_ARCHIVE_DESCRIPTION "îÅ ÁÒÈÉ×, ÎÅÐÏÓÒÅÄÓÔ×ÅÎÎÙÊ ××ÏÄ/×Ù×ÏÄ ÆÁÊÌÏ×ÏÊ ÓÉÓÔÅÍÙ"
+ #define GRP_ARCHIVE_DESCRIPTION "æÏÒÍÁÔ ÇÒÕÐÐÏ×ÏÇÏ ÆÁÊÌÁ Build engine"
+ #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format"
+ #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format"
+ #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip ÓÏ×ÍÅÓÔÉÍÙÊ"
+ #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */
+ #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */
+
+ #define ERR_IS_INITIALIZED "õÖÅ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÎ"
+ #define ERR_NOT_INITIALIZED "îÅ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÎ"
+ #define ERR_INVALID_ARGUMENT "îÅ×ÅÒÎÙÊ ÁÒÇÕÍÅÎÔ"
+ #define ERR_FILES_STILL_OPEN "æÁÊÌÙ ÅÝÅ ÏÔËÒÙÔÙ"
+ #define ERR_NO_DIR_CREATE "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ËÁÔÁÌÏÇÉ"
+ #define ERR_OUT_OF_MEMORY "ëÏÎÞÉÌÁÓØ ÐÁÍÑÔØ"
+ #define ERR_NOT_IN_SEARCH_PATH "îÅÔ ÔÁËÏÇÏ ÜÌÅÍÅÎÔÁ × ÐÕÔÉ ÐÏÉÓËÁ"
+ #define ERR_NOT_SUPPORTED "ïÐÅÒÁÃÉÑ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔÓÑ"
+ #define ERR_UNSUPPORTED_ARCHIVE "áÒÈÉ×Ù ÔÁËÏÇÏ ÔÉÐÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÀÔÓÑ"
+ #define ERR_NOT_A_HANDLE "îÅ ÆÁÊÌÏ×ÙÊ ÄÅÓËÒÉÐÔÏÒ"
+ #define ERR_INSECURE_FNAME "îÅÂÅÚÏÐÁÓÎÏÅ ÉÍÑ ÆÁÊÌÁ"
+ #define ERR_SYMLINK_DISALLOWED "óÉÍ×ÏÌØÎÙÅ ÓÓÙÌËÉ ÏÔËÌÀÞÅÎÙ"
+ #define ERR_NO_WRITE_DIR "ëÁÔÁÌÏÇ ÄÌÑ ÚÁÐÉÓÉ ÎÅ ÕÓÔÁÎÏ×ÌÅÎ"
+ #define ERR_NO_SUCH_FILE "æÁÊÌ ÎÅ ÎÁÊÄÅÎ"
+ #define ERR_NO_SUCH_PATH "ðÕÔØ ÎÅ ÎÁÊÄÅÎ"
+ #define ERR_NO_SUCH_VOLUME "ôÏÍ ÎÅ ÎÁÊÄÅÎ"
+ #define ERR_PAST_EOF "úÁ ËÏÎÃÏÍ ÆÁÊÌÁ"
+ #define ERR_ARC_IS_READ_ONLY "áÒÈÉ× ÔÏÌØËÏ ÄÌÑ ÞÔÅÎÉÑ"
+ #define ERR_IO_ERROR "ïÛÉÂËÁ ××ÏÄÁ/×Ù×ÏÄÁ"
+ #define ERR_CANT_SET_WRITE_DIR "îÅ ÍÏÇÕ ÕÓÔÁÎÏ×ÉÔØ ËÁÔÁÌÏÇ ÄÌÑ ÚÁÐÉÓÉ"
+ #define ERR_SYMLINK_LOOP "âÅÓËÏÎÅÞÎÙÊ ÃÉËÌ ÓÉÍ×ÏÌØÎÏÊ ÓÓÙÌËÉ"
+ #define ERR_COMPRESSION "ïÛÉÂËÁ (òÁÓ)ÐÁËÏ×ËÉ"
+ #define ERR_NOT_IMPLEMENTED "îÅ ÒÅÁÌÉÚÏ×ÁÎÏ"
+ #define ERR_OS_ERROR "ïÐÅÒÁÃÉÏÎÎÁÑ ÓÉÓÔÅÍÁ ÓÏÏÂÝÉÌÁ ÏÛÉÂËÕ"
+ #define ERR_FILE_EXISTS "æÁÊÌ ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ"
+ #define ERR_NOT_A_FILE "îÅ ÆÁÊÌ"
+ #define ERR_NOT_A_DIR "îÅ ËÁÔÁÌÏÇ"
+ #define ERR_NOT_AN_ARCHIVE "îÅ ÁÒÈÉ×"
+ #define ERR_CORRUPTED "ðÏ×ÒÅÖÄÅÎÎÙÊ ÁÒÈÉ×"
+ #define ERR_SEEK_OUT_OF_RANGE "ðÏÚÉÃÉÏÎÉÒÏ×ÁÎÉÅ ÚÁ ÐÒÅÄÅÌÙ"
+ #define ERR_BAD_FILENAME "îÅ×ÅÒÎÏÅ ÉÍÑ ÆÁÊÌÁ"
+ #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS ×ÙÐÏÌÎÉÌÁ ÎÅ×ÅÒÎÙÊ ÓÉÓÔÅÍÎÙÊ ×ÙÚÏ×"
+ #define ERR_ARGV0_IS_NULL "argv0 is NULL"
+ #define ERR_NEED_DICT "ÎÕÖÅÎ ÓÌÏ×ÁÒØ"
+ #define ERR_DATA_ERROR "ÏÛÉÂËÁ ÄÁÎÎÙÈ"
+ #define ERR_MEMORY_ERROR "ÏÛÉÂËÁ ÐÁÍÑÔÉ"
+ #define ERR_BUFFER_ERROR "ÏÛÉÂËÁ ÂÕÆÅÒÁ"
+ #define ERR_VERSION_ERROR "ÏÛÉÂËÁ ×ÅÒÓÉÉ"
+ #define ERR_UNKNOWN_ERROR "ÎÅÉÚ×ÅÓÔÎÁÑ ÏÛÉÂËÁ"
+ #define ERR_SEARCHPATH_TRUNC "ðÕÔØ ÐÏÉÓËÁ ÏÂÒÅÚÁÎ"
+ #define ERR_GETMODFN_TRUNC "GetModuleFileName() ÏÂÒÅÚÁÎ"
+ #define ERR_GETMODFN_NO_DIR "GetModuleFileName() ÎÅ ÐÏÌÕÞÉÌ ËÁÔÁÌÏÇ"
+ #define ERR_DISK_FULL "äÉÓË ÐÏÌÏÎ"
+ #define ERR_DIRECTORY_FULL "ëÁÔÁÌÏÇ ÐÏÌÏÎ"
+ #define ERR_MACOS_GENERIC "MacOS ÓÏÏÂÝÉÌÁ ÏÛÉÂËÕ (%d)"
+ #define ERR_OS2_GENERIC "OS/2 ÓÏÏÂÝÉÌÁ ÏÛÉÂËÕ (%d)"
+ #define ERR_VOL_LOCKED_HW "ôÏÍ ÂÌÏËÉÒÏ×ÁÎ ÁÐÐÁÒÁÔÎÏ"
+ #define ERR_VOL_LOCKED_SW "ôÏÍ ÂÌÏËÉÒÏ×ÁÎ ÐÒÏÇÒÁÍÍÎÏ"
+ #define ERR_FILE_LOCKED "æÁÊÌ ÚÁÂÌÏËÉÒÏ×ÁÎ"
+ #define ERR_FILE_OR_DIR_BUSY "æÁÊÌ/ËÁÔÁÌÏÇ ÚÁÎÑÔ"
+ #define ERR_FILE_ALREADY_OPEN_W "æÁÊÌ ÕÖÅ ÏÔËÒÙÔ ÎÁ ÚÁÐÉÓØ"
+ #define ERR_FILE_ALREADY_OPEN_R "æÁÊÌ ÕÖÅ ÏÔËÒÙÔ ÎÁ ÞÔÅÎÉÅ"
+ #define ERR_INVALID_REFNUM "îÅ×ÅÒÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÓÓÙÌÏË"
+ #define ERR_GETTING_FILE_POS "ïÛÉÂËÁ ÐÒÉ ÐÏÌÕÞÅÎÉÉ ÐÏÚÉÃÉÉ ÆÁÊÌÁ"
+ #define ERR_VOLUME_OFFLINE "ôÏÍ ÏÔÓÏÅÄÉÎÅÎ"
+ #define ERR_PERMISSION_DENIED "ïÔËÁÚÁÎÏ × ÒÁÚÒÅÛÅÎÉÉ"
+ #define ERR_VOL_ALREADY_ONLINE "ôÏÍ ÕÖÅ ÐÏÄÓÏÅÄÉÎÅÎ"
+ #define ERR_NO_SUCH_DRIVE "îÅÔ ÔÁËÏÇÏ ÄÉÓËÁ"
+ #define ERR_NOT_MAC_DISK "îÅ ÄÉÓË Macintosh"
+ #define ERR_VOL_EXTERNAL_FS "ôÏÍ ÐÒÉÎÁÄÌÅÖÉÔ ×ÎÅÛÎÅÊ ÆÁÊÌÏ×ÏÊ ÓÉÓÔÅÍÅ"
+ #define ERR_PROBLEM_RENAME "ðÒÏÂÌÅÍÁ ÐÒÉ ÐÅÒÅÉÍÅÎÏ×ÁÎÉÉ"
+ #define ERR_BAD_MASTER_BLOCK "ðÌÏÈÏÊ ÇÌÁ×ÎÙÊ ÂÌÏË ËÁÔÁÌÏÇÁ"
+ #define ERR_CANT_MOVE_FORBIDDEN "ðÏÐÙÔËÁ ÐÅÒÅÍÅÓÔÉÔØ ÚÁÐÒÅÝÅÎÁ"
+ #define ERR_WRONG_VOL_TYPE "îÅ×ÅÒÎÙÊ ÔÉÐ ÔÏÍÁ"
+ #define ERR_SERVER_VOL_LOST "óÅÒ×ÅÒÎÙÊ ÔÏÍ ÂÙÌ ÏÔÓÏÅÄÉÎÅÎ"
+ #define ERR_FILE_ID_NOT_FOUND "éÄÅÎÔÉÆÉËÁÔÏÒ ÆÁÊÌÁ ÎÅ ÎÁÊÄÅÎ"
+ #define ERR_FILE_ID_EXISTS "éÄÅÎÔÉÆÉËÁÔÏÒ ÆÁÊÌÁ ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ"
+ #define ERR_SERVER_NO_RESPOND "óÅÒ×ÅÒ ÎÅ ÏÔ×ÅÞÁÅÔ"
+ #define ERR_USER_AUTH_FAILED "éÄÅÎÔÉÆÉËÁÃÉÑ ÐÏÌØÚÏ×ÁÔÅÌÑ ÎÅ ÕÄÁÌÁÓØ"
+ #define ERR_PWORD_EXPIRED "ðÁÒÏÌØ ÎÁ ÓÅÒ×ÅÒÅ ÕÓÔÁÒÅÌ"
+ #define ERR_ACCESS_DENIED "ïÔËÁÚÁÎÏ × ÄÏÓÔÕÐÅ"
+ #define ERR_NOT_A_DOS_DISK "îÅ ÄÉÓË DOS"
+ #define ERR_SHARING_VIOLATION "îÁÒÕÛÅÎÉÅ ÓÏ×ÍÅÓÔÎÏÇÏ ÄÏÓÔÕÐÁ"
+ #define ERR_CANNOT_MAKE "îÅ ÍÏÇÕ ÓÏÂÒÁÔØ"
+ #define ERR_DEV_IN_USE "õÓÔÒÏÊÓÔ×Ï ÕÖÅ ÉÓÐÏÌØÚÕÅÔÓÑ"
+ #define ERR_OPEN_FAILED "ïÔËÒÙÔÉÅ ÎÅ ÕÄÁÌÏÓØ"
+ #define ERR_PIPE_BUSY "ëÏÎ×ÅÊÅÒ ÚÁÎÑÔ"
+ #define ERR_SHARING_BUF_EXCEEDED "òÁÚÄÅÌÑÅÍÙÊ ÂÕÆÅÒ ÐÅÒÅÐÏÌÎÅÎ"
+ #define ERR_TOO_MANY_HANDLES "óÌÉÛËÏÍ ÍÎÏÇÏ ÏÔËÒÙÔÙÈ ÄÅÓËÒÉÐÔÏÒÏ×"
+ #define ERR_SEEK_ERROR "ïÛÉÂËÁ ÐÏÚÉÃÉÏÎÉÒÏ×ÁÎÉÑ"
+ #define ERR_DEL_CWD "ðÏÐÙÔËÁ ÕÄÁÌÉÔØ ÔÅËÕÝÉÊ ÒÁÂÏÞÉÊ ËÁÔÁÌÏÇ"
+ #define ERR_WRITE_PROTECT_ERROR "ïÛÉÂËÁ ÚÁÝÉÔÙ ÚÁÐÉÓÉ"
+ #define ERR_WRITE_FAULT "ïÛÉÂËÁ ÚÁÐÉÓÉ"
+ #define ERR_LOCK_VIOLATION "îÁÒÕÛÅÎÉÅ ÂÌÏËÉÒÏ×ËÉ"
+ #define ERR_GEN_FAILURE "ïÂÝÉÊ ÓÂÏÊ"
+ #define ERR_UNCERTAIN_MEDIA "îÅÏÐÒÅÄÅÌÅÎÎÙÊ ÎÏÓÉÔÅÌØ"
+ #define ERR_PROT_VIOLATION "îÁÒÕÛÅÎÉÅ ÚÁÝÉÔÙ"
+ #define ERR_BROKEN_PIPE "óÌÏÍÁÎÎÙÊ ËÏÎ×ÅÊÅÒ"
+
+#elif (PHYSFS_LANG == PHYSFS_LANG_RUSSIAN_CP1251)
+ #define DIR_ARCHIVE_DESCRIPTION "Íå àðõèâ, íåïîñðåäñòâåííûé ââîä/âûâîä ôàéëîâîé ñèñòåìû"
+ #define GRP_ARCHIVE_DESCRIPTION "Ôîðìàò ãðóïïîâîãî ôàéëà Build engine"
+ #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format"
+ #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format"
+ #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip ñîâìåñòèìûé"
+ #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */
+ #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */
+
+ #define ERR_IS_INITIALIZED "Óæå èíèöèàëèçèðîâàí"
+ #define ERR_NOT_INITIALIZED "Íå èíèöèàëèçèðîâàí"
+ #define ERR_INVALID_ARGUMENT "Íåâåðíûé àðãóìåíò"
+ #define ERR_FILES_STILL_OPEN "Ôàéëû åùå îòêðûòû"
+ #define ERR_NO_DIR_CREATE "Íå ìîãó ñîçäàòü êàòàëîãè"
+ #define ERR_OUT_OF_MEMORY "Êîí÷èëàñü ïàìÿòü"
+ #define ERR_NOT_IN_SEARCH_PATH "Íåò òàêîãî ýëåìåíòà â ïóòè ïîèñêà"
+ #define ERR_NOT_SUPPORTED "Îïåðàöèÿ íå ïîääåðæèâàåòñÿ"
+ #define ERR_UNSUPPORTED_ARCHIVE "Àðõèâû òàêîãî òèïà íå ïîääåðæèâàþòñÿ"
+ #define ERR_NOT_A_HANDLE "Íå ôàéëîâûé äåñêðèïòîð"
+ #define ERR_INSECURE_FNAME "Íåáåçîïàñíîå èìÿ ôàéëà"
+ #define ERR_SYMLINK_DISALLOWED "Ñèìâîëüíûå ññûëêè îòêëþ÷åíû"
+ #define ERR_NO_WRITE_DIR "Êàòàëîã äëÿ çàïèñè íå óñòàíîâëåí"
+ #define ERR_NO_SUCH_FILE "Ôàéë íå íàéäåí"
+ #define ERR_NO_SUCH_PATH "Ïóòü íå íàéäåí"
+ #define ERR_NO_SUCH_VOLUME "Òîì íå íàéäåí"
+ #define ERR_PAST_EOF "Çà êîíöîì ôàéëà"
+ #define ERR_ARC_IS_READ_ONLY "Àðõèâ òîëüêî äëÿ ÷òåíèÿ"
+ #define ERR_IO_ERROR "Îøèáêà ââîäà/âûâîäà"
+ #define ERR_CANT_SET_WRITE_DIR "Íå ìîãó óñòàíîâèòü êàòàëîã äëÿ çàïèñè"
+ #define ERR_SYMLINK_LOOP "Áåñêîíå÷íûé öèêë ñèìâîëüíîé ññûëêè"
+ #define ERR_COMPRESSION "Îøèáêà (Ðàñ)ïàêîâêè"
+ #define ERR_NOT_IMPLEMENTED "Íå ðåàëèçîâàíî"
+ #define ERR_OS_ERROR "Îïåðàöèîííàÿ ñèñòåìà ñîîáùèëà îøèáêó"
+ #define ERR_FILE_EXISTS "Ôàéë óæå ñóùåñòâóåò"
+ #define ERR_NOT_A_FILE "Íå ôàéë"
+ #define ERR_NOT_A_DIR "Íå êàòàëîã"
+ #define ERR_NOT_AN_ARCHIVE "Íå àðõèâ"
+ #define ERR_CORRUPTED "Ïîâðåæäåííûé àðõèâ"
+ #define ERR_SEEK_OUT_OF_RANGE "Ïîçèöèîíèðîâàíèå çà ïðåäåëû"
+ #define ERR_BAD_FILENAME "Íåâåðíîå èìÿ ôàéëà"
+ #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS âûïîëíèëà íåâåðíûé ñèñòåìíûé âûçîâ"
+ #define ERR_ARGV0_IS_NULL "argv0 is NULL"
+ #define ERR_NEED_DICT "íóæåí ñëîâàðü"
+ #define ERR_DATA_ERROR "îøèáêà äàííûõ"
+ #define ERR_MEMORY_ERROR "îøèáêà ïàìÿòè"
+ #define ERR_BUFFER_ERROR "îøèáêà áóôåðà"
+ #define ERR_VERSION_ERROR "îøèáêà âåðñèè"
+ #define ERR_UNKNOWN_ERROR "íåèçâåñòíàÿ îøèáêà"
+ #define ERR_SEARCHPATH_TRUNC "Ïóòü ïîèñêà îáðåçàí"
+ #define ERR_GETMODFN_TRUNC "GetModuleFileName() îáðåçàí"
+ #define ERR_GETMODFN_NO_DIR "GetModuleFileName() íå ïîëó÷èë êàòàëîã"
+ #define ERR_DISK_FULL "Äèñê ïîëîí"
+ #define ERR_DIRECTORY_FULL "Êàòàëîã ïîëîí"
+ #define ERR_MACOS_GENERIC "MacOS ñîîáùèëà îøèáêó (%d)"
+ #define ERR_OS2_GENERIC "OS/2 ñîîáùèëà îøèáêó (%d)"
+ #define ERR_VOL_LOCKED_HW "Òîì áëîêèðîâàí àïïàðàòíî"
+ #define ERR_VOL_LOCKED_SW "Òîì áëîêèðîâàí ïðîãðàììíî"
+ #define ERR_FILE_LOCKED "Ôàéë çàáëîêèðîâàí"
+ #define ERR_FILE_OR_DIR_BUSY "Ôàéë/êàòàëîã çàíÿò"
+ #define ERR_FILE_ALREADY_OPEN_W "Ôàéë óæå îòêðûò íà çàïèñü"
+ #define ERR_FILE_ALREADY_OPEN_R "Ôàéë óæå îòêðûò íà ÷òåíèå"
+ #define ERR_INVALID_REFNUM "Íåâåðíîå êîëè÷åñòâî ññûëîê"
+ #define ERR_GETTING_FILE_POS "Îøèáêà ïðè ïîëó÷åíèè ïîçèöèè ôàéëà"
+ #define ERR_VOLUME_OFFLINE "Òîì îòñîåäèíåí"
+ #define ERR_PERMISSION_DENIED "Îòêàçàíî â ðàçðåøåíèè"
+ #define ERR_VOL_ALREADY_ONLINE "Òîì óæå ïîäñîåäèíåí"
+ #define ERR_NO_SUCH_DRIVE "Íåò òàêîãî äèñêà"
+ #define ERR_NOT_MAC_DISK "Íå äèñê Macintosh"
+ #define ERR_VOL_EXTERNAL_FS "Òîì ïðèíàäëåæèò âíåøíåé ôàéëîâîé ñèñòåìå"
+ #define ERR_PROBLEM_RENAME "Ïðîáëåìà ïðè ïåðåèìåíîâàíèè"
+ #define ERR_BAD_MASTER_BLOCK "Ïëîõîé ãëàâíûé áëîê êàòàëîãà"
+ #define ERR_CANT_MOVE_FORBIDDEN "Ïîïûòêà ïåðåìåñòèòü çàïðåùåíà"
+ #define ERR_WRONG_VOL_TYPE "Íåâåðíûé òèï òîìà"
+ #define ERR_SERVER_VOL_LOST "Ñåðâåðíûé òîì áûë îòñîåäèíåí"
+ #define ERR_FILE_ID_NOT_FOUND "Èäåíòèôèêàòîð ôàéëà íå íàéäåí"
+ #define ERR_FILE_ID_EXISTS "Èäåíòèôèêàòîð ôàéëà óæå ñóùåñòâóåò"
+ #define ERR_SERVER_NO_RESPOND "Ñåðâåð íå îòâå÷àåò"
+ #define ERR_USER_AUTH_FAILED "Èäåíòèôèêàöèÿ ïîëüçîâàòåëÿ íå óäàëàñü"
+ #define ERR_PWORD_EXPIRED "Ïàðîëü íà ñåðâåðå óñòàðåë"
+ #define ERR_ACCESS_DENIED "Îòêàçàíî â äîñòóïå"
+ #define ERR_NOT_A_DOS_DISK "Íå äèñê DOS"
+ #define ERR_SHARING_VIOLATION "Íàðóøåíèå ñîâìåñòíîãî äîñòóïà"
+ #define ERR_CANNOT_MAKE "Íå ìîãó ñîáðàòü"
+ #define ERR_DEV_IN_USE "Óñòðîéñòâî óæå èñïîëüçóåòñÿ"
+ #define ERR_OPEN_FAILED "Îòêðûòèå íå óäàëîñü"
+ #define ERR_PIPE_BUSY "Êîíâåéåð çàíÿò"
+ #define ERR_SHARING_BUF_EXCEEDED "Ðàçäåëÿåìûé áóôåð ïåðåïîëíåí"
+ #define ERR_TOO_MANY_HANDLES "Ñëèøêîì ìíîãî îòêðûòûõ äåñêðèïòîðîâ"
+ #define ERR_SEEK_ERROR "Îøèáêà ïîçèöèîíèðîâàíèÿ"
+ #define ERR_DEL_CWD "Ïîïûòêà óäàëèòü òåêóùèé ðàáî÷èé êàòàëîã"
+ #define ERR_WRITE_PROTECT_ERROR "Îøèáêà çàùèòû çàïèñè"
+ #define ERR_WRITE_FAULT "Îøèáêà çàïèñè"
+ #define ERR_LOCK_VIOLATION "Íàðóøåíèå áëîêèðîâêè"
+ #define ERR_GEN_FAILURE "Îáùèé ñáîé"
+ #define ERR_UNCERTAIN_MEDIA "Íåîïðåäåëåííûé íîñèòåëü"
+ #define ERR_PROT_VIOLATION "Íàðóøåíèå çàùèòû"
+ #define ERR_BROKEN_PIPE "Ñëîìàííûé êîíâåéåð"
+
+#elif (PHYSFS_LANG == PHYSFS_LANG_RUSSIAN_CP866)
+ #define DIR_ARCHIVE_DESCRIPTION "\8d¥ à娢, ¥¯®á।áâ¢¥ë© ¢¢®¤/¢ë¢®¤ ä ©«®¢®© á¨á⥬ë"
+ #define GRP_ARCHIVE_DESCRIPTION "\94®à¬ â £à㯯®¢®£® ä ©« Build engine"
+ #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format"
+ #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format"
+ #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip ᮢ¬¥á⨬ë©"
+ #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */
+ #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */
+
+ #define ERR_IS_INITIALIZED "\93¦¥ ¨¨æ¨ «¨§¨à®¢ "
+ #define ERR_NOT_INITIALIZED "\8d¥ ¨¨æ¨ «¨§¨à®¢ "
+ #define ERR_INVALID_ARGUMENT "\8d¥¢¥àë© à£ã¬¥â"
+ #define ERR_FILES_STILL_OPEN "\94 ©«ë ¥é¥ ®âªàëâë"
+ #define ERR_NO_DIR_CREATE "\8d¥ ¬®£ã ᮧ¤ âì ª â «®£¨"
+ #define ERR_OUT_OF_MEMORY "\8a®ç¨« áì ¯ ¬ïâì"
+ #define ERR_NOT_IN_SEARCH_PATH "\8d¥â â ª®£® í«¥¬¥â ¢ ¯ã⨠¯®¨áª "
+ #define ERR_NOT_SUPPORTED "\8e¯¥à æ¨ï ¥ ¯®¤¤¥à¦¨¢ ¥âáï"
+ #define ERR_UNSUPPORTED_ARCHIVE "\80à娢ë â ª®£® ⨯ ¥ ¯®¤¤¥à¦¨¢ îâáï"
+ #define ERR_NOT_A_HANDLE "\8d¥ ä ©«®¢ë© ¤¥áªà¨¯â®à"
+ #define ERR_INSECURE_FNAME "\8d¥¡¥§®¯ ᮥ ¨¬ï ä ©« "
+ #define ERR_SYMLINK_DISALLOWED "\91¨¬¢®«ìë¥ áá뫪¨ ®âª«îç¥ë"
+ #define ERR_NO_WRITE_DIR "\8a â «®£ ¤«ï § ¯¨á¨ ¥ ãáâ ®¢«¥"
+ #define ERR_NO_SUCH_FILE "\94 ©« ¥ ©¤¥"
+ #define ERR_NO_SUCH_PATH "\8fãâì ¥ ©¤¥"
+ #define ERR_NO_SUCH_VOLUME "\92®¬ ¥ ©¤¥"
+ #define ERR_PAST_EOF "\87 ª®æ®¬ ä ©« "
+ #define ERR_ARC_IS_READ_ONLY "\80à娢 ⮫쪮 ¤«ï ç⥨ï"
+ #define ERR_IO_ERROR "\8e訡ª ¢¢®¤ /¢ë¢®¤ "
+ #define ERR_CANT_SET_WRITE_DIR "\8d¥ ¬®£ã ãáâ ®¢¨âì ª â «®£ ¤«ï § ¯¨á¨"
+ #define ERR_SYMLINK_LOOP "\81¥áª®¥çë© æ¨ª« ᨬ¢®«ì®© áá뫪¨"
+ #define ERR_COMPRESSION "\8e訡ª (\90 á)¯ ª®¢ª¨"
+ #define ERR_NOT_IMPLEMENTED "\8d¥ ॠ«¨§®¢ ®"
+ #define ERR_OS_ERROR "\8e¯¥à 樮 ï á¨á⥬ á®®¡é¨« ®è¨¡ªã"
+ #define ERR_FILE_EXISTS "\94 ©« 㦥 áãé¥áâ¢ã¥â"
+ #define ERR_NOT_A_FILE "\8d¥ ä ©«"
+ #define ERR_NOT_A_DIR "\8d¥ ª â «®£"
+ #define ERR_NOT_AN_ARCHIVE "\8d¥ à娢"
+ #define ERR_CORRUPTED "\8f®¢à¥¦¤¥ë© à娢"
+ #define ERR_SEEK_OUT_OF_RANGE "\8f®§¨æ¨®¨à®¢ ¨¥ § ¯à¥¤¥«ë"
+ #define ERR_BAD_FILENAME "\8d¥¢¥à®¥ ¨¬ï ä ©« "
+ #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS ¢ë¯®«¨« ¥¢¥àë© á¨áâ¥¬ë© ¢ë§®¢"
+ #define ERR_ARGV0_IS_NULL "argv0 is NULL"
+ #define ERR_NEED_DICT "㦥 á«®¢ àì"
+ #define ERR_DATA_ERROR "®è¨¡ª ¤ ëå"
+ #define ERR_MEMORY_ERROR "®è¨¡ª ¯ ¬ïâ¨"
+ #define ERR_BUFFER_ERROR "®è¨¡ª ¡ãä¥à "
+ #define ERR_VERSION_ERROR "®è¨¡ª ¢¥àᨨ"
+ #define ERR_UNKNOWN_ERROR "¥¨§¢¥áâ ï ®è¨¡ª "
+ #define ERR_SEARCHPATH_TRUNC "\8fãâì ¯®¨áª ®¡à¥§ "
+ #define ERR_GETMODFN_TRUNC "GetModuleFileName() ®¡à¥§ "
+ #define ERR_GETMODFN_NO_DIR "GetModuleFileName() ¥ ¯®«ã稫 ª â «®£"
+ #define ERR_DISK_FULL "\84¨áª ¯®«®"
+ #define ERR_DIRECTORY_FULL "\8a â «®£ ¯®«®"
+ #define ERR_MACOS_GENERIC "MacOS á®®¡é¨« ®è¨¡ªã (%d)"
+ #define ERR_OS2_GENERIC "OS/2 á®®¡é¨« ®è¨¡ªã (%d)"
+ #define ERR_VOL_LOCKED_HW "\92®¬ ¡«®ª¨à®¢ ¯¯ à â®"
+ #define ERR_VOL_LOCKED_SW "\92®¬ ¡«®ª¨à®¢ ¯à®£à ¬¬®"
+ #define ERR_FILE_LOCKED "\94 ©« § ¡«®ª¨à®¢ "
+ #define ERR_FILE_OR_DIR_BUSY "\94 ©«/ª â «®£ § ïâ"
+ #define ERR_FILE_ALREADY_OPEN_W "\94 ©« 㦥 ®âªàëâ § ¯¨áì"
+ #define ERR_FILE_ALREADY_OPEN_R "\94 ©« 㦥 ®âªàëâ ç⥨¥"
+ #define ERR_INVALID_REFNUM "\8d¥¢¥à®¥ ª®«¨ç¥á⢮ ááë«®ª"
+ #define ERR_GETTING_FILE_POS "\8e訡ª ¯à¨ ¯®«ã票¨ ¯®§¨æ¨¨ ä ©« "
+ #define ERR_VOLUME_OFFLINE "\92®¬ ®âᮥ¤¨¥"
+ #define ERR_PERMISSION_DENIED "\8e⪠§ ® ¢ à §à¥è¥¨¨"
+ #define ERR_VOL_ALREADY_ONLINE "\92®¬ 㦥 ¯®¤á®¥¤¨¥"
+ #define ERR_NO_SUCH_DRIVE "\8d¥â â ª®£® ¤¨áª "
+ #define ERR_NOT_MAC_DISK "\8d¥ ¤¨áª Macintosh"
+ #define ERR_VOL_EXTERNAL_FS "\92®¬ ¯à¨ ¤«¥¦¨â ¢¥è¥© ä ©«®¢®© á¨á⥬¥"
+ #define ERR_PROBLEM_RENAME "\8f஡«¥¬ ¯à¨ ¯¥à¥¨¬¥®¢ ¨¨"
+ #define ERR_BAD_MASTER_BLOCK "\8f«®å®© £« ¢ë© ¡«®ª ª â «®£ "
+ #define ERR_CANT_MOVE_FORBIDDEN "\8f®¯ë⪠¯¥à¥¬¥áâ¨âì § ¯à¥é¥ "
+ #define ERR_WRONG_VOL_TYPE "\8d¥¢¥àë© â¨¯ ⮬ "
+ #define ERR_SERVER_VOL_LOST "\91¥à¢¥àë© â®¬ ¡ë« ®âᮥ¤¨¥"
+ #define ERR_FILE_ID_NOT_FOUND "\88¤¥â¨ä¨ª â®à ä ©« ¥ ©¤¥"
+ #define ERR_FILE_ID_EXISTS "\88¤¥â¨ä¨ª â®à ä ©« 㦥 áãé¥áâ¢ã¥â"
+ #define ERR_SERVER_NO_RESPOND "\91¥à¢¥à ¥ ®â¢¥ç ¥â"
+ #define ERR_USER_AUTH_FAILED "\88¤¥â¨ä¨ª æ¨ï ¯®«ì§®¢ â¥«ï ¥ 㤠« áì"
+ #define ERR_PWORD_EXPIRED "\8f ஫ì á¥à¢¥à¥ ãáâ ५"
+ #define ERR_ACCESS_DENIED "\8e⪠§ ® ¢ ¤®áâ㯥"
+ #define ERR_NOT_A_DOS_DISK "\8d¥ ¤¨áª DOS"
+ #define ERR_SHARING_VIOLATION "\8d àã襨¥ ᮢ¬¥á⮣® ¤®áâ㯠"
+ #define ERR_CANNOT_MAKE "\8d¥ ¬®£ã ᮡà âì"
+ #define ERR_DEV_IN_USE "\93áâனá⢮ 㦥 ¨á¯®«ì§ã¥âáï"
+ #define ERR_OPEN_FAILED "\8eâªàë⨥ ¥ 㤠«®áì"
+ #define ERR_PIPE_BUSY "\8a®¢¥©¥à § ïâ"
+ #define ERR_SHARING_BUF_EXCEEDED "\90 §¤¥«ï¥¬ë© ¡ãä¥à ¯¥à¥¯®«¥"
+ #define ERR_TOO_MANY_HANDLES "\91«¨èª®¬ ¬®£® ®âªàëâëå ¤¥áªà¨¯â®à®¢"
+ #define ERR_SEEK_ERROR "\8e訡ª ¯®§¨æ¨®¨à®¢ ¨ï"
+ #define ERR_DEL_CWD "\8f®¯ë⪠㤠«¨âì ⥪ã騩 à ¡®ç¨© ª â «®£"
+ #define ERR_WRITE_PROTECT_ERROR "\8e訡ª § é¨âë § ¯¨á¨"
+ #define ERR_WRITE_FAULT "\8e訡ª § ¯¨á¨"
+ #define ERR_LOCK_VIOLATION "\8d àã襨¥ ¡«®ª¨à®¢ª¨"
+ #define ERR_GEN_FAILURE "\8e¡é¨© á¡®©"
+ #define ERR_UNCERTAIN_MEDIA "\8d¥®¯à¥¤¥«¥ë© ®á¨â¥«ì"
+ #define ERR_PROT_VIOLATION "\8d àã襨¥ § é¨âë"
+ #define ERR_BROKEN_PIPE "\91«®¬ ë© ª®¢¥©¥à"
+
+#elif (PHYSFS_LANG == PHYSFS_LANG_RUSSIAN_ISO_8859_5)
+ #define DIR_ARCHIVE_DESCRIPTION "½Õ ÐàåØÒ, ÝÕßÞáàÕÔáâÒÕÝÝëÙ ÒÒÞÔ/ÒëÒÞÔ äÐÙÛÞÒÞÙ áØáâÕÜë"
+ #define GRP_ARCHIVE_DESCRIPTION "ÄÞàÜÐâ ÓàãßßÞÒÞÓÞ äÐÙÛÐ Build engine"
+ #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format"
+ #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format"
+ #define ZIP_ARCHIVE_DESCRIPTION "PkZip/WinZip/Info-Zip áÞÒÜÕáâØÜëÙ"
+ #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */
+ #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */
+
+ #define ERR_IS_INITIALIZED "ÃÖÕ ØÝØæØÐÛØ×ØàÞÒÐÝ"
+ #define ERR_NOT_INITIALIZED "½Õ ØÝØæØÐÛØ×ØàÞÒÐÝ"
+ #define ERR_INVALID_ARGUMENT "½ÕÒÕàÝëÙ ÐàÓãÜÕÝâ"
+ #define ERR_FILES_STILL_OPEN "ÄÐÙÛë ÕéÕ ÞâÚàëâë"
+ #define ERR_NO_DIR_CREATE "½Õ ÜÞÓã áÞ×ÔÐâì ÚÐâÐÛÞÓØ"
+ #define ERR_OUT_OF_MEMORY "ºÞÝçØÛÐáì ßÐÜïâì"
+ #define ERR_NOT_IN_SEARCH_PATH "½Õâ âÐÚÞÓÞ íÛÕÜÕÝâÐ Ò ßãâØ ßÞØáÚÐ"
+ #define ERR_NOT_SUPPORTED "¾ßÕàÐæØï ÝÕ ßÞÔÔÕàÖØÒÐÕâáï"
+ #define ERR_UNSUPPORTED_ARCHIVE "°àåØÒë âÐÚÞÓÞ âØßÐ ÝÕ ßÞÔÔÕàÖØÒÐîâáï"
+ #define ERR_NOT_A_HANDLE "½Õ äÐÙÛÞÒëÙ ÔÕáÚàØßâÞà"
+ #define ERR_INSECURE_FNAME "½ÕÑÕ×ÞßÐáÝÞÕ ØÜï äÐÙÛÐ"
+ #define ERR_SYMLINK_DISALLOWED "ÁØÜÒÞÛìÝëÕ ááëÛÚØ ÞâÚÛîçÕÝë"
+ #define ERR_NO_WRITE_DIR "ºÐâÐÛÞÓ ÔÛï ×ÐßØáØ ÝÕ ãáâÐÝÞÒÛÕÝ"
+ #define ERR_NO_SUCH_FILE "ÄÐÙÛ ÝÕ ÝÐÙÔÕÝ"
+ #define ERR_NO_SUCH_PATH "¿ãâì ÝÕ ÝÐÙÔÕÝ"
+ #define ERR_NO_SUCH_VOLUME "ÂÞÜ ÝÕ ÝÐÙÔÕÝ"
+ #define ERR_PAST_EOF "·Ð ÚÞÝæÞÜ äÐÙÛÐ"
+ #define ERR_ARC_IS_READ_ONLY "°àåØÒ âÞÛìÚÞ ÔÛï çâÕÝØï"
+ #define ERR_IO_ERROR "¾èØÑÚÐ ÒÒÞÔÐ/ÒëÒÞÔÐ"
+ #define ERR_CANT_SET_WRITE_DIR "½Õ ÜÞÓã ãáâÐÝÞÒØâì ÚÐâÐÛÞÓ ÔÛï ×ÐßØáØ"
+ #define ERR_SYMLINK_LOOP "±ÕáÚÞÝÕçÝëÙ æØÚÛ áØÜÒÞÛìÝÞÙ ááëÛÚØ"
+ #define ERR_COMPRESSION "¾èØÑÚÐ (ÀÐá)ßÐÚÞÒÚØ"
+ #define ERR_NOT_IMPLEMENTED "½Õ àÕÐÛØ×ÞÒÐÝÞ"
+ #define ERR_OS_ERROR "¾ßÕàÐæØÞÝÝÐï áØáâÕÜÐ áÞÞÑéØÛÐ ÞèØÑÚã"
+ #define ERR_FILE_EXISTS "ÄÐÙÛ ãÖÕ áãéÕáâÒãÕâ"
+ #define ERR_NOT_A_FILE "½Õ äÐÙÛ"
+ #define ERR_NOT_A_DIR "½Õ ÚÐâÐÛÞÓ"
+ #define ERR_NOT_AN_ARCHIVE "½Õ ÐàåØÒ"
+ #define ERR_CORRUPTED "¿ÞÒàÕÖÔÕÝÝëÙ ÐàåØÒ"
+ #define ERR_SEEK_OUT_OF_RANGE "¿Þ×ØæØÞÝØàÞÒÐÝØÕ ×Ð ßàÕÔÕÛë"
+ #define ERR_BAD_FILENAME "½ÕÒÕàÝÞÕ ØÜï äÐÙÛÐ"
+ #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS ÒëßÞÛÝØÛÐ ÝÕÒÕàÝëÙ áØáâÕÜÝëÙ Òë×ÞÒ"
+ #define ERR_ARGV0_IS_NULL "argv0 is NULL"
+ #define ERR_NEED_DICT "ÝãÖÕÝ áÛÞÒÐàì"
+ #define ERR_DATA_ERROR "ÞèØÑÚÐ ÔÐÝÝëå"
+ #define ERR_MEMORY_ERROR "ÞèØÑÚÐ ßÐÜïâØ"
+ #define ERR_BUFFER_ERROR "ÞèØÑÚÐ ÑãäÕàÐ"
+ #define ERR_VERSION_ERROR "ÞèØÑÚÐ ÒÕàáØØ"
+ #define ERR_UNKNOWN_ERROR "ÝÕØ×ÒÕáâÝÐï ÞèØÑÚÐ"
+ #define ERR_SEARCHPATH_TRUNC "¿ãâì ßÞØáÚÐ ÞÑàÕ×ÐÝ"
+ #define ERR_GETMODFN_TRUNC "GetModuleFileName() ÞÑàÕ×ÐÝ"
+ #define ERR_GETMODFN_NO_DIR "GetModuleFileName() ÝÕ ßÞÛãçØÛ ÚÐâÐÛÞÓ"
+ #define ERR_DISK_FULL "´ØáÚ ßÞÛÞÝ"
+ #define ERR_DIRECTORY_FULL "ºÐâÐÛÞÓ ßÞÛÞÝ"
+ #define ERR_MACOS_GENERIC "MacOS áÞÞÑéØÛÐ ÞèØÑÚã (%d)"
+ #define ERR_OS2_GENERIC "OS/2 áÞÞÑéØÛÐ ÞèØÑÚã (%d)"
+ #define ERR_VOL_LOCKED_HW "ÂÞÜ ÑÛÞÚØàÞÒÐÝ ÐßßÐàÐâÝÞ"
+ #define ERR_VOL_LOCKED_SW "ÂÞÜ ÑÛÞÚØàÞÒÐÝ ßàÞÓàÐÜÜÝÞ"
+ #define ERR_FILE_LOCKED "ÄÐÙÛ ×ÐÑÛÞÚØàÞÒÐÝ"
+ #define ERR_FILE_OR_DIR_BUSY "ÄÐÙÛ/ÚÐâÐÛÞÓ ×ÐÝïâ"
+ #define ERR_FILE_ALREADY_OPEN_W "ÄÐÙÛ ãÖÕ ÞâÚàëâ ÝÐ ×ÐßØáì"
+ #define ERR_FILE_ALREADY_OPEN_R "ÄÐÙÛ ãÖÕ ÞâÚàëâ ÝÐ çâÕÝØÕ"
+ #define ERR_INVALID_REFNUM "½ÕÒÕàÝÞÕ ÚÞÛØçÕáâÒÞ ááëÛÞÚ"
+ #define ERR_GETTING_FILE_POS "¾èØÑÚÐ ßàØ ßÞÛãçÕÝØØ ßÞ×ØæØØ äÐÙÛÐ"
+ #define ERR_VOLUME_OFFLINE "ÂÞÜ ÞâáÞÕÔØÝÕÝ"
+ #define ERR_PERMISSION_DENIED "¾âÚÐ×ÐÝÞ Ò àÐ×àÕèÕÝØØ"
+ #define ERR_VOL_ALREADY_ONLINE "ÂÞÜ ãÖÕ ßÞÔáÞÕÔØÝÕÝ"
+ #define ERR_NO_SUCH_DRIVE "½Õâ âÐÚÞÓÞ ÔØáÚÐ"
+ #define ERR_NOT_MAC_DISK "½Õ ÔØáÚ Macintosh"
+ #define ERR_VOL_EXTERNAL_FS "ÂÞÜ ßàØÝÐÔÛÕÖØâ ÒÝÕèÝÕÙ äÐÙÛÞÒÞÙ áØáâÕÜÕ"
+ #define ERR_PROBLEM_RENAME "¿àÞÑÛÕÜÐ ßàØ ßÕàÕØÜÕÝÞÒÐÝØØ"
+ #define ERR_BAD_MASTER_BLOCK "¿ÛÞåÞÙ ÓÛÐÒÝëÙ ÑÛÞÚ ÚÐâÐÛÞÓÐ"
+ #define ERR_CANT_MOVE_FORBIDDEN "¿ÞßëâÚÐ ßÕàÕÜÕáâØâì ×ÐßàÕéÕÝÐ"
+ #define ERR_WRONG_VOL_TYPE "½ÕÒÕàÝëÙ âØß âÞÜÐ"
+ #define ERR_SERVER_VOL_LOST "ÁÕàÒÕàÝëÙ âÞÜ ÑëÛ ÞâáÞÕÔØÝÕÝ"
+ #define ERR_FILE_ID_NOT_FOUND "¸ÔÕÝâØäØÚÐâÞà äÐÙÛÐ ÝÕ ÝÐÙÔÕÝ"
+ #define ERR_FILE_ID_EXISTS "¸ÔÕÝâØäØÚÐâÞà äÐÙÛÐ ãÖÕ áãéÕáâÒãÕâ"
+ #define ERR_SERVER_NO_RESPOND "ÁÕàÒÕà ÝÕ ÞâÒÕçÐÕâ"
+ #define ERR_USER_AUTH_FAILED "¸ÔÕÝâØäØÚÐæØï ßÞÛì×ÞÒÐâÕÛï ÝÕ ãÔÐÛÐáì"
+ #define ERR_PWORD_EXPIRED "¿ÐàÞÛì ÝÐ áÕàÒÕàÕ ãáâÐàÕÛ"
+ #define ERR_ACCESS_DENIED "¾âÚÐ×ÐÝÞ Ò ÔÞáâãßÕ"
+ #define ERR_NOT_A_DOS_DISK "½Õ ÔØáÚ DOS"
+ #define ERR_SHARING_VIOLATION "½ÐàãèÕÝØÕ áÞÒÜÕáâÝÞÓÞ ÔÞáâãßÐ"
+ #define ERR_CANNOT_MAKE "½Õ ÜÞÓã áÞÑàÐâì"
+ #define ERR_DEV_IN_USE "ÃáâàÞÙáâÒÞ ãÖÕ ØáßÞÛì×ãÕâáï"
+ #define ERR_OPEN_FAILED "¾âÚàëâØÕ ÝÕ ãÔÐÛÞáì"
+ #define ERR_PIPE_BUSY "ºÞÝÒÕÙÕà ×ÐÝïâ"
+ #define ERR_SHARING_BUF_EXCEEDED "ÀÐ×ÔÕÛïÕÜëÙ ÑãäÕà ßÕàÕßÞÛÝÕÝ"
+ #define ERR_TOO_MANY_HANDLES "ÁÛØèÚÞÜ ÜÝÞÓÞ ÞâÚàëâëå ÔÕáÚàØßâÞàÞÒ"
+ #define ERR_SEEK_ERROR "¾èØÑÚÐ ßÞ×ØæØÞÝØàÞÒÐÝØï"
+ #define ERR_DEL_CWD "¿ÞßëâÚÐ ãÔÐÛØâì âÕÚãéØÙ àÐÑÞçØÙ ÚÐâÐÛÞÓ"
+ #define ERR_WRITE_PROTECT_ERROR "¾èØÑÚÐ ×ÐéØâë ×ÐßØáØ"
+ #define ERR_WRITE_FAULT "¾èØÑÚÐ ×ÐßØáØ"
+ #define ERR_LOCK_VIOLATION "½ÐàãèÕÝØÕ ÑÛÞÚØàÞÒÚØ"
+ #define ERR_GEN_FAILURE "¾ÑéØÙ áÑÞÙ"
+ #define ERR_UNCERTAIN_MEDIA "½ÕÞßàÕÔÕÛÕÝÝëÙ ÝÞáØâÕÛì"
+ #define ERR_PROT_VIOLATION "½ÐàãèÕÝØÕ ×ÐéØâë"
+ #define ERR_BROKEN_PIPE "ÁÛÞÜÐÝÝëÙ ÚÞÝÒÕÙÕà"
+
+
+#elif (PHYSFS_LANG == PHYSFS_LANG_FRENCH)
+ #define DIR_ARCHIVE_DESCRIPTION "Pas d'archive, E/S directes sur système de fichiers"
+ #define GRP_ARCHIVE_DESCRIPTION "Format Groupfile du moteur Build"
+ #define HOG_ARCHIVE_DESCRIPTION "Descent I/II HOG file format"
+ #define MVL_ARCHIVE_DESCRIPTION "Descent II Movielib format"
+ #define QPAK_ARCHIVE_DESCRIPTION "Quake I/II format"
+ #define ZIP_ARCHIVE_DESCRIPTION "Compatible PkZip/WinZip/Info-Zip"
+ #define WAD_ARCHIVE_DESCRIPTION "Format WAD du moteur DOOM"
+ #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */
+
+ #define ERR_IS_INITIALIZED "Déjà initialisé"
+ #define ERR_NOT_INITIALIZED "Non initialisé"
+ #define ERR_INVALID_ARGUMENT "Argument invalide"
+ #define ERR_FILES_STILL_OPEN "Fichiers encore ouverts"
+ #define ERR_NO_DIR_CREATE "Echec de la création de répertoires"
+ #define ERR_OUT_OF_MEMORY "A court de mémoire"
+ #define ERR_NOT_IN_SEARCH_PATH "Aucune entrée dans le chemin de recherche"
+ #define ERR_NOT_SUPPORTED "Opération non supportée"
+ #define ERR_UNSUPPORTED_ARCHIVE "Type d'archive non supportée"
+ #define ERR_NOT_A_HANDLE "Pas un descripteur de fichier"
+ #define ERR_INSECURE_FNAME "Nom de fichier dangereux"
+ #define ERR_SYMLINK_DISALLOWED "Les liens symboliques sont désactivés"
+ #define ERR_NO_WRITE_DIR "Le répertoire d'écriture n'est pas spécifié"
+ #define ERR_NO_SUCH_FILE "Fichier non trouvé"
+ #define ERR_NO_SUCH_PATH "Chemin non trouvé"
+ #define ERR_NO_SUCH_VOLUME "Volume non trouvé"
+ #define ERR_PAST_EOF "Au-delà de la fin du fichier"
+ #define ERR_ARC_IS_READ_ONLY "L'archive est en lecture seule"
+ #define ERR_IO_ERROR "Erreur E/S"
+ #define ERR_CANT_SET_WRITE_DIR "Ne peut utiliser le répertoire d'écriture"
+ #define ERR_SYMLINK_LOOP "Boucle infinie dans les liens symboliques"
+ #define ERR_COMPRESSION "Erreur de (dé)compression"
+ #define ERR_NOT_IMPLEMENTED "Non implémenté"
+ #define ERR_OS_ERROR "Erreur rapportée par le système d'exploitation"
+ #define ERR_FILE_EXISTS "Le fichier existe déjà"
+ #define ERR_NOT_A_FILE "Pas un fichier"
+ #define ERR_NOT_A_DIR "Pas un répertoire"
+ #define ERR_NOT_AN_ARCHIVE "Pas une archive"
+ #define ERR_CORRUPTED "Archive corrompue"
+ #define ERR_SEEK_OUT_OF_RANGE "Pointeur de fichier hors de portée"
+ #define ERR_BAD_FILENAME "Mauvais nom de fichier"
+ #define ERR_PHYSFS_BAD_OS_CALL "(BOGUE) PhysicsFS a fait un mauvais appel système, le salaud"
+ #define ERR_ARGV0_IS_NULL "argv0 est NULL"
+ #define ERR_NEED_DICT "a besoin du dico"
+ #define ERR_DATA_ERROR "erreur de données"
+ #define ERR_MEMORY_ERROR "erreur mémoire"
+ #define ERR_BUFFER_ERROR "erreur tampon"
+ #define ERR_VERSION_ERROR "erreur de version"
+ #define ERR_UNKNOWN_ERROR "erreur inconnue"
+ #define ERR_SEARCHPATH_TRUNC "Le chemin de recherche a été tronqué"
+ #define ERR_GETMODFN_TRUNC "GetModuleFileName() a été tronqué"
+ #define ERR_GETMODFN_NO_DIR "GetModuleFileName() n'a pas de répertoire"
+ #define ERR_DISK_FULL "Disque plein"
+ #define ERR_DIRECTORY_FULL "Répertoire plein"
+ #define ERR_MACOS_GENERIC "Erreur rapportée par MacOS (%d)"
+ #define ERR_OS2_GENERIC "Erreur rapportée par OS/2 (%d)"
+ #define ERR_VOL_LOCKED_HW "Le volume est verrouillé matériellement"
+ #define ERR_VOL_LOCKED_SW "Le volume est verrouillé par logiciel"
+ #define ERR_FILE_LOCKED "Fichier verrouillé"
+ #define ERR_FILE_OR_DIR_BUSY "Fichier/répertoire occupé"
+ #define ERR_FILE_ALREADY_OPEN_W "Fichier déjà ouvert en écriture"
+ #define ERR_FILE_ALREADY_OPEN_R "Fichier déjà ouvert en lecture"
+ #define ERR_INVALID_REFNUM "Numéro de référence invalide"
+ #define ERR_GETTING_FILE_POS "Erreur lors de l'obtention de la position du pointeur de fichier"
+ #define ERR_VOLUME_OFFLINE "Le volume n'est pas en ligne"
+ #define ERR_PERMISSION_DENIED "Permission refusée"
+ #define ERR_VOL_ALREADY_ONLINE "Volumé déjà en ligne"
+ #define ERR_NO_SUCH_DRIVE "Lecteur inexistant"
+ #define ERR_NOT_MAC_DISK "Pas un disque Macintosh"
+ #define ERR_VOL_EXTERNAL_FS "Le volume appartient à un système de fichiers externe"
+ #define ERR_PROBLEM_RENAME "Problème lors du renommage"
+ #define ERR_BAD_MASTER_BLOCK "Mauvais block maitre de répertoire"
+ #define ERR_CANT_MOVE_FORBIDDEN "Essai de déplacement interdit"
+ #define ERR_WRONG_VOL_TYPE "Mauvais type de volume"
+ #define ERR_SERVER_VOL_LOST "Le volume serveur a été déconnecté"
+ #define ERR_FILE_ID_NOT_FOUND "Identificateur de fichier non trouvé"
+ #define ERR_FILE_ID_EXISTS "Identificateur de fichier existe déjà"
+ #define ERR_SERVER_NO_RESPOND "Le serveur ne répond pas"
+ #define ERR_USER_AUTH_FAILED "Authentification de l'utilisateur échouée"
+ #define ERR_PWORD_EXPIRED "Le mot de passe a expiré sur le serveur"
+ #define ERR_ACCESS_DENIED "Accès refusé"
+ #define ERR_NOT_A_DOS_DISK "Pas un disque DOS"
+ #define ERR_SHARING_VIOLATION "Violation de partage"
+ #define ERR_CANNOT_MAKE "Ne peut faire"
+ #define ERR_DEV_IN_USE "Périphérique déjà en utilisation"
+ #define ERR_OPEN_FAILED "Ouverture échouée"
+ #define ERR_PIPE_BUSY "Le tube est occupé"
+ #define ERR_SHARING_BUF_EXCEEDED "Tampon de partage dépassé"
+ #define ERR_TOO_MANY_HANDLES "Trop de descripteurs ouverts"
+ #define ERR_SEEK_ERROR "Erreur de positionement"
+ #define ERR_DEL_CWD "Essai de supprimer le répertoire courant"
+ #define ERR_WRITE_PROTECT_ERROR "Erreur de protection en écriture"
+ #define ERR_WRITE_FAULT "Erreur d'écriture"
+ #define ERR_LOCK_VIOLATION "Violation de verrou"
+ #define ERR_GEN_FAILURE "Echec général"
+ #define ERR_UNCERTAIN_MEDIA "Média incertain"
+ #define ERR_PROT_VIOLATION "Violation de protection"
+ #define ERR_BROKEN_PIPE "Tube cassé"
+
+#elif (PHYSFS_LANG == PHYSFS_LANG_PORTUGUESE_BR)
+ #define DIR_ARCHIVE_DESCRIPTION "Não arquivo, E/S sistema de arquivos direto"
+ #define GRP_ARCHIVE_DESCRIPTION "Formato Groupfile do engine Build"
+ #define HOG_ARCHIVE_DESCRIPTION "Formato Descent I/II HOG file"
+ #define MVL_ARCHIVE_DESCRIPTION "Formato Descent II Movielib"
+ #define QPAK_ARCHIVE_DESCRIPTION "Formato Quake I/II"
+ #define ZIP_ARCHIVE_DESCRIPTION "Formato compatível PkZip/WinZip/Info-Zip"
+ #define WAD_ARCHIVE_DESCRIPTION "Formato WAD do engine DOOM"
+ #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */
+ #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */
+
+ #define ERR_IS_INITIALIZED "Já inicializado"
+ #define ERR_NOT_INITIALIZED "Não inicializado"
+ #define ERR_INVALID_ARGUMENT "Argumento inválido"
+ #define ERR_FILES_STILL_OPEN "Arquivos ainda abertos"
+ #define ERR_NO_DIR_CREATE "Falha na criação de diretórios"
+ #define ERR_OUT_OF_MEMORY "Memória insuficiente"
+ #define ERR_NOT_IN_SEARCH_PATH "Entrada não encontrada no caminho de busca"
+ #define ERR_NOT_SUPPORTED "Operação não suportada"
+ #define ERR_UNSUPPORTED_ARCHIVE "Tipo de arquivo não suportado"
+ #define ERR_NOT_A_HANDLE "Não é um handler de arquivo"
+ #define ERR_INSECURE_FNAME "Nome de arquivo inseguro"
+ #define ERR_SYMLINK_DISALLOWED "Links simbólicos desabilitados"
+ #define ERR_NO_WRITE_DIR "Diretório de escrita não definido"
+ #define ERR_NO_SUCH_FILE "Arquivo não encontrado"
+ #define ERR_NO_SUCH_PATH "Caminho não encontrado"
+ #define ERR_NO_SUCH_VOLUME "Volume não encontrado"
+ #define ERR_PAST_EOF "Passou o fim do arquivo"
+ #define ERR_ARC_IS_READ_ONLY "Arquivo é somente de leitura"
+ #define ERR_IO_ERROR "Erro de E/S"
+ #define ERR_CANT_SET_WRITE_DIR "Não foi possível definir diretório de escrita"
+ #define ERR_SYMLINK_LOOP "Loop infinito de link simbólico"
+ #define ERR_COMPRESSION "Erro de (Des)compressão"
+ #define ERR_NOT_IMPLEMENTED "Não implementado"
+ #define ERR_OS_ERROR "Erro reportado pelo Sistema Operacional"
+ #define ERR_FILE_EXISTS "Arquivo já existente"
+ #define ERR_NOT_A_FILE "Não é um arquivo"
+ #define ERR_NOT_A_DIR "Não é um diretório"
+ #define ERR_NOT_AN_ARCHIVE "Não é um pacote"
+ #define ERR_CORRUPTED "Pacote corrompido"
+ #define ERR_SEEK_OUT_OF_RANGE "Posicionamento além do tamanho"
+ #define ERR_BAD_FILENAME "Nome de arquivo inválido"
+ #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS realizou uma chamada de sistema inválida"
+ #define ERR_ARGV0_IS_NULL "argv0 é NULL"
+ #define ERR_NEED_DICT "precisa de diretório"
+ #define ERR_DATA_ERROR "erro nos dados"
+ #define ERR_MEMORY_ERROR "erro de memória"
+ #define ERR_BUFFER_ERROR "erro de buffer"
+ #define ERR_VERSION_ERROR "erro na version"
+ #define ERR_UNKNOWN_ERROR "erro desconhecido"
+ #define ERR_SEARCHPATH_TRUNC "Caminho de procura quebrado"
+ #define ERR_GETMODFN_TRUNC "GetModuleFileName() foi quebrado"
+ #define ERR_GETMODFN_NO_DIR "GetModuleFileName() nao teve diretório"
+ #define ERR_DISK_FULL "Disco cheio"
+ #define ERR_DIRECTORY_FULL "Diretório cheio"
+ #define ERR_MACOS_GENERIC "MacOS reportou um erro (%d)"
+ #define ERR_OS2_GENERIC "OS/2 reportou um erro (%d)"
+ #define ERR_VOL_LOCKED_HW "Volume travado por hardware"
+ #define ERR_VOL_LOCKED_SW "Volume travado por software"
+ #define ERR_FILE_LOCKED "Arquivo travado"
+ #define ERR_FILE_OR_DIR_BUSY "Arquivo/Diretório está em uso"
+ #define ERR_FILE_ALREADY_OPEN_W "Arquivo já aberto para escrita"
+ #define ERR_FILE_ALREADY_OPEN_R "Arquivo já aberto para leitura"
+ #define ERR_INVALID_REFNUM "Número de referência"
+ #define ERR_GETTING_FILE_POS "Erro ao tentar obter posição do arquivo"
+ #define ERR_VOLUME_OFFLINE "Volume está indisponível"
+ #define ERR_PERMISSION_DENIED "Permissão negada"
+ #define ERR_VOL_ALREADY_ONLINE "Volume disponível"
+ #define ERR_NO_SUCH_DRIVE "Drive inexistente"
+ #define ERR_NOT_MAC_DISK "Não é um disco Macintosh"
+ #define ERR_VOL_EXTERNAL_FS "Volume pertence a um sistema de arquivos externo"
+ #define ERR_PROBLEM_RENAME "Problema durante renomeação"
+ #define ERR_BAD_MASTER_BLOCK "Bloco master do diretório inválido"
+ #define ERR_CANT_MOVE_FORBIDDEN "Tentativa de mover proibida"
+ #define ERR_WRONG_VOL_TYPE "Tipo inválido de volume"
+ #define ERR_SERVER_VOL_LOST "Volume servidor desconectado"
+ #define ERR_FILE_ID_NOT_FOUND "ID de Arquivo não encontrado"
+ #define ERR_FILE_ID_EXISTS "ID de Arquivo já existente"
+ #define ERR_SERVER_NO_RESPOND "Servidor não respondendo"
+ #define ERR_USER_AUTH_FAILED "Autenticação de usuário falhada"
+ #define ERR_PWORD_EXPIRED "Password foi expirada no servidor"
+ #define ERR_ACCESS_DENIED "Accesso negado"
+ #define ERR_NOT_A_DOS_DISK "Não é um disco DOS"
+ #define ERR_SHARING_VIOLATION "Violação de compartilhamento"
+ #define ERR_CANNOT_MAKE "Não pode ser feito"
+ #define ERR_DEV_IN_USE "Device já em uso"
+ #define ERR_OPEN_FAILED "Falaha na abertura"
+ #define ERR_PIPE_BUSY "Fila ocupada"
+ #define ERR_SHARING_BUF_EXCEEDED "Buffer de compartilhamento excedeu"
+ #define ERR_TOO_MANY_HANDLES "Muitos handles abertos"
+ #define ERR_SEEK_ERROR "Erro de posicionamento"
+ #define ERR_DEL_CWD "Tentando remover diretório de trabalho atual"
+ #define ERR_WRITE_PROTECT_ERROR "Erro de proteção de escrita"
+ #define ERR_WRITE_FAULT "Erro de escrita"
+ #define ERR_LOCK_VIOLATION "Violação de trava"
+ #define ERR_GEN_FAILURE "Falha geral"
+ #define ERR_UNCERTAIN_MEDIA "Media incerta"
+ #define ERR_PROT_VIOLATION "Violação de proteção"
+ #define ERR_BROKEN_PIPE "Fila quebrada"
+
+#elif (PHYSFS_LANG == PHYSFS_LANG_SPANISH)
+ #define DIR_ARCHIVE_DESCRIPTION "No es un archivo, E/S directa al sistema de ficheros"
+ #define GRP_ARCHIVE_DESCRIPTION "Formato Build engine Groupfile"
+ #define HOG_ARCHIVE_DESCRIPTION "Formato Descent I/II HOG file"
+ #define MVL_ARCHIVE_DESCRIPTION "Formato Descent II Movielib"
+ #define QPAK_ARCHIVE_DESCRIPTION "Formato Quake I/II"
+ #define ZIP_ARCHIVE_DESCRIPTION "Compatible con PkZip/WinZip/Info-Zip"
+ #define WAD_ARCHIVE_DESCRIPTION "DOOM engine format" /* !!! FIXME: translate this line if needed */
+ #define LZMA_ARCHIVE_DESCRIPTION "LZMA (7zip) format" /* !!! FIXME: translate this line if needed */
+
+ #define ERR_IS_INITIALIZED "Ya estaba inicializado"
+ #define ERR_NOT_INITIALIZED "No está inicializado"
+ #define ERR_INVALID_ARGUMENT "Argumento inválido"
+ #define ERR_FILES_STILL_OPEN "Archivos aún abiertos"
+ #define ERR_NO_DIR_CREATE "Fallo al crear los directorios"
+ #define ERR_OUT_OF_MEMORY "Memoria agotada"
+ #define ERR_NOT_IN_SEARCH_PATH "No existe tal entrada en la ruta de búsqueda"
+ #define ERR_NOT_SUPPORTED "Operación no soportada"
+ #define ERR_UNSUPPORTED_ARCHIVE "Tipo de archivo no soportado"
+ #define ERR_NOT_A_HANDLE "No es un manejador de ficheo (file handle)"
+ #define ERR_INSECURE_FNAME "Nombre de archivo inseguro"
+ #define ERR_SYMLINK_DISALLOWED "Los enlaces simbólicos están desactivados"
+ #define ERR_NO_WRITE_DIR "No has configurado un directorio de escritura"
+ #define ERR_NO_SUCH_FILE "Archivo no encontrado"
+ #define ERR_NO_SUCH_PATH "Ruta no encontrada"
+ #define ERR_NO_SUCH_VOLUME "Volumen no encontrado"
+ #define ERR_PAST_EOF "Te pasaste del final del archivo"
+ #define ERR_ARC_IS_READ_ONLY "El archivo es de sólo lectura"
+ #define ERR_IO_ERROR "Error E/S"
+ #define ERR_CANT_SET_WRITE_DIR "No puedo configurar el directorio de escritura"
+ #define ERR_SYMLINK_LOOP "Bucle infnito de enlaces simbólicos"
+ #define ERR_COMPRESSION "Error de (des)compresión"
+ #define ERR_NOT_IMPLEMENTED "No implementado"
+ #define ERR_OS_ERROR "El sistema operativo ha devuelto un error"
+ #define ERR_FILE_EXISTS "El archivo ya existe"
+ #define ERR_NOT_A_FILE "No es un archivo"
+ #define ERR_NOT_A_DIR "No es un directorio"
+ #define ERR_NOT_AN_ARCHIVE "No es un archivo"
+ #define ERR_CORRUPTED "Archivo corrupto"
+ #define ERR_SEEK_OUT_OF_RANGE "Búsqueda fuera de rango"
+ #define ERR_BAD_FILENAME "Nombre de archivo incorrecto"
+ #define ERR_PHYSFS_BAD_OS_CALL "(BUG) PhysicsFS ha hecho una llamada incorrecta al sistema"
+ #define ERR_ARGV0_IS_NULL "argv0 es NULL"
+ #define ERR_NEED_DICT "necesito diccionario"
+ #define ERR_DATA_ERROR "error de datos"
+ #define ERR_MEMORY_ERROR "error de memoria"
+ #define ERR_BUFFER_ERROR "error de buffer"
+ #define ERR_VERSION_ERROR "error de versión"
+ #define ERR_UNKNOWN_ERROR "error desconocido"
+ #define ERR_SEARCHPATH_TRUNC "La ruta de búsqueda ha sido truncada"
+ #define ERR_GETMODFN_TRUNC "GetModuleFileName() ha sido truncado"
+ #define ERR_GETMODFN_NO_DIR "GetModuleFileName() no tenia directorio"
+ #define ERR_DISK_FULL "El disco está lleno"
+ #define ERR_DIRECTORY_FULL "El directorio está lleno"
+ #define ERR_MACOS_GENERIC "MacOS ha devuelto un error (%d)"
+ #define ERR_OS2_GENERIC "OS/2 ha devuelto un error (%d)"
+ #define ERR_VOL_LOCKED_HW "El volumen está bloqueado por el hardware"
+ #define ERR_VOL_LOCKED_SW "El volumen está bloqueado por el software"
+ #define ERR_FILE_LOCKED "El archivo está bloqueado"
+ #define ERR_FILE_OR_DIR_BUSY "Fichero o directorio ocupados"
+ #define ERR_FILE_ALREADY_OPEN_W "Fichero ya abierto para escritura"
+ #define ERR_FILE_ALREADY_OPEN_R "Fichero ya abierto para lectura"
+ #define ERR_INVALID_REFNUM "El número de referencia no es válido"
+ #define ERR_GETTING_FILE_POS "Error al tomar la posición del fichero"
+ #define ERR_VOLUME_OFFLINE "El volumen está desconectado"
+ #define ERR_PERMISSION_DENIED "Permiso denegado"
+ #define ERR_VOL_ALREADY_ONLINE "El volumen ya estaba conectado"
+ #define ERR_NO_SUCH_DRIVE "No existe tal unidad"
+ #define ERR_NOT_MAC_DISK "No es un disco Macintosh"
+ #define ERR_VOL_EXTERNAL_FS "El volumen pertence a un sistema de ficheros externo"
+ #define ERR_PROBLEM_RENAME "Problemas al renombrar"
+ #define ERR_BAD_MASTER_BLOCK "Bloque maestro de directorios incorrecto"
+ #define ERR_CANT_MOVE_FORBIDDEN "Intento de mover forbidden"
+ #define ERR_WRONG_VOL_TYPE "Tipo de volumen incorrecto"
+ #define ERR_SERVER_VOL_LOST "El servidor de volúmenes ha sido desconectado"
+ #define ERR_FILE_ID_NOT_FOUND "Identificador de archivo no encontrado"
+ #define ERR_FILE_ID_EXISTS "El identificador de archivo ya existe"
+ #define ERR_SERVER_NO_RESPOND "El servidor no responde"
+ #define ERR_USER_AUTH_FAILED "Fallo al autentificar el usuario"
+ #define ERR_PWORD_EXPIRED "La Password en el servidor ha caducado"
+ #define ERR_ACCESS_DENIED "Acceso denegado"
+ #define ERR_NOT_A_DOS_DISK "No es un disco de DOS"
+ #define ERR_SHARING_VIOLATION "Violación al compartir"
+ #define ERR_CANNOT_MAKE "No puedo hacer make"
+ #define ERR_DEV_IN_USE "El dispositivo ya estaba en uso"
+ #define ERR_OPEN_FAILED "Fallo al abrir"
+ #define ERR_PIPE_BUSY "Tubería ocupada"
+ #define ERR_SHARING_BUF_EXCEEDED "Buffer de compartición sobrepasado"
+ #define ERR_TOO_MANY_HANDLES "Demasiados manejadores (handles)"
+ #define ERR_SEEK_ERROR "Error de búsqueda"
+ #define ERR_DEL_CWD "Intentando borrar el directorio de trabajo actual"
+ #define ERR_WRITE_PROTECT_ERROR "Error de protección contra escritura"
+ #define ERR_WRITE_FAULT "Fallo al escribir"
+ #define ERR_LOCK_VIOLATION "Violación del bloqueo"
+ #define ERR_GEN_FAILURE "Fallo general"
+ #define ERR_UNCERTAIN_MEDIA "Medio incierto"
+ #define ERR_PROT_VIOLATION "Violación de la protección"
+ #define ERR_BROKEN_PIPE "Tubería rota"
+
+#else
+ #error Please define PHYSFS_LANG.
+#endif
+
+/* end LANG section. */
+
+struct __PHYSFS_DIRHANDLE__;
+struct __PHYSFS_FILEFUNCTIONS__;
+
+
+/* !!! FIXME: find something better than "dvoid" and "fvoid" ... */
+/* Opaque data for file and dir handlers... */
+typedef void dvoid;
+typedef void fvoid;
+
+
+typedef struct
+{
+ /*
+ * Basic info about this archiver...
+ */
+ const PHYSFS_ArchiveInfo *info;
+
+
+ /*
+ * DIRECTORY ROUTINES:
+ * These functions are for dir handles. Generate a handle with the
+ * openArchive() method, then pass it as the "opaque" dvoid to the
+ * others.
+ *
+ * Symlinks should always be followed; PhysicsFS will use the
+ * isSymLink() method and make a judgement on whether to
+ * continue to call other methods based on that.
+ */
+
+
+ /*
+ * Returns non-zero if (filename) is a valid archive that this
+ * driver can handle. This filename is in platform-dependent
+ * notation. forWriting is non-zero if this is to be used for
+ * the write directory, and zero if this is to be used for an
+ * element of the search path.
+ */
+ int (*isArchive)(const char *filename, int forWriting);
+
+ /*
+ * Open a dirhandle for dir/archive (name).
+ * This filename is in platform-dependent notation.
+ * forWriting is non-zero if this is to be used for
+ * the write directory, and zero if this is to be used for an
+ * element of the search path.
+ * Returns NULL on failure, and calls __PHYSFS_setError().
+ * Returns non-NULL on success. The pointer returned will be
+ * passed as the "opaque" parameter for later calls.
+ */
+ void *(*openArchive)(const char *name, int forWriting);
+
+ /*
+ * List all files in (dirname). Each file is passed to (callback),
+ * where a copy is made if appropriate, so you should dispose of
+ * it properly upon return from the callback.
+ * You should omit symlinks if (omitSymLinks) is non-zero.
+ * If you have a failure, report as much as you can.
+ * (dirname) is in platform-independent notation.
+ */
+ void (*enumerateFiles)(dvoid *opaque,
+ const char *dirname,
+ int omitSymLinks,
+ PHYSFS_EnumFilesCallback callback,
+ const char *origdir,
+ void *callbackdata);
+
+ /*
+ * Returns non-zero if filename can be opened for reading.
+ * This filename is in platform-independent notation.
+ * You should not follow symlinks.
+ */
+ int (*exists)(dvoid *opaque, const char *name);
+
+ /*
+ * Returns non-zero if filename is really a directory.
+ * This filename is in platform-independent notation.
+ * Symlinks should be followed; if what the symlink points
+ * to is missing, or isn't a directory, then the retval is zero.
+ *
+ * Regardless of success or failure, please set *fileExists to
+ * non-zero if the file existed (even if it's a broken symlink!),
+ * zero if it did not.
+ */
+ int (*isDirectory)(dvoid *opaque, const char *name, int *fileExists);
+
+ /*
+ * Returns non-zero if filename is really a symlink.
+ * This filename is in platform-independent notation.
+ *
+ * Regardless of success or failure, please set *fileExists to
+ * non-zero if the file existed (even if it's a broken symlink!),
+ * zero if it did not.
+ */
+ int (*isSymLink)(dvoid *opaque, const char *name, int *fileExists);
+
+ /*
+ * Retrieve the last modification time (mtime) of a file.
+ * Returns -1 on failure, or the file's mtime in seconds since
+ * the epoch (Jan 1, 1970) on success.
+ * This filename is in platform-independent notation.
+ *
+ * Regardless of success or failure, please set *exists to
+ * non-zero if the file existed (even if it's a broken symlink!),
+ * zero if it did not.
+ */
+ PHYSFS_sint64 (*getLastModTime)(dvoid *opaque, const char *fnm, int *exist);
+
+ /*
+ * Open file for reading.
+ * This filename is in platform-independent notation.
+ * If you can't handle multiple opens of the same file,
+ * you can opt to fail for the second call.
+ * Fail if the file does not exist.
+ * Returns NULL on failure, and calls __PHYSFS_setError().
+ * Returns non-NULL on success. The pointer returned will be
+ * passed as the "opaque" parameter for later file calls.
+ *
+ * Regardless of success or failure, please set *fileExists to
+ * non-zero if the file existed (even if it's a broken symlink!),
+ * zero if it did not.
+ */
+ fvoid *(*openRead)(dvoid *opaque, const char *fname, int *fileExists);
+
+ /*
+ * Open file for writing.
+ * If the file does not exist, it should be created. If it exists,
+ * it should be truncated to zero bytes. The writing
+ * offset should be the start of the file.
+ * This filename is in platform-independent notation.
+ * If you can't handle multiple opens of the same file,
+ * you can opt to fail for the second call.
+ * Returns NULL on failure, and calls __PHYSFS_setError().
+ * Returns non-NULL on success. The pointer returned will be
+ * passed as the "opaque" parameter for later file calls.
+ */
+ fvoid *(*openWrite)(dvoid *opaque, const char *filename);
+
+ /*
+ * Open file for appending.
+ * If the file does not exist, it should be created. The writing
+ * offset should be the end of the file.
+ * This filename is in platform-independent notation.
+ * If you can't handle multiple opens of the same file,
+ * you can opt to fail for the second call.
+ * Returns NULL on failure, and calls __PHYSFS_setError().
+ * Returns non-NULL on success. The pointer returned will be
+ * passed as the "opaque" parameter for later file calls.
+ */
+ fvoid *(*openAppend)(dvoid *opaque, const char *filename);
+
+ /*
+ * Delete a file in the archive/directory.
+ * Return non-zero on success, zero on failure.
+ * This filename is in platform-independent notation.
+ * This method may be NULL.
+ * On failure, call __PHYSFS_setError().
+ */
+ int (*remove)(dvoid *opaque, const char *filename);
+
+ /*
+ * Create a directory in the archive/directory.
+ * If the application is trying to make multiple dirs, PhysicsFS
+ * will split them up into multiple calls before passing them to
+ * your driver.
+ * Return non-zero on success, zero on failure.
+ * This filename is in platform-independent notation.
+ * This method may be NULL.
+ * On failure, call __PHYSFS_setError().
+ */
+ int (*mkdir)(dvoid *opaque, const char *filename);
+
+ /*
+ * Close directories/archives, and free any associated memory,
+ * including (opaque) itself if applicable. Implementation can assume
+ * that it won't be called if there are still files open from
+ * this archive.
+ */
+ void (*dirClose)(dvoid *opaque);
+
+
+
+ /*
+ * FILE ROUTINES:
+ * These functions are for file handles generated by the open*() methods.
+ * They are distinguished by taking a "fvoid" instead of a "dvoid" for
+ * the opaque handle.
+ */
+
+ /*
+ * Read more from the file.
+ * Returns number of objects of (objSize) bytes read from file, -1
+ * if complete failure.
+ * On failure, call __PHYSFS_setError().
+ */
+ PHYSFS_sint64 (*read)(fvoid *opaque, void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount);
+
+ /*
+ * Write more to the file. Archives don't have to implement this.
+ * (Set it to NULL if not implemented).
+ * Returns number of objects of (objSize) bytes written to file, -1
+ * if complete failure.
+ * On failure, call __PHYSFS_setError().
+ */
+ PHYSFS_sint64 (*write)(fvoid *opaque, const void *buffer,
+ PHYSFS_uint32 objSize, PHYSFS_uint32 objCount);
+
+ /*
+ * Returns non-zero if at end of file.
+ */
+ int (*eof)(fvoid *opaque);
+
+ /*
+ * Returns byte offset from start of file.
+ */
+ PHYSFS_sint64 (*tell)(fvoid *opaque);
+
+ /*
+ * Move read/write pointer to byte offset from start of file.
+ * Returns non-zero on success, zero on error.
+ * On failure, call __PHYSFS_setError().
+ */
+ int (*seek)(fvoid *opaque, PHYSFS_uint64 offset);
+
+ /*
+ * Return number of bytes available in the file, or -1 if you
+ * aren't able to determine.
+ * On failure, call __PHYSFS_setError().
+ */
+ PHYSFS_sint64 (*fileLength)(fvoid *opaque);
+
+ /*
+ * Close the file, and free associated resources, including (opaque)
+ * if applicable. Returns non-zero on success, zero if can't close
+ * file. On failure, call __PHYSFS_setError().
+ */
+ int (*fileClose)(fvoid *opaque);
+} PHYSFS_Archiver;
+
+
+/*
+ * Call this to set the message returned by PHYSFS_getLastError().
+ * Please only use the ERR_* constants above, or add new constants to the
+ * above group, but I want these all in one place.
+ *
+ * Calling this with a NULL argument is a safe no-op.
+ */
+void __PHYSFS_setError(const char *err);
+
+
+/*
+ * Convert (dirName) to platform-dependent notation, then prepend (prepend)
+ * and append (append) to the converted string.
+ *
+ * So, on Win32, calling:
+ * __PHYSFS_convertToDependent("C:\", "my/files", NULL);
+ * ...will return the string "C:\my\files".
+ *
+ * This is a convenience function; you might want to hack something out that
+ * is less generic (and therefore more efficient).
+ *
+ * Be sure to free() the return value when done with it.
+ */
+char *__PHYSFS_convertToDependent(const char *prepend,
+ const char *dirName,
+ const char *append);
+
+
+/* This byteorder stuff was lifted from SDL. http://www.libsdl.org/ */
+#define PHYSFS_LIL_ENDIAN 1234
+#define PHYSFS_BIG_ENDIAN 4321
+
+#if defined(__i386__) || defined(__ia64__) || defined(WIN32) || \
+ (defined(__alpha__) || defined(__alpha)) || \
+ defined(__arm__) || defined(ARM) || \
+ (defined(__mips__) && defined(__MIPSEL__)) || \
+ defined(__SYMBIAN32__) || \
+ defined(__x86_64__) || \
+ defined(__LITTLE_ENDIAN__)
+#define PHYSFS_BYTEORDER PHYSFS_LIL_ENDIAN
+#else
+#define PHYSFS_BYTEORDER PHYSFS_BIG_ENDIAN
+#endif
+
+
+/*
+ * When sorting the entries in an archive, we use a modified QuickSort.
+ * When there are less then PHYSFS_QUICKSORT_THRESHOLD entries left to sort,
+ * we switch over to a BubbleSort for the remainder. Tweak to taste.
+ *
+ * You can override this setting by defining PHYSFS_QUICKSORT_THRESHOLD
+ * before #including "physfs_internal.h".
+ */
+#ifndef PHYSFS_QUICKSORT_THRESHOLD
+#define PHYSFS_QUICKSORT_THRESHOLD 4
+#endif
+
+/*
+ * Sort an array (or whatever) of (max) elements. This uses a mixture of
+ * a QuickSort and BubbleSort internally.
+ * (cmpfn) is used to determine ordering, and (swapfn) does the actual
+ * swapping of elements in the list.
+ *
+ * See zip.c for an example.
+ */
+void __PHYSFS_sort(void *entries, PHYSFS_uint32 max,
+ int (*cmpfn)(void *, PHYSFS_uint32, PHYSFS_uint32),
+ void (*swapfn)(void *, PHYSFS_uint32, PHYSFS_uint32));
+
+
+/* These get used all over for lessening code clutter. */
+#define BAIL_MACRO(e, r) { __PHYSFS_setError(e); return r; }
+#define BAIL_IF_MACRO(c, e, r) if (c) { __PHYSFS_setError(e); return r; }
+#define BAIL_MACRO_MUTEX(e, m, r) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); return r; }
+#define BAIL_IF_MACRO_MUTEX(c, e, m, r) if (c) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); return r; }
+#define GOTO_MACRO(e, g) { __PHYSFS_setError(e); goto g; }
+#define GOTO_IF_MACRO(c, e, g) if (c) { __PHYSFS_setError(e); goto g; }
+#define GOTO_MACRO_MUTEX(e, m, g) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); goto g; }
+#define GOTO_IF_MACRO_MUTEX(c, e, m, g) if (c) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); goto g; }
+
+#define __PHYSFS_ARRAYLEN(x) ( (sizeof (x)) / (sizeof (x[0])) )
+
+#if (defined __GNUC__)
+#define __PHYSFS_SI64(x) x##LL
+#define __PHYSFS_UI64(x) x##ULL
+#elif (defined _MSC_VER)
+#define __PHYSFS_SI64(x) x##i64
+#define __PHYSFS_UI64(x) x##ui64
+#else
+#define __PHYSFS_SI64(x) x
+#define __PHYSFS_UI64(x) x
+#endif
+
+/*
+ * Check if a ui64 will fit in the platform's address space.
+ * The initial sizeof check will optimize this macro out entirely on
+ * 64-bit (and larger?!) platforms, and the other condition will
+ * return zero or non-zero if the variable will fit in the platform's
+ * size_t, suitable to pass to malloc. This is kinda messy, but effective.
+ */
+#define __PHYSFS_ui64FitsAddressSpace(s) ( \
+ (sizeof (PHYSFS_uint64) > sizeof (size_t)) && \
+ ((s) > (__PHYSFS_UI64(0xFFFFFFFFFFFFFFFF) >> (64-(sizeof(size_t)*8)))) \
+)
+
+/*
+ * This is a strcasecmp() or stricmp() replacement that expects both strings
+ * to be in UTF-8 encoding. It will do "case folding" to decide if the
+ * Unicode codepoints in the strings match.
+ *
+ * It will report which string is "greater than" the other, but be aware that
+ * this doesn't necessarily mean anything: 'a' may be "less than" 'b', but
+ * a random Kanji codepoint has no meaningful alphabetically relationship to
+ * a Greek Lambda, but being able to assign a reliable "value" makes sorting
+ * algorithms possible, if not entirely sane. Most cases should treat the
+ * return value as "equal" or "not equal".
+ */
+int __PHYSFS_utf8strcasecmp(const char *s1, const char *s2);
+
+/*
+ * This works like __PHYSFS_utf8strcasecmp(), but takes a character (NOT BYTE
+ * COUNT) argument, like strcasencmp().
+ */
+int __PHYSFS_utf8strnicmp(const char *s1, const char *s2, PHYSFS_uint32 l);
+
+/*
+ * stricmp() that guarantees to only work with low ASCII. The C runtime
+ * stricmp() might try to apply a locale/codepage/etc, which we don't want.
+ */
+int __PHYSFS_stricmpASCII(const char *s1, const char *s2);
+
+/*
+ * strnicmp() that guarantees to only work with low ASCII. The C runtime
+ * strnicmp() might try to apply a locale/codepage/etc, which we don't want.
+ */
+int __PHYSFS_strnicmpASCII(const char *s1, const char *s2, PHYSFS_uint32 l);
+
+
+/*
+ * The current allocator. Not valid before PHYSFS_init is called!
+ */
+extern PHYSFS_Allocator __PHYSFS_AllocatorHooks;
+
+/* convenience macro to make this less cumbersome internally... */
+#define allocator __PHYSFS_AllocatorHooks
+
+/*--------------------------------------------------------------------------*/
+/*--------------------------------------------------------------------------*/
+/*------------ ----------------*/
+/*------------ You MUST implement the following functions ----------------*/
+/*------------ if porting to a new platform. ----------------*/
+/*------------ (see platform/unix.c for an example) ----------------*/
+/*------------ ----------------*/
+/*--------------------------------------------------------------------------*/
+/*--------------------------------------------------------------------------*/
+
+
+/*
+ * The dir separator; "/" on unix, "\\" on win32, ":" on MacOS, etc...
+ * Obviously, this isn't a function, but it IS a null-terminated string.
+ */
+extern const char *__PHYSFS_platformDirSeparator;
+
+
+/*
+ * Initialize the platform. This is called when PHYSFS_init() is called from
+ * the application. You can use this to (for example) determine what version
+ * of Windows you're running.
+ *
+ * Return zero if there was a catastrophic failure (which prevents you from
+ * functioning at all), and non-zero otherwise.
+ */
+int __PHYSFS_platformInit(void);
+
+
+/*
+ * Deinitialize the platform. This is called when PHYSFS_deinit() is called
+ * from the application. You can use this to clean up anything you've
+ * allocated in your platform driver.
+ *
+ * Return zero if there was a catastrophic failure (which prevents you from
+ * functioning at all), and non-zero otherwise.
+ */
+int __PHYSFS_platformDeinit(void);
+
+
+/*
+ * Open a file for reading. (filename) is in platform-dependent notation. The
+ * file pointer should be positioned on the first byte of the file.
+ *
+ * The return value will be some platform-specific datatype that is opaque to
+ * the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32.
+ *
+ * The same file can be opened for read multiple times, and each should have
+ * a unique file handle; this is frequently employed to prevent race
+ * conditions in the archivers.
+ *
+ * Call __PHYSFS_setError() and return (NULL) if the file can't be opened.
+ */
+void *__PHYSFS_platformOpenRead(const char *filename);
+
+
+/*
+ * Open a file for writing. (filename) is in platform-dependent notation. If
+ * the file exists, it should be truncated to zero bytes, and if it doesn't
+ * exist, it should be created as a zero-byte file. The file pointer should
+ * be positioned on the first byte of the file.
+ *
+ * The return value will be some platform-specific datatype that is opaque to
+ * the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32,
+ * etc.
+ *
+ * Opening a file for write multiple times has undefined results.
+ *
+ * Call __PHYSFS_setError() and return (NULL) if the file can't be opened.
+ */
+void *__PHYSFS_platformOpenWrite(const char *filename);
+
+
+/*
+ * Open a file for appending. (filename) is in platform-dependent notation. If
+ * the file exists, the file pointer should be place just past the end of the
+ * file, so that the first write will be one byte after the current end of
+ * the file. If the file doesn't exist, it should be created as a zero-byte
+ * file. The file pointer should be positioned on the first byte of the file.
+ *
+ * The return value will be some platform-specific datatype that is opaque to
+ * the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32,
+ * etc.
+ *
+ * Opening a file for append multiple times has undefined results.
+ *
+ * Call __PHYSFS_setError() and return (NULL) if the file can't be opened.
+ */
+void *__PHYSFS_platformOpenAppend(const char *filename);
+
+
+/*
+ * Read more data from a platform-specific file handle. (opaque) should be
+ * cast to whatever data type your platform uses. Read a maximum of (count)
+ * objects of (size) 8-bit bytes to the area pointed to by (buffer). If there
+ * isn't enough data available, return the number of full objects read, and
+ * position the file pointer at the start of the first incomplete object.
+ * On success, return (count) and position the file pointer one byte past
+ * the end of the last read object. Return (-1) if there is a catastrophic
+ * error, and call __PHYSFS_setError() to describe the problem; the file
+ * pointer should not move in such a case.
+ */
+PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
+ PHYSFS_uint32 size, PHYSFS_uint32 count);
+
+/*
+ * Write more data to a platform-specific file handle. (opaque) should be
+ * cast to whatever data type your platform uses. Write a maximum of (count)
+ * objects of (size) 8-bit bytes from the area pointed to by (buffer). If
+ * there isn't enough data available, return the number of full objects
+ * written, and position the file pointer at the start of the first
+ * incomplete object. Return (-1) if there is a catastrophic error, and call
+ * __PHYSFS_setError() to describe the problem; the file pointer should not
+ * move in such a case.
+ */
+PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
+ PHYSFS_uint32 size, PHYSFS_uint32 count);
+
+/*
+ * Set the file pointer to a new position. (opaque) should be cast to
+ * whatever data type your platform uses. (pos) specifies the number
+ * of 8-bit bytes to seek to from the start of the file. Seeking past the
+ * end of the file is an error condition, and you should check for it.
+ *
+ * Not all file types can seek; this is to be expected by the caller.
+ *
+ * On error, call __PHYSFS_setError() and return zero. On success, return
+ * a non-zero value.
+ */
+int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos);
+
+
+/*
+ * Get the file pointer's position, in an 8-bit byte offset from the start of
+ * the file. (opaque) should be cast to whatever data type your platform
+ * uses.
+ *
+ * Not all file types can "tell"; this is to be expected by the caller.
+ *
+ * On error, call __PHYSFS_setError() and return zero. On success, return
+ * a non-zero value.
+ */
+PHYSFS_sint64 __PHYSFS_platformTell(void *opaque);
+
+
+/*
+ * Determine the current size of a file, in 8-bit bytes, from an open file.
+ *
+ * The caller expects that this information may not be available for all
+ * file types on all platforms.
+ *
+ * Return -1 if you can't do it, and call __PHYSFS_setError(). Otherwise,
+ * return the file length in 8-bit bytes.
+ */
+PHYSFS_sint64 __PHYSFS_platformFileLength(void *handle);
+
+/*
+ * Determine if a file is at EOF. (opaque) should be cast to whatever data
+ * type your platform uses.
+ *
+ * The caller expects that there was a short read before calling this.
+ *
+ * Return non-zero if EOF, zero if it is _not_ EOF.
+ */
+int __PHYSFS_platformEOF(void *opaque);
+
+/*
+ * Flush any pending writes to disk. (opaque) should be cast to whatever data
+ * type your platform uses. Be sure to check for errors; the caller expects
+ * that this function can fail if there was a flushing error, etc.
+ *
+ * Return zero on failure, non-zero on success.
+ */
+int __PHYSFS_platformFlush(void *opaque);
+
+/*
+ * Flush and close a file. (opaque) should be cast to whatever data type
+ * your platform uses. Be sure to check for errors when closing; the
+ * caller expects that this function can fail if there was a flushing
+ * error, etc.
+ *
+ * You should clean up all resources associated with (opaque).
+ *
+ * Return zero on failure, non-zero on success.
+ */
+int __PHYSFS_platformClose(void *opaque);
+
+/*
+ * Platform implementation of PHYSFS_getCdRomDirsCallback()...
+ * CD directories are discovered and reported to the callback one at a time.
+ * Pointers passed to the callback are assumed to be invalid to the
+ * application after the callback returns, so you can free them or whatever.
+ * Callback does not assume results will be sorted in any meaningful way.
+ */
+void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data);
+
+/*
+ * Calculate the base dir, if your platform needs special consideration.
+ * Just return NULL if the standard routines will suffice. (see
+ * calculateBaseDir() in physfs.c ...)
+ * Caller will free() the retval if it's not NULL.
+ */
+char *__PHYSFS_platformCalcBaseDir(const char *argv0);
+
+/*
+ * Get the platform-specific user name.
+ * Caller will free() the retval if it's not NULL. If it's NULL, the username
+ * will default to "default".
+ */
+char *__PHYSFS_platformGetUserName(void);
+
+/*
+ * Get the platform-specific user dir.
+ * Caller will free() the retval if it's not NULL. If it's NULL, the userdir
+ * will default to basedir/username.
+ */
+char *__PHYSFS_platformGetUserDir(void);
+
+/*
+ * Return a number that uniquely identifies the current thread.
+ * On a platform without threading, (1) will suffice. These numbers are
+ * arbitrary; the only requirement is that no two threads have the same
+ * number.
+ */
+PHYSFS_uint64 __PHYSFS_platformGetThreadID(void);
+
+/*
+ * Return non-zero if filename (in platform-dependent notation) exists.
+ * Symlinks should NOT be followed; at this stage, we do not care what the
+ * symlink points to. Please call __PHYSFS_SetError() with the details of
+ * why the file does not exist, if it doesn't; you are in a better position
+ * to know (path not found, bogus filename, file itself is missing, etc).
+ */
+int __PHYSFS_platformExists(const char *fname);
+
+/*
+ * Return the last modified time (in seconds since the epoch) of a file.
+ * Returns -1 on failure. (fname) is in platform-dependent notation.
+ * Symlinks should be followed; if what the symlink points to is missing,
+ * then the retval is -1.
+ */
+PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname);
+
+/*
+ * Return non-zero if filename (in platform-dependent notation) is a symlink.
+ */
+int __PHYSFS_platformIsSymLink(const char *fname);
+
+
+/*
+ * Return non-zero if filename (in platform-dependent notation) is a symlink.
+ * Symlinks should be followed; if what the symlink points to is missing,
+ * or isn't a directory, then the retval is false.
+ */
+int __PHYSFS_platformIsDirectory(const char *fname);
+
+
+/*
+ * Convert (dirName) to platform-dependent notation, then prepend (prepend)
+ * and append (append) to the converted string.
+ *
+ * So, on Win32, calling:
+ * __PHYSFS_platformCvtToDependent("C:\", "my/files", NULL);
+ * ...will return the string "C:\my\files".
+ *
+ * This can be implemented in a platform-specific manner, so you can get
+ * get a speed boost that the default implementation can't, since
+ * you can make assumptions about the size of strings, etc..
+ *
+ * Platforms that choose not to implement this may just call
+ * __PHYSFS_convertToDependent() as a passthrough, which may fit the bill
+ * already.
+ *
+ * Be sure to free() the return value when done with it.
+ */
+char *__PHYSFS_platformCvtToDependent(const char *prepend,
+ const char *dirName,
+ const char *append);
+
+
+/*
+ * Enumerate a directory of files. This follows the rules for the
+ * PHYSFS_Archiver->enumerateFiles() method (see above), except that the
+ * (dirName) that is passed to this function is converted to
+ * platform-DEPENDENT notation by the caller. The PHYSFS_Archiver version
+ * uses platform-independent notation. Note that ".", "..", and other
+ * metaentries should always be ignored.
+ */
+void __PHYSFS_platformEnumerateFiles(const char *dirname,
+ int omitSymLinks,
+ PHYSFS_EnumFilesCallback callback,
+ const char *origdir,
+ void *callbackdata);
+
+
+/*
+ * Get the current working directory. The return value should be an
+ * absolute path in platform-dependent notation. The caller will deallocate
+ * the return value with the standard C runtime free() function when it
+ * is done with it.
+ * On error, return NULL and set the error message.
+ */
+char *__PHYSFS_platformCurrentDir(void);
+
+
+/*
+ * Get the real physical path to a file. (path) is specified in
+ * platform-dependent notation, as should your return value be.
+ * All relative paths should be removed, leaving you with an absolute
+ * path. Symlinks should be resolved, too, so that the returned value is
+ * the most direct path to a file.
+ * The return value will be deallocated with the standard C runtime free()
+ * function when the caller is done with it.
+ * On error, return NULL and set the error message.
+ */
+char *__PHYSFS_platformRealPath(const char *path);
+
+
+/*
+ * Make a directory in the actual filesystem. (path) is specified in
+ * platform-dependent notation. On error, return zero and set the error
+ * message. Return non-zero on success.
+ */
+int __PHYSFS_platformMkDir(const char *path);
+
+
+/*
+ * Remove a file or directory entry in the actual filesystem. (path) is
+ * specified in platform-dependent notation. Note that this deletes files
+ * _and_ directories, so you might need to do some determination.
+ * Non-empty directories should report an error and not delete themselves
+ * or their contents.
+ *
+ * Deleting a symlink should remove the link, not what it points to.
+ *
+ * On error, return zero and set the error message. Return non-zero on success.
+ */
+int __PHYSFS_platformDelete(const char *path);
+
+
+/*
+ * Create a platform-specific mutex. This can be whatever datatype your
+ * platform uses for mutexes, but it is cast to a (void *) for abstractness.
+ *
+ * Return (NULL) if you couldn't create one. Systems without threads can
+ * return any arbitrary non-NULL value.
+ */
+void *__PHYSFS_platformCreateMutex(void);
+
+/*
+ * Destroy a platform-specific mutex, and clean up any resources associated
+ * with it. (mutex) is a value previously returned by
+ * __PHYSFS_platformCreateMutex(). This can be a no-op on single-threaded
+ * platforms.
+ */
+void __PHYSFS_platformDestroyMutex(void *mutex);
+
+/*
+ * Grab possession of a platform-specific mutex. Mutexes should be recursive;
+ * that is, the same thread should be able to call this function multiple
+ * times in a row without causing a deadlock. This function should block
+ * until a thread can gain possession of the mutex.
+ *
+ * Return non-zero if the mutex was grabbed, zero if there was an
+ * unrecoverable problem grabbing it (this should not be a matter of
+ * timing out! We're talking major system errors; block until the mutex
+ * is available otherwise.)
+ *
+ * _DO NOT_ call __PHYSFS_setError() in here! Since setError calls this
+ * function, you'll cause an infinite recursion. This means you can't
+ * use the BAIL_*MACRO* macros, either.
+ */
+int __PHYSFS_platformGrabMutex(void *mutex);
+
+/*
+ * Relinquish possession of the mutex when this method has been called
+ * once for each time that platformGrabMutex was called. Once possession has
+ * been released, the next thread in line to grab the mutex (if any) may
+ * proceed.
+ *
+ * _DO NOT_ call __PHYSFS_setError() in here! Since setError calls this
+ * function, you'll cause an infinite recursion. This means you can't
+ * use the BAIL_*MACRO* macros, either.
+ */
+void __PHYSFS_platformReleaseMutex(void *mutex);
+
+/*
+ * Called at the start of PHYSFS_init() to prepare the allocator, if the user
+ * hasn't selected their own allocator via PHYSFS_setAllocator().
+ * If the platform has a custom allocator, it should fill in the fields of
+ * (a) with the proper function pointers and return non-zero.
+ * If the platform just wants to use malloc()/free()/etc, return zero
+ * immediately and the higher level will handle it. The Init and Deinit
+ * fields of (a) are optional...set them to NULL if you don't need them.
+ * Everything else must be implemented. All rules follow those for
+ * PHYSFS_setAllocator(). If Init isn't NULL, it will be called shortly
+ * after this function returns non-zero.
+ */
+int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/* end of physfs_internal.h ... */
+
--- /dev/null
+#ifndef _INCL_PHYSFS_PLATFORMS
+#define _INCL_PHYSFS_PLATFORMS
+
+#ifndef __PHYSICSFS_INTERNAL__
+#error Do not include this header from your applications.
+#endif
+
+/*
+ * These only define the platforms to determine which files in the platforms
+ * directory should be compiled. For example, technically BeOS can be called
+ * a "unix" system, but since it doesn't use unix.c, we don't define
+ * PHYSFS_PLATFORM_UNIX on that system.
+ */
+
+#if ((defined __BEOS__) || (defined __beos__))
+# define PHYSFS_PLATFORM_BEOS
+# define PHYSFS_PLATFORM_POSIX
+#elif (defined _WIN32_WCE) || (defined _WIN64_WCE)
+# define PHYSFS_PLATFORM_POCKETPC
+#elif (((defined _WIN32) || (defined _WIN64)) && (!defined __CYGWIN__))
+# define PHYSFS_PLATFORM_WINDOWS
+#elif (defined OS2)
+# define PHYSFS_PLATFORM_OS2
+#elif ((defined __MACH__) && (defined __APPLE__))
+# define PHYSFS_PLATFORM_MACOSX
+# define PHYSFS_PLATFORM_POSIX
+#elif defined(macintosh)
+# error Classic Mac OS support was dropped from PhysicsFS 2.0. Move to OS X.
+#elif defined(unix)
+# define PHYSFS_PLATFORM_UNIX
+# define PHYSFS_PLATFORM_POSIX
+#else
+# error Unknown platform.
+#endif
+
+#endif /* include-once blocker. */
+
--- /dev/null
+#include "physfs.h"
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_internal.h"
+
+
+/*
+ * From rfc3629, the UTF-8 spec:
+ * http://www.ietf.org/rfc/rfc3629.txt
+ *
+ * Char. number range | UTF-8 octet sequence
+ * (hexadecimal) | (binary)
+ * --------------------+---------------------------------------------
+ * 0000 0000-0000 007F | 0xxxxxxx
+ * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
+ * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
+ * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ */
+
+
+/*
+ * This may not be the best value, but it's one that isn't represented
+ * in Unicode (0x10FFFF is the largest codepoint value). We return this
+ * value from utf8codepoint() if there's bogus bits in the
+ * stream. utf8codepoint() will turn this value into something
+ * reasonable (like a question mark), for text that wants to try to recover,
+ * whereas utf8valid() will use the value to determine if a string has bad
+ * bits.
+ */
+#define UNICODE_BOGUS_CHAR_VALUE 0xFFFFFFFF
+
+/*
+ * This is the codepoint we currently return when there was bogus bits in a
+ * UTF-8 string. May not fly in Asian locales?
+ */
+#define UNICODE_BOGUS_CHAR_CODEPOINT '?'
+
+static PHYSFS_uint32 utf8codepoint(const char **_str)
+{
+ const char *str = *_str;
+ PHYSFS_uint32 retval = 0;
+ PHYSFS_uint32 octet = (PHYSFS_uint32) ((PHYSFS_uint8) *str);
+ PHYSFS_uint32 octet2, octet3, octet4;
+
+ if (octet == 0) /* null terminator, end of string. */
+ return 0;
+
+ else if (octet < 128) /* one octet char: 0 to 127 */
+ {
+ (*_str)++; /* skip to next possible start of codepoint. */
+ return(octet);
+ } /* else if */
+
+ else if ((octet > 127) && (octet < 192)) /* bad (starts with 10xxxxxx). */
+ {
+ /*
+ * Apparently each of these is supposed to be flagged as a bogus
+ * char, instead of just resyncing to the next valid codepoint.
+ */
+ (*_str)++; /* skip to next possible start of codepoint. */
+ return UNICODE_BOGUS_CHAR_VALUE;
+ } /* else if */
+
+ else if (octet < 224) /* two octets */
+ {
+ octet -= (128+64);
+ octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ *_str += 2; /* skip to next possible start of codepoint. */
+ retval = ((octet << 6) | (octet2 - 128));
+ if ((retval >= 0x80) && (retval <= 0x7FF))
+ return retval;
+ } /* else if */
+
+ else if (octet < 240) /* three octets */
+ {
+ octet -= (128+64+32);
+ octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ octet3 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet3 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ *_str += 3; /* skip to next possible start of codepoint. */
+ retval = ( ((octet << 12)) | ((octet2-128) << 6) | ((octet3-128)) );
+
+ /* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */
+ switch (retval)
+ {
+ case 0xD800:
+ case 0xDB7F:
+ case 0xDB80:
+ case 0xDBFF:
+ case 0xDC00:
+ case 0xDF80:
+ case 0xDFFF:
+ return UNICODE_BOGUS_CHAR_VALUE;
+ } /* switch */
+
+ /* 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge. */
+ if ((retval >= 0x800) && (retval <= 0xFFFD))
+ return retval;
+ } /* else if */
+
+ else if (octet < 248) /* four octets */
+ {
+ octet -= (128+64+32+16);
+ octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ octet3 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet3 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ octet4 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet4 & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ *_str += 4; /* skip to next possible start of codepoint. */
+ retval = ( ((octet << 18)) | ((octet2 - 128) << 12) |
+ ((octet3 - 128) << 6) | ((octet4 - 128)) );
+ if ((retval >= 0x10000) && (retval <= 0x10FFFF))
+ return retval;
+ } /* else if */
+
+ /*
+ * Five and six octet sequences became illegal in rfc3629.
+ * We throw the codepoint away, but parse them to make sure we move
+ * ahead the right number of bytes and don't overflow the buffer.
+ */
+
+ else if (octet < 252) /* five octets */
+ {
+ octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ *_str += 5; /* skip to next possible start of codepoint. */
+ return UNICODE_BOGUS_CHAR_VALUE;
+ } /* else if */
+
+ else /* six octets */
+ {
+ octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str));
+ if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */
+ return UNICODE_BOGUS_CHAR_VALUE;
+
+ *_str += 6; /* skip to next possible start of codepoint. */
+ return UNICODE_BOGUS_CHAR_VALUE;
+ } /* else if */
+
+ return UNICODE_BOGUS_CHAR_VALUE;
+} /* utf8codepoint */
+
+
+void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, PHYSFS_uint64 len)
+{
+ len -= sizeof (PHYSFS_uint32); /* save room for null char. */
+ while (len >= sizeof (PHYSFS_uint32))
+ {
+ PHYSFS_uint32 cp = utf8codepoint(&src);
+ if (cp == 0)
+ break;
+ else if (cp == UNICODE_BOGUS_CHAR_VALUE)
+ cp = UNICODE_BOGUS_CHAR_CODEPOINT;
+ *(dst++) = cp;
+ len -= sizeof (PHYSFS_uint32);
+ } /* while */
+
+ *dst = 0;
+} /* PHYSFS_utf8ToUcs4 */
+
+
+void PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len)
+{
+ len -= sizeof (PHYSFS_uint16); /* save room for null char. */
+ while (len >= sizeof (PHYSFS_uint16))
+ {
+ PHYSFS_uint32 cp = utf8codepoint(&src);
+ if (cp == 0)
+ break;
+ else if (cp == UNICODE_BOGUS_CHAR_VALUE)
+ cp = UNICODE_BOGUS_CHAR_CODEPOINT;
+
+ /* !!! BLUESKY: UTF-16 surrogates? */
+ if (cp > 0xFFFF)
+ cp = UNICODE_BOGUS_CHAR_CODEPOINT;
+
+ *(dst++) = cp;
+ len -= sizeof (PHYSFS_uint16);
+ } /* while */
+
+ *dst = 0;
+} /* PHYSFS_utf8ToUcs2 */
+
+static void utf8fromcodepoint(PHYSFS_uint32 cp, char **_dst, PHYSFS_uint64 *_len)
+{
+ char *dst = *_dst;
+ PHYSFS_uint64 len = *_len;
+
+ if (len == 0)
+ return;
+
+ if (cp > 0x10FFFF)
+ cp = UNICODE_BOGUS_CHAR_CODEPOINT;
+ else if ((cp == 0xFFFE) || (cp == 0xFFFF)) /* illegal values. */
+ cp = UNICODE_BOGUS_CHAR_CODEPOINT;
+ else
+ {
+ /* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */
+ switch (cp)
+ {
+ case 0xD800:
+ case 0xDB7F:
+ case 0xDB80:
+ case 0xDBFF:
+ case 0xDC00:
+ case 0xDF80:
+ case 0xDFFF:
+ cp = UNICODE_BOGUS_CHAR_CODEPOINT;
+ } /* switch */
+ } /* else */
+
+ /* Do the encoding... */
+ if (cp < 0x80)
+ {
+ *(dst++) = (char) cp;
+ len--;
+ } /* if */
+
+ else if (cp < 0x800)
+ {
+ if (len < 2)
+ len = 0;
+ else
+ {
+ *(dst++) = (char) ((cp >> 6) | 128 | 64);
+ *(dst++) = (char) (cp & 0x3F) | 128;
+ len -= 2;
+ } /* else */
+ } /* else if */
+
+ else if (cp < 0x10000)
+ {
+ if (len < 3)
+ len = 0;
+ else
+ {
+ *(dst++) = (char) ((cp >> 12) | 128 | 64 | 32);
+ *(dst++) = (char) ((cp >> 6) & 0x3F) | 128;
+ *(dst++) = (char) (cp & 0x3F) | 128;
+ len -= 3;
+ } /* else */
+ } /* else if */
+
+ else
+ {
+ if (len < 4)
+ len = 0;
+ else
+ {
+ *(dst++) = (char) ((cp >> 18) | 128 | 64 | 32 | 16);
+ *(dst++) = (char) ((cp >> 12) & 0x3F) | 128;
+ *(dst++) = (char) ((cp >> 6) & 0x3F) | 128;
+ *(dst++) = (char) (cp & 0x3F) | 128;
+ len -= 4;
+ } /* else if */
+ } /* else */
+
+ *_dst = dst;
+ *_len = len;
+} /* utf8fromcodepoint */
+
+#define UTF8FROMTYPE(typ, src, dst, len) \
+ len--; \
+ while (len) \
+ { \
+ const PHYSFS_uint32 cp = (PHYSFS_uint32) *(src++); \
+ if (cp == 0) break; \
+ utf8fromcodepoint(cp, &dst, &len); \
+ } \
+ *dst = '\0'; \
+
+void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, PHYSFS_uint64 len)
+{
+ UTF8FROMTYPE(PHYSFS_uint32, src, dst, len);
+} /* PHYSFS_utf8FromUcs4 */
+
+void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len)
+{
+ UTF8FROMTYPE(PHYSFS_uint64, src, dst, len);
+} /* PHYSFS_utf8FromUcs4 */
+
+/* latin1 maps to unicode codepoints directly, we just utf-8 encode it. */
+void PHYSFS_utf8FromLatin1(const char *src, char *dst, PHYSFS_uint64 len)
+{
+ UTF8FROMTYPE(PHYSFS_uint8, src, dst, len);
+} /* PHYSFS_utf8FromLatin1 */
+
+#undef UTF8FROMTYPE
+
+
+typedef struct CaseFoldMapping
+{
+ PHYSFS_uint32 from;
+ PHYSFS_uint32 to0;
+ PHYSFS_uint32 to1;
+ PHYSFS_uint32 to2;
+} CaseFoldMapping;
+
+typedef struct CaseFoldHashBucket
+{
+ const PHYSFS_uint8 count;
+ const CaseFoldMapping *list;
+} CaseFoldHashBucket;
+
+#include "physfs_casefolding.h"
+
+static void locate_case_fold_mapping(const PHYSFS_uint32 from,
+ PHYSFS_uint32 *to)
+{
+ PHYSFS_uint32 i;
+ const PHYSFS_uint8 hashed = ((from ^ (from >> 8)) & 0xFF);
+ const CaseFoldHashBucket *bucket = &case_fold_hash[hashed];
+ const CaseFoldMapping *mapping = bucket->list;
+
+ for (i = 0; i < bucket->count; i++, mapping++)
+ {
+ if (mapping->from == from)
+ {
+ to[0] = mapping->to0;
+ to[1] = mapping->to1;
+ to[2] = mapping->to2;
+ return;
+ } /* if */
+ } /* for */
+
+ /* Not found...there's no remapping for this codepoint. */
+ to[0] = from;
+ to[1] = 0;
+ to[2] = 0;
+} /* locate_case_fold_mapping */
+
+
+static int utf8codepointcmp(const PHYSFS_uint32 cp1, const PHYSFS_uint32 cp2)
+{
+ PHYSFS_uint32 folded1[3], folded2[3];
+ locate_case_fold_mapping(cp1, folded1);
+ locate_case_fold_mapping(cp2, folded2);
+ return ( (folded1[0] == folded2[0]) &&
+ (folded1[1] == folded2[1]) &&
+ (folded1[2] == folded2[2]) );
+} /* utf8codepointcmp */
+
+
+int __PHYSFS_utf8strcasecmp(const char *str1, const char *str2)
+{
+ while (1)
+ {
+ const PHYSFS_uint32 cp1 = utf8codepoint(&str1);
+ const PHYSFS_uint32 cp2 = utf8codepoint(&str2);
+ if (!utf8codepointcmp(cp1, cp2)) return 0;
+ if (cp1 == 0) return 1;
+ } /* while */
+
+ return 0; /* shouldn't hit this. */
+} /* __PHYSFS_utf8strcasecmp */
+
+
+int __PHYSFS_utf8strnicmp(const char *str1, const char *str2, PHYSFS_uint32 n)
+{
+ while (n > 0)
+ {
+ const PHYSFS_uint32 cp1 = utf8codepoint(&str1);
+ const PHYSFS_uint32 cp2 = utf8codepoint(&str2);
+ if (!utf8codepointcmp(cp1, cp2)) return 0;
+ if (cp1 == 0) return 1;
+ n--;
+ } /* while */
+
+ return 1; /* matched to n chars. */
+} /* __PHYSFS_utf8strnicmp */
+
+
+int __PHYSFS_stricmpASCII(const char *str1, const char *str2)
+{
+ while (1)
+ {
+ const char ch1 = *(str1++);
+ const char ch2 = *(str2++);
+ const char cp1 = ((ch1 >= 'A') && (ch1 <= 'Z')) ? (ch1+32) : ch1;
+ const char cp2 = ((ch2 >= 'A') && (ch2 <= 'Z')) ? (ch2+32) : ch2;
+ if (cp1 < cp2)
+ return -1;
+ else if (cp1 > cp2)
+ return 1;
+ else if (cp1 == 0) /* they're both null chars? */
+ return 0;
+ } /* while */
+
+ return 0; /* shouldn't hit this. */
+} /* __PHYSFS_stricmpASCII */
+
+
+int __PHYSFS_strnicmpASCII(const char *str1, const char *str2, PHYSFS_uint32 n)
+{
+ while (n-- > 0)
+ {
+ const char ch1 = *(str1++);
+ const char ch2 = *(str2++);
+ const char cp1 = ((ch1 >= 'A') && (ch1 <= 'Z')) ? (ch1+32) : ch1;
+ const char cp2 = ((ch2 >= 'A') && (ch2 <= 'Z')) ? (ch2+32) : ch2;
+ if (cp1 < cp2)
+ return -1;
+ else if (cp1 > cp2)
+ return 1;
+ else if (cp1 == 0) /* they're both null chars? */
+ return 0;
+ } /* while */
+
+ return 0;
+} /* __PHYSFS_stricmpASCII */
+
+
+/* end of physfs_unicode.c ... */
+
--- /dev/null
+/*
+ * BeOS platform-dependent support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_platforms.h"
+
+#ifdef PHYSFS_PLATFORM_BEOS
+
+#include <be/kernel/OS.h>
+#include <be/app/Roster.h>
+#include <be/storage/Volume.h>
+#include <be/storage/VolumeRoster.h>
+#include <be/storage/Directory.h>
+#include <be/storage/Entry.h>
+#include <be/storage/Path.h>
+#include <be/kernel/fs_info.h>
+#include <be/device/scsi.h>
+#include <be/support/Locker.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "physfs_internal.h"
+
+
+int __PHYSFS_platformInit(void)
+{
+ return(1); /* always succeed. */
+} /* __PHYSFS_platformInit */
+
+
+int __PHYSFS_platformDeinit(void)
+{
+ return(1); /* always succeed. */
+} /* __PHYSFS_platformDeinit */
+
+
+static char *getMountPoint(const char *devname)
+{
+ BVolumeRoster mounts;
+ BVolume vol;
+
+ mounts.Rewind();
+ while (mounts.GetNextVolume(&vol) == B_NO_ERROR)
+ {
+ fs_info fsinfo;
+ fs_stat_dev(vol.Device(), &fsinfo);
+ if (strcmp(devname, fsinfo.device_name) == 0)
+ {
+ //char buf[B_FILE_NAME_LENGTH];
+ BDirectory directory;
+ BEntry entry;
+ BPath path;
+ status_t rc;
+ rc = vol.GetRootDirectory(&directory);
+ BAIL_IF_MACRO(rc < B_OK, strerror(rc), NULL);
+ rc = directory.GetEntry(&entry);
+ BAIL_IF_MACRO(rc < B_OK, strerror(rc), NULL);
+ rc = entry.GetPath(&path);
+ BAIL_IF_MACRO(rc < B_OK, strerror(rc), NULL);
+ const char *str = path.Path();
+ BAIL_IF_MACRO(str == NULL, ERR_OS_ERROR, NULL); /* ?! */
+ char *retval = (char *) allocator.Malloc(strlen(str) + 1);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+ strcpy(retval, str);
+ return(retval);
+ } /* if */
+ } /* while */
+
+ return(NULL);
+} /* getMountPoint */
+
+
+ /*
+ * This function is lifted from Simple Directmedia Layer (SDL):
+ * http://www.libsdl.org/
+ */
+static void tryDir(const char *d, PHYSFS_StringCallback callback, void *data)
+{
+ BDirectory dir;
+ dir.SetTo(d);
+ if (dir.InitCheck() != B_NO_ERROR)
+ return;
+
+ dir.Rewind();
+ BEntry entry;
+ while (dir.GetNextEntry(&entry) >= 0)
+ {
+ BPath path;
+ const char *name;
+ entry_ref e;
+
+ if (entry.GetPath(&path) != B_NO_ERROR)
+ continue;
+
+ name = path.Path();
+
+ if (entry.GetRef(&e) != B_NO_ERROR)
+ continue;
+
+ if (entry.IsDirectory())
+ {
+ if (strcmp(e.name, "floppy") != 0)
+ tryDir(name, callback, data);
+ } /* if */
+
+ else
+ {
+ bool add_it = false;
+ int devfd;
+ device_geometry g;
+
+ if (strcmp(e.name, "raw") == 0) /* ignore partitions. */
+ {
+ int devfd = open(name, O_RDONLY);
+ if (devfd >= 0)
+ {
+ if (ioctl(devfd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0)
+ {
+ if (g.device_type == B_CD)
+ {
+ char *mntpnt = getMountPoint(name);
+ if (mntpnt != NULL)
+ {
+ callback(data, mntpnt);
+ allocator.Free(mntpnt); /* !!! FIXME: lose this malloc! */
+ } /* if */
+ } /* if */
+ } /* if */
+ } /* if */
+ } /* if */
+
+ close(devfd);
+ } /* else */
+ } /* while */
+} /* tryDir */
+
+
+void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
+{
+ tryDir("/dev/disk", cb, data);
+} /* __PHYSFS_platformDetectAvailableCDs */
+
+
+static team_id getTeamID(void)
+{
+ thread_info info;
+ thread_id tid = find_thread(NULL);
+ get_thread_info(tid, &info);
+ return(info.team);
+} /* getTeamID */
+
+
+char *__PHYSFS_platformCalcBaseDir(const char *argv0)
+{
+ /* in case there isn't a BApplication yet, we'll construct a roster. */
+ BRoster roster;
+ app_info info;
+ status_t rc = roster.GetRunningAppInfo(getTeamID(), &info);
+ BAIL_IF_MACRO(rc < B_OK, strerror(rc), NULL);
+ BEntry entry(&(info.ref), true);
+ BPath path;
+ rc = entry.GetPath(&path); /* (path) now has binary's path. */
+ assert(rc == B_OK);
+ rc = path.GetParent(&path); /* chop filename, keep directory. */
+ assert(rc == B_OK);
+ const char *str = path.Path();
+ assert(str != NULL);
+ char *retval = (char *) allocator.Malloc(strlen(str) + 1);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+ strcpy(retval, str);
+ return(retval);
+} /* __PHYSFS_platformCalcBaseDir */
+
+
+PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
+{
+ return((PHYSFS_uint64) find_thread(NULL));
+} /* __PHYSFS_platformGetThreadID */
+
+
+char *__PHYSFS_platformRealPath(const char *path)
+{
+ BPath normalized(path, NULL, true); /* force normalization of path. */
+ const char *resolved_path = normalized.Path();
+ BAIL_IF_MACRO(resolved_path == NULL, ERR_NO_SUCH_FILE, NULL);
+ char *retval = (char *) allocator.Malloc(strlen(resolved_path) + 1);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+ strcpy(retval, resolved_path);
+ return(retval);
+} /* __PHYSFS_platformRealPath */
+
+
+char *__PHYSFS_platformCurrentDir(void)
+{
+ return(__PHYSFS_platformRealPath(".")); /* let BPath sort it out. */
+} /* __PHYSFS_platformCurrentDir */
+
+
+void *__PHYSFS_platformCreateMutex(void)
+{
+ return(new BLocker("PhysicsFS lock", true));
+} /* __PHYSFS_platformCreateMutex */
+
+
+void __PHYSFS_platformDestroyMutex(void *mutex)
+{
+ delete ((BLocker *) mutex);
+} /* __PHYSFS_platformDestroyMutex */
+
+
+int __PHYSFS_platformGrabMutex(void *mutex)
+{
+ return ((BLocker *) mutex)->Lock() ? 1 : 0;
+} /* __PHYSFS_platformGrabMutex */
+
+
+void __PHYSFS_platformReleaseMutex(void *mutex)
+{
+ ((BLocker *) mutex)->Unlock();
+} /* __PHYSFS_platformReleaseMutex */
+
+
+int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
+{
+ return(0); /* just use malloc() and friends. */
+} /* __PHYSFS_platformSetDefaultAllocator */
+
+#endif /* PHYSFS_PLATFORM_BEOS */
+
+/* end of beos.cpp ... */
+
--- /dev/null
+/*
+ * Mac OS X support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_platforms.h"
+
+#ifdef PHYSFS_PLATFORM_MACOSX
+
+#include <Carbon/Carbon.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IOCDMedia.h>
+#include <IOKit/storage/IODVDMedia.h>
+#include <sys/mount.h>
+
+/* Seems to get defined in some system header... */
+#ifdef Free
+#undef Free
+#endif
+
+#include "physfs_internal.h"
+
+
+/* Wrap PHYSFS_Allocator in a CFAllocator... */
+static CFAllocatorRef cfallocator = NULL;
+
+CFStringRef cfallocDesc(const void *info)
+{
+ return(CFStringCreateWithCString(cfallocator, "PhysicsFS",
+ kCFStringEncodingASCII));
+} /* cfallocDesc */
+
+
+static void *cfallocMalloc(CFIndex allocSize, CFOptionFlags hint, void *info)
+{
+ return allocator.Malloc(allocSize);
+} /* cfallocMalloc */
+
+
+static void cfallocFree(void *ptr, void *info)
+{
+ allocator.Free(ptr);
+} /* cfallocFree */
+
+
+static void *cfallocRealloc(void *ptr, CFIndex newsize,
+ CFOptionFlags hint, void *info)
+{
+ if ((ptr == NULL) || (newsize <= 0))
+ return NULL; /* ADC docs say you should always return NULL here. */
+ return allocator.Realloc(ptr, newsize);
+} /* cfallocRealloc */
+
+
+int __PHYSFS_platformInit(void)
+{
+ /* set up a CFAllocator, so Carbon can use the physfs allocator, too. */
+ CFAllocatorContext ctx;
+ memset(&ctx, '\0', sizeof (ctx));
+ ctx.copyDescription = cfallocDesc;
+ ctx.allocate = cfallocMalloc;
+ ctx.reallocate = cfallocRealloc;
+ ctx.deallocate = cfallocFree;
+ cfallocator = CFAllocatorCreate(kCFAllocatorUseContext, &ctx);
+ BAIL_IF_MACRO(cfallocator == NULL, ERR_OUT_OF_MEMORY, 0);
+ return(1); /* success. */
+} /* __PHYSFS_platformInit */
+
+
+int __PHYSFS_platformDeinit(void)
+{
+ CFRelease(cfallocator);
+ cfallocator = NULL;
+ return(1); /* always succeed. */
+} /* __PHYSFS_platformDeinit */
+
+
+/* CD-ROM detection code... */
+
+/*
+ * Code based on sample from Apple Developer Connection:
+ * http://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm
+ */
+
+static int darwinIsWholeMedia(io_service_t service)
+{
+ int retval = 0;
+ CFTypeRef wholeMedia;
+
+ if (!IOObjectConformsTo(service, kIOMediaClass))
+ return(0);
+
+ wholeMedia = IORegistryEntryCreateCFProperty(service,
+ CFSTR(kIOMediaWholeKey),
+ cfallocator, 0);
+ if (wholeMedia == NULL)
+ return(0);
+
+ retval = CFBooleanGetValue(wholeMedia);
+ CFRelease(wholeMedia);
+
+ return retval;
+} /* darwinIsWholeMedia */
+
+
+static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort)
+{
+ int retval = 0;
+ CFMutableDictionaryRef matchingDict;
+ kern_return_t rc;
+ io_iterator_t iter;
+ io_service_t service;
+
+ if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL)
+ return(0);
+
+ rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter);
+ if ((rc != KERN_SUCCESS) || (!iter))
+ return(0);
+
+ service = IOIteratorNext(iter);
+ IOObjectRelease(iter);
+ if (!service)
+ return(0);
+
+ rc = IORegistryEntryCreateIterator(service, kIOServicePlane,
+ kIORegistryIterateRecursively | kIORegistryIterateParents, &iter);
+
+ if (!iter)
+ return(0);
+
+ if (rc != KERN_SUCCESS)
+ {
+ IOObjectRelease(iter);
+ return(0);
+ } /* if */
+
+ IOObjectRetain(service); /* add an extra object reference... */
+
+ do
+ {
+ if (darwinIsWholeMedia(service))
+ {
+ if ( (IOObjectConformsTo(service, kIOCDMediaClass)) ||
+ (IOObjectConformsTo(service, kIODVDMediaClass)) )
+ {
+ retval = 1;
+ } /* if */
+ } /* if */
+ IOObjectRelease(service);
+ } while ((service = IOIteratorNext(iter)) && (!retval));
+
+ IOObjectRelease(iter);
+ IOObjectRelease(service);
+
+ return(retval);
+} /* darwinIsMountedDisc */
+
+
+void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
+{
+ const char *devPrefix = "/dev/";
+ const int prefixLen = strlen(devPrefix);
+ mach_port_t masterPort = 0;
+ struct statfs *mntbufp;
+ int i, mounts;
+
+ if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS)
+ BAIL_MACRO(ERR_OS_ERROR, ) /*return void*/;
+
+ mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */
+ for (i = 0; i < mounts; i++)
+ {
+ char *dev = mntbufp[i].f_mntfromname;
+ char *mnt = mntbufp[i].f_mntonname;
+ if (strncmp(dev, devPrefix, prefixLen) != 0) /* a virtual device? */
+ continue;
+
+ dev += prefixLen;
+ if (darwinIsMountedDisc(dev, masterPort))
+ cb(data, mnt);
+ } /* for */
+} /* __PHYSFS_platformDetectAvailableCDs */
+
+
+static char *convertCFString(CFStringRef cfstr)
+{
+ CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
+ kCFStringEncodingUTF8) + 1;
+ char *retval = (char *) allocator.Malloc(len);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ if (CFStringGetCString(cfstr, retval, len, kCFStringEncodingUTF8))
+ {
+ /* shrink overallocated buffer if possible... */
+ CFIndex newlen = strlen(retval) + 1;
+ if (newlen < len)
+ {
+ void *ptr = allocator.Realloc(retval, newlen);
+ if (ptr != NULL)
+ retval = (char *) ptr;
+ } /* if */
+ } /* if */
+
+ else /* probably shouldn't fail, but just in case... */
+ {
+ allocator.Free(retval);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* else */
+
+ return(retval);
+} /* convertCFString */
+
+
+char *__PHYSFS_platformCalcBaseDir(const char *argv0)
+{
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ FSRef fsref;
+ CFRange cfrange;
+ CFURLRef cfurl = NULL;
+ CFStringRef cfstr = NULL;
+ CFMutableStringRef cfmutstr = NULL;
+ char *retval = NULL;
+
+ BAIL_IF_MACRO(GetProcessBundleLocation(&psn, &fsref) != noErr, NULL, NULL);
+ cfurl = CFURLCreateFromFSRef(cfallocator, &fsref);
+ BAIL_IF_MACRO(cfurl == NULL, NULL, NULL);
+ cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle);
+ CFRelease(cfurl);
+ BAIL_IF_MACRO(cfstr == NULL, NULL, NULL);
+ cfmutstr = CFStringCreateMutableCopy(cfallocator, 0, cfstr);
+ CFRelease(cfstr);
+ BAIL_IF_MACRO(cfmutstr == NULL, NULL, NULL);
+
+ /* Find last dirsep so we can chop the binary's filename from the path. */
+ cfrange = CFStringFind(cfmutstr, CFSTR("/"), kCFCompareBackwards);
+ if (cfrange.location == kCFNotFound)
+ {
+ assert(0); /* shouldn't ever hit this... */
+ CFRelease(cfmutstr);
+ return(NULL);
+ } /* if */
+
+ /* chop the "/exename" from the end of the path string... */
+ cfrange.length = CFStringGetLength(cfmutstr) - cfrange.location;
+ CFStringDelete(cfmutstr, cfrange);
+
+ /* If we're an Application Bundle, chop everything but the base. */
+ cfrange = CFStringFind(cfmutstr, CFSTR("/Contents/MacOS"),
+ kCFCompareCaseInsensitive |
+ kCFCompareBackwards |
+ kCFCompareAnchored);
+
+ if (cfrange.location != kCFNotFound)
+ CFStringDelete(cfmutstr, cfrange); /* chop that, too. */
+
+ retval = convertCFString(cfmutstr);
+ CFRelease(cfmutstr);
+
+ return(retval); /* whew. */
+} /* __PHYSFS_platformCalcBaseDir */
+
+
+/* !!! FIXME */
+#define osxerr(x) x
+
+char *__PHYSFS_platformRealPath(const char *path)
+{
+ /* The symlink and relative path resolving happens in FSPathMakeRef() */
+ FSRef fsref;
+ CFURLRef cfurl = NULL;
+ CFStringRef cfstr = NULL;
+ char *retval = NULL;
+ OSStatus rc = osxerr(FSPathMakeRef((UInt8 *) path, &fsref, NULL));
+ BAIL_IF_MACRO(rc != noErr, NULL, NULL);
+
+ /* Now get it to spit out a full path. */
+ cfurl = CFURLCreateFromFSRef(cfallocator, &fsref);
+ BAIL_IF_MACRO(cfurl == NULL, ERR_OUT_OF_MEMORY, NULL);
+ cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle);
+ CFRelease(cfurl);
+ BAIL_IF_MACRO(cfstr == NULL, ERR_OUT_OF_MEMORY, NULL);
+ retval = convertCFString(cfstr);
+ CFRelease(cfstr);
+
+ return(retval);
+} /* __PHYSFS_platformRealPath */
+
+
+char *__PHYSFS_platformCurrentDir(void)
+{
+ return(__PHYSFS_platformRealPath(".")); /* let CFURL sort it out. */
+} /* __PHYSFS_platformCurrentDir */
+
+
+/* Platform allocator uses default CFAllocator at PHYSFS_init() time. */
+
+static CFAllocatorRef cfallocdef = NULL;
+
+static int macosxAllocatorInit(void)
+{
+ int retval = 0;
+ cfallocdef = CFAllocatorGetDefault();
+ retval = (cfallocdef != NULL);
+ if (retval)
+ CFRetain(cfallocdef);
+ return(retval);
+} /* macosxAllocatorInit */
+
+
+static void macosxAllocatorDeinit(void)
+{
+ if (cfallocdef != NULL)
+ {
+ CFRelease(cfallocdef);
+ cfallocdef = NULL;
+ } /* if */
+} /* macosxAllocatorDeinit */
+
+
+static void *macosxAllocatorMalloc(PHYSFS_uint64 s)
+{
+ BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
+ return(CFAllocatorAllocate(cfallocdef, (CFIndex) s, 0));
+} /* macosxAllocatorMalloc */
+
+
+static void *macosxAllocatorRealloc(void *ptr, PHYSFS_uint64 s)
+{
+ BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
+ return(CFAllocatorReallocate(cfallocdef, ptr, (CFIndex) s, 0));
+} /* macosxAllocatorRealloc */
+
+
+static void macosxAllocatorFree(void *ptr)
+{
+ CFAllocatorDeallocate(cfallocdef, ptr);
+} /* macosxAllocatorFree */
+
+
+int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
+{
+ allocator.Init = macosxAllocatorInit;
+ allocator.Deinit = macosxAllocatorDeinit;
+ allocator.Malloc = macosxAllocatorMalloc;
+ allocator.Realloc = macosxAllocatorRealloc;
+ allocator.Free = macosxAllocatorFree;
+ return(1); /* return non-zero: we're supplying custom allocator. */
+} /* __PHYSFS_platformSetDefaultAllocator */
+
+
+PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
+{
+ return( (PHYSFS_uint64) ((size_t) MPCurrentTaskID()) );
+} /* __PHYSFS_platformGetThreadID */
+
+
+void *__PHYSFS_platformCreateMutex(void)
+{
+ MPCriticalRegionID m = NULL;
+ if (osxerr(MPCreateCriticalRegion(&m)) != noErr)
+ return NULL;
+ return m;
+} /* __PHYSFS_platformCreateMutex */
+
+
+void __PHYSFS_platformDestroyMutex(void *mutex)
+{
+ MPCriticalRegionID m = (MPCriticalRegionID) mutex;
+ MPDeleteCriticalRegion(m);
+} /* __PHYSFS_platformDestroyMutex */
+
+
+int __PHYSFS_platformGrabMutex(void *mutex)
+{
+ MPCriticalRegionID m = (MPCriticalRegionID) mutex;
+ if (MPEnterCriticalRegion(m, kDurationForever) != noErr)
+ return(0);
+ return(1);
+} /* __PHYSFS_platformGrabMutex */
+
+
+void __PHYSFS_platformReleaseMutex(void *mutex)
+{
+ MPCriticalRegionID m = (MPCriticalRegionID) mutex;
+ MPExitCriticalRegion(m);
+} /* __PHYSFS_platformReleaseMutex */
+
+#endif /* PHYSFS_PLATFORM_MACOSX */
+
+/* end of macosx.c ... */
+
--- /dev/null
+/*
+ * OS/2 support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_platforms.h"
+
+#ifdef PHYSFS_PLATFORM_OS2
+
+#define INCL_DOSSEMAPHORES
+#define INCL_DOSDATETIME
+#define INCL_DOSFILEMGR
+#define INCL_DOSMODULEMGR
+#define INCL_DOSERRORS
+#define INCL_DOSPROCESS
+#define INCL_DOSDEVICES
+#define INCL_DOSDEVIOCTL
+#define INCL_DOSMISC
+#include <os2.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "physfs_internal.h"
+
+const char *__PHYSFS_platformDirSeparator = "\\";
+
+
+static const char *get_os2_error_string(APIRET rc)
+{
+ switch (rc)
+ {
+ case NO_ERROR: return(NULL); /* not an error. */
+ case ERROR_INTERRUPT: return(NULL); /* not an error. */
+ case ERROR_TIMEOUT: return(NULL); /* not an error. */
+ case ERROR_NOT_ENOUGH_MEMORY: return(ERR_OUT_OF_MEMORY);
+ case ERROR_FILE_NOT_FOUND: return(ERR_NO_SUCH_FILE);
+ case ERROR_PATH_NOT_FOUND: return(ERR_NO_SUCH_PATH);
+ case ERROR_ACCESS_DENIED: return(ERR_ACCESS_DENIED);
+ case ERROR_NOT_DOS_DISK: return(ERR_NOT_A_DOS_DISK);
+ case ERROR_SHARING_VIOLATION: return(ERR_SHARING_VIOLATION);
+ case ERROR_CANNOT_MAKE: return(ERR_CANNOT_MAKE);
+ case ERROR_DEVICE_IN_USE: return(ERR_DEV_IN_USE);
+ case ERROR_OPEN_FAILED: return(ERR_OPEN_FAILED);
+ case ERROR_DISK_FULL: return(ERR_DISK_FULL);
+ case ERROR_PIPE_BUSY: return(ERR_PIPE_BUSY);
+ case ERROR_SHARING_BUFFER_EXCEEDED: return(ERR_SHARING_BUF_EXCEEDED);
+ case ERROR_FILENAME_EXCED_RANGE: return(ERR_BAD_FILENAME);
+ case ERROR_META_EXPANSION_TOO_LONG: return(ERR_BAD_FILENAME);
+ case ERROR_TOO_MANY_HANDLES: return(ERR_TOO_MANY_HANDLES);
+ case ERROR_TOO_MANY_OPEN_FILES: return(ERR_TOO_MANY_HANDLES);
+ case ERROR_NO_MORE_SEARCH_HANDLES: return(ERR_TOO_MANY_HANDLES);
+ case ERROR_SEEK_ON_DEVICE: return(ERR_SEEK_ERROR);
+ case ERROR_NEGATIVE_SEEK: return(ERR_SEEK_OUT_OF_RANGE);
+ /*!!! FIXME: Where did this go? case ERROR_DEL_CURRENT_DIRECTORY: return(ERR_DEL_CWD);*/
+ case ERROR_WRITE_PROTECT: return(ERR_WRITE_PROTECT_ERROR);
+ case ERROR_WRITE_FAULT: return(ERR_WRITE_FAULT);
+ case ERROR_LOCK_VIOLATION: return(ERR_LOCK_VIOLATION);
+ case ERROR_GEN_FAILURE: return(ERR_GEN_FAILURE);
+ case ERROR_UNCERTAIN_MEDIA: return(ERR_UNCERTAIN_MEDIA);
+ case ERROR_PROTECTION_VIOLATION: return(ERR_PROT_VIOLATION);
+ case ERROR_BROKEN_PIPE: return(ERR_BROKEN_PIPE);
+
+ case ERROR_INVALID_PARAMETER:
+ case ERROR_INVALID_NAME:
+ case ERROR_INVALID_DRIVE:
+ case ERROR_INVALID_HANDLE:
+ case ERROR_INVALID_FUNCTION:
+ case ERROR_INVALID_LEVEL:
+ case ERROR_INVALID_CATEGORY:
+ case ERROR_DUPLICATE_NAME:
+ case ERROR_BUFFER_OVERFLOW:
+ case ERROR_BAD_LENGTH:
+ case ERROR_BAD_DRIVER_LEVEL:
+ case ERROR_DIRECT_ACCESS_HANDLE:
+ case ERROR_NOT_OWNER:
+ return(ERR_PHYSFS_BAD_OS_CALL);
+
+ default: return(ERR_OS2_GENERIC);
+ } /* switch */
+
+ return(NULL);
+} /* get_os2_error_string */
+
+
+static APIRET os2err(APIRET retval)
+{
+ char buf[128];
+ const char *err = get_os2_error_string(retval);
+ if (err == ERR_OS2_GENERIC)
+ {
+ snprintf(buf, sizeof (buf), ERR_OS2_GENERIC, (int) retval);
+ err = buf;
+ } /* if */
+
+ if (err != NULL)
+ __PHYSFS_setError(err);
+
+ return(retval);
+} /* os2err */
+
+
+/* (be gentle, this function isn't very robust.) */
+static void cvt_path_to_correct_case(char *buf)
+{
+ char *fname = buf + 3; /* point to first element. */
+ char *ptr = strchr(fname, '\\'); /* find end of first element. */
+
+ buf[0] = toupper(buf[0]); /* capitalize drive letter. */
+
+ /*
+ * Go through each path element, and enumerate its parent dir until
+ * a case-insensitive match is found. If one is (and it SHOULD be)
+ * then overwrite the original element with the correct case.
+ * If there's an error, or the path has vanished for some reason, it
+ * won't hurt to have the original case, so we just keep going.
+ */
+ while (fname != NULL)
+ {
+ char spec[CCHMAXPATH];
+ FILEFINDBUF3 fb;
+ HDIR hdir = HDIR_CREATE;
+ ULONG count = 1;
+ APIRET rc;
+
+ *(fname - 1) = '\0'; /* isolate parent dir string. */
+
+ strcpy(spec, buf); /* copy isolated parent dir... */
+ strcat(spec, "\\*.*"); /* ...and add wildcard search spec. */
+
+ if (ptr != NULL) /* isolate element to find (fname is the start). */
+ *ptr = '\0';
+
+ rc = DosFindFirst(spec, &hdir, FILE_DIRECTORY,
+ &fb, sizeof (fb), &count, FIL_STANDARD);
+ if (rc == NO_ERROR)
+ {
+ while (count == 1) /* while still entries to enumerate... */
+ {
+ if (__PHYSFS_stricmpASCII(fb.achName, fname) == 0)
+ {
+ strcpy(fname, fb.achName);
+ break; /* there it is. Overwrite and stop searching. */
+ } /* if */
+
+ DosFindNext(hdir, &fb, sizeof (fb), &count);
+ } /* while */
+ DosFindClose(hdir);
+ } /* if */
+
+ *(fname - 1) = '\\'; /* unisolate parent dir. */
+ fname = ptr; /* point to next element. */
+ if (ptr != NULL)
+ {
+ *ptr = '\\'; /* unisolate element. */
+ ptr = strchr(++fname, '\\'); /* find next element. */
+ } /* if */
+ } /* while */
+} /* cvt_file_to_correct_case */
+
+
+static char *baseDir = NULL;
+
+int __PHYSFS_platformInit(void)
+{
+ char buf[CCHMAXPATH];
+ APIRET rc;
+ PTIB ptib;
+ PPIB ppib;
+ PHYSFS_sint32 len;
+
+ assert(baseDir == NULL);
+ BAIL_IF_MACRO(os2err(DosGetInfoBlocks(&ptib, &ppib)) != NO_ERROR, NULL, 0);
+ rc = DosQueryModuleName(ppib->pib_hmte, sizeof (buf), (PCHAR) buf);
+ BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0);
+
+ /* chop off filename, leave path. */
+ for (len = strlen(buf) - 1; len >= 0; len--)
+ {
+ if (buf[len] == '\\')
+ {
+ buf[len] = '\0';
+ break;
+ } /* if */
+ } /* for */
+
+ assert(len > 0); /* should have been a "x:\\" on the front on string. */
+
+ /* The string is capitalized! Figure out the REAL case... */
+ cvt_path_to_correct_case(buf);
+
+ baseDir = (char *) allocator.Malloc(len + 1);
+ BAIL_IF_MACRO(baseDir == NULL, ERR_OUT_OF_MEMORY, 0);
+ strcpy(baseDir, buf);
+ return(1); /* success. */
+} /* __PHYSFS_platformInit */
+
+
+int __PHYSFS_platformDeinit(void)
+{
+ assert(baseDir != NULL);
+ allocator.Free(baseDir);
+ baseDir = NULL;
+ return(1); /* success. */
+} /* __PHYSFS_platformDeinit */
+
+
+static int disc_is_inserted(ULONG drive)
+{
+ int rc;
+ char buf[20];
+ DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
+ rc = DosQueryFSInfo(drive + 1, FSIL_VOLSER, buf, sizeof (buf));
+ DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION);
+ return(rc == NO_ERROR);
+} /* is_cdrom_inserted */
+
+
+/* looks like "CD01" in ASCII (littleendian)...used for an ioctl. */
+#define CD01 0x31304443
+
+static int is_cdrom_drive(ULONG drive)
+{
+ PHYSFS_uint32 param, data;
+ ULONG ul1, ul2;
+ APIRET rc;
+ HFILE hfile = NULLHANDLE;
+ char drivename[3] = { 'A' + drive, ':', '\0' };
+
+ rc = DosOpen(drivename, &hfile, &ul1, 0, 0,
+ OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
+ OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR |
+ OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE, NULL);
+ BAIL_IF_MACRO(rc != NO_ERROR, NULL, 0);
+
+ data = 0;
+ param = PHYSFS_swapULE32(CD01);
+ ul1 = ul2 = sizeof (PHYSFS_uint32);
+ rc = DosDevIOCtl(hfile, IOCTL_CDROMDISK, CDROMDISK_GETDRIVER,
+ ¶m, sizeof (param), &ul1, &data, sizeof (data), &ul2);
+
+ DosClose(hfile);
+ return((rc == NO_ERROR) && (PHYSFS_swapULE32(data) == CD01));
+} /* is_cdrom_drive */
+
+
+void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
+{
+ ULONG dummy = 0;
+ ULONG drivemap = 0;
+ ULONG i, bit;
+ APIRET rc = DosQueryCurrentDisk(&dummy, &drivemap);
+ if (os2err(rc) != NO_ERROR)
+ return;
+
+ for (i = 0, bit = 1; i < 26; i++, bit <<= 1)
+ {
+ if (drivemap & bit) /* this logical drive exists. */
+ {
+ if ((is_cdrom_drive(i)) && (disc_is_inserted(i)))
+ {
+ char drive[4] = "x:\\";
+ drive[0] = ('A' + i);
+ cb(data, drive);
+ } /* if */
+ } /* if */
+ } /* for */
+} /* __PHYSFS_platformDetectAvailableCDs */
+
+
+char *__PHYSFS_platformCalcBaseDir(const char *argv0)
+{
+ char *retval = (char *) allocator.Malloc(strlen(baseDir) + 1);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+ strcpy(retval, baseDir); /* calculated at init time. */
+ return(retval);
+} /* __PHYSFS_platformCalcBaseDir */
+
+
+char *__PHYSFS_platformGetUserName(void)
+{
+ return(NULL); /* (*shrug*) */
+} /* __PHYSFS_platformGetUserName */
+
+
+char *__PHYSFS_platformGetUserDir(void)
+{
+ return(__PHYSFS_platformCalcBaseDir(NULL));
+} /* __PHYSFS_platformGetUserDir */
+
+
+int __PHYSFS_platformExists(const char *fname)
+{
+ FILESTATUS3 fs;
+ APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs));
+ return(os2err(rc) == NO_ERROR);
+} /* __PHYSFS_platformExists */
+
+
+int __PHYSFS_platformIsSymLink(const char *fname)
+{
+ return(0); /* no symlinks in OS/2. */
+} /* __PHYSFS_platformIsSymlink */
+
+
+int __PHYSFS_platformIsDirectory(const char *fname)
+{
+ FILESTATUS3 fs;
+ APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs));
+ BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0)
+ return((fs.attrFile & FILE_DIRECTORY) != 0);
+} /* __PHYSFS_platformIsDirectory */
+
+
+/* !!! FIXME: can we lose the malloc here? */
+char *__PHYSFS_platformCvtToDependent(const char *prepend,
+ const char *dirName,
+ const char *append)
+{
+ int len = ((prepend) ? strlen(prepend) : 0) +
+ ((append) ? strlen(append) : 0) +
+ strlen(dirName) + 1;
+ char *retval = allocator.Malloc(len);
+ char *p;
+
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ if (prepend)
+ strcpy(retval, prepend);
+ else
+ retval[0] = '\0';
+
+ strcat(retval, dirName);
+
+ if (append)
+ strcat(retval, append);
+
+ for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/'))
+ *p = '\\';
+
+ return(retval);
+} /* __PHYSFS_platformCvtToDependent */
+
+
+void __PHYSFS_platformEnumerateFiles(const char *dirname,
+ int omitSymLinks,
+ PHYSFS_EnumFilesCallback callback,
+ const char *origdir,
+ void *callbackdata)
+{
+ char spec[CCHMAXPATH];
+ FILEFINDBUF3 fb;
+ HDIR hdir = HDIR_CREATE;
+ ULONG count = 1;
+ APIRET rc;
+
+ if (strlen(dirname) > sizeof (spec) - 5)
+ {
+ __PHYSFS_setError(ERR_BAD_FILENAME);
+ return;
+ } /* if */
+
+ strcpy(spec, dirname);
+ strcat(spec, (spec[strlen(spec) - 1] != '\\') ? "\\*.*" : "*.*");
+
+ rc = DosFindFirst(spec, &hdir,
+ FILE_DIRECTORY | FILE_ARCHIVED |
+ FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM,
+ &fb, sizeof (fb), &count, FIL_STANDARD);
+
+ if (os2err(rc) != NO_ERROR)
+ return;
+
+ while (count == 1)
+ {
+ if ((strcmp(fb.achName, ".") != 0) && (strcmp(fb.achName, "..") != 0))
+ callback(callbackdata, origdir, fb.achName);
+
+ DosFindNext(hdir, &fb, sizeof (fb), &count);
+ } /* while */
+
+ DosFindClose(hdir);
+} /* __PHYSFS_platformEnumerateFiles */
+
+
+char *__PHYSFS_platformCurrentDir(void)
+{
+ char *retval;
+ ULONG currentDisk;
+ ULONG dummy;
+ ULONG pathSize = 0;
+ APIRET rc;
+ BYTE byte;
+
+ rc = DosQueryCurrentDisk(¤tDisk, &dummy);
+ BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, NULL);
+
+ /* The first call just tells us how much space we need for the string. */
+ rc = DosQueryCurrentDir(currentDisk, &byte, &pathSize);
+ pathSize++; /* Add space for null terminator. */
+ retval = (char *) allocator.Malloc(pathSize + 3); /* plus "x:\\" */
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ /* Actually get the string this time. */
+ rc = DosQueryCurrentDir(currentDisk, (PBYTE) (retval + 3), &pathSize);
+ if (os2err(rc) != NO_ERROR)
+ {
+ allocator.Free(retval);
+ return(NULL);
+ } /* if */
+
+ retval[0] = ('A' + (currentDisk - 1));
+ retval[1] = ':';
+ retval[2] = '\\';
+ return(retval);
+} /* __PHYSFS_platformCurrentDir */
+
+
+char *__PHYSFS_platformRealPath(const char *path)
+{
+ char buf[CCHMAXPATH];
+ char *retval;
+ APIRET rc = DosQueryPathInfo(path, FIL_QUERYFULLNAME, buf, sizeof (buf));
+ BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, NULL);
+ retval = (char *) allocator.Malloc(strlen(buf) + 1);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+ strcpy(retval, buf);
+ return(retval);
+} /* __PHYSFS_platformRealPath */
+
+
+int __PHYSFS_platformMkDir(const char *path)
+{
+ return(os2err(DosCreateDir(path, NULL)) == NO_ERROR);
+} /* __PHYSFS_platformMkDir */
+
+
+void *__PHYSFS_platformOpenRead(const char *filename)
+{
+ ULONG actionTaken = 0;
+ HFILE hfile = NULLHANDLE;
+
+ /*
+ * File must be opened SHARE_DENYWRITE and ACCESS_READONLY, otherwise
+ * DosQueryFileInfo() will fail if we try to get a file length, etc.
+ */
+ os2err(DosOpen(filename, &hfile, &actionTaken, 0, FILE_NORMAL,
+ OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
+ OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
+ OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
+ OPEN_ACCESS_READONLY, NULL));
+
+ return((void *) hfile);
+} /* __PHYSFS_platformOpenRead */
+
+
+void *__PHYSFS_platformOpenWrite(const char *filename)
+{
+ ULONG actionTaken = 0;
+ HFILE hfile = NULLHANDLE;
+
+ /*
+ * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise
+ * DosQueryFileInfo() will fail if we try to get a file length, etc.
+ */
+ os2err(DosOpen(filename, &hfile, &actionTaken, 0, FILE_NORMAL,
+ OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
+ OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
+ OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
+ OPEN_ACCESS_READWRITE, NULL));
+
+ return((void *) hfile);
+} /* __PHYSFS_platformOpenWrite */
+
+
+void *__PHYSFS_platformOpenAppend(const char *filename)
+{
+ ULONG dummy = 0;
+ HFILE hfile = NULLHANDLE;
+ APIRET rc;
+
+ /*
+ * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise
+ * DosQueryFileInfo() will fail if we try to get a file length, etc.
+ */
+ rc = os2err(DosOpen(filename, &hfile, &dummy, 0, FILE_NORMAL,
+ OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
+ OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
+ OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
+ OPEN_ACCESS_READWRITE, NULL));
+
+ if (rc == NO_ERROR)
+ {
+ if (os2err(DosSetFilePtr(hfile, 0, FILE_END, &dummy)) != NO_ERROR)
+ {
+ DosClose(hfile);
+ hfile = NULLHANDLE;
+ } /* if */
+ } /* if */
+
+ return((void *) hfile);
+} /* __PHYSFS_platformOpenAppend */
+
+
+PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
+ PHYSFS_uint32 size, PHYSFS_uint32 count)
+{
+ HFILE hfile = (HFILE) opaque;
+ PHYSFS_sint64 retval;
+ ULONG br;
+
+ for (retval = 0; retval < count; retval++)
+ {
+ os2err(DosRead(hfile, buffer, size, &br));
+ if (br < size)
+ {
+ DosSetFilePtr(hfile, -br, FILE_CURRENT, &br); /* try to cleanup. */
+ return(retval);
+ } /* if */
+
+ buffer = (void *) ( ((char *) buffer) + size );
+ } /* for */
+
+ return(retval);
+} /* __PHYSFS_platformRead */
+
+
+PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
+ PHYSFS_uint32 size, PHYSFS_uint32 count)
+{
+ HFILE hfile = (HFILE) opaque;
+ PHYSFS_sint64 retval;
+ ULONG bw;
+
+ for (retval = 0; retval < count; retval++)
+ {
+ os2err(DosWrite(hfile, buffer, size, &bw));
+ if (bw < size)
+ {
+ DosSetFilePtr(hfile, -bw, FILE_CURRENT, &bw); /* try to cleanup. */
+ return(retval);
+ } /* if */
+
+ buffer = (void *) ( ((char *) buffer) + size );
+ } /* for */
+
+ return(retval);
+} /* __PHYSFS_platformWrite */
+
+
+int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
+{
+ ULONG dummy;
+ HFILE hfile = (HFILE) opaque;
+ LONG dist = (LONG) pos;
+
+ /* hooray for 32-bit filesystem limits! :) */
+ BAIL_IF_MACRO((PHYSFS_uint64) dist != pos, ERR_SEEK_OUT_OF_RANGE, 0);
+
+ return(os2err(DosSetFilePtr(hfile, dist, FILE_BEGIN, &dummy)) == NO_ERROR);
+} /* __PHYSFS_platformSeek */
+
+
+PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
+{
+ ULONG pos;
+ HFILE hfile = (HFILE) opaque;
+ APIRET rc = os2err(DosSetFilePtr(hfile, 0, FILE_CURRENT, &pos));
+ BAIL_IF_MACRO(rc != NO_ERROR, NULL, -1);
+ return((PHYSFS_sint64) pos);
+} /* __PHYSFS_platformTell */
+
+
+PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
+{
+ FILESTATUS3 fs;
+ HFILE hfile = (HFILE) opaque;
+ APIRET rc = DosQueryFileInfo(hfile, FIL_STANDARD, &fs, sizeof (fs));
+ BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1);
+ return((PHYSFS_sint64) fs.cbFile);
+} /* __PHYSFS_platformFileLength */
+
+
+int __PHYSFS_platformEOF(void *opaque)
+{
+ PHYSFS_sint64 len, pos;
+
+ len = __PHYSFS_platformFileLength(opaque);
+ BAIL_IF_MACRO(len == -1, NULL, 1); /* (*shrug*) */
+ pos = __PHYSFS_platformTell(opaque);
+ BAIL_IF_MACRO(pos == -1, NULL, 1); /* (*shrug*) */
+
+ return(pos >= len);
+} /* __PHYSFS_platformEOF */
+
+
+int __PHYSFS_platformFlush(void *opaque)
+{
+ return(os2err(DosResetBuffer((HFILE) opaque) == NO_ERROR));
+} /* __PHYSFS_platformFlush */
+
+
+int __PHYSFS_platformClose(void *opaque)
+{
+ return(os2err(DosClose((HFILE) opaque) == NO_ERROR));
+} /* __PHYSFS_platformClose */
+
+
+int __PHYSFS_platformDelete(const char *path)
+{
+ if (__PHYSFS_platformIsDirectory(path))
+ return(os2err(DosDeleteDir(path)) == NO_ERROR);
+
+ return(os2err(DosDelete(path) == NO_ERROR));
+} /* __PHYSFS_platformDelete */
+
+
+PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
+{
+ PHYSFS_sint64 retval;
+ struct tm tm;
+ FILESTATUS3 fs;
+ APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs));
+ BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1);
+
+ /* Convert to a format that mktime() can grok... */
+ tm.tm_sec = ((PHYSFS_uint32) fs.ftimeLastWrite.twosecs) * 2;
+ tm.tm_min = fs.ftimeLastWrite.minutes;
+ tm.tm_hour = fs.ftimeLastWrite.hours;
+ tm.tm_mday = fs.fdateLastWrite.day;
+ tm.tm_mon = fs.fdateLastWrite.month;
+ tm.tm_year = ((PHYSFS_uint32) fs.fdateLastWrite.year) + 80;
+ tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
+ tm.tm_yday = -1;
+ tm.tm_isdst = -1;
+
+ /* Convert to a format PhysicsFS can grok... */
+ retval = (PHYSFS_sint64) mktime(&tm);
+ BAIL_IF_MACRO(retval == -1, strerror(errno), -1);
+ return(retval);
+} /* __PHYSFS_platformGetLastModTime */
+
+
+PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
+{
+ PTIB ptib;
+ PPIB ppib;
+
+ /*
+ * Allegedly, this API never fails, but we'll punt and return a
+ * default value (zero might as well do) if it does.
+ */
+ BAIL_IF_MACRO(os2err(DosGetInfoBlocks(&ptib, &ppib)) != NO_ERROR, 0, 0);
+ return((PHYSFS_uint64) ptib->tib_ordinal);
+} /* __PHYSFS_platformGetThreadID */
+
+
+void *__PHYSFS_platformCreateMutex(void)
+{
+ HMTX hmtx = NULLHANDLE;
+ os2err(DosCreateMutexSem(NULL, &hmtx, 0, 0));
+ return((void *) hmtx);
+} /* __PHYSFS_platformCreateMutex */
+
+
+void __PHYSFS_platformDestroyMutex(void *mutex)
+{
+ DosCloseMutexSem((HMTX) mutex);
+} /* __PHYSFS_platformDestroyMutex */
+
+
+int __PHYSFS_platformGrabMutex(void *mutex)
+{
+ /* Do _NOT_ call os2err() (which sets the physfs error msg) in here! */
+ return(DosRequestMutexSem((HMTX) mutex, SEM_INDEFINITE_WAIT) == NO_ERROR);
+} /* __PHYSFS_platformGrabMutex */
+
+
+void __PHYSFS_platformReleaseMutex(void *mutex)
+{
+ DosReleaseMutexSem((HMTX) mutex);
+} /* __PHYSFS_platformReleaseMutex */
+
+
+/* !!! FIXME: Don't use C runtime for allocators? */
+int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
+{
+ return(0); /* just use malloc() and friends. */
+} /* __PHYSFS_platformSetDefaultAllocator */
+
+#endif /* PHYSFS_PLATFORM_OS2 */
+
+/* end of os2.c ... */
+
--- /dev/null
+/*
+ * PocketPC support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_platforms.h"
+
+#ifdef PHYSFS_PLATFORM_POCKETPC
+
+#include <stdio.h>
+#include <windows.h>
+
+#include "physfs_internal.h"
+
+#define INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
+#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
+typedef struct
+{
+ HANDLE handle;
+ int readonly;
+} winCEfile;
+
+
+const char *__PHYSFS_platformDirSeparator = "\\";
+static char *userDir = NULL;
+
+/*
+ * Figure out what the last failing Win32 API call was, and
+ * generate a human-readable string for the error message.
+ *
+ * The return value is a static buffer that is overwritten with
+ * each call to this function.
+ */
+static const char *win32strerror(void)
+{
+ static TCHAR msgbuf[255];
+ TCHAR *ptr = msgbuf;
+
+ FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+ msgbuf,
+ sizeof (msgbuf) / sizeof (TCHAR),
+ NULL
+ );
+
+ /* chop off newlines. */
+ for (ptr = msgbuf; *ptr; ptr++)
+ {
+ if ((*ptr == '\n') || (*ptr == '\r'))
+ {
+ *ptr = ' ';
+ break;
+ } /* if */
+ } /* for */
+
+ return((const char *) msgbuf);
+} /* win32strerror */
+
+
+/* !!! FIXME: need to check all of these for NULLs. */
+#define UTF8_TO_UNICODE_STACK_MACRO(w_assignto, str) { \
+ if (str == NULL) \
+ w_assignto = NULL; \
+ else { \
+ const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) * 4) + 1); \
+ w_assignto = (char *) __PHYSFS_smallAlloc(len); \
+ PHYSFS_uc2fromutf8(str, (PHYSFS_uint16 *) w_assignto, len); \
+ } \
+} \
+
+
+static char *getExePath()
+{
+ DWORD buflen;
+ int success = 0;
+ TCHAR *ptr = NULL;
+ TCHAR *retval = (TCHAR*) allocator.Malloc(sizeof (TCHAR) * (MAX_PATH + 1));
+ char *charretval;
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ retval[0] = _T('\0');
+ /* !!! FIXME: don't preallocate here? */
+ /* !!! FIXME: use smallAlloc? */
+ buflen = GetModuleFileName(NULL, retval, MAX_PATH + 1);
+ if (buflen <= 0)
+ __PHYSFS_setError(win32strerror());
+ else
+ {
+ retval[buflen] = '\0'; /* does API always null-terminate this? */
+ ptr = retval+buflen;
+ while( ptr != retval )
+ {
+ if( *ptr != _T('\\') )
+ *ptr-- = _T('\0');
+ else
+ break;
+ } /* while */
+ success = 1;
+ } /* else */
+
+ if (!success)
+ {
+ allocator.Free(retval);
+ return(NULL); /* physfs error message will be set, above. */
+ } /* if */
+
+ buflen = (buflen * 4) + 1;
+ charretval = (char *) allocator.Malloc(buflen);
+ if (charretval != NULL)
+ PHYSFS_utf8fromucs2((const PHYSFS_uint16 *) retval, charretval, buflen);
+ allocator.Free(retval);
+ return(charretval); /* w00t. */
+} /* getExePath */
+
+
+int __PHYSFS_platformInit(void)
+{
+ userDir = getExePath();
+ BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* failed? */
+ return(1); /* always succeed. */
+} /* __PHYSFS_platformInit */
+
+
+int __PHYSFS_platformDeinit(void)
+{
+ allocator.Free(userDir);
+ return(1); /* always succeed. */
+} /* __PHYSFS_platformDeinit */
+
+
+void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
+{
+ /* no-op on this platform. */
+} /* __PHYSFS_platformDetectAvailableCDs */
+
+
+char *__PHYSFS_platformCalcBaseDir(const char *argv0)
+{
+ return(getExePath());
+} /* __PHYSFS_platformCalcBaseDir */
+
+
+char *__PHYSFS_platformGetUserName(void)
+{
+ BAIL_MACRO(ERR_NOT_IMPLEMENTED, NULL);
+} /* __PHYSFS_platformGetUserName */
+
+
+char *__PHYSFS_platformGetUserDir(void)
+{
+ return userDir;
+ BAIL_MACRO(ERR_NOT_IMPLEMENTED, NULL);
+} /* __PHYSFS_platformGetUserDir */
+
+
+PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
+{
+ return(1); /* single threaded. */
+} /* __PHYSFS_platformGetThreadID */
+
+
+int __PHYSFS_platformExists(const char *fname)
+{
+ int retval = 0;
+ wchar_t *w_fname = NULL;
+
+ UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname);
+ if (w_fname != NULL)
+ retval = (GetFileAttributes(w_fname) != INVALID_FILE_ATTRIBUTES);
+ __PHYSFS_smallFree(w_fname);
+
+ return(retval);
+} /* __PHYSFS_platformExists */
+
+
+int __PHYSFS_platformIsSymLink(const char *fname)
+{
+ BAIL_MACRO(ERR_NOT_IMPLEMENTED, 0);
+} /* __PHYSFS_platformIsSymlink */
+
+
+int __PHYSFS_platformIsDirectory(const char *fname)
+{
+ int retval = 0;
+ wchar_t *w_fname = NULL;
+
+ UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname);
+ if (w_fname != NULL)
+ retval = ((GetFileAttributes(w_fname) & FILE_ATTRIBUTE_DIRECTORY) != 0);
+ __PHYSFS_smallFree(w_fname);
+
+ return(retval);
+} /* __PHYSFS_platformIsDirectory */
+
+
+char *__PHYSFS_platformCvtToDependent(const char *prepend,
+ const char *dirName,
+ const char *append)
+{
+ int len = ((prepend) ? strlen(prepend) : 0) +
+ ((append) ? strlen(append) : 0) +
+ strlen(dirName) + 1;
+ char *retval = (char *) allocator.Malloc(len);
+ char *p;
+
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ if (prepend)
+ strcpy(retval, prepend);
+ else
+ retval[0] = '\0';
+
+ strcat(retval, dirName);
+
+ if (append)
+ strcat(retval, append);
+
+ for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/'))
+ *p = '\\';
+
+ return(retval);
+} /* __PHYSFS_platformCvtToDependent */
+
+
+static int doEnumCallback(const wchar_t *w_fname)
+{
+ const PHYSFS_uint64 len = (PHYSFS_uint64) ((wcslen(w_fname) * 4) + 1);
+ char *str = (char *) __PHYSFS_smallAlloc(len);
+ PHYSFS_utf8fromucs2((const PHYSFS_uint16 *) w_fname, str, len);
+ callback(callbackdata, origdir, str);
+ __PHYSFS_smallFree(str);
+ return 1;
+} /* doEnumCallback */
+
+
+void __PHYSFS_platformEnumerateFiles(const char *dirname,
+ int omitSymLinks,
+ PHYSFS_EnumFilesCallback callback,
+ const char *origdir,
+ void *callbackdata)
+{
+ HANDLE dir;
+ WIN32_FIND_DATA ent;
+ char *SearchPath;
+ wchar_t *w_SearchPath;
+ size_t len = strlen(dirname);
+
+ /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
+ SearchPath = (char *) __PHYSFS_smallAlloc(len + 3);
+ BAIL_IF_MACRO(SearchPath == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ /* Copy current dirname */
+ strcpy(SearchPath, dirname);
+
+ /* if there's no '\\' at the end of the path, stick one in there. */
+ if (SearchPath[len - 1] != '\\')
+ {
+ SearchPath[len++] = '\\';
+ SearchPath[len] = '\0';
+ } /* if */
+
+ /* Append the "*" to the end of the string */
+ strcat(SearchPath, "*");
+
+ UTF8_TO_UNICODE_STACK_MACRO(w_SearchPath, SearchPath);
+ __PHYSFS_smallFree(SearchPath);
+ dir = FindFirstFile(w_SearchPath, &ent);
+ __PHYSFS_smallFree(w_SearchPath);
+
+ if (dir == INVALID_HANDLE_VALUE)
+ return;
+
+ do
+ {
+ const char *str = NULL;
+
+ if (wcscmp(ent.cFileName, L".") == 0)
+ continue;
+
+ if (wcscmp(ent.cFileName, L"..") == 0)
+ continue;
+
+ if (!doEnumCallback(ent.cFileName))
+ break;
+ } while (FindNextFile(dir, &ent) != 0);
+
+ FindClose(dir);
+} /* __PHYSFS_platformEnumerateFiles */
+
+
+char *__PHYSFS_platformCurrentDir(void)
+{
+ return("\\");
+} /* __PHYSFS_platformCurrentDir */
+
+
+char *__PHYSFS_platformRealPath(const char *path)
+{
+ char *retval = (char *) allocator.Malloc(strlen(path) + 1);
+ strcpy(retval,path);
+ return(retval);
+} /* __PHYSFS_platformRealPath */
+
+
+int __PHYSFS_platformMkDir(const char *path)
+{
+ int retval = 0;
+ wchar_t *w_path = NULL;
+ UTF8_TO_UNICODE_STACK_MACRO(w_path, path);
+ if (w_path != NULL)
+ {
+ retval = CreateDirectory(w_path, NULL);
+ __PHYSFS_smallFree(w_fname);
+ } /* if */
+ return(retval);
+} /* __PHYSFS_platformMkDir */
+
+
+static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly)
+{
+ HANDLE fileHandle;
+ winCEfile *retval;
+ wchar_t *w_fname = NULL;
+
+ UTF8_TO_UNICODE_STACK_MACRO(w_fname, fname);
+ fileHandle = CreateFile(w_fname, mode, FILE_SHARE_READ, NULL,
+ creation, FILE_ATTRIBUTE_NORMAL, NULL);
+ __PHYSFS_smallFree(w_fname);
+
+ BAIL_IF_MACRO(fileHandle == INVALID_HANDLE_VALUE, win32strerror(), NULL);
+
+ retval = (winCEfile *) allocator.Malloc(sizeof (winCEfile));
+ if (retval == NULL)
+ {
+ CloseHandle(fileHandle);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* if */
+
+ retval->readonly = rdonly;
+ retval->handle = fileHandle;
+ return(retval);
+} /* doOpen */
+
+
+void *__PHYSFS_platformOpenRead(const char *filename)
+{
+ return(doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1));
+} /* __PHYSFS_platformOpenRead */
+
+
+void *__PHYSFS_platformOpenWrite(const char *filename)
+{
+ return(doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0));
+} /* __PHYSFS_platformOpenWrite */
+
+
+void *__PHYSFS_platformOpenAppend(const char *filename)
+{
+ void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0);
+ if (retval != NULL)
+ {
+ HANDLE h = ((winCEfile *) retval)->handle;
+ if (SetFilePointer(h, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
+ {
+ const char *err = win32strerror();
+ CloseHandle(h);
+ allocator.Free(retval);
+ BAIL_MACRO(err, NULL);
+ } /* if */
+ } /* if */
+
+ return(retval);
+
+} /* __PHYSFS_platformOpenAppend */
+
+
+PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
+ PHYSFS_uint32 size, PHYSFS_uint32 count)
+{
+ HANDLE Handle = ((winCEfile *) opaque)->handle;
+ DWORD CountOfBytesRead;
+ PHYSFS_sint64 retval;
+
+ /* Read data from the file */
+ /*!!! - uint32 might be a greater # than DWORD */
+ if (!ReadFile(Handle, buffer, count * size, &CountOfBytesRead, NULL))
+ {
+ retval = -1;
+ } /* if */
+ else
+ {
+ /* Return the number of "objects" read. */
+ /* !!! - What if not the right amount of bytes was read to make an object? */
+ retval = CountOfBytesRead / size;
+ } /* else */
+
+ return(retval);
+
+} /* __PHYSFS_platformRead */
+
+
+PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
+ PHYSFS_uint32 size, PHYSFS_uint32 count)
+{
+ HANDLE Handle = ((winCEfile *) opaque)->handle;
+ DWORD CountOfBytesWritten;
+ PHYSFS_sint64 retval;
+
+ /* Read data from the file */
+ /*!!! - uint32 might be a greater # than DWORD */
+ if (!WriteFile(Handle, buffer, count * size, &CountOfBytesWritten, NULL))
+ {
+ retval = -1;
+ } /* if */
+ else
+ {
+ /* Return the number of "objects" read. */
+ /*!!! - What if not the right number of bytes was written? */
+ retval = CountOfBytesWritten / size;
+ } /* else */
+
+ return(retval);
+
+} /* __PHYSFS_platformWrite */
+
+
+int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
+{
+ HANDLE Handle = ((winCEfile *) opaque)->handle;
+ DWORD HighOrderPos;
+ DWORD rc;
+
+ /* Get the high order 32-bits of the position */
+ //HighOrderPos = HIGHORDER_UINT64(pos);
+ HighOrderPos = (unsigned long)(pos>>32);
+
+ /*!!! SetFilePointer needs a signed 64-bit value. */
+ /* Move pointer "pos" count from start of file */
+ rc = SetFilePointer(Handle, (unsigned long)(pos&0x00000000ffffffff),
+ &HighOrderPos, FILE_BEGIN);
+
+ if ((rc == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
+ {
+ BAIL_MACRO(win32strerror(), 0);
+ }
+
+ return(1); /* No error occured */
+} /* __PHYSFS_platformSeek */
+
+
+PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
+{
+ HANDLE Handle = ((winCEfile *) opaque)->handle;
+ DWORD HighPos = 0;
+ DWORD LowPos;
+ PHYSFS_sint64 retval;
+
+ /* Get current position */
+ LowPos = SetFilePointer(Handle, 0, &HighPos, FILE_CURRENT);
+ if ((LowPos == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
+ {
+ BAIL_MACRO(win32strerror(), 0);
+ } /* if */
+ else
+ {
+ /* Combine the high/low order to create the 64-bit position value */
+ retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos;
+ //assert(retval >= 0);
+ } /* else */
+
+ return(retval);
+} /* __PHYSFS_platformTell */
+
+
+PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
+{
+ HANDLE Handle = ((winCEfile *) opaque)->handle;
+ DWORD SizeHigh;
+ DWORD SizeLow;
+ PHYSFS_sint64 retval;
+
+ SizeLow = GetFileSize(Handle, &SizeHigh);
+ if ((SizeLow == INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR))
+ {
+ BAIL_MACRO(win32strerror(), -1);
+ } /* if */
+ else
+ {
+ /* Combine the high/low order to create the 64-bit position value */
+ retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow;
+ //assert(retval >= 0);
+ } /* else */
+
+ return(retval);
+} /* __PHYSFS_platformFileLength */
+
+
+int __PHYSFS_platformEOF(void *opaque)
+{
+ PHYSFS_sint64 FilePosition;
+ int retval = 0;
+
+ /* Get the current position in the file */
+ if ((FilePosition = __PHYSFS_platformTell(opaque)) != 0)
+ {
+ /* Non-zero if EOF is equal to the file length */
+ retval = FilePosition == __PHYSFS_platformFileLength(opaque);
+ } /* if */
+
+ return(retval);
+} /* __PHYSFS_platformEOF */
+
+
+int __PHYSFS_platformFlush(void *opaque)
+{
+ winCEfile *fh = ((winCEfile *) opaque);
+ if (!fh->readonly)
+ BAIL_IF_MACRO(!FlushFileBuffers(fh->handle), win32strerror(), 0);
+
+ return(1);
+} /* __PHYSFS_platformFlush */
+
+
+int __PHYSFS_platformClose(void *opaque)
+{
+ HANDLE Handle = ((winCEfile *) opaque)->handle;
+ BAIL_IF_MACRO(!CloseHandle(Handle), win32strerror(), 0);
+ allocator.Free(opaque);
+ return(1);
+} /* __PHYSFS_platformClose */
+
+
+int __PHYSFS_platformDelete(const char *path)
+{
+ wchar_t *w_path = NULL;
+ UTF8_TO_UNICODE_STACK_MACRO(w_path, path);
+
+ /* If filename is a folder */
+ if (GetFileAttributes(w_path) == FILE_ATTRIBUTE_DIRECTORY)
+ {
+ int retval = !RemoveDirectory(w_path);
+ __PHYSFS_smallFree(w_path);
+ BAIL_IF_MACRO(retval, win32strerror(), 0);
+ } /* if */
+ else
+ {
+ int retval = !DeleteFile(w_path);
+ __PHYSFS_smallFree(w_path);
+ BAIL_IF_MACRO(retval, win32strerror(), 0);
+ } /* else */
+
+ return(1); /* if you got here, it worked. */
+} /* __PHYSFS_platformDelete */
+
+
+/*
+ * !!! FIXME: why aren't we using Critical Sections instead of Mutexes?
+ * !!! FIXME: mutexes on Windows are for cross-process sync. CritSects are
+ * !!! FIXME: mutexes for threads in a single process and are faster.
+ */
+void *__PHYSFS_platformCreateMutex(void)
+{
+ return((void *) CreateMutex(NULL, FALSE, NULL));
+} /* __PHYSFS_platformCreateMutex */
+
+
+void __PHYSFS_platformDestroyMutex(void *mutex)
+{
+ CloseHandle((HANDLE) mutex);
+} /* __PHYSFS_platformDestroyMutex */
+
+
+int __PHYSFS_platformGrabMutex(void *mutex)
+{
+ return(WaitForSingleObject((HANDLE) mutex, INFINITE) != WAIT_FAILED);
+} /* __PHYSFS_platformGrabMutex */
+
+
+void __PHYSFS_platformReleaseMutex(void *mutex)
+{
+ ReleaseMutex((HANDLE) mutex);
+} /* __PHYSFS_platformReleaseMutex */
+
+
+PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
+{
+ BAIL_MACRO(ERR_NOT_IMPLEMENTED, -1);
+} /* __PHYSFS_platformGetLastModTime */
+
+
+/* !!! FIXME: Don't use C runtime for allocators? */
+int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
+{
+ return(0); /* just use malloc() and friends. */
+} /* __PHYSFS_platformSetDefaultAllocator */
+
+#endif /* PHYSFS_PLATFORM_POCKETPC */
+
+/* end of pocketpc.c ... */
+
--- /dev/null
+/*
+ * Posix-esque support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_platforms.h"
+
+#ifdef PHYSFS_PLATFORM_POSIX
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef PHYSFS_HAVE_LLSEEK
+#include <linux/unistd.h>
+#endif
+
+#include "physfs_internal.h"
+
+
+const char *__PHYSFS_platformDirSeparator = "/";
+
+
+char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname)
+{
+ const char *envr = getenv(varname);
+ char *retval = NULL;
+
+ if (envr != NULL)
+ {
+ retval = (char *) allocator.Malloc(strlen(envr) + 1);
+ if (retval != NULL)
+ strcpy(retval, envr);
+ } /* if */
+
+ return(retval);
+} /* __PHYSFS_platformCopyEnvironmentVariable */
+
+
+static char *getUserNameByUID(void)
+{
+ uid_t uid = getuid();
+ struct passwd *pw;
+ char *retval = NULL;
+
+ pw = getpwuid(uid);
+ if ((pw != NULL) && (pw->pw_name != NULL))
+ {
+ retval = (char *) allocator.Malloc(strlen(pw->pw_name) + 1);
+ if (retval != NULL)
+ strcpy(retval, pw->pw_name);
+ } /* if */
+
+ return(retval);
+} /* getUserNameByUID */
+
+
+static char *getUserDirByUID(void)
+{
+ uid_t uid = getuid();
+ struct passwd *pw;
+ char *retval = NULL;
+
+ pw = getpwuid(uid);
+ if ((pw != NULL) && (pw->pw_dir != NULL))
+ {
+ retval = (char *) allocator.Malloc(strlen(pw->pw_dir) + 1);
+ if (retval != NULL)
+ strcpy(retval, pw->pw_dir);
+ } /* if */
+
+ return(retval);
+} /* getUserDirByUID */
+
+
+char *__PHYSFS_platformGetUserName(void)
+{
+ char *retval = getUserNameByUID();
+ if (retval == NULL)
+ retval = __PHYSFS_platformCopyEnvironmentVariable("USER");
+ return(retval);
+} /* __PHYSFS_platformGetUserName */
+
+
+char *__PHYSFS_platformGetUserDir(void)
+{
+ char *retval = __PHYSFS_platformCopyEnvironmentVariable("HOME");
+ if (retval == NULL)
+ retval = getUserDirByUID();
+ return(retval);
+} /* __PHYSFS_platformGetUserDir */
+
+
+int __PHYSFS_platformExists(const char *fname)
+{
+ struct stat statbuf;
+ BAIL_IF_MACRO(lstat(fname, &statbuf) == -1, strerror(errno), 0);
+ return(1);
+} /* __PHYSFS_platformExists */
+
+
+int __PHYSFS_platformIsSymLink(const char *fname)
+{
+ struct stat statbuf;
+ BAIL_IF_MACRO(lstat(fname, &statbuf) == -1, strerror(errno), 0);
+ return( (S_ISLNK(statbuf.st_mode)) ? 1 : 0 );
+} /* __PHYSFS_platformIsSymlink */
+
+
+int __PHYSFS_platformIsDirectory(const char *fname)
+{
+ struct stat statbuf;
+ BAIL_IF_MACRO(stat(fname, &statbuf) == -1, strerror(errno), 0);
+ return( (S_ISDIR(statbuf.st_mode)) ? 1 : 0 );
+} /* __PHYSFS_platformIsDirectory */
+
+
+char *__PHYSFS_platformCvtToDependent(const char *prepend,
+ const char *dirName,
+ const char *append)
+{
+ int len = ((prepend) ? strlen(prepend) : 0) +
+ ((append) ? strlen(append) : 0) +
+ strlen(dirName) + 1;
+ char *retval = (char *) allocator.Malloc(len);
+
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ /* platform-independent notation is Unix-style already. :) */
+
+ if (prepend)
+ strcpy(retval, prepend);
+ else
+ retval[0] = '\0';
+
+ strcat(retval, dirName);
+
+ if (append)
+ strcat(retval, append);
+
+ return(retval);
+} /* __PHYSFS_platformCvtToDependent */
+
+
+
+void __PHYSFS_platformEnumerateFiles(const char *dirname,
+ int omitSymLinks,
+ PHYSFS_EnumFilesCallback callback,
+ const char *origdir,
+ void *callbackdata)
+{
+ DIR *dir;
+ struct dirent *ent;
+ int bufsize = 0;
+ char *buf = NULL;
+ int dlen = 0;
+
+ if (omitSymLinks) /* !!! FIXME: this malloc sucks. */
+ {
+ dlen = strlen(dirname);
+ bufsize = dlen + 256;
+ buf = (char *) allocator.Malloc(bufsize);
+ if (buf == NULL)
+ return;
+ strcpy(buf, dirname);
+ if (buf[dlen - 1] != '/')
+ {
+ buf[dlen++] = '/';
+ buf[dlen] = '\0';
+ } /* if */
+ } /* if */
+
+ errno = 0;
+ dir = opendir(dirname);
+ if (dir == NULL)
+ {
+ allocator.Free(buf);
+ return;
+ } /* if */
+
+ while ((ent = readdir(dir)) != NULL)
+ {
+ if (strcmp(ent->d_name, ".") == 0)
+ continue;
+
+ if (strcmp(ent->d_name, "..") == 0)
+ continue;
+
+ if (omitSymLinks)
+ {
+ char *p;
+ int len = strlen(ent->d_name) + dlen + 1;
+ if (len > bufsize)
+ {
+ p = (char *) allocator.Realloc(buf, len);
+ if (p == NULL)
+ continue;
+ buf = p;
+ bufsize = len;
+ } /* if */
+
+ strcpy(buf + dlen, ent->d_name);
+ if (__PHYSFS_platformIsSymLink(buf))
+ continue;
+ } /* if */
+
+ callback(callbackdata, origdir, ent->d_name);
+ } /* while */
+
+ allocator.Free(buf);
+ closedir(dir);
+} /* __PHYSFS_platformEnumerateFiles */
+
+
+int __PHYSFS_platformMkDir(const char *path)
+{
+ int rc;
+ errno = 0;
+ rc = mkdir(path, S_IRWXU);
+ BAIL_IF_MACRO(rc == -1, strerror(errno), 0);
+ return(1);
+} /* __PHYSFS_platformMkDir */
+
+
+static void *doOpen(const char *filename, int mode)
+{
+ int fd;
+ int *retval;
+ errno = 0;
+
+ fd = open(filename, mode, S_IRUSR | S_IWUSR);
+ BAIL_IF_MACRO(fd < 0, strerror(errno), NULL);
+
+ retval = (int *) allocator.Malloc(sizeof (int));
+ if (retval == NULL)
+ {
+ close(fd);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* if */
+
+ *retval = fd;
+ return((void *) retval);
+} /* doOpen */
+
+
+void *__PHYSFS_platformOpenRead(const char *filename)
+{
+ return(doOpen(filename, O_RDONLY));
+} /* __PHYSFS_platformOpenRead */
+
+
+void *__PHYSFS_platformOpenWrite(const char *filename)
+{
+ return(doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC));
+} /* __PHYSFS_platformOpenWrite */
+
+
+void *__PHYSFS_platformOpenAppend(const char *filename)
+{
+ return(doOpen(filename, O_WRONLY | O_CREAT | O_APPEND));
+} /* __PHYSFS_platformOpenAppend */
+
+
+PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
+ PHYSFS_uint32 size, PHYSFS_uint32 count)
+{
+ int fd = *((int *) opaque);
+ int max = size * count;
+ int rc = read(fd, buffer, max);
+
+ BAIL_IF_MACRO(rc == -1, strerror(errno), rc);
+ assert(rc <= max);
+
+ if ((rc < max) && (size > 1))
+ lseek(fd, -(rc % size), SEEK_CUR); /* rollback to object boundary. */
+
+ return(rc / size);
+} /* __PHYSFS_platformRead */
+
+
+PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
+ PHYSFS_uint32 size, PHYSFS_uint32 count)
+{
+ int fd = *((int *) opaque);
+ int max = size * count;
+ int rc = write(fd, (void *) buffer, max);
+
+ BAIL_IF_MACRO(rc == -1, strerror(errno), rc);
+ assert(rc <= max);
+
+ if ((rc < max) && (size > 1))
+ lseek(fd, -(rc % size), SEEK_CUR); /* rollback to object boundary. */
+
+ return(rc / size);
+} /* __PHYSFS_platformWrite */
+
+
+int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
+{
+ int fd = *((int *) opaque);
+
+ #ifdef PHYSFS_HAVE_LLSEEK
+ unsigned long offset_high = ((pos >> 32) & 0xFFFFFFFF);
+ unsigned long offset_low = (pos & 0xFFFFFFFF);
+ loff_t retoffset;
+ int rc = llseek(fd, offset_high, offset_low, &retoffset, SEEK_SET);
+ BAIL_IF_MACRO(rc == -1, strerror(errno), 0);
+ #else
+ BAIL_IF_MACRO(lseek(fd, (int) pos, SEEK_SET) == -1, strerror(errno), 0);
+ #endif
+
+ return(1);
+} /* __PHYSFS_platformSeek */
+
+
+PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
+{
+ int fd = *((int *) opaque);
+ PHYSFS_sint64 retval;
+
+ #ifdef PHYSFS_HAVE_LLSEEK
+ loff_t retoffset;
+ int rc = llseek(fd, 0, &retoffset, SEEK_CUR);
+ BAIL_IF_MACRO(rc == -1, strerror(errno), -1);
+ retval = (PHYSFS_sint64) retoffset;
+ #else
+ retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR);
+ BAIL_IF_MACRO(retval == -1, strerror(errno), -1);
+ #endif
+
+ return(retval);
+} /* __PHYSFS_platformTell */
+
+
+PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
+{
+ int fd = *((int *) opaque);
+ struct stat statbuf;
+ BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, strerror(errno), -1);
+ return((PHYSFS_sint64) statbuf.st_size);
+} /* __PHYSFS_platformFileLength */
+
+
+int __PHYSFS_platformEOF(void *opaque)
+{
+ PHYSFS_sint64 pos = __PHYSFS_platformTell(opaque);
+ PHYSFS_sint64 len = __PHYSFS_platformFileLength(opaque);
+ return(pos >= len);
+} /* __PHYSFS_platformEOF */
+
+
+int __PHYSFS_platformFlush(void *opaque)
+{
+ int fd = *((int *) opaque);
+ BAIL_IF_MACRO(fsync(fd) == -1, strerror(errno), 0);
+ return(1);
+} /* __PHYSFS_platformFlush */
+
+
+int __PHYSFS_platformClose(void *opaque)
+{
+ int fd = *((int *) opaque);
+ BAIL_IF_MACRO(close(fd) == -1, strerror(errno), 0);
+ allocator.Free(opaque);
+ return(1);
+} /* __PHYSFS_platformClose */
+
+
+int __PHYSFS_platformDelete(const char *path)
+{
+ BAIL_IF_MACRO(remove(path) == -1, strerror(errno), 0);
+ return(1);
+} /* __PHYSFS_platformDelete */
+
+
+PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
+{
+ struct stat statbuf;
+ BAIL_IF_MACRO(stat(fname, &statbuf) < 0, strerror(errno), -1);
+ return statbuf.st_mtime;
+} /* __PHYSFS_platformGetLastModTime */
+
+#endif /* PHYSFS_PLATFORM_POSIX */
+
+/* end of posix.c ... */
+
--- /dev/null
+/*
+ * Unix support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_platforms.h"
+
+#ifdef PHYSFS_PLATFORM_UNIX
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/mount.h>
+
+#if (!defined PHYSFS_NO_PTHREADS_SUPPORT)
+#include <pthread.h>
+#endif
+
+#ifdef PHYSFS_HAVE_SYS_UCRED_H
+# ifdef PHYSFS_HAVE_MNTENT_H
+# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
+# endif
+# include <sys/ucred.h>
+#endif
+
+#ifdef PHYSFS_HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+
+#include "physfs_internal.h"
+
+
+int __PHYSFS_platformInit(void)
+{
+ return(1); /* always succeed. */
+} /* __PHYSFS_platformInit */
+
+
+int __PHYSFS_platformDeinit(void)
+{
+ return(1); /* always succeed. */
+} /* __PHYSFS_platformDeinit */
+
+
+#ifdef PHYSFS_NO_CDROM_SUPPORT
+
+/* Stub version for platforms without CD-ROM support. */
+void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
+{
+} /* __PHYSFS_platformDetectAvailableCDs */
+
+#elif (defined PHYSFS_HAVE_SYS_UCRED_H)
+
+void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
+{
+ int i;
+ struct statfs *mntbufp = NULL;
+ int mounts = getmntinfo(&mntbufp, MNT_WAIT);
+
+ for (i = 0; i < mounts; i++)
+ {
+ int add_it = 0;
+
+ if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
+ add_it = 1;
+ else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
+ add_it = 1;
+
+ /* add other mount types here */
+
+ if (add_it)
+ cb(data, mntbufp[i].f_mntonname);
+ } /* for */
+} /* __PHYSFS_platformDetectAvailableCDs */
+
+#elif (defined PHYSFS_HAVE_MNTENT_H)
+
+void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
+{
+ FILE *mounts = NULL;
+ struct mntent *ent = NULL;
+
+ mounts = setmntent("/etc/mtab", "r");
+ BAIL_IF_MACRO(mounts == NULL, ERR_IO_ERROR, /*return void*/);
+
+ while ( (ent = getmntent(mounts)) != NULL )
+ {
+ int add_it = 0;
+ if (strcmp(ent->mnt_type, "iso9660") == 0)
+ add_it = 1;
+
+ /* add other mount types here */
+
+ if (add_it)
+ cb(data, ent->mnt_dir);
+ } /* while */
+
+ endmntent(mounts);
+
+} /* __PHYSFS_platformDetectAvailableCDs */
+
+#endif
+
+
+/* this is in posix.c ... */
+extern char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname);
+
+
+/*
+ * See where program (bin) resides in the $PATH specified by (envr).
+ * returns a copy of the first element in envr that contains it, or NULL
+ * if it doesn't exist or there were other problems. PHYSFS_SetError() is
+ * called if we have a problem.
+ *
+ * (envr) will be scribbled over, and you are expected to allocator.Free() the
+ * return value when you're done with it.
+ */
+static char *findBinaryInPath(const char *bin, char *envr)
+{
+ size_t alloc_size = 0;
+ char *exe = NULL;
+ char *start = envr;
+ char *ptr;
+
+ BAIL_IF_MACRO(bin == NULL, ERR_INVALID_ARGUMENT, NULL);
+ BAIL_IF_MACRO(envr == NULL, ERR_INVALID_ARGUMENT, NULL);
+
+ do
+ {
+ size_t size;
+ ptr = strchr(start, ':'); /* find next $PATH separator. */
+ if (ptr)
+ *ptr = '\0';
+
+ size = strlen(start) + strlen(bin) + 2;
+ if (size > alloc_size)
+ {
+ char *x = (char *) allocator.Realloc(exe, size);
+ if (x == NULL)
+ {
+ if (exe != NULL)
+ allocator.Free(exe);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* if */
+
+ alloc_size = size;
+ exe = x;
+ } /* if */
+
+ /* build full binary path... */
+ strcpy(exe, start);
+ if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
+ strcat(exe, "/");
+ strcat(exe, bin);
+
+ if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */
+ {
+ strcpy(exe, start); /* i'm lazy. piss off. */
+ return(exe);
+ } /* if */
+
+ start = ptr + 1; /* start points to beginning of next element. */
+ } while (ptr != NULL);
+
+ if (exe != NULL)
+ allocator.Free(exe);
+
+ return(NULL); /* doesn't exist in path. */
+} /* findBinaryInPath */
+
+
+char *__PHYSFS_platformCalcBaseDir(const char *argv0)
+{
+ const char *PROC_SELF_EXE = "/proc/self/exe";
+ char *retval = NULL;
+ char *envr = NULL;
+ struct stat stbuf;
+
+ /* fast path: default behaviour can handle this. */
+ if ( (argv0 != NULL) && (strchr(argv0, '/') != NULL) )
+ return(NULL); /* higher level will parse out real path from argv0. */
+
+ /*
+ * Try to avoid using argv0 unless forced to. If there's a Linux-like
+ * /proc filesystem, you can get the full path to the current process from
+ * the /proc/self/exe symlink.
+ */
+ if ((lstat(PROC_SELF_EXE, &stbuf) != -1) && (S_ISLNK(stbuf.st_mode)))
+ {
+ const size_t len = stbuf.st_size;
+ char *buf = (char *) allocator.Malloc(len+1);
+ if (buf != NULL) /* if NULL, maybe you'll get lucky later. */
+ {
+ if (readlink(PROC_SELF_EXE, buf, len) != len)
+ allocator.Free(buf);
+ else
+ {
+ buf[len] = '\0'; /* readlink doesn't null-terminate. */
+ retval = buf; /* we're good to go. */
+ } /* else */
+ } /* if */
+ } /* if */
+
+ if ((retval == NULL) && (argv0 != NULL))
+ {
+ /* If there's no dirsep on argv0, then look through $PATH for it. */
+ envr = __PHYSFS_platformCopyEnvironmentVariable("PATH");
+ BAIL_IF_MACRO(!envr, NULL, NULL);
+ retval = findBinaryInPath(argv0, envr);
+ allocator.Free(envr);
+ } /* if */
+
+ return(retval);
+} /* __PHYSFS_platformCalcBaseDir */
+
+
+char *__PHYSFS_platformRealPath(const char *path)
+{
+ char resolved_path[MAXPATHLEN];
+ char *retval = NULL;
+
+ errno = 0;
+ BAIL_IF_MACRO(!realpath(path, resolved_path), strerror(errno), NULL);
+ retval = (char *) allocator.Malloc(strlen(resolved_path) + 1);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+ strcpy(retval, resolved_path);
+
+ return(retval);
+} /* __PHYSFS_platformRealPath */
+
+
+char *__PHYSFS_platformCurrentDir(void)
+{
+ /*
+ * This can't just do platformRealPath("."), since that would eventually
+ * just end up calling back into here.
+ */
+
+ int allocSize = 0;
+ char *retval = NULL;
+ char *ptr;
+
+ do
+ {
+ allocSize += 100;
+ ptr = (char *) allocator.Realloc(retval, allocSize);
+ if (ptr == NULL)
+ {
+ if (retval != NULL)
+ allocator.Free(retval);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* if */
+
+ retval = ptr;
+ ptr = getcwd(retval, allocSize);
+ } while (ptr == NULL && errno == ERANGE);
+
+ if (ptr == NULL && errno)
+ {
+ /*
+ * getcwd() failed for some reason, for example current
+ * directory not existing.
+ */
+ if (retval != NULL)
+ allocator.Free(retval);
+ BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
+ } /* if */
+
+ return(retval);
+} /* __PHYSFS_platformCurrentDir */
+
+
+int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
+{
+ return(0); /* just use malloc() and friends. */
+} /* __PHYSFS_platformSetDefaultAllocator */
+
+
+#if (defined PHYSFS_NO_PTHREADS_SUPPORT)
+
+PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) { return(0x0001); }
+void *__PHYSFS_platformCreateMutex(void) { return((void *) 0x0001); }
+void __PHYSFS_platformDestroyMutex(void *mutex) {}
+int __PHYSFS_platformGrabMutex(void *mutex) { return(1); }
+void __PHYSFS_platformReleaseMutex(void *mutex) {}
+
+#else
+
+typedef struct
+{
+ pthread_mutex_t mutex;
+ pthread_t owner;
+ PHYSFS_uint32 count;
+} PthreadMutex;
+
+/* Just in case; this is a panic value. */
+#if ((!defined SIZEOF_INT) || (SIZEOF_INT <= 0))
+# define SIZEOF_INT 4
+#endif
+
+#if (SIZEOF_INT == 4)
+# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint32) (thr)) )
+#elif (SIZEOF_INT == 2)
+# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint16) (thr)) )
+#elif (SIZEOF_INT == 1)
+# define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint8) (thr)) )
+#else
+# define PHTREAD_TO_UI64(thr) ((PHYSFS_uint64) (thr))
+#endif
+
+PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
+{
+ return(PHTREAD_TO_UI64(pthread_self()));
+} /* __PHYSFS_platformGetThreadID */
+
+
+void *__PHYSFS_platformCreateMutex(void)
+{
+ int rc;
+ PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
+ BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL);
+ rc = pthread_mutex_init(&m->mutex, NULL);
+ if (rc != 0)
+ {
+ allocator.Free(m);
+ BAIL_MACRO(strerror(rc), NULL);
+ } /* if */
+
+ m->count = 0;
+ m->owner = (pthread_t) 0xDEADBEEF;
+ return((void *) m);
+} /* __PHYSFS_platformCreateMutex */
+
+
+void __PHYSFS_platformDestroyMutex(void *mutex)
+{
+ PthreadMutex *m = (PthreadMutex *) mutex;
+
+ /* Destroying a locked mutex is a bug, but we'll try to be helpful. */
+ if ((m->owner == pthread_self()) && (m->count > 0))
+ pthread_mutex_unlock(&m->mutex);
+
+ pthread_mutex_destroy(&m->mutex);
+ allocator.Free(m);
+} /* __PHYSFS_platformDestroyMutex */
+
+
+int __PHYSFS_platformGrabMutex(void *mutex)
+{
+ PthreadMutex *m = (PthreadMutex *) mutex;
+ pthread_t tid = pthread_self();
+ if (m->owner != tid)
+ {
+ if (pthread_mutex_lock(&m->mutex) != 0)
+ return(0);
+ m->owner = tid;
+ } /* if */
+
+ m->count++;
+ return(1);
+} /* __PHYSFS_platformGrabMutex */
+
+
+void __PHYSFS_platformReleaseMutex(void *mutex)
+{
+ PthreadMutex *m = (PthreadMutex *) mutex;
+ if (m->owner == pthread_self())
+ {
+ if (--m->count == 0)
+ {
+ m->owner = (pthread_t) 0xDEADBEEF;
+ pthread_mutex_unlock(&m->mutex);
+ } /* if */
+ } /* if */
+} /* __PHYSFS_platformReleaseMutex */
+
+#endif /* !PHYSFS_NO_PTHREADS_SUPPORT */
+
+#endif /* PHYSFS_PLATFORM_UNIX */
+
+/* end of unix.c ... */
+
--- /dev/null
+/*
+ * Windows support routines for PhysicsFS.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon, and made sane by Gregory S. Read.
+ */
+
+#define __PHYSICSFS_INTERNAL__
+#include "physfs_platforms.h"
+
+#ifdef PHYSFS_PLATFORM_WINDOWS
+
+/* Forcibly disable UNICODE, since we manage this ourselves. */
+#ifdef UNICODE
+#undef UNICODE
+#endif
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "physfs_internal.h"
+
+#define LOWORDER_UINT64(pos) (PHYSFS_uint32) \
+ (pos & 0x00000000FFFFFFFF)
+#define HIGHORDER_UINT64(pos) (PHYSFS_uint32) \
+ (((pos & 0xFFFFFFFF00000000) >> 32) & 0x00000000FFFFFFFF)
+
+/*
+ * Users without the platform SDK don't have this defined. The original docs
+ * for SetFilePointer() just said to compare with 0xFFFFFFFF, so this should
+ * work as desired.
+ */
+#define PHYSFS_INVALID_SET_FILE_POINTER 0xFFFFFFFF
+
+/* just in case... */
+#define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF
+
+/* Not defined before the Vista SDK. */
+#define PHYSFS_IO_REPARSE_TAG_SYMLINK 0xA000000C
+
+
+#define UTF8_TO_UNICODE_STACK_MACRO(w_assignto, str) { \
+ if (str == NULL) \
+ w_assignto = NULL; \
+ else { \
+ const PHYSFS_uint64 len = (PHYSFS_uint64) ((strlen(str) * 4) + 1); \
+ w_assignto = (WCHAR *) __PHYSFS_smallAlloc(len); \
+ if (w_assignto != NULL) \
+ PHYSFS_utf8ToUcs2(str, (PHYSFS_uint16 *) w_assignto, len); \
+ } \
+} \
+
+static PHYSFS_uint64 wStrLen(const WCHAR *wstr)
+{
+ PHYSFS_uint64 len = 0;
+ while (*(wstr++))
+ len++;
+ return(len);
+} /* wStrLen */
+
+static char *unicodeToUtf8Heap(const WCHAR *w_str)
+{
+ char *retval = NULL;
+ if (w_str != NULL)
+ {
+ void *ptr = NULL;
+ const PHYSFS_uint64 len = (wStrLen(w_str) * 4) + 1;
+ retval = allocator.Malloc(len);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+ PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) w_str, retval, len);
+ ptr = allocator.Realloc(retval, strlen(retval) + 1); /* shrink. */
+ if (ptr != NULL)
+ retval = (char *) ptr;
+ } /* if */
+ return(retval);
+} /* unicodeToUtf8Heap */
+
+
+static char *codepageToUtf8Heap(const char *cpstr)
+{
+ char *retval = NULL;
+ if (cpstr != NULL)
+ {
+ const int len = (int) (strlen(cpstr) + 1);
+ WCHAR *wbuf = (WCHAR *) __PHYSFS_smallAlloc(len * sizeof (WCHAR));
+ BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL);
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, cpstr, len, wbuf, len);
+ retval = (char *) allocator.Malloc(len * 4);
+ if (retval == NULL)
+ __PHYSFS_setError(ERR_OUT_OF_MEMORY);
+ else
+ PHYSFS_utf8FromUcs2(wbuf, retval, len * 4);
+ __PHYSFS_smallFree(wbuf);
+ } /* if */
+ return(retval);
+} /* codepageToUtf8Heap */
+
+
+typedef struct
+{
+ HANDLE handle;
+ int readonly;
+} WinApiFile;
+
+
+static char *userDir = NULL;
+static int osHasUnicode = 0;
+
+
+/* pointers for APIs that may not exist on some Windows versions... */
+static HANDLE libKernel32 = NULL;
+static HANDLE libUserEnv = NULL;
+static HANDLE libAdvApi32 = NULL;
+static DWORD (WINAPI *pGetModuleFileNameW)(HMODULE, LPWCH, DWORD);
+static BOOL (WINAPI *pGetUserProfileDirectoryW)(HANDLE, LPWSTR, LPDWORD);
+static BOOL (WINAPI *pGetUserNameW)(LPWSTR, LPDWORD);
+static DWORD (WINAPI *pGetFileAttributesW)(LPCWSTR);
+static HANDLE (WINAPI *pFindFirstFileW)(LPCWSTR, LPWIN32_FIND_DATAW);
+static BOOL (WINAPI *pFindNextFileW)(HANDLE, LPWIN32_FIND_DATAW);
+static DWORD (WINAPI *pGetCurrentDirectoryW)(DWORD, LPWSTR);
+static BOOL (WINAPI *pDeleteFileW)(LPCWSTR);
+static BOOL (WINAPI *pRemoveDirectoryW)(LPCWSTR);
+static BOOL (WINAPI *pCreateDirectoryW)(LPCWSTR, LPSECURITY_ATTRIBUTES);
+static BOOL (WINAPI *pGetFileAttributesExA)
+ (LPCSTR, GET_FILEEX_INFO_LEVELS, LPVOID);
+static BOOL (WINAPI *pGetFileAttributesExW)
+ (LPCWSTR, GET_FILEEX_INFO_LEVELS, LPVOID);
+static DWORD (WINAPI *pFormatMessageW)
+ (DWORD, LPCVOID, DWORD, DWORD, LPWSTR, DWORD, va_list *);
+static HANDLE (WINAPI *pCreateFileW)
+ (LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
+
+
+/*
+ * Fallbacks for missing Unicode functions on Win95/98/ME. These are filled
+ * into the function pointers if looking up the real Unicode entry points
+ * in the system DLLs fails, so they're never used on WinNT/XP/Vista/etc.
+ * They make an earnest effort to convert to/from UTF-8 and UCS-2 to
+ * the user's current codepage.
+ */
+
+static BOOL WINAPI fallbackGetUserNameW(LPWSTR buf, LPDWORD len)
+{
+ const DWORD cplen = *len;
+ char *cpstr = __PHYSFS_smallAlloc(cplen);
+ BOOL retval = GetUserNameA(cpstr, len);
+ if (buf != NULL)
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, cpstr, cplen, buf, *len);
+ __PHYSFS_smallFree(cpstr);
+ return(retval);
+} /* fallbackGetUserNameW */
+
+static DWORD WINAPI fallbackFormatMessageW(DWORD dwFlags, LPCVOID lpSource,
+ DWORD dwMessageId, DWORD dwLangId,
+ LPWSTR lpBuf, DWORD nSize,
+ va_list *Arguments)
+{
+ char *cpbuf = (char *) __PHYSFS_smallAlloc(nSize);
+ DWORD retval = FormatMessageA(dwFlags, lpSource, dwMessageId, dwLangId,
+ cpbuf, nSize, Arguments);
+ if (retval > 0)
+ MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,lpBuf,nSize);
+ __PHYSFS_smallFree(cpbuf);
+ return(retval);
+} /* fallbackFormatMessageW */
+
+static DWORD WINAPI fallbackGetModuleFileNameW(HMODULE hMod, LPWCH lpBuf,
+ DWORD nSize)
+{
+ char *cpbuf = (char *) __PHYSFS_smallAlloc(nSize);
+ DWORD retval = GetModuleFileNameA(hMod, cpbuf, nSize);
+ if (retval > 0)
+ MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,lpBuf,nSize);
+ __PHYSFS_smallFree(cpbuf);
+ return(retval);
+} /* fallbackGetModuleFileNameW */
+
+static DWORD WINAPI fallbackGetFileAttributesW(LPCWSTR fname)
+{
+ DWORD retval = 0;
+ const int buflen = (int) (wStrLen(fname) + 1);
+ char *cpstr = (char *) __PHYSFS_smallAlloc(buflen);
+ WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL);
+ retval = GetFileAttributesA(cpstr);
+ __PHYSFS_smallFree(cpstr);
+ return(retval);
+} /* fallbackGetFileAttributesW */
+
+static DWORD WINAPI fallbackGetCurrentDirectoryW(DWORD buflen, LPWSTR buf)
+{
+ DWORD retval = 0;
+ char *cpbuf = NULL;
+ if (buf != NULL)
+ cpbuf = (char *) __PHYSFS_smallAlloc(buflen);
+ retval = GetCurrentDirectoryA(buflen, cpbuf);
+ if (cpbuf != NULL)
+ {
+ MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,cpbuf,retval,buf,buflen);
+ __PHYSFS_smallFree(cpbuf);
+ } /* if */
+ return(retval);
+} /* fallbackGetCurrentDirectoryW */
+
+static BOOL WINAPI fallbackRemoveDirectoryW(LPCWSTR dname)
+{
+ BOOL retval = 0;
+ const int buflen = (int) (wStrLen(dname) + 1);
+ char *cpstr = (char *) __PHYSFS_smallAlloc(buflen);
+ WideCharToMultiByte(CP_ACP, 0, dname, buflen, cpstr, buflen, NULL, NULL);
+ retval = RemoveDirectoryA(cpstr);
+ __PHYSFS_smallFree(cpstr);
+ return(retval);
+} /* fallbackRemoveDirectoryW */
+
+static BOOL WINAPI fallbackCreateDirectoryW(LPCWSTR dname,
+ LPSECURITY_ATTRIBUTES attr)
+{
+ BOOL retval = 0;
+ const int buflen = (int) (wStrLen(dname) + 1);
+ char *cpstr = (char *) __PHYSFS_smallAlloc(buflen);
+ WideCharToMultiByte(CP_ACP, 0, dname, buflen, cpstr, buflen, NULL, NULL);
+ retval = CreateDirectoryA(cpstr, attr);
+ __PHYSFS_smallFree(cpstr);
+ return(retval);
+} /* fallbackCreateDirectoryW */
+
+static BOOL WINAPI fallbackDeleteFileW(LPCWSTR fname)
+{
+ BOOL retval = 0;
+ const int buflen = (int) (wStrLen(fname) + 1);
+ char *cpstr = (char *) __PHYSFS_smallAlloc(buflen);
+ WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL);
+ retval = DeleteFileA(cpstr);
+ __PHYSFS_smallFree(cpstr);
+ return(retval);
+} /* fallbackDeleteFileW */
+
+static HANDLE WINAPI fallbackCreateFileW(LPCWSTR fname,
+ DWORD dwDesiredAccess, DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttrs,
+ DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttrs, HANDLE hTemplFile)
+{
+ HANDLE retval;
+ const int buflen = (int) (wStrLen(fname) + 1);
+ char *cpstr = (char *) __PHYSFS_smallAlloc(buflen);
+ WideCharToMultiByte(CP_ACP, 0, fname, buflen, cpstr, buflen, NULL, NULL);
+ retval = CreateFileA(cpstr, dwDesiredAccess, dwShareMode, lpSecurityAttrs,
+ dwCreationDisposition, dwFlagsAndAttrs, hTemplFile);
+ __PHYSFS_smallFree(cpstr);
+ return(retval);
+} /* fallbackCreateFileW */
+
+
+/* A blatant abuse of pointer casting... */
+static int symLookup(HMODULE dll, void **addr, const char *sym)
+{
+ return( (*addr = GetProcAddress(dll, sym)) != NULL );
+} /* symLookup */
+
+
+static int findApiSymbols(void)
+{
+ HMODULE dll = NULL;
+
+ #define LOOKUP_NOFALLBACK(x, reallyLook) { \
+ if (reallyLook) \
+ symLookup(dll, (void **) &p##x, #x); \
+ else \
+ p##x = NULL; \
+ }
+
+ #define LOOKUP(x, reallyLook) { \
+ if ((!reallyLook) || (!symLookup(dll, (void **) &p##x, #x))) \
+ p##x = fallback##x; \
+ }
+
+ /* Apparently Win9x HAS the Unicode entry points, they just don't WORK. */
+ /* ...so don't look them up unless we're on NT+. (see osHasUnicode.) */
+
+ dll = libUserEnv = LoadLibraryA("userenv.dll");
+ if (dll != NULL)
+ LOOKUP_NOFALLBACK(GetUserProfileDirectoryW, osHasUnicode);
+
+ /* !!! FIXME: what do they call advapi32.dll on Win64? */
+ dll = libAdvApi32 = LoadLibraryA("advapi32.dll");
+ if (dll != NULL)
+ LOOKUP(GetUserNameW, osHasUnicode);
+
+ /* !!! FIXME: what do they call kernel32.dll on Win64? */
+ dll = libKernel32 = LoadLibraryA("kernel32.dll");
+ if (dll != NULL)
+ {
+ LOOKUP_NOFALLBACK(GetFileAttributesExA, 1);
+ LOOKUP_NOFALLBACK(GetFileAttributesExW, osHasUnicode);
+ LOOKUP_NOFALLBACK(FindFirstFileW, osHasUnicode);
+ LOOKUP_NOFALLBACK(FindNextFileW, osHasUnicode);
+ LOOKUP(GetModuleFileNameW, osHasUnicode);
+ LOOKUP(FormatMessageW, osHasUnicode);
+ LOOKUP(GetFileAttributesW, osHasUnicode);
+ LOOKUP(GetCurrentDirectoryW, osHasUnicode);
+ LOOKUP(CreateDirectoryW, osHasUnicode);
+ LOOKUP(RemoveDirectoryW, osHasUnicode);
+ LOOKUP(CreateFileW, osHasUnicode);
+ LOOKUP(DeleteFileW, osHasUnicode);
+ } /* if */
+
+ #undef LOOKUP_NOFALLBACK
+ #undef LOOKUP
+
+ return(1);
+} /* findApiSymbols */
+
+
+const char *__PHYSFS_platformDirSeparator = "\\";
+
+
+/*
+ * Figure out what the last failing Windows API call was, and
+ * generate a human-readable string for the error message.
+ *
+ * The return value is a static buffer that is overwritten with
+ * each call to this function.
+ */
+static const char *winApiStrError(void)
+{
+ static char utf8buf[255];
+ WCHAR msgbuf[255];
+ WCHAR *ptr;
+ DWORD rc = pFormatMessageW(
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ msgbuf, __PHYSFS_ARRAYLEN(msgbuf),
+ NULL);
+
+ /* chop off newlines. */
+ for (ptr = msgbuf; *ptr; ptr++)
+ {
+ if ((*ptr == '\n') || (*ptr == '\r'))
+ {
+ *ptr = '\0';
+ break;
+ } /* if */
+ } /* for */
+
+ /* may truncate, but oh well. */
+ PHYSFS_utf8FromUcs2((PHYSFS_uint16 *) msgbuf, utf8buf, sizeof (utf8buf));
+ return((const char *) utf8buf);
+} /* winApiStrError */
+
+
+static char *getExePath(void)
+{
+ DWORD buflen = 64;
+ int success = 0;
+ LPWSTR modpath = NULL;
+ char *retval = NULL;
+
+ while (1)
+ {
+ DWORD rc;
+ void *ptr;
+
+ if ( !(ptr = allocator.Realloc(modpath, buflen*sizeof(WCHAR))) )
+ {
+ allocator.Free(modpath);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* if */
+ modpath = (LPWSTR) ptr;
+
+ rc = pGetModuleFileNameW(NULL, modpath, buflen);
+ if (rc == 0)
+ {
+ allocator.Free(modpath);
+ BAIL_MACRO(winApiStrError(), NULL);
+ } /* if */
+
+ if (rc < buflen)
+ {
+ buflen = rc;
+ break;
+ } /* if */
+
+ buflen *= 2;
+ } /* while */
+
+ if (buflen > 0) /* just in case... */
+ {
+ WCHAR *ptr = (modpath + buflen) - 1;
+ while (ptr != modpath)
+ {
+ if (*ptr == '\\')
+ break;
+ ptr--;
+ } /* while */
+
+ if ((ptr == modpath) && (*ptr != '\\'))
+ __PHYSFS_setError(ERR_GETMODFN_NO_DIR);
+ else
+ {
+ *(ptr + 1) = '\0'; /* chop off filename. */
+ retval = unicodeToUtf8Heap(modpath);
+ } /* else */
+ } /* else */
+ allocator.Free(modpath);
+
+ return(retval); /* w00t. */
+} /* getExePath */
+
+
+/*
+ * Try to make use of GetUserProfileDirectoryW(), which isn't available on
+ * some common variants of Win32. If we can't use this, we just punt and
+ * use the physfs base dir for the user dir, too.
+ *
+ * On success, module-scope variable (userDir) will have a pointer to
+ * a malloc()'d string of the user's profile dir, and a non-zero value is
+ * returned. If we can't determine the profile dir, (userDir) will
+ * be NULL, and zero is returned.
+ */
+static int determineUserDir(void)
+{
+ if (userDir != NULL)
+ return(1); /* already good to go. */
+
+ /*
+ * GetUserProfileDirectoryW() is only available on NT 4.0 and later.
+ * This means Win95/98/ME (and CE?) users have to do without, so for
+ * them, we'll default to the base directory when we can't get the
+ * function pointer. Since this is originally an NT API, we don't
+ * offer a non-Unicode fallback.
+ */
+ if (pGetUserProfileDirectoryW != NULL)
+ {
+ HANDLE accessToken = NULL; /* Security handle to process */
+ HANDLE processHandle = GetCurrentProcess();
+ if (OpenProcessToken(processHandle, TOKEN_QUERY, &accessToken))
+ {
+ DWORD psize = 0;
+ WCHAR dummy = 0;
+ LPWSTR wstr = NULL;
+ BOOL rc = 0;
+
+ /*
+ * Should fail. Will write the size of the profile path in
+ * psize. Also note that the second parameter can't be
+ * NULL or the function fails.
+ */
+ rc = pGetUserProfileDirectoryW(accessToken, &dummy, &psize);
+ assert(!rc); /* !!! FIXME: handle this gracefully. */
+
+ /* Allocate memory for the profile directory */
+ wstr = (LPWSTR) __PHYSFS_smallAlloc(psize * sizeof (WCHAR));
+ if (wstr != NULL)
+ {
+ if (pGetUserProfileDirectoryW(accessToken, wstr, &psize))
+ userDir = unicodeToUtf8Heap(wstr);
+ __PHYSFS_smallFree(wstr);
+ } /* else */
+ } /* if */
+
+ CloseHandle(accessToken);
+ } /* if */
+
+ if (userDir == NULL) /* couldn't get profile for some reason. */
+ {
+ /* Might just be a non-NT system; resort to the basedir. */
+ userDir = getExePath();
+ BAIL_IF_MACRO(userDir == NULL, NULL, 0); /* STILL failed?! */
+ } /* if */
+
+ return(1); /* We made it: hit the showers. */
+} /* determineUserDir */
+
+
+static BOOL mediaInDrive(const char *drive)
+{
+ UINT oldErrorMode;
+ DWORD tmp;
+ BOOL retval;
+
+ /* Prevent windows warning message appearing when checking media size */
+ oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ /* If this function succeeds, there's media in the drive */
+ retval = GetVolumeInformationA(drive, NULL, 0, NULL, NULL, &tmp, NULL, 0);
+
+ /* Revert back to old windows error handler */
+ SetErrorMode(oldErrorMode);
+
+ return(retval);
+} /* mediaInDrive */
+
+
+void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
+{
+ /* !!! FIXME: Can CD drives be non-drive letter paths? */
+ /* !!! FIXME: (so can they be Unicode paths?) */
+ char drive_str[4] = "x:\\";
+ char ch;
+ for (ch = 'A'; ch <= 'Z'; ch++)
+ {
+ drive_str[0] = ch;
+ if (GetDriveType(drive_str) == DRIVE_CDROM && mediaInDrive(drive_str))
+ cb(data, drive_str);
+ } /* for */
+} /* __PHYSFS_platformDetectAvailableCDs */
+
+
+char *__PHYSFS_platformCalcBaseDir(const char *argv0)
+{
+ if ((argv0 != NULL) && (strchr(argv0, '\\') != NULL))
+ return(NULL); /* default behaviour can handle this. */
+
+ return(getExePath());
+} /* __PHYSFS_platformCalcBaseDir */
+
+
+char *__PHYSFS_platformGetUserName(void)
+{
+ DWORD bufsize = 0;
+ char *retval = NULL;
+
+ if (pGetUserNameW(NULL, &bufsize) == 0) /* This SHOULD fail. */
+ {
+ LPWSTR wbuf = (LPWSTR) __PHYSFS_smallAlloc(bufsize * sizeof (WCHAR));
+ BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL);
+ if (pGetUserNameW(wbuf, &bufsize) == 0) /* ?! */
+ __PHYSFS_setError(winApiStrError());
+ else
+ retval = unicodeToUtf8Heap(wbuf);
+ __PHYSFS_smallFree(wbuf);
+ } /* if */
+
+ return(retval);
+} /* __PHYSFS_platformGetUserName */
+
+
+char *__PHYSFS_platformGetUserDir(void)
+{
+ char *retval = (char *) allocator.Malloc(strlen(userDir) + 1);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+ strcpy(retval, userDir); /* calculated at init time. */
+ return(retval);
+} /* __PHYSFS_platformGetUserDir */
+
+
+PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
+{
+ return((PHYSFS_uint64) GetCurrentThreadId());
+} /* __PHYSFS_platformGetThreadID */
+
+
+static int doPlatformExists(LPWSTR wpath)
+{
+ BAIL_IF_MACRO
+ (
+ pGetFileAttributesW(wpath) == PHYSFS_INVALID_FILE_ATTRIBUTES,
+ winApiStrError(), 0
+ );
+ return(1);
+} /* doPlatformExists */
+
+
+int __PHYSFS_platformExists(const char *fname)
+{
+ int retval = 0;
+ LPWSTR wpath;
+ UTF8_TO_UNICODE_STACK_MACRO(wpath, fname);
+ BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0);
+ retval = doPlatformExists(wpath);
+ __PHYSFS_smallFree(wpath);
+ return(retval);
+} /* __PHYSFS_platformExists */
+
+
+static int isSymlinkAttrs(const DWORD attr, const DWORD tag)
+{
+ return ( (attr & FILE_ATTRIBUTE_REPARSE_POINT) &&
+ (tag == PHYSFS_IO_REPARSE_TAG_SYMLINK) );
+} /* isSymlinkAttrs */
+
+
+int __PHYSFS_platformIsSymLink(const char *fname)
+{
+ /* !!! FIXME:
+ * Windows Vista can have NTFS symlinks. Can older Windows releases have
+ * them when talking to a network file server? What happens when you
+ * mount a NTFS partition on XP that was plugged into a Vista install
+ * that made a symlink?
+ */
+
+ int retval = 0;
+ LPWSTR wpath;
+ HANDLE dir;
+ WIN32_FIND_DATAW entw;
+
+ /* no unicode entry points? Probably no symlinks. */
+ BAIL_IF_MACRO(pFindFirstFileW == NULL, NULL, 0);
+
+ UTF8_TO_UNICODE_STACK_MACRO(wpath, fname);
+ BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0);
+
+ /* !!! FIXME: filter wildcard chars? */
+ dir = pFindFirstFileW(wpath, &entw);
+ if (dir != INVALID_HANDLE_VALUE)
+ {
+ retval = isSymlinkAttrs(entw.dwFileAttributes, entw.dwReserved0);
+ FindClose(dir);
+ } /* if */
+
+ __PHYSFS_smallFree(wpath);
+ return(retval);
+} /* __PHYSFS_platformIsSymlink */
+
+
+int __PHYSFS_platformIsDirectory(const char *fname)
+{
+ int retval = 0;
+ LPWSTR wpath;
+ UTF8_TO_UNICODE_STACK_MACRO(wpath, fname);
+ BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0);
+ retval = ((pGetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY) != 0);
+ __PHYSFS_smallFree(wpath);
+ return(retval);
+} /* __PHYSFS_platformIsDirectory */
+
+
+char *__PHYSFS_platformCvtToDependent(const char *prepend,
+ const char *dirName,
+ const char *append)
+{
+ int len = ((prepend) ? strlen(prepend) : 0) +
+ ((append) ? strlen(append) : 0) +
+ strlen(dirName) + 1;
+ char *retval = (char *) allocator.Malloc(len);
+ char *p;
+
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ if (prepend)
+ strcpy(retval, prepend);
+ else
+ retval[0] = '\0';
+
+ strcat(retval, dirName);
+
+ if (append)
+ strcat(retval, append);
+
+ for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/'))
+ *p = '\\';
+
+ return(retval);
+} /* __PHYSFS_platformCvtToDependent */
+
+
+void __PHYSFS_platformEnumerateFiles(const char *dirname,
+ int omitSymLinks,
+ PHYSFS_EnumFilesCallback callback,
+ const char *origdir,
+ void *callbackdata)
+{
+ const int unicode = (pFindFirstFileW != NULL) && (pFindNextFileW != NULL);
+ HANDLE dir = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATA ent;
+ WIN32_FIND_DATAW entw;
+ size_t len = strlen(dirname);
+ char *searchPath = NULL;
+ WCHAR *wSearchPath = NULL;
+ char *utf8 = NULL;
+
+ /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */
+ searchPath = (char *) __PHYSFS_smallAlloc(len + 3);
+ if (searchPath == NULL)
+ return;
+
+ /* Copy current dirname */
+ strcpy(searchPath, dirname);
+
+ /* if there's no '\\' at the end of the path, stick one in there. */
+ if (searchPath[len - 1] != '\\')
+ {
+ searchPath[len++] = '\\';
+ searchPath[len] = '\0';
+ } /* if */
+
+ /* Append the "*" to the end of the string */
+ strcat(searchPath, "*");
+
+ UTF8_TO_UNICODE_STACK_MACRO(wSearchPath, searchPath);
+ if (wSearchPath == NULL)
+ return; /* oh well. */
+
+ if (unicode)
+ dir = pFindFirstFileW(wSearchPath, &entw);
+ else
+ {
+ const int len = (int) (wStrLen(wSearchPath) + 1);
+ char *cp = (char *) __PHYSFS_smallAlloc(len);
+ if (cp != NULL)
+ {
+ WideCharToMultiByte(CP_ACP, 0, wSearchPath, len, cp, len, 0, 0);
+ dir = FindFirstFileA(cp, &ent);
+ __PHYSFS_smallFree(cp);
+ } /* if */
+ } /* else */
+
+ __PHYSFS_smallFree(wSearchPath);
+ __PHYSFS_smallFree(searchPath);
+ if (dir == INVALID_HANDLE_VALUE)
+ return;
+
+ if (unicode)
+ {
+ do
+ {
+ const DWORD attr = entw.dwFileAttributes;
+ const DWORD tag = entw.dwReserved0;
+ const WCHAR *fn = entw.cFileName;
+ if ((fn[0] == '.') && (fn[1] == '\0'))
+ continue;
+ if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0'))
+ continue;
+ if ((omitSymLinks) && (isSymlinkAttrs(attr, tag)))
+ continue;
+
+ utf8 = unicodeToUtf8Heap(fn);
+ if (utf8 != NULL)
+ {
+ callback(callbackdata, origdir, utf8);
+ allocator.Free(utf8);
+ } /* if */
+ } while (pFindNextFileW(dir, &entw) != 0);
+ } /* if */
+
+ else /* ANSI fallback. */
+ {
+ do
+ {
+ const DWORD attr = ent.dwFileAttributes;
+ const DWORD tag = ent.dwReserved0;
+ const char *fn = ent.cFileName;
+ if ((fn[0] == '.') && (fn[1] == '\0'))
+ continue;
+ if ((fn[0] == '.') && (fn[1] == '.') && (fn[2] == '\0'))
+ continue;
+ if ((omitSymLinks) && (isSymlinkAttrs(attr, tag)))
+ continue;
+
+ utf8 = codepageToUtf8Heap(fn);
+ if (utf8 != NULL)
+ {
+ callback(callbackdata, origdir, utf8);
+ allocator.Free(utf8);
+ } /* if */
+ } while (FindNextFileA(dir, &ent) != 0);
+ } /* else */
+
+ FindClose(dir);
+} /* __PHYSFS_platformEnumerateFiles */
+
+
+char *__PHYSFS_platformCurrentDir(void)
+{
+ char *retval = NULL;
+ WCHAR *wbuf = NULL;
+ DWORD buflen = 0;
+
+ buflen = pGetCurrentDirectoryW(buflen, NULL);
+ wbuf = (WCHAR *) __PHYSFS_smallAlloc((buflen + 2) * sizeof (WCHAR));
+ BAIL_IF_MACRO(wbuf == NULL, ERR_OUT_OF_MEMORY, NULL);
+ pGetCurrentDirectoryW(buflen, wbuf);
+
+ if (wbuf[buflen - 2] == '\\')
+ wbuf[buflen-1] = '\0'; /* just in case... */
+ else
+ {
+ wbuf[buflen - 1] = '\\';
+ wbuf[buflen] = '\0';
+ } /* else */
+
+ retval = unicodeToUtf8Heap(wbuf);
+ __PHYSFS_smallFree(wbuf);
+ return(retval);
+} /* __PHYSFS_platformCurrentDir */
+
+
+/* this could probably use a cleanup. */
+char *__PHYSFS_platformRealPath(const char *path)
+{
+ /* !!! FIXME: try GetFullPathName() instead? */
+ /* this function should be UTF-8 clean. */
+ char *retval = NULL;
+ char *p = NULL;
+
+ BAIL_IF_MACRO(path == NULL, ERR_INVALID_ARGUMENT, NULL);
+ BAIL_IF_MACRO(*path == '\0', ERR_INVALID_ARGUMENT, NULL);
+
+ retval = (char *) allocator.Malloc(MAX_PATH);
+ BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
+
+ /*
+ * If in \\server\path format, it's already an absolute path.
+ * We'll need to check for "." and ".." dirs, though, just in case.
+ */
+ if ((path[0] == '\\') && (path[1] == '\\'))
+ strcpy(retval, path);
+
+ else
+ {
+ char *currentDir = __PHYSFS_platformCurrentDir();
+ if (currentDir == NULL)
+ {
+ allocator.Free(retval);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* if */
+
+ if (path[1] == ':') /* drive letter specified? */
+ {
+ /*
+ * Apparently, "D:mypath" is the same as "D:\\mypath" if
+ * D: is not the current drive. However, if D: is the
+ * current drive, then "D:mypath" is a relative path. Ugh.
+ */
+ if (path[2] == '\\') /* maybe an absolute path? */
+ strcpy(retval, path);
+ else /* definitely an absolute path. */
+ {
+ if (path[0] == currentDir[0]) /* current drive; relative. */
+ {
+ strcpy(retval, currentDir);
+ strcat(retval, path + 2);
+ } /* if */
+
+ else /* not current drive; absolute. */
+ {
+ retval[0] = path[0];
+ retval[1] = ':';
+ retval[2] = '\\';
+ strcpy(retval + 3, path + 2);
+ } /* else */
+ } /* else */
+ } /* if */
+
+ else /* no drive letter specified. */
+ {
+ if (path[0] == '\\') /* absolute path. */
+ {
+ retval[0] = currentDir[0];
+ retval[1] = ':';
+ strcpy(retval + 2, path);
+ } /* if */
+ else
+ {
+ strcpy(retval, currentDir);
+ strcat(retval, path);
+ } /* else */
+ } /* else */
+
+ allocator.Free(currentDir);
+ } /* else */
+
+ /* (whew.) Ok, now take out "." and ".." path entries... */
+
+ p = retval;
+ while ( (p = strstr(p, "\\.")) != NULL)
+ {
+ /* it's a "." entry that doesn't end the string. */
+ if (p[2] == '\\')
+ memmove(p + 1, p + 3, strlen(p + 3) + 1);
+
+ /* it's a "." entry that ends the string. */
+ else if (p[2] == '\0')
+ p[0] = '\0';
+
+ /* it's a ".." entry. */
+ else if (p[2] == '.')
+ {
+ char *prevEntry = p - 1;
+ while ((prevEntry != retval) && (*prevEntry != '\\'))
+ prevEntry--;
+
+ if (prevEntry == retval) /* make it look like a "." entry. */
+ memmove(p + 1, p + 2, strlen(p + 2) + 1);
+ else
+ {
+ if (p[3] != '\0') /* doesn't end string. */
+ *prevEntry = '\0';
+ else /* ends string. */
+ memmove(prevEntry + 1, p + 4, strlen(p + 4) + 1);
+
+ p = prevEntry;
+ } /* else */
+ } /* else if */
+
+ else
+ {
+ p++; /* look past current char. */
+ } /* else */
+ } /* while */
+
+ /* shrink the retval's memory block if possible... */
+ p = (char *) allocator.Realloc(retval, strlen(retval) + 1);
+ if (p != NULL)
+ retval = p;
+
+ return(retval);
+} /* __PHYSFS_platformRealPath */
+
+
+int __PHYSFS_platformMkDir(const char *path)
+{
+ WCHAR *wpath;
+ DWORD rc;
+ UTF8_TO_UNICODE_STACK_MACRO(wpath, path);
+ rc = pCreateDirectoryW(wpath, NULL);
+ __PHYSFS_smallFree(wpath);
+ BAIL_IF_MACRO(rc == 0, winApiStrError(), 0);
+ return(1);
+} /* __PHYSFS_platformMkDir */
+
+
+ /*
+ * Get OS info and save the important parts.
+ *
+ * Returns non-zero if successful, otherwise it returns zero on failure.
+ */
+ static int getOSInfo(void)
+ {
+ OSVERSIONINFO osVerInfo; /* Information about the OS */
+ osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
+ BAIL_IF_MACRO(!GetVersionEx(&osVerInfo), winApiStrError(), 0);
+ osHasUnicode = (osVerInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS);
+ return(1);
+ } /* getOSInfo */
+
+
+int __PHYSFS_platformInit(void)
+{
+ BAIL_IF_MACRO(!getOSInfo(), NULL, 0);
+ BAIL_IF_MACRO(!findApiSymbols(), NULL, 0);
+ BAIL_IF_MACRO(!determineUserDir(), NULL, 0);
+ return(1); /* It's all good */
+} /* __PHYSFS_platformInit */
+
+
+int __PHYSFS_platformDeinit(void)
+{
+ HANDLE *libs[] = { &libKernel32, &libUserEnv, &libAdvApi32, NULL };
+ int i;
+
+ allocator.Free(userDir);
+ userDir = NULL;
+
+ for (i = 0; libs[i] != NULL; i++)
+ {
+ const HANDLE lib = *(libs[i]);
+ if (lib)
+ FreeLibrary(lib);
+ *(libs[i]) = NULL;
+ } /* for */
+
+ return(1); /* It's all good */
+} /* __PHYSFS_platformDeinit */
+
+
+static void *doOpen(const char *fname, DWORD mode, DWORD creation, int rdonly)
+{
+ HANDLE fileHandle;
+ WinApiFile *retval;
+ WCHAR *wfname;
+
+ UTF8_TO_UNICODE_STACK_MACRO(wfname, fname);
+ BAIL_IF_MACRO(wfname == NULL, ERR_OUT_OF_MEMORY, NULL);
+ fileHandle = pCreateFileW(wfname, mode, FILE_SHARE_READ, NULL,
+ creation, FILE_ATTRIBUTE_NORMAL, NULL);
+ __PHYSFS_smallFree(wfname);
+
+ BAIL_IF_MACRO
+ (
+ fileHandle == INVALID_HANDLE_VALUE,
+ winApiStrError(), NULL
+ );
+
+ retval = (WinApiFile *) allocator.Malloc(sizeof (WinApiFile));
+ if (retval == NULL)
+ {
+ CloseHandle(fileHandle);
+ BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
+ } /* if */
+
+ retval->readonly = rdonly;
+ retval->handle = fileHandle;
+ return(retval);
+} /* doOpen */
+
+
+void *__PHYSFS_platformOpenRead(const char *filename)
+{
+ return(doOpen(filename, GENERIC_READ, OPEN_EXISTING, 1));
+} /* __PHYSFS_platformOpenRead */
+
+
+void *__PHYSFS_platformOpenWrite(const char *filename)
+{
+ return(doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS, 0));
+} /* __PHYSFS_platformOpenWrite */
+
+
+void *__PHYSFS_platformOpenAppend(const char *filename)
+{
+ void *retval = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS, 0);
+ if (retval != NULL)
+ {
+ HANDLE h = ((WinApiFile *) retval)->handle;
+ DWORD rc = SetFilePointer(h, 0, NULL, FILE_END);
+ if (rc == PHYSFS_INVALID_SET_FILE_POINTER)
+ {
+ const char *err = winApiStrError();
+ CloseHandle(h);
+ allocator.Free(retval);
+ BAIL_MACRO(err, NULL);
+ } /* if */
+ } /* if */
+
+ return(retval);
+} /* __PHYSFS_platformOpenAppend */
+
+
+PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
+ PHYSFS_uint32 size, PHYSFS_uint32 count)
+{
+ HANDLE Handle = ((WinApiFile *) opaque)->handle;
+ DWORD CountOfBytesRead;
+ PHYSFS_sint64 retval;
+
+ /* Read data from the file */
+ /* !!! FIXME: uint32 might be a greater # than DWORD */
+ if(!ReadFile(Handle, buffer, count * size, &CountOfBytesRead, NULL))
+ {
+ BAIL_MACRO(winApiStrError(), -1);
+ } /* if */
+ else
+ {
+ /* Return the number of "objects" read. */
+ /* !!! FIXME: What if not the right amount of bytes was read to make an object? */
+ retval = CountOfBytesRead / size;
+ } /* else */
+
+ return(retval);
+} /* __PHYSFS_platformRead */
+
+
+PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
+ PHYSFS_uint32 size, PHYSFS_uint32 count)
+{
+ HANDLE Handle = ((WinApiFile *) opaque)->handle;
+ DWORD CountOfBytesWritten;
+ PHYSFS_sint64 retval;
+
+ /* Read data from the file */
+ /* !!! FIXME: uint32 might be a greater # than DWORD */
+ if(!WriteFile(Handle, buffer, count * size, &CountOfBytesWritten, NULL))
+ {
+ BAIL_MACRO(winApiStrError(), -1);
+ } /* if */
+ else
+ {
+ /* Return the number of "objects" read. */
+ /* !!! FIXME: What if not the right number of bytes was written? */
+ retval = CountOfBytesWritten / size;
+ } /* else */
+
+ return(retval);
+} /* __PHYSFS_platformWrite */
+
+
+int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
+{
+ HANDLE Handle = ((WinApiFile *) opaque)->handle;
+ DWORD HighOrderPos;
+ DWORD *pHighOrderPos;
+ DWORD rc;
+
+ /* Get the high order 32-bits of the position */
+ HighOrderPos = HIGHORDER_UINT64(pos);
+
+ /*
+ * MSDN: "If you do not need the high-order 32 bits, this
+ * pointer must be set to NULL."
+ */
+ pHighOrderPos = (HighOrderPos) ? &HighOrderPos : NULL;
+
+ /*
+ * !!! FIXME: MSDN: "Windows Me/98/95: If the pointer
+ * !!! FIXME: lpDistanceToMoveHigh is not NULL, then it must
+ * !!! FIXME: point to either 0, INVALID_SET_FILE_POINTER, or
+ * !!! FIXME: the sign extension of the value of lDistanceToMove.
+ * !!! FIXME: Any other value will be rejected."
+ */
+
+ /* Move pointer "pos" count from start of file */
+ rc = SetFilePointer(Handle, LOWORDER_UINT64(pos),
+ pHighOrderPos, FILE_BEGIN);
+
+ if ( (rc == PHYSFS_INVALID_SET_FILE_POINTER) &&
+ (GetLastError() != NO_ERROR) )
+ {
+ BAIL_MACRO(winApiStrError(), 0);
+ } /* if */
+
+ return(1); /* No error occured */
+} /* __PHYSFS_platformSeek */
+
+
+PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
+{
+ HANDLE Handle = ((WinApiFile *) opaque)->handle;
+ DWORD HighPos = 0;
+ DWORD LowPos;
+ PHYSFS_sint64 retval;
+
+ /* Get current position */
+ LowPos = SetFilePointer(Handle, 0, &HighPos, FILE_CURRENT);
+ if ( (LowPos == PHYSFS_INVALID_SET_FILE_POINTER) &&
+ (GetLastError() != NO_ERROR) )
+ {
+ BAIL_MACRO(winApiStrError(), 0);
+ } /* if */
+ else
+ {
+ /* Combine the high/low order to create the 64-bit position value */
+ retval = (((PHYSFS_uint64) HighPos) << 32) | LowPos;
+ assert(retval >= 0);
+ } /* else */
+
+ return(retval);
+} /* __PHYSFS_platformTell */
+
+
+PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
+{
+ HANDLE Handle = ((WinApiFile *) opaque)->handle;
+ DWORD SizeHigh;
+ DWORD SizeLow;
+ PHYSFS_sint64 retval;
+
+ SizeLow = GetFileSize(Handle, &SizeHigh);
+ if ( (SizeLow == PHYSFS_INVALID_SET_FILE_POINTER) &&
+ (GetLastError() != NO_ERROR) )
+ {
+ BAIL_MACRO(winApiStrError(), -1);
+ } /* if */
+ else
+ {
+ /* Combine the high/low order to create the 64-bit position value */
+ retval = (((PHYSFS_uint64) SizeHigh) << 32) | SizeLow;
+ assert(retval >= 0);
+ } /* else */
+
+ return(retval);
+} /* __PHYSFS_platformFileLength */
+
+
+int __PHYSFS_platformEOF(void *opaque)
+{
+ PHYSFS_sint64 FilePosition;
+ int retval = 0;
+
+ /* Get the current position in the file */
+ if ((FilePosition = __PHYSFS_platformTell(opaque)) != 0)
+ {
+ /* Non-zero if EOF is equal to the file length */
+ retval = FilePosition == __PHYSFS_platformFileLength(opaque);
+ } /* if */
+
+ return(retval);
+} /* __PHYSFS_platformEOF */
+
+
+int __PHYSFS_platformFlush(void *opaque)
+{
+ WinApiFile *fh = ((WinApiFile *) opaque);
+ if (!fh->readonly)
+ BAIL_IF_MACRO(!FlushFileBuffers(fh->handle), winApiStrError(), 0);
+
+ return(1);
+} /* __PHYSFS_platformFlush */
+
+
+int __PHYSFS_platformClose(void *opaque)
+{
+ HANDLE Handle = ((WinApiFile *) opaque)->handle;
+ BAIL_IF_MACRO(!CloseHandle(Handle), winApiStrError(), 0);
+ allocator.Free(opaque);
+ return(1);
+} /* __PHYSFS_platformClose */
+
+
+static int doPlatformDelete(LPWSTR wpath)
+{
+ /* If filename is a folder */
+ if (pGetFileAttributesW(wpath) == FILE_ATTRIBUTE_DIRECTORY)
+ {
+ BAIL_IF_MACRO(!pRemoveDirectoryW(wpath), winApiStrError(), 0);
+ } /* if */
+ else
+ {
+ BAIL_IF_MACRO(!pDeleteFileW(wpath), winApiStrError(), 0);
+ } /* else */
+
+ return(1); /* if you made it here, it worked. */
+} /* doPlatformDelete */
+
+
+int __PHYSFS_platformDelete(const char *path)
+{
+ int retval = 0;
+ LPWSTR wpath;
+ UTF8_TO_UNICODE_STACK_MACRO(wpath, path);
+ BAIL_IF_MACRO(wpath == NULL, ERR_OUT_OF_MEMORY, 0);
+ retval = doPlatformDelete(wpath);
+ __PHYSFS_smallFree(wpath);
+ return(retval);
+} /* __PHYSFS_platformDelete */
+
+
+/*
+ * !!! FIXME: why aren't we using Critical Sections instead of Mutexes?
+ * !!! FIXME: mutexes on Windows are for cross-process sync. CritSects are
+ * !!! FIXME: mutexes for threads in a single process and are faster.
+ */
+void *__PHYSFS_platformCreateMutex(void)
+{
+ return((void *) CreateMutex(NULL, FALSE, NULL));
+} /* __PHYSFS_platformCreateMutex */
+
+
+void __PHYSFS_platformDestroyMutex(void *mutex)
+{
+ CloseHandle((HANDLE) mutex);
+} /* __PHYSFS_platformDestroyMutex */
+
+
+int __PHYSFS_platformGrabMutex(void *mutex)
+{
+ return(WaitForSingleObject((HANDLE) mutex, INFINITE) != WAIT_FAILED);
+} /* __PHYSFS_platformGrabMutex */
+
+
+void __PHYSFS_platformReleaseMutex(void *mutex)
+{
+ ReleaseMutex((HANDLE) mutex);
+} /* __PHYSFS_platformReleaseMutex */
+
+
+static PHYSFS_sint64 FileTimeToPhysfsTime(const FILETIME *ft)
+{
+ SYSTEMTIME st_utc;
+ SYSTEMTIME st_localtz;
+ TIME_ZONE_INFORMATION tzi;
+ DWORD tzid;
+ PHYSFS_sint64 retval;
+ struct tm tm;
+
+ BAIL_IF_MACRO(!FileTimeToSystemTime(ft, &st_utc), winApiStrError(), -1);
+ tzid = GetTimeZoneInformation(&tzi);
+ BAIL_IF_MACRO(tzid == TIME_ZONE_ID_INVALID, winApiStrError(), -1);
+
+ /* (This API is unsupported and fails on non-NT systems. */
+ if (!SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_localtz))
+ {
+ /* do it by hand. Grumble... */
+ ULARGE_INTEGER ui64;
+ FILETIME new_ft;
+ ui64.LowPart = ft->dwLowDateTime;
+ ui64.HighPart = ft->dwHighDateTime;
+
+ if (tzid == TIME_ZONE_ID_STANDARD)
+ tzi.Bias += tzi.StandardBias;
+ else if (tzid == TIME_ZONE_ID_DAYLIGHT)
+ tzi.Bias += tzi.DaylightBias;
+
+ /* convert from minutes to 100-nanosecond increments... */
+ ui64.QuadPart -= (((LONGLONG) tzi.Bias) * (600000000));
+
+ /* Move it back into a FILETIME structure... */
+ new_ft.dwLowDateTime = ui64.LowPart;
+ new_ft.dwHighDateTime = ui64.HighPart;
+
+ /* Convert to something human-readable... */
+ if (!FileTimeToSystemTime(&new_ft, &st_localtz))
+ BAIL_MACRO(winApiStrError(), -1);
+ } /* if */
+
+ /* Convert to a format that mktime() can grok... */
+ tm.tm_sec = st_localtz.wSecond;
+ tm.tm_min = st_localtz.wMinute;
+ tm.tm_hour = st_localtz.wHour;
+ tm.tm_mday = st_localtz.wDay;
+ tm.tm_mon = st_localtz.wMonth - 1;
+ tm.tm_year = st_localtz.wYear - 1900;
+ tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
+ tm.tm_yday = -1;
+ tm.tm_isdst = -1;
+
+ /* Convert to a format PhysicsFS can grok... */
+ retval = (PHYSFS_sint64) mktime(&tm);
+ BAIL_IF_MACRO(retval == -1, strerror(errno), -1);
+ return(retval);
+} /* FileTimeToPhysfsTime */
+
+
+PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
+{
+ PHYSFS_sint64 retval = -1;
+ WIN32_FILE_ATTRIBUTE_DATA attr;
+ int rc = 0;
+
+ memset(&attr, '\0', sizeof (attr));
+
+ /* GetFileAttributesEx didn't show up until Win98 and NT4. */
+ if ((pGetFileAttributesExW != NULL) || (pGetFileAttributesExA != NULL))
+ {
+ WCHAR *wstr;
+ UTF8_TO_UNICODE_STACK_MACRO(wstr, fname);
+ if (wstr != NULL) /* if NULL, maybe the fallback will work. */
+ {
+ if (pGetFileAttributesExW != NULL) /* NT/XP/Vista/etc system. */
+ rc = pGetFileAttributesExW(wstr, GetFileExInfoStandard, &attr);
+ else /* Win98/ME system */
+ {
+ const int len = (int) (wStrLen(wstr) + 1);
+ char *cp = (char *) __PHYSFS_smallAlloc(len);
+ if (cp != NULL)
+ {
+ WideCharToMultiByte(CP_ACP, 0, wstr, len, cp, len, 0, 0);
+ rc = pGetFileAttributesExA(cp, GetFileExInfoStandard, &attr);
+ __PHYSFS_smallFree(cp);
+ } /* if */
+ } /* else */
+ __PHYSFS_smallFree(wstr);
+ } /* if */
+ } /* if */
+
+ if (rc) /* had API entry point and it worked. */
+ {
+ /* 0 return value indicates an error or not supported */
+ if ( (attr.ftLastWriteTime.dwHighDateTime != 0) ||
+ (attr.ftLastWriteTime.dwLowDateTime != 0) )
+ {
+ retval = FileTimeToPhysfsTime(&attr.ftLastWriteTime);
+ } /* if */
+ } /* if */
+
+ /* GetFileTime() has been in the Win32 API since the start. */
+ if (retval == -1) /* try a fallback... */
+ {
+ FILETIME ft;
+ BOOL rc;
+ const char *err;
+ WinApiFile *f = (WinApiFile *) __PHYSFS_platformOpenRead(fname);
+ BAIL_IF_MACRO(f == NULL, NULL, -1)
+ rc = GetFileTime(f->handle, NULL, NULL, &ft);
+ err = winApiStrError();
+ CloseHandle(f->handle);
+ allocator.Free(f);
+ BAIL_IF_MACRO(!rc, err, -1);
+ retval = FileTimeToPhysfsTime(&ft);
+ } /* if */
+
+ return(retval);
+} /* __PHYSFS_platformGetLastModTime */
+
+
+/* !!! FIXME: Don't use C runtime for allocators? */
+int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
+{
+ return(0); /* just use malloc() and friends. */
+} /* __PHYSFS_platformSetDefaultAllocator */
+
+#endif /* PHYSFS_PLATFORM_WINDOWS */
+
+/* end of windows.c ... */
+
+
--- /dev/null
+/**
+ * Test program for PhysicsFS. May only work on Unix.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#if (defined __MWERKS__)
+#include <SIOUX.h>
+#endif
+
+#if (defined PHYSFS_HAVE_READLINE)
+#include <unistd.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#include <time.h>
+
+#include "physfs.h"
+
+#define TEST_VERSION_MAJOR 1
+#define TEST_VERSION_MINOR 1
+#define TEST_VERSION_PATCH 1
+
+static FILE *history_file = NULL;
+static PHYSFS_uint32 do_buffer_size = 0;
+
+static void output_versions(void)
+{
+ PHYSFS_Version compiled;
+ PHYSFS_Version linked;
+
+ PHYSFS_VERSION(&compiled);
+ PHYSFS_getLinkedVersion(&linked);
+
+ printf("test_physfs version %d.%d.%d.\n"
+ " Compiled against PhysicsFS version %d.%d.%d,\n"
+ " and linked against %d.%d.%d.\n\n",
+ TEST_VERSION_MAJOR, TEST_VERSION_MINOR, TEST_VERSION_PATCH,
+ (int) compiled.major, (int) compiled.minor, (int) compiled.patch,
+ (int) linked.major, (int) linked.minor, (int) linked.patch);
+} /* output_versions */
+
+
+static void output_archivers(void)
+{
+ const PHYSFS_ArchiveInfo **rc = PHYSFS_supportedArchiveTypes();
+ const PHYSFS_ArchiveInfo **i;
+
+ printf("Supported archive types:\n");
+ if (*rc == NULL)
+ printf(" * Apparently, NONE!\n");
+ else
+ {
+ for (i = rc; *i != NULL; i++)
+ {
+ printf(" * %s: %s\n Written by %s.\n %s\n",
+ (*i)->extension, (*i)->description,
+ (*i)->author, (*i)->url);
+ } /* for */
+ } /* else */
+
+ printf("\n");
+} /* output_archivers */
+
+
+static int cmd_quit(char *args)
+{
+ return(0);
+} /* cmd_quit */
+
+
+static int cmd_init(char *args)
+{
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ if (PHYSFS_init(args))
+ printf("Successful.\n");
+ else
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+
+ return(1);
+} /* cmd_init */
+
+
+static int cmd_deinit(char *args)
+{
+ if (PHYSFS_deinit())
+ printf("Successful.\n");
+ else
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+
+ return(1);
+} /* cmd_deinit */
+
+
+static int cmd_addarchive(char *args)
+{
+ char *ptr = strrchr(args, ' ');
+ int appending = atoi(ptr + 1);
+ *ptr = '\0';
+
+ if (*args == '\"')
+ {
+ args++;
+ *(ptr - 1) = '\0';
+ } /* if */
+
+ /*printf("[%s], [%d]\n", args, appending);*/
+
+ if (PHYSFS_addToSearchPath(args, appending))
+ printf("Successful.\n");
+ else
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+
+ return(1);
+} /* cmd_addarchive */
+
+
+static int cmd_mount(char *args)
+{
+ char *ptr;
+ char *mntpoint = NULL;
+ int appending = 0;
+
+ if (*args == '\"')
+ {
+ args++;
+ ptr = strchr(args, '\"');
+ if (ptr == NULL)
+ {
+ printf("missing string terminator in argument.\n");
+ return(1);
+ } /* if */
+ *(ptr) = '\0';
+ } /* if */
+ else
+ {
+ ptr = strchr(args, ' ');
+ *ptr = '\0';
+ } /* else */
+
+ mntpoint = ptr + 1;
+ if (*mntpoint == '\"')
+ {
+ mntpoint++;
+ ptr = strchr(mntpoint, '\"');
+ if (ptr == NULL)
+ {
+ printf("missing string terminator in argument.\n");
+ return(1);
+ } /* if */
+ *(ptr) = '\0';
+ } /* if */
+ else
+ {
+ ptr = strchr(mntpoint, ' ');
+ *(ptr) = '\0';
+ } /* else */
+ appending = atoi(ptr + 1);
+
+ /*printf("[%s], [%s], [%d]\n", args, mntpoint, appending);*/
+
+ if (PHYSFS_mount(args, mntpoint, appending))
+ printf("Successful.\n");
+ else
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+
+ return(1);
+} /* cmd_mount */
+
+
+static int cmd_removearchive(char *args)
+{
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ if (PHYSFS_removeFromSearchPath(args))
+ printf("Successful.\n");
+ else
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+
+ return(1);
+} /* cmd_removearchive */
+
+
+static int cmd_enumerate(char *args)
+{
+ char **rc;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ rc = PHYSFS_enumerateFiles(args);
+
+ if (rc == NULL)
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+ else
+ {
+ int file_count;
+ char **i;
+ for (i = rc, file_count = 0; *i != NULL; i++, file_count++)
+ printf("%s\n", *i);
+
+ printf("\n total (%d) files.\n", file_count);
+ PHYSFS_freeList(rc);
+ } /* else */
+
+ return(1);
+} /* cmd_enumerate */
+
+
+static int cmd_getdirsep(char *args)
+{
+ printf("Directory separator is [%s].\n", PHYSFS_getDirSeparator());
+ return(1);
+} /* cmd_getdirsep */
+
+
+static int cmd_getlasterror(char *args)
+{
+ printf("last error is [%s].\n", PHYSFS_getLastError());
+ return(1);
+} /* cmd_getlasterror */
+
+
+static int cmd_getcdromdirs(char *args)
+{
+ char **rc = PHYSFS_getCdRomDirs();
+
+ if (rc == NULL)
+ printf("Failure. Reason: [%s].\n", PHYSFS_getLastError());
+ else
+ {
+ int dir_count;
+ char **i;
+ for (i = rc, dir_count = 0; *i != NULL; i++, dir_count++)
+ printf("%s\n", *i);
+
+ printf("\n total (%d) drives.\n", dir_count);
+ PHYSFS_freeList(rc);
+ } /* else */
+
+ return(1);
+} /* cmd_getcdromdirs */
+
+
+static int cmd_getsearchpath(char *args)
+{
+ char **rc = PHYSFS_getSearchPath();
+
+ if (rc == NULL)
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+ else
+ {
+ int dir_count;
+ char **i;
+ for (i = rc, dir_count = 0; *i != NULL; i++, dir_count++)
+ printf("%s\n", *i);
+
+ printf("\n total (%d) directories.\n", dir_count);
+ PHYSFS_freeList(rc);
+ } /* else */
+
+ return(1);
+} /* cmd_getcdromdirs */
+
+
+static int cmd_getbasedir(char *args)
+{
+ printf("Base dir is [%s].\n", PHYSFS_getBaseDir());
+ return(1);
+} /* cmd_getbasedir */
+
+
+static int cmd_getuserdir(char *args)
+{
+ printf("User dir is [%s].\n", PHYSFS_getUserDir());
+ return(1);
+} /* cmd_getuserdir */
+
+
+static int cmd_getwritedir(char *args)
+{
+ printf("Write dir is [%s].\n", PHYSFS_getWriteDir());
+ return(1);
+} /* cmd_getwritedir */
+
+
+static int cmd_setwritedir(char *args)
+{
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ if (PHYSFS_setWriteDir(args))
+ printf("Successful.\n");
+ else
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+
+ return(1);
+} /* cmd_setwritedir */
+
+
+static int cmd_permitsyms(char *args)
+{
+ int num;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ num = atoi(args);
+ PHYSFS_permitSymbolicLinks(num);
+ printf("Symlinks are now %s.\n", num ? "permitted" : "forbidden");
+ return(1);
+} /* cmd_permitsyms */
+
+
+static int cmd_setbuffer(char *args)
+{
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ do_buffer_size = (unsigned int) atoi(args);
+ if (do_buffer_size)
+ {
+ printf("Further tests will set a (%lu) size buffer.\n",
+ (unsigned long) do_buffer_size);
+ } /* if */
+
+ else
+ {
+ printf("Further tests will NOT use a buffer.\n");
+ } /* else */
+
+ return(1);
+} /* cmd_setbuffer */
+
+
+static int cmd_stressbuffer(char *args)
+{
+ int num;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ num = atoi(args);
+ if (num < 0)
+ printf("buffer must be greater than or equal to zero.\n");
+ else
+ {
+ PHYSFS_File *f;
+ int rndnum;
+
+ printf("Stress testing with (%d) byte buffer...\n", num);
+ f = PHYSFS_openWrite("test.txt");
+ if (f == NULL)
+ printf("Couldn't open test.txt for writing: %s.\n", PHYSFS_getLastError());
+ else
+ {
+ int i, j;
+ char buf[37];
+ char buf2[37];
+
+ if (!PHYSFS_setBuffer(f, num))
+ {
+ printf("PHYSFS_setBuffer() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ PHYSFS_delete("test.txt");
+ return(1);
+ } /* if */
+
+ strcpy(buf, "abcdefghijklmnopqrstuvwxyz0123456789");
+ srand((unsigned int) time(NULL));
+
+ for (i = 0; i < 10; i++)
+ {
+ for (j = 0; j < 10000; j++)
+ {
+ PHYSFS_uint32 right = 1 + (PHYSFS_uint32) (35.0 * rand() / (RAND_MAX + 1.0));
+ PHYSFS_uint32 left = 36 - right;
+ if (PHYSFS_write(f, buf, left, 1) != 1)
+ {
+ printf("PHYSFS_write() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+
+ rndnum = 1 + (int) (1000.0 * rand() / (RAND_MAX + 1.0));
+ if (rndnum == 42)
+ {
+ if (!PHYSFS_flush(f))
+ {
+ printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+ } /* if */
+
+ if (PHYSFS_write(f, buf + left, 1, right) != right)
+ {
+ printf("PHYSFS_write() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+
+ rndnum = 1 + (int) (1000.0 * rand() / (RAND_MAX + 1.0));
+ if (rndnum == 42)
+ {
+ if (!PHYSFS_flush(f))
+ {
+ printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+ } /* if */
+ } /* for */
+
+ if (!PHYSFS_flush(f))
+ {
+ printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+
+ } /* for */
+
+ if (!PHYSFS_close(f))
+ {
+ printf("PHYSFS_close() failed: %s.\n", PHYSFS_getLastError());
+ return(1); /* oh well. */
+ } /* if */
+
+ printf(" ... test file written ...\n");
+ f = PHYSFS_openRead("test.txt");
+ if (f == NULL)
+ {
+ printf("Failed to reopen stress file for reading: %s.\n", PHYSFS_getLastError());
+ return(1);
+ } /* if */
+
+ if (!PHYSFS_setBuffer(f, num))
+ {
+ printf("PHYSFS_setBuffer() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+
+ for (i = 0; i < 10; i++)
+ {
+ for (j = 0; j < 10000; j++)
+ {
+ PHYSFS_uint32 right = 1 + (PHYSFS_uint32) (35.0 * rand() / (RAND_MAX + 1.0));
+ PHYSFS_uint32 left = 36 - right;
+ if (PHYSFS_read(f, buf2, left, 1) != 1)
+ {
+ printf("PHYSFS_read() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+
+ rndnum = 1 + (int) (1000.0 * rand() / (RAND_MAX + 1.0));
+ if (rndnum == 42)
+ {
+ if (!PHYSFS_flush(f))
+ {
+ printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+ } /* if */
+
+ if (PHYSFS_read(f, buf2 + left, 1, right) != right)
+ {
+ printf("PHYSFS_read() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+
+ rndnum = 1 + (int) (1000.0 * rand() / (RAND_MAX + 1.0));
+ if (rndnum == 42)
+ {
+ if (!PHYSFS_flush(f))
+ {
+ printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+ } /* if */
+
+ if (memcmp(buf, buf2, 36) != 0)
+ {
+ printf("readback is mismatched on iterations (%d, %d).\n", i, j);
+ printf("wanted: [");
+ for (i = 0; i < 36; i++)
+ printf("%c", buf[i]);
+ printf("]\n");
+
+ printf(" got: [");
+ for (i = 0; i < 36; i++)
+ printf("%c", buf2[i]);
+ printf("]\n");
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+ } /* for */
+
+ if (!PHYSFS_flush(f))
+ {
+ printf("PHYSFS_flush() failed: %s.\n", PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+
+ } /* for */
+
+ printf(" ... test file read ...\n");
+
+ if (!PHYSFS_eof(f))
+ printf("PHYSFS_eof() returned true! That's wrong.\n");
+
+ if (!PHYSFS_close(f))
+ {
+ printf("PHYSFS_close() failed: %s.\n", PHYSFS_getLastError());
+ return(1); /* oh well. */
+ } /* if */
+
+ PHYSFS_delete("test.txt");
+ printf("stress test completed successfully.\n");
+ } /* else */
+ } /* else */
+
+ return(1);
+} /* cmd_stressbuffer */
+
+
+static int cmd_setsaneconfig(char *args)
+{
+ char *org;
+ char *appName;
+ char *arcExt;
+ int inclCD;
+ int arcsFirst;
+ char *ptr = args;
+
+ /* ugly. */
+ org = ptr;
+ ptr = strchr(ptr, ' '); *ptr = '\0'; ptr++; appName = ptr;
+ ptr = strchr(ptr, ' '); *ptr = '\0'; ptr++; arcExt = ptr;
+ ptr = strchr(ptr, ' '); *ptr = '\0'; ptr++; inclCD = atoi(arcExt);
+ arcsFirst = atoi(ptr);
+
+ if (strcmp(arcExt, "!") == 0)
+ arcExt = NULL;
+
+ if (PHYSFS_setSaneConfig(org, appName, arcExt, inclCD, arcsFirst))
+ printf("Successful.\n");
+ else
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+
+ return(1);
+} /* cmd_setsaneconfig */
+
+
+static int cmd_mkdir(char *args)
+{
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ if (PHYSFS_mkdir(args))
+ printf("Successful.\n");
+ else
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+
+ return(1);
+} /* cmd_mkdir */
+
+
+static int cmd_delete(char *args)
+{
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ if (PHYSFS_delete(args))
+ printf("Successful.\n");
+ else
+ printf("Failure. reason: %s.\n", PHYSFS_getLastError());
+
+ return(1);
+} /* cmd_delete */
+
+
+static int cmd_getrealdir(char *args)
+{
+ const char *rc;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ rc = PHYSFS_getRealDir(args);
+ if (rc)
+ printf("Found at [%s].\n", rc);
+ else
+ printf("Not found.\n");
+
+ return(1);
+} /* cmd_getrealdir */
+
+
+static int cmd_exists(char *args)
+{
+ int rc;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ rc = PHYSFS_exists(args);
+ printf("File %sexists.\n", rc ? "" : "does not ");
+ return(1);
+} /* cmd_exists */
+
+
+static int cmd_isdir(char *args)
+{
+ int rc;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ rc = PHYSFS_isDirectory(args);
+ printf("File %s a directory.\n", rc ? "is" : "is NOT");
+ return(1);
+} /* cmd_isdir */
+
+
+static int cmd_issymlink(char *args)
+{
+ int rc;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ rc = PHYSFS_isSymbolicLink(args);
+ printf("File %s a symlink.\n", rc ? "is" : "is NOT");
+ return(1);
+} /* cmd_issymlink */
+
+
+static int cmd_cat(char *args)
+{
+ PHYSFS_File *f;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ f = PHYSFS_openRead(args);
+ if (f == NULL)
+ printf("failed to open. Reason: [%s].\n", PHYSFS_getLastError());
+ else
+ {
+ if (do_buffer_size)
+ {
+ if (!PHYSFS_setBuffer(f, do_buffer_size))
+ {
+ printf("failed to set file buffer. Reason: [%s].\n",
+ PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+ } /* if */
+
+ while (1)
+ {
+ char buffer[128];
+ PHYSFS_sint64 rc;
+ PHYSFS_sint64 i;
+ rc = PHYSFS_read(f, buffer, 1, sizeof (buffer));
+
+ for (i = 0; i < rc; i++)
+ fputc((int) buffer[i], stdout);
+
+ if (rc < sizeof (buffer))
+ {
+ printf("\n\n");
+ if (!PHYSFS_eof(f))
+ {
+ printf("\n (Error condition in reading. Reason: [%s])\n\n",
+ PHYSFS_getLastError());
+ } /* if */
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+ } /* while */
+ } /* else */
+
+ return(1);
+} /* cmd_cat */
+
+
+static int cmd_filelength(char *args)
+{
+ PHYSFS_File *f;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ f = PHYSFS_openRead(args);
+ if (f == NULL)
+ printf("failed to open. Reason: [%s].\n", PHYSFS_getLastError());
+ else
+ {
+ PHYSFS_sint64 len = PHYSFS_fileLength(f);
+ if (len == -1)
+ printf("failed to determine length. Reason: [%s].\n", PHYSFS_getLastError());
+ else
+ printf(" (cast to int) %d bytes.\n", (int) len);
+
+ PHYSFS_close(f);
+ } /* else */
+
+ return(1);
+} /* cmd_filelength */
+
+
+#define WRITESTR "The cat sat on the mat.\n\n"
+
+static int cmd_append(char *args)
+{
+ PHYSFS_File *f;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ f = PHYSFS_openAppend(args);
+ if (f == NULL)
+ printf("failed to open. Reason: [%s].\n", PHYSFS_getLastError());
+ else
+ {
+ size_t bw;
+ PHYSFS_sint64 rc;
+
+ if (do_buffer_size)
+ {
+ if (!PHYSFS_setBuffer(f, do_buffer_size))
+ {
+ printf("failed to set file buffer. Reason: [%s].\n",
+ PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+ } /* if */
+
+ bw = strlen(WRITESTR);
+ rc = PHYSFS_write(f, WRITESTR, 1, bw);
+ if (rc != bw)
+ {
+ printf("Wrote (%d) of (%d) bytes. Reason: [%s].\n",
+ (int) rc, (int) bw, PHYSFS_getLastError());
+ } /* if */
+ else
+ {
+ printf("Successful.\n");
+ } /* else */
+
+ PHYSFS_close(f);
+ } /* else */
+
+ return(1);
+} /* cmd_append */
+
+
+static int cmd_write(char *args)
+{
+ PHYSFS_File *f;
+
+ if (*args == '\"')
+ {
+ args++;
+ args[strlen(args) - 1] = '\0';
+ } /* if */
+
+ f = PHYSFS_openWrite(args);
+ if (f == NULL)
+ printf("failed to open. Reason: [%s].\n", PHYSFS_getLastError());
+ else
+ {
+ size_t bw;
+ PHYSFS_sint64 rc;
+
+ if (do_buffer_size)
+ {
+ if (!PHYSFS_setBuffer(f, do_buffer_size))
+ {
+ printf("failed to set file buffer. Reason: [%s].\n",
+ PHYSFS_getLastError());
+ PHYSFS_close(f);
+ return(1);
+ } /* if */
+ } /* if */
+
+ bw = strlen(WRITESTR);
+ rc = PHYSFS_write(f, WRITESTR, 1, bw);
+ if (rc != bw)
+ {
+ printf("Wrote (%d) of (%d) bytes. Reason: [%s].\n",
+ (int) rc, (int) bw, PHYSFS_getLastError());
+ } /* if */
+ else
+ {
+ printf("Successful.\n");
+ } /* else */
+
+ PHYSFS_close(f);
+ } /* else */
+
+ return(1);
+} /* cmd_write */
+
+
+static void modTimeToStr(PHYSFS_sint64 modtime, char *modstr, size_t strsize)
+{
+ time_t t = (time_t) modtime;
+ char *str = ctime(&t);
+ strncpy(modstr, str, strsize);
+ modstr[strsize-1] = '\0';
+} /* modTimeToStr */
+
+
+static int cmd_getlastmodtime(char *args)
+{
+ PHYSFS_sint64 rc = PHYSFS_getLastModTime(args);
+ if (rc == -1)
+ printf("Failed to determine. Reason: [%s].\n", PHYSFS_getLastError());
+ else
+ {
+ char modstr[64];
+ modTimeToStr(rc, modstr, sizeof (modstr));
+ printf("Last modified: %s (%ld).\n", modstr, (long) rc);
+ } /* else */
+
+ return(1);
+} /* cmd_getLastModTime */
+
+
+/* must have spaces trimmed prior to this call. */
+static int count_args(const char *str)
+{
+ int retval = 0;
+ int in_quotes = 0;
+
+ if (str != NULL)
+ {
+ for (; *str != '\0'; str++)
+ {
+ if (*str == '\"')
+ in_quotes = !in_quotes;
+ else if ((*str == ' ') && (!in_quotes))
+ retval++;
+ } /* for */
+ retval++;
+ } /* if */
+
+ return(retval);
+} /* count_args */
+
+
+static int cmd_help(char *args);
+
+typedef struct
+{
+ const char *cmd;
+ int (*func)(char *args);
+ int argcount;
+ const char *usage;
+} command_info;
+
+static const command_info commands[] =
+{
+ { "quit", cmd_quit, 0, NULL },
+ { "q", cmd_quit, 0, NULL },
+ { "help", cmd_help, 0, NULL },
+ { "init", cmd_init, 1, "<argv0>" },
+ { "deinit", cmd_deinit, 0, NULL },
+ { "addarchive", cmd_addarchive, 2, "<archiveLocation> <append>" },
+ { "mount", cmd_mount, 3, "<archiveLocation> <mntpoint> <append>" },
+ { "removearchive", cmd_removearchive, 1, "<archiveLocation>" },
+ { "enumerate", cmd_enumerate, 1, "<dirToEnumerate>" },
+ { "ls", cmd_enumerate, 1, "<dirToEnumerate>" },
+ { "getlasterror", cmd_getlasterror, 0, NULL },
+ { "getdirsep", cmd_getdirsep, 0, NULL },
+ { "getcdromdirs", cmd_getcdromdirs, 0, NULL },
+ { "getsearchpath", cmd_getsearchpath, 0, NULL },
+ { "getbasedir", cmd_getbasedir, 0, NULL },
+ { "getuserdir", cmd_getuserdir, 0, NULL },
+ { "getwritedir", cmd_getwritedir, 0, NULL },
+ { "setwritedir", cmd_setwritedir, 1, "<newWriteDir>" },
+ { "permitsymlinks", cmd_permitsyms, 1, "<1or0>" },
+ { "setsaneconfig", cmd_setsaneconfig, 5, "<org> <appName> <arcExt> <includeCdRoms> <archivesFirst>" },
+ { "mkdir", cmd_mkdir, 1, "<dirToMk>" },
+ { "delete", cmd_delete, 1, "<dirToDelete>" },
+ { "getrealdir", cmd_getrealdir, 1, "<fileToFind>" },
+ { "exists", cmd_exists, 1, "<fileToCheck>" },
+ { "isdir", cmd_isdir, 1, "<fileToCheck>" },
+ { "issymlink", cmd_issymlink, 1, "<fileToCheck>" },
+ { "cat", cmd_cat, 1, "<fileToCat>" },
+ { "filelength", cmd_filelength, 1, "<fileToCheck>" },
+ { "append", cmd_append, 1, "<fileToAppend>" },
+ { "write", cmd_write, 1, "<fileToCreateOrTrash>" },
+ { "getlastmodtime", cmd_getlastmodtime, 1, "<fileToExamine>" },
+ { "setbuffer", cmd_setbuffer, 1, "<bufferSize>" },
+ { "stressbuffer", cmd_stressbuffer, 1, "<bufferSize>" },
+ { NULL, NULL, -1, NULL }
+};
+
+
+static void output_usage(const char *intro, const command_info *cmdinfo)
+{
+ if (cmdinfo->argcount == 0)
+ printf("%s \"%s\" (no arguments)\n", intro, cmdinfo->cmd);
+ else
+ printf("%s \"%s %s\"\n", intro, cmdinfo->cmd, cmdinfo->usage);
+} /* output_usage */
+
+
+static int cmd_help(char *args)
+{
+ const command_info *i;
+
+ printf("Commands:\n");
+ for (i = commands; i->cmd != NULL; i++)
+ output_usage(" -", i);
+
+ return(1);
+} /* output_cmd_help */
+
+
+static void trim_command(const char *orig, char *copy)
+{
+ const char *i;
+ char *writeptr = copy;
+ int spacecount = 0;
+ int have_first = 0;
+
+ for (i = orig; *i != '\0'; i++)
+ {
+ if (*i == ' ')
+ {
+ if ((*(i + 1) != ' ') && (*(i + 1) != '\0'))
+ {
+ if ((have_first) && (!spacecount))
+ {
+ spacecount++;
+ *writeptr = ' ';
+ writeptr++;
+ } /* if */
+ } /* if */
+ } /* if */
+ else
+ {
+ have_first = 1;
+ spacecount = 0;
+ *writeptr = *i;
+ writeptr++;
+ } /* else */
+ } /* for */
+
+ *writeptr = '\0';
+
+ /*
+ printf("\n command is [%s].\n", copy);
+ */
+} /* trim_command */
+
+
+static int process_command(char *complete_cmd)
+{
+ const command_info *i;
+ char *cmd_copy;
+ char *args;
+ int rc = 1;
+
+ if (complete_cmd == NULL) /* can happen if user hits CTRL-D, etc. */
+ {
+ printf("\n");
+ return(0);
+ } /* if */
+
+ cmd_copy = (char *) malloc(strlen(complete_cmd) + 1);
+ if (cmd_copy == NULL)
+ {
+ printf("\n\n\nOUT OF MEMORY!\n\n\n");
+ return(0);
+ } /* if */
+
+ trim_command(complete_cmd, cmd_copy);
+ args = strchr(cmd_copy, ' ');
+ if (args != NULL)
+ {
+ *args = '\0';
+ args++;
+ } /* else */
+
+ if (cmd_copy[0] != '\0')
+ {
+ for (i = commands; i->cmd != NULL; i++)
+ {
+ if (strcmp(i->cmd, cmd_copy) == 0)
+ {
+ if ((i->argcount >= 0) && (count_args(args) != i->argcount))
+ output_usage("usage:", i);
+ else
+ rc = i->func(args);
+ break;
+ } /* if */
+ } /* for */
+
+ if (i->cmd == NULL)
+ printf("Unknown command. Enter \"help\" for instructions.\n");
+
+#if (defined PHYSFS_HAVE_READLINE)
+ add_history(complete_cmd);
+ if (history_file)
+ {
+ fprintf(history_file, "%s\n", complete_cmd);
+ fflush(history_file);
+ } /* if */
+#endif
+
+ } /* if */
+
+ free(cmd_copy);
+ return(rc);
+} /* process_command */
+
+
+static void open_history_file(void)
+{
+#if (defined PHYSFS_HAVE_READLINE)
+#if 0
+ const char *envr = getenv("TESTPHYSFS_HISTORY");
+ if (!envr)
+ return;
+#else
+ char envr[256];
+ strcpy(envr, PHYSFS_getUserDir());
+ strcat(envr, ".testphys_history");
+#endif
+
+ if (access(envr, F_OK) == 0)
+ {
+ char buf[512];
+ FILE *f = fopen(envr, "r");
+ if (!f)
+ {
+ printf("\n\n"
+ "Could not open history file [%s] for reading!\n"
+ " Will not have past history available.\n\n",
+ envr);
+ return;
+ } /* if */
+
+ do
+ {
+ fgets(buf, sizeof (buf), f);
+ if (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+ add_history(buf);
+ } while (!feof(f));
+
+ fclose(f);
+ } /* if */
+
+ history_file = fopen(envr, "ab");
+ if (!history_file)
+ {
+ printf("\n\n"
+ "Could not open history file [%s] for appending!\n"
+ " Will not be able to record this session's history.\n\n",
+ envr);
+ } /* if */
+#endif
+} /* open_history_file */
+
+
+int main(int argc, char **argv)
+{
+ char *buf = NULL;
+ int rc = 0;
+
+#if (defined __MWERKS__)
+ extern tSIOUXSettings SIOUXSettings;
+ SIOUXSettings.asktosaveonclose = 0;
+ SIOUXSettings.autocloseonquit = 1;
+ SIOUXSettings.rows = 40;
+ SIOUXSettings.columns = 120;
+#endif
+
+ printf("\n");
+
+ if (!PHYSFS_init(argv[0]))
+ {
+ printf("PHYSFS_init() failed!\n reason: %s.\n", PHYSFS_getLastError());
+ return(1);
+ } /* if */
+
+ output_versions();
+ output_archivers();
+
+ open_history_file();
+
+ printf("Enter commands. Enter \"help\" for instructions.\n");
+
+ do
+ {
+#if (defined PHYSFS_HAVE_READLINE)
+ buf = readline("> ");
+#else
+ int i;
+ buf = (char *) malloc(512);
+ memset(buf, '\0', 512);
+ printf("> ");
+ for (i = 0; i < 511; i++)
+ {
+ int ch = fgetc(stdin);
+ if (ch == EOF)
+ {
+ strcpy(buf, "quit");
+ break;
+ } /* if */
+ else if ((ch == '\n') || (ch == '\r'))
+ {
+ buf[i] = '\0';
+ break;
+ } /* else if */
+ else if (ch == '\b')
+ {
+ if (i > 0)
+ i--;
+ } /* else if */
+ else
+ {
+ buf[i] = (char) ch;
+ } /* else */
+ } /* for */
+#endif
+
+ rc = process_command(buf);
+ if (buf != NULL)
+ free(buf);
+ } while (rc);
+
+ if (!PHYSFS_deinit())
+ printf("PHYSFS_deinit() failed!\n reason: %s.\n", PHYSFS_getLastError());
+
+ if (history_file)
+ fclose(history_file);
+
+/*
+ printf("\n\ntest_physfs written by ryan c. gordon.\n");
+ printf(" it makes you shoot teh railgun bettar.\n");
+*/
+
+ return(0);
+} /* main */
+
+/* end of test_physfs.c ... */
+
--- /dev/null
+/**
+ * Test program for PhysicsFS, using wxWidgets. May only work on Unix.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#if ( (defined(__MACH__)) && (defined(__APPLE__)) )
+#define PLATFORM_MACOSX 1
+#include <Carbon/Carbon.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include <wx/wx.h>
+#include <wx/treectrl.h>
+
+#include "physfs.h"
+
+#define TEST_VER_MAJOR 1
+#define TEST_VER_MINOR 1
+#define TEST_VER_PATCH 1
+
+//static PHYSFS_uint32 do_buffer_size = 0;
+
+enum WxTestPhysfsMenuCommands
+{
+ // start with standard menu items, since using the wxIDs will map them
+ // to sane things in the platform's UI (gnome icons in GTK+, moves the
+ // about and quit items to the Apple menu on Mac OS X, etc).
+ MENUCMD_About = wxID_ABOUT,
+ MENUCMD_Quit = wxID_EXIT,
+
+ // non-standard menu items go here.
+ MENUCMD_Init = wxID_HIGHEST,
+ MENUCMD_Deinit,
+ MENUCMD_AddArchive,
+ MENUCMD_Mount,
+ MENUCMD_Remove,
+ MENUCMD_GetCDs,
+ MENUCMD_SetWriteDir,
+ MENUCMD_PermitSymLinks,
+ MENUCMD_SetSaneConfig,
+ MENUCMD_MkDir,
+ MENUCMD_Delete,
+ MENUCMD_Cat,
+ MENUCMD_SetBuffer,
+ MENUCMD_StressBuffer,
+ MENUCMD_Append,
+ MENUCMD_Write,
+ MENUCMD_GetLastError,
+
+/*
+ { "getdirsep", cmd_getdirsep, 0, NULL },
+ { "getsearchpath", cmd_getsearchpath, 0, NULL },
+ { "getbasedir", cmd_getbasedir, 0, NULL },
+ { "getuserdir", cmd_getuserdir, 0, NULL },
+ { "getwritedir", cmd_getwritedir, 0, NULL },
+ { "getrealdir", cmd_getrealdir, 1, "<fileToFind>" },
+ { "exists", cmd_exists, 1, "<fileToCheck>" },
+ { "isdir", cmd_isdir, 1, "<fileToCheck>" },
+ { "issymlink", cmd_issymlink, 1, "<fileToCheck>" },
+ { "filelength", cmd_filelength, 1, "<fileToCheck>" },
+ { "getlastmodtime", cmd_getlastmodtime, 1, "<fileToExamine>" },
+*/
+};
+
+
+class WxTestPhysfsFrame : public wxFrame
+{
+public:
+ WxTestPhysfsFrame(const wxChar *argv0);
+
+ void rebuildTree();
+
+ void onMenuInit(wxCommandEvent &evt);
+ void onMenuDeinit(wxCommandEvent &evt);
+ void onMenuAddArchive(wxCommandEvent &evt);
+ void onMenuGetCDs(wxCommandEvent &evt);
+ void onMenuPermitSymLinks(wxCommandEvent &evt);
+
+private:
+ wxTreeCtrl *fileTree;
+ wxTreeItemId stateItem;
+ wxTreeItemId fsItem;
+
+ int err(int success);
+ void fillFileSystemTree(const char *path, const wxTreeItemId &item);
+ void doInit(const char *argv0);
+ void doDeinit();
+
+ DECLARE_EVENT_TABLE()
+};
+
+BEGIN_EVENT_TABLE(WxTestPhysfsFrame, wxFrame)
+ EVT_MENU(MENUCMD_Init, WxTestPhysfsFrame::onMenuInit)
+ EVT_MENU(MENUCMD_Deinit, WxTestPhysfsFrame::onMenuDeinit)
+ EVT_MENU(MENUCMD_AddArchive, WxTestPhysfsFrame::onMenuAddArchive)
+ EVT_MENU(MENUCMD_GetCDs, WxTestPhysfsFrame::onMenuGetCDs)
+ EVT_MENU(MENUCMD_PermitSymLinks, WxTestPhysfsFrame::onMenuPermitSymLinks)
+END_EVENT_TABLE()
+
+
+
+// This is the the Application itself.
+class WxTestPhysfsApp : public wxApp
+{
+public:
+ WxTestPhysfsApp() : mainWindow(NULL) { /* no-op. */ }
+ virtual bool OnInit();
+
+private:
+ WxTestPhysfsFrame *mainWindow;
+};
+
+DECLARE_APP(WxTestPhysfsApp)
+
+
+static inline char *newstr(const char *str)
+{
+ char *retval = NULL;
+ if (str != NULL)
+ {
+ retval = new char[strlen(str) + 1];
+ strcpy(retval, str);
+ } // if
+ return retval;
+} // newstr
+
+static char *newutf8(const wxString &wxstr)
+{
+ #if wxUSE_UNICODE
+ size_t len = wxstr.Len() + 1;
+ char *utf8text = new char[len * 6];
+ wxConvUTF8.WC2MB(utf8text, wxstr, len);
+ return utf8text;
+ #else
+ return newstr(wxstr);
+ #endif
+} // newutf8
+
+
+WxTestPhysfsFrame::WxTestPhysfsFrame(const wxChar *argv0)
+ : wxFrame(NULL, -1, wxT("WxTestPhysfs"))
+{
+ this->CreateStatusBar();
+
+ wxMenuBar *menuBar = new wxMenuBar;
+
+ wxMenu *stuffMenu = new wxMenu;
+ stuffMenu->Append(MENUCMD_Init, wxT("&Init"));
+ stuffMenu->Append(MENUCMD_Deinit, wxT("&Deinit"));
+ stuffMenu->Append(MENUCMD_AddArchive, wxT("&Add Archive"));
+ stuffMenu->Append(MENUCMD_Mount, wxT("&Mount Archive"));
+ stuffMenu->Append(MENUCMD_Remove, wxT("&Remove Archive"));
+ stuffMenu->Append(MENUCMD_GetCDs, wxT("&Get CD-ROM drives"));
+ stuffMenu->Append(MENUCMD_SetWriteDir, wxT("&Set Write Dir"));
+ stuffMenu->Append(MENUCMD_SetSaneConfig, wxT("Set Sane &Config"));
+ stuffMenu->Append(MENUCMD_MkDir, wxT("M&kDir"));
+ stuffMenu->Append(MENUCMD_Delete, wxT("D&elete"));
+ stuffMenu->Append(MENUCMD_Cat, wxT("&Cat"));
+ stuffMenu->Append(MENUCMD_SetBuffer, wxT("Set &Buffer"));
+ stuffMenu->Append(MENUCMD_StressBuffer, wxT("Stress &Test Buffer"));
+ stuffMenu->Append(MENUCMD_Append, wxT("&Append"));
+ stuffMenu->Append(MENUCMD_Write, wxT("&Write"));
+ stuffMenu->Append(MENUCMD_Write, wxT("&Update getLastError"));
+ stuffMenu->AppendCheckItem(MENUCMD_PermitSymLinks, wxT("&Permit symlinks"));
+ menuBar->Append(stuffMenu, wxT("&Stuff"));
+
+ //wxMenu *helpMenu = new wxMenu;
+ //helpMenu->Append(MENUCMD_About, wxT("&About\tF1"));
+ //menuBar->Append(helpMenu, wxT("&Help"));
+
+ this->SetMenuBar(menuBar);
+
+ this->fileTree = new wxTreeCtrl(this, -1);
+
+ // The sizer just makes sure that fileTree owns whole client area.
+ wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
+ sizer->Add(this->fileTree, 1, wxALL | wxEXPAND | wxALIGN_CENTRE);
+ sizer->SetItemMinSize(this->fileTree, 1, 1);
+ this->SetSizer(sizer);
+
+ char *utf8argv0 = newutf8(wxString(argv0));
+ this->doInit(utf8argv0);
+ delete[] utf8argv0;
+} // WxTestPhysfsFrame::WxTestPhysfsFrame
+
+
+int WxTestPhysfsFrame::err(int success)
+{
+ if (success)
+ this->SetStatusText(wxT(""));
+ else
+ this->SetStatusText(wxString(PHYSFS_getLastError(), wxConvUTF8));
+ return success;
+} // WxTestPhysfsFrame::err
+
+
+void WxTestPhysfsFrame::fillFileSystemTree(const char *path,
+ const wxTreeItemId &item)
+{
+ char **rc = PHYSFS_enumerateFiles(path);
+ char **i;
+ wxTreeItemId id;
+
+ if (rc == NULL)
+ {
+ const wxString quote(wxT("'"));
+ wxString str(wxT("Enumeration error: "));
+ str << quote << wxString(PHYSFS_getLastError(), wxConvUTF8) << quote;
+ id = this->fileTree->AppendItem(item, str);
+ this->fileTree->SetItemTextColour(id, wxColour(255, 0, 0));
+ } // if
+ else
+ {
+ for (i = rc; *i != NULL; i++)
+ {
+ id = this->fileTree->AppendItem(item, wxString(*i, wxConvUTF8));
+ const int len = strlen(path) + strlen(*i) + 2;
+ char *fname = new char[len];
+ const char *origdir = path;
+ if (strcmp(origdir, "/") == 0)
+ origdir = "";
+ snprintf(fname, len, "%s/%s", origdir, *i);
+
+ if (PHYSFS_isDirectory(fname))
+ {
+ this->fileTree->SetItemTextColour(id, wxColour(0, 0, 255));
+ this->fillFileSystemTree(fname, id);
+ } // if
+
+ else if (PHYSFS_isSymbolicLink(fname))
+ {
+ this->fileTree->SetItemTextColour(id, wxColour(0, 255, 0));
+ } // else if
+
+ else // ...file.
+ {
+ } // else
+
+ delete[] fname;
+ } // for
+
+ PHYSFS_freeList(rc);
+ } // else
+} // fillFileSystemTree
+
+
+void WxTestPhysfsFrame::rebuildTree()
+{
+ const wxString dot(wxT("."));
+ const wxString quote(wxT("'"));
+ wxTreeItemId item;
+ wxString str;
+ const char *cstr = NULL;
+ const bool wasInit = PHYSFS_isInit() ? true : false;
+
+ this->fileTree->DeleteAllItems();
+ wxTreeItemId root = this->fileTree->AddRoot(wxT("PhysicsFS"));
+ this->stateItem = this->fileTree->AppendItem(root, wxT("Library state"));
+
+ str = wxT("Initialized: ");
+ str << ((wasInit) ? wxT("true") : wxT("false"));
+ this->fileTree->AppendItem(this->stateItem, str);
+
+ this->fileTree->Expand(this->stateItem);
+ this->fileTree->Expand(root);
+
+ // Fill in version information...
+
+ PHYSFS_Version ver;
+ item = this->stateItem;
+ str = wxT("wxtest_physfs version: ");
+ str << TEST_VER_MAJOR << dot << TEST_VER_MINOR << dot << TEST_VER_PATCH;
+ this->fileTree->AppendItem(item, str);
+ PHYSFS_VERSION(&ver);
+ str = wxT("Compiled against PhysicsFS version: ");
+ str << (int) ver.major << dot << (int) ver.minor << dot << ver.patch;
+ this->fileTree->AppendItem(item, str);
+ PHYSFS_getLinkedVersion(&ver);
+ str = wxT("Linked against PhysicsFS version: ");
+ str << (int) ver.major << dot << (int) ver.minor << dot << ver.patch;
+ this->fileTree->AppendItem(item, str);
+
+ if (!wasInit)
+ return; // nothing else to do before initialization...
+
+ str = wxT("Symbolic links permitted: ");
+ str << ((PHYSFS_symbolicLinksPermitted()) ? wxT("true") : wxT("false"));
+ this->fileTree->AppendItem(this->stateItem, str);
+
+ str = wxT("Native directory separator: ");
+ str << quote << wxString(PHYSFS_getDirSeparator(), wxConvUTF8) << quote;
+ this->fileTree->AppendItem(this->stateItem, str);
+
+ // Fill in supported archives...
+
+ item = this->fileTree->AppendItem(this->stateItem, wxT("Archivers"));
+ const PHYSFS_ArchiveInfo **arcs = PHYSFS_supportedArchiveTypes();
+ if (*arcs == NULL)
+ this->fileTree->AppendItem(item, wxT("(none)"));
+ else
+ {
+ const PHYSFS_ArchiveInfo **i;
+ for (i = arcs; *i != NULL; i++)
+ {
+ const wxString ext((*i)->extension, wxConvUTF8);
+ const wxString desc((*i)->description, wxConvUTF8);
+ const wxString auth((*i)->author, wxConvUTF8);
+ const wxString url((*i)->url, wxConvUTF8);
+ wxTreeItemId arcitem = this->fileTree->AppendItem(item, ext);
+ this->fileTree->AppendItem(arcitem, desc);
+ this->fileTree->AppendItem(arcitem, auth);
+ this->fileTree->AppendItem(arcitem, url);
+ } // for
+ } // else
+
+
+ // Fill in the standard paths...
+
+ item = this->fileTree->AppendItem(this->stateItem, wxT("Paths"));
+ str = wxT("Base directory: ");
+ str << quote << wxString(PHYSFS_getBaseDir(), wxConvUTF8) << quote;
+ this->fileTree->AppendItem(item, str);
+ str = wxT("User directory: ");
+ str << quote << wxString(PHYSFS_getUserDir(), wxConvUTF8) << quote;
+ this->fileTree->AppendItem(item, str);
+ str = wxT("Write directory: ");
+ if ((cstr = PHYSFS_getWriteDir()) == NULL)
+ str << wxT("(NULL)");
+ else
+ str << quote << wxString(cstr ? cstr : "(NULL)", wxConvUTF8) << quote;
+ this->fileTree->AppendItem(item, str);
+ //str = wxT("Preference directory: ");
+ //str << wxString(PHYSFS_getUserDir(), wxConvUTF8);
+ //this->fileTree->AppendItem(item, str);
+
+ // Fill in the CD-ROMs...
+
+ item = this->fileTree->AppendItem(this->stateItem, wxT("CD-ROMs"));
+ char **cds = PHYSFS_getCdRomDirs();
+ if (cds == NULL)
+ {
+ str = wxT("Error: ");
+ str << quote << wxString(PHYSFS_getLastError(), wxConvUTF8) << quote;
+ wxTreeItemId id = this->fileTree->AppendItem(item, str);
+ this->fileTree->SetItemTextColour(id, wxColour(255, 0, 0));
+ } // if
+ else
+ {
+ if (*cds == NULL)
+ this->fileTree->AppendItem(item, wxT("(none)"));
+ else
+ {
+ char **i;
+ for (i = cds; *i != NULL; i++)
+ this->fileTree->AppendItem(item, wxString(*i, wxConvUTF8));
+ } // else
+ PHYSFS_freeList(cds);
+ } // else
+
+ // Fill in search path...
+
+ item = this->fileTree->AppendItem(this->stateItem, wxT("Search path"));
+ char **sp = PHYSFS_getSearchPath();
+ if (sp == NULL)
+ {
+ str = wxT("Error: ");
+ str << quote << wxString(PHYSFS_getLastError(), wxConvUTF8) << quote;
+ wxTreeItemId id = this->fileTree->AppendItem(item, str);
+ this->fileTree->SetItemTextColour(id, wxColour(255, 0, 0));
+ } // if
+ else
+ {
+ if (*sp == NULL)
+ this->fileTree->AppendItem(item, wxT("(none)"));
+ else
+ {
+ char **i;
+ for (i = sp; *i != NULL; i++)
+ this->fileTree->AppendItem(item, wxString(*i, wxConvUTF8));
+ } // else
+ PHYSFS_freeList(sp);
+ } // else
+
+ // Now fill in the filesystem...
+
+ this->fsItem = this->fileTree->AppendItem(root, wxT("Filesystem"));
+ this->fillFileSystemTree("/", this->fsItem);
+ this->fileTree->Expand(this->fsItem);
+} // WxTestPhysfsFrame::rebuildTree
+
+
+void WxTestPhysfsFrame::doInit(const char *argv0)
+{
+ if (!this->err(PHYSFS_init(argv0)))
+ ::wxMessageBox(wxT("PHYSFS_init() failed!"), wxT("wxTestPhysfs"));
+ this->rebuildTree();
+} // WxTestPhysfsFrame::doInit
+
+
+void WxTestPhysfsFrame::doDeinit()
+{
+ if (!this->err(PHYSFS_deinit()))
+ ::wxMessageBox(wxT("PHYSFS_deinit() failed!"), wxT("wxTestPhysfs"));
+ this->rebuildTree();
+} // WxTestPhysfsFrame::doDeinit
+
+
+void WxTestPhysfsFrame::onMenuInit(wxCommandEvent &evt)
+{
+ wxString argv0(wxGetApp().argv[0] == NULL ? wxT("") : wxGetApp().argv[0]);
+ wxString str(wxGetTextFromUser(wxT("PHYSFS_init"),
+ wxT("argv[0]? (cancel for NULL)"), argv0));
+ char *cstr = str.IsEmpty() ? NULL : newutf8(str);
+ this->doInit(cstr);
+ delete[] cstr;
+} // WxTestPhysfsFrame::onMenuInit
+
+
+void WxTestPhysfsFrame::onMenuDeinit(wxCommandEvent &evt)
+{
+ this->doDeinit();
+} // WxTestPhysfsFrame::onMenuDeinit
+
+
+void WxTestPhysfsFrame::onMenuAddArchive(wxCommandEvent &evt)
+{
+ wxString arc = wxFileSelector(wxT("Choose archive to add"));
+ if (!arc.IsEmpty())
+ {
+ char *cstr = newutf8(arc);
+ // !!! FIXME: add to start/end?
+ if (!this->err(PHYSFS_addToSearchPath(cstr, 1)))
+ ::wxMessageBox(wxT("PHYSFS_addToSearchPath() failed!"), wxT("wxTestPhysfs"));
+ delete[] cstr;
+ this->rebuildTree();
+ } // if
+} // WxTestPhysfsFrame::onMenuAddArchive
+
+
+void WxTestPhysfsFrame::onMenuGetCDs(wxCommandEvent &evt)
+{
+ this->rebuildTree(); // This will call PHYSFS_getCdRomDirs()...
+} // WxTestPhysfsFrame::onMenuGetCDs
+
+
+void WxTestPhysfsFrame::onMenuPermitSymLinks(wxCommandEvent &evt)
+{
+ PHYSFS_permitSymbolicLinks(evt.IsChecked() ? 1 : 0);
+ this->rebuildTree();
+} // WxTestPhysfsFrame::onMenuPermitSymLinks
+
+
+
+IMPLEMENT_APP(WxTestPhysfsApp)
+
+bool WxTestPhysfsApp::OnInit()
+{
+ #if PLATFORM_MACOSX
+ // This lets a stdio app become a GUI app. Otherwise, you won't get
+ // GUI events from the system and other things will fail to work.
+ // Putting the app in an application bundle does the same thing.
+ // TransformProcessType() is a 10.3+ API. SetFrontProcess() is 10.0+.
+ if (TransformProcessType != NULL) // check it as a weak symbol.
+ {
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ SetFrontProcess(&psn);
+ } // if
+ #endif
+
+ this->mainWindow = new WxTestPhysfsFrame(this->argv[0]);
+ this->mainWindow->Show(true);
+ SetTopWindow(this->mainWindow);
+ return true;
+} // WxTestPhysfsApp::OnInit
+
+// end of wxtest_physfs.cpp ...
+
--- /dev/null
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+#define BASE 65521UL /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+/* use NO_DIVIDE if your processor does not do division in hardware */
+#ifdef NO_DIVIDE
+# define MOD(a) \
+ do { \
+ if (a >= (BASE << 16)) a -= (BASE << 16); \
+ if (a >= (BASE << 15)) a -= (BASE << 15); \
+ if (a >= (BASE << 14)) a -= (BASE << 14); \
+ if (a >= (BASE << 13)) a -= (BASE << 13); \
+ if (a >= (BASE << 12)) a -= (BASE << 12); \
+ if (a >= (BASE << 11)) a -= (BASE << 11); \
+ if (a >= (BASE << 10)) a -= (BASE << 10); \
+ if (a >= (BASE << 9)) a -= (BASE << 9); \
+ if (a >= (BASE << 8)) a -= (BASE << 8); \
+ if (a >= (BASE << 7)) a -= (BASE << 7); \
+ if (a >= (BASE << 6)) a -= (BASE << 6); \
+ if (a >= (BASE << 5)) a -= (BASE << 5); \
+ if (a >= (BASE << 4)) a -= (BASE << 4); \
+ if (a >= (BASE << 3)) a -= (BASE << 3); \
+ if (a >= (BASE << 2)) a -= (BASE << 2); \
+ if (a >= (BASE << 1)) a -= (BASE << 1); \
+ if (a >= BASE) a -= BASE; \
+ } while (0)
+# define MOD4(a) \
+ do { \
+ if (a >= (BASE << 4)) a -= (BASE << 4); \
+ if (a >= (BASE << 3)) a -= (BASE << 3); \
+ if (a >= (BASE << 2)) a -= (BASE << 2); \
+ if (a >= (BASE << 1)) a -= (BASE << 1); \
+ if (a >= BASE) a -= BASE; \
+ } while (0)
+#else
+# define MOD(a) a %= BASE
+# define MOD4(a) a %= BASE
+#endif
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+ uLong adler;
+ const Bytef *buf;
+ uInt len;
+{
+ unsigned long sum2;
+ unsigned n;
+
+ /* split Adler-32 into component sums */
+ sum2 = (adler >> 16) & 0xffff;
+ adler &= 0xffff;
+
+ /* in case user likes doing a byte at a time, keep it fast */
+ if (len == 1) {
+ adler += buf[0];
+ if (adler >= BASE)
+ adler -= BASE;
+ sum2 += adler;
+ if (sum2 >= BASE)
+ sum2 -= BASE;
+ return adler | (sum2 << 16);
+ }
+
+ /* initial Adler-32 value (deferred check for len == 1 speed) */
+ if (buf == Z_NULL)
+ return 1L;
+
+ /* in case short lengths are provided, keep it somewhat fast */
+ if (len < 16) {
+ while (len--) {
+ adler += *buf++;
+ sum2 += adler;
+ }
+ if (adler >= BASE)
+ adler -= BASE;
+ MOD4(sum2); /* only added so many BASE's */
+ return adler | (sum2 << 16);
+ }
+
+ /* do length NMAX blocks -- requires just one modulo operation */
+ while (len >= NMAX) {
+ len -= NMAX;
+ n = NMAX / 16; /* NMAX is divisible by 16 */
+ do {
+ DO16(buf); /* 16 sums unrolled */
+ buf += 16;
+ } while (--n);
+ MOD(adler);
+ MOD(sum2);
+ }
+
+ /* do remaining bytes (less than NMAX, still just one modulo) */
+ if (len) { /* avoid modulos if none remaining */
+ while (len >= 16) {
+ len -= 16;
+ DO16(buf);
+ buf += 16;
+ }
+ while (len--) {
+ adler += *buf++;
+ sum2 += adler;
+ }
+ MOD(adler);
+ MOD(sum2);
+ }
+
+ /* return recombined sums */
+ return adler | (sum2 << 16);
+}
+
+/* ========================================================================= */
+uLong ZEXPORT adler32_combine(adler1, adler2, len2)
+ uLong adler1;
+ uLong adler2;
+ z_off_t len2;
+{
+ unsigned long sum1;
+ unsigned long sum2;
+ unsigned rem;
+
+ /* the derivation of this formula is left as an exercise for the reader */
+ rem = (unsigned)(len2 % BASE);
+ sum1 = adler1 & 0xffff;
+ sum2 = rem * sum1;
+ MOD(sum2);
+ sum1 += (adler2 & 0xffff) + BASE - 1;
+ sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem;
+ if (sum1 > BASE) sum1 -= BASE;
+ if (sum1 > BASE) sum1 -= BASE;
+ if (sum2 > (BASE << 1)) sum2 -= (BASE << 1);
+ if (sum2 > BASE) sum2 -= BASE;
+ return sum1 | (sum2 << 16);
+}
--- /dev/null
+/* compress.c -- compress a memory buffer
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+ int level;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+#ifdef MAXSEG_64K
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#endif
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+ stream.opaque = (voidpf)0;
+
+ err = deflateInit(&stream, level);
+ if (err != Z_OK) return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+/* ===========================================================================
+ */
+int ZEXPORT compress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
+}
+
+/* ===========================================================================
+ If the default memLevel or windowBits for deflateInit() is changed, then
+ this function needs to be updated.
+ */
+uLong ZEXPORT compressBound (sourceLen)
+ uLong sourceLen;
+{
+ return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11;
+}
--- /dev/null
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
+ * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
+ * tables for updating the shift register in one step with three exclusive-ors
+ * instead of four steps with four exclusive-ors. This results in about a
+ * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
+ */
+
+/* @(#) $Id$ */
+
+/*
+ Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore
+ protection on the static variables used to control the first-use generation
+ of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should
+ first call get_crc_table() to initialize the tables before allowing more than
+ one thread to use crc32().
+ */
+
+#ifdef MAKECRCH
+# include <stdio.h>
+# ifndef DYNAMIC_CRC_TABLE
+# define DYNAMIC_CRC_TABLE
+# endif /* !DYNAMIC_CRC_TABLE */
+#endif /* MAKECRCH */
+
+#include "zutil.h" /* for STDC and FAR definitions */
+
+#define local static
+
+/* Find a four-byte integer type for crc32_little() and crc32_big(). */
+#ifndef NOBYFOUR
+# ifdef STDC /* need ANSI C limits.h to determine sizes */
+# include <limits.h>
+# define BYFOUR
+# if (UINT_MAX == 0xffffffffUL)
+ typedef unsigned int u4;
+# else
+# if (ULONG_MAX == 0xffffffffUL)
+ typedef unsigned long u4;
+# else
+# if (USHRT_MAX == 0xffffffffUL)
+ typedef unsigned short u4;
+# else
+# undef BYFOUR /* can't find a four-byte integer type! */
+# endif
+# endif
+# endif
+# endif /* STDC */
+#endif /* !NOBYFOUR */
+
+/* Definitions for doing the crc four data bytes at a time. */
+#ifdef BYFOUR
+# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \
+ (((w)&0xff00)<<8)+(((w)&0xff)<<24))
+ local unsigned long crc32_little OF((unsigned long,
+ const unsigned char FAR *, unsigned));
+ local unsigned long crc32_big OF((unsigned long,
+ const unsigned char FAR *, unsigned));
+# define TBLS 8
+#else
+# define TBLS 1
+#endif /* BYFOUR */
+
+/* Local functions for crc concatenation */
+local unsigned long gf2_matrix_times OF((unsigned long *mat,
+ unsigned long vec));
+local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat));
+
+#ifdef DYNAMIC_CRC_TABLE
+
+local volatile int crc_table_empty = 1;
+local unsigned long FAR crc_table[TBLS][256];
+local void make_crc_table OF((void));
+#ifdef MAKECRCH
+ local void write_table OF((FILE *, const unsigned long FAR *));
+#endif /* MAKECRCH */
+/*
+ Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
+ x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+
+ Polynomials over GF(2) are represented in binary, one bit per coefficient,
+ with the lowest powers in the most significant bit. Then adding polynomials
+ is just exclusive-or, and multiplying a polynomial by x is a right shift by
+ one. If we call the above polynomial p, and represent a byte as the
+ polynomial q, also with the lowest power in the most significant bit (so the
+ byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+ where a mod b means the remainder after dividing a by b.
+
+ This calculation is done using the shift-register method of multiplying and
+ taking the remainder. The register is initialized to zero, and for each
+ incoming bit, x^32 is added mod p to the register if the bit is a one (where
+ x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+ x (which is shifting right by one and adding x^32 mod p if the bit shifted
+ out is a one). We start with the highest power (least significant bit) of
+ q and repeat for all eight bits of q.
+
+ The first table is simply the CRC of all possible eight bit values. This is
+ all the information needed to generate CRCs on data a byte at a time for all
+ combinations of CRC register values and incoming bytes. The remaining tables
+ allow for word-at-a-time CRC calculation for both big-endian and little-
+ endian machines, where a word is four bytes.
+*/
+local void make_crc_table()
+{
+ unsigned long c;
+ int n, k;
+ unsigned long poly; /* polynomial exclusive-or pattern */
+ /* terms of polynomial defining this crc (except x^32): */
+ static volatile int first = 1; /* flag to limit concurrent making */
+ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+ /* See if another task is already doing this (not thread-safe, but better
+ than nothing -- significantly reduces duration of vulnerability in
+ case the advice about DYNAMIC_CRC_TABLE is ignored) */
+ if (first) {
+ first = 0;
+
+ /* make exclusive-or pattern from polynomial (0xedb88320UL) */
+ poly = 0UL;
+ for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++)
+ poly |= 1UL << (31 - p[n]);
+
+ /* generate a crc for every 8-bit value */
+ for (n = 0; n < 256; n++) {
+ c = (unsigned long)n;
+ for (k = 0; k < 8; k++)
+ c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+ crc_table[0][n] = c;
+ }
+
+#ifdef BYFOUR
+ /* generate crc for each value followed by one, two, and three zeros,
+ and then the byte reversal of those as well as the first table */
+ for (n = 0; n < 256; n++) {
+ c = crc_table[0][n];
+ crc_table[4][n] = REV(c);
+ for (k = 1; k < 4; k++) {
+ c = crc_table[0][c & 0xff] ^ (c >> 8);
+ crc_table[k][n] = c;
+ crc_table[k + 4][n] = REV(c);
+ }
+ }
+#endif /* BYFOUR */
+
+ crc_table_empty = 0;
+ }
+ else { /* not first */
+ /* wait for the other guy to finish (not efficient, but rare) */
+ while (crc_table_empty)
+ ;
+ }
+
+#ifdef MAKECRCH
+ /* write out CRC tables to crc32.h */
+ {
+ FILE *out;
+
+ out = fopen("crc32.h", "w");
+ if (out == NULL) return;
+ fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
+ fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
+ fprintf(out, "local const unsigned long FAR ");
+ fprintf(out, "crc_table[TBLS][256] =\n{\n {\n");
+ write_table(out, crc_table[0]);
+# ifdef BYFOUR
+ fprintf(out, "#ifdef BYFOUR\n");
+ for (k = 1; k < 8; k++) {
+ fprintf(out, " },\n {\n");
+ write_table(out, crc_table[k]);
+ }
+ fprintf(out, "#endif\n");
+# endif /* BYFOUR */
+ fprintf(out, " }\n};\n");
+ fclose(out);
+ }
+#endif /* MAKECRCH */
+}
+
+#ifdef MAKECRCH
+local void write_table(out, table)
+ FILE *out;
+ const unsigned long FAR *table;
+{
+ int n;
+
+ for (n = 0; n < 256; n++)
+ fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n],
+ n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
+}
+#endif /* MAKECRCH */
+
+#else /* !DYNAMIC_CRC_TABLE */
+/* ========================================================================
+ * Tables of CRC-32s of all single-byte values, made by make_crc_table().
+ */
+#include "crc32.h"
+#endif /* DYNAMIC_CRC_TABLE */
+
+/* =========================================================================
+ * This function can be used by asm versions of crc32()
+ */
+const unsigned long FAR * ZEXPORT get_crc_table()
+{
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty)
+ make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+ return (const unsigned long FAR *)crc_table;
+}
+
+/* ========================================================================= */
+#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
+#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
+
+/* ========================================================================= */
+unsigned long ZEXPORT crc32(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ if (buf == Z_NULL) return 0UL;
+
+#ifdef DYNAMIC_CRC_TABLE
+ if (crc_table_empty)
+ make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+
+#ifdef BYFOUR
+ if (sizeof(void *) == sizeof(ptrdiff_t)) {
+ u4 endian;
+
+ endian = 1;
+ if (*((unsigned char *)(&endian)))
+ return crc32_little(crc, buf, len);
+ else
+ return crc32_big(crc, buf, len);
+ }
+#endif /* BYFOUR */
+ crc = crc ^ 0xffffffffUL;
+ while (len >= 8) {
+ DO8;
+ len -= 8;
+ }
+ if (len) do {
+ DO1;
+ } while (--len);
+ return crc ^ 0xffffffffUL;
+}
+
+#ifdef BYFOUR
+
+/* ========================================================================= */
+#define DOLIT4 c ^= *buf4++; \
+ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
+ crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
+#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
+
+/* ========================================================================= */
+local unsigned long crc32_little(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ register u4 c;
+ register const u4 FAR *buf4;
+
+ c = (u4)crc;
+ c = ~c;
+ while (len && ((ptrdiff_t)buf & 3)) {
+ c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+ len--;
+ }
+
+ buf4 = (const u4 FAR *)(const void FAR *)buf;
+ while (len >= 32) {
+ DOLIT32;
+ len -= 32;
+ }
+ while (len >= 4) {
+ DOLIT4;
+ len -= 4;
+ }
+ buf = (const unsigned char FAR *)buf4;
+
+ if (len) do {
+ c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+ } while (--len);
+ c = ~c;
+ return (unsigned long)c;
+}
+
+/* ========================================================================= */
+#define DOBIG4 c ^= *++buf4; \
+ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
+ crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
+#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
+
+/* ========================================================================= */
+local unsigned long crc32_big(crc, buf, len)
+ unsigned long crc;
+ const unsigned char FAR *buf;
+ unsigned len;
+{
+ register u4 c;
+ register const u4 FAR *buf4;
+
+ c = REV((u4)crc);
+ c = ~c;
+ while (len && ((ptrdiff_t)buf & 3)) {
+ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+ len--;
+ }
+
+ buf4 = (const u4 FAR *)(const void FAR *)buf;
+ buf4--;
+ while (len >= 32) {
+ DOBIG32;
+ len -= 32;
+ }
+ while (len >= 4) {
+ DOBIG4;
+ len -= 4;
+ }
+ buf4++;
+ buf = (const unsigned char FAR *)buf4;
+
+ if (len) do {
+ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+ } while (--len);
+ c = ~c;
+ return (unsigned long)(REV(c));
+}
+
+#endif /* BYFOUR */
+
+#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */
+
+/* ========================================================================= */
+local unsigned long gf2_matrix_times(mat, vec)
+ unsigned long *mat;
+ unsigned long vec;
+{
+ unsigned long sum;
+
+ sum = 0;
+ while (vec) {
+ if (vec & 1)
+ sum ^= *mat;
+ vec >>= 1;
+ mat++;
+ }
+ return sum;
+}
+
+/* ========================================================================= */
+local void gf2_matrix_square(square, mat)
+ unsigned long *square;
+ unsigned long *mat;
+{
+ int n;
+
+ for (n = 0; n < GF2_DIM; n++)
+ square[n] = gf2_matrix_times(mat, mat[n]);
+}
+
+/* ========================================================================= */
+uLong ZEXPORT crc32_combine(crc1, crc2, len2)
+ uLong crc1;
+ uLong crc2;
+ z_off_t len2;
+{
+ int n;
+ unsigned long row;
+ unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */
+ unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */
+
+ /* degenerate case */
+ if (len2 == 0)
+ return crc1;
+
+ /* put operator for one zero bit in odd */
+ odd[0] = 0xedb88320L; /* CRC-32 polynomial */
+ row = 1;
+ for (n = 1; n < GF2_DIM; n++) {
+ odd[n] = row;
+ row <<= 1;
+ }
+
+ /* put operator for two zero bits in even */
+ gf2_matrix_square(even, odd);
+
+ /* put operator for four zero bits in odd */
+ gf2_matrix_square(odd, even);
+
+ /* apply len2 zeros to crc1 (first square will put the operator for one
+ zero byte, eight zero bits, in even) */
+ do {
+ /* apply zeros operator for this bit of len2 */
+ gf2_matrix_square(even, odd);
+ if (len2 & 1)
+ crc1 = gf2_matrix_times(even, crc1);
+ len2 >>= 1;
+
+ /* if no more bits set, then done */
+ if (len2 == 0)
+ break;
+
+ /* another iteration of the loop with odd and even swapped */
+ gf2_matrix_square(odd, even);
+ if (len2 & 1)
+ crc1 = gf2_matrix_times(odd, crc1);
+ len2 >>= 1;
+
+ /* if no more bits set, then done */
+ } while (len2 != 0);
+
+ /* return combined crc */
+ crc1 ^= crc2;
+ return crc1;
+}
--- /dev/null
+/* crc32.h -- tables for rapid CRC calculation
+ * Generated automatically by crc32.c
+ */
+
+local const unsigned long FAR crc_table[TBLS][256] =
+{
+ {
+ 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
+ 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
+ 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
+ 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+ 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
+ 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
+ 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
+ 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+ 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
+ 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
+ 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
+ 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+ 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
+ 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
+ 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
+ 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+ 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
+ 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
+ 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
+ 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+ 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
+ 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
+ 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
+ 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+ 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
+ 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
+ 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
+ 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+ 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
+ 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
+ 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
+ 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+ 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
+ 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
+ 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
+ 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+ 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
+ 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
+ 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
+ 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+ 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
+ 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
+ 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
+ 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+ 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
+ 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
+ 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
+ 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+ 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
+ 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
+ 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
+ 0x2d02ef8dUL
+#ifdef BYFOUR
+ },
+ {
+ 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
+ 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
+ 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
+ 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
+ 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
+ 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
+ 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
+ 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
+ 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
+ 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
+ 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
+ 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
+ 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
+ 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
+ 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
+ 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
+ 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
+ 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
+ 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
+ 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
+ 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
+ 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
+ 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
+ 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
+ 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
+ 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
+ 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
+ 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
+ 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
+ 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
+ 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
+ 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
+ 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
+ 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
+ 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
+ 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
+ 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
+ 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
+ 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
+ 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
+ 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
+ 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
+ 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
+ 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
+ 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
+ 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
+ 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
+ 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
+ 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
+ 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
+ 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
+ 0x9324fd72UL
+ },
+ {
+ 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
+ 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
+ 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
+ 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
+ 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
+ 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
+ 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
+ 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
+ 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
+ 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
+ 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
+ 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
+ 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
+ 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
+ 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
+ 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
+ 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
+ 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
+ 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
+ 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
+ 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
+ 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
+ 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
+ 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
+ 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
+ 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
+ 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
+ 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
+ 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
+ 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
+ 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
+ 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
+ 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
+ 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
+ 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
+ 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
+ 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
+ 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
+ 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
+ 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
+ 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
+ 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
+ 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
+ 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
+ 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
+ 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
+ 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
+ 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
+ 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
+ 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
+ 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
+ 0xbe9834edUL
+ },
+ {
+ 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
+ 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
+ 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
+ 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
+ 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
+ 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
+ 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
+ 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
+ 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
+ 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
+ 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
+ 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
+ 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
+ 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
+ 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
+ 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
+ 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
+ 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
+ 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
+ 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
+ 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
+ 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
+ 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
+ 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
+ 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
+ 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
+ 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
+ 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
+ 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
+ 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
+ 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
+ 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
+ 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
+ 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
+ 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
+ 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
+ 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
+ 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
+ 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
+ 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
+ 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
+ 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
+ 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
+ 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
+ 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
+ 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
+ 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
+ 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
+ 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
+ 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
+ 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
+ 0xde0506f1UL
+ },
+ {
+ 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
+ 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
+ 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
+ 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
+ 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
+ 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
+ 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
+ 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
+ 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
+ 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
+ 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
+ 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
+ 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
+ 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
+ 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
+ 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
+ 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
+ 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
+ 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
+ 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
+ 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
+ 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
+ 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
+ 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
+ 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
+ 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
+ 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
+ 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
+ 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
+ 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
+ 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
+ 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
+ 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
+ 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
+ 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
+ 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
+ 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
+ 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
+ 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
+ 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
+ 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
+ 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
+ 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
+ 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
+ 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
+ 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
+ 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
+ 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
+ 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
+ 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
+ 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
+ 0x8def022dUL
+ },
+ {
+ 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
+ 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
+ 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
+ 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
+ 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
+ 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
+ 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
+ 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
+ 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
+ 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
+ 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
+ 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
+ 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
+ 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
+ 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
+ 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
+ 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
+ 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
+ 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
+ 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
+ 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
+ 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
+ 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
+ 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
+ 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
+ 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
+ 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
+ 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
+ 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
+ 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
+ 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
+ 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
+ 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
+ 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
+ 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
+ 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
+ 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
+ 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
+ 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
+ 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
+ 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
+ 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
+ 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
+ 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
+ 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
+ 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
+ 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
+ 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
+ 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
+ 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
+ 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
+ 0x72fd2493UL
+ },
+ {
+ 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
+ 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
+ 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
+ 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
+ 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
+ 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
+ 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
+ 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
+ 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
+ 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
+ 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
+ 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
+ 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
+ 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
+ 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
+ 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
+ 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
+ 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
+ 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
+ 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
+ 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
+ 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
+ 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
+ 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
+ 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
+ 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
+ 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
+ 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
+ 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
+ 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
+ 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
+ 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
+ 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
+ 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
+ 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
+ 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
+ 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
+ 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
+ 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
+ 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
+ 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
+ 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
+ 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
+ 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
+ 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
+ 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
+ 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
+ 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
+ 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
+ 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
+ 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
+ 0xed3498beUL
+ },
+ {
+ 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
+ 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
+ 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
+ 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
+ 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
+ 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
+ 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
+ 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
+ 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
+ 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
+ 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
+ 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
+ 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
+ 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
+ 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
+ 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
+ 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
+ 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
+ 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
+ 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
+ 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
+ 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
+ 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
+ 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
+ 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
+ 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
+ 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
+ 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
+ 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
+ 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
+ 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
+ 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
+ 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
+ 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
+ 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
+ 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
+ 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
+ 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
+ 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
+ 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
+ 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
+ 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
+ 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
+ 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
+ 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
+ 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
+ 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
+ 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
+ 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
+ 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
+ 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
+ 0xf10605deUL
+#endif
+ }
+};
--- /dev/null
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process depends on being able to identify portions
+ * of the input text which are identical to earlier input (within a
+ * sliding window trailing behind the input currently being processed).
+ *
+ * The most straightforward technique turns out to be the fastest for
+ * most input files: try all possible matches and select the longest.
+ * The key feature of this algorithm is that insertions into the string
+ * dictionary are very simple and thus fast, and deletions are avoided
+ * completely. Insertions are performed at each input character, whereas
+ * string matches are performed only when the previous match ends. So it
+ * is preferable to spend more time in matches to allow very fast string
+ * insertions and avoid deletions. The matching algorithm for small
+ * strings is inspired from that of Rabin & Karp. A brute force approach
+ * is used to find longer strings when a small match has been found.
+ * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ * (by Leonid Broukhis).
+ * A previous version of this file used a more sophisticated algorithm
+ * (by Fiala and Greene) which is guaranteed to run in linear amortized
+ * time, but has a larger average cost, uses more memory and is patented.
+ * However the F&G algorithm may be faster for some highly redundant
+ * files if the parameter max_chain_length (described below) is too large.
+ *
+ * ACKNOWLEDGEMENTS
+ *
+ * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ * I found it in 'freeze' written by Leonid Broukhis.
+ * Thanks to many people for bug reports and testing.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ * Available in http://www.ietf.org/rfc/rfc1951.txt
+ *
+ * A description of the Rabin and Karp algorithm is given in the book
+ * "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ * Fiala,E.R., and Greene,D.H.
+ * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* @(#) $Id$ */
+
+#include "deflate.h"
+
+const char deflate_copyright[] =
+ " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ * Function prototypes.
+ */
+typedef enum {
+ need_more, /* block not completed, need more input or more output */
+ block_done, /* block flush performed */
+ finish_started, /* finish started, need only more output at next deflate */
+ finish_done /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast OF((deflate_state *s, int flush));
+#ifndef FASTEST
+local block_state deflate_slow OF((deflate_state *s, int flush));
+#endif
+local void lm_init OF((deflate_state *s));
+local void putShortMSB OF((deflate_state *s, uInt b));
+local void flush_pending OF((z_streamp strm));
+local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size));
+#ifndef FASTEST
+#ifdef ASMV
+ void match_init OF((void)); /* asm code initialization */
+ uInt longest_match OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match OF((deflate_state *s, IPos cur_match));
+#endif
+#endif
+local uInt longest_match_fast OF((deflate_state *s, IPos cur_match));
+
+#ifdef DEBUG
+local void check_match OF((deflate_state *s, IPos start, IPos match,
+ int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+# define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+ ush good_length; /* reduce lazy search above this match length */
+ ush max_lazy; /* do not perform lazy search above this match length */
+ ush nice_length; /* quit search above this match length */
+ ush max_chain;
+ compress_func func;
+} config;
+
+#ifdef FASTEST
+local const config configuration_table[2] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */
+#else
+local const config configuration_table[10] = {
+/* good lazy nice chain */
+/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */
+/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */
+/* 2 */ {4, 5, 16, 8, deflate_fast},
+/* 3 */ {4, 6, 32, 32, deflate_fast},
+
+/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */
+/* 5 */ {8, 16, 32, 32, deflate_slow},
+/* 6 */ {8, 16, 128, 128, deflate_slow},
+/* 7 */ {8, 32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
+#endif
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+#ifndef NO_DUMMY_DECL
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+#endif
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN assertion: all calls to to UPDATE_HASH are made with consecutive
+ * input characters, so that a running hash key can be computed from the
+ * previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * If this file is compiled with -DFASTEST, the compression level is forced
+ * to 1, and no hash chains are maintained.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of str are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#ifdef FASTEST
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#else
+#define INSERT_STRING(s, str, match_head) \
+ (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+ match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \
+ s->head[s->ins_h] = (Pos)(str))
+#endif
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+ s->head[s->hash_size-1] = NIL; \
+ zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int ZEXPORT deflateInit_(strm, level, version, stream_size)
+ z_streamp strm;
+ int level;
+ const char *version;
+ int stream_size;
+{
+ return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY, version, stream_size);
+ /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+ version, stream_size)
+ z_streamp strm;
+ int level;
+ int method;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char *version;
+ int stream_size;
+{
+ deflate_state *s;
+ int wrap = 1;
+ static const char my_version[] = ZLIB_VERSION;
+
+ ushf *overlay;
+ /* We overlay pending_buf and d_buf+l_buf. This works since the average
+ * output size for (length,distance) codes is <= 24 bits.
+ */
+
+ if (version == Z_NULL || version[0] != my_version[0] ||
+ stream_size != sizeof(z_stream)) {
+ return Z_VERSION_ERROR;
+ }
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+ strm->msg = Z_NULL;
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+
+#ifdef FASTEST
+ if (level != 0) level = 1;
+#else
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+
+ if (windowBits < 0) { /* suppress zlib wrapper */
+ wrap = 0;
+ windowBits = -windowBits;
+ }
+#ifdef GZIP
+ else if (windowBits > 15) {
+ wrap = 2; /* write gzip wrapper instead */
+ windowBits -= 16;
+ }
+#endif
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+ windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+ strategy < 0 || strategy > Z_FIXED) {
+ return Z_STREAM_ERROR;
+ }
+ if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */
+ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+ if (s == Z_NULL) return Z_MEM_ERROR;
+ strm->state = (struct internal_state FAR *)s;
+ s->strm = strm;
+
+ s->wrap = wrap;
+ s->gzhead = Z_NULL;
+ s->w_bits = windowBits;
+ s->w_size = 1 << s->w_bits;
+ s->w_mask = s->w_size - 1;
+
+ s->hash_bits = memLevel + 7;
+ s->hash_size = 1 << s->hash_bits;
+ s->hash_mask = s->hash_size - 1;
+ s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+ s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+ s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos));
+ s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+ s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+ s->pending_buf = (uchf *) overlay;
+ s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+ s->pending_buf == Z_NULL) {
+ s->status = FINISH_STATE;
+ strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+ deflateEnd (strm);
+ return Z_MEM_ERROR;
+ }
+ s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+ s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+ s->level = level;
+ s->strategy = strategy;
+ s->method = (Byte)method;
+
+ return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
+ z_streamp strm;
+ const Bytef *dictionary;
+ uInt dictLength;
+{
+ deflate_state *s;
+ uInt length = dictLength;
+ uInt n;
+ IPos hash_head = 0;
+
+ if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
+ strm->state->wrap == 2 ||
+ (strm->state->wrap == 1 && strm->state->status != INIT_STATE))
+ return Z_STREAM_ERROR;
+
+ s = strm->state;
+ if (s->wrap)
+ strm->adler = adler32(strm->adler, dictionary, dictLength);
+
+ if (length < MIN_MATCH) return Z_OK;
+ if (length > MAX_DIST(s)) {
+ length = MAX_DIST(s);
+ dictionary += dictLength - length; /* use the tail of the dictionary */
+ }
+ zmemcpy(s->window, dictionary, length);
+ s->strstart = length;
+ s->block_start = (long)length;
+
+ /* Insert all strings in the hash table (except for the last two bytes).
+ * s->lookahead stays null, so s->ins_h will be recomputed at the next
+ * call of fill_window.
+ */
+ s->ins_h = s->window[0];
+ UPDATE_HASH(s, s->ins_h, s->window[1]);
+ for (n = 0; n <= length - MIN_MATCH; n++) {
+ INSERT_STRING(s, n, hash_head);
+ }
+ if (hash_head) hash_head = 0; /* to make compiler happy */
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+ z_streamp strm;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) {
+ return Z_STREAM_ERROR;
+ }
+
+ strm->total_in = strm->total_out = 0;
+ strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+ strm->data_type = Z_UNKNOWN;
+
+ s = (deflate_state *)strm->state;
+ s->pending = 0;
+ s->pending_out = s->pending_buf;
+
+ if (s->wrap < 0) {
+ s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
+ }
+ s->status = s->wrap ? INIT_STATE : BUSY_STATE;
+ strm->adler =
+#ifdef GZIP
+ s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
+#endif
+ adler32(0L, Z_NULL, 0);
+ s->last_flush = Z_NO_FLUSH;
+
+ _tr_init(s);
+ lm_init(s);
+
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetHeader (strm, head)
+ z_streamp strm;
+ gz_headerp head;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ if (strm->state->wrap != 2) return Z_STREAM_ERROR;
+ strm->state->gzhead = head;
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflatePrime (strm, bits, value)
+ z_streamp strm;
+ int bits;
+ int value;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ strm->state->bi_valid = bits;
+ strm->state->bi_buf = (ush)(value & ((1 << bits) - 1));
+ return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateParams(strm, level, strategy)
+ z_streamp strm;
+ int level;
+ int strategy;
+{
+ deflate_state *s;
+ compress_func func;
+ int err = Z_OK;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = strm->state;
+
+#ifdef FASTEST
+ if (level != 0) level = 1;
+#else
+ if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+ if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {
+ return Z_STREAM_ERROR;
+ }
+ func = configuration_table[s->level].func;
+
+ if (func != configuration_table[level].func && strm->total_in != 0) {
+ /* Flush the last buffer: */
+ err = deflate(strm, Z_PARTIAL_FLUSH);
+ }
+ if (s->level != level) {
+ s->level = level;
+ s->max_lazy_match = configuration_table[level].max_lazy;
+ s->good_match = configuration_table[level].good_length;
+ s->nice_match = configuration_table[level].nice_length;
+ s->max_chain_length = configuration_table[level].max_chain;
+ }
+ s->strategy = strategy;
+ return err;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
+ z_streamp strm;
+ int good_length;
+ int max_lazy;
+ int nice_length;
+ int max_chain;
+{
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ s = strm->state;
+ s->good_match = good_length;
+ s->max_lazy_match = max_lazy;
+ s->nice_match = nice_length;
+ s->max_chain_length = max_chain;
+ return Z_OK;
+}
+
+/* =========================================================================
+ * For the default windowBits of 15 and memLevel of 8, this function returns
+ * a close to exact, as well as small, upper bound on the compressed size.
+ * They are coded as constants here for a reason--if the #define's are
+ * changed, then this function needs to be changed as well. The return
+ * value for 15 and 8 only works for those exact settings.
+ *
+ * For any setting other than those defaults for windowBits and memLevel,
+ * the value returned is a conservative worst case for the maximum expansion
+ * resulting from using fixed blocks instead of stored blocks, which deflate
+ * can emit on compressed data for some combinations of the parameters.
+ *
+ * This function could be more sophisticated to provide closer upper bounds
+ * for every combination of windowBits and memLevel, as well as wrap.
+ * But even the conservative upper bound of about 14% expansion does not
+ * seem onerous for output buffer allocation.
+ */
+uLong ZEXPORT deflateBound(strm, sourceLen)
+ z_streamp strm;
+ uLong sourceLen;
+{
+ deflate_state *s;
+ uLong destLen;
+
+ /* conservative upper bound */
+ destLen = sourceLen +
+ ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11;
+
+ /* if can't get parameters, return conservative bound */
+ if (strm == Z_NULL || strm->state == Z_NULL)
+ return destLen;
+
+ /* if not default parameters, return conservative bound */
+ s = strm->state;
+ if (s->w_bits != 15 || s->hash_bits != 8 + 7)
+ return destLen;
+
+ /* default settings: return tight bound for that case */
+ return compressBound(sourceLen);
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+ deflate_state *s;
+ uInt b;
+{
+ put_byte(s, (Byte)(b >> 8));
+ put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+ z_streamp strm;
+{
+ unsigned len = strm->state->pending;
+
+ if (len > strm->avail_out) len = strm->avail_out;
+ if (len == 0) return;
+
+ zmemcpy(strm->next_out, strm->state->pending_out, len);
+ strm->next_out += len;
+ strm->state->pending_out += len;
+ strm->total_out += len;
+ strm->avail_out -= len;
+ strm->state->pending -= len;
+ if (strm->state->pending == 0) {
+ strm->state->pending_out = strm->state->pending_buf;
+ }
+}
+
+/* ========================================================================= */
+int ZEXPORT deflate (strm, flush)
+ z_streamp strm;
+ int flush;
+{
+ int old_flush; /* value of flush param for previous deflate call */
+ deflate_state *s;
+
+ if (strm == Z_NULL || strm->state == Z_NULL ||
+ flush > Z_FINISH || flush < 0) {
+ return Z_STREAM_ERROR;
+ }
+ s = strm->state;
+
+ if (strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0) ||
+ (s->status == FINISH_STATE && flush != Z_FINISH)) {
+ ERR_RETURN(strm, Z_STREAM_ERROR);
+ }
+ if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+ s->strm = strm; /* just in case */
+ old_flush = s->last_flush;
+ s->last_flush = flush;
+
+ /* Write the header */
+ if (s->status == INIT_STATE) {
+#ifdef GZIP
+ if (s->wrap == 2) {
+ strm->adler = crc32(0L, Z_NULL, 0);
+ put_byte(s, 31);
+ put_byte(s, 139);
+ put_byte(s, 8);
+ if (s->gzhead == NULL) {
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, s->level == 9 ? 2 :
+ (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+ 4 : 0));
+ put_byte(s, OS_CODE);
+ s->status = BUSY_STATE;
+ }
+ else {
+ put_byte(s, (s->gzhead->text ? 1 : 0) +
+ (s->gzhead->hcrc ? 2 : 0) +
+ (s->gzhead->extra == Z_NULL ? 0 : 4) +
+ (s->gzhead->name == Z_NULL ? 0 : 8) +
+ (s->gzhead->comment == Z_NULL ? 0 : 16)
+ );
+ put_byte(s, (Byte)(s->gzhead->time & 0xff));
+ put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff));
+ put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff));
+ put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff));
+ put_byte(s, s->level == 9 ? 2 :
+ (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+ 4 : 0));
+ put_byte(s, s->gzhead->os & 0xff);
+ if (s->gzhead->extra != NULL) {
+ put_byte(s, s->gzhead->extra_len & 0xff);
+ put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);
+ }
+ if (s->gzhead->hcrc)
+ strm->adler = crc32(strm->adler, s->pending_buf,
+ s->pending);
+ s->gzindex = 0;
+ s->status = EXTRA_STATE;
+ }
+ }
+ else
+#endif
+ {
+ uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+ uInt level_flags;
+
+ if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
+ level_flags = 0;
+ else if (s->level < 6)
+ level_flags = 1;
+ else if (s->level == 6)
+ level_flags = 2;
+ else
+ level_flags = 3;
+ header |= (level_flags << 6);
+ if (s->strstart != 0) header |= PRESET_DICT;
+ header += 31 - (header % 31);
+
+ s->status = BUSY_STATE;
+ putShortMSB(s, header);
+
+ /* Save the adler32 of the preset dictionary: */
+ if (s->strstart != 0) {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ strm->adler = adler32(0L, Z_NULL, 0);
+ }
+ }
+#ifdef GZIP
+ if (s->status == EXTRA_STATE) {
+ if (s->gzhead->extra != NULL) {
+ uInt beg = s->pending; /* start of bytes to update crc */
+
+ while (s->gzindex < (s->gzhead->extra_len & 0xffff)) {
+ if (s->pending == s->pending_buf_size) {
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ flush_pending(strm);
+ beg = s->pending;
+ if (s->pending == s->pending_buf_size)
+ break;
+ }
+ put_byte(s, s->gzhead->extra[s->gzindex]);
+ s->gzindex++;
+ }
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ if (s->gzindex == s->gzhead->extra_len) {
+ s->gzindex = 0;
+ s->status = NAME_STATE;
+ }
+ }
+ else
+ s->status = NAME_STATE;
+ }
+ if (s->status == NAME_STATE) {
+ if (s->gzhead->name != NULL) {
+ uInt beg = s->pending; /* start of bytes to update crc */
+ int val;
+
+ do {
+ if (s->pending == s->pending_buf_size) {
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ flush_pending(strm);
+ beg = s->pending;
+ if (s->pending == s->pending_buf_size) {
+ val = 1;
+ break;
+ }
+ }
+ val = s->gzhead->name[s->gzindex++];
+ put_byte(s, val);
+ } while (val != 0);
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ if (val == 0) {
+ s->gzindex = 0;
+ s->status = COMMENT_STATE;
+ }
+ }
+ else
+ s->status = COMMENT_STATE;
+ }
+ if (s->status == COMMENT_STATE) {
+ if (s->gzhead->comment != NULL) {
+ uInt beg = s->pending; /* start of bytes to update crc */
+ int val;
+
+ do {
+ if (s->pending == s->pending_buf_size) {
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ flush_pending(strm);
+ beg = s->pending;
+ if (s->pending == s->pending_buf_size) {
+ val = 1;
+ break;
+ }
+ }
+ val = s->gzhead->comment[s->gzindex++];
+ put_byte(s, val);
+ } while (val != 0);
+ if (s->gzhead->hcrc && s->pending > beg)
+ strm->adler = crc32(strm->adler, s->pending_buf + beg,
+ s->pending - beg);
+ if (val == 0)
+ s->status = HCRC_STATE;
+ }
+ else
+ s->status = HCRC_STATE;
+ }
+ if (s->status == HCRC_STATE) {
+ if (s->gzhead->hcrc) {
+ if (s->pending + 2 > s->pending_buf_size)
+ flush_pending(strm);
+ if (s->pending + 2 <= s->pending_buf_size) {
+ put_byte(s, (Byte)(strm->adler & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+ strm->adler = crc32(0L, Z_NULL, 0);
+ s->status = BUSY_STATE;
+ }
+ }
+ else
+ s->status = BUSY_STATE;
+ }
+#endif
+
+ /* Flush as much pending output as possible */
+ if (s->pending != 0) {
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ /* Since avail_out is 0, deflate will be called again with
+ * more output space, but possibly with both pending and
+ * avail_in equal to zero. There won't be anything to do,
+ * but this is not an error situation so make sure we
+ * return OK instead of BUF_ERROR at next call of deflate:
+ */
+ s->last_flush = -1;
+ return Z_OK;
+ }
+
+ /* Make sure there is something to do and avoid duplicate consecutive
+ * flushes. For repeated and useless calls with Z_FINISH, we keep
+ * returning Z_STREAM_END instead of Z_BUF_ERROR.
+ */
+ } else if (strm->avail_in == 0 && flush <= old_flush &&
+ flush != Z_FINISH) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (s->status == FINISH_STATE && strm->avail_in != 0) {
+ ERR_RETURN(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm->avail_in != 0 || s->lookahead != 0 ||
+ (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+ block_state bstate;
+
+ bstate = (*(configuration_table[s->level].func))(s, flush);
+
+ if (bstate == finish_started || bstate == finish_done) {
+ s->status = FINISH_STATE;
+ }
+ if (bstate == need_more || bstate == finish_started) {
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+ }
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+ if (bstate == block_done) {
+ if (flush == Z_PARTIAL_FLUSH) {
+ _tr_align(s);
+ } else { /* FULL_FLUSH or SYNC_FLUSH */
+ _tr_stored_block(s, (char*)0, 0L, 0);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush == Z_FULL_FLUSH) {
+ CLEAR_HASH(s); /* forget history */
+ }
+ }
+ flush_pending(strm);
+ if (strm->avail_out == 0) {
+ s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+ return Z_OK;
+ }
+ }
+ }
+ Assert(strm->avail_out > 0, "bug2");
+
+ if (flush != Z_FINISH) return Z_OK;
+ if (s->wrap <= 0) return Z_STREAM_END;
+
+ /* Write the trailer */
+#ifdef GZIP
+ if (s->wrap == 2) {
+ put_byte(s, (Byte)(strm->adler & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 16) & 0xff));
+ put_byte(s, (Byte)((strm->adler >> 24) & 0xff));
+ put_byte(s, (Byte)(strm->total_in & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));
+ put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));
+ }
+ else
+#endif
+ {
+ putShortMSB(s, (uInt)(strm->adler >> 16));
+ putShortMSB(s, (uInt)(strm->adler & 0xffff));
+ }
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */
+ return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateEnd (strm)
+ z_streamp strm;
+{
+ int status;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+
+ status = strm->state->status;
+ if (status != INIT_STATE &&
+ status != EXTRA_STATE &&
+ status != NAME_STATE &&
+ status != COMMENT_STATE &&
+ status != HCRC_STATE &&
+ status != BUSY_STATE &&
+ status != FINISH_STATE) {
+ return Z_STREAM_ERROR;
+ }
+
+ /* Deallocate in reverse order of allocations: */
+ TRY_FREE(strm, strm->state->pending_buf);
+ TRY_FREE(strm, strm->state->head);
+ TRY_FREE(strm, strm->state->prev);
+ TRY_FREE(strm, strm->state->window);
+
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+
+ return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ * To simplify the source, this is not supported for 16-bit MSDOS (which
+ * doesn't have enough memory anyway to duplicate compression states).
+ */
+int ZEXPORT deflateCopy (dest, source)
+ z_streamp dest;
+ z_streamp source;
+{
+#ifdef MAXSEG_64K
+ return Z_STREAM_ERROR;
+#else
+ deflate_state *ds;
+ deflate_state *ss;
+ ushf *overlay;
+
+
+ if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
+ return Z_STREAM_ERROR;
+ }
+
+ ss = source->state;
+
+ zmemcpy(dest, source, sizeof(z_stream));
+
+ ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+ if (ds == Z_NULL) return Z_MEM_ERROR;
+ dest->state = (struct internal_state FAR *) ds;
+ zmemcpy(ds, ss, sizeof(deflate_state));
+ ds->strm = dest;
+
+ ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+ ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos));
+ ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos));
+ overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+ ds->pending_buf = (uchf *) overlay;
+
+ if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+ ds->pending_buf == Z_NULL) {
+ deflateEnd (dest);
+ return Z_MEM_ERROR;
+ }
+ /* following zmemcpy do not work for 16-bit MSDOS */
+ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+ zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
+ zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+ zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+ ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+ ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+ ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+ ds->l_desc.dyn_tree = ds->dyn_ltree;
+ ds->d_desc.dyn_tree = ds->dyn_dtree;
+ ds->bl_desc.dyn_tree = ds->bl_tree;
+
+ return Z_OK;
+#endif /* MAXSEG_64K */
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read. All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+ z_streamp strm;
+ Bytef *buf;
+ unsigned size;
+{
+ unsigned len = strm->avail_in;
+
+ if (len > size) len = size;
+ if (len == 0) return 0;
+
+ strm->avail_in -= len;
+
+ if (strm->state->wrap == 1) {
+ strm->adler = adler32(strm->adler, strm->next_in, len);
+ }
+#ifdef GZIP
+ else if (strm->state->wrap == 2) {
+ strm->adler = crc32(strm->adler, strm->next_in, len);
+ }
+#endif
+ zmemcpy(buf, strm->next_in, len);
+ strm->next_in += len;
+ strm->total_in += len;
+
+ return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+ deflate_state *s;
+{
+ s->window_size = (ulg)2L*s->w_size;
+
+ CLEAR_HASH(s);
+
+ /* Set the default configuration parameters:
+ */
+ s->max_lazy_match = configuration_table[s->level].max_lazy;
+ s->good_match = configuration_table[s->level].good_length;
+ s->nice_match = configuration_table[s->level].nice_length;
+ s->max_chain_length = configuration_table[s->level].max_chain;
+
+ s->strstart = 0;
+ s->block_start = 0L;
+ s->lookahead = 0;
+ s->match_length = s->prev_length = MIN_MATCH-1;
+ s->match_available = 0;
+ s->ins_h = 0;
+#ifndef FASTEST
+#ifdef ASMV
+ match_init(); /* initialize the asm code */
+#endif
+#endif
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local uInt longest_match(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ unsigned chain_length = s->max_chain_length;/* max hash chain length */
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ int best_len = s->prev_length; /* best match length so far */
+ int nice_match = s->nice_match; /* stop if match long enough */
+ IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+ s->strstart - (IPos)MAX_DIST(s) : NIL;
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+ Posf *prev = s->prev;
+ uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+ /* Compare two bytes at a time. Note: this is not always beneficial.
+ * Try with and without -DUNALIGNED_OK to check.
+ */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+ register ush scan_start = *(ushf*)scan;
+ register ush scan_end = *(ushf*)(scan+best_len-1);
+#else
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+ register Byte scan_end1 = scan[best_len-1];
+ register Byte scan_end = scan[best_len];
+#endif
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s->prev_length >= s->good_match) {
+ chain_length >>= 2;
+ }
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ Assert(cur_match < s->strstart, "no future");
+ match = s->window + cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2. Note that the checks below
+ * for insufficient lookahead only occur occasionally for performance
+ * reasons. Therefore uninitialized memory will be accessed, and
+ * conditional jumps will be made that depend on those values.
+ * However the length of the match is limited to the lookahead, so
+ * the output of deflate is not affected by the uninitialized values.
+ */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+ /* This code assumes sizeof(unsigned short) == 2. Do not use
+ * UNALIGNED_OK if your compiler uses a different size.
+ */
+ if (*(ushf*)(match+best_len-1) != scan_end ||
+ *(ushf*)match != scan_start) continue;
+
+ /* It is not necessary to compare scan[2] and match[2] since they are
+ * always equal when the other bytes match, given that the hash keys
+ * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+ * strstart+3, +5, ... up to strstart+257. We check for insufficient
+ * lookahead only every 4th comparison; the 128th check will be made
+ * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+ * necessary to put more guard bytes at the end of the window, or
+ * to check more often for insufficient lookahead.
+ */
+ Assert(scan[2] == match[2], "scan[2]?");
+ scan++, match++;
+ do {
+ } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+ scan < strend);
+ /* The funny "do {}" generates better code on most compilers */
+
+ /* Here, scan <= window+strstart+257 */
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+ if (*scan == *match) scan++;
+
+ len = (MAX_MATCH - 1) - (int)(strend-scan);
+ scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+ if (match[best_len] != scan_end ||
+ match[best_len-1] != scan_end1 ||
+ *match != *scan ||
+ *++match != scan[1]) continue;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match++;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+ scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+ if (len > best_len) {
+ s->match_start = cur_match;
+ best_len = len;
+ if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+ scan_end = *(ushf*)(scan+best_len-1);
+#else
+ scan_end1 = scan[best_len-1];
+ scan_end = scan[best_len];
+#endif
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit
+ && --chain_length != 0);
+
+ if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
+ return s->lookahead;
+}
+#endif /* ASMV */
+#endif /* FASTEST */
+
+/* ---------------------------------------------------------------------------
+ * Optimized version for level == 1 or strategy == Z_RLE only
+ */
+local uInt longest_match_fast(s, cur_match)
+ deflate_state *s;
+ IPos cur_match; /* current match */
+{
+ register Bytef *scan = s->window + s->strstart; /* current string */
+ register Bytef *match; /* matched string */
+ register int len; /* length of current match */
+ register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ Assert(cur_match < s->strstart, "no future");
+
+ match = s->window + cur_match;
+
+ /* Return failure if the match length is less than 2:
+ */
+ if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2, match += 2;
+ Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ } while (*++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ *++scan == *++match && *++scan == *++match &&
+ scan < strend);
+
+ Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (int)(strend - scan);
+
+ if (len < MIN_MATCH) return MIN_MATCH - 1;
+
+ s->match_start = cur_match;
+ return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;
+}
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+ deflate_state *s;
+ IPos start, match;
+ int length;
+{
+ /* check that the match is indeed a match */
+ if (zmemcmp(s->window + match,
+ s->window + start, length) != EQUAL) {
+ fprintf(stderr, " start %u, match %u, length %d\n",
+ start, match, length);
+ do {
+ fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+ } while (--length != 0);
+ z_error("invalid match");
+ }
+ if (z_verbose > 1) {
+ fprintf(stderr,"\\[%d,%d]", start-match, length);
+ do { putc(s->window[start++], stderr); } while (--length != 0);
+ }
+}
+#else
+# define check_match(s, start, match, length)
+#endif /* DEBUG */
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+local void fill_window(s)
+ deflate_state *s;
+{
+ register unsigned n, m;
+ register Posf *p;
+ unsigned more; /* Amount of free space at the end of the window. */
+ uInt wsize = s->w_size;
+
+ do {
+ more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+ /* Deal with !@#$% 64K limit: */
+ if (sizeof(int) <= 2) {
+ if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ more = wsize;
+
+ } else if (more == (unsigned)(-1)) {
+ /* Very unlikely, but possible on 16 bit machine if
+ * strstart == 0 && lookahead == 1 (input done a byte at time)
+ */
+ more--;
+ }
+ }
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (s->strstart >= wsize+MAX_DIST(s)) {
+
+ zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
+ s->match_start -= wsize;
+ s->strstart -= wsize; /* we now have strstart >= MAX_DIST */
+ s->block_start -= (long) wsize;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage). We slide even when level == 0
+ to keep the hash table consistent if we switch back to level > 0
+ later. (Using level 0 permanently is not an optimal usage of
+ zlib, so we don't care about this pathological case.)
+ */
+ /* %%% avoid this when Z_RLE */
+ n = s->hash_size;
+ p = &s->head[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ } while (--n);
+
+ n = wsize;
+#ifndef FASTEST
+ p = &s->prev[n];
+ do {
+ m = *--p;
+ *p = (Pos)(m >= wsize ? m-wsize : NIL);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+#endif
+ more += wsize;
+ }
+ if (s->strm->avail_in == 0) return;
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ Assert(more >= 2, "more < 2");
+
+ n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+ s->lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s->lookahead >= MIN_MATCH) {
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, eof) { \
+ _tr_flush_block(s, (s->block_start >= 0L ? \
+ (charf *)&s->window[(unsigned)s->block_start] : \
+ (charf *)Z_NULL), \
+ (ulg)((long)s->strstart - s->block_start), \
+ (eof)); \
+ s->block_start = s->strstart; \
+ flush_pending(s->strm); \
+ Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, eof) { \
+ FLUSH_BLOCK_ONLY(s, eof); \
+ if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+ * to pending_buf_size, and each stored block has a 5 byte header:
+ */
+ ulg max_block_size = 0xffff;
+ ulg max_start;
+
+ if (max_block_size > s->pending_buf_size - 5) {
+ max_block_size = s->pending_buf_size - 5;
+ }
+
+ /* Copy as much as possible from input to output: */
+ for (;;) {
+ /* Fill the window as much as possible: */
+ if (s->lookahead <= 1) {
+
+ Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+ s->block_start >= (long)s->w_size, "slide too late");
+
+ fill_window(s);
+ if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+ Assert(s->block_start >= 0L, "block gone");
+
+ s->strstart += s->lookahead;
+ s->lookahead = 0;
+
+ /* Emit a stored block if pending_buf will be full: */
+ max_start = s->block_start + max_block_size;
+ if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+ /* strstart == 0 is possible when wraparound on 16-bit machine */
+ s->lookahead = (uInt)(s->strstart - max_start);
+ s->strstart = (uInt)max_start;
+ FLUSH_BLOCK(s, 0);
+ }
+ /* Flush if we may have to slide, otherwise block_start may become
+ * negative and the data will be gone:
+ */
+ if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+ FLUSH_BLOCK(s, 0);
+ }
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of the hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+#ifdef FASTEST
+ if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) ||
+ (s->strategy == Z_RLE && s->strstart - hash_head == 1)) {
+ s->match_length = longest_match_fast (s, hash_head);
+ }
+#else
+ if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) {
+ s->match_length = longest_match (s, hash_head);
+ } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) {
+ s->match_length = longest_match_fast (s, hash_head);
+ }
+#endif
+ /* longest_match() or longest_match_fast() sets match_start */
+ }
+ if (s->match_length >= MIN_MATCH) {
+ check_match(s, s->strstart, s->match_start, s->match_length);
+
+ _tr_tally_dist(s, s->strstart - s->match_start,
+ s->match_length - MIN_MATCH, bflush);
+
+ s->lookahead -= s->match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+#ifndef FASTEST
+ if (s->match_length <= s->max_insert_length &&
+ s->lookahead >= MIN_MATCH) {
+ s->match_length--; /* string at strstart already in table */
+ do {
+ s->strstart++;
+ INSERT_STRING(s, s->strstart, hash_head);
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s->match_length != 0);
+ s->strstart++;
+ } else
+#endif
+ {
+ s->strstart += s->match_length;
+ s->match_length = 0;
+ s->ins_h = s->window[s->strstart];
+ UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+ Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ IPos hash_head = NIL; /* head of hash chain */
+ int bflush; /* set if current block must be flushed */
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s->lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ if (s->lookahead >= MIN_MATCH) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s->prev_length = s->match_length, s->prev_match = s->match_start;
+ s->match_length = MIN_MATCH-1;
+
+ if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+ s->strstart - hash_head <= MAX_DIST(s)) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) {
+ s->match_length = longest_match (s, hash_head);
+ } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) {
+ s->match_length = longest_match_fast (s, hash_head);
+ }
+ /* longest_match() or longest_match_fast() sets match_start */
+
+ if (s->match_length <= 5 && (s->strategy == Z_FILTERED
+#if TOO_FAR <= 32767
+ || (s->match_length == MIN_MATCH &&
+ s->strstart - s->match_start > TOO_FAR)
+#endif
+ )) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s->match_length = MIN_MATCH-1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+ uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+ _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+ s->prev_length - MIN_MATCH, bflush);
+
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s->lookahead -= s->prev_length-1;
+ s->prev_length -= 2;
+ do {
+ if (++s->strstart <= max_insert) {
+ INSERT_STRING(s, s->strstart, hash_head);
+ }
+ } while (--s->prev_length != 0);
+ s->match_available = 0;
+ s->match_length = MIN_MATCH-1;
+ s->strstart++;
+
+ if (bflush) FLUSH_BLOCK(s, 0);
+
+ } else if (s->match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ if (bflush) {
+ FLUSH_BLOCK_ONLY(s, 0);
+ }
+ s->strstart++;
+ s->lookahead--;
+ if (s->strm->avail_out == 0) return need_more;
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s->match_available = 1;
+ s->strstart++;
+ s->lookahead--;
+ }
+ }
+ Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s->match_available) {
+ Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+ s->match_available = 0;
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+#endif /* FASTEST */
+
+#if 0
+/* ===========================================================================
+ * For Z_RLE, simply look for runs of bytes, generate matches only of distance
+ * one. Do not maintain a hash table. (It will be regenerated if this run of
+ * deflate switches away from Z_RLE.)
+ */
+local block_state deflate_rle(s, flush)
+ deflate_state *s;
+ int flush;
+{
+ int bflush; /* set if current block must be flushed */
+ uInt run; /* length of run */
+ uInt max; /* maximum length of run */
+ uInt prev; /* byte at distance one to match */
+ Bytef *scan; /* scan for end of run */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the longest encodable run.
+ */
+ if (s->lookahead < MAX_MATCH) {
+ fill_window(s);
+ if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) {
+ return need_more;
+ }
+ if (s->lookahead == 0) break; /* flush the current block */
+ }
+
+ /* See how many times the previous byte repeats */
+ run = 0;
+ if (s->strstart > 0) { /* if there is a previous byte, that is */
+ max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH;
+ scan = s->window + s->strstart - 1;
+ prev = *scan++;
+ do {
+ if (*scan++ != prev)
+ break;
+ } while (++run < max);
+ }
+
+ /* Emit match if have run of MIN_MATCH or longer, else emit literal */
+ if (run >= MIN_MATCH) {
+ check_match(s, s->strstart, s->strstart - 1, run);
+ _tr_tally_dist(s, 1, run - MIN_MATCH, bflush);
+ s->lookahead -= run;
+ s->strstart += run;
+ } else {
+ /* No match, output a literal byte */
+ Tracevv((stderr,"%c", s->window[s->strstart]));
+ _tr_tally_lit (s, s->window[s->strstart], bflush);
+ s->lookahead--;
+ s->strstart++;
+ }
+ if (bflush) FLUSH_BLOCK(s, 0);
+ }
+ FLUSH_BLOCK(s, flush == Z_FINISH);
+ return flush == Z_FINISH ? finish_done : block_done;
+}
+#endif
--- /dev/null
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2004 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef DEFLATE_H
+#define DEFLATE_H
+
+#include "zutil.h"
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+ trailer creation by deflate(). NO_GZIP would be used to avoid linking in
+ the crc code when it is not needed. For shared libraries, gzip encoding
+ should be left enabled. */
+#ifndef NO_GZIP
+# define GZIP
+#endif
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS 256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES 30
+/* number of distance codes */
+
+#define BL_CODES 19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE 42
+#define EXTRA_STATE 69
+#define NAME_STATE 73
+#define COMMENT_STATE 91
+#define HCRC_STATE 103
+#define BUSY_STATE 113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+ union {
+ ush freq; /* frequency count */
+ ush code; /* bit string */
+ } fc;
+ union {
+ ush dad; /* father node in Huffman tree */
+ ush len; /* length of bit string */
+ } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad dl.dad
+#define Len dl.len
+
+typedef struct static_tree_desc_s static_tree_desc;
+
+typedef struct tree_desc_s {
+ ct_data *dyn_tree; /* the dynamic tree */
+ int max_code; /* largest code with non zero frequency */
+ static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+ z_streamp strm; /* pointer back to this zlib stream */
+ int status; /* as the name implies */
+ Bytef *pending_buf; /* output still pending */
+ ulg pending_buf_size; /* size of pending_buf */
+ Bytef *pending_out; /* next pending byte to output to the stream */
+ uInt pending; /* nb of bytes in the pending buffer */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ gz_headerp gzhead; /* gzip header information to write */
+ uInt gzindex; /* where in extra, name, or comment */
+ Byte method; /* STORED (for zip only) or DEFLATED */
+ int last_flush; /* value of flush param for previous deflate call */
+
+ /* used by deflate.c: */
+
+ uInt w_size; /* LZ77 window size (32K by default) */
+ uInt w_bits; /* log2(w_size) (8..16) */
+ uInt w_mask; /* w_size - 1 */
+
+ Bytef *window;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: use the user input buffer as sliding window.
+ */
+
+ ulg window_size;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ Posf *prev;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ Posf *head; /* Heads of the hash chains or NIL. */
+
+ uInt ins_h; /* hash index of string to be inserted */
+ uInt hash_size; /* number of elements in hash table */
+ uInt hash_bits; /* log2(hash_size) */
+ uInt hash_mask; /* hash_size-1 */
+
+ uInt hash_shift;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ long block_start;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ uInt match_length; /* length of best match */
+ IPos prev_match; /* previous match */
+ int match_available; /* set if previous match exists */
+ uInt strstart; /* start of string to insert */
+ uInt match_start; /* start of matching string */
+ uInt lookahead; /* number of valid bytes ahead in window */
+
+ uInt prev_length;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ uInt max_chain_length;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ uInt max_lazy_match;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+# define max_insert_length max_lazy_match
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ int level; /* compression level (1..9) */
+ int strategy; /* favor or force Huffman coding*/
+
+ uInt good_match;
+ /* Use a faster search when the previous match is longer than this */
+
+ int nice_match; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+ /* Didn't use ct_data typedef below to supress compiler warning */
+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ struct tree_desc_s l_desc; /* desc. for literal tree */
+ struct tree_desc_s d_desc; /* desc. for distance tree */
+ struct tree_desc_s bl_desc; /* desc. for bit length tree */
+
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ int heap_len; /* number of elements in the heap */
+ int heap_max; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ uch depth[2*L_CODES+1];
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ uchf *l_buf; /* buffer for literals or lengths */
+
+ uInt lit_bufsize;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ uInt last_lit; /* running index in l_buf */
+
+ ushf *d_buf;
+ /* Buffer for distances. To simplify the code, d_buf and l_buf have
+ * the same number of elements. To use different lengths, an extra flag
+ * array would be necessary.
+ */
+
+ ulg opt_len; /* bit length of current block with optimal trees */
+ ulg static_len; /* bit length of current block with static trees */
+ uInt matches; /* number of string matches in current block */
+ int last_eob_len; /* bit length of EOB code for last block */
+
+#ifdef DEBUG
+ ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */
+#endif
+
+ ush bi_buf;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ int bi_valid;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+ /* in trees.c */
+void _tr_init OF((deflate_state *s));
+int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
+void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+void _tr_align OF((deflate_state *s));
+void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+ int eof));
+
+#define d_code(dist) \
+ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+ extern uch _length_code[];
+ extern uch _dist_code[];
+#else
+ extern const uch _length_code[];
+ extern const uch _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+ { uch cc = (c); \
+ s->d_buf[s->last_lit] = 0; \
+ s->l_buf[s->last_lit++] = cc; \
+ s->dyn_ltree[cc].Freq++; \
+ flush = (s->last_lit == s->lit_bufsize-1); \
+ }
+# define _tr_tally_dist(s, distance, length, flush) \
+ { uch len = (length); \
+ ush dist = (distance); \
+ s->d_buf[s->last_lit] = dist; \
+ s->l_buf[s->last_lit++] = len; \
+ dist--; \
+ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+ s->dyn_dtree[d_code(dist)].Freq++; \
+ flush = (s->last_lit == s->lit_bufsize-1); \
+ }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+ flush = _tr_tally(s, distance, length)
+#endif
+
+#endif /* DEFLATE_H */
--- /dev/null
+/* gzio.c -- IO on .gz files
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Compile this file with -DNO_GZCOMPRESS to avoid the compression code.
+ */
+
+/* @(#) $Id$ */
+
+#include <stdio.h>
+
+#include "zutil.h"
+
+#ifdef NO_DEFLATE /* for compatibility with old definition */
+# define NO_GZCOMPRESS
+#endif
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#ifndef Z_BUFSIZE
+# ifdef MAXSEG_64K
+# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
+# else
+# define Z_BUFSIZE 16384
+# endif
+#endif
+#ifndef Z_PRINTF_BUFSIZE
+# define Z_PRINTF_BUFSIZE 4096
+#endif
+
+#ifdef __MVS__
+# pragma map (fdopen , "\174\174FDOPEN")
+ FILE *fdopen(int, const char *);
+#endif
+
+#ifndef STDC
+extern voidp malloc OF((uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+#define ALLOC(size) malloc(size)
+#define TRYFREE(p) {if (p) free(p);}
+
+static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+
+/* gzip flag byte */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+typedef struct gz_stream {
+ z_stream stream;
+ int z_err; /* error code for last stream operation */
+ int z_eof; /* set if end of input file */
+ FILE *file; /* .gz file */
+ Byte *inbuf; /* input buffer */
+ Byte *outbuf; /* output buffer */
+ uLong crc; /* crc32 of uncompressed data */
+ char *msg; /* error message */
+ char *path; /* path name for debugging only */
+ int transparent; /* 1 if input file is not a .gz file */
+ char mode; /* 'w' or 'r' */
+ z_off_t start; /* start of compressed data in file (header skipped) */
+ z_off_t in; /* bytes into deflate or inflate */
+ z_off_t out; /* bytes out of deflate or inflate */
+ int back; /* one character push-back */
+ int last; /* true if push-back is last character */
+} gz_stream;
+
+
+local gzFile gz_open OF((const char *path, const char *mode, int fd));
+local int do_flush OF((gzFile file, int flush));
+local int get_byte OF((gz_stream *s));
+local void check_header OF((gz_stream *s));
+local int destroy OF((gz_stream *s));
+local void putLong OF((FILE *file, uLong x));
+local uLong getLong OF((gz_stream *s));
+
+/* ===========================================================================
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb"). The file is given either by file descriptor
+ or path name (if fd == -1).
+ gz_open returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR).
+*/
+local gzFile gz_open (path, mode, fd)
+ const char *path;
+ const char *mode;
+ int fd;
+{
+ int err;
+ int level = Z_DEFAULT_COMPRESSION; /* compression level */
+ int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
+ char *p = (char*)mode;
+ gz_stream *s;
+ char fmode[80]; /* copy of mode, without the compression level */
+ char *m = fmode;
+
+ if (!path || !mode) return Z_NULL;
+
+ s = (gz_stream *)ALLOC(sizeof(gz_stream));
+ if (!s) return Z_NULL;
+
+ s->stream.zalloc = (alloc_func)0;
+ s->stream.zfree = (free_func)0;
+ s->stream.opaque = (voidpf)0;
+ s->stream.next_in = s->inbuf = Z_NULL;
+ s->stream.next_out = s->outbuf = Z_NULL;
+ s->stream.avail_in = s->stream.avail_out = 0;
+ s->file = NULL;
+ s->z_err = Z_OK;
+ s->z_eof = 0;
+ s->in = 0;
+ s->out = 0;
+ s->back = EOF;
+ s->crc = crc32(0L, Z_NULL, 0);
+ s->msg = NULL;
+ s->transparent = 0;
+
+ s->path = (char*)ALLOC(strlen(path)+1);
+ if (s->path == NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ strcpy(s->path, path); /* do this early for debugging */
+
+ s->mode = '\0';
+ do {
+ if (*p == 'r') s->mode = 'r';
+ if (*p == 'w' || *p == 'a') s->mode = 'w';
+ if (*p >= '0' && *p <= '9') {
+ level = *p - '0';
+ } else if (*p == 'f') {
+ strategy = Z_FILTERED;
+ } else if (*p == 'h') {
+ strategy = Z_HUFFMAN_ONLY;
+ } else if (*p == 'R') {
+ strategy = Z_RLE;
+ } else {
+ *m++ = *p; /* copy the mode */
+ }
+ } while (*p++ && m != fmode + sizeof(fmode));
+ if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
+
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ err = Z_STREAM_ERROR;
+#else
+ err = deflateInit2(&(s->stream), level,
+ Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
+ /* windowBits is passed < 0 to suppress zlib header */
+
+ s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+#endif
+ if (err != Z_OK || s->outbuf == Z_NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ } else {
+ s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE);
+
+ err = inflateInit2(&(s->stream), -MAX_WBITS);
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+ * present after the compressed stream.
+ */
+ if (err != Z_OK || s->inbuf == Z_NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+
+ errno = 0;
+ s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode);
+
+ if (s->file == NULL) {
+ return destroy(s), (gzFile)Z_NULL;
+ }
+ if (s->mode == 'w') {
+ /* Write a very simple .gz header:
+ */
+ fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
+ Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
+ s->start = 10L;
+ /* We use 10L instead of ftell(s->file) to because ftell causes an
+ * fflush on some systems. This version of the library doesn't use
+ * start anyway in write mode, so this initialization is not
+ * necessary.
+ */
+ } else {
+ check_header(s); /* skip the .gz header */
+ s->start = ftell(s->file) - s->stream.avail_in;
+ }
+
+ return (gzFile)s;
+}
+
+/* ===========================================================================
+ Opens a gzip (.gz) file for reading or writing.
+*/
+gzFile ZEXPORT gzopen (path, mode)
+ const char *path;
+ const char *mode;
+{
+ return gz_open (path, mode, -1);
+}
+
+/* ===========================================================================
+ Associate a gzFile with the file descriptor fd. fd is not dup'ed here
+ to mimic the behavio(u)r of fdopen.
+*/
+gzFile ZEXPORT gzdopen (fd, mode)
+ int fd;
+ const char *mode;
+{
+ char name[46]; /* allow for up to 128-bit integers */
+
+ if (fd < 0) return (gzFile)Z_NULL;
+ sprintf(name, "<fd:%d>", fd); /* for debugging */
+
+ return gz_open (name, mode, fd);
+}
+
+/* ===========================================================================
+ * Update the compression level and strategy
+ */
+int ZEXPORT gzsetparams (file, level, strategy)
+ gzFile file;
+ int level;
+ int strategy;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ /* Make room to allow flushing */
+ if (s->stream.avail_out == 0) {
+
+ s->stream.next_out = s->outbuf;
+ if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+ s->z_err = Z_ERRNO;
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+
+ return deflateParams (&(s->stream), level, strategy);
+}
+
+/* ===========================================================================
+ Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+ for end of file.
+ IN assertion: the stream s has been sucessfully opened for reading.
+*/
+local int get_byte(s)
+ gz_stream *s;
+{
+ if (s->z_eof) return EOF;
+ if (s->stream.avail_in == 0) {
+ errno = 0;
+ s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+ if (s->stream.avail_in == 0) {
+ s->z_eof = 1;
+ if (ferror(s->file)) s->z_err = Z_ERRNO;
+ return EOF;
+ }
+ s->stream.next_in = s->inbuf;
+ }
+ s->stream.avail_in--;
+ return *(s->stream.next_in)++;
+}
+
+/* ===========================================================================
+ Check the gzip header of a gz_stream opened for reading. Set the stream
+ mode to transparent if the gzip magic header is not present; set s->err
+ to Z_DATA_ERROR if the magic header is present but the rest of the header
+ is incorrect.
+ IN assertion: the stream s has already been created sucessfully;
+ s->stream.avail_in is zero for the first time, but may be non-zero
+ for concatenated .gz files.
+*/
+local void check_header(s)
+ gz_stream *s;
+{
+ int method; /* method byte */
+ int flags; /* flags byte */
+ uInt len;
+ int c;
+
+ /* Assure two bytes in the buffer so we can peek ahead -- handle case
+ where first byte of header is at the end of the buffer after the last
+ gzip segment */
+ len = s->stream.avail_in;
+ if (len < 2) {
+ if (len) s->inbuf[0] = s->stream.next_in[0];
+ errno = 0;
+ len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file);
+ if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO;
+ s->stream.avail_in += len;
+ s->stream.next_in = s->inbuf;
+ if (s->stream.avail_in < 2) {
+ s->transparent = s->stream.avail_in;
+ return;
+ }
+ }
+
+ /* Peek ahead to check the gzip magic header */
+ if (s->stream.next_in[0] != gz_magic[0] ||
+ s->stream.next_in[1] != gz_magic[1]) {
+ s->transparent = 1;
+ return;
+ }
+ s->stream.avail_in -= 2;
+ s->stream.next_in += 2;
+
+ /* Check the rest of the gzip header */
+ method = get_byte(s);
+ flags = get_byte(s);
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ s->z_err = Z_DATA_ERROR;
+ return;
+ }
+
+ /* Discard time, xflags and OS code: */
+ for (len = 0; len < 6; len++) (void)get_byte(s);
+
+ if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+ len = (uInt)get_byte(s);
+ len += ((uInt)get_byte(s))<<8;
+ /* len is garbage if EOF but the loop below will quit anyway */
+ while (len-- != 0 && get_byte(s) != EOF) ;
+ }
+ if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
+ while ((c = get_byte(s)) != 0 && c != EOF) ;
+ }
+ if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
+ while ((c = get_byte(s)) != 0 && c != EOF) ;
+ }
+ if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
+ for (len = 0; len < 2; len++) (void)get_byte(s);
+ }
+ s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
+}
+
+ /* ===========================================================================
+ * Cleanup then free the given gz_stream. Return a zlib error code.
+ Try freeing in the reverse order of allocations.
+ */
+local int destroy (s)
+ gz_stream *s;
+{
+ int err = Z_OK;
+
+ if (!s) return Z_STREAM_ERROR;
+
+ TRYFREE(s->msg);
+
+ if (s->stream.state != NULL) {
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ err = Z_STREAM_ERROR;
+#else
+ err = deflateEnd(&(s->stream));
+#endif
+ } else if (s->mode == 'r') {
+ err = inflateEnd(&(s->stream));
+ }
+ }
+ if (s->file != NULL && fclose(s->file)) {
+#ifdef ESPIPE
+ if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */
+#endif
+ err = Z_ERRNO;
+ }
+ if (s->z_err < 0) err = s->z_err;
+
+ TRYFREE(s->inbuf);
+ TRYFREE(s->outbuf);
+ TRYFREE(s->path);
+ TRYFREE(s);
+ return err;
+}
+
+/* ===========================================================================
+ Reads the given number of uncompressed bytes from the compressed file.
+ gzread returns the number of bytes actually read (0 for end of file).
+*/
+int ZEXPORT gzread (file, buf, len)
+ gzFile file;
+ voidp buf;
+ unsigned len;
+{
+ gz_stream *s = (gz_stream*)file;
+ Bytef *start = (Bytef*)buf; /* starting point for crc computation */
+ Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
+
+ if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
+
+ if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
+ if (s->z_err == Z_STREAM_END) return 0; /* EOF */
+
+ next_out = (Byte*)buf;
+ s->stream.next_out = (Bytef*)buf;
+ s->stream.avail_out = len;
+
+ if (s->stream.avail_out && s->back != EOF) {
+ *next_out++ = s->back;
+ s->stream.next_out++;
+ s->stream.avail_out--;
+ s->back = EOF;
+ s->out++;
+ start++;
+ if (s->last) {
+ s->z_err = Z_STREAM_END;
+ return 1;
+ }
+ }
+
+ while (s->stream.avail_out != 0) {
+
+ if (s->transparent) {
+ /* Copy first the lookahead bytes: */
+ uInt n = s->stream.avail_in;
+ if (n > s->stream.avail_out) n = s->stream.avail_out;
+ if (n > 0) {
+ zmemcpy(s->stream.next_out, s->stream.next_in, n);
+ next_out += n;
+ s->stream.next_out = next_out;
+ s->stream.next_in += n;
+ s->stream.avail_out -= n;
+ s->stream.avail_in -= n;
+ }
+ if (s->stream.avail_out > 0) {
+ s->stream.avail_out -=
+ (uInt)fread(next_out, 1, s->stream.avail_out, s->file);
+ }
+ len -= s->stream.avail_out;
+ s->in += len;
+ s->out += len;
+ if (len == 0) s->z_eof = 1;
+ return (int)len;
+ }
+ if (s->stream.avail_in == 0 && !s->z_eof) {
+
+ errno = 0;
+ s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+ if (s->stream.avail_in == 0) {
+ s->z_eof = 1;
+ if (ferror(s->file)) {
+ s->z_err = Z_ERRNO;
+ break;
+ }
+ }
+ s->stream.next_in = s->inbuf;
+ }
+ s->in += s->stream.avail_in;
+ s->out += s->stream.avail_out;
+ s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
+ s->in -= s->stream.avail_in;
+ s->out -= s->stream.avail_out;
+
+ if (s->z_err == Z_STREAM_END) {
+ /* Check CRC and original size */
+ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+ start = s->stream.next_out;
+
+ if (getLong(s) != s->crc) {
+ s->z_err = Z_DATA_ERROR;
+ } else {
+ (void)getLong(s);
+ /* The uncompressed length returned by above getlong() may be
+ * different from s->out in case of concatenated .gz files.
+ * Check for such files:
+ */
+ check_header(s);
+ if (s->z_err == Z_OK) {
+ inflateReset(&(s->stream));
+ s->crc = crc32(0L, Z_NULL, 0);
+ }
+ }
+ }
+ if (s->z_err != Z_OK || s->z_eof) break;
+ }
+ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+
+ if (len == s->stream.avail_out &&
+ (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO))
+ return -1;
+ return (int)(len - s->stream.avail_out);
+}
+
+
+/* ===========================================================================
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+int ZEXPORT gzgetc(file)
+ gzFile file;
+{
+ unsigned char c;
+
+ return gzread(file, &c, 1) == 1 ? c : -1;
+}
+
+
+/* ===========================================================================
+ Push one byte back onto the stream.
+*/
+int ZEXPORT gzungetc(c, file)
+ int c;
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF;
+ s->back = c;
+ s->out--;
+ s->last = (s->z_err == Z_STREAM_END);
+ if (s->last) s->z_err = Z_OK;
+ s->z_eof = 0;
+ return c;
+}
+
+
+/* ===========================================================================
+ Reads bytes from the compressed file until len-1 characters are
+ read, or a newline character is read and transferred to buf, or an
+ end-of-file condition is encountered. The string is then terminated
+ with a null character.
+ gzgets returns buf, or Z_NULL in case of error.
+
+ The current implementation is not optimized at all.
+*/
+char * ZEXPORT gzgets(file, buf, len)
+ gzFile file;
+ char *buf;
+ int len;
+{
+ char *b = buf;
+ if (buf == Z_NULL || len <= 0) return Z_NULL;
+
+ while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ;
+ *buf = '\0';
+ return b == buf && len > 0 ? Z_NULL : b;
+}
+
+
+#ifndef NO_GZCOMPRESS
+/* ===========================================================================
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of bytes actually written (0 in case of error).
+*/
+int ZEXPORT gzwrite (file, buf, len)
+ gzFile file;
+ voidpc buf;
+ unsigned len;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ s->stream.next_in = (Bytef*)buf;
+ s->stream.avail_in = len;
+
+ while (s->stream.avail_in != 0) {
+
+ if (s->stream.avail_out == 0) {
+
+ s->stream.next_out = s->outbuf;
+ if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+ s->z_err = Z_ERRNO;
+ break;
+ }
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+ s->in += s->stream.avail_in;
+ s->out += s->stream.avail_out;
+ s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
+ s->in -= s->stream.avail_in;
+ s->out -= s->stream.avail_out;
+ if (s->z_err != Z_OK) break;
+ }
+ s->crc = crc32(s->crc, (const Bytef *)buf, len);
+
+ return (int)(len - s->stream.avail_in);
+}
+
+
+/* ===========================================================================
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error).
+*/
+#ifdef STDC
+#include <stdarg.h>
+
+int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...)
+{
+ char buf[Z_PRINTF_BUFSIZE];
+ va_list va;
+ int len;
+
+ buf[sizeof(buf) - 1] = 0;
+ va_start(va, format);
+#ifdef NO_vsnprintf
+# ifdef HAS_vsprintf_void
+ (void)vsprintf(buf, format, va);
+ va_end(va);
+ for (len = 0; len < sizeof(buf); len++)
+ if (buf[len] == 0) break;
+# else
+ len = vsprintf(buf, format, va);
+ va_end(va);
+# endif
+#else
+# ifdef HAS_vsnprintf_void
+ (void)vsnprintf(buf, sizeof(buf), format, va);
+ va_end(va);
+ len = strlen(buf);
+# else
+ len = vsnprintf(buf, sizeof(buf), format, va);
+ va_end(va);
+# endif
+#endif
+ if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0)
+ return 0;
+ return gzwrite(file, buf, (unsigned)len);
+}
+#else /* not ANSI C */
+
+int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+ gzFile file;
+ const char *format;
+ int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+{
+ char buf[Z_PRINTF_BUFSIZE];
+ int len;
+
+ buf[sizeof(buf) - 1] = 0;
+#ifdef NO_snprintf
+# ifdef HAS_sprintf_void
+ sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+ for (len = 0; len < sizeof(buf); len++)
+ if (buf[len] == 0) break;
+# else
+ len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+# endif
+#else
+# ifdef HAS_snprintf_void
+ snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+ len = strlen(buf);
+# else
+ len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+ a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+# endif
+#endif
+ if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0)
+ return 0;
+ return gzwrite(file, buf, len);
+}
+#endif
+
+/* ===========================================================================
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+int ZEXPORT gzputc(file, c)
+ gzFile file;
+ int c;
+{
+ unsigned char cc = (unsigned char) c; /* required for big endian systems */
+
+ return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1;
+}
+
+
+/* ===========================================================================
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+int ZEXPORT gzputs(file, s)
+ gzFile file;
+ const char *s;
+{
+ return gzwrite(file, (char*)s, (unsigned)strlen(s));
+}
+
+
+/* ===========================================================================
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function.
+*/
+local int do_flush (file, flush)
+ gzFile file;
+ int flush;
+{
+ uInt len;
+ int done = 0;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ s->stream.avail_in = 0; /* should be zero already anyway */
+
+ for (;;) {
+ len = Z_BUFSIZE - s->stream.avail_out;
+
+ if (len != 0) {
+ if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) {
+ s->z_err = Z_ERRNO;
+ return Z_ERRNO;
+ }
+ s->stream.next_out = s->outbuf;
+ s->stream.avail_out = Z_BUFSIZE;
+ }
+ if (done) break;
+ s->out += s->stream.avail_out;
+ s->z_err = deflate(&(s->stream), flush);
+ s->out -= s->stream.avail_out;
+
+ /* Ignore the second of two consecutive flushes: */
+ if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
+
+ /* deflate has finished flushing only when it hasn't used up
+ * all the available space in the output buffer:
+ */
+ done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
+
+ if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
+ }
+ return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+
+int ZEXPORT gzflush (file, flush)
+ gzFile file;
+ int flush;
+{
+ gz_stream *s = (gz_stream*)file;
+ int err = do_flush (file, flush);
+
+ if (err) return err;
+ fflush(s->file);
+ return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+#endif /* NO_GZCOMPRESS */
+
+/* ===========================================================================
+ Sets the starting position for the next gzread or gzwrite on the given
+ compressed file. The offset represents a number of bytes in the
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error.
+ SEEK_END is not implemented, returns error.
+ In this version of the library, gzseek can be extremely slow.
+*/
+z_off_t ZEXPORT gzseek (file, offset, whence)
+ gzFile file;
+ z_off_t offset;
+ int whence;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || whence == SEEK_END ||
+ s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) {
+ return -1L;
+ }
+
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ return -1L;
+#else
+ if (whence == SEEK_SET) {
+ offset -= s->in;
+ }
+ if (offset < 0) return -1L;
+
+ /* At this point, offset is the number of zero bytes to write. */
+ if (s->inbuf == Z_NULL) {
+ s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */
+ if (s->inbuf == Z_NULL) return -1L;
+ zmemzero(s->inbuf, Z_BUFSIZE);
+ }
+ while (offset > 0) {
+ uInt size = Z_BUFSIZE;
+ if (offset < Z_BUFSIZE) size = (uInt)offset;
+
+ size = gzwrite(file, s->inbuf, size);
+ if (size == 0) return -1L;
+
+ offset -= size;
+ }
+ return s->in;
+#endif
+ }
+ /* Rest of function is for reading only */
+
+ /* compute absolute position */
+ if (whence == SEEK_CUR) {
+ offset += s->out;
+ }
+ if (offset < 0) return -1L;
+
+ if (s->transparent) {
+ /* map to fseek */
+ s->back = EOF;
+ s->stream.avail_in = 0;
+ s->stream.next_in = s->inbuf;
+ if (fseek(s->file, offset, SEEK_SET) < 0) return -1L;
+
+ s->in = s->out = offset;
+ return offset;
+ }
+
+ /* For a negative seek, rewind and use positive seek */
+ if (offset >= s->out) {
+ offset -= s->out;
+ } else if (gzrewind(file) < 0) {
+ return -1L;
+ }
+ /* offset is now the number of bytes to skip. */
+
+ if (offset != 0 && s->outbuf == Z_NULL) {
+ s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+ if (s->outbuf == Z_NULL) return -1L;
+ }
+ if (offset && s->back != EOF) {
+ s->back = EOF;
+ s->out++;
+ offset--;
+ if (s->last) s->z_err = Z_STREAM_END;
+ }
+ while (offset > 0) {
+ int size = Z_BUFSIZE;
+ if (offset < Z_BUFSIZE) size = (int)offset;
+
+ size = gzread(file, s->outbuf, (uInt)size);
+ if (size <= 0) return -1L;
+ offset -= size;
+ }
+ return s->out;
+}
+
+/* ===========================================================================
+ Rewinds input file.
+*/
+int ZEXPORT gzrewind (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'r') return -1;
+
+ s->z_err = Z_OK;
+ s->z_eof = 0;
+ s->back = EOF;
+ s->stream.avail_in = 0;
+ s->stream.next_in = s->inbuf;
+ s->crc = crc32(0L, Z_NULL, 0);
+ if (!s->transparent) (void)inflateReset(&s->stream);
+ s->in = 0;
+ s->out = 0;
+ return fseek(s->file, s->start, SEEK_SET);
+}
+
+/* ===========================================================================
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+*/
+z_off_t ZEXPORT gztell (file)
+ gzFile file;
+{
+ return gzseek(file, 0L, SEEK_CUR);
+}
+
+/* ===========================================================================
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+int ZEXPORT gzeof (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ /* With concatenated compressed files that can have embedded
+ * crc trailers, z_eof is no longer the only/best indicator of EOF
+ * on a gz_stream. Handle end-of-stream error explicitly here.
+ */
+ if (s == NULL || s->mode != 'r') return 0;
+ if (s->z_eof) return 1;
+ return s->z_err == Z_STREAM_END;
+}
+
+/* ===========================================================================
+ Returns 1 if reading and doing so transparently, otherwise zero.
+*/
+int ZEXPORT gzdirect (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL || s->mode != 'r') return 0;
+ return s->transparent;
+}
+
+/* ===========================================================================
+ Outputs a long in LSB order to the given file
+*/
+local void putLong (file, x)
+ FILE *file;
+ uLong x;
+{
+ int n;
+ for (n = 0; n < 4; n++) {
+ fputc((int)(x & 0xff), file);
+ x >>= 8;
+ }
+}
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets z_err in case
+ of error.
+*/
+local uLong getLong (s)
+ gz_stream *s;
+{
+ uLong x = (uLong)get_byte(s);
+ int c;
+
+ x += ((uLong)get_byte(s))<<8;
+ x += ((uLong)get_byte(s))<<16;
+ c = get_byte(s);
+ if (c == EOF) s->z_err = Z_DATA_ERROR;
+ x += ((uLong)c)<<24;
+ return x;
+}
+
+/* ===========================================================================
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state.
+*/
+int ZEXPORT gzclose (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) return Z_STREAM_ERROR;
+
+ if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+ return Z_STREAM_ERROR;
+#else
+ if (do_flush (file, Z_FINISH) != Z_OK)
+ return destroy((gz_stream*)file);
+
+ putLong (s->file, s->crc);
+ putLong (s->file, (uLong)(s->in & 0xffffffff));
+#endif
+ }
+ return destroy((gz_stream*)file);
+}
+
+#ifdef STDC
+# define zstrerror(errnum) strerror(errnum)
+#else
+# define zstrerror(errnum) ""
+#endif
+
+/* ===========================================================================
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+const char * ZEXPORT gzerror (file, errnum)
+ gzFile file;
+ int *errnum;
+{
+ char *m;
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) {
+ *errnum = Z_STREAM_ERROR;
+ return (const char*)ERR_MSG(Z_STREAM_ERROR);
+ }
+ *errnum = s->z_err;
+ if (*errnum == Z_OK) return (const char*)"";
+
+ m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg);
+
+ if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err);
+
+ TRYFREE(s->msg);
+ s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3);
+ if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR);
+ strcpy(s->msg, s->path);
+ strcat(s->msg, ": ");
+ strcat(s->msg, m);
+ return (const char*)s->msg;
+}
+
+/* ===========================================================================
+ Clear the error and end-of-file flags, and do the same for the real file.
+*/
+void ZEXPORT gzclearerr (file)
+ gzFile file;
+{
+ gz_stream *s = (gz_stream*)file;
+
+ if (s == NULL) return;
+ if (s->z_err != Z_STREAM_END) s->z_err = Z_OK;
+ s->z_eof = 0;
+ clearerr(s->file);
+}
--- /dev/null
+/* infback.c -- inflate using a call-back interface
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ This code is largely copied from inflate.c. Normally either infback.o or
+ inflate.o would be linked into an application--not both. The interface
+ with inffast.c is retained so that optimized assembler-coded versions of
+ inflate_fast() can be used with either inflate.c or infback.c.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+
+/*
+ strm provides memory allocation functions in zalloc and zfree, or
+ Z_NULL to use the library memory allocation functions.
+
+ windowBits is in the range 8..15, and window is a user-supplied
+ window and output buffer that is 2**windowBits bytes.
+ */
+int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size)
+z_streamp strm;
+int windowBits;
+unsigned char FAR *window;
+const char *version;
+int stream_size;
+{
+ struct inflate_state FAR *state;
+
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != (int)(sizeof(z_stream)))
+ return Z_VERSION_ERROR;
+ if (strm == Z_NULL || window == Z_NULL ||
+ windowBits < 8 || windowBits > 15)
+ return Z_STREAM_ERROR;
+ strm->msg = Z_NULL; /* in case we return an error */
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+ state = (struct inflate_state FAR *)ZALLOC(strm, 1,
+ sizeof(struct inflate_state));
+ if (state == Z_NULL) return Z_MEM_ERROR;
+ Tracev((stderr, "inflate: allocated\n"));
+ strm->state = (struct internal_state FAR *)state;
+ state->dmax = 32768U;
+ state->wbits = windowBits;
+ state->wsize = 1U << windowBits;
+ state->window = window;
+ state->write = 0;
+ state->whave = 0;
+ return Z_OK;
+}
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+ static int virgin = 1;
+ static code *lenfix, *distfix;
+ static code fixed[544];
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ unsigned sym, bits;
+ static code *next;
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) state->lens[sym++] = 8;
+ while (sym < 256) state->lens[sym++] = 9;
+ while (sym < 280) state->lens[sym++] = 7;
+ while (sym < 288) state->lens[sym++] = 8;
+ next = fixed;
+ lenfix = next;
+ bits = 9;
+ inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) state->lens[sym++] = 5;
+ distfix = next;
+ bits = 5;
+ inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+ /* do this just once */
+ virgin = 0;
+ }
+#else /* !BUILDFIXED */
+# include "inffixed.h"
+#endif /* BUILDFIXED */
+ state->lencode = lenfix;
+ state->lenbits = 9;
+ state->distcode = distfix;
+ state->distbits = 5;
+}
+
+/* Macros for inflateBack(): */
+
+/* Load returned state from inflate_fast() */
+#define LOAD() \
+ do { \
+ put = strm->next_out; \
+ left = strm->avail_out; \
+ next = strm->next_in; \
+ have = strm->avail_in; \
+ hold = state->hold; \
+ bits = state->bits; \
+ } while (0)
+
+/* Set state from registers for inflate_fast() */
+#define RESTORE() \
+ do { \
+ strm->next_out = put; \
+ strm->avail_out = left; \
+ strm->next_in = next; \
+ strm->avail_in = have; \
+ state->hold = hold; \
+ state->bits = bits; \
+ } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+ do { \
+ hold = 0; \
+ bits = 0; \
+ } while (0)
+
+/* Assure that some input is available. If input is requested, but denied,
+ then return a Z_BUF_ERROR from inflateBack(). */
+#define PULL() \
+ do { \
+ if (have == 0) { \
+ have = in(in_desc, &next); \
+ if (have == 0) { \
+ next = Z_NULL; \
+ ret = Z_BUF_ERROR; \
+ goto inf_leave; \
+ } \
+ } \
+ } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflateBack()
+ with an error if there is no input available. */
+#define PULLBYTE() \
+ do { \
+ PULL(); \
+ have--; \
+ hold += (unsigned long)(*next++) << bits; \
+ bits += 8; \
+ } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator. If there is
+ not enough available input to do that, then return from inflateBack() with
+ an error. */
+#define NEEDBITS(n) \
+ do { \
+ while (bits < (unsigned)(n)) \
+ PULLBYTE(); \
+ } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+ ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+ do { \
+ hold >>= (n); \
+ bits -= (unsigned)(n); \
+ } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+ do { \
+ hold >>= bits & 7; \
+ bits -= bits & 7; \
+ } while (0)
+
+/* Assure that some output space is available, by writing out the window
+ if it's full. If the write fails, return from inflateBack() with a
+ Z_BUF_ERROR. */
+#define ROOM() \
+ do { \
+ if (left == 0) { \
+ put = state->window; \
+ left = state->wsize; \
+ state->whave = left; \
+ if (out(out_desc, put, left)) { \
+ ret = Z_BUF_ERROR; \
+ goto inf_leave; \
+ } \
+ } \
+ } while (0)
+
+/*
+ strm provides the memory allocation functions and window buffer on input,
+ and provides information on the unused input on return. For Z_DATA_ERROR
+ returns, strm will also provide an error message.
+
+ in() and out() are the call-back input and output functions. When
+ inflateBack() needs more input, it calls in(). When inflateBack() has
+ filled the window with output, or when it completes with data in the
+ window, it calls out() to write out the data. The application must not
+ change the provided input until in() is called again or inflateBack()
+ returns. The application must not change the window/output buffer until
+ inflateBack() returns.
+
+ in() and out() are called with a descriptor parameter provided in the
+ inflateBack() call. This parameter can be a structure that provides the
+ information required to do the read or write, as well as accumulated
+ information on the input and output such as totals and check values.
+
+ in() should return zero on failure. out() should return non-zero on
+ failure. If either in() or out() fails, than inflateBack() returns a
+ Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it
+ was in() or out() that caused in the error. Otherwise, inflateBack()
+ returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format
+ error, or Z_MEM_ERROR if it could not allocate memory for the state.
+ inflateBack() can also return Z_STREAM_ERROR if the input parameters
+ are not correct, i.e. strm is Z_NULL or the state was not initialized.
+ */
+int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc)
+z_streamp strm;
+in_func in;
+void FAR *in_desc;
+out_func out;
+void FAR *out_desc;
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *next; /* next input */
+ unsigned char FAR *put; /* next output */
+ unsigned have, left; /* available input and output */
+ unsigned long hold; /* bit buffer */
+ unsigned bits; /* bits in bit buffer */
+ unsigned copy; /* number of stored or match bytes to copy */
+ unsigned char FAR *from; /* where to copy match bytes from */
+ code this; /* current decoding table entry */
+ code last; /* parent table entry */
+ unsigned len; /* length to copy for repeats, bits to drop */
+ int ret; /* return code */
+ static const unsigned short order[19] = /* permutation of code lengths */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ /* Check that the strm exists and that the state was initialized */
+ if (strm == Z_NULL || strm->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* Reset the state */
+ strm->msg = Z_NULL;
+ state->mode = TYPE;
+ state->last = 0;
+ state->whave = 0;
+ next = strm->next_in;
+ have = next != Z_NULL ? strm->avail_in : 0;
+ hold = 0;
+ bits = 0;
+ put = state->window;
+ left = state->wsize;
+
+ /* Inflate until end of block marked as last */
+ for (;;)
+ switch (state->mode) {
+ case TYPE:
+ /* determine and dispatch block type */
+ if (state->last) {
+ BYTEBITS();
+ state->mode = DONE;
+ break;
+ }
+ NEEDBITS(3);
+ state->last = BITS(1);
+ DROPBITS(1);
+ switch (BITS(2)) {
+ case 0: /* stored block */
+ Tracev((stderr, "inflate: stored block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = LEN; /* decode codes */
+ break;
+ case 2: /* dynamic block */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = TABLE;
+ break;
+ case 3:
+ strm->msg = (char *)"invalid block type";
+ state->mode = BAD;
+ }
+ DROPBITS(2);
+ break;
+
+ case STORED:
+ /* get and verify stored block length */
+ BYTEBITS(); /* go to byte boundary */
+ NEEDBITS(32);
+ if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+ strm->msg = (char *)"invalid stored block lengths";
+ state->mode = BAD;
+ break;
+ }
+ state->length = (unsigned)hold & 0xffff;
+ Tracev((stderr, "inflate: stored length %u\n",
+ state->length));
+ INITBITS();
+
+ /* copy stored block from input to output */
+ while (state->length != 0) {
+ copy = state->length;
+ PULL();
+ ROOM();
+ if (copy > have) copy = have;
+ if (copy > left) copy = left;
+ zmemcpy(put, next, copy);
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state->length -= copy;
+ }
+ Tracev((stderr, "inflate: stored end\n"));
+ state->mode = TYPE;
+ break;
+
+ case TABLE:
+ /* get dynamic table entries descriptor */
+ NEEDBITS(14);
+ state->nlen = BITS(5) + 257;
+ DROPBITS(5);
+ state->ndist = BITS(5) + 1;
+ DROPBITS(5);
+ state->ncode = BITS(4) + 4;
+ DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+ if (state->nlen > 286 || state->ndist > 30) {
+ strm->msg = (char *)"too many length or distance symbols";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracev((stderr, "inflate: table sizes ok\n"));
+
+ /* get code length code lengths (not a typo) */
+ state->have = 0;
+ while (state->have < state->ncode) {
+ NEEDBITS(3);
+ state->lens[order[state->have++]] = (unsigned short)BITS(3);
+ DROPBITS(3);
+ }
+ while (state->have < 19)
+ state->lens[order[state->have++]] = 0;
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 7;
+ ret = inflate_table(CODES, state->lens, 19, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid code lengths set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: code lengths ok\n"));
+
+ /* get length and distance code code lengths */
+ state->have = 0;
+ while (state->have < state->nlen + state->ndist) {
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.val < 16) {
+ NEEDBITS(this.bits);
+ DROPBITS(this.bits);
+ state->lens[state->have++] = this.val;
+ }
+ else {
+ if (this.val == 16) {
+ NEEDBITS(this.bits + 2);
+ DROPBITS(this.bits);
+ if (state->have == 0) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ len = (unsigned)(state->lens[state->have - 1]);
+ copy = 3 + BITS(2);
+ DROPBITS(2);
+ }
+ else if (this.val == 17) {
+ NEEDBITS(this.bits + 3);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 3 + BITS(3);
+ DROPBITS(3);
+ }
+ else {
+ NEEDBITS(this.bits + 7);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 11 + BITS(7);
+ DROPBITS(7);
+ }
+ if (state->have + copy > state->nlen + state->ndist) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ while (copy--)
+ state->lens[state->have++] = (unsigned short)len;
+ }
+ }
+
+ /* handle error breaks in while */
+ if (state->mode == BAD) break;
+
+ /* build code tables */
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 9;
+ ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid literal/lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->distcode = (code const FAR *)(state->next);
+ state->distbits = 6;
+ ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+ &(state->next), &(state->distbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid distances set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: codes ok\n"));
+ state->mode = LEN;
+
+ case LEN:
+ /* use inflate_fast() if we have enough input and output */
+ if (have >= 6 && left >= 258) {
+ RESTORE();
+ if (state->whave < state->wsize)
+ state->whave = state->wsize - left;
+ inflate_fast(strm, state->wsize);
+ LOAD();
+ break;
+ }
+
+ /* get a literal, length, or end-of-block code */
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.op && (this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->lencode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ state->length = (unsigned)this.val;
+
+ /* process literal */
+ if (this.op == 0) {
+ Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", this.val));
+ ROOM();
+ *put++ = (unsigned char)(state->length);
+ left--;
+ state->mode = LEN;
+ break;
+ }
+
+ /* process end of block */
+ if (this.op & 32) {
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+
+ /* invalid code */
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+
+ /* length code -- get extra bits, if any */
+ state->extra = (unsigned)(this.op) & 15;
+ if (state->extra != 0) {
+ NEEDBITS(state->extra);
+ state->length += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ Tracevv((stderr, "inflate: length %u\n", state->length));
+
+ /* get distance code */
+ for (;;) {
+ this = state->distcode[BITS(state->distbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if ((this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->distcode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ state->offset = (unsigned)this.val;
+
+ /* get distance extra bits, if any */
+ state->extra = (unsigned)(this.op) & 15;
+ if (state->extra != 0) {
+ NEEDBITS(state->extra);
+ state->offset += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ if (state->offset > state->wsize - (state->whave < state->wsize ?
+ left : 0)) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ Tracevv((stderr, "inflate: distance %u\n", state->offset));
+
+ /* copy match from window to output */
+ do {
+ ROOM();
+ copy = state->wsize - state->offset;
+ if (copy < left) {
+ from = put + copy;
+ copy = left - copy;
+ }
+ else {
+ from = put - state->offset;
+ copy = left;
+ }
+ if (copy > state->length) copy = state->length;
+ state->length -= copy;
+ left -= copy;
+ do {
+ *put++ = *from++;
+ } while (--copy);
+ } while (state->length != 0);
+ break;
+
+ case DONE:
+ /* inflate stream terminated properly -- write leftover output */
+ ret = Z_STREAM_END;
+ if (left < state->wsize) {
+ if (out(out_desc, state->window, state->wsize - left))
+ ret = Z_BUF_ERROR;
+ }
+ goto inf_leave;
+
+ case BAD:
+ ret = Z_DATA_ERROR;
+ goto inf_leave;
+
+ default: /* can't happen, but makes compilers happy */
+ ret = Z_STREAM_ERROR;
+ goto inf_leave;
+ }
+
+ /* Return unused input */
+ inf_leave:
+ strm->next_in = next;
+ strm->avail_in = have;
+ return ret;
+}
+
+int ZEXPORT inflateBackEnd(strm)
+z_streamp strm;
+{
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
--- /dev/null
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifndef ASMINF
+
+/* Allow machine dependent optimization for post-increment or pre-increment.
+ Based on testing to date,
+ Pre-increment preferred for:
+ - PowerPC G3 (Adler)
+ - MIPS R5000 (Randers-Pehrson)
+ Post-increment preferred for:
+ - none
+ No measurable difference:
+ - Pentium III (Anderson)
+ - M68060 (Nikl)
+ */
+#ifdef POSTINC
+# define OFF 0
+# define PUP(a) *(a)++
+#else
+# define OFF 1
+# define PUP(a) *++(a)
+#endif
+
+/*
+ Decode literal, length, and distance codes and write out the resulting
+ literal and match bytes until either not enough input or output is
+ available, an end-of-block is encountered, or a data error is encountered.
+ When large enough input and output buffers are supplied to inflate(), for
+ example, a 16K input buffer and a 64K output buffer, more than 95% of the
+ inflate execution time is spent in this routine.
+
+ Entry assumptions:
+
+ state->mode == LEN
+ strm->avail_in >= 6
+ strm->avail_out >= 258
+ start >= strm->avail_out
+ state->bits < 8
+
+ On return, state->mode is one of:
+
+ LEN -- ran out of enough output space or enough available input
+ TYPE -- reached end of block code, inflate() to interpret next block
+ BAD -- error in block data
+
+ Notes:
+
+ - The maximum input bits used by a length/distance pair is 15 bits for the
+ length code, 5 bits for the length extra, 15 bits for the distance code,
+ and 13 bits for the distance extra. This totals 48 bits, or six bytes.
+ Therefore if strm->avail_in >= 6, then there is enough input to avoid
+ checking for available input while decoding.
+
+ - The maximum bytes that a single length/distance pair can output is 258
+ bytes, which is the maximum length that can be coded. inflate_fast()
+ requires strm->avail_out >= 258 for each loop to avoid checking for
+ output space.
+ */
+void inflate_fast(strm, start)
+z_streamp strm;
+unsigned start; /* inflate()'s starting value for strm->avail_out */
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *in; /* local strm->next_in */
+ unsigned char FAR *last; /* while in < last, enough input available */
+ unsigned char FAR *out; /* local strm->next_out */
+ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */
+ unsigned char FAR *end; /* while out < end, enough space available */
+#ifdef INFLATE_STRICT
+ unsigned dmax; /* maximum distance from zlib header */
+#endif
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned write; /* window write index */
+ unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */
+ unsigned long hold; /* local strm->hold */
+ unsigned bits; /* local strm->bits */
+ code const FAR *lcode; /* local strm->lencode */
+ code const FAR *dcode; /* local strm->distcode */
+ unsigned lmask; /* mask for first level of length codes */
+ unsigned dmask; /* mask for first level of distance codes */
+ code this; /* retrieved table entry */
+ unsigned op; /* code bits, operation, extra bits, or */
+ /* window position, window bytes to copy */
+ unsigned len; /* match length, unused bytes */
+ unsigned dist; /* match distance */
+ unsigned char FAR *from; /* where to copy match from */
+
+ /* copy state to local variables */
+ state = (struct inflate_state FAR *)strm->state;
+ in = strm->next_in - OFF;
+ last = in + (strm->avail_in - 5);
+ out = strm->next_out - OFF;
+ beg = out - (start - strm->avail_out);
+ end = out + (strm->avail_out - 257);
+#ifdef INFLATE_STRICT
+ dmax = state->dmax;
+#endif
+ wsize = state->wsize;
+ whave = state->whave;
+ write = state->write;
+ window = state->window;
+ hold = state->hold;
+ bits = state->bits;
+ lcode = state->lencode;
+ dcode = state->distcode;
+ lmask = (1U << state->lenbits) - 1;
+ dmask = (1U << state->distbits) - 1;
+
+ /* decode literals and length/distances until end-of-block or not enough
+ input data or output space */
+ do {
+ if (bits < 15) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ this = lcode[hold & lmask];
+ dolen:
+ op = (unsigned)(this.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(this.op);
+ if (op == 0) { /* literal */
+ Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", this.val));
+ PUP(out) = (unsigned char)(this.val);
+ }
+ else if (op & 16) { /* length base */
+ len = (unsigned)(this.val);
+ op &= 15; /* number of extra bits */
+ if (op) {
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ len += (unsigned)hold & ((1U << op) - 1);
+ hold >>= op;
+ bits -= op;
+ }
+ Tracevv((stderr, "inflate: length %u\n", len));
+ if (bits < 15) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ this = dcode[hold & dmask];
+ dodist:
+ op = (unsigned)(this.bits);
+ hold >>= op;
+ bits -= op;
+ op = (unsigned)(this.op);
+ if (op & 16) { /* distance base */
+ dist = (unsigned)(this.val);
+ op &= 15; /* number of extra bits */
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ if (bits < op) {
+ hold += (unsigned long)(PUP(in)) << bits;
+ bits += 8;
+ }
+ }
+ dist += (unsigned)hold & ((1U << op) - 1);
+#ifdef INFLATE_STRICT
+ if (dist > dmax) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ hold >>= op;
+ bits -= op;
+ Tracevv((stderr, "inflate: distance %u\n", dist));
+ op = (unsigned)(out - beg); /* max distance in output */
+ if (dist > op) { /* see if copy from window */
+ op = dist - op; /* distance back in window */
+ if (op > whave) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ from = window - OFF;
+ if (write == 0) { /* very common case */
+ from += wsize - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ else if (write < op) { /* wrap around window */
+ from += wsize + write - op;
+ op -= write;
+ if (op < len) { /* some from end of window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = window - OFF;
+ if (write < len) { /* some from start of window */
+ op = write;
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ }
+ else { /* contiguous in window */
+ from += write - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ PUP(out) = PUP(from);
+ } while (--op);
+ from = out - dist; /* rest from output */
+ }
+ }
+ while (len > 2) {
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ len -= 3;
+ }
+ if (len) {
+ PUP(out) = PUP(from);
+ if (len > 1)
+ PUP(out) = PUP(from);
+ }
+ }
+ else {
+ from = out - dist; /* copy direct from output */
+ do { /* minimum length is three */
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ PUP(out) = PUP(from);
+ len -= 3;
+ } while (len > 2);
+ if (len) {
+ PUP(out) = PUP(from);
+ if (len > 1)
+ PUP(out) = PUP(from);
+ }
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level distance code */
+ this = dcode[this.val + (hold & ((1U << op) - 1))];
+ goto dodist;
+ }
+ else {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ }
+ else if ((op & 64) == 0) { /* 2nd level length code */
+ this = lcode[this.val + (hold & ((1U << op) - 1))];
+ goto dolen;
+ }
+ else if (op & 32) { /* end-of-block */
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+ else {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ } while (in < last && out < end);
+
+ /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+ len = bits >> 3;
+ in -= len;
+ bits -= len << 3;
+ hold &= (1U << bits) - 1;
+
+ /* update state and return */
+ strm->next_in = in + OFF;
+ strm->next_out = out + OFF;
+ strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+ strm->avail_out = (unsigned)(out < end ?
+ 257 + (end - out) : 257 - (out - end));
+ state->hold = hold;
+ state->bits = bits;
+ return;
+}
+
+/*
+ inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+ - Using bit fields for code structure
+ - Different op definition to avoid & for extra bits (do & for table bits)
+ - Three separate decoding do-loops for direct, window, and write == 0
+ - Special case for distance > 1 copies to do overlapped load and store copy
+ - Explicit branch predictions (based on measured branch probabilities)
+ - Deferring match copy and interspersed it with decoding subsequent codes
+ - Swapping literal/length else
+ - Swapping window/direct else
+ - Larger unrolled copy loops (three is about right)
+ - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
--- /dev/null
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+void inflate_fast OF((z_streamp strm, unsigned start));
--- /dev/null
+ /* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by makefixed().
+ */
+
+ /* WARNING: this file should *not* be used by applications. It
+ is part of the implementation of the compression library and
+ is subject to change. Applications should only use zlib.h.
+ */
+
+ static const code lenfix[512] = {
+ {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+ {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+ {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+ {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+ {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+ {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+ {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+ {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+ {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+ {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+ {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+ {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+ {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+ {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+ {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+ {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+ {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+ {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+ {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+ {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+ {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+ {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+ {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+ {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+ {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+ {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+ {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+ {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+ {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+ {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+ {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+ {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+ {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+ {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+ {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+ {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+ {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+ {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+ {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+ {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+ {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+ {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+ {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+ {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+ {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+ {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+ {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+ {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+ {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+ {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+ {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+ {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+ {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+ {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+ {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+ {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+ {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+ {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+ {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+ {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+ {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+ {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+ {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+ {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+ {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+ {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+ {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+ {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+ {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+ {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+ {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+ {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+ {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+ {0,9,255}
+ };
+
+ static const code distfix[32] = {
+ {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+ {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+ {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+ {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+ {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+ {22,5,193},{64,5,0}
+ };
--- /dev/null
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * Change history:
+ *
+ * 1.2.beta0 24 Nov 2002
+ * - First version -- complete rewrite of inflate to simplify code, avoid
+ * creation of window when not needed, minimize use of window when it is
+ * needed, make inffast.c even faster, implement gzip decoding, and to
+ * improve code readability and style over the previous zlib inflate code
+ *
+ * 1.2.beta1 25 Nov 2002
+ * - Use pointers for available input and output checking in inffast.c
+ * - Remove input and output counters in inffast.c
+ * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
+ * - Remove unnecessary second byte pull from length extra in inffast.c
+ * - Unroll direct copy to three copies per loop in inffast.c
+ *
+ * 1.2.beta2 4 Dec 2002
+ * - Change external routine names to reduce potential conflicts
+ * - Correct filename to inffixed.h for fixed tables in inflate.c
+ * - Make hbuf[] unsigned char to match parameter type in inflate.c
+ * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
+ * to avoid negation problem on Alphas (64 bit) in inflate.c
+ *
+ * 1.2.beta3 22 Dec 2002
+ * - Add comments on state->bits assertion in inffast.c
+ * - Add comments on op field in inftrees.h
+ * - Fix bug in reuse of allocated window after inflateReset()
+ * - Remove bit fields--back to byte structure for speed
+ * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
+ * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
+ * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
+ * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
+ * - Use local copies of stream next and avail values, as well as local bit
+ * buffer and bit count in inflate()--for speed when inflate_fast() not used
+ *
+ * 1.2.beta4 1 Jan 2003
+ * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
+ * - Move a comment on output buffer sizes from inffast.c to inflate.c
+ * - Add comments in inffast.c to introduce the inflate_fast() routine
+ * - Rearrange window copies in inflate_fast() for speed and simplification
+ * - Unroll last copy for window match in inflate_fast()
+ * - Use local copies of window variables in inflate_fast() for speed
+ * - Pull out common write == 0 case for speed in inflate_fast()
+ * - Make op and len in inflate_fast() unsigned for consistency
+ * - Add FAR to lcode and dcode declarations in inflate_fast()
+ * - Simplified bad distance check in inflate_fast()
+ * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
+ * source file infback.c to provide a call-back interface to inflate for
+ * programs like gzip and unzip -- uses window as output buffer to avoid
+ * window copying
+ *
+ * 1.2.beta5 1 Jan 2003
+ * - Improved inflateBack() interface to allow the caller to provide initial
+ * input in strm.
+ * - Fixed stored blocks bug in inflateBack()
+ *
+ * 1.2.beta6 4 Jan 2003
+ * - Added comments in inffast.c on effectiveness of POSTINC
+ * - Typecasting all around to reduce compiler warnings
+ * - Changed loops from while (1) or do {} while (1) to for (;;), again to
+ * make compilers happy
+ * - Changed type of window in inflateBackInit() to unsigned char *
+ *
+ * 1.2.beta7 27 Jan 2003
+ * - Changed many types to unsigned or unsigned short to avoid warnings
+ * - Added inflateCopy() function
+ *
+ * 1.2.0 9 Mar 2003
+ * - Changed inflateBack() interface to provide separate opaque descriptors
+ * for the in() and out() functions
+ * - Changed inflateBack() argument and in_func typedef to swap the length
+ * and buffer address return values for the input function
+ * - Check next_in and next_out for Z_NULL on entry to inflate()
+ *
+ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifdef MAKEFIXED
+# ifndef BUILDFIXED
+# define BUILDFIXED
+# endif
+#endif
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+local int updatewindow OF((z_streamp strm, unsigned out));
+#ifdef BUILDFIXED
+ void makefixed OF((void));
+#endif
+local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf,
+ unsigned len));
+
+int ZEXPORT inflateReset(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ strm->total_in = strm->total_out = state->total = 0;
+ strm->msg = Z_NULL;
+ strm->adler = 1; /* to support ill-conceived Java test suite */
+ state->mode = HEAD;
+ state->last = 0;
+ state->havedict = 0;
+ state->dmax = 32768U;
+ state->head = Z_NULL;
+ state->wsize = 0;
+ state->whave = 0;
+ state->write = 0;
+ state->hold = 0;
+ state->bits = 0;
+ state->lencode = state->distcode = state->next = state->codes;
+ Tracev((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflatePrime(strm, bits, value)
+z_streamp strm;
+int bits;
+int value;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR;
+ value &= (1L << bits) - 1;
+ state->hold += value << state->bits;
+ state->bits += bits;
+ return Z_OK;
+}
+
+int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
+z_streamp strm;
+int windowBits;
+const char *version;
+int stream_size;
+{
+ struct inflate_state FAR *state;
+
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != (int)(sizeof(z_stream)))
+ return Z_VERSION_ERROR;
+ if (strm == Z_NULL) return Z_STREAM_ERROR;
+ strm->msg = Z_NULL; /* in case we return an error */
+ if (strm->zalloc == (alloc_func)0) {
+ strm->zalloc = zcalloc;
+ strm->opaque = (voidpf)0;
+ }
+ if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+ state = (struct inflate_state FAR *)
+ ZALLOC(strm, 1, sizeof(struct inflate_state));
+ if (state == Z_NULL) return Z_MEM_ERROR;
+ Tracev((stderr, "inflate: allocated\n"));
+ strm->state = (struct internal_state FAR *)state;
+ if (windowBits < 0) {
+ state->wrap = 0;
+ windowBits = -windowBits;
+ }
+ else {
+ state->wrap = (windowBits >> 4) + 1;
+#ifdef GUNZIP
+ if (windowBits < 48) windowBits &= 15;
+#endif
+ }
+ if (windowBits < 8 || windowBits > 15) {
+ ZFREE(strm, state);
+ strm->state = Z_NULL;
+ return Z_STREAM_ERROR;
+ }
+ state->wbits = (unsigned)windowBits;
+ state->window = Z_NULL;
+ return inflateReset(strm);
+}
+
+int ZEXPORT inflateInit_(strm, version, stream_size)
+z_streamp strm;
+const char *version;
+int stream_size;
+{
+ return inflateInit2_(strm, DEF_WBITS, version, stream_size);
+}
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+ static int virgin = 1;
+ static code *lenfix, *distfix;
+ static code fixed[544];
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ unsigned sym, bits;
+ static code *next;
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) state->lens[sym++] = 8;
+ while (sym < 256) state->lens[sym++] = 9;
+ while (sym < 280) state->lens[sym++] = 7;
+ while (sym < 288) state->lens[sym++] = 8;
+ next = fixed;
+ lenfix = next;
+ bits = 9;
+ inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) state->lens[sym++] = 5;
+ distfix = next;
+ bits = 5;
+ inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+ /* do this just once */
+ virgin = 0;
+ }
+#else /* !BUILDFIXED */
+# include "inffixed.h"
+#endif /* BUILDFIXED */
+ state->lencode = lenfix;
+ state->lenbits = 9;
+ state->distcode = distfix;
+ state->distbits = 5;
+}
+
+#ifdef MAKEFIXED
+#include <stdio.h>
+
+/*
+ Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also
+ defines BUILDFIXED, so the tables are built on the fly. makefixed() writes
+ those tables to stdout, which would be piped to inffixed.h. A small program
+ can simply call makefixed to do this:
+
+ void makefixed(void);
+
+ int main(void)
+ {
+ makefixed();
+ return 0;
+ }
+
+ Then that can be linked with zlib built with MAKEFIXED defined and run:
+
+ a.out > inffixed.h
+ */
+void makefixed()
+{
+ unsigned low, size;
+ struct inflate_state state;
+
+ fixedtables(&state);
+ puts(" /* inffixed.h -- table for decoding fixed codes");
+ puts(" * Generated automatically by makefixed().");
+ puts(" */");
+ puts("");
+ puts(" /* WARNING: this file should *not* be used by applications.");
+ puts(" It is part of the implementation of this library and is");
+ puts(" subject to change. Applications should only use zlib.h.");
+ puts(" */");
+ puts("");
+ size = 1U << 9;
+ printf(" static const code lenfix[%u] = {", size);
+ low = 0;
+ for (;;) {
+ if ((low % 7) == 0) printf("\n ");
+ printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits,
+ state.lencode[low].val);
+ if (++low == size) break;
+ putchar(',');
+ }
+ puts("\n };");
+ size = 1U << 5;
+ printf("\n static const code distfix[%u] = {", size);
+ low = 0;
+ for (;;) {
+ if ((low % 6) == 0) printf("\n ");
+ printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
+ state.distcode[low].val);
+ if (++low == size) break;
+ putchar(',');
+ }
+ puts("\n };");
+}
+#endif /* MAKEFIXED */
+
+/*
+ Update the window with the last wsize (normally 32K) bytes written before
+ returning. If window does not exist yet, create it. This is only called
+ when a window is already in use, or when output has been written during this
+ inflate call, but the end of the deflate stream has not been reached yet.
+ It is also called to create a window for dictionary data when a dictionary
+ is loaded.
+
+ Providing output buffers larger than 32K to inflate() should provide a speed
+ advantage, since only the last 32K of output is copied to the sliding window
+ upon return from inflate(), and since all distances after the first 32K of
+ output will fall in the output data, making match copies simpler and faster.
+ The advantage may be dependent on the size of the processor's data caches.
+ */
+local int updatewindow(strm, out)
+z_streamp strm;
+unsigned out;
+{
+ struct inflate_state FAR *state;
+ unsigned copy, dist;
+
+ state = (struct inflate_state FAR *)strm->state;
+
+ /* if it hasn't been done already, allocate space for the window */
+ if (state->window == Z_NULL) {
+ state->window = (unsigned char FAR *)
+ ZALLOC(strm, 1U << state->wbits,
+ sizeof(unsigned char));
+ if (state->window == Z_NULL) return 1;
+ }
+
+ /* if window not in use yet, initialize */
+ if (state->wsize == 0) {
+ state->wsize = 1U << state->wbits;
+ state->write = 0;
+ state->whave = 0;
+ }
+
+ /* copy state->wsize or less output bytes into the circular window */
+ copy = out - strm->avail_out;
+ if (copy >= state->wsize) {
+ zmemcpy(state->window, strm->next_out - state->wsize, state->wsize);
+ state->write = 0;
+ state->whave = state->wsize;
+ }
+ else {
+ dist = state->wsize - state->write;
+ if (dist > copy) dist = copy;
+ zmemcpy(state->window + state->write, strm->next_out - copy, dist);
+ copy -= dist;
+ if (copy) {
+ zmemcpy(state->window, strm->next_out - copy, copy);
+ state->write = copy;
+ state->whave = state->wsize;
+ }
+ else {
+ state->write += dist;
+ if (state->write == state->wsize) state->write = 0;
+ if (state->whave < state->wsize) state->whave += dist;
+ }
+ }
+ return 0;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#ifdef GUNZIP
+# define UPDATE(check, buf, len) \
+ (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
+#else
+# define UPDATE(check, buf, len) adler32(check, buf, len)
+#endif
+
+/* check macros for header crc */
+#ifdef GUNZIP
+# define CRC2(check, word) \
+ do { \
+ hbuf[0] = (unsigned char)(word); \
+ hbuf[1] = (unsigned char)((word) >> 8); \
+ check = crc32(check, hbuf, 2); \
+ } while (0)
+
+# define CRC4(check, word) \
+ do { \
+ hbuf[0] = (unsigned char)(word); \
+ hbuf[1] = (unsigned char)((word) >> 8); \
+ hbuf[2] = (unsigned char)((word) >> 16); \
+ hbuf[3] = (unsigned char)((word) >> 24); \
+ check = crc32(check, hbuf, 4); \
+ } while (0)
+#endif
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+ do { \
+ put = strm->next_out; \
+ left = strm->avail_out; \
+ next = strm->next_in; \
+ have = strm->avail_in; \
+ hold = state->hold; \
+ bits = state->bits; \
+ } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+ do { \
+ strm->next_out = put; \
+ strm->avail_out = left; \
+ strm->next_in = next; \
+ strm->avail_in = have; \
+ state->hold = hold; \
+ state->bits = bits; \
+ } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+ do { \
+ hold = 0; \
+ bits = 0; \
+ } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+ if there is no input available. */
+#define PULLBYTE() \
+ do { \
+ if (have == 0) goto inf_leave; \
+ have--; \
+ hold += (unsigned long)(*next++) << bits; \
+ bits += 8; \
+ } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator. If there is
+ not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+ do { \
+ while (bits < (unsigned)(n)) \
+ PULLBYTE(); \
+ } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+ ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+ do { \
+ hold >>= (n); \
+ bits -= (unsigned)(n); \
+ } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+ do { \
+ hold >>= bits & 7; \
+ bits -= bits & 7; \
+ } while (0)
+
+/* Reverse the bytes in a 32-bit value */
+#define REVERSE(q) \
+ ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+ (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+/*
+ inflate() uses a state machine to process as much input data and generate as
+ much output data as possible before returning. The state machine is
+ structured roughly as follows:
+
+ for (;;) switch (state) {
+ ...
+ case STATEn:
+ if (not enough input data or output space to make progress)
+ return;
+ ... make progress ...
+ state = STATEm;
+ break;
+ ...
+ }
+
+ so when inflate() is called again, the same case is attempted again, and
+ if the appropriate resources are provided, the machine proceeds to the
+ next state. The NEEDBITS() macro is usually the way the state evaluates
+ whether it can proceed or should return. NEEDBITS() does the return if
+ the requested bits are not available. The typical use of the BITS macros
+ is:
+
+ NEEDBITS(n);
+ ... do something with BITS(n) ...
+ DROPBITS(n);
+
+ where NEEDBITS(n) either returns from inflate() if there isn't enough
+ input left to load n bits into the accumulator, or it continues. BITS(n)
+ gives the low n bits in the accumulator. When done, DROPBITS(n) drops
+ the low n bits off the accumulator. INITBITS() clears the accumulator
+ and sets the number of available bits to zero. BYTEBITS() discards just
+ enough bits to put the accumulator on a byte boundary. After BYTEBITS()
+ and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+ NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+ if there is no input available. The decoding of variable length codes uses
+ PULLBYTE() directly in order to pull just enough bytes to decode the next
+ code, and no more.
+
+ Some states loop until they get enough input, making sure that enough
+ state information is maintained to continue the loop where it left off
+ if NEEDBITS() returns in the loop. For example, want, need, and keep
+ would all have to actually be part of the saved state in case NEEDBITS()
+ returns:
+
+ case STATEw:
+ while (want < need) {
+ NEEDBITS(n);
+ keep[want++] = BITS(n);
+ DROPBITS(n);
+ }
+ state = STATEx;
+ case STATEx:
+
+ As shown above, if the next state is also the next case, then the break
+ is omitted.
+
+ A state may also return if there is not enough output space available to
+ complete that state. Those states are copying stored data, writing a
+ literal byte, and copying a matching string.
+
+ When returning, a "goto inf_leave" is used to update the total counters,
+ update the check value, and determine whether any progress has been made
+ during that inflate() call in order to return the proper return code.
+ Progress is defined as a change in either strm->avail_in or strm->avail_out.
+ When there is a window, goto inf_leave will update the window with the last
+ output written. If a goto inf_leave occurs in the middle of decompression
+ and there is no window currently, goto inf_leave will create one and copy
+ output to the window for the next call of inflate().
+
+ In this implementation, the flush parameter of inflate() only affects the
+ return code (per zlib.h). inflate() always writes as much as possible to
+ strm->next_out, given the space available and the provided input--the effect
+ documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers
+ the allocation of and copying into a sliding window until necessary, which
+ provides the effect documented in zlib.h for Z_FINISH when the entire input
+ stream available. So the only thing the flush parameter actually does is:
+ when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it
+ will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int ZEXPORT inflate(strm, flush)
+z_streamp strm;
+int flush;
+{
+ struct inflate_state FAR *state;
+ unsigned char FAR *next; /* next input */
+ unsigned char FAR *put; /* next output */
+ unsigned have, left; /* available input and output */
+ unsigned long hold; /* bit buffer */
+ unsigned bits; /* bits in bit buffer */
+ unsigned in, out; /* save starting available input and output */
+ unsigned copy; /* number of stored or match bytes to copy */
+ unsigned char FAR *from; /* where to copy match bytes from */
+ code this; /* current decoding table entry */
+ code last; /* parent table entry */
+ unsigned len; /* length to copy for repeats, bits to drop */
+ int ret; /* return code */
+#ifdef GUNZIP
+ unsigned char hbuf[4]; /* buffer for gzip header crc calculation */
+#endif
+ static const unsigned short order[19] = /* permutation of code lengths */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL ||
+ (strm->next_in == Z_NULL && strm->avail_in != 0))
+ return Z_STREAM_ERROR;
+
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */
+ LOAD();
+ in = have;
+ out = left;
+ ret = Z_OK;
+ for (;;)
+ switch (state->mode) {
+ case HEAD:
+ if (state->wrap == 0) {
+ state->mode = TYPEDO;
+ break;
+ }
+ NEEDBITS(16);
+#ifdef GUNZIP
+ if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */
+ state->check = crc32(0L, Z_NULL, 0);
+ CRC2(state->check, hold);
+ INITBITS();
+ state->mode = FLAGS;
+ break;
+ }
+ state->flags = 0; /* expect zlib header */
+ if (state->head != Z_NULL)
+ state->head->done = -1;
+ if (!(state->wrap & 1) || /* check if zlib header allowed */
+#else
+ if (
+#endif
+ ((BITS(8) << 8) + (hold >> 8)) % 31) {
+ strm->msg = (char *)"incorrect header check";
+ state->mode = BAD;
+ break;
+ }
+ if (BITS(4) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ DROPBITS(4);
+ len = BITS(4) + 8;
+ if (len > state->wbits) {
+ strm->msg = (char *)"invalid window size";
+ state->mode = BAD;
+ break;
+ }
+ state->dmax = 1U << len;
+ Tracev((stderr, "inflate: zlib header ok\n"));
+ strm->adler = state->check = adler32(0L, Z_NULL, 0);
+ state->mode = hold & 0x200 ? DICTID : TYPE;
+ INITBITS();
+ break;
+#ifdef GUNZIP
+ case FLAGS:
+ NEEDBITS(16);
+ state->flags = (int)(hold);
+ if ((state->flags & 0xff) != Z_DEFLATED) {
+ strm->msg = (char *)"unknown compression method";
+ state->mode = BAD;
+ break;
+ }
+ if (state->flags & 0xe000) {
+ strm->msg = (char *)"unknown header flags set";
+ state->mode = BAD;
+ break;
+ }
+ if (state->head != Z_NULL)
+ state->head->text = (int)((hold >> 8) & 1);
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ state->mode = TIME;
+ case TIME:
+ NEEDBITS(32);
+ if (state->head != Z_NULL)
+ state->head->time = hold;
+ if (state->flags & 0x0200) CRC4(state->check, hold);
+ INITBITS();
+ state->mode = OS;
+ case OS:
+ NEEDBITS(16);
+ if (state->head != Z_NULL) {
+ state->head->xflags = (int)(hold & 0xff);
+ state->head->os = (int)(hold >> 8);
+ }
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ state->mode = EXLEN;
+ case EXLEN:
+ if (state->flags & 0x0400) {
+ NEEDBITS(16);
+ state->length = (unsigned)(hold);
+ if (state->head != Z_NULL)
+ state->head->extra_len = (unsigned)hold;
+ if (state->flags & 0x0200) CRC2(state->check, hold);
+ INITBITS();
+ }
+ else if (state->head != Z_NULL)
+ state->head->extra = Z_NULL;
+ state->mode = EXTRA;
+ case EXTRA:
+ if (state->flags & 0x0400) {
+ copy = state->length;
+ if (copy > have) copy = have;
+ if (copy) {
+ if (state->head != Z_NULL &&
+ state->head->extra != Z_NULL) {
+ len = state->head->extra_len - state->length;
+ zmemcpy(state->head->extra + len, next,
+ len + copy > state->head->extra_max ?
+ state->head->extra_max - len : copy);
+ }
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ state->length -= copy;
+ }
+ if (state->length) goto inf_leave;
+ }
+ state->length = 0;
+ state->mode = NAME;
+ case NAME:
+ if (state->flags & 0x0800) {
+ if (have == 0) goto inf_leave;
+ copy = 0;
+ do {
+ len = (unsigned)(next[copy++]);
+ if (state->head != Z_NULL &&
+ state->head->name != Z_NULL &&
+ state->length < state->head->name_max)
+ state->head->name[state->length++] = len;
+ } while (len && copy < have);
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ if (len) goto inf_leave;
+ }
+ else if (state->head != Z_NULL)
+ state->head->name = Z_NULL;
+ state->length = 0;
+ state->mode = COMMENT;
+ case COMMENT:
+ if (state->flags & 0x1000) {
+ if (have == 0) goto inf_leave;
+ copy = 0;
+ do {
+ len = (unsigned)(next[copy++]);
+ if (state->head != Z_NULL &&
+ state->head->comment != Z_NULL &&
+ state->length < state->head->comm_max)
+ state->head->comment[state->length++] = len;
+ } while (len && copy < have);
+ if (state->flags & 0x0200)
+ state->check = crc32(state->check, next, copy);
+ have -= copy;
+ next += copy;
+ if (len) goto inf_leave;
+ }
+ else if (state->head != Z_NULL)
+ state->head->comment = Z_NULL;
+ state->mode = HCRC;
+ case HCRC:
+ if (state->flags & 0x0200) {
+ NEEDBITS(16);
+ if (hold != (state->check & 0xffff)) {
+ strm->msg = (char *)"header crc mismatch";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ }
+ if (state->head != Z_NULL) {
+ state->head->hcrc = (int)((state->flags >> 9) & 1);
+ state->head->done = 1;
+ }
+ strm->adler = state->check = crc32(0L, Z_NULL, 0);
+ state->mode = TYPE;
+ break;
+#endif
+ case DICTID:
+ NEEDBITS(32);
+ strm->adler = state->check = REVERSE(hold);
+ INITBITS();
+ state->mode = DICT;
+ case DICT:
+ if (state->havedict == 0) {
+ RESTORE();
+ return Z_NEED_DICT;
+ }
+ strm->adler = state->check = adler32(0L, Z_NULL, 0);
+ state->mode = TYPE;
+ case TYPE:
+ if (flush == Z_BLOCK) goto inf_leave;
+ case TYPEDO:
+ if (state->last) {
+ BYTEBITS();
+ state->mode = CHECK;
+ break;
+ }
+ NEEDBITS(3);
+ state->last = BITS(1);
+ DROPBITS(1);
+ switch (BITS(2)) {
+ case 0: /* stored block */
+ Tracev((stderr, "inflate: stored block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = LEN; /* decode codes */
+ break;
+ case 2: /* dynamic block */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ state->last ? " (last)" : ""));
+ state->mode = TABLE;
+ break;
+ case 3:
+ strm->msg = (char *)"invalid block type";
+ state->mode = BAD;
+ }
+ DROPBITS(2);
+ break;
+ case STORED:
+ BYTEBITS(); /* go to byte boundary */
+ NEEDBITS(32);
+ if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+ strm->msg = (char *)"invalid stored block lengths";
+ state->mode = BAD;
+ break;
+ }
+ state->length = (unsigned)hold & 0xffff;
+ Tracev((stderr, "inflate: stored length %u\n",
+ state->length));
+ INITBITS();
+ state->mode = COPY;
+ case COPY:
+ copy = state->length;
+ if (copy) {
+ if (copy > have) copy = have;
+ if (copy > left) copy = left;
+ if (copy == 0) goto inf_leave;
+ zmemcpy(put, next, copy);
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state->length -= copy;
+ break;
+ }
+ Tracev((stderr, "inflate: stored end\n"));
+ state->mode = TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14);
+ state->nlen = BITS(5) + 257;
+ DROPBITS(5);
+ state->ndist = BITS(5) + 1;
+ DROPBITS(5);
+ state->ncode = BITS(4) + 4;
+ DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+ if (state->nlen > 286 || state->ndist > 30) {
+ strm->msg = (char *)"too many length or distance symbols";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ state->have = 0;
+ state->mode = LENLENS;
+ case LENLENS:
+ while (state->have < state->ncode) {
+ NEEDBITS(3);
+ state->lens[order[state->have++]] = (unsigned short)BITS(3);
+ DROPBITS(3);
+ }
+ while (state->have < 19)
+ state->lens[order[state->have++]] = 0;
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 7;
+ ret = inflate_table(CODES, state->lens, 19, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid code lengths set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: code lengths ok\n"));
+ state->have = 0;
+ state->mode = CODELENS;
+ case CODELENS:
+ while (state->have < state->nlen + state->ndist) {
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.val < 16) {
+ NEEDBITS(this.bits);
+ DROPBITS(this.bits);
+ state->lens[state->have++] = this.val;
+ }
+ else {
+ if (this.val == 16) {
+ NEEDBITS(this.bits + 2);
+ DROPBITS(this.bits);
+ if (state->have == 0) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ len = state->lens[state->have - 1];
+ copy = 3 + BITS(2);
+ DROPBITS(2);
+ }
+ else if (this.val == 17) {
+ NEEDBITS(this.bits + 3);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 3 + BITS(3);
+ DROPBITS(3);
+ }
+ else {
+ NEEDBITS(this.bits + 7);
+ DROPBITS(this.bits);
+ len = 0;
+ copy = 11 + BITS(7);
+ DROPBITS(7);
+ }
+ if (state->have + copy > state->nlen + state->ndist) {
+ strm->msg = (char *)"invalid bit length repeat";
+ state->mode = BAD;
+ break;
+ }
+ while (copy--)
+ state->lens[state->have++] = (unsigned short)len;
+ }
+ }
+
+ /* handle error breaks in while */
+ if (state->mode == BAD) break;
+
+ /* build code tables */
+ state->next = state->codes;
+ state->lencode = (code const FAR *)(state->next);
+ state->lenbits = 9;
+ ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+ &(state->lenbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid literal/lengths set";
+ state->mode = BAD;
+ break;
+ }
+ state->distcode = (code const FAR *)(state->next);
+ state->distbits = 6;
+ ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+ &(state->next), &(state->distbits), state->work);
+ if (ret) {
+ strm->msg = (char *)"invalid distances set";
+ state->mode = BAD;
+ break;
+ }
+ Tracev((stderr, "inflate: codes ok\n"));
+ state->mode = LEN;
+ case LEN:
+ if (have >= 6 && left >= 258) {
+ RESTORE();
+ inflate_fast(strm, out);
+ LOAD();
+ break;
+ }
+ for (;;) {
+ this = state->lencode[BITS(state->lenbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if (this.op && (this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->lencode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ state->length = (unsigned)this.val;
+ if ((int)(this.op) == 0) {
+ Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", this.val));
+ state->mode = LIT;
+ break;
+ }
+ if (this.op & 32) {
+ Tracevv((stderr, "inflate: end of block\n"));
+ state->mode = TYPE;
+ break;
+ }
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid literal/length code";
+ state->mode = BAD;
+ break;
+ }
+ state->extra = (unsigned)(this.op) & 15;
+ state->mode = LENEXT;
+ case LENEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->length += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+ Tracevv((stderr, "inflate: length %u\n", state->length));
+ state->mode = DIST;
+ case DIST:
+ for (;;) {
+ this = state->distcode[BITS(state->distbits)];
+ if ((unsigned)(this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ if ((this.op & 0xf0) == 0) {
+ last = this;
+ for (;;) {
+ this = state->distcode[last.val +
+ (BITS(last.bits + last.op) >> last.bits)];
+ if ((unsigned)(last.bits + this.bits) <= bits) break;
+ PULLBYTE();
+ }
+ DROPBITS(last.bits);
+ }
+ DROPBITS(this.bits);
+ if (this.op & 64) {
+ strm->msg = (char *)"invalid distance code";
+ state->mode = BAD;
+ break;
+ }
+ state->offset = (unsigned)this.val;
+ state->extra = (unsigned)(this.op) & 15;
+ state->mode = DISTEXT;
+ case DISTEXT:
+ if (state->extra) {
+ NEEDBITS(state->extra);
+ state->offset += BITS(state->extra);
+ DROPBITS(state->extra);
+ }
+#ifdef INFLATE_STRICT
+ if (state->offset > state->dmax) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+#endif
+ if (state->offset > state->whave + out - left) {
+ strm->msg = (char *)"invalid distance too far back";
+ state->mode = BAD;
+ break;
+ }
+ Tracevv((stderr, "inflate: distance %u\n", state->offset));
+ state->mode = MATCH;
+ case MATCH:
+ if (left == 0) goto inf_leave;
+ copy = out - left;
+ if (state->offset > copy) { /* copy from window */
+ copy = state->offset - copy;
+ if (copy > state->write) {
+ copy -= state->write;
+ from = state->window + (state->wsize - copy);
+ }
+ else
+ from = state->window + (state->write - copy);
+ if (copy > state->length) copy = state->length;
+ }
+ else { /* copy from output */
+ from = put - state->offset;
+ copy = state->length;
+ }
+ if (copy > left) copy = left;
+ left -= copy;
+ state->length -= copy;
+ do {
+ *put++ = *from++;
+ } while (--copy);
+ if (state->length == 0) state->mode = LEN;
+ break;
+ case LIT:
+ if (left == 0) goto inf_leave;
+ *put++ = (unsigned char)(state->length);
+ left--;
+ state->mode = LEN;
+ break;
+ case CHECK:
+ if (state->wrap) {
+ NEEDBITS(32);
+ out -= left;
+ strm->total_out += out;
+ state->total += out;
+ if (out)
+ strm->adler = state->check =
+ UPDATE(state->check, put - out, out);
+ out = left;
+ if ((
+#ifdef GUNZIP
+ state->flags ? hold :
+#endif
+ REVERSE(hold)) != state->check) {
+ strm->msg = (char *)"incorrect data check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ Tracev((stderr, "inflate: check matches trailer\n"));
+ }
+#ifdef GUNZIP
+ state->mode = LENGTH;
+ case LENGTH:
+ if (state->wrap && state->flags) {
+ NEEDBITS(32);
+ if (hold != (state->total & 0xffffffffUL)) {
+ strm->msg = (char *)"incorrect length check";
+ state->mode = BAD;
+ break;
+ }
+ INITBITS();
+ Tracev((stderr, "inflate: length matches trailer\n"));
+ }
+#endif
+ state->mode = DONE;
+ case DONE:
+ ret = Z_STREAM_END;
+ goto inf_leave;
+ case BAD:
+ ret = Z_DATA_ERROR;
+ goto inf_leave;
+ case MEM:
+ return Z_MEM_ERROR;
+ case SYNC:
+ default:
+ return Z_STREAM_ERROR;
+ }
+
+ /*
+ Return from inflate(), updating the total counts and the check value.
+ If there was no progress during the inflate() call, return a buffer
+ error. Call updatewindow() to create and/or update the window state.
+ Note: a memory error from inflate() is non-recoverable.
+ */
+ inf_leave:
+ RESTORE();
+ if (state->wsize || (state->mode < CHECK && out != strm->avail_out))
+ if (updatewindow(strm, out)) {
+ state->mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ in -= strm->avail_in;
+ out -= strm->avail_out;
+ strm->total_in += in;
+ strm->total_out += out;
+ state->total += out;
+ if (state->wrap && out)
+ strm->adler = state->check =
+ UPDATE(state->check, strm->next_out - out, out);
+ strm->data_type = state->bits + (state->last ? 64 : 0) +
+ (state->mode == TYPE ? 128 : 0);
+ if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+ ret = Z_BUF_ERROR;
+ return ret;
+}
+
+int ZEXPORT inflateEnd(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+ if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->window != Z_NULL) ZFREE(strm, state->window);
+ ZFREE(strm, strm->state);
+ strm->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+const Bytef *dictionary;
+uInt dictLength;
+{
+ struct inflate_state FAR *state;
+ unsigned long id;
+
+ /* check state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (state->wrap != 0 && state->mode != DICT)
+ return Z_STREAM_ERROR;
+
+ /* check for correct dictionary id */
+ if (state->mode == DICT) {
+ id = adler32(0L, Z_NULL, 0);
+ id = adler32(id, dictionary, dictLength);
+ if (id != state->check)
+ return Z_DATA_ERROR;
+ }
+
+ /* copy dictionary to window */
+ if (updatewindow(strm, strm->avail_out)) {
+ state->mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ if (dictLength > state->wsize) {
+ zmemcpy(state->window, dictionary + dictLength - state->wsize,
+ state->wsize);
+ state->whave = state->wsize;
+ }
+ else {
+ zmemcpy(state->window + state->wsize - dictLength, dictionary,
+ dictLength);
+ state->whave = dictLength;
+ }
+ state->havedict = 1;
+ Tracev((stderr, "inflate: dictionary set\n"));
+ return Z_OK;
+}
+
+int ZEXPORT inflateGetHeader(strm, head)
+z_streamp strm;
+gz_headerp head;
+{
+ struct inflate_state FAR *state;
+
+ /* check state */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if ((state->wrap & 2) == 0) return Z_STREAM_ERROR;
+
+ /* save header structure */
+ state->head = head;
+ head->done = 0;
+ return Z_OK;
+}
+
+/*
+ Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found
+ or when out of input. When called, *have is the number of pattern bytes
+ found in order so far, in 0..3. On return *have is updated to the new
+ state. If on return *have equals four, then the pattern was found and the
+ return value is how many bytes were read including the last byte of the
+ pattern. If *have is less than four, then the pattern has not been found
+ yet and the return value is len. In the latter case, syncsearch() can be
+ called again with more data and the *have state. *have is initialized to
+ zero for the first call.
+ */
+local unsigned syncsearch(have, buf, len)
+unsigned FAR *have;
+unsigned char FAR *buf;
+unsigned len;
+{
+ unsigned got;
+ unsigned next;
+
+ got = *have;
+ next = 0;
+ while (next < len && got < 4) {
+ if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
+ got++;
+ else if (buf[next])
+ got = 0;
+ else
+ got = 4 - got;
+ next++;
+ }
+ *have = got;
+ return next;
+}
+
+int ZEXPORT inflateSync(strm)
+z_streamp strm;
+{
+ unsigned len; /* number of bytes to look at or looked at */
+ unsigned long in, out; /* temporary to save total_in and total_out */
+ unsigned char buf[4]; /* to restore bit buffer to byte string */
+ struct inflate_state FAR *state;
+
+ /* check parameters */
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
+
+ /* if first time, start search in bit buffer */
+ if (state->mode != SYNC) {
+ state->mode = SYNC;
+ state->hold <<= state->bits & 7;
+ state->bits -= state->bits & 7;
+ len = 0;
+ while (state->bits >= 8) {
+ buf[len++] = (unsigned char)(state->hold);
+ state->hold >>= 8;
+ state->bits -= 8;
+ }
+ state->have = 0;
+ syncsearch(&(state->have), buf, len);
+ }
+
+ /* search available input */
+ len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
+ strm->avail_in -= len;
+ strm->next_in += len;
+ strm->total_in += len;
+
+ /* return no joy or set up to restart inflate() on a new block */
+ if (state->have != 4) return Z_DATA_ERROR;
+ in = strm->total_in; out = strm->total_out;
+ inflateReset(strm);
+ strm->total_in = in; strm->total_out = out;
+ state->mode = TYPE;
+ return Z_OK;
+}
+
+/*
+ Returns true if inflate is currently at the end of a block generated by
+ Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+ implementation to provide an additional safety check. PPP uses
+ Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+ block. When decompressing, PPP checks that at the end of input packet,
+ inflate is waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(strm)
+z_streamp strm;
+{
+ struct inflate_state FAR *state;
+
+ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)strm->state;
+ return state->mode == STORED && state->bits == 0;
+}
+
+int ZEXPORT inflateCopy(dest, source)
+z_streamp dest;
+z_streamp source;
+{
+ struct inflate_state FAR *state;
+ struct inflate_state FAR *copy;
+ unsigned char FAR *window;
+ unsigned wsize;
+
+ /* check input */
+ if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL ||
+ source->zalloc == (alloc_func)0 || source->zfree == (free_func)0)
+ return Z_STREAM_ERROR;
+ state = (struct inflate_state FAR *)source->state;
+
+ /* allocate space */
+ copy = (struct inflate_state FAR *)
+ ZALLOC(source, 1, sizeof(struct inflate_state));
+ if (copy == Z_NULL) return Z_MEM_ERROR;
+ window = Z_NULL;
+ if (state->window != Z_NULL) {
+ window = (unsigned char FAR *)
+ ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
+ if (window == Z_NULL) {
+ ZFREE(source, copy);
+ return Z_MEM_ERROR;
+ }
+ }
+
+ /* copy state */
+ zmemcpy(dest, source, sizeof(z_stream));
+ zmemcpy(copy, state, sizeof(struct inflate_state));
+ if (state->lencode >= state->codes &&
+ state->lencode <= state->codes + ENOUGH - 1) {
+ copy->lencode = copy->codes + (state->lencode - state->codes);
+ copy->distcode = copy->codes + (state->distcode - state->codes);
+ }
+ copy->next = copy->codes + (state->next - state->codes);
+ if (window != Z_NULL) {
+ wsize = 1U << state->wbits;
+ zmemcpy(window, state->window, wsize);
+ }
+ copy->window = window;
+ dest->state = (struct internal_state FAR *)copy;
+ return Z_OK;
+}
--- /dev/null
+/* inflate.h -- internal inflate state definition
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+ trailer decoding by inflate(). NO_GZIP would be used to avoid linking in
+ the crc code when it is not needed. For shared libraries, gzip decoding
+ should be left enabled. */
+#ifndef NO_GZIP
+# define GUNZIP
+#endif
+
+/* Possible inflate modes between inflate() calls */
+typedef enum {
+ HEAD, /* i: waiting for magic header */
+ FLAGS, /* i: waiting for method and flags (gzip) */
+ TIME, /* i: waiting for modification time (gzip) */
+ OS, /* i: waiting for extra flags and operating system (gzip) */
+ EXLEN, /* i: waiting for extra length (gzip) */
+ EXTRA, /* i: waiting for extra bytes (gzip) */
+ NAME, /* i: waiting for end of file name (gzip) */
+ COMMENT, /* i: waiting for end of comment (gzip) */
+ HCRC, /* i: waiting for header crc (gzip) */
+ DICTID, /* i: waiting for dictionary check value */
+ DICT, /* waiting for inflateSetDictionary() call */
+ TYPE, /* i: waiting for type bits, including last-flag bit */
+ TYPEDO, /* i: same, but skip check to exit inflate on new block */
+ STORED, /* i: waiting for stored size (length and complement) */
+ COPY, /* i/o: waiting for input or output to copy stored block */
+ TABLE, /* i: waiting for dynamic block table lengths */
+ LENLENS, /* i: waiting for code length code lengths */
+ CODELENS, /* i: waiting for length/lit and distance code lengths */
+ LEN, /* i: waiting for length/lit code */
+ LENEXT, /* i: waiting for length extra bits */
+ DIST, /* i: waiting for distance code */
+ DISTEXT, /* i: waiting for distance extra bits */
+ MATCH, /* o: waiting for output space to copy string */
+ LIT, /* o: waiting for output space to write literal */
+ CHECK, /* i: waiting for 32-bit check value */
+ LENGTH, /* i: waiting for 32-bit length (gzip) */
+ DONE, /* finished check, done -- remain here until reset */
+ BAD, /* got a data error -- remain here until reset */
+ MEM, /* got an inflate() memory error -- remain here until reset */
+ SYNC /* looking for synchronization bytes to restart inflate() */
+} inflate_mode;
+
+/*
+ State transitions between above modes -
+
+ (most modes can go to the BAD or MEM mode -- not shown for clarity)
+
+ Process header:
+ HEAD -> (gzip) or (zlib)
+ (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME
+ NAME -> COMMENT -> HCRC -> TYPE
+ (zlib) -> DICTID or TYPE
+ DICTID -> DICT -> TYPE
+ Read deflate blocks:
+ TYPE -> STORED or TABLE or LEN or CHECK
+ STORED -> COPY -> TYPE
+ TABLE -> LENLENS -> CODELENS -> LEN
+ Read deflate codes:
+ LEN -> LENEXT or LIT or TYPE
+ LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
+ LIT -> LEN
+ Process trailer:
+ CHECK -> LENGTH -> DONE
+ */
+
+/* state maintained between inflate() calls. Approximately 7K bytes. */
+struct inflate_state {
+ inflate_mode mode; /* current inflate mode */
+ int last; /* true if processing last block */
+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */
+ int havedict; /* true if dictionary provided */
+ int flags; /* gzip header method and flags (0 if zlib) */
+ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */
+ unsigned long check; /* protected copy of check value */
+ unsigned long total; /* protected copy of output count */
+ gz_headerp head; /* where to save gzip header information */
+ /* sliding window */
+ unsigned wbits; /* log base 2 of requested window size */
+ unsigned wsize; /* window size or zero if not using window */
+ unsigned whave; /* valid bytes in the window */
+ unsigned write; /* window write index */
+ unsigned char FAR *window; /* allocated sliding window, if needed */
+ /* bit accumulator */
+ unsigned long hold; /* input bit accumulator */
+ unsigned bits; /* number of bits in "in" */
+ /* for string and stored block copying */
+ unsigned length; /* literal or length of data to copy */
+ unsigned offset; /* distance back to copy string from */
+ /* for table and code decoding */
+ unsigned extra; /* extra bits needed */
+ /* fixed and dynamic code tables */
+ code const FAR *lencode; /* starting table for length/literal codes */
+ code const FAR *distcode; /* starting table for distance codes */
+ unsigned lenbits; /* index bits for lencode */
+ unsigned distbits; /* index bits for distcode */
+ /* dynamic table building */
+ unsigned ncode; /* number of code length code lengths */
+ unsigned nlen; /* number of length code lengths */
+ unsigned ndist; /* number of distance code lengths */
+ unsigned have; /* number of code lengths in lens[] */
+ code FAR *next; /* next available space in codes[] */
+ unsigned short lens[320]; /* temporary storage for code lengths */
+ unsigned short work[288]; /* work area for code table building */
+ code codes[ENOUGH]; /* space for code tables */
+};
--- /dev/null
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#define MAXBITS 15
+
+const char inflate_copyright[] =
+ " inflate 1.2.3 Copyright 1995-2005 Mark Adler ";
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/*
+ Build a set of tables to decode the provided canonical Huffman code.
+ The code lengths are lens[0..codes-1]. The result starts at *table,
+ whose indices are 0..2^bits-1. work is a writable array of at least
+ lens shorts, which is used as a work area. type is the type of code
+ to be generated, CODES, LENS, or DISTS. On return, zero is success,
+ -1 is an invalid code, and +1 means that ENOUGH isn't enough. table
+ on return points to the next available entry's address. bits is the
+ requested root table index bits, and on return it is the actual root
+ table index bits. It will differ if the request is greater than the
+ longest code or if it is less than the shortest code.
+ */
+int inflate_table(type, lens, codes, table, bits, work)
+codetype type;
+unsigned short FAR *lens;
+unsigned codes;
+code FAR * FAR *table;
+unsigned FAR *bits;
+unsigned short FAR *work;
+{
+ unsigned len; /* a code's length in bits */
+ unsigned sym; /* index of code symbols */
+ unsigned min, max; /* minimum and maximum code lengths */
+ unsigned root; /* number of index bits for root table */
+ unsigned curr; /* number of index bits for current table */
+ unsigned drop; /* code bits to drop for sub-table */
+ int left; /* number of prefix codes available */
+ unsigned used; /* code entries in table used */
+ unsigned huff; /* Huffman code */
+ unsigned incr; /* for incrementing code, index */
+ unsigned fill; /* index for replicating entries */
+ unsigned low; /* low bits for current root entry */
+ unsigned mask; /* mask for low root bits */
+ code this; /* table entry for duplication */
+ code FAR *next; /* next available space in table */
+ const unsigned short FAR *base; /* base value table to use */
+ const unsigned short FAR *extra; /* extra bits table to use */
+ int end; /* use base and extra for symbol > end */
+ unsigned short count[MAXBITS+1]; /* number of codes of each length */
+ unsigned short offs[MAXBITS+1]; /* offsets in table for each length */
+ static const unsigned short lbase[31] = { /* Length codes 257..285 base */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ static const unsigned short lext[31] = { /* Length codes 257..285 extra */
+ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196};
+ static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577, 0, 0};
+ static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
+ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+ 28, 28, 29, 29, 64, 64};
+
+ /*
+ Process a set of code lengths to create a canonical Huffman code. The
+ code lengths are lens[0..codes-1]. Each length corresponds to the
+ symbols 0..codes-1. The Huffman code is generated by first sorting the
+ symbols by length from short to long, and retaining the symbol order
+ for codes with equal lengths. Then the code starts with all zero bits
+ for the first code of the shortest length, and the codes are integer
+ increments for the same length, and zeros are appended as the length
+ increases. For the deflate format, these bits are stored backwards
+ from their more natural integer increment ordering, and so when the
+ decoding tables are built in the large loop below, the integer codes
+ are incremented backwards.
+
+ This routine assumes, but does not check, that all of the entries in
+ lens[] are in the range 0..MAXBITS. The caller must assure this.
+ 1..MAXBITS is interpreted as that code length. zero means that that
+ symbol does not occur in this code.
+
+ The codes are sorted by computing a count of codes for each length,
+ creating from that a table of starting indices for each length in the
+ sorted table, and then entering the symbols in order in the sorted
+ table. The sorted table is work[], with that space being provided by
+ the caller.
+
+ The length counts are used for other purposes as well, i.e. finding
+ the minimum and maximum length codes, determining if there are any
+ codes at all, checking for a valid set of lengths, and looking ahead
+ at length counts to determine sub-table sizes when building the
+ decoding tables.
+ */
+
+ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+ for (len = 0; len <= MAXBITS; len++)
+ count[len] = 0;
+ for (sym = 0; sym < codes; sym++)
+ count[lens[sym]]++;
+
+ /* bound code lengths, force root to be within code lengths */
+ root = *bits;
+ for (max = MAXBITS; max >= 1; max--)
+ if (count[max] != 0) break;
+ if (root > max) root = max;
+ if (max == 0) { /* no symbols to code at all */
+ this.op = (unsigned char)64; /* invalid code marker */
+ this.bits = (unsigned char)1;
+ this.val = (unsigned short)0;
+ *(*table)++ = this; /* make a table to force an error */
+ *(*table)++ = this;
+ *bits = 1;
+ return 0; /* no symbols, but wait for decoding to report error */
+ }
+ for (min = 1; min <= MAXBITS; min++)
+ if (count[min] != 0) break;
+ if (root < min) root = min;
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1;
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1;
+ left -= count[len];
+ if (left < 0) return -1; /* over-subscribed */
+ }
+ if (left > 0 && (type == CODES || max != 1))
+ return -1; /* incomplete set */
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++)
+ offs[len + 1] = offs[len] + count[len];
+
+ /* sort symbols by length, by symbol order within each length */
+ for (sym = 0; sym < codes; sym++)
+ if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
+
+ /*
+ Create and fill in decoding tables. In this loop, the table being
+ filled is at next and has curr index bits. The code being used is huff
+ with length len. That code is converted to an index by dropping drop
+ bits off of the bottom. For codes where len is less than drop + curr,
+ those top drop + curr - len bits are incremented through all values to
+ fill the table with replicated entries.
+
+ root is the number of index bits for the root table. When len exceeds
+ root, sub-tables are created pointed to by the root entry with an index
+ of the low root bits of huff. This is saved in low to check for when a
+ new sub-table should be started. drop is zero when the root table is
+ being filled, and drop is root when sub-tables are being filled.
+
+ When a new sub-table is needed, it is necessary to look ahead in the
+ code lengths to determine what size sub-table is needed. The length
+ counts are used for this, and so count[] is decremented as codes are
+ entered in the tables.
+
+ used keeps track of how many table entries have been allocated from the
+ provided *table space. It is checked when a LENS table is being made
+ against the space in *table, ENOUGH, minus the maximum space needed by
+ the worst case distance code, MAXD. This should never happen, but the
+ sufficiency of ENOUGH has not been proven exhaustively, hence the check.
+ This assumes that when type == LENS, bits == 9.
+
+ sym increments through all symbols, and the loop terminates when
+ all codes of length max, i.e. all codes, have been processed. This
+ routine permits incomplete codes, so another loop after this one fills
+ in the rest of the decoding tables with invalid code markers.
+ */
+
+ /* set up for code type */
+ switch (type) {
+ case CODES:
+ base = extra = work; /* dummy value--not used */
+ end = 19;
+ break;
+ case LENS:
+ base = lbase;
+ base -= 257;
+ extra = lext;
+ extra -= 257;
+ end = 256;
+ break;
+ default: /* DISTS */
+ base = dbase;
+ extra = dext;
+ end = -1;
+ }
+
+ /* initialize state for loop */
+ huff = 0; /* starting code */
+ sym = 0; /* starting code symbol */
+ len = min; /* starting code length */
+ next = *table; /* current table to fill in */
+ curr = root; /* current table index bits */
+ drop = 0; /* current bits to drop from code for index */
+ low = (unsigned)(-1); /* trigger new sub-table when len > root */
+ used = 1U << root; /* use root table entries */
+ mask = used - 1; /* mask for comparing low */
+
+ /* check available table space */
+ if (type == LENS && used >= ENOUGH - MAXD)
+ return 1;
+
+ /* process all codes and make table entries */
+ for (;;) {
+ /* create table entry */
+ this.bits = (unsigned char)(len - drop);
+ if ((int)(work[sym]) < end) {
+ this.op = (unsigned char)0;
+ this.val = work[sym];
+ }
+ else if ((int)(work[sym]) > end) {
+ this.op = (unsigned char)(extra[work[sym]]);
+ this.val = base[work[sym]];
+ }
+ else {
+ this.op = (unsigned char)(32 + 64); /* end of block */
+ this.val = 0;
+ }
+
+ /* replicate for those indices with low len bits equal to huff */
+ incr = 1U << (len - drop);
+ fill = 1U << curr;
+ min = fill; /* save offset to next table */
+ do {
+ fill -= incr;
+ next[(huff >> drop) + fill] = this;
+ } while (fill != 0);
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+
+ /* go to next symbol, update count, len */
+ sym++;
+ if (--(count[len]) == 0) {
+ if (len == max) break;
+ len = lens[work[sym]];
+ }
+
+ /* create new sub-table if needed */
+ if (len > root && (huff & mask) != low) {
+ /* if first time, transition to sub-tables */
+ if (drop == 0)
+ drop = root;
+
+ /* increment past last table */
+ next += min; /* here min is 1 << curr */
+
+ /* determine length of next table */
+ curr = len - drop;
+ left = (int)(1 << curr);
+ while (curr + drop < max) {
+ left -= count[curr + drop];
+ if (left <= 0) break;
+ curr++;
+ left <<= 1;
+ }
+
+ /* check for enough space */
+ used += 1U << curr;
+ if (type == LENS && used >= ENOUGH - MAXD)
+ return 1;
+
+ /* point entry in root table to sub-table */
+ low = huff & mask;
+ (*table)[low].op = (unsigned char)curr;
+ (*table)[low].bits = (unsigned char)root;
+ (*table)[low].val = (unsigned short)(next - *table);
+ }
+ }
+
+ /*
+ Fill in rest of table for incomplete codes. This loop is similar to the
+ loop above in incrementing huff for table indices. It is assumed that
+ len is equal to curr + drop, so there is no loop needed to increment
+ through high index bits. When the current sub-table is filled, the loop
+ drops back to the root table to fill in any remaining entries there.
+ */
+ this.op = (unsigned char)64; /* invalid code marker */
+ this.bits = (unsigned char)(len - drop);
+ this.val = (unsigned short)0;
+ while (huff != 0) {
+ /* when done with sub-table, drop back to root table */
+ if (drop != 0 && (huff & mask) != low) {
+ drop = 0;
+ len = root;
+ next = *table;
+ this.bits = (unsigned char)len;
+ }
+
+ /* put invalid code marker in table */
+ next[huff >> drop] = this;
+
+ /* backwards increment the len-bit code huff */
+ incr = 1U << (len - 1);
+ while (huff & incr)
+ incr >>= 1;
+ if (incr != 0) {
+ huff &= incr - 1;
+ huff += incr;
+ }
+ else
+ huff = 0;
+ }
+
+ /* set return parameters */
+ *table += used;
+ *bits = root;
+ return 0;
+}
--- /dev/null
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Structure for decoding tables. Each entry provides either the
+ information needed to do the operation requested by the code that
+ indexed that table entry, or it provides a pointer to another
+ table that indexes more bits of the code. op indicates whether
+ the entry is a pointer to another table, a literal, a length or
+ distance, an end-of-block, or an invalid code. For a table
+ pointer, the low four bits of op is the number of index bits of
+ that table. For a length or distance, the low four bits of op
+ is the number of extra bits to get after the code. bits is
+ the number of bits in this code or part of the code to drop off
+ of the bit buffer. val is the actual byte to output in the case
+ of a literal, the base length or distance, or the offset from
+ the current table to the next table. Each entry is four bytes. */
+typedef struct {
+ unsigned char op; /* operation, extra bits, table bits */
+ unsigned char bits; /* bits in this part of the code */
+ unsigned short val; /* offset in table or code value */
+} code;
+
+/* op values as set by inflate_table():
+ 00000000 - literal
+ 0000tttt - table link, tttt != 0 is the number of table index bits
+ 0001eeee - length or distance, eeee is the number of extra bits
+ 01100000 - end of block
+ 01000000 - invalid code
+ */
+
+/* Maximum size of dynamic tree. The maximum found in a long but non-
+ exhaustive search was 1444 code structures (852 for length/literals
+ and 592 for distances, the latter actually the result of an
+ exhaustive search). The true maximum is not known, but the value
+ below is more than safe. */
+#define ENOUGH 2048
+#define MAXD 592
+
+/* Type of code to build for inftable() */
+typedef enum {
+ CODES,
+ LENS,
+ DISTS
+} codetype;
+
+extern int inflate_table OF((codetype type, unsigned short FAR *lens,
+ unsigned codes, code FAR * FAR *table,
+ unsigned FAR *bits, unsigned short FAR *work));
--- /dev/null
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-2005 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * ALGORITHM
+ *
+ * The "deflation" process uses several Huffman trees. The more
+ * common source values are represented by shorter bit sequences.
+ *
+ * Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values). The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ * REFERENCES
+ *
+ * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ * Storer, James A.
+ * Data Compression: Methods and Theory, pp. 49-50.
+ * Computer Science Press, 1988. ISBN 0-7167-8156-5.
+ *
+ * Sedgewick, R.
+ * Algorithms, p290.
+ * Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* @(#) $Id$ */
+
+/* #define GEN_TREES_H */
+
+#include "deflate.h"
+
+#ifdef DEBUG
+# include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6 16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10 17
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+#define REPZ_11_138 18
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+ = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local const int extra_dbits[D_CODES] /* extra bits for each distance code */
+ = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+ = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local const uch bl_order[BL_CODES]
+ = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+#define DIST_CODE_LEN 512 /* see definition of array dist_code below */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+/* non ANSI compilers may not accept trees.h */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+uch _dist_code[DIST_CODE_LEN];
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+uch _length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+#else
+# include "trees.h"
+#endif /* GEN_TREES_H */
+
+struct static_tree_desc_s {
+ const ct_data *static_tree; /* static tree or NULL */
+ const intf *extra_bits; /* extra bits for each code or NULL */
+ int extra_base; /* base index for extra_bits */
+ int elems; /* max number of elements in the tree */
+ int max_length; /* max bit length for the codes */
+};
+
+local static_tree_desc static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc static_d_desc =
+{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS};
+
+local static_tree_desc static_bl_desc =
+{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block OF((deflate_state *s));
+local void pqdownheap OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen OF((deflate_state *s, tree_desc *desc));
+local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree OF((deflate_state *s, tree_desc *desc));
+local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree OF((deflate_state *s, ct_data *tree, int max_code));
+local int build_bl_tree OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+ int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+ ct_data *dtree));
+local void set_data_type OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup OF((deflate_state *s));
+local void bi_flush OF((deflate_state *s));
+local void copy_block OF((deflate_state *s, charf *buf, unsigned len,
+ int header));
+
+#ifdef GEN_TREES_H
+local void gen_trees_header OF((void));
+#endif
+
+#ifndef DEBUG
+# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+ /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG */
+# define send_code(s, c, tree) \
+ { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+ send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+ put_byte(s, (uch)((w) & 0xff)); \
+ put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG
+local void send_bits OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+ deflate_state *s;
+ int value; /* value to send */
+ int length; /* number of bits */
+{
+ Tracevv((stderr," l %2d v %4x ", length, value));
+ Assert(length > 0 && length <= 15, "invalid length");
+ s->bits_sent += (ulg)length;
+
+ /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+ * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+ * unused bits in value.
+ */
+ if (s->bi_valid > (int)Buf_size - length) {
+ s->bi_buf |= (value << s->bi_valid);
+ put_short(s, s->bi_buf);
+ s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+ s->bi_valid += length - Buf_size;
+ } else {
+ s->bi_buf |= value << s->bi_valid;
+ s->bi_valid += length;
+ }
+}
+#else /* !DEBUG */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+ if (s->bi_valid > (int)Buf_size - len) {\
+ int val = value;\
+ s->bi_buf |= (val << s->bi_valid);\
+ put_short(s, s->bi_buf);\
+ s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+ s->bi_valid += len - Buf_size;\
+ } else {\
+ s->bi_buf |= (value) << s->bi_valid;\
+ s->bi_valid += len;\
+ }\
+}
+#endif /* DEBUG */
+
+
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+local void tr_static_init()
+{
+#if defined(GEN_TREES_H) || !defined(STDC)
+ static int static_init_done = 0;
+ int n; /* iterates over tree elements */
+ int bits; /* bit counter */
+ int length; /* length value */
+ int code; /* code value */
+ int dist; /* distance index */
+ ush bl_count[MAX_BITS+1];
+ /* number of codes at each bit length for an optimal tree */
+
+ if (static_init_done) return;
+
+ /* For some embedded targets, global variables are not initialized: */
+ static_l_desc.static_tree = static_ltree;
+ static_l_desc.extra_bits = extra_lbits;
+ static_d_desc.static_tree = static_dtree;
+ static_d_desc.extra_bits = extra_dbits;
+ static_bl_desc.extra_bits = extra_blbits;
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES-1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1<<extra_lbits[code]); n++) {
+ _length_code[length++] = (uch)code;
+ }
+ }
+ Assert (length == 256, "tr_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ _length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ _dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for ( ; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+ _dist_code[256 + dist++] = (uch)code;
+ }
+ }
+ Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+ n = 0;
+ while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+ while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+ while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+ while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n].Len = 5;
+ static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+ }
+ static_init_done = 1;
+
+# ifdef GEN_TREES_H
+ gen_trees_header();
+# endif
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
+}
+
+/* ===========================================================================
+ * Genererate the file trees.h describing the static trees.
+ */
+#ifdef GEN_TREES_H
+# ifndef DEBUG
+# include <stdio.h>
+# endif
+
+# define SEPARATOR(i, last, width) \
+ ((i) == (last)? "\n};\n\n" : \
+ ((i) % (width) == (width)-1 ? ",\n" : ", "))
+
+void gen_trees_header()
+{
+ FILE *header = fopen("trees.h", "w");
+ int i;
+
+ Assert (header != NULL, "Can't open trees.h");
+ fprintf(header,
+ "/* header created automatically with -DGEN_TREES_H */\n\n");
+
+ fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
+ for (i = 0; i < L_CODES+2; i++) {
+ fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
+ static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
+ }
+
+ fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
+ static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
+ }
+
+ fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n");
+ for (i = 0; i < DIST_CODE_LEN; i++) {
+ fprintf(header, "%2u%s", _dist_code[i],
+ SEPARATOR(i, DIST_CODE_LEN-1, 20));
+ }
+
+ fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
+ for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
+ fprintf(header, "%2u%s", _length_code[i],
+ SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
+ }
+
+ fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
+ for (i = 0; i < LENGTH_CODES; i++) {
+ fprintf(header, "%1u%s", base_length[i],
+ SEPARATOR(i, LENGTH_CODES-1, 20));
+ }
+
+ fprintf(header, "local const int base_dist[D_CODES] = {\n");
+ for (i = 0; i < D_CODES; i++) {
+ fprintf(header, "%5u%s", base_dist[i],
+ SEPARATOR(i, D_CODES-1, 10));
+ }
+
+ fclose(header);
+}
+#endif /* GEN_TREES_H */
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void _tr_init(s)
+ deflate_state *s;
+{
+ tr_static_init();
+
+ s->l_desc.dyn_tree = s->dyn_ltree;
+ s->l_desc.stat_desc = &static_l_desc;
+
+ s->d_desc.dyn_tree = s->dyn_dtree;
+ s->d_desc.stat_desc = &static_d_desc;
+
+ s->bl_desc.dyn_tree = s->bl_tree;
+ s->bl_desc.stat_desc = &static_bl_desc;
+
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG
+ s->compressed_len = 0L;
+ s->bits_sent = 0L;
+#endif
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+ deflate_state *s;
+{
+ int n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0;
+ for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0;
+ for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+ s->dyn_ltree[END_BLOCK].Freq = 1;
+ s->opt_len = s->static_len = 0L;
+ s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+ top = s->heap[SMALLEST]; \
+ s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+ pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+ (tree[n].Freq < tree[m].Freq || \
+ (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+ deflate_state *s;
+ ct_data *tree; /* the tree to restore */
+ int k; /* node to move down */
+{
+ int v = s->heap[k];
+ int j = k << 1; /* left son of k */
+ while (j <= s->heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s->heap_len &&
+ smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+ /* Exchange v with the smallest son */
+ s->heap[k] = s->heap[j]; k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+local void gen_bitlen(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ int max_code = desc->max_code;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ const intf *extra = desc->stat_desc->extra_bits;
+ int base = desc->stat_desc->extra_base;
+ int max_length = desc->stat_desc->max_length;
+ int h; /* heap index */
+ int n, m; /* iterate over the tree elements */
+ int bits; /* bit length */
+ int xbits; /* extra bits */
+ ush f; /* frequency */
+ int overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+ for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+ n = s->heap[h];
+ bits = tree[tree[n].Dad].Len + 1;
+ if (bits > max_length) bits = max_length, overflow++;
+ tree[n].Len = (ush)bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) continue; /* not a leaf node */
+
+ s->bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) xbits = extra[n-base];
+ f = tree[n].Freq;
+ s->opt_len += (ulg)f * (bits + xbits);
+ if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+ }
+ if (overflow == 0) return;
+
+ Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length-1;
+ while (s->bl_count[bits] == 0) bits--;
+ s->bl_count[bits]--; /* move one leaf down the tree */
+ s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+ s->bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits != 0; bits--) {
+ n = s->bl_count[bits];
+ while (n != 0) {
+ m = s->heap[--h];
+ if (m > max_code) continue;
+ if ((unsigned) tree[m].Len != (unsigned) bits) {
+ Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s->opt_len += ((long)bits - (long)tree[m].Len)
+ *(long)tree[m].Freq;
+ tree[m].Len = (ush)bits;
+ }
+ n--;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+ ct_data *tree; /* the tree to decorate */
+ int max_code; /* largest code with non zero frequency */
+ ushf *bl_count; /* number of codes at each bit length */
+{
+ ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+ ush code = 0; /* running code value */
+ int bits; /* bit index */
+ int n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ "inconsistent bit counts");
+ Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ int len = tree[n].Len;
+ if (len == 0) continue;
+ /* Now reverse the bits */
+ tree[n].Code = bi_reverse(next_code[len]++, len);
+
+ Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+ deflate_state *s;
+ tree_desc *desc; /* the tree descriptor */
+{
+ ct_data *tree = desc->dyn_tree;
+ const ct_data *stree = desc->stat_desc->static_tree;
+ int elems = desc->stat_desc->elems;
+ int n, m; /* iterate over heap elements */
+ int max_code = -1; /* largest code with non zero frequency */
+ int node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n].Freq != 0) {
+ s->heap[++(s->heap_len)] = max_code = n;
+ s->depth[n] = 0;
+ } else {
+ tree[n].Len = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s->heap_len < 2) {
+ node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+ tree[node].Freq = 1;
+ s->depth[node] = 0;
+ s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc->max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ pqremove(s, tree, n); /* n = node of least frequency */
+ m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+ s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+ s->heap[--(s->heap_max)] = m;
+
+ /* Create a new node father of n and m */
+ tree[node].Freq = tree[n].Freq + tree[m].Freq;
+ s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
+ s->depth[n] : s->depth[m]) + 1);
+ tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+ if (tree == s->bl_tree) {
+ fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+ node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+ }
+#endif
+ /* and insert the new node in the heap */
+ s->heap[SMALLEST] = node++;
+ pqdownheap(s, tree, SMALLEST);
+
+ } while (s->heap_len >= 2);
+
+ s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, (tree_desc *)desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ if (nextlen == 0) max_count = 138, min_count = 3;
+ tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ s->bl_tree[curlen].Freq += count;
+ } else if (curlen != 0) {
+ if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+ s->bl_tree[REP_3_6].Freq++;
+ } else if (count <= 10) {
+ s->bl_tree[REPZ_3_10].Freq++;
+ } else {
+ s->bl_tree[REPZ_11_138].Freq++;
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+ deflate_state *s;
+ ct_data *tree; /* the tree to be scanned */
+ int max_code; /* and its largest code of non zero frequency */
+{
+ int n; /* iterates over all tree elements */
+ int prevlen = -1; /* last emitted length */
+ int curlen; /* length of current code */
+ int nextlen = tree[0].Len; /* length of next code */
+ int count = 0; /* repeat count of the current code */
+ int max_count = 7; /* max repeat count */
+ int min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen == 0) max_count = 138, min_count = 3;
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen; nextlen = tree[n+1].Len;
+ if (++count < max_count && curlen == nextlen) {
+ continue;
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+ } else if (curlen != 0) {
+ if (curlen != prevlen) {
+ send_code(s, curlen, s->bl_tree); count--;
+ }
+ Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+ }
+ count = 0; prevlen = curlen;
+ if (nextlen == 0) {
+ max_count = 138, min_count = 3;
+ } else if (curlen == nextlen) {
+ max_count = 6, min_count = 3;
+ } else {
+ max_count = 7, min_count = 4;
+ }
+ }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+ deflate_state *s;
+{
+ int max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+ scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, (tree_desc *)(&(s->bl_desc)));
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+ if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s->opt_len += 3*(max_blindex+1) + 5+5+4;
+ Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+ deflate_state *s;
+ int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ int rank; /* index in bl_order */
+
+ Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ "too many codes");
+ Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes-1, 5);
+ send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+ }
+ Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+ Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+ Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void _tr_stored_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */
+#ifdef DEBUG
+ s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+ s->compressed_len += (stored_len + 4) << 3;
+#endif
+ copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the
+ * last two codes for the previous block (real code plus EOB) were coded
+ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+ * the last real code. In this case we send two empty static blocks instead
+ * of one. (There are no problems if the previous block is stored or fixed.)
+ * To simplify the code, we assume the worst case of last real code encoded
+ * on one bit only.
+ */
+void _tr_align(s)
+ deflate_state *s;
+{
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+#endif
+ bi_flush(s);
+ /* Of the 10 bits for the empty block, we have already sent
+ * (10 - bi_valid) bits. The lookahead for the last real code (before
+ * the EOB of the previous block) was thus at least one plus the length
+ * of the EOB plus what we have just sent of the empty static block.
+ */
+ if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
+ send_bits(s, STATIC_TREES<<1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+ s->compressed_len += 10L;
+#endif
+ bi_flush(s);
+ }
+ s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file.
+ */
+void _tr_flush_block(s, buf, stored_len, eof)
+ deflate_state *s;
+ charf *buf; /* input block, or NULL if too old */
+ ulg stored_len; /* length of input block */
+ int eof; /* true if this is the last block for a file */
+{
+ ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ int max_blindex = 0; /* index of last bit length code of non zero freq */
+
+ /* Build the Huffman trees unless a stored block is forced */
+ if (s->level > 0) {
+
+ /* Check if the file is binary or text */
+ if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN)
+ set_data_type(s);
+
+ /* Construct the literal and distance trees */
+ build_tree(s, (tree_desc *)(&(s->l_desc)));
+ Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+
+ build_tree(s, (tree_desc *)(&(s->d_desc)));
+ Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute the block lengths in bytes. */
+ opt_lenb = (s->opt_len+3+7)>>3;
+ static_lenb = (s->static_len+3+7)>>3;
+
+ Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ s->last_lit));
+
+ if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+ } else {
+ Assert(buf != (char*)0, "lost buf");
+ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+ }
+
+#ifdef FORCE_STORED
+ if (buf != (char*)0) { /* force stored block */
+#else
+ if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+ /* 4: two words for the lengths */
+#endif
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ _tr_stored_block(s, buf, stored_len, eof);
+
+#ifdef FORCE_STATIC
+ } else if (static_lenb >= 0) { /* force static trees */
+#else
+ } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {
+#endif
+ send_bits(s, (STATIC_TREES<<1)+eof, 3);
+ compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->static_len;
+#endif
+ } else {
+ send_bits(s, (DYN_TREES<<1)+eof, 3);
+ send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+ max_blindex+1);
+ compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+#ifdef DEBUG
+ s->compressed_len += 3 + s->opt_len;
+#endif
+ }
+ Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ /* The above check is made mod 2^32, for files larger than 512 MB
+ * and uLong implemented on 32 bits.
+ */
+ init_block(s);
+
+ if (eof) {
+ bi_windup(s);
+#ifdef DEBUG
+ s->compressed_len += 7; /* align on byte boundary */
+#endif
+ }
+ Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ s->compressed_len-7*eof));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int _tr_tally (s, dist, lc)
+ deflate_state *s;
+ unsigned dist; /* distance of matched string */
+ unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ s->d_buf[s->last_lit] = (ush)dist;
+ s->l_buf[s->last_lit++] = (uch)lc;
+ if (dist == 0) {
+ /* lc is the unmatched char */
+ s->dyn_ltree[lc].Freq++;
+ } else {
+ s->matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ Assert((ush)dist < (ush)MAX_DIST(s) &&
+ (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
+
+ s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+ s->dyn_dtree[d_code(dist)].Freq++;
+ }
+
+#ifdef TRUNCATE_BLOCK
+ /* Try to guess if it is profitable to stop the current block here */
+ if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
+ /* Compute an upper bound for the compressed length */
+ ulg out_length = (ulg)s->last_lit*8L;
+ ulg in_length = (ulg)((long)s->strstart - s->block_start);
+ int dcode;
+ for (dcode = 0; dcode < D_CODES; dcode++) {
+ out_length += (ulg)s->dyn_dtree[dcode].Freq *
+ (5L+extra_dbits[dcode]);
+ }
+ out_length >>= 3;
+ Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+ s->last_lit, in_length, out_length,
+ 100L - out_length*100L/in_length));
+ if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+ }
+#endif
+ return (s->last_lit == s->lit_bufsize-1);
+ /* We avoid equality with lit_bufsize because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+ deflate_state *s;
+ ct_data *ltree; /* literal tree */
+ ct_data *dtree; /* distance tree */
+{
+ unsigned dist; /* distance of matched string */
+ int lc; /* match length or unmatched char (if dist == 0) */
+ unsigned lx = 0; /* running index in l_buf */
+ unsigned code; /* the code to send */
+ int extra; /* number of extra bits to send */
+
+ if (s->last_lit != 0) do {
+ dist = s->d_buf[lx];
+ lc = s->l_buf[lx++];
+ if (dist == 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = _length_code[lc];
+ send_code(s, code+LITERALS+1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra != 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra != 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
+ "pendingBuf overflow");
+
+ } while (lx < s->last_lit);
+
+ send_code(s, END_BLOCK, ltree);
+ s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to BINARY or TEXT, using a crude approximation:
+ * set it to Z_TEXT if all symbols are either printable characters (33 to 255)
+ * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise.
+ * IN assertion: the fields Freq of dyn_ltree are set.
+ */
+local void set_data_type(s)
+ deflate_state *s;
+{
+ int n;
+
+ for (n = 0; n < 9; n++)
+ if (s->dyn_ltree[n].Freq != 0)
+ break;
+ if (n == 9)
+ for (n = 14; n < 32; n++)
+ if (s->dyn_ltree[n].Freq != 0)
+ break;
+ s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY;
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+ unsigned code; /* the value to invert */
+ int len; /* its bit length */
+{
+ register unsigned res = 0;
+ do {
+ res |= code & 1;
+ code >>= 1, res <<= 1;
+ } while (--len > 0);
+ return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+ deflate_state *s;
+{
+ if (s->bi_valid == 16) {
+ put_short(s, s->bi_buf);
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+ } else if (s->bi_valid >= 8) {
+ put_byte(s, (Byte)s->bi_buf);
+ s->bi_buf >>= 8;
+ s->bi_valid -= 8;
+ }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+ deflate_state *s;
+{
+ if (s->bi_valid > 8) {
+ put_short(s, s->bi_buf);
+ } else if (s->bi_valid > 0) {
+ put_byte(s, (Byte)s->bi_buf);
+ }
+ s->bi_buf = 0;
+ s->bi_valid = 0;
+#ifdef DEBUG
+ s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+ deflate_state *s;
+ charf *buf; /* the input data */
+ unsigned len; /* its length */
+ int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+ s->last_eob_len = 8; /* enough lookahead for inflate */
+
+ if (header) {
+ put_short(s, (ush)len);
+ put_short(s, (ush)~len);
+#ifdef DEBUG
+ s->bits_sent += 2*16;
+#endif
+ }
+#ifdef DEBUG
+ s->bits_sent += (ulg)len<<3;
+#endif
+ while (len--) {
+ put_byte(s, *buf++);
+ }
+}
--- /dev/null
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},
+{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},
+{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},
+{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},
+{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},
+{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},
+{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},
+{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},
+{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},
+{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},
+{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},
+{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},
+{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},
+{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},
+{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},
+{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},
+{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},
+{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},
+{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},
+{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},
+{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},
+{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},
+{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},
+{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},
+{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},
+{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},
+{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},
+{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},
+{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},
+{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},
+{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},
+{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},
+{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},
+{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},
+{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},
+{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},
+{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},
+{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},
+{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},
+{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},
+{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},
+{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},
+{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},
+{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},
+{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},
+{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},
+{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},
+{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},
+{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},
+{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},
+{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},
+{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},
+{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},
+{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},
+{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},
+{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},
+{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},
+{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch _dist_code[DIST_CODE_LEN] = {
+ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,
+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+};
+
+const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24,
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
+ 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576
+};
+
--- /dev/null
+/* uncompr.c -- decompress a memory buffer
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+int ZEXPORT uncompress (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong sourceLen;
+{
+ z_stream stream;
+ int err;
+
+ stream.next_in = (Bytef*)source;
+ stream.avail_in = (uInt)sourceLen;
+ /* Check for source > 64K on 16-bit machine: */
+ if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+ stream.next_out = dest;
+ stream.avail_out = (uInt)*destLen;
+ if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+
+ err = inflateInit(&stream);
+ if (err != Z_OK) return err;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
+ return Z_DATA_ERROR;
+ return err;
+ }
+ *destLen = stream.total_out;
+
+ err = inflateEnd(&stream);
+ return err;
+}
--- /dev/null
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflateParams z_deflateParams
+# define deflateBound z_deflateBound
+# define deflatePrime z_deflatePrime
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateCopy z_inflateCopy
+# define inflateReset z_inflateReset
+# define inflateBack z_inflateBack
+# define inflateBackEnd z_inflateBackEnd
+# define compress z_compress
+# define compress2 z_compress2
+# define compressBound z_compressBound
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+# define zError z_zError
+
+# define alloc_func z_alloc_func
+# define free_func z_free_func
+# define in_func z_in_func
+# define out_func z_out_func
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+# define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+# define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+# ifndef WIN32
+# define WIN32
+# endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+# ifndef SYS16BIT
+# define SYS16BIT
+# endif
+# endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+# ifndef STDC
+# define STDC
+# endif
+# if __STDC_VERSION__ >= 199901L
+# ifndef STDC99
+# define STDC99
+# endif
+# endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+# define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+# define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
+# define STDC
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const /* note: need a more gentle solution here */
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+# if defined(M_I86SM) || defined(M_I86MM)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+# if (defined(__SMALL__) || defined(__MEDIUM__))
+ /* Turbo C small or medium model */
+# define SMALL_MEDIUM
+# ifdef __BORLANDC__
+# define FAR _far
+# else
+# define FAR far
+# endif
+# endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+ /* If building or using zlib as a DLL, define ZLIB_DLL.
+ * This is not mandatory, but it offers a little performance increase.
+ */
+# ifdef ZLIB_DLL
+# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+# ifdef ZLIB_INTERNAL
+# define ZEXTERN extern __declspec(dllexport)
+# else
+# define ZEXTERN extern __declspec(dllimport)
+# endif
+# endif
+# endif /* ZLIB_DLL */
+ /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+ * define ZLIB_WINAPI.
+ * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+ */
+# ifdef ZLIB_WINAPI
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+ /* No need for _export, use ZLIB.DEF instead. */
+ /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+# define ZEXPORT WINAPI
+# ifdef WIN32
+# define ZEXPORTVA WINAPIV
+# else
+# define ZEXPORTVA FAR CDECL
+# endif
+# endif
+#endif
+
+#if defined (__BEOS__)
+# ifdef ZLIB_DLL
+# ifdef ZLIB_INTERNAL
+# define ZEXPORT __declspec(dllexport)
+# define ZEXPORTVA __declspec(dllexport)
+# else
+# define ZEXPORT __declspec(dllimport)
+# define ZEXPORTVA __declspec(dllimport)
+# endif
+# endif
+#endif
+
+#ifndef ZEXTERN
+# define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+# define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void const *voidpc;
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte const *voidpc;
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+#if defined(__OS400__)
+# define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+# define NO_vsnprintf
+# ifdef FAR
+# undef FAR
+# endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+# pragma map(deflateInit_,"DEIN")
+# pragma map(deflateInit2_,"DEIN2")
+# pragma map(deflateEnd,"DEEND")
+# pragma map(deflateBound,"DEBND")
+# pragma map(inflateInit_,"ININ")
+# pragma map(inflateInit2_,"ININ2")
+# pragma map(inflateEnd,"INEND")
+# pragma map(inflateSync,"INSY")
+# pragma map(inflateSetDictionary,"INSEDI")
+# pragma map(compressBound,"CMBND")
+# pragma map(inflate_table,"INTABL")
+# pragma map(inflate_fast,"INFA")
+# pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
--- /dev/null
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.2.3, July 18th, 2005
+
+ Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.3"
+#define ZLIB_VERNUM 0x1230
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms will be added later and will have the same
+ stream interface.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The compressed data format used by default by the in-memory functions is
+ the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+ around a deflate stream, which is itself documented in RFC 1951.
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio using the functions that start
+ with "gz". The gzip format is different from the zlib format. gzip is a
+ gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+ This library can optionally read and write gzip streams in memory as well.
+
+ The zlib format was designed to be compact and fast for use in memory
+ and on communications channels. The gzip format was designed for single-
+ file compression on file systems, has a larger header than zlib to maintain
+ directory information, and uses a different, slower check method than zlib.
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never
+ crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: binary or text */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ gzip header information passed to and from zlib routines. See RFC 1952
+ for more details on the meanings of these fields.
+*/
+typedef struct gz_header_s {
+ int text; /* true if compressed data believed to be text */
+ uLong time; /* modification time */
+ int xflags; /* extra flags (not used when writing a gzip file) */
+ int os; /* operating system */
+ Bytef *extra; /* pointer to extra field or Z_NULL if none */
+ uInt extra_len; /* extra field length (valid if extra != Z_NULL) */
+ uInt extra_max; /* space at extra (only when reading header) */
+ Bytef *name; /* pointer to zero-terminated file name or Z_NULL */
+ uInt name_max; /* space at name (only when reading header) */
+ Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */
+ uInt comm_max; /* space at comment (only when reading header) */
+ int hcrc; /* true if there was or will be a header crc */
+ int done; /* true when done reading gzip header (not used
+ when writing a gzip file) */
+} gz_header;
+
+typedef gz_header FAR *gz_headerp;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, zalloc and zfree must be
+ thread safe.
+
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH 2
+#define Z_FULL_FLUSH 3
+#define Z_FINISH 4
+#define Z_BLOCK 5
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_RLE 3
+#define Z_FIXED 4
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_TEXT 1
+#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field (though see inflate()) */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+ /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+ deflate compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce some
+ output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. deflate performs one or both of the
+ following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+ and with zero avail_out, it must be called again after making room in the
+ output buffer because there might be more output pending.
+
+ Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
+ decide how much data to accumualte before producing output, in order to
+ maximize compression.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In particular
+ avail_in is zero after the call if enough output space has been provided
+ before the call.) Flushing may degrade compression for some compression
+ algorithms and so it should be used only when necessary.
+
+ If flush is set to Z_FULL_FLUSH, all output is flushed as with
+ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+ compression.
+
+ If deflate returns with avail_out == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ avail_out), until the flush is complete (deflate returns with non-zero
+ avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+ avail_out is greater than six to avoid repeated flush markers due to
+ avail_out == 0 on return.
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ the value returned by deflateBound (see below). If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() sets strm->adler to the adler32 checksum of all input read
+ so far (that is, total_in bytes).
+
+ deflate() may update strm->data_type if it can make a good guess about
+ the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+ (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not
+ fatal, and deflate() can be called again with more input and more output
+ space to continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ msg may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+ value depends on the compression method), inflateInit determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly; otherwise the allocation will be deferred to the first call of
+ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+ use default allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller. msg is set to null if there is no error
+ message. inflateInit does not perform any decompression apart from reading
+ the zlib header if present: this will be done by inflate(). (So next_in and
+ avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+ inflate decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce
+ some output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. inflate performs one or both of the
+ following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there
+ is no more input data or no more space in the output buffer (see below
+ about the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+
+ The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH,
+ Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much
+ output as possible to the output buffer. Z_BLOCK requests that inflate() stop
+ if and when it gets to the next deflate block boundary. When decoding the
+ zlib or gzip format, this will cause inflate() to return immediately after
+ the header and before the first block. When doing a raw inflate, inflate()
+ will go ahead and process the first block, and will return when it gets to
+ the end of that block, or when it runs out of data.
+
+ The Z_BLOCK option assists in appending to or combining deflate streams.
+ Also to assist in this, on return inflate() will set strm->data_type to the
+ number of unused bits in the last byte taken from strm->next_in, plus 64
+ if inflate() is currently decoding the last block in the deflate stream,
+ plus 128 if inflate() returned immediately after decoding an end-of-block
+ code or decoding the complete header up to just before the first byte of the
+ deflate stream. The end-of-block will not be indicated until all of the
+ uncompressed data from that block has been written to strm->next_out. The
+ number of unused bits may in general be greater than seven, except when
+ bit 7 of data_type is set, in which case the number of unused bits will be
+ less than eight.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster approach
+ may be used for the single inflate() call.
+
+ In this implementation, inflate() always flushes as much output as
+ possible to the output buffer, and always uses the faster approach on the
+ first call. So the only effect of the flush parameter in this implementation
+ is on the return value of inflate(), as noted below, or when it returns early
+ because Z_BLOCK is used.
+
+ If a preset dictionary is needed after this call (see inflateSetDictionary
+ below), inflate sets strm->adler to the adler32 checksum of the dictionary
+ chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+ strm->adler to the adler32 checksum of all output produced so far (that is,
+ total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+ below. At the end of the stream, inflate() checks that its computed adler32
+ checksum is equal to that saved by the compressor and returns Z_STREAM_END
+ only if the checksum is correct.
+
+ inflate() will decompress and check either zlib-wrapped or gzip-wrapped
+ deflate data. The header type is detected automatically. Any information
+ contained in the gzip header is not retained, so applications that need that
+ information should instead use raw inflate, see inflateInit2() below, or
+ inflateBack() and perform their own processing of the gzip header and
+ trailer.
+
+ inflate() returns Z_OK if some progress has been made (more input processed
+ or more output produced), Z_STREAM_END if the end of the compressed data has
+ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect check
+ value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+ if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
+ Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+ output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
+ inflate() can be called again with more input and more output space to
+ continue decompressing. If Z_DATA_ERROR is returned, the application may then
+ call inflateSync() to look for a good compression block if a partial recovery
+ of the data is desired.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library.
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ deflateInit is used instead.
+
+ windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
+ determines the window size. deflate() will then generate raw deflate data
+ with no zlib header or trailer, and will not compute an adler32 check value.
+
+ windowBits can also be greater than 15 for optional gzip encoding. Add
+ 16 to windowBits to write a simple gzip header and trailer around the
+ compressed data instead of a zlib wrapper. The gzip header will have no
+ file name, no extra data, no comment, no modification time (set to zero),
+ no header crc, and the operating system will be set to 255 (unknown). If a
+ gzip stream is being written, strm->adler is a crc32 instead of an adler32.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match), or Z_RLE to limit match distances to one (run-length
+ encoding). Filtered data consists mostly of small values with a somewhat
+ random distribution. In this case, the compression algorithm is tuned to
+ compress them better. The effect of Z_FILTERED is to force more Huffman
+ coding and less string matching; it is somewhat intermediate between
+ Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as
+ Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy
+ parameter only affects the compression ratio but not the correctness of the
+ compressed output even if it is not set appropriately. Z_FIXED prevents the
+ use of dynamic Huffman codes, allowing for a simpler decoder for special
+ applications.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+ method). msg is set to null if there is no error message. deflateInit2 does
+ not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. This function must be called
+ immediately after deflateInit, deflateInit2 or deflateReset, before any
+ call of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy; the data can then be compressed better than
+ with the default empty dictionary.
+
+ Depending on the size of the compression data structures selected by
+ deflateInit or deflateInit2, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size in
+ deflate or deflate2. Thus the strings most likely to be useful should be
+ put at the end of the dictionary, not at the front. In addition, the
+ current implementation of deflate will use at most the window size minus
+ 262 bytes of the provided dictionary.
+
+ Upon return of this function, strm->adler is set to the adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.) If a raw deflate was requested, then the
+ adler32 value is not computed and strm->adler is not set.
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent (for example if deflate has already been called for this stream
+ or if the compression method is bsort). deflateSetDictionary does not
+ perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+ int level,
+ int strategy));
+/*
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in deflateInit2. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different
+ strategy. If the compression level is changed, the input available so far
+ is compressed with the old level (and may be flushed); the new level will
+ take effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to
+ be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+ if strm->avail_out was zero.
+*/
+
+ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
+ int good_length,
+ int max_lazy,
+ int nice_length,
+ int max_chain));
+/*
+ Fine tune deflate's internal compression parameters. This should only be
+ used by someone who understands the algorithm used by zlib's deflate for
+ searching for the best matching string, and even then only by the most
+ fanatic optimizer trying to squeeze out the last compressed bit for their
+ specific input data. Read the deflate.c source code for the meaning of the
+ max_lazy, good_length, nice_length, and max_chain parameters.
+
+ deflateTune() can be called after deflateInit() or deflateInit2(), and
+ returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
+ */
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+ uLong sourceLen));
+/*
+ deflateBound() returns an upper bound on the compressed size after
+ deflation of sourceLen bytes. It must be called after deflateInit()
+ or deflateInit2(). This would be used to allocate an output buffer
+ for deflation in a single pass, and so would be called before deflate().
+*/
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ deflatePrime() inserts bits in the deflate output stream. The intent
+ is that this function is used to start off the deflate output with the
+ bits leftover from a previous deflate stream when appending to it. As such,
+ this function can only be used for raw deflate, and must be used before the
+ first deflate() call after a deflateInit2() or deflateReset(). bits must be
+ less than or equal to 16, and that many of the least significant bits of
+ value will be inserted in the output.
+
+ deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
+ gz_headerp head));
+/*
+ deflateSetHeader() provides gzip header information for when a gzip
+ stream is requested by deflateInit2(). deflateSetHeader() may be called
+ after deflateInit2() or deflateReset() and before the first call of
+ deflate(). The text, time, os, extra field, name, and comment information
+ in the provided gz_header structure are written to the gzip header (xflag is
+ ignored -- the extra flags are set according to the compression level). The
+ caller must assure that, if not Z_NULL, name and comment are terminated with
+ a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
+ available there. If hcrc is true, a gzip header crc is included. Note that
+ the current versions of the command-line version of gzip (up through version
+ 1.3.x) do not support header crc's, and will report that it is a "multi-part
+ gzip file" and give up.
+
+ If deflateSetHeader is not used, the default gzip header has text false,
+ the time set to zero, and os set to 255, with no extra, name, or comment
+ fields. The gzip header is returned to the default state by deflateReset().
+
+ deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with an extra parameter. The
+ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+ before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if inflateInit is used
+ instead. windowBits must be greater than or equal to the windowBits value
+ provided to deflateInit2() while compressing, or it must be equal to 15 if
+ deflateInit2() was not used. If a compressed stream with a larger window
+ size is given as input, inflate() will return with the error code
+ Z_DATA_ERROR instead of trying to allocate a larger window.
+
+ windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
+ determines the window size. inflate() will then process raw deflate data,
+ not looking for a zlib or gzip header, not generating a check value, and not
+ looking for any check values for comparison at the end of the stream. This
+ is for use with other formats that use the deflate compressed data format
+ such as zip. Those formats provide their own check values. If a custom
+ format is developed using the raw deflate format for compressed data, it is
+ recommended that a check value such as an adler32 or a crc32 be applied to
+ the uncompressed data as is done in the zlib, gzip, and zip formats. For
+ most applications, the zlib format should be used as is. Note that comments
+ above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+ windowBits can also be greater than 15 for optional gzip decoding. Add
+ 32 to windowBits to enable zlib and gzip decoding with automatic header
+ detection, or add 16 to decode only the gzip format (the zlib format will
+ return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is
+ a crc32 instead of an adler32.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg
+ is set to null if there is no error message. inflateInit2 does not perform
+ any decompression apart from reading the zlib header if present: this will
+ be done by inflate(). (So next_in and avail_in may be modified, but next_out
+ and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+ const Bytef *dictionary,
+ uInt dictLength));
+/*
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of inflate,
+ if that call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the adler32 value returned by that call of inflate.
+ The compressor and decompressor must use exactly the same dictionary (see
+ deflateSetDictionary). For raw inflate, this function can be called
+ immediately after inflateInit2() or inflateReset() and before any call of
+ inflate() to set the dictionary. The application must insure that the
+ dictionary that was used for compression is provided.
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+ Skips invalid compressed data until a full flush point (see above the
+ description of deflate with Z_FULL_FLUSH) can be found, or until all
+ available input is skipped. No output is provided.
+
+ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+ z_streamp source));
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when randomly accessing a large stream. The
+ first pass through the stream can periodically record the inflate state,
+ allowing restarting inflate at those points when randomly accessing the
+ stream.
+
+ inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
+ int bits,
+ int value));
+/*
+ This function inserts bits in the inflate input stream. The intent is
+ that this function is used to start inflating at a bit position in the
+ middle of a byte. The provided bits will be used before any bytes are used
+ from next_in. This function should only be used with raw inflate, and
+ should be used before the first inflate() call after inflateInit2() or
+ inflateReset(). bits must be less than or equal to 16, and that many of the
+ least significant bits of value will be inserted in the input.
+
+ inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
+ gz_headerp head));
+/*
+ inflateGetHeader() requests that gzip header information be stored in the
+ provided gz_header structure. inflateGetHeader() may be called after
+ inflateInit2() or inflateReset(), and before the first call of inflate().
+ As inflate() processes the gzip stream, head->done is zero until the header
+ is completed, at which time head->done is set to one. If a zlib stream is
+ being decoded, then head->done is set to -1 to indicate that there will be
+ no gzip header information forthcoming. Note that Z_BLOCK can be used to
+ force inflate() to return immediately after header processing is complete
+ and before any actual data is decompressed.
+
+ The text, time, xflags, and os fields are filled in with the gzip header
+ contents. hcrc is set to true if there is a header CRC. (The header CRC
+ was valid if done is set to one.) If extra is not Z_NULL, then extra_max
+ contains the maximum number of bytes to write to extra. Once done is true,
+ extra_len contains the actual extra field length, and extra contains the
+ extra field, or that field truncated if extra_max is less than extra_len.
+ If name is not Z_NULL, then up to name_max characters are written there,
+ terminated with a zero unless the length is greater than name_max. If
+ comment is not Z_NULL, then up to comm_max characters are written there,
+ terminated with a zero unless the length is greater than comm_max. When
+ any of extra, name, or comment are not Z_NULL and the respective field is
+ not present in the header, then that field is set to Z_NULL to signal its
+ absence. This allows the use of deflateSetHeader() with the returned
+ structure to duplicate the header. However if those fields are set to
+ allocated memory, then the application will need to save those pointers
+ elsewhere so that they can be eventually freed.
+
+ If inflateGetHeader is not used, then the header information is simply
+ discarded. The header is always checked for validity, including the header
+ CRC if present. inflateReset() will reset the process to discard the header
+ information. The application would need to call inflateGetHeader() again to
+ retrieve the header from the next gzip stream.
+
+ inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
+ unsigned char FAR *window));
+
+ Initialize the internal stream state for decompression using inflateBack()
+ calls. The fields zalloc, zfree and opaque in strm must be initialized
+ before the call. If zalloc and zfree are Z_NULL, then the default library-
+ derived memory allocation routines are used. windowBits is the base two
+ logarithm of the window size, in the range 8..15. window is a caller
+ supplied buffer of that size. Except for special applications where it is
+ assured that deflate was used with small window sizes, windowBits must be 15
+ and a 32K byte window must be supplied to be able to decompress general
+ deflate streams.
+
+ See inflateBack() for the usage of these routines.
+
+ inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+ the paramaters are invalid, Z_MEM_ERROR if the internal state could not
+ be allocated, or Z_VERSION_ERROR if the version of the library does not
+ match the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
+ in_func in, void FAR *in_desc,
+ out_func out, void FAR *out_desc));
+/*
+ inflateBack() does a raw inflate with a single call using a call-back
+ interface for input and output. This is more efficient than inflate() for
+ file i/o applications in that it avoids copying between the output and the
+ sliding window by simply making the window itself the output buffer. This
+ function trusts the application to not change the output buffer passed by
+ the output function, at least until inflateBack() returns.
+
+ inflateBackInit() must be called first to allocate the internal state
+ and to initialize the state with the user-provided window buffer.
+ inflateBack() may then be used multiple times to inflate a complete, raw
+ deflate stream with each call. inflateBackEnd() is then called to free
+ the allocated state.
+
+ A raw deflate stream is one with no zlib or gzip header or trailer.
+ This routine would normally be used in a utility that reads zip or gzip
+ files and writes out uncompressed files. The utility would decode the
+ header and process the trailer on its own, hence this routine expects
+ only the raw deflate stream to decompress. This is different from the
+ normal behavior of inflate(), which expects either a zlib or gzip header and
+ trailer around the deflate stream.
+
+ inflateBack() uses two subroutines supplied by the caller that are then
+ called by inflateBack() for input and output. inflateBack() calls those
+ routines until it reads a complete deflate stream and writes out all of the
+ uncompressed data, or until it encounters an error. The function's
+ parameters and return types are defined above in the in_func and out_func
+ typedefs. inflateBack() will call in(in_desc, &buf) which should return the
+ number of bytes of provided input, and a pointer to that input in buf. If
+ there is no input available, in() must return zero--buf is ignored in that
+ case--and inflateBack() will return a buffer error. inflateBack() will call
+ out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out()
+ should return zero on success, or non-zero on failure. If out() returns
+ non-zero, inflateBack() will return with an error. Neither in() nor out()
+ are permitted to change the contents of the window provided to
+ inflateBackInit(), which is also the buffer that out() uses to write from.
+ The length written by out() will be at most the window size. Any non-zero
+ amount of input may be provided by in().
+
+ For convenience, inflateBack() can be provided input on the first call by
+ setting strm->next_in and strm->avail_in. If that input is exhausted, then
+ in() will be called. Therefore strm->next_in must be initialized before
+ calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called
+ immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in
+ must also be initialized, and then if strm->avail_in is not zero, input will
+ initially be taken from strm->next_in[0 .. strm->avail_in - 1].
+
+ The in_desc and out_desc parameters of inflateBack() is passed as the
+ first parameter of in() and out() respectively when they are called. These
+ descriptors can be optionally used to pass any information that the caller-
+ supplied in() and out() functions need to do their job.
+
+ On return, inflateBack() will set strm->next_in and strm->avail_in to
+ pass back any unused input that was provided by the last in() call. The
+ return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+ if in() or out() returned an error, Z_DATA_ERROR if there was a format
+ error in the deflate stream (in which case strm->msg is set to indicate the
+ nature of the error), or Z_STREAM_ERROR if the stream was not properly
+ initialized. In the case of Z_BUF_ERROR, an input or output error can be
+ distinguished using strm->next_in which will be Z_NULL only if in() returned
+ an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to
+ out() returning non-zero. (in() will always be called before out(), so
+ strm->next_in is assured to be defined if out() returns non-zero.) Note
+ that inflateBack() cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
+/*
+ All memory allocated by inflateBackInit() is freed.
+
+ inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+ state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+/* Return flags indicating compile-time options.
+
+ Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+ 1.0: size of uInt
+ 3.2: size of uLong
+ 5.4: size of voidpf (pointer)
+ 7.6: size of z_off_t
+
+ Compiler, assembler, and debug options:
+ 8: DEBUG
+ 9: ASMV or ASMINF -- use ASM code
+ 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+ 11: 0 (reserved)
+
+ One-time table building (smaller code, but not thread-safe if true):
+ 12: BUILDFIXED -- build static block decoding tables when needed
+ 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+ 14,15: 0 (reserved)
+
+ Library content (indicates missing functionality):
+ 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+ deflate code when not needed)
+ 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+ and decode gzip streams (to avoid linking crc code)
+ 18-19: 0 (reserved)
+
+ Operation variations (changes in library functionality):
+ 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+ 21: FASTEST -- deflate algorithm with only one, lowest compression level
+ 22,23: 0 (reserved)
+
+ The sprintf variant used by gzprintf (zero is best):
+ 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+ 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+ 26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+ Remainder:
+ 27-31: 0 (reserved)
+ */
+
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the
+ basic stream-oriented functions. To simplify the interface, some
+ default options are assumed (compression level and memory usage,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least the value returned
+ by compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen,
+ int level));
+/*
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least the value returned by
+ compressBound(sourceLen). Upon exit, destLen is the actual size of the
+ compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+ compressBound() returns an upper bound on the compressed size after
+ compress() or compress2() on sourceLen bytes. It would be used before
+ a compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,
+ const Bytef *source, uLong sourceLen));
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+/*
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+ Huffman only compression as in "wb1h", or 'R' for run-length encoding
+ as in "wb1R". (See the description of deflateInit2 for more information
+ about the strategy parameter.)
+
+ gzopen can be used to read a file which is not in gzip format; in this
+ case gzread will directly read from the file without decompression.
+
+ gzopen returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR). */
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+ gzdopen() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in gzopen.
+ The next call of gzclose on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+ gzdopen returns NULL if there was insufficient memory to allocate
+ the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+ Dynamically update the compression level or strategy. See the description
+ of deflateInit2 for the meaning of these parameters.
+ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+ opened for writing.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, gzread copies the given number
+ of bytes into the buffer.
+ gzread returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+ voidpc buf, unsigned len));
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes actually written
+ (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...));
+/*
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error). The number of
+ uncompressed bytes written is limited to 4095. The caller should assure that
+ this limit is not exceeded. If it is exceeded, then gzprintf() will return
+ return an error (0) with nothing written. In this case, there may also be a
+ buffer overflow with unpredictable consequences, which is possible only if
+ zlib was compiled with the insecure functions sprintf() or vsprintf()
+ because the secure snprintf() or vsnprintf() functions were not available.
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+ Reads bytes from the compressed file until len-1 characters are read, or
+ a newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. The string is then terminated with a null
+ character.
+ gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
+/*
+ Push one character back onto the stream to be read again later.
+ Only one character of push-back is allowed. gzungetc() returns the
+ character pushed, or -1 on failure. gzungetc() will fail if a
+ character has been pushed but not read yet, or if c is -1. The pushed
+ character will be discarded if the stream is repositioned with gzseek()
+ or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function. The return value is the zlib
+ error number (see function gzerror below). gzflush returns Z_OK if
+ the flush parameter is Z_FINISH and all output could be flushed.
+ gzflush should be called only when strictly necessary because it can
+ degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+ z_off_t offset, int whence));
+/*
+ Sets the starting position for the next gzread or gzwrite on the
+ given compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported; gzseek then compresses a sequence of zeroes up to the new
+ starting position.
+
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT gzrewind OF((gzFile file));
+/*
+ Rewinds the given file. This function is supported only for reading.
+
+ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file));
+/*
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+
+ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
+/*
+ Returns 1 if file is being read directly without decompression, otherwise
+ zero.
+*/
+
+ZEXTERN int ZEXPORT gzclose OF((gzFile file));
+/*
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state. The return value is the zlib
+ error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+ Clears the error and end-of-file flags for file. This is analogous to the
+ clearerr() function in stdio. This is useful for continuing to read a gzip
+ file that is being written concurrently.
+*/
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
+ z_off_t len2));
+/*
+ Combine two Adler-32 checksums into one. For two sequences of bytes, seq1
+ and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
+ each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of
+ seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.
+*/
+
+ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len));
+/*
+ Update a running CRC-32 with the bytes buf[0..len-1] and return the
+ updated CRC-32. If buf is NULL, this function returns the required initial
+ value for the for the crc. Pre- and post-conditioning (one's complement) is
+ performed within this function so it shouldn't be done by the application.
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
+
+/*
+ Combine two CRC-32 check values into one. For two sequences of bytes,
+ seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
+ calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32
+ check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
+ len2.
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method,
+ int windowBits, int memLevel,
+ int strategy, const char *version,
+ int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
+ unsigned char FAR *window,
+ const char *version,
+ int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+#define inflateBackInit(strm, windowBits, window) \
+ inflateBackInit_((strm), (windowBits), (window), \
+ ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
+ struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char * ZEXPORT zError OF((int));
+ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */
--- /dev/null
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zutil.h"
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+const char * const z_errmsg[10] = {
+"need dictionary", /* Z_NEED_DICT 2 */
+"stream end", /* Z_STREAM_END 1 */
+"", /* Z_OK 0 */
+"file error", /* Z_ERRNO (-1) */
+"stream error", /* Z_STREAM_ERROR (-2) */
+"data error", /* Z_DATA_ERROR (-3) */
+"insufficient memory", /* Z_MEM_ERROR (-4) */
+"buffer error", /* Z_BUF_ERROR (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char * ZEXPORT zlibVersion()
+{
+ return ZLIB_VERSION;
+}
+
+uLong ZEXPORT zlibCompileFlags()
+{
+ uLong flags;
+
+ flags = 0;
+ switch (sizeof(uInt)) {
+ case 2: break;
+ case 4: flags += 1; break;
+ case 8: flags += 2; break;
+ default: flags += 3;
+ }
+ switch (sizeof(uLong)) {
+ case 2: break;
+ case 4: flags += 1 << 2; break;
+ case 8: flags += 2 << 2; break;
+ default: flags += 3 << 2;
+ }
+ switch (sizeof(voidpf)) {
+ case 2: break;
+ case 4: flags += 1 << 4; break;
+ case 8: flags += 2 << 4; break;
+ default: flags += 3 << 4;
+ }
+ switch (sizeof(z_off_t)) {
+ case 2: break;
+ case 4: flags += 1 << 6; break;
+ case 8: flags += 2 << 6; break;
+ default: flags += 3 << 6;
+ }
+#ifdef DEBUG
+ flags += 1 << 8;
+#endif
+#if defined(ASMV) || defined(ASMINF)
+ flags += 1 << 9;
+#endif
+#ifdef ZLIB_WINAPI
+ flags += 1 << 10;
+#endif
+#ifdef BUILDFIXED
+ flags += 1 << 12;
+#endif
+#ifdef DYNAMIC_CRC_TABLE
+ flags += 1 << 13;
+#endif
+#ifdef NO_GZCOMPRESS
+ flags += 1L << 16;
+#endif
+#ifdef NO_GZIP
+ flags += 1L << 17;
+#endif
+#ifdef PKZIP_BUG_WORKAROUND
+ flags += 1L << 20;
+#endif
+#ifdef FASTEST
+ flags += 1L << 21;
+#endif
+#ifdef STDC
+# ifdef NO_vsnprintf
+ flags += 1L << 25;
+# ifdef HAS_vsprintf_void
+ flags += 1L << 26;
+# endif
+# else
+# ifdef HAS_vsnprintf_void
+ flags += 1L << 26;
+# endif
+# endif
+#else
+ flags += 1L << 24;
+# ifdef NO_snprintf
+ flags += 1L << 25;
+# ifdef HAS_sprintf_void
+ flags += 1L << 26;
+# endif
+# else
+# ifdef HAS_snprintf_void
+ flags += 1L << 26;
+# endif
+# endif
+#endif
+ return flags;
+}
+
+#ifdef DEBUG
+
+# ifndef verbose
+# define verbose 0
+# endif
+int z_verbose = verbose;
+
+void z_error (m)
+ char *m;
+{
+ fprintf(stderr, "%s\n", m);
+ exit(1);
+}
+#endif
+
+/* exported to allow conversion of error code to string for compress() and
+ * uncompress()
+ */
+const char * ZEXPORT zError(err)
+ int err;
+{
+ return ERR_MSG(err);
+}
+
+#if defined(_WIN32_WCE)
+ /* The Microsoft C Run-Time Library for Windows CE doesn't have
+ * errno. We define it as a global variable to simplify porting.
+ * Its value is always 0 and should not be used.
+ */
+ int errno = 0;
+#endif
+
+#ifndef HAVE_MEMCPY
+
+void zmemcpy(dest, source, len)
+ Bytef* dest;
+ const Bytef* source;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = *source++; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+
+int zmemcmp(s1, s2, len)
+ const Bytef* s1;
+ const Bytef* s2;
+ uInt len;
+{
+ uInt j;
+
+ for (j = 0; j < len; j++) {
+ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+ }
+ return 0;
+}
+
+void zmemzero(dest, len)
+ Bytef* dest;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = 0; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+#endif
+
+
+#ifdef SYS16BIT
+
+#ifdef __TURBOC__
+/* Turbo C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+ voidpf org_ptr;
+ voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ voidpf buf = opaque; /* just to make some compilers happy */
+ ulg bsize = (ulg)items*size;
+
+ /* If we allocate less than 65520 bytes, we assume that farmalloc
+ * will return a usable pointer which doesn't have to be normalized.
+ */
+ if (bsize < 65520L) {
+ buf = farmalloc(bsize);
+ if (*(ush*)&buf != 0) return buf;
+ } else {
+ buf = farmalloc(bsize + 16L);
+ }
+ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+ table[next_ptr].org_ptr = buf;
+
+ /* Normalize the pointer to seg:0 */
+ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+ *(ush*)&buf = 0;
+ table[next_ptr++].new_ptr = buf;
+ return buf;
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ int n;
+ if (*(ush*)&ptr != 0) { /* object < 64K */
+ farfree(ptr);
+ return;
+ }
+ /* Find the original pointer */
+ for (n = 0; n < next_ptr; n++) {
+ if (ptr != table[n].new_ptr) continue;
+
+ farfree(table[n].org_ptr);
+ while (++n < next_ptr) {
+ table[n-1] = table[n];
+ }
+ next_ptr--;
+ return;
+ }
+ ptr = opaque; /* just to make some compilers happy */
+ Assert(0, "zcfree: ptr not found");
+}
+
+#endif /* __TURBOC__ */
+
+
+#ifdef M_I86
+/* Microsoft C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+# define _halloc halloc
+# define _hfree hfree
+#endif
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ return _halloc((long)items, size);
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ _hfree(ptr);
+}
+
+#endif /* M_I86 */
+
+#endif /* SYS16BIT */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp malloc OF((uInt size));
+extern voidp calloc OF((uInt items, uInt size));
+extern void free OF((voidpf ptr));
+#endif
+
+voidpf zcalloc (opaque, items, size)
+ voidpf opaque;
+ unsigned items;
+ unsigned size;
+{
+ if (opaque) items += size - size; /* make compiler happy */
+ return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
+ (voidpf)calloc(items, size);
+}
+
+void zcfree (opaque, ptr)
+ voidpf opaque;
+ voidpf ptr;
+{
+ free(ptr);
+ if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
--- /dev/null
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZUTIL_H
+#define ZUTIL_H
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+#ifdef STDC
+# ifndef _WIN32_WCE
+# include <stddef.h>
+# endif
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+# ifdef _WIN32_WCE
+ /* The Microsoft C Run-Time Library for Windows CE doesn't have
+ * errno. We define it as a global variable to simplify porting.
+ * Its value is always 0 and should not be used. We rename it to
+ * avoid conflict with other libraries that use the same workaround.
+ */
+# define errno z_errno
+# endif
+ extern int errno;
+#else
+# ifndef _WIN32_WCE
+# include <errno.h>
+# endif
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+ return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+ /* common constants */
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+ /* target dependencies */
+
+#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
+# define OS_CODE 0x00
+# if defined(__TURBOC__) || defined(__BORLANDC__)
+# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+ /* Allow compilation with ANSI keywords only enabled */
+ void _Cdecl farfree( void *block );
+ void *_Cdecl farmalloc( unsigned long nbytes );
+# else
+# include <alloc.h>
+# endif
+# else /* MSC or DJGPP */
+# include <malloc.h>
+# endif
+#endif
+
+#ifdef AMIGA
+# define OS_CODE 0x01
+#endif
+
+#if defined(VAXC) || defined(VMS)
+# define OS_CODE 0x02
+# define F_OPEN(name, mode) \
+ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#if defined(ATARI) || defined(atarist)
+# define OS_CODE 0x05
+#endif
+
+#ifdef OS2
+# define OS_CODE 0x06
+# ifdef M_I86
+ #include <malloc.h>
+# endif
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+# define OS_CODE 0x07
+# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fdopen */
+# else
+# ifndef fdopen
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# endif
+# endif
+#endif
+
+#ifdef TOPS20
+# define OS_CODE 0x0a
+#endif
+
+#ifdef WIN32
+# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */
+# define OS_CODE 0x0b
+# endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+# define OS_CODE 0x0f
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600))
+# if defined(_WIN32_WCE)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# ifndef _PTRDIFF_T_DEFINED
+ typedef int ptrdiff_t;
+# define _PTRDIFF_T_DEFINED
+# endif
+# else
+# define fdopen(fd,type) _fdopen(fd,type)
+# endif
+#endif
+
+ /* common defaults */
+
+#ifndef OS_CODE
+# define OS_CODE 0x03 /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+# define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+ /* functions */
+
+#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+#if defined(__CYGWIN__)
+# ifndef HAVE_VSNPRINTF
+# define HAVE_VSNPRINTF
+# endif
+#endif
+#ifndef HAVE_VSNPRINTF
+# ifdef MSDOS
+ /* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
+ but for now we just assume it doesn't. */
+# define NO_vsnprintf
+# endif
+# ifdef __TURBOC__
+# define NO_vsnprintf
+# endif
+# ifdef WIN32
+ /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
+# if !defined(vsnprintf) && !defined(NO_vsnprintf)
+# define vsnprintf _vsnprintf
+# endif
+# endif
+# ifdef __SASC
+# define NO_vsnprintf
+# endif
+#endif
+#ifdef VMS
+# define NO_vsnprintf
+#endif
+
+#if defined(pyr)
+# define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+ * You may have to use the same strategy for Borland C (untested).
+ * The __SC__ check is for Symantec.
+ */
+# define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+# define zmemcpy _fmemcpy
+# define zmemcmp _fmemcmp
+# define zmemzero(dest, len) _fmemset(dest, 0, len)
+# else
+# define zmemcpy memcpy
+# define zmemcmp memcmp
+# define zmemzero(dest, len) memset(dest, 0, len)
+# endif
+#else
+ extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
+ extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
+ extern void zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+# include <stdio.h>
+ extern int z_verbose;
+ extern void z_error OF((char *m));
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) {if (z_verbose>=0) fprintf x ;}
+# define Tracev(x) {if (z_verbose>0) fprintf x ;}
+# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+void zcfree OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* ZUTIL_H */
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/vfs/FileSystem.hpp>
+
+#include <fstream>
+
+#include <physfs.h>
+
+namespace Unison
+{
+ namespace VFS
+ {
+ FileSystem::FileSystem()
+ {
+#if __USE_POSIX
+ std::ifstream cmdline("/proc/self/cmdline");
+ std::string argv0;
+ std::getline(cmdline, argv0, '\0');
+ PHYSFS_init(argv0.c_str());
+#else
+ PHYSFS_init(0);
+#endif
+ }
+
+ FileSystem::~FileSystem()
+ {
+ PHYSFS_deinit();
+ }
+
+ void FileSystem::follow_sym_links(bool follow)
+ {
+ PHYSFS_permitSymbolicLinks(follow);
+ }
+
+ std::string FileSystem::get_dir_sep()
+ {
+ return PHYSFS_getDirSeparator();
+ }
+
+ std::string FileSystem::get_base_dir()
+ {
+ return PHYSFS_getBaseDir();
+ }
+
+ std::string FileSystem::get_user_dir()
+ {
+ return PHYSFS_getUserDir();
+ }
+
+ std::string FileSystem::get_write_dir()
+ {
+ return PHYSFS_getWriteDir();
+ }
+
+ void FileSystem::set_write_dir(const std::string &write_dir)
+ {
+ PHYSFS_setWriteDir(write_dir.c_str());
+ }
+
+ void FileSystem::mount(const std::string &path, const std::string &mount_point, bool append)
+ {
+ PHYSFS_mount(path.c_str(), mount_point.c_str(), append);
+ }
+
+ void FileSystem::umount(const std::string &path)
+ {
+ PHYSFS_removeFromSearchPath(path.c_str());
+ }
+
+ std::vector<std::string> FileSystem::get_search_path()
+ {
+ std::vector<std::string> paths;
+ char **search_path = PHYSFS_getSearchPath();
+ for(char **iter = search_path;*iter;++iter)
+ {
+ paths.push_back(*iter);
+ }
+ PHYSFS_freeList(search_path);
+ return paths;
+ }
+
+ std::string FileSystem::get_mount_point(const std::string &path)
+ {
+ return PHYSFS_getMountPoint(path.c_str());
+ }
+
+ void FileSystem::mkdir(const std::string &dir)
+ {
+ PHYSFS_mkdir(dir.c_str());
+ }
+
+ void FileSystem::rm(const std::string &filename)
+ {
+ PHYSFS_delete(filename.c_str());
+ }
+
+ std::vector<std::string> FileSystem::ls(const std::string &path)
+ {
+ std::vector<std::string> files;
+ char **physfs_files = PHYSFS_enumerateFiles(path.c_str());
+ for(char **iter = physfs_files;*iter;++iter)
+ {
+ files.push_back(*iter);
+ }
+ PHYSFS_freeList(physfs_files);
+ return files;
+ }
+
+ bool FileSystem::exists(const std::string &filename)
+ {
+ return PHYSFS_exists(filename.c_str());
+ }
+
+ bool FileSystem::is_dir(const std::string &filename)
+ {
+ return PHYSFS_isDirectory(filename.c_str());
+ }
+
+ std::string FileSystem::get_real_dir(const std::string &filename)
+ {
+ return PHYSFS_getRealDir(filename.c_str());
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/vfs/sdl/Utils.hpp>
+
+#include <fstream>
+#include <assert.h>
+
+#include <physfs.h>
+#include "SDL.h"
+
+namespace
+{
+ int rwops_seek(SDL_RWops *context, int offset, int whence)
+ {
+ PHYSFS_File *file = reinterpret_cast<PHYSFS_File *>(context->hidden.unknown.data1);
+ int res = 0;
+ switch(whence) {
+ case SEEK_SET:
+ res = PHYSFS_seek(file, offset);
+ break;
+ case SEEK_CUR:
+ res = PHYSFS_seek(file, PHYSFS_tell(file) + offset);
+ break;
+ case SEEK_END:
+ res = PHYSFS_seek(file, PHYSFS_fileLength(file) + offset);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return (int) PHYSFS_tell(file);
+ }
+
+ int rwops_read(SDL_RWops *context, void *ptr, int size, int maxnum)
+ {
+ PHYSFS_File *file = reinterpret_cast<PHYSFS_File *>(context->hidden.unknown.data1);
+
+ int res = PHYSFS_read(file, ptr, size, maxnum);
+ return res;
+ }
+
+ int rwops_close(SDL_RWops *context)
+ {
+ PHYSFS_File *file = reinterpret_cast<PHYSFS_File *>(context->hidden.unknown.data1);
+
+ PHYSFS_close(file);
+ delete context;
+
+ return 0;
+ }
+}
+
+
+namespace Unison
+{
+ namespace VFS
+ {
+ namespace SDL
+ {
+ SDL_RWops *Utils::open_physfs_in(const std::string &filename)
+ {
+ PHYSFS_File *file = PHYSFS_openRead(filename.c_str());
+ assert(file);
+ SDL_RWops* ops = new SDL_RWops;
+ ops->type = 0;
+ ops->hidden.unknown.data1 = file;
+ ops->seek = rwops_seek;
+ ops->read = rwops_read;
+ ops->write = 0;
+ ops->close = rwops_close;
+ return ops;
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/vfs/stream.hpp>
+
+#include <streambuf>
+#include <assert.h>
+#include <physfs.h>
+
+// FIXME: make streambufs more complete
+namespace
+{
+ class iphysfsfilebuf : public std::streambuf
+ {
+ public:
+ iphysfsfilebuf(const std::string &filename)
+ {
+ file = PHYSFS_openRead(filename.c_str());
+ assert(file);
+ }
+
+ ~iphysfsfilebuf()
+ {
+ PHYSFS_close(file);
+ }
+ protected:
+ int underflow()
+ {
+ if(PHYSFS_eof(file))
+ {
+ return traits_type::eof();
+ }
+ PHYSFS_sint64 bytes = PHYSFS_read(file, buffer, 1, sizeof(buffer));
+ if(bytes <= 0)
+ {
+ return traits_type::eof();
+ }
+ setg(buffer, buffer, buffer + bytes);
+ return buffer[0];
+ }
+
+ pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode which)
+ {
+ off_type nsp = off;
+ PHYSFS_sint64 pos = PHYSFS_tell(file);
+ switch(way)
+ {
+ case std::ios_base::beg:
+ break;
+ case std::ios_base::cur:
+ if(off == 0)
+ {
+ return pos - (egptr() - gptr());
+ }
+ nsp += pos - (egptr() - gptr());
+ break;
+ case std::ios_base::end:
+ nsp += PHYSFS_fileLength(file);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ return seekpos(nsp, which);
+ }
+
+ pos_type seekpos(pos_type sp, std::ios_base::openmode /*which*/)
+ {
+ if(PHYSFS_seek(file, sp) == 0)
+ {
+ return -1;
+ }
+ setg(buffer, buffer, buffer);
+ return sp;
+ }
+ private:
+ PHYSFS_File *file;
+ char buffer[1024];
+ };
+
+ class ophysfsfilebuf : public std::streambuf
+ {
+ public:
+ ophysfsfilebuf(const std::string &filename)
+ {
+ file = PHYSFS_openWrite(filename.c_str());
+ assert(file);
+ setp(buffer, buffer + sizeof(buffer));
+ }
+
+ ~ophysfsfilebuf()
+ {
+ sync();
+ PHYSFS_close(file);
+ }
+ protected:
+ int sync()
+ {
+ return overflow(traits_type::eof());
+ }
+
+ int overflow(int c)
+ {
+ char ch = static_cast<char>(c);
+ size_t size = pptr() - pbase();
+ if(size == 0)
+ {
+ return 0;
+ }
+ PHYSFS_sint64 bytes = PHYSFS_write(file, pbase(), 1, size);
+ if(bytes <= 0)
+ {
+ return traits_type::eof();
+ }
+ if(c != traits_type::eof())
+ {
+ PHYSFS_sint64 bytes = PHYSFS_write(file, &ch, 1, 1);
+ if(bytes <= 0)
+ {
+ return traits_type::eof();
+ }
+ }
+ setp(buffer, buffer + bytes);
+ return 0;
+ }
+ private:
+ PHYSFS_File *file;
+ char buffer[1024];
+ };
+}
+
+namespace Unison
+{
+ namespace VFS
+ {
+ istream::istream(const std::string &filename) :
+ std::istream(new iphysfsfilebuf(filename))
+ {
+ }
+
+ istream::~istream()
+ {
+ delete rdbuf();
+ }
+
+ ostream::ostream(const std::string &filename) :
+ std::ostream(new ophysfsfilebuf(filename))
+ {
+ }
+
+ ostream::~ostream()
+ {
+ delete rdbuf();
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/video/Blittable.hpp>
+#include <unison/video/DisplayList.hpp>
+
+namespace Unison
+{
+ namespace Video
+ {
+ Blittable::~Blittable()
+ {
+ }
+
+ void Blittable::blit_section(const SurfaceSection §ion, const Point &dst_pos, const RenderOptions &options)
+ {
+ blit(section.image, dst_pos, section.clip_rect, options);
+ }
+
+ void Blittable::blit_section(const TextureSection §ion, const Point &dst_pos, const RenderOptions &options)
+ {
+ blit(section.image, dst_pos, section.clip_rect, options);
+ }
+
+ void Blittable::draw(const DisplayList &list)
+ {
+ list.draw(this);
+ }
+
+ BlittableSection::BlittableSection(Blittable &image, const Rect &clip_rect) :
+ image(image),
+ clip_rect(clip_rect)
+ {
+ }
+
+ void BlittableSection::blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ Rect overlap = clip_rect.get_overlap(Rect(dst_pos, src_rect.size));
+ if(overlap == Rect())
+ {
+ return;
+ }
+ Rect clipped_src_rect(src_rect.pos, overlap.size);
+ clipped_src_rect.pos += overlap.pos;
+ clipped_src_rect.pos -= dst_pos;
+ image.blit(src, overlap.pos - clip_rect.pos, clipped_src_rect, options);
+ }
+
+ void BlittableSection::blit(const Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ Rect overlap = clip_rect.get_overlap(Rect(dst_pos, src_rect.size));
+ if(overlap == Rect())
+ {
+ return;
+ }
+ Rect clipped_src_rect(src_rect.pos, overlap.size);
+ clipped_src_rect.pos += overlap.pos;
+ clipped_src_rect.pos -= dst_pos;
+ image.blit(src, overlap.pos - clip_rect.pos, clipped_src_rect, options);
+ }
+
+ void BlittableSection::fill(const Color &color, const Rect &rect)
+ {
+ Rect overlap = clip_rect.get_overlap(rect);
+ overlap.pos -= clip_rect.pos;
+ image.fill(color, overlap);
+ }
+
+ void BlittableSection::fill_blend(const Color &color, const Rect &rect)
+ {
+ Rect overlap = clip_rect.get_overlap(rect);
+ overlap.pos -= clip_rect.pos;
+ image.fill_blend(color, overlap);
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/video/Blitters.hpp>
+#include <unison/video/Surface.hpp>
+
+#include <assert.h>
+
+namespace Unison
+{
+ namespace Video
+ {
+ void Blitters::blit_upper(const Surface &src, Rect src_rect, Surface &dst, Point dst_pos, void (*blit_lower)(const Surface &, const Rect &, Surface &, const Point &))
+ {
+ assert(src.get_pixels());
+ assert(dst.get_pixels());
+ assert(blit_lower);
+ if(src_rect == Rect())
+ {
+ src_rect.size.x = src.get_size().x;
+ src_rect.size.y = src.get_size().y;
+ }
+ if(dst_pos.x < 0)
+ {
+ if(src_rect.size.x < (unsigned int) -dst_pos.x)
+ {
+ return;
+ }
+ src_rect.pos.x += -dst_pos.x;
+ src_rect.size.x += dst_pos.x;
+ dst_pos.x = 0;
+ }
+ if(dst_pos.y < 0)
+ {
+ if(src_rect.size.y < (unsigned int) -dst_pos.y)
+ {
+ return;
+ }
+ src_rect.pos.y += -dst_pos.y;
+ src_rect.size.y += dst_pos.y;
+ dst_pos.y = 0;
+ }
+ if(src_rect.pos.x < 0)
+ {
+ if(src_rect.size.x < (unsigned int) -src_rect.pos.x)
+ {
+ return;
+ }
+ src_rect.size.x += src_rect.pos.x;
+ src_rect.pos.x = 0;
+ }
+ if(src_rect.pos.y < 0)
+ {
+ if(src_rect.size.y < (unsigned int) -src_rect.pos.y)
+ {
+ return;
+ }
+ src_rect.size.y += src_rect.pos.y;
+ src_rect.pos.y = 0;
+ }
+ if(src_rect.get_right() > (int) src.get_size().x)
+ {
+ src_rect.size.x = src.get_size().x - src_rect.pos.x;
+ }
+ if(src_rect.get_bottom() > (int) src.get_size().y)
+ {
+ src_rect.size.y = src.get_size().y - src_rect.pos.y;
+ }
+ if(dst_pos.x + src_rect.size.x > dst.get_size().x)
+ {
+ src_rect.size.x = dst.get_size().x - dst_pos.x;
+ }
+ if(dst_pos.y + src_rect.size.y > dst.get_size().y)
+ {
+ src_rect.size.y = dst.get_size().y - dst_pos.y;
+ }
+ blit_lower(src, src_rect, dst, dst_pos);
+ }
+
+ void Blitters::blit_lower_none(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos)
+ {
+ if(src_rect.pos == Point() && dst_pos == Point() && src.get_size().x == dst.get_size().x && src_rect.size.x == dst.get_size().x)
+ {
+ memcpy(dst.get_pixels(), src.get_pixels(), src_rect.size.x * src_rect.size.y * sizeof(Color));
+ }
+ else
+ {
+ const Color *src_line = src.get_pixels() + src_rect.pos.y * src.get_size().x + src_rect.pos.x;
+ Color *dst_line = dst.get_pixels() + dst_pos.y * dst.get_size().x + dst_pos.x;
+ for(unsigned int y = 0;y < src_rect.size.y;y++)
+ {
+ memcpy(dst_line, src_line, src_rect.size.x * sizeof(Color));
+ src_line += src.get_size().x;
+ dst_line += dst.get_size().x;
+ }
+ }
+ }
+
+ void Blitters::blit_lower_mask(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos)
+ {
+ const Color *src_pixel = src.get_pixels() + src_rect.pos.y * src.get_size().x + src_rect.pos.x;
+ Color *dst_pixel = dst.get_pixels() + dst_pos.y * dst.get_size().x + dst_pos.x;
+ for(unsigned int y = 0;y < src_rect.size.y;y++)
+ {
+ for(unsigned int x = 0;x < src_rect.size.x;x++)
+ {
+ if(src_pixel->alpha)
+ {
+ *dst_pixel = *src_pixel;
+ }
+ src_pixel++;
+ dst_pixel++;
+ }
+ src_pixel += src.get_size().x - src_rect.size.x;
+ dst_pixel += dst.get_size().x - src_rect.size.x;
+ }
+ }
+
+ void Blitters::blit_lower_alpha(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos)
+ {
+ const Color *src_pixel = src.get_pixels() + src_rect.pos.y * src.get_size().x + src_rect.pos.x;
+ Color *dst_pixel = dst.get_pixels() + dst_pos.y * dst.get_size().x + dst_pos.x;
+ for(unsigned int y = 0;y < src_rect.size.y;y++)
+ {
+ for(unsigned int x = 0;x < src_rect.size.x;x++)
+ {
+ dst_pixel->red += src_pixel->red * src_pixel->alpha - dst_pixel->red * src_pixel->alpha;
+ dst_pixel->green += src_pixel->green * src_pixel->alpha - dst_pixel->green * src_pixel->alpha;
+ dst_pixel->blue += src_pixel->blue * src_pixel->alpha - dst_pixel->green * src_pixel->blue;
+ src_pixel++;
+ dst_pixel++;
+ }
+ src_pixel += src.get_size().x - src_rect.size.x;
+ dst_pixel += dst.get_size().x - src_rect.size.x;
+ }
+ }
+
+ void Blitters::blit_lower_add(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos)
+ {
+ const Color *src_pixel = src.get_pixels() + src_rect.pos.y * src.get_size().x + src_rect.pos.x;
+ Color *dst_pixel = dst.get_pixels() + dst_pos.y * dst.get_size().x + dst_pos.x;
+ for(unsigned int y = 0;y < src_rect.size.y;y++)
+ {
+ for(unsigned int x = 0;x < src_rect.size.x;x++)
+ {
+ if(src_pixel->red != 0 && dst_pixel->red != 0xff)
+ {
+ int redsum = dst_pixel->red + src_pixel->red * src_pixel->alpha / 0xff;
+ dst_pixel->red = redsum & ~0xff ? 0xff : redsum;
+ }
+ if(src_pixel->green != 0 && dst_pixel->green != 0xff)
+ {
+ int greensum = dst_pixel->green + src_pixel->green * src_pixel->alpha / 0xff;
+ dst_pixel->green = greensum & ~0xff ? 0xff : greensum;
+ }
+ if(src_pixel->blue != 0 && dst_pixel->blue != 0xff)
+ {
+ int bluesum = dst_pixel->blue + src_pixel->blue * src_pixel->alpha / 0xff;
+ dst_pixel->blue = bluesum & ~0xff ? 0xff : bluesum;
+ }
+ src_pixel++;
+ dst_pixel++;
+ }
+ src_pixel += src.get_size().x - src_rect.size.x;
+ dst_pixel += dst.get_size().x - src_rect.size.x;
+ }
+ }
+
+ void Blitters::blit_lower_mod(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos)
+ {
+ const Color *src_pixel = src.get_pixels() + src_rect.pos.y * src.get_size().x + src_rect.pos.x;
+ Color *dst_pixel = dst.get_pixels() + dst_pos.y * dst.get_size().x + dst_pos.x;
+ for(unsigned int y = 0;y < src_rect.size.y;y++)
+ {
+ for(unsigned int x = 0;x < src_rect.size.x;x++)
+ {
+ dst_pixel->red = dst_pixel->red * src_pixel->red / 0xff;
+ dst_pixel->green = dst_pixel->green * src_pixel->green / 0xff;
+ dst_pixel->blue = dst_pixel->blue * src_pixel->blue / 0xff;
+ dst_pixel->alpha = dst_pixel->alpha * src_pixel->alpha / 0xff;
+ src_pixel++;
+ dst_pixel++;
+ }
+ src_pixel += src.get_size().x - src_rect.size.x;
+ dst_pixel += dst.get_size().x - src_rect.size.x;
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/video/Color.hpp>
+
+namespace Unison
+{
+ namespace Video
+ {
+ const Color Color::BLACK(0, 0, 0);
+ const Color Color::RED(0xff, 0, 0);
+ const Color Color::GREEN(0, 0xff, 0);
+ const Color Color::BLUE(0, 0, 0xff);
+ const Color Color::CYAN(0, 0xff, 0xff);
+ const Color Color::MAGENTA(0xff, 0, 0xff);
+ const Color Color::YELLOW(0xff, 0xff, 0);
+ const Color Color::WHITE(0xff, 0xff, 0xff);
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/video/Renderers.hpp>
+#include <unison/video/Window.hpp>
+#include <unison/video/Texture.hpp>
+#include "auto/Renderer.hpp"
+#include "sdl/Renderer.hpp"
+#include "opengl/Renderer.hpp"
+
+#include <assert.h>
+
+namespace Unison
+{
+ namespace Video
+ {
+ Renderers::Renderers() :
+ auto_renderer(0),
+ renderer(0),
+ renderers()
+ {
+ auto_renderer = new Auto::Renderer(renderers);
+ renderer = auto_renderer;
+ add_renderer(new SDL::Renderer());
+ add_renderer(new OpenGL::Renderer());
+ renderer->init();
+ }
+
+ Renderers::~Renderers()
+ {
+ assert(renderer);
+ renderer->quit();
+ std::for_each(renderers.begin(), renderers.end(), std::ptr_fun(operator delete));
+ delete auto_renderer;
+ }
+
+ Renderers &Renderers::get()
+ {
+ static Renderers renderers;
+ return renderers;
+ }
+
+ namespace
+ {
+ bool match_name(Backend::Renderer *renderer, std::string name)
+ {
+ return renderer && renderer->get_name() == name;
+ }
+ }
+
+ void Renderers::set_renderer(const std::string &name)
+ {
+ Area window_size;
+ bool fullscreen = false;
+ if(Window::get().is_open())
+ {
+ window_size = Window::get().get_size();
+ fullscreen = Window::get().is_fullscreen();
+ }
+ std::vector<Surface> surfaces = Texture::save_textures();
+ renderer->quit();
+ if(name == "auto")
+ {
+ renderer = auto_renderer;
+ }
+ else
+ {
+ std::vector<Backend::Renderer *>::iterator found = std::find_if(renderers.begin(), renderers.end(), std::bind2nd(std::ptr_fun(match_name), name));
+ if(found == renderers.end())
+ {
+ fprintf(stderr, "Renderer '%s' not found.\n", name.c_str());
+ return;
+ }
+ renderer = *found;
+ }
+ renderer->init();
+ Texture::load_textures(surfaces);
+ if(window_size != Area())
+ {
+ Window::get().open(window_size, fullscreen);
+ }
+ }
+
+
+ Backend::Renderer &Renderers::get_renderer()
+ {
+ assert(renderer);
+ return *renderer;
+ }
+
+ void Renderers::add_renderer(Backend::Renderer *renderer)
+ {
+ renderers.push_back(renderer);
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/video/Texture.hpp>
+#include <unison/video/Window.hpp>
+#include <unison/video/Renderers.hpp>
+#include <unison/video/Color.hpp>
+#include <unison/video/sdl/Blitters.hpp>
+#include <unison/video/backend/Renderer.hpp>
+#include <unison/video/backend/Texture.hpp>
+#include <unison/vfs/stream.hpp>
+
+#include <assert.h>
+
+namespace Unison
+{
+ namespace Video
+ {
+ Surface::Surface() :
+ pixels(0)
+ {
+ }
+
+ Surface::Surface(const std::string &filename) :
+ pixels(0)
+ {
+ *this = Renderers::get().get_renderer().load_surface(filename);
+ }
+
+ Surface::Surface(const std::string &filename, const Color &colorkey) :
+ pixels(0)
+ {
+ *this = Renderers::get().get_renderer().load_surface(filename, colorkey);
+ }
+
+ Surface::Surface(const Area &size) :
+ pixels(new PixelBuffer(size))
+ {
+ //fill(Color::WHITE);
+ }
+
+ Surface::Surface(const Surface &rhs) :
+ Blittable(),
+ pixels(rhs.pixels)
+ {
+ if(pixels)
+ {
+ pixels->ref();
+ }
+ }
+
+ Surface::~Surface()
+ {
+ if(pixels)
+ {
+ pixels->unref();
+ }
+ }
+
+ Surface &Surface::operator =(const Surface &rhs)
+ {
+ if(rhs.pixels)
+ {
+ rhs.pixels->ref();
+ }
+ if(pixels)
+ {
+ pixels->unref();
+ }
+ pixels = rhs.pixels;
+ return *this;
+ }
+
+ void Surface::save(const std::string &filename) const
+ {
+ Renderers::get().get_renderer().save_surface(*this, filename);
+ }
+
+ void Surface::blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ cow();
+ assert(pixels);
+ Renderers::get().get_renderer().blit(src, src_rect, *this, dst_pos, options);
+ }
+
+ void Surface::blit(const Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ cow();
+ assert(pixels);
+ Renderers::get().get_renderer().blit(Backend::Texture::get_texture(src.get_id()), src_rect, *this, dst_pos, options);
+ }
+
+ void Surface::fill(const Color &color, const Rect &rect)
+ {
+ cow();
+ assert(pixels);
+ Renderers::get().get_renderer().fill(*this, color, rect);
+ }
+
+ void Surface::fill_blend(const Color &color, const Rect &rect)
+ {
+ cow();
+ assert(pixels);
+ Renderers::get().get_renderer().fill_blend(*this, color, rect);
+ }
+
+ namespace
+ {
+ Color merge(const Color &color0, const Color &color1, int rem, int total)
+ {
+ return Color((color0.red * (total - rem) + color1.red * rem) / total,
+ (color0.green * (total - rem) + color1.green * rem) / total,
+ (color0.blue * (total - rem) + color1.blue * rem) / total,
+ (color0.alpha * (total - rem) + color1.alpha * rem) / total);
+ }
+ }
+
+ Surface Surface::scale(unsigned int numerator, unsigned int denominator) const
+ {
+ assert(pixels);
+ if(numerator == denominator)
+ {
+ return *this;
+ }
+ else
+ {
+ Surface scaled(get_size() * numerator / denominator);
+ for(unsigned int y = 0;y < scaled.get_size().y;y++)
+ {
+ for(unsigned int x = 0;x < scaled.get_size().x;x++)
+ {
+ unsigned int srcx = x * denominator / numerator;
+ unsigned int srcy = y * denominator / numerator;
+ //scaled.set_pixel(Point(x, y), get_pixel(Point(srcx, srcy)));
+ int incx = (srcx + 1 == get_size().x ? 0 : 1);
+ int incy = (srcy + 1 == get_size().y ? 0 : 1);
+ Color color00 = get_pixel(srcx, srcy);
+ Color color01 = get_pixel(srcx + incx, srcy);
+ Color color10 = get_pixel(srcx, srcy + incy);
+ Color color11 = get_pixel(srcx + incx, srcy + incy);
+ int remx = x * denominator % numerator;
+ Color color0 = merge(color00, color01, remx, numerator);
+ Color color1 = merge(color10, color11, remx, numerator);
+ int remy = y * denominator % numerator;
+ Color color = merge(color0, color1, remy, numerator);
+ scaled.get_pixel(x, y) = color;
+ }
+ }
+ return scaled;
+ }
+ }
+
+ Surface Surface::h_flip() const
+ {
+ assert(pixels);
+ Surface flipped(get_size());
+ for(unsigned int y = 0;y < get_size().y;y++)
+ {
+ for(unsigned int x = 0;x < get_size().x;x++)
+ {
+ flipped.get_pixel(x, y) = get_pixel(get_size().x - x - 1, y);
+ }
+ }
+ return flipped;
+ }
+
+ Surface Surface::v_flip() const
+ {
+ assert(pixels);
+ Surface flipped(get_size());
+ for(unsigned int y = 0;y < get_size().y;y++)
+ {
+ for(unsigned int x = 0;x < get_size().x;x++)
+ {
+ flipped.get_pixel(x, y) = get_pixel(x, get_size().y - y - 1);
+ }
+ }
+ return flipped;
+ }
+
+ Surface Surface::modulate(const Color &color) const
+ {
+ assert(pixels);
+ if(color == Color::WHITE)
+ {
+ return *this;
+ }
+ else
+ {
+ Surface modulated(get_size());
+ for(unsigned int y = 0;y < get_size().y;y++)
+ {
+ for(unsigned int x = 0;x < get_size().x;x++)
+ {
+ Color pixel = get_pixel(x, y);
+ pixel.red = pixel.red * color.red / 0xff;
+ pixel.green = pixel.green * color.green / 0xff;
+ pixel.blue = pixel.blue * color.blue / 0xff;
+ pixel.alpha = pixel.alpha * color.alpha / 0xff;
+ modulated.get_pixel(x, y) = pixel;
+ }
+ }
+ return modulated;
+ }
+ }
+
+ Surface Surface::modulate(unsigned char alpha) const
+ {
+ assert(pixels);
+ if(alpha == 0xff)
+ {
+ return *this;
+ }
+ else
+ {
+ Surface modulated(get_size());
+ for(unsigned int y = 0;y < get_size().y;y++)
+ {
+ for(unsigned int x = 0;x < get_size().x;x++)
+ {
+ Color pixel = get_pixel(x, y);
+ pixel.alpha = pixel.alpha * alpha / 0xff;
+ modulated.get_pixel(x, y) = pixel;
+ }
+ }
+ return modulated;
+ }
+ }
+
+ void Surface::cow()
+ {
+ if(pixels && pixels->refcount > 1)
+ {
+ PixelBuffer *original = pixels;
+ pixels = new PixelBuffer(pixels->size);
+ memcpy(pixels->buffer, original->buffer, pixels->size.x * pixels->size.y * sizeof(Color));
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/video/Texture.hpp>
+#include <unison/video/backend/Texture.hpp>
+
+#include <assert.h>
+
+namespace Unison
+{
+ namespace Video
+ {
+ std::set<Texture *> Texture::textures = std::set<Texture*>();
+
+ Texture::Texture() :
+ id(INVALID_TEXTURE_ID)
+ {
+ }
+
+ Texture::Texture(const std::string &filename) :
+ id(Backend::Texture::get_texture_id(filename))
+ {
+ assert(id != INVALID_TEXTURE_ID);
+ Backend::Texture::get_texture(id)->ref();
+ textures.insert(this);
+ }
+
+ Texture::Texture(const std::string &filename, const Color &colorkey) :
+ id(Backend::Texture::get_texture_id(filename, colorkey))
+ {
+ assert(id != INVALID_TEXTURE_ID);
+ Backend::Texture::get_texture(id)->ref();
+ textures.insert(this);
+ }
+
+ Texture::Texture(const Surface &surface) :
+ id(Backend::Texture::get_texture_id(surface))
+ {
+ assert(id != INVALID_TEXTURE_ID);
+ Backend::Texture::get_texture(id)->ref();
+ textures.insert(this);
+ }
+
+ Texture::Texture(const Texture &rhs) :
+ Blittable(),
+ id(rhs.id)
+ {
+ if(id != INVALID_TEXTURE_ID)
+ {
+ Backend::Texture::get_texture(id)->ref();
+ }
+ textures.insert(this);
+ }
+
+ Texture::~Texture()
+ {
+ textures.erase(this);
+ if(id != INVALID_TEXTURE_ID)
+ {
+ Backend::Texture::get_texture(id)->unref();
+ }
+ }
+
+ Texture &Texture::operator =(const Texture &rhs)
+ {
+ if(rhs.id != INVALID_TEXTURE_ID)
+ {
+ Backend::Texture::get_texture(rhs.id)->ref();
+ }
+ if(id != INVALID_TEXTURE_ID)
+ {
+ Backend::Texture::get_texture(id)->unref();
+ }
+ id = rhs.id;
+ return *this;
+ }
+
+ TextureID Texture::get_id() const
+ {
+ return id;
+ }
+
+ Area Texture::get_size() const
+ {
+ assert(id != INVALID_TEXTURE_ID);
+ return Backend::Texture::get_texture(id)->get_size();
+ }
+
+ void Texture::blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ assert(id != INVALID_TEXTURE_ID);
+ cow();
+ Backend::Texture::get_texture(id)->blit(src, dst_pos, src_rect, options);
+ }
+
+ void Texture::blit(const Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ assert(id != INVALID_TEXTURE_ID);
+ cow();
+ Backend::Texture::get_texture(id)->blit(src, dst_pos, src_rect, options);
+ }
+
+ void Texture::fill(const Color &color, const Rect &rect)
+ {
+ assert(id != INVALID_TEXTURE_ID);
+ cow();
+ Backend::Texture::get_texture(id)->fill(color, rect);
+ }
+
+ void Texture::fill_blend(const Color &color, const Rect &rect)
+ {
+ assert(id != INVALID_TEXTURE_ID);
+ cow();
+ Backend::Texture::get_texture(id)->fill_blend(color, rect);
+ }
+
+ std::vector<Surface> Texture::save_textures()
+ {
+ recover_texture_ids();
+ return Backend::Texture::save_textures();
+ }
+
+ namespace
+ {
+ void ref(Texture *texture)
+ {
+ assert(texture);
+ TextureID id = texture->get_id();
+ if(id != INVALID_TEXTURE_ID)
+ {
+ Backend::Texture::get_texture(id)->ref();
+ }
+ }
+ }
+
+ void Texture::load_textures(const std::vector<Surface> &surfaces)
+ {
+ Backend::Texture::load_textures(surfaces);
+ std::for_each(textures.begin(), textures.end(), ref);
+ }
+
+ void Texture::recover_texture_ids()
+ {
+ std::map<TextureID, TextureID> change_map = Backend::Texture::recover_texture_ids();
+ if(!change_map.empty())
+ {
+ for(std::set<Texture *>::iterator iter = textures.begin(), end = textures.end();iter != end;++iter)
+ {
+ if(change_map.find((*iter)->id) != change_map.end())
+ {
+ (*iter)->id = change_map[(*iter)->id];
+ }
+ }
+ }
+ }
+
+ void Texture::cow()
+ {
+ assert(id != INVALID_TEXTURE_ID);
+ if(Backend::Texture::get_texture(id)->get_refcount() > 1)
+ {
+ TextureID old = id;
+ id = Backend::Texture::get_texture_id(Backend::Texture::get_texture(id)->get_surface());
+ assert(id != INVALID_TEXTURE_ID);
+ Backend::Texture::get_texture(id)->ref();
+ Backend::Texture::get_texture(old)->unref();
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/video/Window.hpp>
+#include <unison/video/Surface.hpp>
+#include <unison/video/Texture.hpp>
+#include <unison/video/Renderers.hpp>
+#include <unison/video/backend/Renderer.hpp>
+#include <unison/video/backend/Window.hpp>
+
+#include <assert.h>
+
+namespace Unison
+{
+ namespace Video
+ {
+ Window::Window() :
+ logical_size(),
+ title(),
+ icon(Surface(Area(1, 1))),
+ window(0),
+ list(),
+ layers()
+ {
+ }
+
+ Window::~Window()
+ {
+ }
+
+ Window &Window::get()
+ {
+ // FIXME: fix init order more naturally
+ Renderers::get();
+ static Window window;
+ return window;
+ }
+
+ void Window::set_logical_size(const Area &logical_size)
+ {
+ this->logical_size = logical_size;
+ if(window)
+ {
+ std::vector<Surface> surfaces = Texture::save_textures();
+ open(get_size(), is_fullscreen());
+ Texture::load_textures(surfaces);
+ }
+ }
+
+ Area Window::get_logical_size() const
+ {
+ return logical_size;
+ }
+
+ void Window::open(const Area &size, bool fullscreen)
+ {
+ std::vector<Surface> surfaces = Texture::save_textures();
+ if(logical_size.x == 0 || logical_size.y == 0)
+ {
+ logical_size = size;
+ }
+ delete window;
+ window = Renderers::get().get_renderer().create_window(size, logical_size, fullscreen);
+ assert(window);
+ Texture::load_textures(surfaces);
+ window->set_title(title);
+ window->set_icon(icon);
+ redraw();
+ }
+
+ void Window::take_screenshot(const std::string &filename) const
+ {
+ assert(window);
+ window->take_screenshot(filename);
+ }
+
+ void Window::flip()
+ {
+ list = layers;
+ layers.clear();
+ redraw();
+ }
+
+ void Window::redraw()
+ {
+ assert(window);
+ fill(Color::BLACK);
+ window->draw(list);
+ window->flip();
+ }
+
+ void Window::set_title(const std::string &title)
+ {
+ this->title = title;
+ if(window)
+ {
+ window->set_title(title);
+ }
+ }
+
+ void Window::set_icon(const Surface &icon)
+ {
+ this->icon = icon.get_size() == Area() ? Surface(Area(1, 1)) : icon;
+ if(window)
+ {
+ window->set_icon(icon);
+ }
+ }
+
+ Area Window::get_size() const
+ {
+ assert(window);
+ return window->get_size();
+ }
+
+ bool Window::is_fullscreen() const
+ {
+ assert(window);
+ return window->is_fullscreen();
+ }
+
+ bool Window::is_open() const
+ {
+ return window;
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "Renderer.hpp"
+#include <unison/video/Surface.hpp>
+#include <unison/video/Window.hpp>
+
+#include <assert.h>
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace Auto
+ {
+ Renderer::Renderer(const std::vector<Backend::Renderer *> &renderers) :
+ renderer(0),
+ renderers(renderers)
+ {
+ }
+
+ Renderer::~Renderer()
+ {
+ }
+
+ void Renderer::init()
+ {
+ assert(!renderer);
+ std::vector<Backend::Renderer *>::const_reverse_iterator found = std::find_if(renderers.rbegin(), renderers.rend(), std::mem_fun(&Unison::Video::Backend::Renderer::is_usable));
+ if(found == renderers.rend())
+ {
+ fputs("No usable renderers found\n", stderr);
+ return;
+ }
+ renderer = *found;
+ renderer->init();
+ }
+
+ void Renderer::quit()
+ {
+ assert(renderer);
+ renderer->quit();
+ renderer = 0;
+ }
+
+ std::string Renderer::get_name()
+ {
+ return "auto";
+ }
+
+ bool Renderer::is_usable()
+ {
+ return std::find_if(renderers.begin(), renderers.end(), std::mem_fun(&Unison::Video::Backend::Renderer::is_usable)) != renderers.end();
+ }
+
+ Surface Renderer::load_surface(const std::string &filename)
+ {
+ assert(renderer);
+ return renderer->load_surface(filename);
+ }
+
+ Surface Renderer::load_surface(const std::string &filename, const Color &colorkey)
+ {
+ assert(renderer);
+ return renderer->load_surface(filename, colorkey);
+ }
+
+ void Renderer::save_surface(const Surface &surface, const std::string &filename)
+ {
+ assert(renderer);
+ return renderer->save_surface(surface, filename);
+ }
+
+ void Renderer::blit(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options)
+ {
+ assert(renderer);
+ renderer->blit(src, src_rect, dst, dst_pos, options);
+ }
+
+ void Renderer::blit(Backend::Texture *src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options)
+ {
+ assert(renderer);
+ renderer->blit(src, src_rect, dst, dst_pos, options);
+ }
+
+ void Renderer::fill(Surface &dst, const Color &color, const Rect &rect)
+ {
+ assert(renderer);
+ renderer->fill(dst, color, rect);
+ }
+
+ void Renderer::fill_blend(Surface &dst, const Color &color, const Rect &rect)
+ {
+ assert(renderer);
+ renderer->fill_blend(dst, color, rect);
+ }
+
+ Backend::Window *Renderer::create_window(const Area &size, const Area &logical_size, bool fullscreen)
+ {
+ assert(renderer);
+ return renderer->create_window(size, logical_size, fullscreen);
+ }
+
+ Backend::Texture *Renderer::create_texture(const Surface &surface)
+ {
+ assert(renderer);
+ return renderer->create_texture(surface);
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_AUTO_RENDERER_HPP
+#define UNISON_VIDEO_AUTO_RENDERER_HPP
+
+#include <unison/video/backend/Renderer.hpp>
+
+#include <string>
+#include <vector>
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace Backend
+ {
+ class Texture;
+ class Window;
+ }
+ namespace Auto
+ {
+ /// Does rendering tasks like blits and color fills using SDL
+ class Renderer : public Backend::Renderer
+ {
+ public:
+ Renderer(const std::vector<Backend::Renderer *> &renderers);
+ ~Renderer();
+
+ void init();
+ void quit();
+
+ std::string get_name();
+ bool is_usable();
+
+ Surface load_surface(const std::string &filename);
+ Surface load_surface(const std::string &filename, const Color &colorkey);
+ void save_surface(const Surface &surface, const std::string &filename);
+
+ void blit(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options);
+ void blit(Backend::Texture *src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options);
+ void fill(Surface &dst, const Color &color, const Rect &rect);
+ void fill_blend(Surface &dst, const Color &color, const Rect &rect);
+
+ Backend::Window *create_window(const Area &size, const Area &logical_size, bool fullscreen);
+ Backend::Texture *create_texture(const Surface &surface);
+ private:
+ Backend::Renderer *renderer;
+ const std::vector<Backend::Renderer *> &renderers;
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/video/backend/Texture.hpp>
+#include <unison/video/Renderers.hpp>
+#include <unison/video/backend/Renderer.hpp>
+
+#include <assert.h>
+#include <algorithm>
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace Backend
+ {
+ std::vector<Texture *> Texture::textures = std::vector<Texture *>();
+ std::map<std::string, TextureID> Texture::named_textures = std::map<std::string, TextureID>();
+
+ Texture::Texture(const Surface &surface) :
+ surface(surface),
+ size(surface.get_size()),
+ refcount(0)
+ {
+ assert(surface.get_size() != Area());
+ }
+
+ Texture::~Texture()
+ {
+ }
+
+ Area Texture::get_size()
+ {
+ return size;
+ }
+
+ void Texture::ref()
+ {
+ refcount++;
+ }
+
+ void Texture::unref()
+ {
+ assert(refcount > 0);
+ refcount--;
+ if(refcount == 0)
+ {
+ std::string name = get_name(get_texture_id(this));
+ *std::find(textures.begin(), textures.end(), this) = 0;
+ if(!name.empty())
+ {
+ named_textures.erase(name);
+ }
+ delete this;
+ }
+ }
+
+ int Texture::get_refcount()
+ {
+ return refcount;
+ }
+
+ namespace
+ {
+ class TextureSaver
+ {
+ public:
+ void operator () (Texture *texture)
+ {
+ if(texture)
+ {
+ texture->save();
+ surfaces.push_back(texture->get_surface());
+ }
+ else
+ {
+ surfaces.push_back(Surface());
+ }
+ delete texture;
+ }
+ std::vector<Surface> surfaces;
+ };
+
+ class TextureLoader
+ {
+ public:
+ void operator () (Surface surface)
+ {
+ if(surface.get_size() != Area())
+ {
+ textures.push_back(Renderers::get().get_renderer().create_texture(surface));
+ }
+ else
+ {
+ textures.push_back(0);
+ }
+ }
+ std::vector<Texture *> textures;
+ };
+ }
+
+ std::vector<Surface> Texture::save_textures()
+ {
+ std::vector<Surface> surfaces = std::for_each(textures.begin(), textures.end(), TextureSaver()).surfaces;
+ textures.clear();
+ return surfaces;
+ }
+
+ void Texture::load_textures(const std::vector<Surface> &surfaces)
+ {
+ assert(textures.empty());
+ textures = std::for_each(surfaces.begin(), surfaces.end(), TextureLoader()).textures;
+ }
+
+ std::map<TextureID, TextureID> Texture::recover_texture_ids()
+ {
+ std::map<TextureID, TextureID> change_map;
+ std::vector<Texture *> new_textures;
+ bool null_texture_found = false;
+ for(std::vector<Texture *>::iterator iter = textures.begin(), end = textures.end();iter != end;++iter)
+ {
+ if(*iter)
+ {
+ new_textures.push_back(*iter);
+ if(null_texture_found)
+ {
+ change_map[iter - textures.begin()] = new_textures.size() -1;
+ }
+ }
+ else
+ {
+ null_texture_found = true;
+ }
+ }
+ textures = new_textures;
+ return change_map;
+ }
+
+ TextureID Texture::get_texture_id(const std::string &filename)
+ {
+ if(named_textures.find(filename) != named_textures.end())
+ {
+ return named_textures[filename];
+ }
+ textures.push_back(Renderers::get().get_renderer().create_texture(Surface(filename)));
+ named_textures[filename] = textures.size() - 1;
+ return textures.size() - 1;
+ }
+
+ TextureID Texture::get_texture_id(const std::string &filename, const Color&colorkey)
+ {
+ if(named_textures.find(filename) != named_textures.end())
+ {
+ return named_textures[filename];
+ }
+ textures.push_back(Renderers::get().get_renderer().create_texture(Surface(filename, colorkey)));
+ named_textures[filename] = textures.size() - 1;
+ return textures.size() - 1;
+ }
+
+ TextureID Texture::get_texture_id(const Surface &surface)
+ {
+ textures.push_back(Renderers::get().get_renderer().create_texture(surface));
+ return textures.size() - 1;
+ }
+
+ TextureID Texture::get_texture_id(Texture *texture)
+ {
+ std::vector<Texture *>::iterator found = std::find(textures.begin(), textures.end(), texture);
+ if(found != textures.end())
+ {
+ return found - textures.begin();
+ }
+ return INVALID_TEXTURE_ID;
+ }
+
+ Texture *Texture::get_texture(TextureID id)
+ {
+ assert(id < textures.size());
+ assert(textures[id]);
+ return textures[id];
+ }
+
+ namespace
+ {
+ bool match_id(std::pair<std::string, TextureID> pair, TextureID id)
+ {
+ return pair.second == id;
+ }
+ }
+
+ std::string Texture::get_name(TextureID id)
+ {
+ if(id == INVALID_TEXTURE_ID)
+ {
+ return std::string();
+ }
+ std::map<std::string, TextureID>::iterator found = std::find_if(named_textures.begin(), named_textures.end(), std::bind2nd(std::ptr_fun(match_id), id));
+ if(found == named_textures.end())
+ {
+ return std::string();
+ }
+ return found->first;
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "Renderer.hpp"
+#include "Texture.hpp"
+#include "Window.hpp"
+#include <unison/video/Surface.hpp>
+#include <unison/video/Window.hpp>
+#include <unison/video/sdl/Blitters.hpp>
+#include <unison/video/backend/Texture.hpp>
+#include <unison/vfs/sdl/Utils.hpp>
+
+#include <assert.h>
+
+#include "SDL.h"
+#include "SDL_image.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace OpenGL
+ {
+ Renderer::Renderer()
+ {
+ }
+
+ Renderer::~Renderer()
+ {
+ }
+
+ void Renderer::init()
+ {
+ SDL_InitSubSystem(SDL_INIT_VIDEO);
+
+ glLoad(0);
+ }
+
+ void Renderer::quit()
+ {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ }
+
+ std::string Renderer::get_name()
+ {
+ return "opengl";
+ }
+
+ bool Renderer::is_usable()
+ {
+ SDL_InitSubSystem(SDL_INIT_VIDEO);
+
+ if(SDL_GL_LoadLibrary(0))
+ {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ return false;
+ }
+
+ if(!SDL_ListModes(0, SDL_OPENGL))
+ {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ return false;
+ }
+
+ /*const SDL_VideoInfo *info = SDL_GetVideoInfo();
+ if(SDL_VideoModeOK(info->current_w, info->current_h, 0, SDL_OPENGL | SDL_FULLSCREEN))
+ {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ return false;
+ }*/
+
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+
+ return true;
+ }
+
+ Surface Renderer::load_surface(const std::string &filename)
+ {
+ SDL_Surface *image = IMG_Load_RW(VFS::SDL::Utils::open_physfs_in(filename), 1);
+ assert(image);
+ Surface surface = Surface(Area(image->w, image->h));
+ SDL_Surface *sdl_surface = SDL::Blitters::create_sdl_surface_from(surface);
+ assert(sdl_surface);
+ SDL::Blitters::blit_blend_none(image, Rect(), sdl_surface, Point());
+ SDL_FreeSurface(sdl_surface);
+ return surface;
+ }
+
+ Surface Renderer::load_surface(const std::string &filename, const Color &colorkey)
+ {
+ SDL_Surface *image = IMG_Load_RW(VFS::SDL::Utils::open_physfs_in(filename), 1);
+ assert(image);
+ Surface surface = Surface(Area(image->w, image->h));
+ SDL_Surface *sdl_surface = SDL::Blitters::create_sdl_surface_from(surface);
+ assert(sdl_surface);
+ SDL_SetColorKey(image, SDL_SRCCOLORKEY, SDL_MapRGB(image->format, colorkey.red, colorkey.blue, colorkey.alpha));
+ SDL::Blitters::blit_blend_none(image, Rect(), sdl_surface, Point());
+ SDL_FreeSurface(sdl_surface);
+ return surface;
+ }
+
+ void Renderer::save_surface(const Surface &surface, const std::string &filename)
+ {
+ SDL_Surface *sdl_surface = SDL::Blitters::create_sdl_surface_from(surface);
+ assert(sdl_surface);
+ SDL_SaveBMP(sdl_surface, filename.c_str());
+ SDL_FreeSurface(sdl_surface);
+ }
+
+ void Renderer::blit(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options)
+ {
+ Surface fragment;
+ if(src_rect.pos == Point() && (src_rect.size == Area() || src_rect.size == src.get_size()))
+ {
+ fragment = src;
+ }
+ else
+ {
+ fragment = Surface(src_rect.size);
+ Blitters::blit_blend_none(src, src_rect, fragment, Point());
+ }
+ if(options.h_flip)
+ {
+ fragment = fragment.h_flip();
+ }
+ if(options.v_flip)
+ {
+ fragment = fragment.v_flip();
+ }
+ SDL_Surface *src_surface = SDL::Blitters::create_sdl_surface_from(fragment.modulate(options.color).modulate(options.alpha));
+ assert(src_surface);
+
+ SDL_Surface *dst_surface = SDL::Blitters::create_sdl_surface_from(dst);
+ assert(dst_surface);
+
+
+ SDL::Blitters::blit_blend(src_surface, Rect(Point(), fragment.get_size()), dst_surface, dst_pos, options.blend);
+
+ SDL_FreeSurface(dst_surface);
+ SDL_FreeSurface(src_surface);
+ }
+
+ void Renderer::blit(Backend::Texture *src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options)
+ {
+ assert(src);
+ assert(dst.get_size() != Area());
+
+ blit(src->get_surface(), src_rect, dst, dst_pos, options);
+ }
+
+ void Renderer::fill(Surface &dst, const Color &color, const Rect &rect)
+ {
+ SDL_Surface *dst_surface = SDL::Blitters::create_sdl_surface_from(dst);
+ assert(dst_surface);
+
+ Uint32 mapped = SDL_MapRGBA(dst_surface->format, color.red, color.green, color.blue, color.alpha);
+ SDL_FillRect(dst_surface, &rect, mapped);
+ }
+
+ void Renderer::fill_blend(Surface &dst, const Color &color, const Rect &rect)
+ {
+ SDL_Surface *dst_surface = SDL::Blitters::create_sdl_surface_from(dst);
+ assert(dst_surface);
+
+ Uint32 mapped = SDL_MapRGBA(dst_surface->format, color.red, color.green, color.blue, color.alpha);
+ if(color.alpha == 0xff)
+ {
+ SDL_FillRect(dst_surface, &rect, mapped);
+ }
+ else if(color.alpha != 0x00)
+ {
+ SDL_Surface *temp = SDL_CreateRGBSurface(dst_surface->flags, rect.size.x, rect.size.y, dst_surface->format->BitsPerPixel, dst_surface->format->Rmask, dst_surface->format->Gmask, dst_surface->format->Bmask, dst_surface->format->Amask);
+
+ SDL_FillRect(temp, 0, mapped);
+ SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, color.alpha);
+ SDL_BlitSurface(temp, 0, dst_surface, &rect);
+ SDL_FreeSurface(temp);
+ }
+ }
+
+ Backend::Window *Renderer::create_window(const Area &size, const Area &logical_size, bool fullscreen)
+ {
+ return new Window(size, logical_size, fullscreen);
+ }
+
+ Backend::Texture *Renderer::create_texture(const Surface &surface)
+ {
+ return new Texture(surface);
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_OPENGL_RENDERER_HPP
+#define UNISON_VIDEO_OPENGL_RENDERER_HPP
+
+#include <unison/video/backend/Renderer.hpp>
+
+#include <string>
+
+#include "SDL.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace Backend
+ {
+ class Texture;
+ class Window;
+ }
+ namespace OpenGL
+ {
+ /// Does rendering tasks like blits and color fills using OpenGL
+ class Renderer : public Backend::Renderer
+ {
+ public:
+ /// Default constructor
+ Renderer();
+
+ /// Destructor
+ ~Renderer();
+
+ /// Initialize the backend
+ void init();
+
+ /// Cleanup the backend
+ void quit();
+
+ /// Get the name of the renderer
+ /// \return the name of the renderer
+ std::string get_name();
+
+ /// Check if the backend is usable
+ /// \return Whether the backend is usable
+ bool is_usable();
+
+ Surface load_surface(const std::string &filename);
+ Surface load_surface(const std::string &filename, const Color &colorkey);
+ void save_surface(const Surface &surface, const std::string &filename);
+
+ /// Does a surface-to-surface blit
+ /// \param[in] src The source surface
+ /// \param[in] src_rect The part of the source surface to blit from
+ /// \param[in] dst The destination surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] options Extra blit options
+ void blit(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options);
+
+ /// Does a texture-to-surface blit
+ /// \param[in] src The source texture
+ /// \param[in] src_rect The part of the source texture to blit from
+ /// \param[in] dst The destination surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] options Extra blit options
+ void blit(Backend::Texture *src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options);
+
+ /// Fills a portion of a surface with the given color
+ /// \param[in] dst The destination surface
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill(Surface &dst, const Color &color, const Rect &rect);
+
+ /// Fills with alpha blend a portion of a surface with the given color
+ /// \param[in] dst The destination surface
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill_blend(Surface &dst, const Color &color, const Rect &rect);
+
+ /// Create a window
+ /// \param[in] size The size of the window
+ /// \param[in] logical_size The logical size of the window
+ /// \param[in] fullscreen Whether to open in fullscreen mode
+ /// \return The created window
+ Backend::Window *create_window(const Area &size, const Area &logical_size, bool fullscreen);
+
+ /// Create a texture for the given surface
+ /// \param[in] surface The surface to convert
+ /// \return The texture for the surface
+ Backend::Texture *create_texture(const Surface &surface);
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "SDL.h"
+#include "SDL_gl.h"
+
+#define GL_PROC(ret, func, params) ret (*func) params;
+#include "SDL_glfuncs.h"
+#undef GL_PROC
+
+int glLoad(const char *path)
+{
+ int ret = SDL_GL_LoadLibrary(path);
+ if(ret)
+ {
+ return ret;
+ }
+#define GL_PROC(ret, func, params) func = SDL_GL_GetProcAddress(#func);
+#include "SDL_glfuncs.h"
+#undef GL_PROC
+ return 0;
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef _SDL_GL_H
+#define _SDL_GL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef unsigned int GLbitfield;
+typedef void GLvoid;
+typedef signed char GLbyte;
+typedef short GLshort;
+typedef int GLint;
+typedef unsigned char GLubyte;
+typedef unsigned short GLushort;
+typedef unsigned int GLuint;
+typedef int GLsizei;
+typedef float GLfloat;
+typedef float GLclampf;
+typedef double GLdouble;
+typedef double GLclampd;
+
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_QUADS 0x0007
+#define GL_MODELVIEW 0x1700
+#define GL_PROJECTION 0x1701
+#define GL_FRONT 0x0404
+#define GL_BACK 0x0405
+#define GL_DEPTH_TEST 0x0B71
+#define GL_CULL_FACE 0x0B44
+#define GL_BLEND 0x0BE2
+#define GL_ZERO 0x0
+#define GL_ONE 0x1
+#define GL_SRC_COLOR 0x0300
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_DST_COLOR 0x0306
+#define GL_RGB 0x1907
+#define GL_RGBA 0x1908
+#define GL_MAX_TEXTURE_SIZE 0x0D33
+#define GL_UNPACK_ALIGNMENT 0x0CF5
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_CLAMP 0x2900
+#define GL_LINEAR 0x2601
+#define GL_COLOR_BUFFER_BIT 0x00004000
+
+#define GL_PROC(ret, func, params) extern ret (*func) params;
+#include "SDL_glfuncs.h"
+#undef GL_PROC
+
+int glLoad(const char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+GL_PROC(void, glClearColor, (GLclampf, GLclampf, GLclampf, GLclampf))
+GL_PROC(void, glClear, (GLbitfield))
+GL_PROC(void, glBlendFunc, (GLenum, GLenum))
+GL_PROC(void, glDrawBuffer, (GLenum))
+GL_PROC(void, glReadBuffer, (GLenum))
+GL_PROC(void, glEnable, (GLenum))
+GL_PROC(void, glDisable, (GLenum))
+GL_PROC(void, glGetIntegerv, (GLenum, GLint *))
+GL_PROC(GLenum, glGetError, (void))
+GL_PROC(const GLubyte *, glGetString, (GLenum))
+GL_PROC(void, glMatrixMode, (GLenum))
+GL_PROC(void, glOrtho, (GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble))
+GL_PROC(void, glViewport, (GLint, GLint, GLsizei, GLsizei))
+GL_PROC(void, glPushMatrix, (void))
+GL_PROC(void, glPopMatrix, (void))
+GL_PROC(void, glLoadIdentity, (void))
+GL_PROC(void, glRotatef, (GLfloat, GLfloat, GLfloat))
+GL_PROC(void, glScalef, (GLfloat, GLfloat, GLfloat))
+GL_PROC(void, glTranslatef, (GLfloat, GLfloat, GLfloat))
+GL_PROC(void, glBegin, (GLenum))
+GL_PROC(void, glEnd, (void))
+GL_PROC(void, glVertex2i, (GLint, GLint))
+GL_PROC(void, glColor4ub, (GLubyte, GLubyte, GLubyte, GLubyte))
+GL_PROC(void, glReadPixels, (GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *))
+GL_PROC(void, glTexCoord2f, (GLfloat, GLfloat))
+GL_PROC(void, glPixelStorei, (GLenum, GLint))
+GL_PROC(void, glTexParameteri, (GLenum, GLenum, GLint))
+GL_PROC(void, glTexImage2D, (GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *))
+GL_PROC(void, glTexSubImage2D, (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *))
+GL_PROC(void, glGetTexImage, (GLenum, GLint, GLenum, GLenum, GLvoid *))
+GL_PROC(void, glGenTextures, (GLsizei, GLuint *))
+GL_PROC(void, glDeleteTextures, (GLsizei, GLuint *))
+GL_PROC(void, glBindTexture, (GLsizei, GLuint))
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "Texture.hpp"
+#include <unison/video/Surface.hpp>
+#include <unison/video/Window.hpp>
+#include <unison/video/Renderers.hpp>
+#include <unison/video/backend/Renderer.hpp>
+
+#include <assert.h>
+#include <algorithm>
+
+namespace
+{
+ int next_power_of_two(int val)
+ {
+ int result = 1;
+ while(result < val)
+ result *= 2;
+ return result;
+ }
+}
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace OpenGL
+ {
+ Texture::Texture(const Surface &surface) :
+ Backend::Texture(surface),
+ handles()
+ {
+ }
+
+ /*Texture::Texture(const Surface &surface, const std::string &name) :
+ Backend::Texture(surface, name),
+ handles()
+ {
+ }
+
+ Texture::Texture(Backend::Texture *texture) :
+ Backend::Texture(texture),
+ handles()
+ {
+ }*/
+
+ Texture::~Texture()
+ {
+ for(std::vector<Handle>::iterator iter = handles.begin(), end = handles.end();iter != end;++iter)
+ {
+ glDeleteTextures(1, &iter->texture);
+ }
+ }
+
+ const Surface Texture::get_surface()
+ {
+ if(surface.get_size() == Area())
+ {
+ assert(!handles.empty());
+ surface = Surface(size);
+ for(std::vector<Handle>::iterator iter = handles.begin(), end = handles.end();iter != end;++iter)
+ {
+ Surface section(iter->rect.size);
+ glBindTexture(GL_TEXTURE_2D, iter->texture);
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, section.get_pixels());
+ surface.blit(section, iter->rect.pos, Rect(), BLEND_NONE);
+ }
+ assert(!glGetError());
+ }
+ return surface;
+ }
+
+ void Texture::save()
+ {
+ get_surface();
+ for(std::vector<Handle>::iterator iter = handles.begin(), end = handles.end();iter != end;++iter)
+ {
+ glDeleteTextures(1, &iter->texture);
+ }
+ handles.clear();
+ }
+
+ void Texture::blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ save();
+ Renderers::get().get_renderer().blit(src, src_rect, surface, dst_pos, options);
+ }
+
+ void Texture::blit(const Video::Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ save();
+ Texture *texture = dynamic_cast<Texture *>(Backend::Texture::get_texture(src.get_id()));
+ Renderers::get().get_renderer().blit(texture, src_rect, surface, dst_pos, options);
+ }
+
+ void Texture::fill(const Color &color, const Rect &rect)
+ {
+ save();
+ Renderers::get().get_renderer().fill(surface, color, rect);
+ }
+
+ void Texture::fill_blend(const Color &color, const Rect &rect)
+ {
+ save();
+ Renderers::get().get_renderer().fill_blend(surface, color, rect);
+ }
+
+ void Texture::blit_draw_buffer(const Rect &src_rect, const Point &dst_pos, const RenderOptions &options)
+ {
+ if(handles.empty())
+ {
+ assert(surface.get_size() != Area());
+ GLint max_size;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
+ for(unsigned int y = 0;y < surface.get_size().y;y += max_size)
+ {
+ for(unsigned int x = 0;x < surface.get_size().x;x += max_size)
+ {
+ Rect rect;
+ rect.pos.x = x;
+ rect.pos.y = y;
+ rect.size.x = std::min(surface.get_size().x - x, (unsigned int)max_size);
+ rect.size.y = std::min(surface.get_size().y - y, (unsigned int)max_size);
+ handles.push_back(create_handle(surface, rect));
+ }
+ }
+ surface = Surface();
+ }
+
+ glColor4ub(options.color.red, options.color.green, options.color.blue, options.alpha);
+ switch(options.blend)
+ {
+ case BLEND_NONE:
+ glDisable(GL_BLEND);
+ //glBlendFunc(GL_ONE, GL_ZERO);
+ break;
+ case BLEND_MASK:
+ assert(0 && "Mask blending not implemented");
+ return;
+ case BLEND_ALPHA:
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ break;
+ case BLEND_ADD:
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ break;
+ case BLEND_MOD:
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ZERO, GL_SRC_COLOR);
+ break;
+ default:
+ assert(0 && "Unrecognized blend mode");
+ return;
+ }
+
+ glPushMatrix();
+
+ glTranslatef(dst_pos.x - src_rect.pos.x, dst_pos.y - src_rect.pos.y, 0);
+
+ for(std::vector<Handle>::iterator iter = handles.begin(), end = handles.end();iter != end;++iter)
+ {
+ Rect overlap = iter->rect.get_overlap(src_rect);
+ if(overlap != Rect())
+ {
+ GLfloat uv_left = GLfloat(overlap.get_left()) / iter->rect.size.x;
+ GLfloat uv_top = GLfloat(overlap.get_top()) / iter->rect.size.y;
+ GLfloat uv_right = GLfloat(overlap.get_right()) / iter->rect.size.x;
+ GLfloat uv_bottom = GLfloat(overlap.get_bottom()) / iter->rect.size.y;
+
+ if(options.h_flip)
+ {
+ std::swap(uv_left, uv_right);
+ }
+
+ if(options.v_flip)
+ {
+ std::swap(uv_top, uv_bottom);
+ }
+
+ glPushMatrix();
+
+ glTranslatef(overlap.pos.x, overlap.pos.y, 0);
+
+ glBindTexture(GL_TEXTURE_2D, iter->texture);
+ glBegin(GL_QUADS);
+ glTexCoord2f(uv_left, uv_top);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(uv_right, uv_top);
+ glVertex2i(overlap.size.x, 0);
+
+ glTexCoord2f(uv_right, uv_bottom);
+ glVertex2i(overlap.size.x, overlap.size.y);
+
+ glTexCoord2f(uv_left, uv_bottom);
+ glVertex2i(0, overlap.size.y);
+ glEnd();
+
+ glPopMatrix();
+ }
+ }
+
+ glPopMatrix();
+
+ //glColor4ub(0xff, 0xff, 0xff, 0xff);
+ //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ Texture::Handle Texture::create_handle(const Surface &surface, const Rect &rect)
+ {
+ Texture::Handle handle;
+ handle.rect = rect;
+ handle.rect.size.x = next_power_of_two(rect.size.x);
+ handle.rect.size.y = next_power_of_two(rect.size.y);
+ Surface convert(handle.rect.size);
+ convert.blit(surface, Point(), rect, BLEND_NONE);
+
+
+ glGenTextures(1, &handle.texture);
+ assert(!glGetError());
+ try
+ {
+ glBindTexture(GL_TEXTURE_2D, handle.texture);
+ //glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ //surface.lock();
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ handle.rect.size.x, handle.rect.size.y,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, convert.get_pixels());
+ //surface.unlock();
+
+ assert(!glGetError());
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ }
+ catch(...)
+ {
+ glDeleteTextures(1, &handle.texture);
+ throw;
+ }
+ return handle;
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_OPENGL_TEXTURE_HPP
+#define UNISON_VIDEO_OPENGL_TEXTURE_HPP
+
+#include <unison/video/backend/Texture.hpp>
+#include <unison/video/Surface.hpp>
+
+#include <string>
+#include <vector>
+
+#include "SDL.h"
+#include "SDL_gl.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace OpenGL
+ {
+ class Texture : public Backend::Texture
+ {
+ public:
+ Texture(const Surface &surface);
+ //Texture(const Surface &surface, const std::string &name);
+ //Texture(Backend::Texture *texture);
+ Texture();
+ ~Texture();
+
+ const Surface get_surface();
+ void save();
+ void blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options);
+ void blit(const Video::Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options);
+ void fill(const Color &color, const Rect &rect);
+ void fill_blend(const Color &color, const Rect &rect);
+
+ void blit_draw_buffer(const Rect &src_rect, const Point &dst_pos, const RenderOptions &options);
+ private:
+ struct Handle
+ {
+ GLuint texture;
+ Rect rect;
+ };
+ std::vector<Handle> handles;
+
+ static Handle create_handle(const Surface &surface, const Rect &rect);
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "Window.hpp"
+#include "Texture.hpp"
+#include <unison/video/Window.hpp>
+#include <unison/video/Surface.hpp>
+#include <unison/video/Texture.hpp>
+#include <unison/video/Renderers.hpp>
+#include <unison/video/sdl/Blitters.hpp>
+#include <unison/video/backend/Renderer.hpp>
+#include <unison/video/backend/Texture.hpp>
+
+#include <assert.h>
+
+#include "SDL.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace OpenGL
+ {
+ Window::Window(const Area &size, const Area &logical_size, bool fullscreen) :
+ logical_size(logical_size),
+ window(0)
+ {
+ assert(size.x);
+ assert(size.y);
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ //SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
+ SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
+
+ window = SDL_SetVideoMode(size.x, size.y, 0, SDL_OPENGL | (fullscreen ? SDL_FULLSCREEN : 0));
+ assert(window);
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_TEXTURE_2D);
+ //glEnable(GL_BLEND);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, logical_size.x, logical_size.y, 0, -1.0, 1.0);
+ glMatrixMode(GL_MODELVIEW);
+ glViewport(0, 0, size.x, size.y);
+ glLoadIdentity();
+ //glTranslatef(0, 0, 0);
+ //glScalef(GLfloat(size.x) / logical_size.x, GLfloat(size.y) / logical_size.y, 1);
+ //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ assert(!glGetError());
+ }
+
+ Window::~Window()
+ {
+ }
+
+ void Window::take_screenshot(const std::string &filename) const
+ {
+ //Surface surface(logical_size);
+ Surface surface(get_size());
+ glReadBuffer(GL_FRONT);
+ glReadPixels(0, 0, get_size().x, get_size().y, GL_RGBA, GL_UNSIGNED_BYTE, surface.get_pixels());
+ //glReadPixels(0, 0, logical_size.x, logical_size.y, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels);
+ surface.v_flip().save(filename);
+ }
+
+ void Window::flip()
+ {
+ SDL_GL_SwapBuffers();
+ }
+
+ void Window::set_title(const std::string &title)
+ {
+ SDL_WM_SetCaption(title.c_str(), title.c_str());
+ }
+
+ void Window::set_icon(const Surface &icon)
+ {
+ SDL_Surface *icon_surface = SDL::Blitters::create_sdl_surface_from(icon);
+ assert(icon_surface);
+ SDL_WM_SetIcon(icon_surface, 0);
+ SDL_FreeSurface(icon_surface);
+ }
+
+ Area Window::get_size() const
+ {
+ return Area(window->w, window->h);
+ }
+
+ bool Window::is_fullscreen() const
+ {
+ return window->flags & SDL_FULLSCREEN;
+ }
+
+ void Window::blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ Video::Texture texture(src);
+ blit(src, dst_pos, src_rect, options);
+ }
+
+ void Window::blit(const Video::Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ Texture *texture = dynamic_cast<Texture *>(Backend::Texture::get_texture(src.get_id()));
+ assert(texture);
+ assert(window);
+
+ texture->blit_draw_buffer(src_rect, dst_pos, options);
+ }
+
+ void Window::fill(const Color &color, const Rect &rect)
+ {
+ assert(window);
+
+ glDisable(GL_BLEND);
+ glDisable(GL_TEXTURE_2D);
+ glColor4ub(color.red, color.green, color.blue, color.alpha);
+ if(rect == Rect())
+ {
+ glBegin(GL_QUADS);
+ glVertex2i(0, 0);
+ glVertex2i(get_size().x, 0);
+ glVertex2i(get_size().x, get_size().y);
+ glVertex2i(0, get_size().y);
+ glEnd();
+ }
+ else
+ {
+ glPushMatrix();
+
+ glTranslatef(rect.pos.x, rect.pos.y, 0);
+
+ glBegin(GL_QUADS);
+ glVertex2i(0, 0);
+ glVertex2i(rect.size.x, 0);
+ glVertex2i(rect.size.x, rect.size.y);
+ glVertex2i(0, rect.size.y);
+ glEnd();
+
+ glPopMatrix();
+ }
+ glEnable(GL_TEXTURE_2D);
+ //glColor4ub(0xff, 0xff, 0xff, 0xff);
+ }
+
+ void Window::fill_blend(const Color &color, const Rect &rect)
+ {
+ assert(window);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_TEXTURE_2D);
+ glColor4ub(color.red, color.green, color.blue, color.alpha);
+ if(rect == Rect())
+ {
+ glBegin(GL_QUADS);
+ glVertex2i(0, 0);
+ glVertex2i(get_size().x, 0);
+ glVertex2i(get_size().x, get_size().y);
+ glVertex2i(0, get_size().y);
+ glEnd();
+ }
+ else
+ {
+ glPushMatrix();
+
+ glTranslatef(rect.pos.x, rect.pos.y, 0);
+
+ glBegin(GL_QUADS);
+ glVertex2i(0, 0);
+ glVertex2i(rect.size.x, 0);
+ glVertex2i(rect.size.x, rect.size.y);
+ glVertex2i(0, rect.size.y);
+ glEnd();
+
+ glPopMatrix();
+ }
+ glEnable(GL_TEXTURE_2D);
+ //glColor4ub(0xff, 0xff, 0xff, 0xff);
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_OPENGL_WINDOW_HPP
+#define UNISON_VIDEO_OPENGL_WINDOW_HPP
+
+#include <unison/video/backend/Window.hpp>
+#include <unison/video/RenderOptions.hpp>
+
+#include <string>
+
+#include "SDL.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace Backend
+ {
+ class Texture;
+ }
+ namespace OpenGL
+ {
+ class Window : public Backend::Window
+ {
+ public:
+ Window(const Area &size, const Area &logical_size, bool fullscreen = false);
+
+ ~Window();
+ void take_screenshot(const std::string &filename) const;
+ void flip();
+ void set_title(const std::string &title);
+ void set_icon(const Surface &icon);
+ Area get_size() const;
+ bool is_fullscreen() const;
+ void blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options);
+ void blit(const Video::Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options);
+ void fill(const Color &color, const Rect &rect);
+ void fill_blend(const Color &color, const Rect &rect);
+ private:
+ /// The logical size of the window;
+ Area logical_size;
+
+ /// The window
+ SDL_Surface *window;
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <unison/video/sdl/Blitters.hpp>
+#include <unison/video/Surface.hpp>
+
+#include <assert.h>
+
+#include "SDL.h"
+
+namespace
+{
+ unsigned char add_saturate(unsigned char lhs, int rhs)
+ {
+ if(lhs + rhs < 0x00)
+ {
+ return 0x00;
+ }
+ if(lhs + rhs > 0xff)
+ {
+ return 0xff;
+ }
+ return lhs + rhs;
+ }
+}
+
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace SDL
+ {
+ /*SDL_Surface *Blitters::optimize(const Surface &src)
+ {
+ bool colors[(1 << 12)];
+ memset(colors, 0, (1 << 12) * sizeof(bool));
+
+ Surface dst(src);
+ Color *iter = dst.get_pixels();
+ for(unsigned int y = 0;y < dst.get_size().y;y++)
+ {
+ for(unsigned int x = 0;x < dst.get_size().x;x++, iter++)
+ {
+ unsigned char oldalpha = iter->alpha;
+ unsigned char newalpha = oldalpha & 0x80 ? 0xff : 0x00;
+ iter->alpha = newalpha;
+ int error = oldalpha - newalpha;
+ if(x != dst.get_size().x - 1)
+ {
+ (iter + 1)->alpha = add_saturate((iter + 1)->alpha, error * 7 / 16);
+ }
+ if(y != dst.get_size().y - 1)
+ {
+ if(x != 0)
+ {
+ (iter + dst.get_size().x - 1)->alpha = add_saturate((iter + dst.get_size().x - 1)->alpha, error * 3 / 16);
+ }
+ (iter + dst.get_size().x)->alpha = add_saturate((iter + dst.get_size().x)->alpha, error * 5 / 16);
+ if(x != dst.get_size().x - 1)
+ {
+ (iter + dst.get_size().x + 1)->alpha = add_saturate((iter + dst.get_size().x + 1)->alpha, error * 1 / 16);
+ }
+ }
+ if(newalpha != 0)
+ {
+ colors[((iter->red & 0xf0) << 4) | (iter->green & 0xf0) | ((iter->blue & 0xf0) >> 4)] = true;
+ }
+ }
+ }
+
+ int keycolor = -1;
+ for(int i = 0;i < (1 << 12);i++)
+ {
+ if(!colors[i])
+ {
+ keycolor = i;
+ break;
+ }
+ }
+ if(keycolor == -1)
+ {
+ SDL_Surface *converted = create_sdl_surface_from(src);
+ SDL_Surface *optimized = SDL_DisplayFormatAlpha(converted);
+ SDL_FreeSurface(converted);
+ return optimized;
+ }
+
+ Color key(((keycolor & 0xf00) >> 4) | 0xf, (keycolor & 0x0f0) | 0xf, ((keycolor & 0x00f) << 4) | 0xf);
+
+ Color *end = dst.get_pixels() + dst.get_size().x * dst.get_size().y;
+ for(iter = dst.get_pixels();iter != end;++iter)
+ {
+ if(iter->alpha == 0x00)
+ {
+ *iter = key;
+ }
+ }
+
+ SDL_Surface *converted = create_sdl_surface_from(dst);
+ SDL_SetColorKey(converted, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(converted->format, key.red, key.green, key.blue));
+ SDL_Surface *optimized = SDL_DisplayFormat(converted);
+ SDL_FreeSurface(converted);
+ return optimized;
+ }*/
+ SDL_Surface *Blitters::optimize(const Surface &src)
+ {
+ unsigned int transparent = 0;
+ unsigned int opaque = 0;
+ unsigned int semitransparent = 0;
+ unsigned int alphasum = 0;
+ unsigned int squaredalphasum = 0;
+ bool colors[(1 << 12)];
+ memset(colors, 0, (1 << 12) * sizeof(bool));
+
+ const Color *src_end = src.get_pixels() + src.get_size().x * src.get_size().y;
+ for(const Color *iter = src.get_pixels();iter != src_end;++iter)
+ {
+ if(iter->alpha < 16)
+ {
+ transparent++;
+ }
+ else if(iter->alpha > 240)
+ {
+ opaque++;
+ alphasum += iter->alpha;
+ squaredalphasum += iter->alpha * iter->alpha;
+ }
+ else
+ {
+ semitransparent++;
+ squaredalphasum += iter->alpha * iter->alpha;
+ }
+ if(iter->alpha != 0)
+ {
+ colors[((iter->red & 0xf0) << 4) | (iter->green & 0xf0) | ((iter->blue & 0xf0) >> 4)] = true;
+ }
+ }
+
+ unsigned int avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
+ unsigned int avgsquaredalpha = (opaque + semitransparent) ? squaredalphasum / (opaque + semitransparent) : 0;
+ unsigned int alphavariance = avgsquaredalpha - avgalpha * avgalpha;
+ if(semitransparent > ((transparent + opaque + semitransparent) / 8) && alphavariance > 16)
+ {
+ SDL_Surface *converted = create_sdl_surface_from(src);
+ SDL_Surface *optimized = SDL_DisplayFormatAlpha(converted);
+ SDL_FreeSurface(converted);
+ return optimized;
+ }
+
+ int keycolor = -1;
+ for(int i = 0;i < (1 << 12);i++)
+ {
+ if(!colors[i])
+ {
+ keycolor = i;
+ break;
+ }
+ }
+ if(keycolor == -1)
+ {
+ SDL_Surface *converted = create_sdl_surface_from(src);
+ SDL_Surface *optimized = SDL_DisplayFormatAlpha(converted);
+ SDL_FreeSurface(converted);
+ return optimized;
+ }
+
+ Color key(((keycolor & 0xf00) >> 4) | 0xf, (keycolor & 0x0f0) | 0xf, ((keycolor & 0x00f) << 4) | 0xf);
+ Surface dst(src.get_size());
+ Color *dst_end = dst.get_pixels() + dst.get_size().x * dst.get_size().y;
+ const Color *src_iter = src.get_pixels();
+ Color *dst_iter = dst.get_pixels();
+ for(;dst_iter != dst_end;++dst_iter, ++src_iter)
+ {
+ *dst_iter = (src_iter->alpha < avgalpha / 4) ? key : *src_iter;
+ }
+
+ SDL_Surface *converted = create_sdl_surface_from(dst);
+ if(avgalpha < 240)
+ {
+ SDL_SetAlpha(converted, SDL_SRCALPHA | SDL_RLEACCEL, avgalpha);
+ }
+ SDL_SetColorKey(converted, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(converted->format, key.red, key.green, key.blue));
+ SDL_Surface *optimized = SDL_DisplayFormat(converted);
+ SDL_FreeSurface(converted);
+ return optimized;
+ }
+
+ void Blitters::blit_upper(SDL_Surface *src, Rect src_rect, SDL_Surface *dst, Point dst_pos, void (*blit_lower)(SDL_Surface *, const Rect &, SDL_Surface *, const Point &))
+ {
+ assert(src);
+ assert(dst);
+ assert(blit_lower);
+ if(src_rect == Rect())
+ {
+ src_rect.size.x = src->w;
+ src_rect.size.y = src->h;
+ }
+ if(dst_pos.x < 0)
+ {
+ if(src_rect.size.x < (unsigned int) -dst_pos.x)
+ {
+ return;
+ }
+ src_rect.pos.x += -dst_pos.x;
+ src_rect.size.x += dst_pos.x;
+ dst_pos.x = 0;
+ }
+ if(dst_pos.y < 0)
+ {
+ if(src_rect.size.y < (unsigned int) -dst_pos.y)
+ {
+ return;
+ }
+ src_rect.pos.y += -dst_pos.y;
+ src_rect.size.y += dst_pos.y;
+ dst_pos.y = 0;
+ }
+ if(src_rect.pos.x < 0)
+ {
+ if(src_rect.size.x < (unsigned int) -src_rect.pos.x)
+ {
+ return;
+ }
+ src_rect.size.x += src_rect.pos.x;
+ src_rect.pos.x = 0;
+ }
+ if(src_rect.pos.y < 0)
+ {
+ if(src_rect.size.y < (unsigned int) -src_rect.pos.y)
+ {
+ return;
+ }
+ src_rect.size.y += src_rect.pos.y;
+ src_rect.pos.y = 0;
+ }
+ if(src_rect.get_right() > src->w)
+ {
+ src_rect.size.x = src->w - src_rect.pos.x;
+ }
+ if(src_rect.get_bottom() > src->h)
+ {
+ src_rect.size.y = src->h - src_rect.pos.y;
+ }
+ if(int(dst_pos.x + src_rect.size.x) > dst->w)
+ {
+ src_rect.size.x = dst->w - dst_pos.x;
+ }
+ if(int(dst_pos.y + src_rect.size.y) > dst->h)
+ {
+ src_rect.size.y = dst->h - dst_pos.y;
+ }
+ blit_lower(src, src_rect, dst, dst_pos);
+ }
+
+ void Blitters::blit_lower_none(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos)
+ {
+ SDL_Rect sdl_src_rect = {src_rect.pos.x, src_rect.pos.y, src_rect.size.x, src_rect.size.y};
+ SDL_Rect sdl_dst_rect = {dst_pos.x, dst_pos.y, 0, 0};
+
+ Uint8 alpha = src->format->alpha;
+ SDL_SetAlpha(src, 0, 0);
+ SDL_BlitSurface(src, &sdl_src_rect, dst, &sdl_dst_rect);
+ SDL_SetAlpha(src, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
+ }
+
+ void Blitters::blit_lower_mask(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos)
+ {
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_LockSurface(dst);
+ }
+ int src_bpp = src->format->BytesPerPixel;
+ int dst_bpp = dst->format->BytesPerPixel;
+ Uint8 *src_pixel = (Uint8 *)src->pixels + src_rect.pos.y * src->pitch + src_rect.pos.x * src_bpp;
+ Uint8 *dst_pixel = (Uint8 *)dst->pixels + dst_pos.y * dst->pitch + dst_pos.x * dst_bpp;
+ for(unsigned int y = 0;y < src_rect.size.y;y++)
+ {
+ for(unsigned int x = 0;x < src_rect.size.x;x++)
+ {
+ Uint32 src_mapped = 0;
+ switch(src_bpp) {
+ case 1:
+ src_mapped = *src_pixel;
+ break;
+ case 2:
+ src_mapped = *(Uint16 *)src_pixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ src_mapped |= src_pixel[0] << 16;
+ src_mapped |= src_pixel[1] << 8;
+ src_mapped |= src_pixel[2] << 0;
+#else
+ src_mapped |= src_pixel[0] << 0;
+ src_mapped |= src_pixel[1] << 8;
+ src_mapped |= src_pixel[2] << 16;
+#endif
+ break;
+ case 4:
+ src_mapped = *(Uint32 *)src_pixel;
+ break;
+ }
+ Uint8 src_red, src_green, src_blue, src_alpha;
+ SDL_GetRGBA(src_mapped, src->format, &src_red, &src_green, &src_blue, &src_alpha);
+ if(src_alpha)
+ {
+ Uint32 blend_mapped = SDL_MapRGBA(dst->format, src_red, src_green, src_blue, src_alpha);
+ switch(dst_bpp) {
+ case 1:
+ *dst_pixel = blend_mapped;
+ break;
+ case 2:
+ *(Uint16 *)dst_pixel = blend_mapped;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ dst_pixel[0] = (blend_mapped >> 16) & 0xff;
+ dst_pixel[1] = (blend_mapped >> 8) & 0xff;
+ dst_pixel[2] = (blend_mapped >> 0) & 0xff;
+#else
+ dst_pixel[0] = (blend_mapped >> 0) & 0xff;
+ dst_pixel[1] = (blend_mapped >> 8) & 0xff;
+ dst_pixel[2] = (blend_mapped >> 16) & 0xff;
+#endif
+ break;
+ case 4:
+ *(Uint32 *)dst_pixel = blend_mapped;
+ break;
+ }
+ }
+ src_pixel += src_bpp;
+ dst_pixel += dst_bpp;
+ }
+ src_pixel += src->pitch - src_rect.size.x * src_bpp;
+ dst_pixel += dst->pitch - src_rect.size.x * dst_bpp;
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_UnlockSurface(dst);
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ }
+
+ void Blitters::blit_lower_alpha(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos)
+ {
+ SDL_Rect sdl_src_rect = {src_rect.pos.x, src_rect.pos.y, src_rect.size.x, src_rect.size.y};
+ SDL_Rect sdl_dst_rect = {dst_pos.x, dst_pos.y, 0, 0};
+
+ SDL_BlitSurface(src, &sdl_src_rect, dst, &sdl_dst_rect);
+ }
+
+ void Blitters::blit_lower_add(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos)
+ {
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_LockSurface(dst);
+ }
+ int src_bpp = src->format->BytesPerPixel;
+ int dst_bpp = dst->format->BytesPerPixel;
+ Uint8 *src_pixel = (Uint8 *)src->pixels + src_rect.pos.y * src->pitch + src_rect.pos.x * src_bpp;
+ Uint8 *dst_pixel = (Uint8 *)dst->pixels + dst_pos.y * dst->pitch + dst_pos.x * dst_bpp;
+ for(unsigned int y = 0;y < src_rect.size.y;y++)
+ {
+ for(unsigned int x = 0;x < src_rect.size.x;x++)
+ {
+ Uint32 src_mapped = 0;
+ switch(src_bpp) {
+ case 1:
+ src_mapped = *src_pixel;
+ break;
+ case 2:
+ src_mapped = *(Uint16 *)src_pixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ src_mapped |= src_pixel[0] << 16;
+ src_mapped |= src_pixel[1] << 8;
+ src_mapped |= src_pixel[2] << 0;
+#else
+ src_mapped |= src_pixel[0] << 0;
+ src_mapped |= src_pixel[1] << 8;
+ src_mapped |= src_pixel[2] << 16;
+#endif
+ break;
+ case 4:
+ src_mapped = *(Uint32 *)src_pixel;
+ break;
+ }
+ Uint8 src_red, src_green, src_blue, src_alpha;
+ SDL_GetRGBA(src_mapped, src->format, &src_red, &src_green, &src_blue, &src_alpha);
+ Uint32 dst_mapped = 0;
+ switch(dst_bpp) {
+ case 1:
+ dst_mapped = *dst_pixel;
+ break;
+ case 2:
+ dst_mapped = *(Uint16 *)dst_pixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ dst_mapped |= dst_pixel[0] << 16;
+ dst_mapped |= dst_pixel[1] << 8;
+ dst_mapped |= dst_pixel[2] << 0;
+#else
+ dst_mapped |= dst_pixel[0] << 0;
+ dst_mapped |= dst_pixel[1] << 8;
+ dst_mapped |= dst_pixel[2] << 16;
+#endif
+ break;
+ case 4:
+ dst_mapped = *(Uint32 *)dst_pixel;
+ break;
+ }
+ Uint8 dst_red, dst_green, dst_blue, dst_alpha;
+ SDL_GetRGBA(dst_mapped, dst->format, &dst_red, &dst_green, &dst_blue, &dst_alpha);
+ Uint8 blend_red = src_red, blend_green = src_green, blend_blue = src_blue, blend_alpha = src_alpha;
+ if(src_red != 0 && dst_red != 0xff)
+ {
+ int redsum = dst_red + src_red * src_alpha / 0xff;
+ blend_red = redsum & ~0xff ? 0xff : redsum;
+ }
+ else
+ {
+ }
+ if(src_green != 0 && dst_green != 0xff)
+ {
+ int greensum = dst_green + src_green * src_alpha / 0xff;
+ blend_green = greensum & ~0xff ? 0xff : greensum;
+ }
+ if(src_blue != 0 && dst_blue != 0xff)
+ {
+ int bluesum = dst_blue + src_blue * src_alpha / 0xff;
+ blend_blue = bluesum & ~0xff ? 0xff : bluesum;
+ }
+ if(src_red != blend_red || src_green != blend_green || src_blue != blend_blue || src_alpha != blend_alpha)
+ {
+ Uint32 blend_mapped = SDL_MapRGBA(dst->format, blend_red, blend_green, blend_blue, blend_alpha);
+ switch(dst_bpp) {
+ case 1:
+ *dst_pixel = blend_mapped;
+ break;
+ case 2:
+ *(Uint16 *)dst_pixel = blend_mapped;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ dst_pixel[0] = (blend_mapped >> 16) & 0xff;
+ dst_pixel[1] = (blend_mapped >> 8) & 0xff;
+ dst_pixel[2] = (blend_mapped >> 0) & 0xff;
+#else
+ dst_pixel[0] = (blend_mapped >> 0) & 0xff;
+ dst_pixel[1] = (blend_mapped >> 8) & 0xff;
+ dst_pixel[2] = (blend_mapped >> 16) & 0xff;
+#endif
+ break;
+ case 4:
+ *(Uint32 *)dst_pixel = blend_mapped;
+ break;
+ }
+ }
+ src_pixel += src_bpp;
+ dst_pixel += dst_bpp;
+ }
+ src_pixel += src->pitch - src_rect.size.x * src_bpp;
+ dst_pixel += dst->pitch - src_rect.size.x * dst_bpp;
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_UnlockSurface(dst);
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ }
+
+ void Blitters::blit_lower_mod(SDL_Surface *src, const Rect &src_rect, SDL_Surface *dst, const Point &dst_pos)
+ {
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_LockSurface(src);
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_LockSurface(dst);
+ }
+ int src_bpp = src->format->BytesPerPixel;
+ int dst_bpp = dst->format->BytesPerPixel;
+ Uint8 *src_pixel = (Uint8 *)src->pixels + src_rect.pos.y * src->pitch + src_rect.pos.x * src_bpp;
+ Uint8 *dst_pixel = (Uint8 *)dst->pixels + dst_pos.y * dst->pitch + dst_pos.x * dst_bpp;
+ for(unsigned int y = 0;y < src_rect.size.y;y++)
+ {
+ for(unsigned int x = 0;x < src_rect.size.x;x++)
+ {
+ Uint32 src_mapped = 0;
+ switch(src_bpp) {
+ case 1:
+ src_mapped = *src_pixel;
+ break;
+ case 2:
+ src_mapped = *(Uint16 *)src_pixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ src_mapped |= src_pixel[0] << 16;
+ src_mapped |= src_pixel[1] << 8;
+ src_mapped |= src_pixel[2] << 0;
+#else
+ src_mapped |= src_pixel[0] << 0;
+ src_mapped |= src_pixel[1] << 8;
+ src_mapped |= src_pixel[2] << 16;
+#endif
+ break;
+ case 4:
+ src_mapped = *(Uint32 *)src_pixel;
+ break;
+ }
+ Uint8 src_red, src_green, src_blue, src_alpha;
+ SDL_GetRGBA(src_mapped, src->format, &src_red, &src_green, &src_blue, &src_alpha);
+ Uint32 dst_mapped = 0;
+ switch(dst_bpp) {
+ case 1:
+ dst_mapped = *dst_pixel;
+ break;
+ case 2:
+ dst_mapped = *(Uint16 *)dst_pixel;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ dst_mapped |= dst_pixel[0] << 16;
+ dst_mapped |= dst_pixel[1] << 8;
+ dst_mapped |= dst_pixel[2] << 0;
+#else
+ dst_mapped |= dst_pixel[0] << 0;
+ dst_mapped |= dst_pixel[1] << 8;
+ dst_mapped |= dst_pixel[2] << 16;
+#endif
+ break;
+ case 4:
+ dst_mapped = *(Uint32 *)dst_pixel;
+ break;
+ }
+ Uint8 dst_red, dst_green, dst_blue, dst_alpha;
+ SDL_GetRGBA(dst_mapped, dst->format, &dst_red, &dst_green, &dst_blue, &dst_alpha);
+ Uint8 blend_red, blend_green, blend_blue, blend_alpha;
+ blend_red = dst_red * src_red / 0xff;
+ blend_green = dst_green * src_green / 0xff;
+ blend_blue = dst_blue * src_blue / 0xff;
+ blend_alpha = dst_alpha * src_alpha / 0xff;
+ if(src_red != blend_red || src_green != blend_green || src_blue != blend_blue || src_alpha != blend_alpha)
+ {
+ Uint32 blend_mapped = SDL_MapRGBA(dst->format, blend_red, blend_green, blend_blue, blend_alpha);
+ switch(dst_bpp) {
+ case 1:
+ *dst_pixel = blend_mapped;
+ break;
+ case 2:
+ *(Uint16 *)dst_pixel = blend_mapped;
+ break;
+ case 3:
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ dst_pixel[0] = (blend_mapped >> 16) & 0xff;
+ dst_pixel[1] = (blend_mapped >> 8) & 0xff;
+ dst_pixel[2] = (blend_mapped >> 0) & 0xff;
+#else
+ dst_pixel[0] = (blend_mapped >> 0) & 0xff;
+ dst_pixel[1] = (blend_mapped >> 8) & 0xff;
+ dst_pixel[2] = (blend_mapped >> 16) & 0xff;
+#endif
+ break;
+ case 4:
+ *(Uint32 *)dst_pixel = blend_mapped;
+ break;
+ }
+ }
+ src_pixel += src_bpp;
+ dst_pixel += dst_bpp;
+ }
+ src_pixel += src->pitch - src_rect.size.x * src_bpp;
+ dst_pixel += dst->pitch - src_rect.size.x * dst_bpp;
+ }
+ if(SDL_MUSTLOCK(dst))
+ {
+ SDL_UnlockSurface(dst);
+ }
+ if(SDL_MUSTLOCK(src))
+ {
+ SDL_UnlockSurface(src);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "Renderer.hpp"
+#include "Texture.hpp"
+#include "Window.hpp"
+#include <unison/video/Surface.hpp>
+#include <unison/video/Window.hpp>
+#include <unison/video/sdl/Blitters.hpp>
+#include <unison/video/backend/Texture.hpp>
+#include <unison/vfs/sdl/Utils.hpp>
+
+#include <assert.h>
+
+#include "SDL.h"
+#include "SDL_image.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace SDL
+ {
+ Renderer::Renderer()
+ {
+ }
+
+ Renderer::~Renderer()
+ {
+ }
+
+ void Renderer::init()
+ {
+ SDL_InitSubSystem(SDL_INIT_VIDEO);
+ }
+
+ void Renderer::quit()
+ {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ }
+
+ std::string Renderer::get_name()
+ {
+ return "sdl";
+ }
+
+ bool Renderer::is_usable()
+ {
+ SDL_InitSubSystem(SDL_INIT_VIDEO);
+
+ if(!SDL_ListModes(0, SDL_SWSURFACE))
+ {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ return false;
+ }
+
+ /*const SDL_VideoInfo *info = SDL_GetVideoInfo();
+ if(SDL_VideoModeOK(info->current_w, info->current_h, 0, SDL_SWSURFACE | SDL_FULLSCREEN))
+ {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ return false;
+ }*/
+
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+
+ return true;
+ }
+
+ Surface Renderer::load_surface(const std::string &filename)
+ {
+ SDL_Surface *image = IMG_Load_RW(VFS::SDL::Utils::open_physfs_in(filename), 1);
+ assert(image);
+ Surface surface = Surface(Area(image->w, image->h));
+ SDL_Surface *sdl_surface = SDL::Blitters::create_sdl_surface_from(surface);
+ assert(sdl_surface);
+ SDL::Blitters::blit_blend_none(image, Rect(), sdl_surface, Point());
+ SDL_FreeSurface(sdl_surface);
+ return surface;
+ }
+
+ Surface Renderer::load_surface(const std::string &filename, const Color &colorkey)
+ {
+ SDL_Surface *image = IMG_Load_RW(VFS::SDL::Utils::open_physfs_in(filename), 1);
+ assert(image);
+ Surface surface = Surface(Area(image->w, image->h));
+ SDL_Surface *sdl_surface = SDL::Blitters::create_sdl_surface_from(surface);
+ assert(sdl_surface);
+ SDL_SetColorKey(image, SDL_SRCCOLORKEY, SDL_MapRGB(image->format, colorkey.red, colorkey.blue, colorkey.alpha));
+ SDL::Blitters::blit_blend_none(image, Rect(), sdl_surface, Point());
+ SDL_FreeSurface(sdl_surface);
+ return surface;
+ }
+
+ void Renderer::save_surface(const Surface &surface, const std::string &filename)
+ {
+ SDL_Surface *sdl_surface = SDL::Blitters::create_sdl_surface_from(surface);
+ assert(sdl_surface);
+ SDL_SaveBMP(sdl_surface, filename.c_str());
+ SDL_FreeSurface(sdl_surface);
+ }
+
+ void Renderer::blit(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options)
+ {
+ Surface fragment;
+ if(src_rect.pos == Point() && (src_rect.size == Area() || src_rect.size == src.get_size()))
+ {
+ fragment = src;
+ }
+ else
+ {
+ fragment = Surface(src_rect.size);
+ Video::Blitters::blit_blend_none(src, src_rect, fragment, Point());
+ }
+ if(options.h_flip)
+ {
+ fragment = fragment.h_flip();
+ }
+ if(options.v_flip)
+ {
+ fragment = fragment.v_flip();
+ }
+ SDL_Surface *src_surface = Blitters::create_sdl_surface_from(fragment.modulate(options.color).modulate(options.alpha));
+ assert(src_surface);
+
+ SDL_Surface *dst_surface = Blitters::create_sdl_surface_from(dst);
+ assert(dst_surface);
+
+
+ Blitters::blit_blend(src_surface, Rect(Point(), fragment.get_size()), dst_surface, dst_pos, options.blend);
+
+ SDL_FreeSurface(dst_surface);
+ SDL_FreeSurface(src_surface);
+ }
+
+ void Renderer::blit(Backend::Texture *src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options)
+ {
+ blit(src->get_surface(), src_rect, dst, dst_pos, options);
+ }
+
+ void Renderer::fill(Surface &dst, const Color &color, const Rect &rect)
+ {
+ SDL_Surface *dst_surface = Blitters::create_sdl_surface_from(dst);
+ assert(dst_surface);
+
+ Uint32 mapped = SDL_MapRGBA(dst_surface->format, color.red, color.green, color.blue, color.alpha);
+ SDL_FillRect(dst_surface, &rect, mapped);
+ }
+
+ void Renderer::fill_blend(Surface &dst, const Color &color, const Rect &rect)
+ {
+ SDL_Surface *dst_surface = Blitters::create_sdl_surface_from(dst);
+ assert(dst_surface);
+
+ Uint32 mapped = SDL_MapRGBA(dst_surface->format, color.red, color.green, color.blue, color.alpha);
+ if(color.alpha == 0xff)
+ {
+ SDL_FillRect(dst_surface, &rect, mapped);
+ }
+ else if(color.alpha != 0x00)
+ {
+ SDL_Surface *temp = SDL_CreateRGBSurface(dst_surface->flags, rect.size.x, rect.size.y, dst_surface->format->BitsPerPixel, dst_surface->format->Rmask, dst_surface->format->Gmask, dst_surface->format->Bmask, dst_surface->format->Amask);
+
+ SDL_FillRect(temp, 0, mapped);
+ SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, color.alpha);
+ SDL_BlitSurface(temp, 0, dst_surface, &rect);
+ SDL_FreeSurface(temp);
+ }
+ }
+
+ Backend::Window *Renderer::create_window(const Area &size, const Area &logical_size, bool fullscreen)
+ {
+ return new Window(size, logical_size, fullscreen);
+ }
+
+ Backend::Texture *Renderer::create_texture(const Surface &surface)
+ {
+ return new Texture(surface);
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_SDL_RENDERER_HPP
+#define UNISON_VIDEO_SDL_RENDERER_HPP
+
+#include <unison/video/backend/Renderer.hpp>
+
+#include <string>
+#include "SDL.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace Backend
+ {
+ class Texture;
+ class Window;
+ }
+ namespace SDL
+ {
+ /// Does rendering tasks like blits and color fills using SDL
+ class Renderer : public Backend::Renderer
+ {
+ public:
+ /// Default constructor
+ Renderer();
+
+ /// Destructor
+ ~Renderer();
+
+ /// Initialize the backend
+ void init();
+
+ /// Cleanup the backend
+ void quit();
+
+ /// Get the name of the renderer
+ /// \return the name of the renderer
+ std::string get_name();
+
+ /// Check if the backend is usable
+ /// \return Whether the backend is usable
+ bool is_usable();
+
+ Surface load_surface(const std::string &filename);
+ Surface load_surface(const std::string &filename, const Color &colorkey);
+ void save_surface(const Surface &surface, const std::string &filename);
+
+ /// Does a surface-to-surface blit
+ /// \param[in] src The source surface
+ /// \param[in] src_rect The part of the source surface to blit from
+ /// \param[in] dst The destination surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] options Extra blit options
+ void blit(const Surface &src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options);
+
+ /// Does a texture-to-surface blit
+ /// \param[in] src The source texture
+ /// \param[in] src_rect The part of the source texture to blit from
+ /// \param[in] dst The destination surface
+ /// \param[in] dst_pos The position to blit to
+ /// \param[in] options Extra blit options
+ void blit(Backend::Texture *src, const Rect &src_rect, Surface &dst, const Point &dst_pos, const RenderOptions &options);
+
+ /// Fills a portion of a surface with the given color
+ /// \param[in] dst The destination surface
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill(Surface &dst, const Color &color, const Rect &rect);
+
+ /// Fills with alpha blend a portion of a surface with the given color
+ /// \param[in] dst The destination surface
+ /// \param[in] color The color
+ /// \param[in] rect The portion to fill
+ void fill_blend(Surface &dst, const Color &color, const Rect &rect);
+
+ /// Create a window
+ /// \param[in] size The size of the window
+ /// \param[in] logical_size The logical size of the window
+ /// \param[in] fullscreen Whether to open in fullscreen mode
+ /// \return The created window
+ Backend::Window *create_window(const Area &size, const Area &logical_size, bool fullscreen);
+
+ /// Create a texture data for the given surface
+ /// \param[in] surface The surface to convert
+ /// \return The texture data for the surface
+ Backend::Texture *create_texture(const Surface &surface);
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "Texture.hpp"
+#include <unison/video/Surface.hpp>
+#include <unison/video/Window.hpp>
+#include <unison/video/Renderers.hpp>
+#include <unison/video/sdl/Blitters.hpp>
+#include <unison/video/backend/Renderer.hpp>
+
+#include <assert.h>
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace SDL
+ {
+ Texture::Texture(const Surface &surface) :
+ Backend::Texture(surface),
+ scaled(),
+ n_cache(),
+ h_cache(),
+ v_cache(),
+ d_cache()
+ {
+ }
+ /*Texture::Texture(const Surface &surface, const std::string &name) :
+ Backend::Texture(surface, name),
+ scaled(),
+ n_cache(),
+ h_cache(),
+ v_cache(),
+ d_cache()
+ {
+ }
+
+ Texture::Texture(Backend::Texture *texture) :
+ Backend::Texture(texture),
+ scaled(),
+ n_cache(),
+ h_cache(),
+ v_cache(),
+ d_cache()
+ {
+ }*/
+
+ Texture::~Texture()
+ {
+ }
+
+ const Surface Texture::get_surface()
+ {
+ return surface;
+ }
+
+ void Texture::save()
+ {
+ scaled = Surface();
+ n_cache.clear();
+ h_cache.clear();
+ v_cache.clear();
+ d_cache.clear();
+ }
+
+ void Texture::blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ save();
+ Renderers::get().get_renderer().blit(src, src_rect, surface, dst_pos, options);
+ }
+
+ void Texture::blit(const Video::Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ save();
+ Texture *texture = dynamic_cast<Texture *>(Backend::Texture::get_texture(src.get_id()));
+ Renderers::get().get_renderer().blit(texture, src_rect, surface, dst_pos, options);
+ }
+
+ void Texture::fill(const Color &color, const Rect &rect)
+ {
+ save();
+ Renderers::get().get_renderer().fill(surface, color, rect);
+ }
+
+ void Texture::fill_blend(const Color &color, const Rect &rect)
+ {
+ save();
+ Renderers::get().get_renderer().fill_blend(surface, color, rect);
+ }
+
+ SDL_Surface *Texture::get_transform(const RenderOptions &options, unsigned int numerator, unsigned int denominator)
+ {
+ if(scaled.get_size() == Area())
+ {
+ scaled = surface.scale(numerator, denominator);
+ }
+ assert(scaled.get_size() == surface.get_size() * numerator / denominator);
+ std::map<Color, AlphaMap> &cache = options.h_flip ? (options.v_flip ? d_cache : h_cache) : (options.v_flip ? v_cache : n_cache);
+ if(!cache[options.color][options.alpha])
+ {
+ Surface transformed = scaled.modulate(options.color).modulate(options.alpha);
+ if(options.h_flip)
+ {
+ transformed = transformed.h_flip();
+ }
+ if(options.v_flip)
+ {
+ transformed = transformed.v_flip();
+ }
+ cache[options.color][options.alpha] = Blitters::optimize(transformed);
+ }
+ return cache[options.color][options.alpha];
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_SDL_TEXTURE_HPP
+#define UNISON_VIDEO_SDL_TEXTURE_HPP
+
+#include <unison/video/backend/Texture.hpp>
+#include <unison/video/Surface.hpp>
+
+#include <string>
+#include <map>
+
+#include "SDL.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace SDL
+ {
+ class Texture : public Backend::Texture
+ {
+ public:
+ Texture(const Surface &surface);
+ //Texture(const Surface &surface, const std::string &name);
+ //Texture(Backend::Texture *texture);
+ ~Texture();
+
+ const Surface get_surface();
+ void save();
+ void blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options);
+ void blit(const Video::Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options);
+ void fill(const Color &color, const Rect &rect);
+ void fill_blend(const Color &color, const Rect &rect);
+
+ SDL_Surface *get_transform(const RenderOptions &options, unsigned int numerator, unsigned int denominator);
+ private:
+ Surface scaled;
+ struct AlphaMap
+ {
+ static void ref(SDL_Surface *surface)
+ {
+ if(surface)
+ {
+ surface->refcount++;
+ }
+ }
+
+ SDL_Surface *data[256];
+
+ AlphaMap()
+ {
+ memset(data, 0, 256 * sizeof(SDL_Surface *));
+ }
+
+ ~AlphaMap()
+ {
+ std::for_each(data, data + 256, SDL_FreeSurface);
+ }
+
+ void operator = (const AlphaMap &other)
+ {
+ std::for_each(other.data, other.data + 256, ref);
+ std::for_each(data, data + 256, SDL_FreeSurface);
+ memcpy(data, other.data, 256 * sizeof(SDL_Surface *));
+ }
+
+ SDL_Surface *&operator [] (Uint8 alpha)
+ {
+ return data[alpha];
+ }
+ };
+ //std::map<Color, AlphaMap> cache;
+ std::map<Color, AlphaMap> n_cache;
+ std::map<Color, AlphaMap> h_cache;
+ std::map<Color, AlphaMap> v_cache;
+ std::map<Color, AlphaMap> d_cache;
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include "Window.hpp"
+#include "Texture.hpp"
+#include <unison/video/Window.hpp>
+#include <unison/video/Surface.hpp>
+#include <unison/video/Texture.hpp>
+#include <unison/video/Renderers.hpp>
+#include <unison/video/sdl/Blitters.hpp>
+#include <unison/video/backend/Renderer.hpp>
+#include <unison/video/backend/Texture.hpp>
+
+#include <functional>
+#include <assert.h>
+
+#include "SDL.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ namespace SDL
+ {
+ Window::Window(const Area &size, const Area &logical_size, bool fullscreen) :
+ scale_numerator(1),
+ scale_denominator(1),
+ offset(),
+ window(0)
+ {
+ assert(size.x);
+ assert(size.y);
+ unsigned int xratio = size.x * logical_size.y;
+ unsigned int yratio = size.y * logical_size.x;
+ if(xratio < yratio)
+ {
+ scale_numerator = size.x;
+ scale_denominator = logical_size.x;
+ }
+ else
+ {
+ scale_numerator = size.y;
+ scale_denominator = logical_size.y;
+ }
+ offset = (size - logical_size * scale_numerator / scale_denominator) / 2;
+ window = SDL_SetVideoMode(size.x, size.y, 0, SDL_ANYFORMAT | SDL_SWSURFACE | (fullscreen ? SDL_FULLSCREEN : 0));
+ assert(window);
+ Uint32 black = SDL_MapRGB(window->format, 0, 0, 0);
+ SDL_FillRect(window, 0, black);
+ }
+
+ Window::~Window()
+ {
+ }
+
+ void Window::take_screenshot(const std::string &filename) const
+ {
+ assert(window);
+ SDL_SaveBMP(window, filename.c_str());
+ }
+
+ void Window::flip()
+ {
+ assert(window);
+ SDL_Flip(window);
+ }
+
+ void Window::set_title(const std::string &title)
+ {
+ SDL_WM_SetCaption(title.c_str(), title.c_str());
+ }
+
+ void Window::set_icon(const Surface &icon)
+ {
+ SDL_Surface *icon_surface = Blitters::create_sdl_surface_from(icon);
+ assert(icon_surface);
+ SDL_WM_SetIcon(icon_surface, 0);
+ SDL_FreeSurface(icon_surface);
+ }
+
+ Area Window::get_size() const
+ {
+ return Area(window->w, window->h);
+ }
+
+ bool Window::is_fullscreen() const
+ {
+ return window->flags & SDL_FULLSCREEN;
+ }
+
+ void Window::blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ assert(src.get_pixels());
+ assert(window);
+
+ Surface fragment;
+ if(src_rect.pos == Point() && (src_rect.size == Area() || src_rect.size == src.get_size()))
+ {
+ fragment = src;
+ }
+ else
+ {
+ fragment = Surface(src_rect.size);
+ Video::Blitters::blit_blend_none(src, src_rect, fragment, Point());
+ }
+ fragment = fragment.scale(scale_numerator, scale_denominator);
+ if(options.h_flip)
+ {
+ fragment = fragment.h_flip();
+ }
+ if(options.v_flip)
+ {
+ fragment = fragment.v_flip();
+ }
+ SDL_Surface *src_surface = Blitters::create_sdl_surface_from(fragment.modulate(options.color).modulate(options.alpha));
+ assert(src_surface);
+
+ Point scaled_dst_pos(dst_pos);
+ scaled_dst_pos *= scale_numerator;
+ scaled_dst_pos /= scale_denominator;
+ scaled_dst_pos += offset;
+
+ Blitters::blit_blend(src_surface, Rect(Point(), fragment.get_size()), window, scaled_dst_pos, options.blend);
+
+ SDL_FreeSurface(src_surface);
+ }
+
+ void Window::blit(const Video::Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options)
+ {
+ Texture *texture = dynamic_cast<Texture *>(Backend::Texture::get_texture(src.get_id()));
+ assert(texture);
+ assert(window);
+
+ Rect scaled_src_rect(src_rect);
+ /*if(options.h_flip)
+ {
+ scaled_src_rect.pos.x = src.get_size().x - src_rect.pos.x - src_rect.size.x;
+ }
+ if(options.v_flip)
+ {
+ scaled_src_rect.pos.y = src.get_size().y - src_rect.pos.y - src_rect.size.y;
+ }*/
+ if(dst_pos.x < 0)
+ {
+ if(scaled_src_rect.size.x < (unsigned int) -dst_pos.x)
+ {
+ return;
+ }
+ scaled_src_rect.pos.x += -dst_pos.x;
+ scaled_src_rect.size.x += dst_pos.x;
+ }
+ if(dst_pos.y < 0)
+ {
+ if(scaled_src_rect.size.y < (unsigned int) -dst_pos.y)
+ {
+ return;
+ }
+ scaled_src_rect.pos.y += -dst_pos.y;
+ scaled_src_rect.size.y += dst_pos.y;
+ }
+ scaled_src_rect.pos *= scale_numerator;
+ scaled_src_rect.pos /= scale_denominator;
+ scaled_src_rect.size *= scale_numerator;
+ scaled_src_rect.size /= scale_denominator;
+
+ Point scaled_dst_pos(dst_pos);
+ if(dst_pos.x < 0)
+ {
+ scaled_dst_pos.x = 0;
+ }
+ if(dst_pos.y < 0)
+ {
+ scaled_dst_pos.y = 0;
+ }
+ scaled_dst_pos *= scale_numerator;
+ scaled_dst_pos /= scale_denominator;
+ scaled_dst_pos += offset;
+
+ Blitters::blit_blend(texture->get_transform(options, scale_numerator, scale_denominator), scaled_src_rect, window, scaled_dst_pos, options.blend);
+ }
+
+ void Window::fill(const Color &color, const Rect &rect)
+ {
+ assert(window);
+
+ Uint32 mapped = SDL_MapRGBA(window->format, color.red, color.green, color.blue, color.alpha);
+
+ Rect scaled_rect(rect);
+ scaled_rect.pos *= scale_numerator;
+ scaled_rect.pos /= scale_denominator;
+ scaled_rect.size *= scale_numerator;
+ scaled_rect.size /= scale_denominator;
+ scaled_rect.pos += offset;
+
+ SDL_FillRect(window, &scaled_rect, mapped);
+ }
+
+ void Window::fill_blend(const Color &color, const Rect &rect)
+ {
+ assert(window);
+
+ Uint32 mapped = SDL_MapRGB(window->format, color.red, color.green, color.blue);
+
+ Rect scaled_rect(rect);
+ scaled_rect.pos *= scale_numerator;
+ scaled_rect.pos /= scale_denominator;
+ scaled_rect.size *= scale_numerator;
+ scaled_rect.size /= scale_denominator;
+ scaled_rect.pos += offset;
+
+ if(color.alpha == 0xff)
+ {
+ SDL_FillRect(window, &scaled_rect, mapped);
+ }
+ else if(color.alpha != 0x00)
+ {
+ SDL_Surface *temp = SDL_CreateRGBSurface(window->flags, scaled_rect.size.x, scaled_rect.size.y, window->format->BitsPerPixel, window->format->Rmask, window->format->Gmask, window->format->Bmask, window->format->Amask);
+
+ SDL_FillRect(temp, 0, mapped);
+ SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, color.alpha);
+ SDL_BlitSurface(temp, 0, window, &scaled_rect);
+ SDL_FreeSurface(temp);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Copyright Timothy Goya 2007.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef UNISON_VIDEO_SDL_WINDOW_HPP
+#define UNISON_VIDEO_SDL_WINDOW_HPP
+
+#include <unison/video/backend/Window.hpp>
+#include <unison/video/RenderOptions.hpp>
+#include <string>
+
+#include "SDL.h"
+
+namespace Unison
+{
+ namespace Video
+ {
+ class Texture;
+ namespace SDL
+ {
+ class Window : public Backend::Window
+ {
+ public:
+ Window(const Area &size, const Area &logical_size, bool fullscreen = false);
+
+ ~Window();
+ void take_screenshot(const std::string &filename) const;
+ void flip();
+ void set_title(const std::string &title);
+ void set_icon(const Surface &icon);
+ Area get_size() const;
+ bool is_fullscreen() const;
+ void blit(const Surface &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options);
+ void blit(const Video::Texture &src, const Point &dst_pos, const Rect &src_rect, const RenderOptions &options);
+ void fill(const Color &color, const Rect &rect);
+ void fill_blend(const Color &color, const Rect &rect);
+ private:
+ /// The numerator of the scaling ratio
+ unsigned int scale_numerator;
+
+ /// The denominator of the scaling ratio
+ unsigned int scale_denominator;
+
+ /// The offset used to center the drawing area
+ Point offset;
+
+ /// The window
+ SDL_Surface *window;
+ };
+ }
+ }
+}
+
+#endif
// $Id$
//
// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de> //
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
#include <assert.h>
#include "log.hpp"
+#include <unison/video/Color.hpp>
+
class Color
{
public:
return greyscale() < other.greyscale();
}
+ Unison::Video::Color to_unison_color() const
+ {
+ Unison::Video::Color color;
+ color.red = (unsigned char) (red * 0xff);
+ color.green = (unsigned char) (green * 0xff);
+ color.blue =(unsigned char) (blue * 0xff);
+ color.alpha = (unsigned char) (alpha * 0xff);
+ return color;
+ }
+
float red, green, blue, alpha;
static const Color BLACK;
#include <algorithm>
#include <cassert>
#include <iostream>
-#include <SDL_image.h>
+//#include <SDL_image.h>
#include <sstream>
#include <iomanip>
#include <physfs.h>
#include "drawing_context.hpp"
-#include "drawing_request.hpp"
-#include "video_systems.hpp"
-#include "renderer.hpp"
-#include "lightmap.hpp"
+//#include "drawing_request.hpp"
+//#include "video_systems.hpp"
+//#include "renderer.hpp"
+//#include "lightmap.hpp"
#include "surface.hpp"
#include "main.hpp"
#include "gameconfig.hpp"
-#include "texture.hpp"
-#include "texture_manager.hpp"
-#include "obstack/obstackpp.hpp"
+//#include "texture.hpp"
+//#include "texture_manager.hpp"
+//#include "obstack/obstackpp.hpp"
-static inline int next_po2(int val)
+#include "math/vector.hpp"
+#include "math/rect.hpp"
+
+#include <unison/video/Renderers.hpp>
+
+/*static inline int next_po2(int val)
{
int result = 1;
while(result < val)
result *= 2;
return result;
-}
+}*/
-DrawingContext::DrawingContext() :
- renderer(0), lightmap(0), ambient_color(1.0f, 1.0f, 1.0f, 1.0f), target(NORMAL), screenshot_requested(false)
+DrawingContext::DrawingContext()
+ /*renderer(0), lightmap(0), ambient_color(1.0f, 1.0f, 1.0f, 1.0f), target(NORMAL), screenshot_requested(false)*/
{
- requests = &drawing_requests;
- obstack_init(&obst);
+ ambient_color = Color(1.0f, 1.0f, 1.0f, 1.0f);
+ target = NORMAL;
+ screenshot_requested = false;
+ draw_target = &normal_list;
+ /*requests = &drawing_requests;
+ obstack_init(&obst);*/
}
DrawingContext::~DrawingContext()
{
- delete renderer;
+ /*delete renderer;
delete lightmap;
- obstack_free(&obst, NULL);
+ obstack_free(&obst, NULL);*/
}
void
DrawingContext::init_renderer()
{
- delete renderer;
+ Unison::Video::Renderers::get().set_renderer(config->video);
+ Unison::Video::Window::get().set_logical_size(Unison::Video::Area(SCREEN_WIDTH, SCREEN_HEIGHT));
+ Unison::Video::Window::get().open(Unison::Video::Area(config->screenwidth, config->screenheight), config->use_fullscreen);
+ lightmap = Unison::Video::Surface(Unison::Video::Area(SCREEN_WIDTH, SCREEN_HEIGHT));
+ /*delete renderer;
delete lightmap;
renderer = new_renderer();
- lightmap = new_lightmap();
+ lightmap = new_lightmap();*/
}
void
{
assert(surface != 0);
- DrawingRequest* request = new(obst) DrawingRequest();
+ Unison::Video::RenderOptions options;
+ options.color = color.to_unison_color();
+ options.alpha = (unsigned char) transform.alpha * 0xff;
+ options.blend = blend.to_unison_blend();
+ options.h_flip = surface->get_flipx() != (transform.drawing_effect == HORIZONTAL_FLIP);
+ options.v_flip = (transform.drawing_effect == VERTICAL_FLIP);
+
+ Vector transformed = transform.apply(position);
+ Unison::Video::Point dst_pos((int) transformed.x, (int) transformed.y);
+
+ (*draw_target)[layer].blit_section(surface->get_texture(), dst_pos, options);
+
+ /*DrawingRequest* request = new(obst) DrawingRequest();
request->target = target;
request->type = SURFACE;
request->request_data = const_cast<Surface*> (surface);
- requests->push_back(request);
+ requests->push_back(request);*/
}
void
{
assert(surface != 0);
- DrawingRequest* request = new(obst) DrawingRequest();
+ Unison::Video::TextureSection texture = surface->get_texture();
+ texture.clip_rect.pos.x += (int) source.x;
+ texture.clip_rect.pos.y += (int) source.y;
+ texture.clip_rect.size.x += (unsigned int) size.x;
+ texture.clip_rect.size.y += (unsigned int) size.y;
+
+ Unison::Video::RenderOptions options;
+ options.alpha = (unsigned char) transform.alpha * 0xff;
+ options.h_flip = surface->get_flipx() != (transform.drawing_effect == HORIZONTAL_FLIP);
+ options.v_flip = (transform.drawing_effect == VERTICAL_FLIP);
+
+ Vector transformed = transform.apply(dest);
+ Unison::Video::Point dst_pos((int) transformed.x, (int) transformed.y);
+
+ (*draw_target)[layer].blit_section(texture, dst_pos, options);
+
+ /*DrawingRequest* request = new(obst) DrawingRequest();
request->target = target;
request->type = SURFACE_PART;
}
request->request_data = surfacepartrequest;
- requests->push_back(request);
+ requests->push_back(request);*/
}
void
DrawingContext::draw_text(const Font* font, const std::string& text,
const Vector& position, FontAlignment alignment, int layer)
{
- DrawingRequest* request = new(obst) DrawingRequest();
+ font->draw((*draw_target)[layer], text, transform.apply(position),
+ alignment, transform.drawing_effect, transform.alpha);
+
+ /*DrawingRequest* request = new(obst) DrawingRequest();
request->target = target;
request->type = TEXT;
textrequest->alignment = alignment;
request->request_data = textrequest;
- requests->push_back(request);
+ requests->push_back(request);*/
}
void
ALIGN_CENTER, layer);
}
+namespace
+{
+ class GradientRequest : public Unison::Video::DisplayList::Request
+ {
+ public:
+ GradientRequest(const Color &top, const Color &bottom) :
+ top(top),
+ bottom(bottom)
+ {
+ }
+
+ void do_request(Unison::Video::Blittable *dst) const
+ {
+ for(int y = 0;y < SCREEN_HEIGHT;++y)
+ {
+ Unison::Video::Color color;
+ color.red = (Uint8)((((float)(top.red-bottom.red)/(0-SCREEN_HEIGHT)) * y + top.red) * 255);
+ color.green = (Uint8)((((float)(top.green-bottom.green)/(0-SCREEN_HEIGHT)) * y + top.green) * 255);
+ color.green = (Uint8)((((float)(top.blue-bottom.blue)/(0-SCREEN_HEIGHT)) * y + top.blue) * 255);
+ color.alpha = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-SCREEN_HEIGHT)) * y + top.alpha) * 255);
+ dst->fill(color, Unison::Video::Rect(0, y, SCREEN_WIDTH, 1));
+ }
+ }
+ private:
+ Color top;
+ Color bottom;
+ };
+}
+
void
DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
{
- DrawingRequest* request = new(obst) DrawingRequest();
+ (*draw_target)[layer].add_request(new GradientRequest(top, bottom));
+
+ /*DrawingRequest* request = new(obst) DrawingRequest();
request->target = target;
request->type = GRADIENT;
gradientrequest->bottom = bottom;
request->request_data = gradientrequest;
- requests->push_back(request);
+ requests->push_back(request);*/
}
void
DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
const Color& color, int layer)
{
- DrawingRequest* request = new(obst) DrawingRequest();
+ Vector transformed = transform.apply(topleft);
+
+ Unison::Video::Rect rect;
+ rect.pos = Unison::Video::Point((int) transformed.x, (int) transformed.y);
+ rect.size.x = (unsigned int) size.x;
+ rect.size.y = (unsigned int) size.y;
+
+ (*draw_target)[layer].fill_blend(color.to_unison_color(), rect);
+
+ /*DrawingRequest* request = new(obst) DrawingRequest();
request->target = target;
request->type = FILLRECT;
fillrectrequest->color.alpha = color.alpha * transform.alpha;
request->request_data = fillrectrequest;
- requests->push_back(request);
+ requests->push_back(request);*/
}
void
DrawingContext::draw_filled_rect(const Rect& rect, const Color& color,
int layer)
{
- DrawingRequest* request = new(obst) DrawingRequest();
+ Vector transformed = transform.apply(rect.p1);
+
+ Unison::Video::Rect unison_rect;
+ unison_rect.pos.x = (int) transformed.x;
+ unison_rect.pos.y = (int) transformed.y;
+ unison_rect.size.x = (unsigned int) rect.get_width();
+ unison_rect.size.y = (unsigned int) rect.get_height();
+
+ (*draw_target)[layer].fill_blend(color.to_unison_color(), unison_rect);
+
+ /*DrawingRequest* request = new(obst) DrawingRequest();
request->target = target;
request->type = FILLRECT;
fillrectrequest->color.alpha = color.alpha * transform.alpha;
request->request_data = fillrectrequest;
- requests->push_back(request);
+ requests->push_back(request);*/
}
void
return;
}
- DrawingRequest* request = new(obst) DrawingRequest();
+ /*DrawingRequest* request = new(obst) DrawingRequest();
request->target = target;
request->type = GETLIGHT;
- request->pos = transform.apply(position);
+ request->pos = transform.apply(position);*/
//There is no light offscreen.
- if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
- || request->pos.x < 0 || request->pos.y < 0){
+ if(position.x >= SCREEN_WIDTH || position.y >= SCREEN_HEIGHT
+ || position.x < 0 || position.y < 0){
*color = Color( 0, 0, 0);
return;
}
- request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
+ Vector transformed = transform.apply(position);
+ Unison::Video::Point pos((int) transformed.x, (int) transformed.y);
+ get_light_requests.push_back(std::make_pair(pos, color));
+
+ /*request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
GetLightRequest* getlightrequest = new(obst) GetLightRequest();
getlightrequest->color_ptr = color;
request->request_data = getlightrequest;
- lightmap_requests.push_back(request);
+ lightmap_requests.push_back(request);*/
}
void
// PART1: create lightmap
if(use_lightmap) {
- lightmap->start_draw(ambient_color);
- handle_drawing_requests(lightmap_requests);
- lightmap->end_draw();
+ lightmap.fill(ambient_color.to_unison_color());
+ lightmap.draw(lightmap_list);
+ //lightmap->start_draw(ambient_color);
+ //handle_drawing_requests(lightmap_requests);
+ //lightmap->end_draw();
}
- handle_drawing_requests(drawing_requests);
+ Unison::Video::Window::get().draw(normal_list);
+
+ //handle_drawing_requests(drawing_requests);
if(use_lightmap) {
- lightmap->do_draw();
+ Unison::Video::Window::get().blit(lightmap, Unison::Video::Point(), Unison::Video::Rect(), Unison::Video::BLEND_MOD);
+ //lightmap->do_draw();
}
- obstack_free(&obst, NULL);
- obstack_init(&obst);
+ //obstack_free(&obst, NULL);
+ //obstack_init(&obst);
// if a screenshot was requested, take one
if (screenshot_requested) {
- renderer->do_take_screenshot();
+ // FIXME renderer->do_take_screenshot();
screenshot_requested = false;
}
- renderer->flip();
+ //renderer->flip();
+ Unison::Video::Window::get().flip();
+
+ normal_list.clear();
+ lightmap_list.clear();
}
-class RequestPtrCompare
+/*class RequestPtrCompare
: public std::binary_function<const DrawingRequest*,
+ std::vector<std::pair<Unison::Video::Point, Color *> > get_light_requests;
const DrawingRequest*,
bool>
{
}
}
requests.clear();
-}
+}*/
void
DrawingContext::push_transform()
{
this->target = target;
if(target == LIGHTMAP) {
- requests = &lightmap_requests;
+ draw_target = &lightmap_list;
} else {
assert(target == NORMAL);
- requests = &drawing_requests;
+ draw_target = &normal_list;
}
}
#include <string>
#include <memory>
-#include <stdint.h>
+//#include <stdint.h>
-#include <SDL_video.h>
-
-#include "glutil.hpp"
-#include "obstack/obstack.h"
-#include "math/vector.hpp"
-#include "math/rect.hpp"
+//#include "obstack/obstack.h"
+//#include "math/vector.hpp"
+//#include "math/rect.hpp"
#include "drawing_request.hpp"
#include "font.hpp"
#include "color.hpp"
+#include <unison/video/Texture.hpp>
+#include <unison/video/Window.hpp>
+#include <unison/video/DisplayList.hpp>
+
+class Vector;
+class Rect;
+
class Surface;
-class Texture;
+/*class Texture;
struct DrawingRequest;
class Renderer;
-class Lightmap;
+class Lightmap;*/
/**
* This class provides functions for drawing things on screen. It also
}
};
- Renderer *renderer;
- Lightmap *lightmap;
+ Unison::Video::Surface lightmap;
+ std::vector<std::pair<Unison::Video::Point, Color *> > get_light_requests;
+
+ std::map<int, Unison::Video::DisplayList> normal_list;
+ std::map<int, Unison::Video::DisplayList> lightmap_list;
+ std::map<int, Unison::Video::DisplayList> *draw_target;
+
+ //Renderer *renderer;
+ //Lightmap *lightmap;
/// the transform stack
std::vector<Transform> transformstack;
std::vector<Blend> blend_stack;
Blend blend_mode;
- typedef std::vector<DrawingRequest*> DrawingRequests;
+ /*typedef std::vector<DrawingRequest*> DrawingRequests;
void handle_drawing_requests(DrawingRequests& requests);
DrawingRequests drawing_requests;
DrawingRequests lightmap_requests;
- DrawingRequests* requests;
+ DrawingRequests* requests;*/
Color ambient_color;
Target target;
std::vector<Target> target_stack;
/* obstack holding the memory of the drawing requests */
- struct obstack obst;
+ //struct obstack obst;
bool screenshot_requested; /**< true if a screenshot should be taken after the next frame has been rendered */
};
#include <string>
#include <memory>
-#include <stdint.h>
+#include <assert.h>
-#include <SDL_video.h>
+//#include <stdint.h>
+
+//#include <SDL_video.h>
#include "glutil.hpp"
-#include "math/vector.hpp"
-#include "color.hpp"
-#include "font.hpp"
+//#include "math/vector.hpp"
+//#include "color.hpp"
+//#include "font.hpp"
+#include <unison/video/RenderOptions.hpp>
class Surface;
Blend(GLenum s, GLenum d)
: sfactor(s), dfactor(d)
{}
+
+ Unison::Video::BlendMode to_unison_blend() const
+ {
+ if(sfactor == GL_ONE && dfactor == GL_ZERO)
+ {
+ return Unison::Video::BLEND_NONE;
+ }
+ else if(sfactor == GL_SRC_ALPHA && dfactor == GL_ONE_MINUS_SRC_ALPHA)
+ {
+ return Unison::Video::BLEND_ALPHA;
+ }
+ else if(sfactor == GL_SRC_ALPHA && dfactor == GL_ONE)
+ {
+ return Unison::Video::BLEND_ADD;
+ }
+ else if(sfactor == GL_ZERO && dfactor == GL_SRC_COLOR)
+ {
+ return Unison::Video::BLEND_MOD;
+ }
+ else
+ {
+ assert(0 && "Unsupported blend factors");
+ }
+ }
};
enum Target {
NORMAL, LIGHTMAP
};
-enum RequestType
+/*enum RequestType
{
SURFACE, SURFACE_PART, TEXT, GRADIENT, FILLRECT, GETLIGHT
};
struct GetLightRequest
{
Color* color_ptr;
-};
+};*/
#endif
#include "lisp/lisp.hpp"
#include "screen.hpp"
#include "font.hpp"
-#include "renderer.hpp"
+//#include "renderer.hpp"
+#include <unison/video/Blittable.hpp>
#include "drawing_context.hpp"
#include "log.hpp"
}
void
-Font::draw(Renderer *renderer, const std::string& text, const Vector& pos_,
+Font::draw(Unison::Video::Blittable &dst, const std::string& text, const Vector& pos_,
FontAlignment alignment, DrawingEffect drawing_effect,
float alpha) const
{
// no bluring as we would get with subpixel positions
pos.x = static_cast<int>(pos.x);
- draw_text(renderer, temp, pos, drawing_effect, alpha);
+ draw_text(dst, temp, pos, drawing_effect, alpha);
if (i == text.size())
break;
}
void
-Font::draw_text(Renderer *renderer, const std::string& text, const Vector& pos,
+Font::draw_text(Unison::Video::Blittable &dst, const std::string& text, const Vector& pos,
DrawingEffect drawing_effect, float alpha) const
{
if(shadowsize > 0)
// FIXME: shadow_glyph_surface and glyph_surface do currently
// share the same glyph array, this is incorrect and should be
// fixed, it is however hardly noticable
- draw_chars(renderer, shadow_glyph_surface, text,
+ draw_chars(dst, shadow_glyph_surface, text,
pos + Vector(shadowsize, shadowsize), drawing_effect, alpha);
}
- draw_chars(renderer, glyph_surface, text, pos, drawing_effect, alpha);
+ draw_chars(dst, glyph_surface, text, pos, drawing_effect, alpha);
}
int
}
void
-Font::draw_chars(Renderer *renderer, Surface* pchars, const std::string& text,
+Font::draw_chars(Unison::Video::Blittable &dst, Surface* pchars, const std::string& text,
const Vector& pos, DrawingEffect drawing_effect,
float alpha) const
{
else
{
const Glyph& glyph = glyphs[font_index];
- DrawingRequest request;
+
+ assert(pchars != 0);
+
+ Unison::Video::TextureSection texture = pchars->get_texture();
+ texture.clip_rect.pos.x += (int) glyph.rect.p1.x;
+ texture.clip_rect.pos.y += (int) glyph.rect.p1.y;
+ texture.clip_rect.size.x += (unsigned int) glyph.rect.get_width();
+ texture.clip_rect.size.y += (unsigned int) glyph.rect.get_height();
+
+ Unison::Video::RenderOptions options;
+ options.alpha = (unsigned char) alpha * 0xff;
+ options.h_flip = (drawing_effect == HORIZONTAL_FLIP);
+ options.v_flip = (drawing_effect == VERTICAL_FLIP);
+
+ Vector transformed = p + glyph.offset;
+ Unison::Video::Point dst_pos((int) transformed.x, (int) transformed.y);
+
+ dst.blit_section(texture, dst_pos, options);
+
+ /*DrawingRequest request;
request.pos = p + glyph.offset;
request.drawing_effect = drawing_effect;
request.request_data = &surfacepartrequest;
renderer->draw_surface_part(request);
- p.x += glyphs[font_index].advance;
+ p.x += glyphs[font_index].advance;*/
}
}
}
#include "math/vector.hpp"
#include "math/rect.hpp"
-class Renderer;
+class Unison::Video::Blittable;
enum FontAlignment {
ALIGN_LEFT,
/** Draws the given text to the screen. Also needs the position.
* Type of alignment, drawing effect and alpha are optional. */
- void draw(Renderer *renderer, const std::string& text, const Vector& pos,
+ void draw(Unison::Video::Blittable &dst, const std::string& text, const Vector& pos,
FontAlignment allignment = ALIGN_LEFT,
DrawingEffect drawing_effect = NO_EFFECT,
float alpha = 1.0f) const;
private:
friend class DrawingContext;
- void draw_text(Renderer *renderer, const std::string& text, const Vector& pos,
+ void draw_text(Unison::Video::Blittable &dst, const std::string& text, const Vector& pos,
DrawingEffect drawing_effect = NO_EFFECT,
float alpha = 1.0f) const;
- void draw_chars(Renderer *renderer, Surface* pchars, const std::string& text,
+ void draw_chars(Unison::Video::Blittable &dst, Surface* pchars, const std::string& text,
const Vector& position, DrawingEffect drawing_effect,
float alpha) const;
+++ /dev/null
-// $Id: gl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#include <config.h>
-
-#ifdef HAVE_OPENGL
-
-#include <functional>
-#include <algorithm>
-#include <cassert>
-#include <math.h>
-#include <iostream>
-#include <SDL_image.h>
-#include <sstream>
-#include <iomanip>
-#include <physfs.h>
-
-#include "glutil.hpp"
-#include "gl_lightmap.hpp"
-#include "gl_surface_data.hpp"
-#include "drawing_context.hpp"
-#include "drawing_request.hpp"
-#include "renderer.hpp"
-#include "surface.hpp"
-#include "font.hpp"
-#include "main.hpp"
-#include "gameconfig.hpp"
-#include "gl_texture.hpp"
-#include "texture_manager.hpp"
-#include "obstack/obstackpp.hpp"
-
-namespace
-{
- inline void intern_draw(float left, float top, float right, float bottom,
- float uv_left, float uv_top,
- float uv_right, float uv_bottom,
- float angle, float alpha,
- const Color& color,
- const Blend& blend,
- DrawingEffect effect)
- {
- if(effect & HORIZONTAL_FLIP)
- std::swap(uv_left, uv_right);
- if(effect & VERTICAL_FLIP) {
- std::swap(uv_top, uv_bottom);
- }
-
- float center_x = (left + right) / 2;
- float center_y = (top + bottom) / 2;
-
- float sa = sinf(angle/180.0f*M_PI);
- float ca = cosf(angle/180.0f*M_PI);
-
- left -= center_x;
- right -= center_x;
-
- top -= center_y;
- bottom -= center_y;
-
- glBlendFunc(blend.sfactor, blend.dfactor);
- glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
- glBegin(GL_QUADS);
- glTexCoord2f(uv_left, uv_top);
- glVertex2f(left*ca - top*sa + center_x,
- left*sa + top*ca + center_y);
-
- glTexCoord2f(uv_right, uv_top);
- glVertex2f(right*ca - top*sa + center_x,
- right*sa + top*ca + center_y);
-
- glTexCoord2f(uv_right, uv_bottom);
- glVertex2f(right*ca - bottom*sa + center_x,
- right*sa + bottom*ca + center_y);
-
- glTexCoord2f(uv_left, uv_bottom);
- glVertex2f(left*ca - bottom*sa + center_x,
- left*sa + bottom*ca + center_y);
- glEnd();
-
- // FIXME: find a better way to restore the blend mode
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-}
-
-namespace GL
-{
- static inline int next_po2(int val)
- {
- int result = 1;
- while(result < val)
- result *= 2;
-
- return result;
- }
-
- Lightmap::Lightmap()
- {
- screen = SDL_GetVideoSurface();
-
- lightmap_width = screen->w / LIGHTMAP_DIV;
- lightmap_height = screen->h / LIGHTMAP_DIV;
- unsigned int width = next_po2(lightmap_width);
- unsigned int height = next_po2(lightmap_height);
-
- lightmap = new Texture(width, height);
-
- lightmap_uv_right = static_cast<float>(lightmap_width) / static_cast<float>(width);
- lightmap_uv_bottom = static_cast<float>(lightmap_height) / static_cast<float>(height);
- texture_manager->register_texture(lightmap);
- }
-
- Lightmap::~Lightmap()
- {
- texture_manager->remove_texture(lightmap);
- delete lightmap;
- }
-
- void
- Lightmap::start_draw(const Color &ambient_color)
- {
- glViewport(0, screen->h - lightmap_height, lightmap_width, lightmap_height);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- glClearColor( ambient_color.red, ambient_color.green, ambient_color.blue, 1 );
- glClear(GL_COLOR_BUFFER_BIT);
- }
-
- void
- Lightmap::end_draw()
- {
- glDisable(GL_BLEND);
- glBindTexture(GL_TEXTURE_2D, lightmap->get_handle());
- glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, screen->h - lightmap_height, lightmap_width, lightmap_height);
-
- glViewport(0, 0, screen->w, screen->h);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glEnable(GL_BLEND);
- //glClear(GL_COLOR_BUFFER_BIT);
- }
-
- void
- Lightmap::do_draw()
- {
- const Texture* texture = lightmap;
-
- // multiple the lightmap with the framebuffer
- glBlendFunc(GL_DST_COLOR, GL_ZERO);
-
- glBindTexture(GL_TEXTURE_2D, texture->get_handle());
- glBegin(GL_QUADS);
-
- glTexCoord2f(0, lightmap_uv_bottom);
- glVertex2f(0, 0);
-
- glTexCoord2f(lightmap_uv_right, lightmap_uv_bottom);
- glVertex2f(SCREEN_WIDTH, 0);
-
- glTexCoord2f(lightmap_uv_right, 0);
- glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
-
- glTexCoord2f(0, 0);
- glVertex2f(0, SCREEN_HEIGHT);
-
- glEnd();
-
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-
- void
- Lightmap::draw_surface(const DrawingRequest& request)
- {
- const Surface* surface = (const Surface*) request.request_data;
- GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
- GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
-
- glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
- intern_draw(request.pos.x, request.pos.y,
- request.pos.x + surface->get_width(),
- request.pos.y + surface->get_height(),
- surface_data->get_uv_left(),
- surface_data->get_uv_top(),
- surface_data->get_uv_right(),
- surface_data->get_uv_bottom(),
- request.angle,
- request.alpha,
- request.color,
- request.blend,
- request.drawing_effect);
- }
-
- void
- Lightmap::draw_surface_part(const DrawingRequest& request)
- {
- const SurfacePartRequest* surfacepartrequest
- = (SurfacePartRequest*) request.request_data;
- const Surface *surface = surfacepartrequest->surface;
- GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
- GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
-
- float uv_width = surface_data->get_uv_right() - surface_data->get_uv_left();
- float uv_height = surface_data->get_uv_bottom() - surface_data->get_uv_top();
-
- float uv_left = surface_data->get_uv_left() + (uv_width * surfacepartrequest->source.x) / surface->get_width();
- float uv_top = surface_data->get_uv_top() + (uv_height * surfacepartrequest->source.y) / surface->get_height();
- float uv_right = surface_data->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width();
- float uv_bottom = surface_data->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height();
-
- glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
- intern_draw(request.pos.x, request.pos.y,
- request.pos.x + surfacepartrequest->size.x,
- request.pos.y + surfacepartrequest->size.y,
- uv_left,
- uv_top,
- uv_right,
- uv_bottom,
- 0.0,
- request.alpha,
- Color(1.0, 1.0, 1.0),
- Blend(),
- request.drawing_effect);
- }
-
- void
- Lightmap::draw_gradient(const DrawingRequest& request)
- {
- const GradientRequest* gradientrequest
- = (GradientRequest*) request.request_data;
- const Color& top = gradientrequest->top;
- const Color& bottom = gradientrequest->bottom;
-
- glDisable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- glColor4f(top.red, top.green, top.blue, top.alpha);
- glVertex2f(0, 0);
- glVertex2f(SCREEN_WIDTH, 0);
- glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha);
- glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
- glVertex2f(0, SCREEN_HEIGHT);
- glEnd();
- glEnable(GL_TEXTURE_2D);
- glColor4f(1, 1, 1, 1);
- }
-
- void
- Lightmap::draw_filled_rect(const DrawingRequest& request)
- {
- const FillRectRequest* fillrectrequest
- = (FillRectRequest*) request.request_data;
-
- float x = request.pos.x;
- float y = request.pos.y;
- float w = fillrectrequest->size.x;
- float h = fillrectrequest->size.y;
-
- glDisable(GL_TEXTURE_2D);
- glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
- fillrectrequest->color.blue, fillrectrequest->color.alpha);
-
- glBegin(GL_QUADS);
- glVertex2f(x, y);
- glVertex2f(x+w, y);
- glVertex2f(x+w, y+h);
- glVertex2f(x, y+h);
- glEnd();
- glEnable(GL_TEXTURE_2D);
- glColor4f(1, 1, 1, 1);
- }
-
- void
- Lightmap::get_light(const DrawingRequest& request) const
- {
- const GetLightRequest* getlightrequest
- = (GetLightRequest*) request.request_data;
-
- float pixels[3];
- for( int i = 0; i<3; i++)
- pixels[i] = 0.0f; //set to black
-
- float posX = request.pos.x * lightmap_width / SCREEN_WIDTH;
- float posY = screen->h - request.pos.y * lightmap_height / SCREEN_HEIGHT;
- glReadPixels((GLint) posX, (GLint) posY , 1, 1, GL_RGB, GL_FLOAT, pixels);
- *(getlightrequest->color_ptr) = Color( pixels[0], pixels[1], pixels[2]);
- //printf("get_light %f/%f =>%f/%f r%f g%f b%f\n", request.pos.x, request.pos.y, posX, posY, pixels[0], pixels[1], pixels[2]);
- }
-}
-
-#endif
+++ /dev/null
-// $Id: gl_lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#include <config.h>
-
-#ifdef HAVE_OPENGL
-
-#ifndef SUPERTUX_GL_LIGHTMAP_H
-#define SUPERTUX_GL_LIGHTMAP_H
-
-#include <SDL_video.h>
-
-#include "lightmap.hpp"
-
-struct DrawingRequest;
-
-namespace GL
-{
- class Texture;
- class Lightmap : public ::Lightmap
- {
- public:
- Lightmap();
- ~Lightmap();
-
- void start_draw(const Color &ambient_color);
- void end_draw();
- void do_draw();
- void draw_surface(const DrawingRequest& request);
- void draw_surface_part(const DrawingRequest& request);
- void draw_text(const DrawingRequest& request);
- void draw_gradient(const DrawingRequest& request);
- void draw_filled_rect(const DrawingRequest& request);
- void get_light(const DrawingRequest& request) const;
-
- private:
- static const int LIGHTMAP_DIV = 5;
-
- SDL_Surface* screen;
- Texture* lightmap;
- int lightmap_width, lightmap_height;
- float lightmap_uv_right, lightmap_uv_bottom;
- };
-}
-
-#endif
-
-#endif
+++ /dev/null
-// $Id: gl_renderer.cpp 5063 2007-05-27 11:32:00Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#include <config.h>
-
-#ifdef HAVE_OPENGL
-
-#include <functional>
-#include <algorithm>
-#include <cassert>
-#include <math.h>
-#include <iostream>
-#include <SDL_image.h>
-#include <sstream>
-#include <iomanip>
-#include <physfs.h>
-
-#include "glutil.hpp"
-#include "gl_renderer.hpp"
-#include "gl_texture.hpp"
-#include "gl_surface_data.hpp"
-#include "drawing_context.hpp"
-#include "drawing_request.hpp"
-#include "surface.hpp"
-#include "font.hpp"
-#include "main.hpp"
-#include "gameconfig.hpp"
-#include "texture.hpp"
-#include "texture_manager.hpp"
-#include "obstack/obstackpp.hpp"
-#define LIGHTMAP_DIV 5
-
-namespace
-{
- inline void intern_draw(float left, float top, float right, float bottom,
- float uv_left, float uv_top,
- float uv_right, float uv_bottom,
- float angle, float alpha,
- const Color& color,
- const Blend& blend,
- DrawingEffect effect)
- {
- if(effect & HORIZONTAL_FLIP)
- std::swap(uv_left, uv_right);
- if(effect & VERTICAL_FLIP) {
- std::swap(uv_top, uv_bottom);
- }
-
- float center_x = (left + right) / 2;
- float center_y = (top + bottom) / 2;
-
- float sa = sinf(angle/180.0f*M_PI);
- float ca = cosf(angle/180.0f*M_PI);
-
- left -= center_x;
- right -= center_x;
-
- top -= center_y;
- bottom -= center_y;
-
- glBlendFunc(blend.sfactor, blend.dfactor);
- glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
- glBegin(GL_QUADS);
- glTexCoord2f(uv_left, uv_top);
- glVertex2f(left*ca - top*sa + center_x,
- left*sa + top*ca + center_y);
-
- glTexCoord2f(uv_right, uv_top);
- glVertex2f(right*ca - top*sa + center_x,
- right*sa + top*ca + center_y);
-
- glTexCoord2f(uv_right, uv_bottom);
- glVertex2f(right*ca - bottom*sa + center_x,
- right*sa + bottom*ca + center_y);
-
- glTexCoord2f(uv_left, uv_bottom);
- glVertex2f(left*ca - bottom*sa + center_x,
- left*sa + bottom*ca + center_y);
- glEnd();
-
- // FIXME: find a better way to restore the blend mode
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-}
-
-namespace GL
-{
- Renderer::Renderer()
- {
- if(texture_manager != 0)
- texture_manager->save_textures();
-
- if(config->try_vsync) {
- /* we want vsync for smooth scrolling */
- SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
- }
-
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
- SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
-
- int flags = SDL_OPENGL;
- if(config->use_fullscreen)
- flags |= SDL_FULLSCREEN;
- int width = config->screenwidth;
- int height = config->screenheight;
- int bpp = 0;
-
- SDL_Surface *screen = SDL_SetVideoMode(width, height, bpp, flags);
- if(screen == 0) {
- std::stringstream msg;
- msg << "Couldn't set video mode (" << width << "x" << height
- << "-" << bpp << "bpp): " << SDL_GetError();
- throw std::runtime_error(msg.str());
- }
-
- // setup opengl state and transform
- glDisable(GL_DEPTH_TEST);
- glDisable(GL_CULL_FACE);
- glEnable(GL_TEXTURE_2D);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glViewport(0, 0, screen->w, screen->h);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- // logical resolution here not real monitor resolution
- glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glTranslatef(0, 0, 0);
-
- check_gl_error("Setting up view matrices");
-
-
- if(texture_manager == 0)
- texture_manager = new TextureManager();
- else
- texture_manager->reload_textures();
- }
-
- Renderer::~Renderer()
- {
- }
-
- void
- Renderer::draw_surface(const DrawingRequest& request)
- {
- const Surface* surface = (const Surface*) request.request_data;
- GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
- GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
-
- glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
- intern_draw(request.pos.x, request.pos.y,
- request.pos.x + surface->get_width(),
- request.pos.y + surface->get_height(),
- surface_data->get_uv_left(),
- surface_data->get_uv_top(),
- surface_data->get_uv_right(),
- surface_data->get_uv_bottom(),
- request.angle,
- request.alpha,
- request.color,
- request.blend,
- request.drawing_effect);
- }
-
- void
- Renderer::draw_surface_part(const DrawingRequest& request)
- {
- const SurfacePartRequest* surfacepartrequest
- = (SurfacePartRequest*) request.request_data;
- const Surface *surface = surfacepartrequest->surface;
- GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
- GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
-
- float uv_width = surface_data->get_uv_right() - surface_data->get_uv_left();
- float uv_height = surface_data->get_uv_bottom() - surface_data->get_uv_top();
-
- float uv_left = surface_data->get_uv_left() + (uv_width * surfacepartrequest->source.x) / surface->get_width();
- float uv_top = surface_data->get_uv_top() + (uv_height * surfacepartrequest->source.y) / surface->get_height();
- float uv_right = surface_data->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width();
- float uv_bottom = surface_data->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height();
-
- glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
- intern_draw(request.pos.x, request.pos.y,
- request.pos.x + surfacepartrequest->size.x,
- request.pos.y + surfacepartrequest->size.y,
- uv_left,
- uv_top,
- uv_right,
- uv_bottom,
- 0.0,
- request.alpha,
- Color(1.0, 1.0, 1.0),
- Blend(),
- request.drawing_effect);
- }
-
- void
- Renderer::draw_gradient(const DrawingRequest& request)
- {
- const GradientRequest* gradientrequest
- = (GradientRequest*) request.request_data;
- const Color& top = gradientrequest->top;
- const Color& bottom = gradientrequest->bottom;
-
- glDisable(GL_TEXTURE_2D);
- glBegin(GL_QUADS);
- glColor4f(top.red, top.green, top.blue, top.alpha);
- glVertex2f(0, 0);
- glVertex2f(SCREEN_WIDTH, 0);
- glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha);
- glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
- glVertex2f(0, SCREEN_HEIGHT);
- glEnd();
- glEnable(GL_TEXTURE_2D);
- glColor4f(1, 1, 1, 1);
- }
-
- void
- Renderer::draw_filled_rect(const DrawingRequest& request)
- {
- const FillRectRequest* fillrectrequest
- = (FillRectRequest*) request.request_data;
-
- float x = request.pos.x;
- float y = request.pos.y;
- float w = fillrectrequest->size.x;
- float h = fillrectrequest->size.y;
-
- glDisable(GL_TEXTURE_2D);
- glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
- fillrectrequest->color.blue, fillrectrequest->color.alpha);
-
- glBegin(GL_QUADS);
- glVertex2f(x, y);
- glVertex2f(x+w, y);
- glVertex2f(x+w, y+h);
- glVertex2f(x, y+h);
- glEnd();
- glEnable(GL_TEXTURE_2D);
- glColor4f(1, 1, 1, 1);
- }
-
- void
- Renderer::do_take_screenshot()
- {
- // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
-
- SDL_Surface *shot_surf;
- // create surface to hold screenshot
- #if SDL_BYTEORDER == SDL_BIG_ENDIAN
- shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0);
- #else
- shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
- #endif
- if (!shot_surf) {
- log_warning << "Could not create RGB Surface to contain screenshot" << std::endl;
- return;
- }
-
- // read pixels into array
- char* pixels = new char[3 * SCREEN_WIDTH * SCREEN_HEIGHT];
- if (!pixels) {
- log_warning << "Could not allocate memory to store screenshot" << std::endl;
- SDL_FreeSurface(shot_surf);
- return;
- }
- glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, pixels);
-
- // copy array line-by-line
- for (int i = 0; i < SCREEN_HEIGHT; i++) {
- char* src = pixels + (3 * SCREEN_WIDTH * (SCREEN_HEIGHT - i - 1));
- if(SDL_MUSTLOCK(shot_surf))
- {
- SDL_LockSurface(shot_surf);
- }
- char* dst = ((char*)shot_surf->pixels) + i * shot_surf->pitch;
- memcpy(dst, src, 3 * SCREEN_WIDTH);
- if(SDL_MUSTLOCK(shot_surf))
- {
- SDL_UnlockSurface(shot_surf);
- }
- }
-
- // free array
- delete[](pixels);
-
- // save screenshot
- static const std::string writeDir = PHYSFS_getWriteDir();
- static const std::string dirSep = PHYSFS_getDirSeparator();
- static const std::string baseName = "screenshot";
- static const std::string fileExt = ".bmp";
- std::string fullFilename;
- for (int num = 0; num < 1000; num++) {
- std::ostringstream oss;
- oss << baseName;
- oss << std::setw(3) << std::setfill('0') << num;
- oss << fileExt;
- std::string fileName = oss.str();
- fullFilename = writeDir + dirSep + fileName;
- if (!PHYSFS_exists(fileName.c_str())) {
- SDL_SaveBMP(shot_surf, fullFilename.c_str());
- log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
- SDL_FreeSurface(shot_surf);
- return;
- }
- }
- log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
- SDL_FreeSurface(shot_surf);
- }
-
- void
- Renderer::flip()
- {
- assert_gl("drawing");
- SDL_GL_SwapBuffers();
- }
-}
-
-#endif
+++ /dev/null
-// $Id: gl_renderer.hpp 4986 2007-04-16 17:48:28Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#include <config.h>
-
-#ifdef HAVE_OPENGL
-
-#ifndef SUPERTUX_GL_RENDERER_H
-#define SUPERTUX_GL_RENDERER_H
-
-#include "renderer.hpp"
-
-namespace GL
-{
- class Renderer : public ::Renderer
- {
- public:
- Renderer();
- ~Renderer();
-
- void draw_surface(const DrawingRequest& request);
- void draw_surface_part(const DrawingRequest& request);
- void draw_text(const DrawingRequest& request);
- void draw_gradient(const DrawingRequest& request);
- void draw_filled_rect(const DrawingRequest& request);
- void do_take_screenshot();
- void flip();
- };
-}
-
-#endif
-
-#endif
+++ /dev/null
-// $Id: gl_surface_data.hpp 4063 2006-07-21 21:05:23Z anmaster $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#include <config.h>
-
-#ifdef HAVE_OPENGL
-
-#ifndef __GL_SURFACE_DATA_HPP__
-#define __GL_SURFACE_DATA_HPP__
-
-#include "surface.hpp"
-
-namespace GL
-{
- class SurfaceData
- {
- private:
- const Surface &surface;
- float uv_left;
- float uv_top;
- float uv_right;
- float uv_bottom;
-
- public:
- SurfaceData(const Surface &surface) :
- surface(surface)
- {
- uv_left = (float) surface.get_x() / surface.get_texture()->get_texture_width();
- uv_top = (float) surface.get_y() / surface.get_texture()->get_texture_height();
- uv_right = (float) (surface.get_x() + surface.get_width()) / surface.get_texture()->get_texture_width();
- uv_bottom = (float) (surface.get_y() + surface.get_height()) / surface.get_texture()->get_texture_height();
- }
-
- float get_uv_left() const
- {
- return surface.get_flipx() ? uv_right : uv_left;
- }
-
- float get_uv_top() const
- {
- return uv_top;
- }
-
- float get_uv_right() const
- {
- return surface.get_flipx() ? uv_left : uv_right;
- }
-
- float get_uv_bottom() const
- {
- return uv_bottom;
- }
- };
-}
-
-#endif
-
-#endif
+++ /dev/null
-// $Id: gl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#include <config.h>
-
-#ifdef HAVE_OPENGL
-
-#include "gl_texture.hpp"
-#include "gameconfig.hpp"
-#include "glutil.hpp"
-#include "log.hpp"
-
-#include <assert.h>
-#include <stdexcept>
-
-namespace
-{
- inline bool is_power_of_2(int v)
- {
- return (v & (v-1)) == 0;
- }
-
- inline int next_power_of_two(int val)
- {
- int result = 1;
- while(result < val)
- result *= 2;
- return result;
- }
-}
-
-namespace GL
-{
- Texture::Texture(unsigned int width, unsigned int height)
- {
- assert(is_power_of_2(width));
- assert(is_power_of_2(height));
- texture_width = width;
- texture_height = height;
- image_width = width;
- image_height = height;
-
- assert_gl("before creating texture");
- glGenTextures(1, &handle);
-
- try {
- glBindTexture(GL_TEXTURE_2D, handle);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_width,
- texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
-
- set_texture_params();
- } catch(...) {
- glDeleteTextures(1, &handle);
- throw;
- }
- }
-
- Texture::Texture(SDL_Surface* image)
- {
- texture_width = next_power_of_two(image->w);
- texture_height = next_power_of_two(image->h);
- image_width = image->w;
- image_height = image->h;
-
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE,
- texture_width, texture_height, 32,
- 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
-#else
- SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE,
- texture_width, texture_height, 32,
- 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
-#endif
-
- if(convert == 0) {
- throw std::runtime_error("Couldn't create texture: out of memory");
- }
-
- SDL_SetAlpha(image, 0, 0);
- SDL_BlitSurface(image, 0, convert, 0);
-
- assert_gl("before creating texture");
- glGenTextures(1, &handle);
-
- try {
- GLenum sdl_format;
- if(convert->format->BytesPerPixel == 3)
- sdl_format = GL_RGB;
- else if(convert->format->BytesPerPixel == 4)
- sdl_format = GL_RGBA;
- else
- assert(false);
-
- glBindTexture(GL_TEXTURE_2D, handle);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glPixelStorei(GL_UNPACK_ROW_LENGTH, convert->pitch/convert->format->BytesPerPixel);
- if(SDL_MUSTLOCK(convert))
- {
- SDL_LockSurface(convert);
- }
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_width,
- texture_height, 0, sdl_format,
- GL_UNSIGNED_BYTE, convert->pixels);
- if(SDL_MUSTLOCK(convert))
- {
- SDL_UnlockSurface(convert);
- }
-
- assert_gl("creating texture");
-
- set_texture_params();
- } catch(...) {
- glDeleteTextures(1, &handle);
- SDL_FreeSurface(convert);
- throw;
- }
- SDL_FreeSurface(convert);
- }
-
- Texture::~Texture()
- {
- glDeleteTextures(1, &handle);
- }
-
- void
- Texture::set_texture_params()
- {
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
-
- assert_gl("set texture params");
- }
-}
-
-#endif
+++ /dev/null
-// $Id: gl_texture.hpp 4063 2006-07-21 21:05:23Z anmaster $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#include <config.h>
-
-#ifdef HAVE_OPENGL
-
-#ifndef __GL_TEXTURE_HPP__
-#define __GL_TEXTURE_HPP__
-
-#include <SDL.h>
-
-#include "texture.hpp"
-#include "glutil.hpp"
-
-/**
- * This class is a wrapper around a texture handle. It stores the texture width
- * and height and provides convenience functions for uploading SDL_Surfaces
- * into the texture
- */
-namespace GL
-{
- class Texture : public ::Texture
- {
- protected:
- GLuint handle;
- unsigned int texture_width;
- unsigned int texture_height;
- unsigned int image_width;
- unsigned int image_height;
-
- public:
- Texture(unsigned int width, unsigned int height);
- Texture(SDL_Surface* image);
- ~Texture();
-
- const GLuint &get_handle() const {
- return handle;
- }
-
- void set_handle(GLuint handle) {
- this->handle = handle;
- }
-
- unsigned int get_texture_width() const
- {
- return texture_width;
- }
-
- unsigned int get_texture_height() const
- {
- return texture_height;
- }
-
- unsigned int get_image_width() const
- {
- return image_width;
- }
-
- unsigned int get_image_height() const
- {
- return image_height;
- }
-
- void set_image_width(unsigned int width)
- {
- image_width = width;
- }
-
- void set_image_height(unsigned int height)
- {
- image_height = height;
- }
-
- private:
- void set_texture_params();
- };
-}
-
-#endif
-
-#endif
#ifndef __GLUTIL_HPP__
#define __GLUTIL_HPP__
-#include <config.h>
+typedef unsigned int GLenum;
+typedef int GLint;
+#define GL_ZERO 0x0
+#define GL_ONE 0x1
+#define GL_SRC_COLOR 0x0300
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_DST_COLOR 0x0306
+
+//#include <config.h>
+
+#if 0
#ifdef HAVE_OPENGL
#include <sstream>
#define GL_ONE 3
#endif
+#endif
#endif
+++ /dev/null
-// $Id: lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#ifndef SUPERTUX_LIGHTMAP_H
-#define SUPERTUX_LIGHTMAP_H
-
-#include <vector>
-#include <string>
-#include <memory>
-
-#include <stdint.h>
-
-#include <SDL_video.h>
-
-#include "glutil.hpp"
-#include "obstack/obstack.h"
-#include "math/vector.hpp"
-#include "math/rect.hpp"
-#include "drawing_request.hpp"
-#include "surface.hpp"
-#include "font.hpp"
-#include "color.hpp"
-
-class Texture;
-struct DrawingRequest;
-
-class Lightmap
-{
-public:
- virtual ~Lightmap() {}
-
- virtual void start_draw(const Color &ambient_color) = 0;
- virtual void end_draw() = 0;
- virtual void do_draw() = 0;
- virtual void draw_surface(const DrawingRequest& request) = 0;
- virtual void draw_surface_part(const DrawingRequest& request) = 0;
- virtual void draw_gradient(const DrawingRequest& request) = 0;
- virtual void draw_filled_rect(const DrawingRequest& request) = 0;
- virtual void get_light(const DrawingRequest& request) const = 0;
-};
-
-#endif
-
+++ /dev/null
-// $Id: drawing_context.hpp 4986 2007-04-16 17:48:28Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#ifndef SUPERTUX_RENDERER_H
-#define SUPERTUX_RENDERER_H
-
-#include <vector>
-#include <string>
-#include <memory>
-
-#include <stdint.h>
-
-#include <SDL_video.h>
-
-#include "glutil.hpp"
-#include "obstack/obstack.h"
-#include "math/vector.hpp"
-#include "math/rect.hpp"
-#include "surface.hpp"
-#include "font.hpp"
-#include "color.hpp"
-
-class Surface;
-class Texture;
-struct DrawingRequest;
-
-class Renderer
-{
-public:
- virtual ~Renderer() {}
-
- virtual void draw_surface(const DrawingRequest& request) = 0;
- virtual void draw_surface_part(const DrawingRequest& request) = 0;
- virtual void draw_gradient(const DrawingRequest& request) = 0;
- virtual void draw_filled_rect(const DrawingRequest& request)= 0;
- virtual void do_take_screenshot() = 0;
- virtual void flip() = 0;
-};
-
-#endif
-
+++ /dev/null
-// $Id: sdl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#include <config.h>
-
-#include <functional>
-#include <algorithm>
-#include <cassert>
-#include <iostream>
-#include <SDL_image.h>
-#include <sstream>
-#include <iomanip>
-#include <physfs.h>
-
-#include "glutil.hpp"
-#include "sdl_lightmap.hpp"
-#include "sdl_texture.hpp"
-#include "sdl_surface_data.hpp"
-#include "drawing_context.hpp"
-#include "drawing_request.hpp"
-#include "renderer.hpp"
-#include "surface.hpp"
-#include "font.hpp"
-#include "main.hpp"
-#include "gameconfig.hpp"
-#include "texture.hpp"
-#include "texture_manager.hpp"
-#include "obstack/obstackpp.hpp"
-
-namespace SDL
-{
- Lightmap::Lightmap()
- {
- screen = SDL_GetVideoSurface();
-
- float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
- float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
- if(xfactor < yfactor)
- {
- numerator = config->screenwidth;
- denominator = SCREEN_WIDTH;
- }
- else
- {
- numerator = config->screenheight;
- denominator = SCREEN_HEIGHT;
- }
-
- LIGHTMAP_DIV = 8 * numerator / denominator;
-
- width = screen->w / LIGHTMAP_DIV;
- height = screen->h / LIGHTMAP_DIV;
-
- red_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
- green_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
- blue_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
- }
-
- Lightmap::~Lightmap()
- {
- free(red_channel);
- free(green_channel);
- free(blue_channel);
- }
-
- void
- Lightmap::start_draw(const Color &ambient_color)
- {
- memset(red_channel, (Uint8) (ambient_color.red * 255), width * height * sizeof(Uint8));
- memset(green_channel, (Uint8) (ambient_color.green * 255), width * height * sizeof(Uint8));
- memset(blue_channel, (Uint8) (ambient_color.blue * 255), width * height * sizeof(Uint8));
- }
-
- void
- Lightmap::end_draw()
- {
- }
-
-//#define BILINEAR
-
-#ifdef BILINEAR
- namespace
- {
- void merge(Uint8 color[3], Uint8 color0[3], Uint8 color1[3], int rem, int total)
- {
- color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
- color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
- color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
- }
- }
-#endif
-
- void
- Lightmap::do_draw()
- {
- // FIXME: This is really slow
- if(LIGHTMAP_DIV == 1)
- {
- int bpp = screen->format->BytesPerPixel;
- if(SDL_MUSTLOCK(screen))
- {
- SDL_LockSurface(screen);
- }
- Uint8 *pixel = (Uint8 *) screen->pixels;
- int loc = 0;
- for(int y = 0;y < height;y++) {
- for(int x = 0;x < width;x++, pixel += bpp, loc++) {
- if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
- {
- continue;
- }
- Uint32 mapped = 0;
- switch(bpp) {
- case 1:
- mapped = *pixel;
- break;
- case 2:
- mapped = *(Uint16 *)pixel;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- mapped |= pixel[0] << 16;
- mapped |= pixel[1] << 8;
- mapped |= pixel[2] << 0;
-#else
- mapped |= pixel[0] << 0;
- mapped |= pixel[1] << 8;
- mapped |= pixel[2] << 16;
-#endif
- break;
- case 4:
- mapped = *(Uint32 *)pixel;
- break;
- }
- Uint8 red, green, blue, alpha;
- SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
- red = (red * red_channel[loc]) >> 8;
- green = (green * green_channel[loc]) >> 8;
- blue = (blue * blue_channel[loc]) >> 8;
- mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
- switch(bpp) {
- case 1:
- *pixel = mapped;
- break;
- case 2:
- *(Uint16 *)pixel = mapped;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- pixel[0] = (mapped >> 16) & 0xff;
- pixel[1] = (mapped >> 8) & 0xff;
- pixel[2] = (mapped >> 0) & 0xff;
-#else
- pixel[0] = (mapped >> 0) & 0xff;
- pixel[1] = (mapped >> 8) & 0xff;
- pixel[2] = (mapped >> 16) & 0xff;
-#endif
- break;
- case 4:
- *(Uint32 *)pixel = mapped;
- break;
- }
- }
- pixel += screen->pitch - width * bpp;
- }
- if(SDL_MUSTLOCK(screen))
- {
- SDL_UnlockSurface(screen);
- }
- }
- else
- {
- int bpp = screen->format->BytesPerPixel;
- if(SDL_MUSTLOCK(screen))
- {
- SDL_LockSurface(screen);
- }
- Uint8 *div_pixel = (Uint8 *) screen->pixels;
- int loc = 0;
- for(int y = 0;y < height;y++) {
- for(int x = 0;x < width;x++, div_pixel += bpp * LIGHTMAP_DIV, loc++) {
- if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
- {
- continue;
- }
- Uint8 *pixel = div_pixel;
- for(int div_y = 0;div_y < LIGHTMAP_DIV;div_y++) {
- for(int div_x = 0;div_x < LIGHTMAP_DIV;pixel += bpp, div_x++) {
- Uint32 mapped = 0;
- switch(bpp) {
- case 1:
- mapped = *pixel;
- break;
- case 2:
- mapped = *(Uint16 *)pixel;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- mapped |= pixel[0] << 16;
- mapped |= pixel[1] << 8;
- mapped |= pixel[2] << 0;
-#else
- mapped |= pixel[0] << 0;
- mapped |= pixel[1] << 8;
- mapped |= pixel[2] << 16;
-#endif
- break;
- case 4:
- mapped = *(Uint32 *)pixel;
- break;
- }
- Uint8 red, green, blue, alpha;
- SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
-
-#ifdef BILINEAR
- int xinc = (x + 1 != width ? 1 : 0);
- int yinc = (y + 1 != height ? width : 0);
- Uint8 color00[3], color01[3], color10[3], color11[3];
- {
- color00[0] = red_channel[loc];
- color00[1] = green_channel[loc];
- color00[2] = blue_channel[loc];
- }
- {
- color01[0] = red_channel[loc + xinc];
- color01[1] = green_channel[loc + xinc];
- color01[2] = blue_channel[loc + xinc];
- }
- {
- color10[0] = red_channel[loc + yinc];
- color10[1] = green_channel[loc + yinc];
- color10[2] = blue_channel[loc + yinc];
- }
- {
- color11[0] = red_channel[loc + yinc + xinc];
- color11[1] = green_channel[loc + yinc + xinc];
- color11[2] = blue_channel[loc + yinc + xinc];
- }
- Uint8 color0[3], color1[3], color[3];
- merge(color0, color00, color01, div_x, LIGHTMAP_DIV);
- merge(color1, color10, color11, div_x, LIGHTMAP_DIV);
- merge(color, color0, color1, div_y, LIGHTMAP_DIV);
- red = (red * color[0]) >> 8;
- green = (green * color[1]) >> 8;
- blue = (blue * color[2]) >> 8;
-#else
- red = (red * red_channel[loc]) >> 8;
- green = (green * green_channel[loc]) >> 8;
- blue = (blue * blue_channel[loc]) >> 8;
-#endif
-
- mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
- switch(bpp) {
- case 1:
- *pixel = mapped;
- break;
- case 2:
- *(Uint16 *)pixel = mapped;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- pixel[0] = (mapped >> 16) & 0xff;
- pixel[1] = (mapped >> 8) & 0xff;
- pixel[2] = (mapped >> 0) & 0xff;
-#else
- pixel[0] = (mapped >> 0) & 0xff;
- pixel[1] = (mapped >> 8) & 0xff;
- pixel[2] = (mapped >> 16) & 0xff;
-#endif
- break;
- case 4:
- *(Uint32 *)pixel = mapped;
- break;
- }
- }
- pixel += screen->pitch - LIGHTMAP_DIV * bpp;
- }
- }
- div_pixel += (screen->pitch - width * bpp) * LIGHTMAP_DIV;
- }
- if(SDL_MUSTLOCK(screen))
- {
- SDL_UnlockSurface(screen);
- }
- }
- }
-
- void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
- {
- dstx /= LIGHTMAP_DIV;
- dsty /= LIGHTMAP_DIV;
- int srcx = src_rect->x / LIGHTMAP_DIV;
- int srcy = src_rect->y / LIGHTMAP_DIV;
- int blit_width = src_rect->w / LIGHTMAP_DIV;
- int blit_height = src_rect->h / LIGHTMAP_DIV;
- int bpp = src->format->BytesPerPixel;
- if(SDL_MUSTLOCK(src))
- {
- SDL_LockSurface(src);
- }
- Uint8 *pixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
- int loc = dsty * width + dstx;
- for(int y = 0;y < blit_height;y++) {
- for(int x = 0;x < blit_width;x++, pixel += bpp * LIGHTMAP_DIV, loc++) {
- if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
- {
- continue;
- }
- if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
- {
- continue;
- }
-
- Uint32 mapped = 0;
- switch(bpp) {
- case 1:
- mapped = *pixel;
- break;
- case 2:
- mapped = *(Uint16 *)pixel;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- mapped |= pixel[0] << 16;
- mapped |= pixel[1] << 8;
- mapped |= pixel[2] << 0;
-#else
- mapped |= pixel[0] << 0;
- mapped |= pixel[1] << 8;
- mapped |= pixel[2] << 16;
-#endif
- break;
- case 4:
- mapped = *(Uint32 *)pixel;
- break;
- }
- Uint8 red, green, blue, alpha;
- SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
-
- if(red != 0)
- {
- int redsum = red_channel[loc] + (red * alpha >> 8);
- red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
- }
- if(green != 0)
- {
- int greensum = green_channel[loc] + (green * alpha >> 8);
- green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
- }
- if(blue != 0)
- {
- int bluesum = blue_channel[loc] + (blue * alpha >> 8);
- blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
- }
- }
- pixel += (src->pitch - blit_width * bpp) * LIGHTMAP_DIV;
- loc += width - blit_width;
- }
- if(SDL_MUSTLOCK(src))
- {
- SDL_UnlockSurface(src);
- }
- }
-
- /*void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
- {
- int bpp = src->format->BytesPerPixel;
- if(SDL_MUSTLOCK(src))
- {
- SDL_LockSurface(src);
- }
- Uint8 *pixel = (Uint8 *) src->pixels + src_rect->y * src->pitch + src_rect->x * bpp;
- int loc = dsty * width + dstx;
- for(int y = 0;y < src_rect->h;y++) {
- for(int x = 0;x < src_rect->w;x++, pixel += bpp, loc++) {
- if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
- {
- continue;
- }
- if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
- {
- continue;
- }
-
- Uint32 mapped = 0;
- switch(bpp) {
- case 1:
- mapped = *pixel;
- break;
- case 2:
- mapped = *(Uint16 *)pixel;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- mapped |= pixel[0] << 16;
- mapped |= pixel[1] << 8;
- mapped |= pixel[2] << 0;
-#else
- mapped |= pixel[0] << 0;
- mapped |= pixel[1] << 8;
- mapped |= pixel[2] << 16;
-#endif
- break;
- case 4:
- mapped = *(Uint32 *)pixel;
- break;
- }
- Uint8 red, green, blue, alpha;
- SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
-
- if(red != 0)
- {
- int redsum = red_channel[loc] + (red * alpha >> 8);
- red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
- }
- if(green != 0)
- {
- int greensum = green_channel[loc] + (green * alpha >> 8);
- green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
- }
- if(blue != 0)
- {
- int bluesum = blue_channel[loc] + (blue * alpha >> 8);
- blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
- }
- }
- pixel += src->pitch - src_rect->w * bpp;
- loc += width - src_rect->w;
- }
- if(SDL_MUSTLOCK(src))
- {
- SDL_UnlockSurface(src);
- }
- }*/
-
- void
- Lightmap::draw_surface(const DrawingRequest& request)
- {
- if((request.color.red == 0.0 && request.color.green == 0.0 && request.color.blue == 0.0) || request.color.alpha == 0.0 || request.alpha == 0.0)
- {
- return;
- }
- //FIXME: support parameters request.alpha, request.angle, request.blend
-
- const Surface* surface = (const Surface*) request.request_data;
- SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
- SDL::SurfaceData *surface_data = reinterpret_cast<SDL::SurfaceData *>(surface->get_surface_data());
-
- DrawingEffect effect = request.drawing_effect;
- if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
-
- SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
-
- // get and check SDL_Surface
- if (transform == 0) {
- std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
- return;
- }
-
- SDL_Rect *src_rect = surface_data->get_src_rect(effect);
- int dstx = (int) request.pos.x * numerator / denominator;
- int dsty = (int) request.pos.y * numerator / denominator;
- light_blit(transform, src_rect, dstx, dsty);
- }
-
- void
- Lightmap::draw_surface_part(const DrawingRequest& request)
- {
- const SurfacePartRequest* surfacepartrequest
- = (SurfacePartRequest*) request.request_data;
-
- const Surface* surface = surfacepartrequest->surface;
- SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
-
- DrawingEffect effect = request.drawing_effect;
- if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
-
- SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
-
- // get and check SDL_Surface
- if (transform == 0) {
- std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
- return;
- }
-
- int ox, oy;
- if (effect == HORIZONTAL_FLIP)
- {
- ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
- }
- else
- {
- ox = surface->get_x();
- }
- if (effect == VERTICAL_FLIP)
- {
- oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
- }
- else
- {
- oy = surface->get_y();
- }
-
- SDL_Rect src_rect;
- src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
- src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
- src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
- src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
- int dstx = (int) request.pos.x * numerator / denominator;
- int dsty = (int) request.pos.y * numerator / denominator;
- light_blit(transform, &src_rect, dstx, dsty);
- }
-
- void
- Lightmap::draw_gradient(const DrawingRequest& request)
- {
- const GradientRequest* gradientrequest
- = (GradientRequest*) request.request_data;
- const Color& top = gradientrequest->top;
- const Color& bottom = gradientrequest->bottom;
-
- int loc = 0;
- for(int y = 0;y < height;++y)
- {
- Uint8 red = (Uint8)((((float)(top.red-bottom.red)/(0-height)) * y + top.red) * 255);
- Uint8 green = (Uint8)((((float)(top.green-bottom.green)/(0-height)) * y + top.green) * 255);
- Uint8 blue = (Uint8)((((float)(top.blue-bottom.blue)/(0-height)) * y + top.blue) * 255);
- Uint8 alpha = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-height)) * y + top.alpha) * 255);
- for(int x = 0;x < width;x++, loc++) {
- if(red != 0)
- {
- int redsum = red_channel[loc] + (red * alpha >> 8);
- red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
- }
- if(green != 0)
- {
- int greensum = green_channel[loc] + (green * alpha >> 8);
- green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
- }
- if(blue != 0)
- {
- int bluesum = blue_channel[loc] + (blue * alpha >> 8);
- blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
- }
- }
- }
- }
-
- void
- Lightmap::draw_filled_rect(const DrawingRequest& request)
- {
- const FillRectRequest* fillrectrequest
- = (FillRectRequest*) request.request_data;
-
- int rect_x = (int) (request.pos.x * width / SCREEN_WIDTH);
- int rect_y = (int) (request.pos.y * height / SCREEN_HEIGHT);
- int rect_w = (int) (fillrectrequest->size.x * width / SCREEN_WIDTH);
- int rect_h = (int) (fillrectrequest->size.y * height / SCREEN_HEIGHT);
- Uint8 red = (Uint8) (fillrectrequest->color.red * fillrectrequest->color.alpha * 255);
- Uint8 green = (Uint8) (fillrectrequest->color.green * fillrectrequest->color.alpha * 255);
- Uint8 blue = (Uint8) (fillrectrequest->color.blue * fillrectrequest->color.alpha * 255);
- if(red == 0 && green == 0 && blue == 0)
- {
- return;
- }
- for(int y = rect_y;y < rect_y + rect_h;y++) {
- for(int x = rect_x;x < rect_x + rect_w;x++) {
- int loc = y * width + x;
- if(red != 0)
- {
- int redsum = red_channel[loc] + red;
- red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
- }
- if(green != 0)
- {
- int greensum = green_channel[loc] + green;
- green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
- }
- if(blue != 0)
- {
- int bluesum = blue_channel[loc] + blue;
- blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
- }
- }
- }
- }
-
- void
- Lightmap::get_light(const DrawingRequest& request) const
- {
- const GetLightRequest* getlightrequest
- = (GetLightRequest*) request.request_data;
-
- int x = (int) (request.pos.x * width / SCREEN_WIDTH);
- int y = (int) (request.pos.y * height / SCREEN_HEIGHT);
- int loc = y * width + x;
- *(getlightrequest->color_ptr) = Color(((float)red_channel[loc])/255, ((float)green_channel[loc])/255, ((float)blue_channel[loc])/255);
- }
-}
+++ /dev/null
-// $Id: sdl_lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#ifndef SUPERTUX_SDL_LIGHTMAP_H
-#define SUPERTUX_SDL_LIGHTMAP_H
-
-#include <SDL_video.h>
-
-#include "lightmap.hpp"
-
-class Color;
-struct DrawingRequest;
-
-namespace SDL
-{
- class Lightmap : public ::Lightmap
- {
- public:
- Lightmap();
- ~Lightmap();
-
- void start_draw(const Color &ambient_color);
- void end_draw();
- void do_draw();
- void draw_surface(const DrawingRequest& request);
- void draw_surface_part(const DrawingRequest& request);
- void draw_text(const DrawingRequest& request);
- void draw_gradient(const DrawingRequest& request);
- void draw_filled_rect(const DrawingRequest& request);
- void get_light(const DrawingRequest& request) const;
-
- private:
- SDL_Surface* screen;
- Uint8 *red_channel;
- Uint8 *blue_channel;
- Uint8 *green_channel;
- int width, height;
- int numerator, denominator;
- int LIGHTMAP_DIV;
-
- void light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty);
- };
-}
-
-#endif
-
+++ /dev/null
-// $Id: sdl_renderer.cpp 5063 2007-05-27 11:32:00Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#include <config.h>
-
-#include <functional>
-#include <algorithm>
-#include <stdexcept>
-#include <cassert>
-#include <iostream>
-#include <SDL_image.h>
-#include <sstream>
-#include <iomanip>
-#include <physfs.h>
-
-#include "glutil.hpp"
-#include "sdl_renderer.hpp"
-#include "sdl_texture.hpp"
-#include "sdl_surface_data.hpp"
-#include "drawing_context.hpp"
-#include "drawing_request.hpp"
-#include "surface.hpp"
-#include "font.hpp"
-#include "main.hpp"
-#include "gameconfig.hpp"
-#include "log.hpp"
-#include "texture.hpp"
-#include "texture_manager.hpp"
-#include "obstack/obstackpp.hpp"
-
-namespace
-{
- SDL_Surface *apply_alpha(SDL_Surface *src, float alpha_factor)
- {
- // FIXME: This is really slow
- assert(src->format->Amask);
- int alpha = (int) (alpha_factor * 256);
- SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
- int bpp = dst->format->BytesPerPixel;
- if(SDL_MUSTLOCK(src))
- {
- SDL_LockSurface(src);
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_LockSurface(dst);
- }
- for(int y = 0;y < dst->h;y++) {
- for(int x = 0;x < dst->w;x++) {
- Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
- Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
- Uint32 mapped = 0;
- switch(bpp) {
- case 1:
- mapped = *srcpixel;
- break;
- case 2:
- mapped = *(Uint16 *)srcpixel;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- mapped |= srcpixel[0] << 16;
- mapped |= srcpixel[1] << 8;
- mapped |= srcpixel[2] << 0;
-#else
- mapped |= srcpixel[0] << 0;
- mapped |= srcpixel[1] << 8;
- mapped |= srcpixel[2] << 16;
-#endif
- break;
- case 4:
- mapped = *(Uint32 *)srcpixel;
- break;
- }
- Uint8 r, g, b, a;
- SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
- mapped = SDL_MapRGBA(dst->format, r, g, b, (a * alpha) >> 8);
- switch(bpp) {
- case 1:
- *dstpixel = mapped;
- break;
- case 2:
- *(Uint16 *)dstpixel = mapped;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- dstpixel[0] = (mapped >> 16) & 0xff;
- dstpixel[1] = (mapped >> 8) & 0xff;
- dstpixel[2] = (mapped >> 0) & 0xff;
-#else
- dstpixel[0] = (mapped >> 0) & 0xff;
- dstpixel[1] = (mapped >> 8) & 0xff;
- dstpixel[2] = (mapped >> 16) & 0xff;
-#endif
- break;
- case 4:
- *(Uint32 *)dstpixel = mapped;
- break;
- }
- }
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_UnlockSurface(dst);
- }
- if(SDL_MUSTLOCK(src))
- {
- SDL_UnlockSurface(src);
- }
- return dst;
- }
-}
-
-namespace SDL
-{
- Renderer::Renderer()
- {
- const SDL_VideoInfo *info = SDL_GetVideoInfo();
- log_info << "Hardware surfaces are " << (info->hw_available ? "" : "not ") << "available." << std::endl;
- log_info << "Hardware to hardware blits are " << (info->blit_hw ? "" : "not ") << "accelerated." << std::endl;
- log_info << "Hardware to hardware blits with colorkey are " << (info->blit_hw_CC ? "" : "not ") << "accelerated." << std::endl;
- log_info << "Hardware to hardware blits with alpha are " << (info->blit_hw_A ? "" : "not ") << "accelerated." << std::endl;
- log_info << "Software to hardware blits are " << (info->blit_sw ? "" : "not ") << "accelerated." << std::endl;
- log_info << "Software to hardware blits with colorkey are " << (info->blit_sw_CC ? "" : "not ") << "accelerated." << std::endl;
- log_info << "Software to hardware blits with alpha are " << (info->blit_sw_A ? "" : "not ") << "accelerated." << std::endl;
- log_info << "Color fills are " << (info->blit_fill ? "" : "not ") << "accelerated." << std::endl;
-
- int flags = SDL_SWSURFACE | SDL_ANYFORMAT;
- if(config->use_fullscreen)
- flags |= SDL_FULLSCREEN;
- int width = config->screenwidth;
- int height = config->screenheight;
-
- screen = SDL_SetVideoMode(width, height, 0, flags);
- if(screen == 0) {
- std::stringstream msg;
- msg << "Couldn't set video mode (" << width << "x" << height
- << "): " << SDL_GetError();
- throw std::runtime_error(msg.str());
- }
-
- float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
- float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
- if(xfactor < yfactor)
- {
- numerator = config->screenwidth;
- denominator = SCREEN_WIDTH;
- }
- else
- {
- numerator = config->screenheight;
- denominator = SCREEN_HEIGHT;
- }
-
- if(texture_manager == 0)
- texture_manager = new TextureManager();
- }
-
- Renderer::~Renderer()
- {
- }
-
- void
- Renderer::draw_surface(const DrawingRequest& request)
- {
- //FIXME: support parameters request.alpha, request.angle, request.blend
- const Surface* surface = (const Surface*) request.request_data;
- SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
- SDL::SurfaceData *surface_data = reinterpret_cast<SDL::SurfaceData *>(surface->get_surface_data());
-
- DrawingEffect effect = request.drawing_effect;
- if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
-
- SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
-
- // get and check SDL_Surface
- if (transform == 0) {
- std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
- return;
- }
-
- SDL_Rect *src_rect = surface_data->get_src_rect(effect);
- SDL_Rect dst_rect;
- dst_rect.x = (int) request.pos.x * numerator / denominator;
- dst_rect.y = (int) request.pos.y * numerator / denominator;
-
- Uint8 alpha = 0;
- if(request.alpha != 1.0)
- {
- if(!transform->format->Amask)
- {
- if(transform->flags & SDL_SRCALPHA)
- {
- alpha = transform->format->alpha;
- }
- else
- {
- alpha = 255;
- }
- SDL_SetAlpha(transform, SDL_SRCALPHA, (Uint8) (request.alpha * alpha));
- }
- /*else
- {
- transform = apply_alpha(transform, request.alpha);
- }*/
- }
-
- SDL_BlitSurface(transform, src_rect, screen, &dst_rect);
-
- if(request.alpha != 1.0)
- {
- if(!transform->format->Amask)
- {
- if(alpha == 255)
- {
- SDL_SetAlpha(transform, SDL_RLEACCEL, 0);
- }
- else
- {
- SDL_SetAlpha(transform, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
- }
- }
- /*else
- {
- SDL_FreeSurface(transform);
- }*/
- }
- }
-
- void
- Renderer::draw_surface_part(const DrawingRequest& request)
- {
- const SurfacePartRequest* surfacepartrequest
- = (SurfacePartRequest*) request.request_data;
-
- const Surface* surface = surfacepartrequest->surface;
- SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
-
- DrawingEffect effect = request.drawing_effect;
- if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
-
- SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
-
- // get and check SDL_Surface
- if (transform == 0) {
- std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
- return;
- }
-
- int ox, oy;
- if (effect == HORIZONTAL_FLIP)
- {
- ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
- }
- else
- {
- ox = surface->get_x();
- }
- if (effect == VERTICAL_FLIP)
- {
- oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
- }
- else
- {
- oy = surface->get_y();
- }
-
- SDL_Rect src_rect;
- src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
- src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
- src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
- src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
-
- SDL_Rect dst_rect;
- dst_rect.x = (int) request.pos.x * numerator / denominator;
- dst_rect.y = (int) request.pos.y * numerator / denominator;
-
- Uint8 alpha = 0;
- if(request.alpha != 1.0)
- {
- if(!transform->format->Amask)
- {
- if(transform->flags & SDL_SRCALPHA)
- {
- alpha = transform->format->alpha;
- }
- else
- {
- alpha = 255;
- }
- SDL_SetAlpha(transform, SDL_SRCALPHA, (Uint8) (request.alpha * alpha));
- }
- /*else
- {
- transform = apply_alpha(transform, request.alpha);
- }*/
- }
-
- SDL_BlitSurface(transform, &src_rect, screen, &dst_rect);
-
- if(request.alpha != 1.0)
- {
- if(!transform->format->Amask)
- {
- if(alpha == 255)
- {
- SDL_SetAlpha(transform, SDL_RLEACCEL, 0);
- }
- else
- {
- SDL_SetAlpha(transform, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
- }
- }
- /*else
- {
- SDL_FreeSurface(transform);
- }*/
- }
- }
-
- void
- Renderer::draw_gradient(const DrawingRequest& request)
- {
- const GradientRequest* gradientrequest
- = (GradientRequest*) request.request_data;
- const Color& top = gradientrequest->top;
- const Color& bottom = gradientrequest->bottom;
-
- for(int y = 0;y < screen->h;++y)
- {
- Uint8 r = (Uint8)((((float)(top.red-bottom.red)/(0-screen->h)) * y + top.red) * 255);
- Uint8 g = (Uint8)((((float)(top.green-bottom.green)/(0-screen->h)) * y + top.green) * 255);
- Uint8 b = (Uint8)((((float)(top.blue-bottom.blue)/(0-screen->h)) * y + top.blue) * 255);
- Uint8 a = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-screen->h)) * y + top.alpha) * 255);
- Uint32 color = SDL_MapRGB(screen->format, r, g, b);
-
- SDL_Rect rect;
- rect.x = 0;
- rect.y = y;
- rect.w = screen->w;
- rect.h = 1;
-
- if(a == SDL_ALPHA_OPAQUE) {
- SDL_FillRect(screen, &rect, color);
- } else if(a != SDL_ALPHA_TRANSPARENT) {
- SDL_Surface *temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
-
- SDL_FillRect(temp, 0, color);
- SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, a);
- SDL_BlitSurface(temp, 0, screen, &rect);
- SDL_FreeSurface(temp);
- }
- }
- }
-
- void
- Renderer::draw_filled_rect(const DrawingRequest& request)
- {
- const FillRectRequest* fillrectrequest
- = (FillRectRequest*) request.request_data;
-
- SDL_Rect rect;
- rect.x = (Sint16)request.pos.x * screen->w / SCREEN_WIDTH;
- rect.y = (Sint16)request.pos.y * screen->h / SCREEN_HEIGHT;
- rect.w = (Uint16)fillrectrequest->size.x * screen->w / SCREEN_WIDTH;
- rect.h = (Uint16)fillrectrequest->size.y * screen->h / SCREEN_HEIGHT;
- Uint8 r = static_cast<Uint8>(fillrectrequest->color.red * 255);
- Uint8 g = static_cast<Uint8>(fillrectrequest->color.green * 255);
- Uint8 b = static_cast<Uint8>(fillrectrequest->color.blue * 255);
- Uint8 a = static_cast<Uint8>(fillrectrequest->color.alpha * 255);
- Uint32 color = SDL_MapRGB(screen->format, r, g, b);
- if(a == SDL_ALPHA_OPAQUE) {
- SDL_FillRect(screen, &rect, color);
- } else if(a != SDL_ALPHA_TRANSPARENT) {
- SDL_Surface *temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
-
- SDL_FillRect(temp, 0, color);
- SDL_SetAlpha(temp, SDL_SRCALPHA | SDL_RLEACCEL, a);
- SDL_BlitSurface(temp, 0, screen, &rect);
- SDL_FreeSurface(temp);
- }
- }
-
- void
- Renderer::do_take_screenshot()
- {
- // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
-
- SDL_Surface *screen = SDL_GetVideoSurface();
-
- // save screenshot
- static const std::string writeDir = PHYSFS_getWriteDir();
- static const std::string dirSep = PHYSFS_getDirSeparator();
- static const std::string baseName = "screenshot";
- static const std::string fileExt = ".bmp";
- std::string fullFilename;
- for (int num = 0; num < 1000; num++) {
- std::ostringstream oss;
- oss << baseName;
- oss << std::setw(3) << std::setfill('0') << num;
- oss << fileExt;
- std::string fileName = oss.str();
- fullFilename = writeDir + dirSep + fileName;
- if (!PHYSFS_exists(fileName.c_str())) {
- SDL_SaveBMP(screen, fullFilename.c_str());
- log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
- return;
- }
- }
- log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
- }
-
- void
- Renderer::flip()
- {
- SDL_Flip(screen);
- }
-}
+++ /dev/null
-// $Id: sdl_renderer.hpp 4986 2007-04-16 17:48:28Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#ifndef SUPERTUX_SDL_RENDERER_H
-#define SUPERTUX_SDL_RENDERER_H
-
-#include <SDL_video.h>
-
-#include "renderer.hpp"
-
-namespace SDL
-{
- class Renderer : public ::Renderer
- {
- public:
- Renderer();
- ~Renderer();
-
- void draw_surface(const DrawingRequest& request);
- void draw_surface_part(const DrawingRequest& request);
- void draw_text(const DrawingRequest& request);
- void draw_gradient(const DrawingRequest& request);
- void draw_filled_rect(const DrawingRequest& request);
- void do_take_screenshot();
- void flip();
- private:
- SDL_Surface *screen;
- int numerator, denominator;
- };
-}
-
-#endif
-
+++ /dev/null
-// $Id: gl_surface_data.hpp 4063 2006-07-21 21:05:23Z anmaster $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#ifndef __SDL_SURFACE_DATA_HPP__
-#define __SDL_SURFACE_DATA_HPP__
-
-#include <config.h>
-
-#include "surface.hpp"
-#include "texture.hpp"
-#include "main.hpp"
-#include "gameconfig.hpp"
-
-namespace SDL
-{
- class SurfaceData
- {
- private:
- const Surface &surface;
- SDL_Rect src_rects[NUM_EFFECTS];
-
- public:
- SurfaceData(const Surface &surface) :
- surface(surface)
- {
- int numerator, denominator;
- float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
- float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
- if(xfactor < yfactor)
- {
- numerator = config->screenwidth;
- denominator = SCREEN_WIDTH;
- }
- else
- {
- numerator = config->screenheight;
- denominator = SCREEN_HEIGHT;
- }
-
- src_rects[NO_EFFECT].x = surface.get_x() * numerator / denominator;
- src_rects[NO_EFFECT].y = surface.get_y() * numerator / denominator;
- src_rects[NO_EFFECT].w = surface.get_width() * numerator / denominator;
- src_rects[NO_EFFECT].h = surface.get_height() * numerator / denominator;
-
- int flipped_x = surface.get_texture()->get_texture_width() - surface.get_x() - surface.get_width();
- src_rects[HORIZONTAL_FLIP].x = flipped_x * numerator / denominator;
- src_rects[HORIZONTAL_FLIP].y = surface.get_y() * numerator / denominator;
- src_rects[HORIZONTAL_FLIP].w = surface.get_width() * numerator / denominator;
- src_rects[HORIZONTAL_FLIP].h = surface.get_height() * numerator / denominator;
-
- int flipped_y = surface.get_texture()->get_texture_height() - surface.get_y() - surface.get_height();
- src_rects[VERTICAL_FLIP].x = flipped_y * numerator / denominator;
- src_rects[VERTICAL_FLIP].y = surface.get_y() * numerator / denominator;
- src_rects[VERTICAL_FLIP].w = surface.get_width() * numerator / denominator;
- src_rects[VERTICAL_FLIP].h = surface.get_height() * numerator / denominator;
- }
-
- SDL_Rect *get_src_rect(DrawingEffect effect)
- {
- return src_rects + effect;
- }
- };
-}
-
-#endif
+++ /dev/null
-// $Id: sdl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#include <config.h>
-
-#include "sdl_texture.hpp"
-#include "color.hpp"
-#include "gameconfig.hpp"
-#include "main.hpp"
-
-#include <assert.h>
-
-#include <SDL.h>
-
-namespace
-{
-#define BILINEAR
-
-#ifdef NAIVE
- SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
- {
- if(numerator == denominator)
- {
- src->refcount++;
- return src;
- }
- else
- {
- SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
- int bpp = dst->format->BytesPerPixel;
- if(SDL_MUSTLOCK(src))
- {
- SDL_LockSurface(src);
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_LockSurface(dst);
- }
- for(int y = 0;y < dst->h;y++) {
- for(int x = 0;x < dst->w;x++) {
- Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp;
- Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
- switch(bpp) {
- case 4:
- dstpixel[3] = srcpixel[3];
- case 3:
- dstpixel[2] = srcpixel[2];
- case 2:
- dstpixel[1] = srcpixel[1];
- case 1:
- dstpixel[0] = srcpixel[0];
- }
- }
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_UnlockSurface(dst);
- }
- if(SDL_MUSTLOCK(src))
- {
- SDL_UnlockSurface(src);
- }
- if(!src->format->Amask)
- {
- if(src->flags & SDL_SRCALPHA)
- {
- SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
- }
- if(src->flags & SDL_SRCCOLORKEY)
- {
- SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
- }
- }
- return dst;
- }
- }
-#endif
-
-#ifdef BILINEAR
- void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4])
- {
- int bpp = src->format->BytesPerPixel;
- if(srcx == src->w)
- {
- srcx--;
- }
- if(srcy == src->h)
- {
- srcy--;
- }
- Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
- Uint32 mapped = 0;
- switch(bpp) {
- case 1:
- mapped = *srcpixel;
- break;
- case 2:
- mapped = *(Uint16 *)srcpixel;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- mapped |= srcpixel[0] << 16;
- mapped |= srcpixel[1] << 8;
- mapped |= srcpixel[2] << 0;
-#else
- mapped |= srcpixel[0] << 0;
- mapped |= srcpixel[1] << 8;
- mapped |= srcpixel[2] << 16;
-#endif
- break;
- case 4:
- mapped = *(Uint32 *)srcpixel;
- break;
- }
- SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]);
- }
-
- void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total)
- {
- color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
- color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
- color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
- color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total;
- }
-
- SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator)
- {
- if(numerator == denominator)
- {
- src->refcount++;
- return src;
- }
- else
- {
- SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
- int bpp = dst->format->BytesPerPixel;
- if(SDL_MUSTLOCK(src))
- {
- SDL_LockSurface(src);
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_LockSurface(dst);
- }
- for(int y = 0;y < dst->h;y++) {
- for(int x = 0;x < dst->w;x++) {
- int srcx = x * denominator / numerator;
- int srcy = y * denominator / numerator;
- Uint8 color00[4], color01[4], color10[4], color11[4];
- getpixel(src, srcx, srcy, color00);
- getpixel(src, srcx + 1, srcy, color01);
- getpixel(src, srcx, srcy + 1, color10);
- getpixel(src, srcx + 1, srcy + 1, color11);
- Uint8 color0[4], color1[4], color[4];
- int remx = x * denominator % numerator;
- merge(color0, color00, color01, remx, numerator);
- merge(color1, color10, color11, remx, numerator);
- int remy = y * denominator % numerator;
- merge(color, color0, color1, remy, numerator);
- Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
- Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]);
- switch(bpp) {
- case 1:
- *dstpixel = mapped;
- break;
- case 2:
- *(Uint16 *)dstpixel = mapped;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- dstpixel[0] = (mapped >> 16) & 0xff;
- dstpixel[1] = (mapped >> 8) & 0xff;
- dstpixel[2] = (mapped >> 0) & 0xff;
-#else
- dstpixel[0] = (mapped >> 0) & 0xff;
- dstpixel[1] = (mapped >> 8) & 0xff;
- dstpixel[2] = (mapped >> 16) & 0xff;
-#endif
- break;
- case 4:
- *(Uint32 *)dstpixel = mapped;
- break;
- }
- }
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_UnlockSurface(dst);
- }
- if(SDL_MUSTLOCK(src))
- {
- SDL_UnlockSurface(src);
- }
- if(!src->format->Amask)
- {
- if(src->flags & SDL_SRCALPHA)
- {
- SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
- }
- if(src->flags & SDL_SRCCOLORKEY)
- {
- SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
- }
- }
- return dst;
- }
- }
-#endif
-
- SDL_Surface *horz_flip(SDL_Surface *src)
- {
- SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
- int bpp = dst->format->BytesPerPixel;
- if(SDL_MUSTLOCK(src))
- {
- SDL_LockSurface(src);
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_LockSurface(dst);
- }
- for(int y = 0;y < dst->h;y++) {
- for(int x = 0;x < dst->w;x++) {
- Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
- Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp;
- switch(bpp) {
- case 4:
- dstpixel[3] = srcpixel[3];
- case 3:
- dstpixel[2] = srcpixel[2];
- case 2:
- dstpixel[1] = srcpixel[1];
- case 1:
- dstpixel[0] = srcpixel[0];
- }
- }
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_UnlockSurface(dst);
- }
- if(SDL_MUSTLOCK(src))
- {
- SDL_UnlockSurface(src);
- }
- if(!src->format->Amask)
- {
- if(src->flags & SDL_SRCALPHA)
- {
- SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
- }
- if(src->flags & SDL_SRCCOLORKEY)
- {
- SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
- }
- }
- return dst;
- }
-
- SDL_Surface *vert_flip(SDL_Surface *src)
- {
- SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
- int bpp = dst->format->BytesPerPixel;
- if(SDL_MUSTLOCK(src))
- {
- SDL_LockSurface(src);
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_LockSurface(dst);
- }
- for(int y = 0;y < dst->h;y++) {
- for(int x = 0;x < dst->w;x++) {
- Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
- Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp;
- switch(bpp) {
- case 4:
- dstpixel[3] = srcpixel[3];
- case 3:
- dstpixel[2] = srcpixel[2];
- case 2:
- dstpixel[1] = srcpixel[1];
- case 1:
- dstpixel[0] = srcpixel[0];
- }
- }
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_UnlockSurface(dst);
- }
- if(SDL_MUSTLOCK(src))
- {
- SDL_UnlockSurface(src);
- }
- if(!src->format->Amask)
- {
- if(src->flags & SDL_SRCALPHA)
- {
- SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
- }
- if(src->flags & SDL_SRCCOLORKEY)
- {
- SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
- }
- }
- return dst;
- }
-
- SDL_Surface *colorize(SDL_Surface *src, const Color &color)
- {
- // FIXME: This is really slow
- assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0);
- int red = (int) (color.red * 256);
- int green = (int) (color.green * 256);
- int blue = (int) (color.blue * 256);
- SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
- int bpp = dst->format->BytesPerPixel;
- if(SDL_MUSTLOCK(src))
- {
- SDL_LockSurface(src);
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_LockSurface(dst);
- }
- for(int y = 0;y < dst->h;y++) {
- for(int x = 0;x < dst->w;x++) {
- Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
- Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
- Uint32 mapped = 0;
- switch(bpp) {
- case 1:
- mapped = *srcpixel;
- break;
- case 2:
- mapped = *(Uint16 *)srcpixel;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- mapped |= srcpixel[0] << 16;
- mapped |= srcpixel[1] << 8;
- mapped |= srcpixel[2] << 0;
-#else
- mapped |= srcpixel[0] << 0;
- mapped |= srcpixel[1] << 8;
- mapped |= srcpixel[2] << 16;
-#endif
- break;
- case 4:
- mapped = *(Uint32 *)srcpixel;
- break;
- }
- if(src->format->Amask || !(src->flags & SDL_SRCCOLORKEY) || mapped != src->format->colorkey)
- {
- Uint8 r, g, b, a;
- SDL_GetRGBA(mapped, src->format, &r, &g, &b, &a);
- mapped = SDL_MapRGBA(dst->format, (r * red) >> 8, (g * green) >> 8, (b * blue) >> 8, a);
- }
- switch(bpp) {
- case 1:
- *dstpixel = mapped;
- break;
- case 2:
- *(Uint16 *)dstpixel = mapped;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- dstpixel[0] = (mapped >> 16) & 0xff;
- dstpixel[1] = (mapped >> 8) & 0xff;
- dstpixel[2] = (mapped >> 0) & 0xff;
-#else
- dstpixel[0] = (mapped >> 0) & 0xff;
- dstpixel[1] = (mapped >> 8) & 0xff;
- dstpixel[2] = (mapped >> 16) & 0xff;
-#endif
- break;
- case 4:
- *(Uint32 *)dstpixel = mapped;
- break;
- }
- }
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_UnlockSurface(dst);
- }
- if(SDL_MUSTLOCK(src))
- {
- SDL_UnlockSurface(src);
- }
- if(!src->format->Amask)
- {
- if(src->flags & SDL_SRCALPHA)
- {
- SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, src->format->alpha);
- }
- if(src->flags & SDL_SRCCOLORKEY)
- {
- SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, src->format->colorkey);
- }
- }
- return dst;
- }
-
- SDL_Surface *optimize(SDL_Surface *src)
- {
- if(!src->format->Amask)
- {
- return SDL_DisplayFormat(src);
- }
- else
- {
- int transparent = 0;
- int opaque = 0;
- int semitransparent = 0;
- int alphasum = 0;
- int squaredalphasum = 0;
- bool colors[(1 << 12)];
- memset(colors, 0, (1 << 12) * sizeof(bool));
-
- int bpp = src->format->BytesPerPixel;
- if(SDL_MUSTLOCK(src))
- {
- SDL_LockSurface(src);
- }
- for(int y = 0;y < src->h;y++) {
- for(int x = 0;x < src->w;x++) {
- Uint8 *pixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
- Uint32 mapped = 0;
- switch(bpp) {
- case 1:
- mapped = *pixel;
- break;
- case 2:
- mapped = *(Uint16 *)pixel;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- mapped |= pixel[0] << 16;
- mapped |= pixel[1] << 8;
- mapped |= pixel[2] << 0;
-#else
- mapped |= pixel[0] << 0;
- mapped |= pixel[1] << 8;
- mapped |= pixel[2] << 16;
-#endif
- break;
- case 4:
- mapped = *(Uint32 *)pixel;
- break;
- }
- Uint8 red, green, blue, alpha;
- SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
- if(alpha < 16)
- {
- transparent++;
- }
- else if (alpha > 240)
- {
- opaque++;
- alphasum += alpha;
- squaredalphasum += alpha * alpha;
- }
- else
- {
- semitransparent++;
- squaredalphasum += alpha * alpha;
- }
- if(alpha != 0)
- {
- colors[((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0) >> 4)] = true;
- }
- }
- }
- if(SDL_MUSTLOCK(src))
- {
- SDL_UnlockSurface(src);
- }
- int avgalpha = (opaque + semitransparent) ? alphasum / (opaque + semitransparent) : 0;
- int avgsquaredalpha = (opaque + semitransparent) ? squaredalphasum / (opaque + semitransparent) : 0;
- int alphavariance = avgsquaredalpha - avgalpha * avgalpha;
- if(semitransparent > ((transparent + opaque + semitransparent) / 8) && alphavariance > 16)
- {
- return SDL_DisplayFormatAlpha(src);
- }
- int keycolor = -1;
- for(int i = 0;i < (1 << 12);i++)
- {
- if(!colors[i])
- {
- keycolor = i;
- }
- }
- if(keycolor == -1)
- {
- return SDL_DisplayFormatAlpha(src);
- }
- SDL_Surface *dst = SDL_CreateRGBSurface(src->flags & ~(SDL_SRCALPHA), src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, 0);
- bpp = dst->format->BytesPerPixel;
- Uint32 key = SDL_MapRGB(dst->format, (((keycolor & 0xf00) >> 4) | 0xf), ((keycolor & 0xf0) | 0xf), (((keycolor & 0xf) << 4) | 0xf));
- if(SDL_MUSTLOCK(src))
- {
- SDL_LockSurface(src);
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_LockSurface(dst);
- }
- for(int y = 0;y < dst->h;y++) {
- for(int x = 0;x < dst->w;x++) {
- Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp;
- Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp;
- Uint32 mapped = 0;
- switch(bpp) {
- case 1:
- mapped = *srcpixel;
- break;
- case 2:
- mapped = *(Uint16 *)srcpixel;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- mapped |= srcpixel[0] << 16;
- mapped |= srcpixel[1] << 8;
- mapped |= srcpixel[2] << 0;
-#else
- mapped |= srcpixel[0] << 0;
- mapped |= srcpixel[1] << 8;
- mapped |= srcpixel[2] << 16;
-#endif
- break;
- case 4:
- mapped = *(Uint32 *)srcpixel;
- break;
- }
- Uint8 red, green, blue, alpha;
- SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
- if(alpha < (avgalpha / 4))
- {
- mapped = key;
- }
- else
- {
- mapped = SDL_MapRGB(dst->format, red, green, blue);
- }
- switch(bpp) {
- case 1:
- *dstpixel = mapped;
- break;
- case 2:
- *(Uint16 *)dstpixel = mapped;
- break;
- case 3:
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- dstpixel[0] = (mapped >> 16) & 0xff;
- dstpixel[1] = (mapped >> 8) & 0xff;
- dstpixel[2] = (mapped >> 0) & 0xff;
-#else
- dstpixel[0] = (mapped >> 0) & 0xff;
- dstpixel[1] = (mapped >> 8) & 0xff;
- dstpixel[2] = (mapped >> 16) & 0xff;
-#endif
- break;
- case 4:
- *(Uint32 *)dstpixel = mapped;
- break;
- }
- }
- }
- if(SDL_MUSTLOCK(dst))
- {
- SDL_UnlockSurface(dst);
- }
- if(SDL_MUSTLOCK(src))
- {
- SDL_UnlockSurface(src);
- }
- if(avgalpha < 240)
- {
- SDL_SetAlpha(dst, SDL_SRCALPHA | SDL_RLEACCEL, avgalpha);
- }
- SDL_SetColorKey(dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, key);
- SDL_Surface *convert = SDL_DisplayFormat(dst);
- SDL_FreeSurface(dst);
- return convert;
- }
- }
-}
-
-namespace SDL
-{
- Texture::Texture(SDL_Surface* image)
- {
- texture = optimize(image);
- //width = texture->w;
- //height = texture->h;
- int numerator, denominator;
- float xfactor = (float) config->screenwidth / SCREEN_WIDTH;
- float yfactor = (float) config->screenheight / SCREEN_HEIGHT;
- if(xfactor < yfactor)
- {
- numerator = config->screenwidth;
- denominator = SCREEN_WIDTH;
- }
- else
- {
- numerator = config->screenheight;
- denominator = SCREEN_HEIGHT;
- }
- cache[NO_EFFECT][Color::WHITE] = scale(texture, numerator, denominator);
- }
-
- Texture::~Texture()
- {
- SDL_FreeSurface(texture);
- }
-
- SDL_Surface *Texture::get_transform(const Color &color, DrawingEffect effect)
- {
- if(cache[NO_EFFECT][color] == 0) {
- assert(cache[NO_EFFECT][Color::WHITE]);
- cache[NO_EFFECT][color] = colorize(cache[NO_EFFECT][Color::WHITE], color);
- }
- if(cache[effect][color] == 0) {
- assert(cache[NO_EFFECT][color]);
- switch(effect) {
- case NO_EFFECT:
- break;
- case HORIZONTAL_FLIP:
- cache[HORIZONTAL_FLIP][color] = horz_flip(cache[NO_EFFECT][color]);
- break;
- case VERTICAL_FLIP:
- cache[VERTICAL_FLIP][color] = vert_flip(cache[NO_EFFECT][color]);
- break;
- default:
- return 0;
- }
- }
- return cache[effect][color];
- }
-}
+++ /dev/null
-// $Id: sdl_texture.hpp 4063 2006-07-21 21:05:23Z anmaster $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#ifndef __SDL_TEXTURE_HPP__
-#define __SDL_TEXTURE_HPP__
-
-#include <config.h>
-
-#include <SDL.h>
-
-#include "texture.hpp"
-#include "color.hpp"
-
-namespace SDL
-{
- class Texture : public ::Texture
- {
- protected:
- SDL_Surface *texture;
- //unsigned int width;
- //unsigned int height;
-
- struct ColorCache
- {
- static const int HASHED_BITS = 3;
- static const int CACHE_SIZE = 1 << (HASHED_BITS * 3);
-
- static void ref(SDL_Surface *surface)
- {
- if(surface)
- {
- surface->refcount++;
- }
- }
-
- static int hash(const Color &color)
- {
- return
- ((int) (color.red * ((1 << HASHED_BITS) - 1)) << (HASHED_BITS - 1) * 2) |
- ((int) (color.green * ((1 << HASHED_BITS) - 1)) << (HASHED_BITS - 1)) |
- ((int) (color.blue * ((1 << HASHED_BITS) - 1)) << 0);
- }
-
- SDL_Surface *data[CACHE_SIZE];
-
- ColorCache()
- {
- memset(data, 0, CACHE_SIZE * sizeof(SDL_Surface *));
- }
-
- ~ColorCache()
- {
- std::for_each(data, data + CACHE_SIZE, SDL_FreeSurface);
- }
-
- void operator = (const ColorCache &other)
- {
- std::for_each(other.data, other.data + CACHE_SIZE, ref);
- std::for_each(data, data + CACHE_SIZE, SDL_FreeSurface);
- memcpy(data, other.data, CACHE_SIZE * sizeof(SDL_Surface *));
- }
-
- SDL_Surface *&operator [] (const Color &color)
- {
- return data[hash(color)];
- }
- };
- //typedef std::map<Color, SDL_Surface *> ColorCache;
- ColorCache cache[NUM_EFFECTS];
-
- public:
- Texture(SDL_Surface* sdlsurface);
- virtual ~Texture();
-
- SDL_Surface *get_transform(const Color &color, DrawingEffect effect);
-
- SDL_Surface *get_texture() const
- {
- return texture;
- }
-
- unsigned int get_texture_width() const
- {
- return texture->w;
- }
-
- unsigned int get_texture_height() const
- {
- return texture->h;
- }
-
- unsigned int get_image_width() const
- {
- return texture->w;
- }
-
- unsigned int get_image_height() const
- {
- return texture->h;
- }
-
- /*unsigned int get_texture_width() const
- {
- return width;
- }
-
- unsigned int get_texture_height() const
- {
- return height;
- }
-
- unsigned int get_image_width() const
- {
- return width;
- }
-
- unsigned int get_image_height() const
- {
- return height;
- }*/
- };
-}
-
-#endif
#include <string>
#include <SDL.h>
#include "math/vector.hpp"
-#include "texture.hpp"
-#include "video_systems.hpp"
+#include "file_system.hpp"
+#include <unison/video/Texture.hpp>
+
+/// bitset for drawing effects
+enum DrawingEffect {
+ /** Don't apply anything */
+ NO_EFFECT,
+ /** Draw the Surface upside down */
+ VERTICAL_FLIP,
+ /** Draw the Surface from left to down */
+ HORIZONTAL_FLIP,
+ NUM_EFFECTS
+};
/**
* A rectangular image.
class Surface
{
private:
- Texture* texture;
- void *surface_data;
- int x;
- int y;
- int w;
- int h;
+ Unison::Video::TextureSection texture;
bool flipx;
public:
Surface(const std::string& file) :
- texture(texture_manager->get(file)),
- x(0), y(0), w(0), h(0),
+ texture(FileSystem::normalize(file)),
flipx(false)
{
- texture->ref();
- w = texture->get_image_width();
- h = texture->get_image_height();
- surface_data = new_surface_data(*this);
}
Surface(const std::string& file, int x, int y, int w, int h) :
- texture(texture_manager->get(file)),
- x(x), y(y), w(w), h(h),
+ texture(FileSystem::normalize(file), Unison::Video::Rect(x, y, w, h)),
flipx(false)
{
- texture->ref();
- surface_data = new_surface_data(*this);
}
Surface(const Surface& other) :
texture(other.texture),
- x(other.x), y(other.y),
- w(other.w), h(other.h),
flipx(false)
{
- texture->ref();
- surface_data = new_surface_data(*this);
}
~Surface()
{
- free_surface_data(surface_data);
- texture->unref();
}
/** flip the surface horizontally */
const Surface& operator= (const Surface& other)
{
- other.texture->ref();
- texture->unref();
texture = other.texture;
- x = other.x;
- y = other.y;
- w = other.w;
- h = other.h;
return *this;
}
- Texture *get_texture() const
+ Unison::Video::TextureSection get_texture() const
{
return texture;
}
- void *get_surface_data() const
- {
- return surface_data;
- }
-
int get_x() const
{
- return x;
+ return texture.clip_rect.pos.x;
}
int get_y() const
{
- return y;
+ return texture.clip_rect.pos.y;
}
int get_width() const
{
- return w;
+ return texture.clip_rect.size.x ? texture.clip_rect.size.x : texture.image.get_size().x;
}
int get_height() const
{
- return h;
+ return texture.clip_rect.size.y ? texture.clip_rect.size.y : texture.image.get_size().y;
}
Vector get_position() const
+++ /dev/null
-// $Id$
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#ifndef __TEXTURE_HPP__
-#define __TEXTURE_HPP__
-
-#include <config.h>
-
-#include <assert.h>
-#include <string>
-
-#include "texture_manager.hpp"
-
-/// bitset for drawing effects
-enum DrawingEffect {
- /** Don't apply anything */
- NO_EFFECT,
- /** Draw the Surface upside down */
- VERTICAL_FLIP,
- /** Draw the Surface from left to down */
- HORIZONTAL_FLIP,
- NUM_EFFECTS
-};
-
-/**
- * This class is a wrapper around a texture handle. It stores the texture width
- * and height and provides convenience functions for uploading SDL_Surfaces
- * into the texture
- */
-class Texture
-{
-protected:
- int refcount;
- std::string filename;
-
-public:
- Texture() : refcount(0), filename() {}
- virtual ~Texture() {}
-
- virtual unsigned int get_texture_width() const = 0;
- virtual unsigned int get_texture_height() const = 0;
- virtual unsigned int get_image_width() const = 0;
- virtual unsigned int get_image_height() const = 0;
-
- std::string get_filename() const
- {
- return filename;
- }
-
- void set_filename(std::string filename)
- {
- this->filename = filename;
- }
-
- void ref()
- {
- refcount++;
- }
-
- void unref()
- {
- assert(refcount > 0);
- refcount--;
- if(refcount == 0)
- release();
- }
-
-private:
- void release()
- {
- texture_manager->release(this);
- }
-};
-
-#endif
+++ /dev/null
-// $Id$
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#include <config.h>
-
-#include "texture_manager.hpp"
-
-#include <assert.h>
-#include <SDL.h>
-#include <SDL_image.h>
-#include <iostream>
-#include <sstream>
-#include <stdexcept>
-#include "physfs/physfs_sdl.hpp"
-#include "video_systems.hpp"
-#include "gl_texture.hpp"
-#include "glutil.hpp"
-#include "gameconfig.hpp"
-#include "file_system.hpp"
-#include "log.hpp"
-#include "texture.hpp"
-
-TextureManager* texture_manager = NULL;
-
-TextureManager::TextureManager()
-{
-}
-
-TextureManager::~TextureManager()
-{
- for(ImageTextures::iterator i = image_textures.begin();
- i != image_textures.end(); ++i) {
- if(i->second == NULL)
- continue;
- log_warning << "Texture '" << i->first << "' not freed" << std::endl;
- delete i->second;
- }
-}
-
-Texture*
-TextureManager::get(const std::string& _filename)
-{
- std::string filename = FileSystem::normalize(_filename);
- ImageTextures::iterator i = image_textures.find(filename);
-
- Texture* texture = NULL;
- if(i != image_textures.end())
- texture = i->second;
-
- if(texture == NULL) {
- texture = create_image_texture(filename);
- image_textures[filename] = texture;
- }
-
- return texture;
-}
-
-void
-TextureManager::release(Texture* texture)
-{
- image_textures.erase(texture->get_filename());
- delete texture;
-}
-
-#ifdef HAVE_OPENGL
-void
-TextureManager::register_texture(GL::Texture* texture)
-{
- textures.insert(texture);
-}
-
-void
-TextureManager::remove_texture(GL::Texture* texture)
-{
- textures.erase(texture);
-}
-#endif
-
-Texture*
-TextureManager::create_image_texture(const std::string& filename)
-{
- SDL_Surface* image = IMG_Load_RW(get_physfs_SDLRWops(filename), 1);
- if(image == 0) {
- std::ostringstream msg;
- msg << "Couldn't load image '" << filename << "' :" << SDL_GetError();
- throw std::runtime_error(msg.str());
- }
-
- Texture* result = 0;
- try {
- result = new_texture(image);
- result->set_filename(filename);
- } catch(...) {
- delete result;
- SDL_FreeSurface(image);
- throw;
- }
-
- SDL_FreeSurface(image);
- return result;
-}
-
-#ifdef HAVE_OPENGL
-void
-TextureManager::save_textures()
-{
- glPixelStorei(GL_PACK_ROW_LENGTH, 0);
- glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
- glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
- glPixelStorei(GL_PACK_SKIP_ROWS, 0);
- glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
- glPixelStorei(GL_PACK_ALIGNMENT, 1);
- for(Textures::iterator i = textures.begin(); i != textures.end(); ++i) {
- save_texture(*i);
- }
- for(ImageTextures::iterator i = image_textures.begin();
- i != image_textures.end(); ++i) {
- save_texture(dynamic_cast<GL::Texture *>(i->second));
- }
-}
-
-void
-TextureManager::save_texture(GL::Texture* texture)
-{
- SavedTexture saved_texture;
- saved_texture.texture = texture;
- glBindTexture(GL_TEXTURE_2D, texture->get_handle());
- glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,
- &saved_texture.width);
- glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT,
- &saved_texture.height);
- glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_BORDER,
- &saved_texture.border);
- glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
- &saved_texture.min_filter);
- glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
- &saved_texture.mag_filter);
- glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
- &saved_texture.wrap_s);
- glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
- &saved_texture.wrap_t);
-
- size_t pixelssize = saved_texture.width * saved_texture.height * 4;
- saved_texture.pixels = new char[pixelssize];
-
- glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE,
- saved_texture.pixels);
-
- saved_textures.push_back(saved_texture);
-
- glDeleteTextures(1, &(texture->get_handle()));
- texture->set_handle(0);
-
- assert_gl("retrieving texture for save");
-}
-
-void
-TextureManager::reload_textures()
-{
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
- glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
- glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
- glPixelStorei(GL_UNPACK_SKIP_IMAGES, 0);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
- for(std::vector<SavedTexture>::iterator i = saved_textures.begin();
- i != saved_textures.end(); ++i) {
- SavedTexture& saved_texture = *i;
-
- GLuint handle;
- glGenTextures(1, &handle);
- assert_gl("creating texture handle");
-
- glBindTexture(GL_TEXTURE_2D, handle);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
- saved_texture.width, saved_texture.height,
- saved_texture.border, GL_RGBA,
- GL_UNSIGNED_BYTE, saved_texture.pixels);
- delete[] saved_texture.pixels;
- assert_gl("uploading texture pixel data");
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
- saved_texture.min_filter);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
- saved_texture.mag_filter);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
- saved_texture.wrap_s);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
- saved_texture.wrap_t);
-
- assert_gl("setting texture_params");
- saved_texture.texture->set_handle(handle);
- }
-
- saved_textures.clear();
-}
-#endif
+++ /dev/null
-// $Id$
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-#ifndef __IMAGE_TEXTURE_MANAGER_HPP__
-#define __IMAGE_TEXTURE_MANAGER_HPP__
-
-#include <config.h>
-
-#include "glutil.hpp"
-#include <string>
-#include <vector>
-#include <map>
-#include <set>
-
-class Texture;
-namespace GL { class Texture; }
-
-class TextureManager
-{
-public:
- TextureManager();
- ~TextureManager();
-
- Texture* get(const std::string& filename);
-
-#ifdef HAVE_OPENGL
- void register_texture(GL::Texture* texture);
- void remove_texture(GL::Texture* texture);
-
- void save_textures();
- void reload_textures();
-#endif
-
-private:
- friend class Texture;
- void release(Texture* texture);
-
- typedef std::map<std::string, Texture*> ImageTextures;
- ImageTextures image_textures;
-
- Texture* create_image_texture(const std::string& filename);
-
-#ifdef HAVE_OPENGL
- typedef std::set<GL::Texture*> Textures;
- Textures textures;
-
- struct SavedTexture
- {
- GL::Texture* texture;
- GLint width;
- GLint height;
- char* pixels;
- GLint border;
-
- GLint min_filter;
- GLint mag_filter;
- GLint wrap_s;
- GLint wrap_t;
- };
- std::vector<SavedTexture> saved_textures;
-
- void save_texture(GL::Texture* texture);
-#endif
-};
-
-extern TextureManager* texture_manager;
-
-#endif
+++ /dev/null
-// $Id: video_systems.cpp 5063 2007-05-27 11:32:00Z matzeb $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-#include <config.h>
-
-#include "video_systems.hpp"
-#include "gameconfig.hpp"
-#include "renderer.hpp"
-#include "gl_renderer.hpp"
-#include "sdl_renderer.hpp"
-#include "lightmap.hpp"
-#include "gl_lightmap.hpp"
-#include "sdl_lightmap.hpp"
-#include "texture.hpp"
-#include "gl_texture.hpp"
-#include "sdl_texture.hpp"
-#include "gl_surface_data.hpp"
-#include "sdl_surface_data.hpp"
-
-Renderer *new_renderer()
-{
- switch(config->video)
- {
- case AUTO_VIDEO:
-#ifdef HAVE_OPENGL
- return new GL::Renderer();
-#else
- return new SDL::Renderer();
-#endif
-#ifdef HAVE_OPENGL
- case OPENGL:
- return new GL::Renderer();
-#endif
- case PURE_SDL:
- return new SDL::Renderer();
- default:
- assert(0 && "invalid video system in config");
-#ifdef HAVE_OPENGL
- return new GL::Renderer();
-#else
- return new SDL::Renderer();
-#endif
- }
-}
-
-Lightmap *new_lightmap()
-{
- switch(config->video)
- {
- case AUTO_VIDEO:
-#ifdef HAVE_OPENGL
- return new GL::Lightmap();
-#else
- return new SDL::Lightmap();
-#endif
-#ifdef HAVE_OPENGL
- case OPENGL:
- return new GL::Lightmap();
-#endif
- case PURE_SDL:
- return new SDL::Lightmap();
- default:
- assert(0 && "invalid video system in config");
-#ifdef HAVE_OPENGL
- return new GL::Lightmap();
-#else
- return new SDL::Lightmap();
-#endif
- }
-}
-
-Texture *new_texture(SDL_Surface *image)
-{
- switch(config->video)
- {
- case AUTO_VIDEO:
-#ifdef HAVE_OPENGL
- return new GL::Texture(image);
-#else
- return new SDL::Texture(image);
-#endif
-#ifdef HAVE_OPENGL
- case OPENGL:
- return new GL::Texture(image);
-#endif
- case PURE_SDL:
- return new SDL::Texture(image);
- default:
- assert(0 && "invalid video system in config");
-#ifdef HAVE_OPENGL
- return new GL::Texture(image);
-#else
- return new SDL::Texture(image);
-#endif
- }
-}
-
-void *new_surface_data(const Surface &surface)
-{
- switch(config->video)
- {
- case AUTO_VIDEO:
-#ifdef HAVE_OPENGL
- return new GL::SurfaceData(surface);
-#else
- return new SDL::SurfaceData(surface);
-#endif
-#ifdef HAVE_OPENGL
- case OPENGL:
- return new GL::SurfaceData(surface);
-#endif
- case PURE_SDL:
- return new SDL::SurfaceData(surface);
- default:
- assert(0 && "invalid video system in config");
-#ifdef HAVE_OPENGL
- return new GL::SurfaceData(surface);
-#else
- return new SDL::SurfaceData(surface);
-#endif
- }
-}
-
-void free_surface_data(void *surface_data)
-{
- delete reinterpret_cast<char *>(surface_data);
-}
-
-VideoSystem get_video_system(const std::string &video)
-{
- if(video == "auto")
- {
- return AUTO_VIDEO;
- }
-#ifdef HAVE_OPENGL
- else if(video == "opengl")
- {
- return OPENGL;
- }
-#endif
- else if(video == "sdl")
- {
- return PURE_SDL;
- }
- else
- {
- return AUTO_VIDEO;
- }
-}
-
-std::string get_video_string(VideoSystem video)
-{
- switch(video)
- {
- case AUTO_VIDEO:
- return "auto";
- case OPENGL:
- return "opengl";
- case PURE_SDL:
- return "sdl";
- default:
- assert(0 && "invalid video system in config");
- return "auto";
- }
-}
+++ /dev/null
-// $Id: video_systems.hpp 5138 2007-08-15 01:02:22Z tuxdev $
-//
-// SuperTux
-// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
-// 02111-1307, USA.
-#ifndef __RENDER_SYTSTEMS_HPP__
-#define __RENDER_SYTSTEMS_HPP__
-
-#include <config.h>
-
-#include <string>
-#include <SDL.h>
-
-class Renderer;
-class Lightmap;
-class Texture;
-class Surface;
-
-enum VideoSystem {
- AUTO_VIDEO,
- OPENGL,
- PURE_SDL,
- NUM_SYSTEMS
-};
-
-Renderer *new_renderer();
-Lightmap *new_lightmap();
-Texture *new_texture(SDL_Surface *image);
-void *new_surface_data(const Surface &surface);
-void free_surface_data(void *surface_data);
-VideoSystem get_video_system(const std::string &video);
-std::string get_video_string(VideoSystem video);
-
-#endif
#include <config.h>
#include <stddef.h>
-#include <physfs.h>
+//#include <physfs.h>
+#include <unison/vfs/FileSystem.hpp>
#include <stdexcept>
#include "world.hpp"
void
World::set_savegame_filename(const std::string& filename)
{
+ Unison::VFS::FileSystem &fs = Unison::VFS::FileSystem::get();
this->savegame_filename = filename;
// make sure the savegame directory exists
std::string dirname = FileSystem::dirname(filename);
- if(!PHYSFS_exists(dirname.c_str())) {
- if(PHYSFS_mkdir(dirname.c_str())) {
+ if(!fs.exists(dirname)) {
+ fs.mkdir(dirname);
+ /*if(PHYSFS_mkdir(dirname.c_str())) {
std::ostringstream msg;
msg << "Couldn't create directory for savegames '"
<< dirname << "': " <<PHYSFS_getLastError();
throw std::runtime_error(msg.str());
- }
+ }*/
}
- if(!PHYSFS_isDirectory(dirname.c_str())) {
+ if(!fs.is_dir(dirname)) {
std::ostringstream msg;
msg << "Savegame path '" << dirname << "' is not a directory";
throw std::runtime_error(msg.str());
// directory to see what we can find
std::string path = basedir;
- char** files = PHYSFS_enumerateFiles(path.c_str());
+ std::vector<std::string> files = Unison::VFS::FileSystem::get().ls(path);
+ for(std::vector<std::string>::iterator iter = files.begin();iter != files.end();++iter)
+ {
+ if(has_suffix(iter->c_str(), ".stl")) {
+ levels.push_back(path + *iter);
+ }
+ }
+ /*char** files = PHYSFS_enumerateFiles(path.c_str());
if(!files) {
log_warning << "Couldn't read subset dir '" << path << "'" << std::endl;
return;
levels.push_back(path + *filename);
}
}
- PHYSFS_freeList(files);
+ PHYSFS_freeList(files);*/
}
void
#include <config.h>
#include <stddef.h>
-#include <physfs.h>
+//#include <physfs.h>
#include "worldmap/level.hpp"
#include "sprite/sprite_manager.hpp"
#include "sprite/sprite.hpp"
#include "video/drawing_context.hpp"
#include "log.hpp"
#include "file_system.hpp"
+#include <unison/vfs/FileSystem.hpp>
namespace WorldMapNS
{
lisp->get("extro-script", extro_script);
- if (!PHYSFS_exists((basedir + name).c_str()))
+ if (!Unison::VFS::FileSystem::get().exists(basedir + name))
{
log_warning << "level file '" << name
<< "' does not exist and will not be added to the worldmap" << std::endl;
if (picture_cached) return picture;
picture_cached = true;
std::string fname = FileSystem::strip_extension(basedir + name)+".jpg";
- if (!PHYSFS_exists(fname.c_str())) {
+ if (!Unison::VFS::FileSystem::get().exists(fname)) {
return 0;
}
picture = new Surface(fname);
#include <stdexcept>
#include <sstream>
#include <unistd.h>
-#include <physfs.h>
+//#include <physfs.h>
#include "worldmap.hpp"