commit 22dfdd2832d46ade8aa350f8747138ea8fde2a41 Author: Henrik Rydgard Date: Sat Mar 24 23:39:19 2012 +0100 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000000..e19503015c --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +native +====== + +This is my library of stuff that I use when writing C++ programs, mostly for Android. It has some basic OpenGL utility code, JSON read/write (two libraries that should be made more similar), 2D texture atlases and drawing code, ETC1 texture loading support, basic logging, etc. The associated tools to create ZIM texture files and atlases do not yet live here but I might move them here eventually. + +If you find this useful for your own projects, drop me a line at hrydgard@gmail.com . + +I hereby release this code under the BSD license. + +Henrik Rydgård diff --git a/audio/CMakeLists.txt b/audio/CMakeLists.txt new file mode 100644 index 0000000000..3d742499b2 --- /dev/null +++ b/audio/CMakeLists.txt @@ -0,0 +1,11 @@ +set(SRCS + mixer.cpp + wav_read.cpp) + +set(SRCS ${SRCS}) + +add_library(mixer STATIC ${SRCS}) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) diff --git a/audio/mixer.cpp b/audio/mixer.cpp new file mode 100644 index 0000000000..f69233de75 --- /dev/null +++ b/audio/mixer.cpp @@ -0,0 +1,195 @@ +#include + +#include "audio/mixer.h" +#include "audio/wav_read.h" +#include "base/logging.h" +#include "ext/stb_vorbis/stb_vorbis.h" +#include "file/vfs.h" + +// TODO: +// * Vorbis streaming playback + +struct ChannelEffectState { + // Filter state +}; + +enum CLIP_TYPE { + CT_PCM16, + CT_SYNTHFX, + CT_VORBIS, + // CT_PHOENIX? +}; + +struct Clip { + int type; + + short *data; + int length; + int num_channels; // this is NOT stereo vs mono + int sample_rate; + int loop_start; + int loop_end; +}; + +// If current_clip == 0, the channel is free. + +enum PlaybackState { + PB_STOPPED = 0, + PB_PLAYING = 1, +}; + + +struct Channel { + const Clip *current_clip; + // Playback state + PlaybackState state; + int pos; + PlayParams params; + // Effect state + ChannelEffectState effect_state; +}; + +struct Mixer { + Channel *channels; + int sample_rate; + int num_channels; + int num_fixed_channels; +}; + +Mixer *mixer_create(int sample_rate, int channels, int fixed_channels) { + Mixer *mixer = new Mixer(); + memset(mixer, 0, sizeof(Mixer)); + mixer->channels = new Channel[channels]; + memset(mixer->channels, 0, sizeof(Channel) * channels); + mixer->sample_rate = sample_rate; + mixer->num_channels = channels; + mixer->num_fixed_channels = fixed_channels; + return mixer; +} + +void mixer_destroy(Mixer *mixer) { + delete [] mixer->channels; + delete mixer; +} + +static int get_free_channel(Mixer *mixer) { + int chan_with_biggest_pos = -1; + int biggest_pos = -1; + for (int i = mixer->num_fixed_channels; i < mixer->num_channels; i++) { + Channel *chan = &mixer->channels[i]; + if (!chan->current_clip) { + return i; + } + if (chan->pos > biggest_pos) { + biggest_pos = chan->pos; + chan_with_biggest_pos = i; + } + } + return chan_with_biggest_pos; +} + +Clip *clip_load(const char *filename) { + short *data; + int num_samples, sample_rate, num_channels; + + if (!strcmp(filename + strlen(filename) - 4, ".ogg")) { + // Ogg file. For now, directly decompress, no streaming support. + uint8_t *filedata; + size_t size; + filedata = VFSReadFile(filename, &size); + num_samples = stb_vorbis_decode_memory(filedata, size, &num_channels, &data); + if (num_samples <= 0) + return NULL; + sample_rate = 44100; + ILOG("read ogg %s, length %i, rate %i", filename, num_samples, sample_rate); + } else { + // Wav file. Easy peasy. + data = wav_read(filename, &num_samples, &sample_rate, &num_channels); + if (!data) { + return NULL; + } + } + + Clip *clip = new Clip(); + clip->type = CT_PCM16; + clip->data = data; + clip->length = num_samples; + clip->num_channels = num_channels; + clip->sample_rate = sample_rate; + clip->loop_start = 0; + clip->loop_end = 0; + return clip; +} + +void clip_destroy(Clip *clip) { + if (clip) { + free(clip->data); + delete clip; + } else { + ELOG("Can't destroy zero clip"); + } +} + +int clip_length(const Clip *clip) { + return clip->length; +} + +void clip_set_loop(Clip *clip, int start, int end) { + clip->loop_start = start; + clip->loop_end = end; +} + +PlayParams *mixer_play_clip(Mixer *mixer, const Clip *clip, int channel) { + if (channel == -1) { + channel = get_free_channel(mixer); + } + + Channel *chan = &mixer->channels[channel]; + // Think about this order and make sure it's thread"safe" (not perfect but should not cause crashes). + chan->pos = 0; + chan->current_clip = clip; + chan->state = PB_PLAYING; + PlayParams *params = &chan->params; + params->volume = 128; + params->pan = 128; + return params; +} + +void mixer_mix(Mixer *mixer, short *buffer, int num_samples) { + // Clear the buffer. + memset(buffer, 0, num_samples * sizeof(short) * 2); + for (int i = 0; i < mixer->num_channels; i++) { + Channel *chan = &mixer->channels[i]; + if (chan->state == PB_PLAYING) { + const Clip *clip = chan->current_clip; + if (clip->type == CT_PCM16) { + // For now, only allow mono PCM + CHECK(clip->num_channels == 1); + if (true || chan->params.delta == 0) { + // Fast playback of non pitched clips + int cnt = num_samples; + if (clip->length - chan->pos < cnt) { + cnt = clip->length - chan->pos; + } + // TODO: Take pan into account. + int left_volume = chan->params.volume; + int right_volume = chan->params.volume; + // TODO: NEONize. Can also make special loops for left_volume == right_volume etc. + for (int s = 0; s < cnt; s++) { + int cdata = clip->data[chan->pos]; + buffer[s * 2 ] += cdata * left_volume >> 8; + buffer[s * 2 + 1] += cdata * right_volume >> 8; + chan->pos++; + } + if (chan->pos >= clip->length) { + chan->state = PB_STOPPED; + chan->current_clip = 0; + break; + } + } + } else if (clip->type == CT_VORBIS) { + // For music + } + } + } +} diff --git a/audio/mixer.h b/audio/mixer.h new file mode 100644 index 0000000000..685b901948 --- /dev/null +++ b/audio/mixer.h @@ -0,0 +1,41 @@ +#include "base/basictypes.h" + +// Simple mixer intended for sound effects for games. +// Intended both for fire and forget sfx (auto channels) and for +// realtime-modifiable sounds like pitched engine noises (fixed channels). + +struct Mixer; +struct Clip; +struct Channel; + +// This struct is public for easy manipulation of running channels. +struct PlayParams { + uint8_t volume; // 0-255 + uint8_t pan; // 0-255, 127 is dead center. + int32_t delta; +}; + +// Mixer +// ========================== +// For now, channels is required to be 2. +Mixer *mixer_create(int sample_rate, int channels, int fixed_channels); +void mixer_destroy(Mixer *mixer); + +// Buffer must be r/w, for efficient mixing +// TODO: Use local buffer instead. +void mixer_mix(Mixer *mixer, short *buffer, int num_samples); + +// Clip +// ========================== +Clip *clip_load(const char *filename); +void clip_destroy(Clip *clip); + +int clip_length(); +void clip_set_loop(int start, int end); + + +// The returned PlayState pointer can be used to set the playback parameters, +// but must not be kept around unless you are using a fixed channel. +// Channel must be either -1 for auto assignment to a free channel, or less +// than the number of fixed channels that the mixer was created with. +PlayParams *mixer_play_clip(Mixer *mixer, const Clip *clip, int channel); diff --git a/audio/wav_read.cpp b/audio/wav_read.cpp new file mode 100644 index 0000000000..0d7c72c9a1 --- /dev/null +++ b/audio/wav_read.cpp @@ -0,0 +1,73 @@ +#include "base/basictypes.h" +#include "base/logging.h" +#include "audio/wav_read.h" +#include "file/chunk_file.h" + +short *wav_read(const char *filename, + int *num_samples, int *sample_rate, + int *num_channels) { + ChunkFile cf(filename, true); + if (cf.failed()) { + WLOG("ERROR: Wave file %s could not be opened", filename); + return 0; + } + + short *data = 0; + int samplesPerSec, avgBytesPerSec,wBlockAlign,wBytesPerSample; + if (cf.descend('RIFF')) { + cf.readInt(); //get past 'WAVE' + if (cf.descend('fmt ')) //enter the format chunk + { + int temp; + temp = cf.readInt(); + int format = temp & 0xFFFF; + if (format != 1) { + cf.ascend(); + cf.ascend(); + ELOG("Error - bad format"); + return NULL; + } + *num_channels = temp >> 16; + samplesPerSec = cf.readInt(); + avgBytesPerSec = cf.readInt(); + + temp = cf.readInt(); + wBlockAlign = temp & 0xFFFF; + wBytesPerSample = temp >> 16; + cf.ascend(); + // ILOG("got fmt data: %i", samplesPerSec); + } else { + ELOG("Error - no format chunk in wav"); + cf.ascend(); + return NULL; + } + + if (cf.descend('data')) //enter the data chunk + { + int numBytes = cf.getCurrentChunkSize(); + int numSamples = numBytes / wBlockAlign; + data = (short *)malloc(sizeof(short) * numSamples * *num_channels); + *num_samples = numSamples; + if (wBlockAlign == 2 && *num_channels == 1) { + cf.readData((uint8*)data,numBytes); + } else { + ELOG("Error - bad blockalign or channels"); + free(data); + return NULL; + } + // ILOG("got wave data: %i", numSamples); + cf.ascend(); + } else { + ELOG("Error - no data chunk in wav"); + cf.ascend(); + return NULL; + } + cf.ascend(); + } else { + ELOG("Could not descend into RIFF file"); + return NULL; + } + *sample_rate = samplesPerSec; + ILOG("read wav %s, length %i, rate %i", filename, *num_samples, *sample_rate); + return data; +} diff --git a/audio/wav_read.h b/audio/wav_read.h new file mode 100644 index 0000000000..a3df5f616d --- /dev/null +++ b/audio/wav_read.h @@ -0,0 +1,7 @@ +#include "base/basictypes.h" + +// Allocates a buffer that should be freed using free(). +short *wav_read(const char *filename, + int *num_samples, int *sample_rate, + int *num_channels); +// TODO: Non-allocating version. diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt new file mode 100644 index 0000000000..060fd25425 --- /dev/null +++ b/base/CMakeLists.txt @@ -0,0 +1,20 @@ +set(SRCS + LAME.cpp + LAMEApp.cpp + LAMEString.cpp + colorutil.cpp + error_context.cpp) + +set(SRCS ${SRCS}) + +add_library(base STATIC ${SRCS}) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) + +add_library(timeutil STATIC timeutil.cpp) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) diff --git a/base/LAME.cpp b/base/LAME.cpp new file mode 100644 index 0000000000..1e3636fd43 --- /dev/null +++ b/base/LAME.cpp @@ -0,0 +1,7 @@ +#include "LAME.h" +#include "LAMEString.h" + +String getUserName() +{ + return String("user"); +} diff --git a/base/LAME.h b/base/LAME.h new file mode 100644 index 0000000000..892ae6aae6 --- /dev/null +++ b/base/LAME.h @@ -0,0 +1,27 @@ +#ifndef __LAME_H__ +#define __LAME_H__ + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/color.h" + + +#ifndef PI +#define PI 3.141592653589793f +#endif + +template +inline T MIN(T t1, T t2) { + return t1 +inline T MAX(T t1, T t2) { + return t1>t2 ? t1 : t2; +} + +template +inline T ABS(T &t) { + if (t<0) return -t; else return t; +} + +#endif //__LAME_H__ diff --git a/base/LAMEApp.cpp b/base/LAMEApp.cpp new file mode 100644 index 0000000000..3c9240cbc2 --- /dev/null +++ b/base/LAMEApp.cpp @@ -0,0 +1,48 @@ +#ifndef _WIN32 +#include +#endif + +#include "LAMEApp.h" + +LAMEApp::LAMEApp() { + penDown=false; + wannaQuit=false; + active=0; + + TCHAR tmp[256]; +#ifdef _WIN32 + hWnd=0; + GetModuleFileName(0,tmp,255); +#else + char *discard = getcwd(tmp, 256); + discard++; +#endif + + path=tmp; + path=path.getPath()+String(TEXT("/")); +} + +LAMEApp::~LAMEApp() { +} + +bool LAMEApp::init(int width, int height) { + this->width = width; + this->height = height; + return true; +} + +int LAMEApp::run() { + return -1; +} + +void LAMEApp::close() { + wannaQuit = true; +} + +void LAMEApp::startTimer(int ID, int interval) { +// SetTimer(hWnd, ID, interval, 0); +} + +void LAMEApp::stopTimer(int ID) { +// KillTimer(hWnd, ID); +} diff --git a/base/LAMEApp.h b/base/LAMEApp.h new file mode 100644 index 0000000000..77310588cf --- /dev/null +++ b/base/LAMEApp.h @@ -0,0 +1,84 @@ +#pragma once + +//LAME: the Lean And MEan Extensions. + +// #include "gfx/draw_buffer.h" +#include "LAMEString.h" + +class LAMEApp { + protected: + bool wannaQuit; + bool active; + int width; + int height; +#ifndef WINCE + public: +#endif + bool penDown; + protected: + String path; + public: + LAMEApp(); + virtual ~LAMEApp(); + + bool init(int width, int height); + virtual int run(); + void close(); + + void startTimer(int ID, int interval); + void stopTimer(int ID); + + void refresh(); + + virtual void onLoop() { } + + virtual void onButtonDown(int b) {} + virtual void onButtonUp(int b) {} + + virtual void onPenDown(int x, int y) { } + virtual void onPenMove(int x, int y, bool pressed) { } + virtual void onPenUp(int x, int y) { } + + virtual void onKeyDown(int key) { } + virtual void onKeyChar(int key) { } + virtual void onKeyUp(int key) { } + + virtual void onLostFocus() { } + virtual void onGotFocus() { } + + virtual void onCreate() { } + virtual void onDestroy() { } + virtual void onTimer(int timerID) { } + + virtual void draw() { } + + int getWidth() const {return width;} + int getHeight() const { return height;} + String getPath() const { + return path; + } +}; + +inline String getRelativePath(String temp) +{ +#if defined(ANDROID) || defined(SDL) + return temp; +#else + #error TODO: check this + if (!theApp) return temp; +#ifdef WINCE + if (temp[0] != TCHAR('\\')) //if it's a relative path (assume to the program location), then let's expand it to a full path + if (temp.find(theApp->getPath(),0)==-1) + { + return theApp->getPath() + temp; + } +#else + if (temp[2] != TCHAR('\\')) //if it's a relative path (assume to the program location), then let's expand it to a full path + if (temp.find(theApp->getPath(),0)==-1) + { + return theApp->getPath() + temp; + } +#endif + return temp; +#endif +} diff --git a/base/LAMEString.cpp b/base/LAMEString.cpp new file mode 100644 index 0000000000..a5a601a870 --- /dev/null +++ b/base/LAMEString.cpp @@ -0,0 +1,411 @@ +#include +#include +#include +#include "LAMEString.h" + +#define assert(a) + +const int MAX_LENGTH = 1024; // largest size string for input + +#define _tcslen strlen +#define _tcscpy strcpy +#define _tcscmp strcmp +#define _tcsncmp strncmp + + +String::String() +{ + Capacity = 1; + CString = new TCHAR[Capacity]; + CString[0] = '\0'; // make c-style string zero length +} + +String::String(const TCHAR * s) +{ + Capacity = (int)_tcslen(s) + 1; + CString = new TCHAR[Capacity]; + _tcscpy(CString,s); +} + +String::String(const String & str) +{ + Capacity = str.length() + 1; + CString = new TCHAR[Capacity]; + _tcscpy(CString,str.CString); +} + +String::~String() +{ + delete [] CString; // free memory +} + +const String & String::operator = (const String & rhs) +{ + if (this != &rhs) { // check aliasing + if (Capacity < rhs.length() + 1) { // more memory needed? + delete[] CString; // delete old string + Capacity = rhs.length() + 1; // add 1 for '\0' + CString = new TCHAR[Capacity]; + } + _tcscpy(CString,rhs.CString); + } + return *this; +} + + +const String & String::operator = (const TCHAR * s) +{ + int len = 0; // length of newly constructed string + assert(s != 0); // make sure s non-NULL + len = (int)_tcslen(s); // # of characters in string + + // free old string if necessary + + if (Capacity < len + 1) + { + delete[] CString; // delete old string + Capacity = len + 1; // add 1 for '\0' + CString = new TCHAR[Capacity]; + } + _tcscpy(CString,s); + return *this; +} + +const String & String::operator = (TCHAR ch) +{ + if (Capacity < 2) + { + delete [] CString; + Capacity = 2; + CString = new TCHAR[Capacity]; + } + CString[0] = ch; // make string one character long + CString[1] = '\0'; + return *this; +} + +int String::length() const { + int myLength = 0; + while (CString[myLength] != '\0') + myLength++; + return myLength; +} + +TCHAR * String::getPointer() const +{ + return CString; +} + +TCHAR & String::operator [] (int k) +{ + if (k < 0 || (int)_tcslen(CString) <= k) + { + //cerr << "index out of range: " << k << " string: " << CString << endl; + assert(0 <= k && k < _tcslen(CString)); + } + return CString[k]; +} + + +const String & String::operator += (const String & str) +{ + String copystring(str); // copy to avoid aliasing problems + + int newLength = length() + str.length(); // self + added string + int lastLocation = length(); // index of '\0' + + // check to see if local buffer not big enough + if (newLength >= Capacity) + { + Capacity = newLength + 1; + TCHAR * newBuffer = new TCHAR[Capacity]; + _tcscpy(newBuffer,CString); // copy into new buffer + delete [] CString; // delete old string + CString = newBuffer; + } + + // now catenate str (copystring) to end of CString + _tcscpy(CString+lastLocation,copystring.getPointer() ); + + return *this; +} + +const String & String::operator += (const TCHAR * s) +{ + int newLength = length() + (int)_tcslen(s); // self + added string + int lastLocation = length(); // index of '\0' + + // check to see if local buffer not big enough + if (newLength >= Capacity) + { + Capacity = newLength + 1; + TCHAR * newBuffer = new TCHAR[Capacity]; + _tcscpy(newBuffer,CString); // copy into new buffer + delete [] CString; // delete old string + CString = newBuffer; + } + + // now catenate s to end of CString + _tcscpy(CString+lastLocation,s); + + return *this; +} + +const String & String::operator += ( TCHAR ch ) +{ + String temp; // make string equivalent of ch + temp = ch; + *this += temp; + return *this; +} + +String operator + (const String & lhs, const String & rhs) +{ + String result(lhs); // copies lhs to result + result += rhs; // catenate rhs + return result; // returns a copy of result +} + +String operator + ( TCHAR ch, const String & str ) +{ + String result; // make string equivalent of ch + result = ch; + result += str; + return result; +} + +String operator + ( const String & str, TCHAR ch ) +{ + String result(str); + result += ch; + return result; +} + + +String String::subString(int pos, int len) const +{ + String result(*this); // make sure enough space allocated + + if(pos < 0) // start at front when pos < 0 + { + pos = 0; + } + + if(pos >= (int)_tcslen(CString)) + { + result = TEXT(""); // empty string + return result; + } + + int lastIndex = pos + len - 1; // last char's index (to copy) + if(lastIndex >= (int)_tcslen(CString)) { // off end of string? + lastIndex = (int)_tcslen(CString)-1; + } + + int j,k; + for(j=0,k=pos; k <= lastIndex; j++,k++) { + result.CString[j] = CString[k]; + } + result.CString[j] = '\0'; // properly terminate C-string + return result; +} + +int String::find(const String & str, int startpos) const +{ + int len = str.length(); + int lastIndex = length() - len; + int k; + for (k=startpos; k <= lastIndex; k++) { + if (_tcsncmp(CString + k,str.getPointer(),len) == 0) return k; + } + return -1; +} + + +int String::find(TCHAR ch, int startpos) const +{ + int k; + for(k=startpos; k < (int)_tcslen(CString); k++) { + if (CString[k] == ch) { + return k; + } + } + return -1; +} + +int String::findLast(TCHAR ch, int count) +{ + for (int k=length()-1; k; k--) + { + if (CString[k] == ch) + { + if (count==0) + return k; + count--; + } + } + return -1; +} + +int String::parseIntoWords(String *words, int maxWords) +{ + int numWords=0; + String currentWord; + for (int i=0; i0) + { + words[numWords] = currentWord; + currentWord=TEXT(""); + numWords++; + if (numWords == maxWords) + return numWords; + } + } + else + { + currentWord += c; + } + } + return numWords; +} + +void String::toLower() +{ + for (int i=0; i ( const String & lhs, const String & rhs ) +{ + return rhs < lhs; +} + +bool operator >= ( const String & lhs, const String & rhs ) +{ + return rhs <= lhs; +} +/* +int String::convertToInt() const +{ + return(_ttoi(CString)); +}*/ + + +void String::reverseString() +{ + int n = 0, i = 0; + TCHAR *ny = new TCHAR[length()+1]; + + for (i=length()-1; i >= 0; i--, n++) { + ny[i] = CString[n]; + } + + ny[length()]='\0'; + + _tcscpy(CString, ny); + + delete [] ny; +} + + + + +String String::getPath() +{ + int p=find(String(TEXT("\\")),0); + int lastp=-1; + while (p!=-1) { + lastp=p; + p=find(String(TEXT("\\")),p+1); + } + if (lastp!=-1) { + return subString(0,lastp); + } else { + return String(TEXT("")); + } +} + +String String::getFName() +{ + int p=find(String(TEXT("\\")),0); + int lastp=-1; + while (p!=-1) + { + lastp=p; + p=find(String(TEXT("\\")),p+1); + } + if (lastp!=-1) + { + return subString(lastp+1,100); + } + else + { + return String(TEXT("")); + } +} + + +void String::toUnicode(wchar_t *dest) +{ + for (int i=0; i255?' ':i; + } + }; + + int newLength = wideLength(src); + delete [] CString; + Capacity = newLength + 1; + CString = new TCHAR[Capacity]; + + int i; + for (i=0; i +#include + +#include "base/basictypes.h" + +#ifdef UNICODE +typedef wchar_t TCHAR; +#else +typedef char TCHAR; +#endif + +class String +{ +public: + // constructors/destructor + String(); // construct empty string "" + explicit String(const char * s); // construct from string literal + String(const String & str); // copy constructor + ~String(); // destructor + + + // accessors + + int find(const String & str, int startpos=0) const; // index of first occurrence of str + int find(TCHAR ch, int startpos=0) const; // index of first occurrence of ch + int findLast(TCHAR ch,int count=0); + String subString(int pos, int len) const; // substring of len chars + + + int length() const; // number of chars + + // starting at pos + TCHAR * getPointer( ) const; // explicit conversion to char * + const TCHAR *c_str() const { + return getPointer(); + } + + // assignment + const String & operator = ( const String & str ); // assign str + const String & operator = ( const TCHAR * s ); // assign s + const String & operator = ( TCHAR ch ); // assign ch + + //int operator == (String &other); + //int operator != (String &other) {return !(*this == other);} + // indexing + TCHAR & operator [] ( int k ); // range-checked indexing + + // modifiers + const String & operator += ( const String & str ); // append str + const String & operator += ( const TCHAR * s); // append s + const String & operator += ( TCHAR ch ); // append char + + int parseIntoWords(String *words, int maxWords); + int convertToInt() const; + /* + static String fromInt(int i) { + TCHAR temp[15]; + _itot_s(i,temp,15,10); + return String(temp); + }*/ + void reverseString(); + void toUnicode(wchar_t *dest); + void fromUnicode(wchar_t *src); + String getPath(); + String getFName(); + + void toUpper(); + void toLower(); + +private: + int Capacity; // capacity of string + TCHAR * CString; // storage for characters +}; + +// The following free (non-member) functions operate on strings +// +// I/O functions +/* +ostream & operator << ( ostream & os, const String & str ); +istream & operator >> ( istream & is, String & str ); +istream & getline( istream & is, String & str ); +*/ +// comparison operators: + +bool operator == ( const String & lhs, const String & rhs ); +bool operator != ( const String & lhs, const String & rhs ); +bool operator < ( const String & lhs, const String & rhs ); +bool operator <= ( const String & lhs, const String & rhs ); +bool operator > ( const String & lhs, const String & rhs ); +bool operator >= ( const String & lhs, const String & rhs ); + +// concatenation operator + + +String operator + ( const String & lhs, const String & rhs ); +String operator + ( TCHAR ch, const String & str ); +String operator + ( const String & str, TCHAR ch ); + diff --git a/base/basictypes.h b/base/basictypes.h new file mode 100644 index 0000000000..c6da99928c --- /dev/null +++ b/base/basictypes.h @@ -0,0 +1,92 @@ +#ifndef _BASE_BASICTYPES +#define _BASE_BASICTYPES + +#include +#include // for byte swapping + +#ifdef _WIN32 +#pragma warning(disable:4244) +#pragma warning(disable:4996) +#pragma warning(disable:4305) // truncation from double to float +#endif + +#define DISALLOW_COPY_AND_ASSIGN(t) \ + private: \ + t(const t &other); \ + void operator =(const t &other); + + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef int8_t sint8; +typedef int16_t sint16; +typedef int32_t sint32; +typedef int64_t sint64; +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +#ifdef _WIN32 + +#include + +#define ALIGNED16(x) __declspec(align(16)) x +#define ALIGNED32(x) __declspec(align(32)) x +#define ALIGNED64(x) __declspec(align(64)) x +#define ALIGNED16_DECL(x) __declspec(align(16)) x +#define ALIGNED64_DECL(x) __declspec(align(64)) x + +#else + +#define ALIGNED16(x) __attribute__((aligned(16))) x +#define ALIGNED32(x) __attribute__((aligned(32))) x +#define ALIGNED64(x) __attribute__((aligned(64))) x +#define ALIGNED16_DECL(x) __attribute__((aligned(16))) x +#define ALIGNED64_DECL(x) __attribute__((aligned(64))) x + +#endif // _WIN32 + +#ifndef TEXT +#define TEXT(x) x +#endif + +// Byteswapping + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +inline uint8 swap8(uint8 _data) {return _data;} + +#ifdef _WIN32 +inline uint16 swap16(uint16 _data) {return _byteswap_ushort(_data);} +inline uint32 swap32(uint32 _data) {return _byteswap_ulong (_data);} +inline uint64 swap64(uint64 _data) {return _byteswap_uint64(_data);} +#elif __linux__ +#include +#undef swap16 +#undef swap32 +#undef swap64 +inline uint16 swap16(uint16 _data) {return bswap_16(_data);} +inline uint32 swap32(uint32 _data) {return bswap_32(_data);} +inline uint64 swap64(uint64 _data) {return bswap_64(_data);} +#else +// Slow generic implementation. +inline uint16 swap16(uint16 data) {return (data >> 8) | (data << 8);} +inline uint32 swap32(uint32 data) {return (swap16(data) << 16) | swap16(data >> 16);} +inline uint64 swap64(uint64 data) {return ((uint64)swap32(data) << 32) | swap32(data >> 32);} +#endif + +inline uint16 swap16(const uint8* _pData) {return swap16(*(const uint16*)_pData);} +inline uint32 swap32(const uint8* _pData) {return swap32(*(const uint32*)_pData);} +inline uint64 swap64(const uint8* _pData) {return swap64(*(const uint64*)_pData);} + +// Thread local storage +#ifdef _WIN32 +#define __THREAD __declspec( thread ) +#else +#define __THREAD __thread +#endif + +#endif // _BASE_BASICTYPES diff --git a/base/color.h b/base/color.h new file mode 100644 index 0000000000..5042756936 --- /dev/null +++ b/base/color.h @@ -0,0 +1,43 @@ +#pragma once + +// #define COLOR16 + +#ifdef COLOR16 +typedef unsigned short Color; +#else +typedef unsigned int Color; +#endif + + +//have to use a define to ensure constant folding.. with an inline I don't get that, sucks +#ifdef COLOR16 +#error +#define COLOR(i) (short16)(((i&0xF80000)>>8) | ((i&0xFC00)>>5) | ((i&0xF8)>>3)) +inline Color darkenColor(Color color) { + return (color>>1)&0x7BEF; +} +inline Color whitenColor(Color color) { + return ((color>>1)&0x7BEF)+0x7BEF; +} +//single multiplication 16-bit color alpha blending... pretty cool huh? +inline Color colorInterpol(Color x, Color y, int n) +{ + uint32 c1 = (x|(x<<16))&0x7E0F81F; + uint32 c2 = (y|(y<<16))&0x7E0F81F; + uint32 c = (c1 + ((c2 - c1)*n >> 5)) & 0x7E0F81F; + return c | (c >> 16); +} + +#else +#define COLOR(i) (((i << 16) & 0xFF0000) | (i & 0xFF00) | ((i >> 16) & 0xFF) | 0xFF000000) +inline Color darkenColor(Color color) { + return (color & 0xFF000000) | ((color >> 1)&0x7F7F7F); +} +inline Color whitenColor(Color color) { + return ((color & 0xFF000000) | ((color >> 1)&0x7F7F7F)) + 0x7F7F7F; +} +inline Color colorInterpol(Color x, Color y, int n) { + // TODO + return x; +} +#endif \ No newline at end of file diff --git a/base/colorutil.cpp b/base/colorutil.cpp new file mode 100644 index 0000000000..d17310d33f --- /dev/null +++ b/base/colorutil.cpp @@ -0,0 +1,86 @@ +/* hsv2rgb.c +* Convert Hue Saturation Value to Red Green Blue +* +* P.J. 08-Aug-98 +* +* Reference: +* D. F. Rogers +* Procedural Elements for Computer Graphics +* McGraw Hill 1985 +*/ + +#include "base/colorutil.h" + +uint32_t whiteAlpha(float alpha) { + if (alpha < 0.0f) alpha = 0.0f; + if (alpha > 1.0f) alpha = 1.0f; + uint32_t color = (int)(alpha*255)<<24; + color |= 0xFFFFFF; + return color; +} + +uint32_t blackAlpha(float alpha) { + if (alpha < 0.0f) alpha = 0.0f; + if (alpha > 1.0f) alpha = 1.0f; + return (int)(alpha*255)<<24; +} + +uint32_t rgba(float r, float g, float b, float alpha) { + uint32_t color = (int)(alpha*255)<<24; + color |= (int)(b*255)<<16; + color |= (int)(g*255)<<8; + color |= (int)(r*255); + return color; +} + +uint32_t rgba_clamp(float r, float g, float b, float a) { + if (r > 1.0f) r = 1.0f; + if (g > 1.0f) g = 1.0f; + if (b > 1.0f) b = 1.0f; + if (a > 1.0f) a = 1.0f; + + if (r < 0.0f) r = 0.0f; + if (g < 0.0f) g = 0.0f; + if (b < 0.0f) b = 0.0f; + if (a < 0.0f) a = 0.0f; + + return rgba(r,g,b,a); +} + +uint32_t hsva(float H, float S, float V, float alpha) { + /* + * Purpose: + * Convert HSV values to RGB values + * All values are in the range [0.0 .. 1.0] + */ + float F, M, N, K; + int I; + float r, g, b; + if ( S == 0.0 ) { + // Achromatic case, set level of grey + return rgba(V, V, V, alpha); + } else { + /* + * Determine levels of primary colours. + */ + if (H >= 1.0) { + H = 0.0; + } else { + H = H * 6; + } + I = (int) H; /* should be in the range 0..5 */ + F = H - I; /* fractional part */ + + M = V * (1 - S); + N = V * (1 - S * F); + K = V * (1 - S * (1 - F)); + + if (I == 0) { r = V; g = K; b = M; } + if (I == 1) { r = N; g = V; b = M; } + if (I == 2) { r = M; g = V; b = K; } + if (I == 3) { r = M; g = N; b = V; } + if (I == 4) { r = K; g = M; b = V; } + if (I == 5) { r = V; g = M; b = N; } + return rgba(r, g, b, alpha); + } +} diff --git a/base/colorutil.h b/base/colorutil.h new file mode 100644 index 0000000000..005b4190bd --- /dev/null +++ b/base/colorutil.h @@ -0,0 +1,9 @@ +#pragma once + +#include "base/basictypes.h" + +uint32_t whiteAlpha(float alpha); +uint32_t blackAlpha(float alpha); +uint32_t rgba(float r, float g, float b, float alpha); +uint32_t rgba_clamp(float r, float g, float b, float alpha); +uint32_t hsva(float h, float s, float v, float alpha); \ No newline at end of file diff --git a/base/error_context.cpp b/base/error_context.cpp new file mode 100644 index 0000000000..0dfc3e5961 --- /dev/null +++ b/base/error_context.cpp @@ -0,0 +1,39 @@ +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/error_context.h" +#include + +#ifndef _WIN32 +#undef __THREAD +#define __THREAD +#endif + +__THREAD std::vector *_error_context_name; +__THREAD std::vector *_error_context_data; + +_ErrorContext::_ErrorContext(const char *name, const char *data) { + if (!_error_context_name) { + _error_context_name = new std::vector(); + _error_context_data = new std::vector(); + _error_context_name->reserve(16); + _error_context_data->reserve(16); + } + _error_context_name->push_back(name); + _error_context_data->push_back(data); +} + +_ErrorContext::~_ErrorContext() { + _error_context_name->pop_back(); + _error_context_data->pop_back(); +} + +void _ErrorContext::Log(const char *message) { + ILOG("EC: %s", message); + for (size_t i = 0; i < _error_context_name->size(); i++) { + if ((*_error_context_data)[i] != 0) { + ILOG("EC: %s: %s", (*_error_context_name)[i], (*_error_context_data)[i]); + } else { + ILOG("EC: %s: %s", (*_error_context_name)[i], (*_error_context_data)[i]); + } + } +} diff --git a/base/error_context.h b/base/error_context.h new file mode 100644 index 0000000000..228c714065 --- /dev/null +++ b/base/error_context.h @@ -0,0 +1,18 @@ +#pragma once + +// do not inherit + +// TODO: Have the crash handler investigate the error context. +// Must only be constructed on the stack - DO NOT put these on the heap. +class _ErrorContext +{ +public: + _ErrorContext(const char *name, const char *data = 0); + ~_ErrorContext(); + + // Logs the current context stack. + static void Log(const char *message); +}; + +#define ErrorContext(...) _ErrorContext __ec(__VA_ARGS__) +#define LogErrorContext(msg) _ErrorContext::Log(msg) \ No newline at end of file diff --git a/base/logging.h b/base/logging.h new file mode 100644 index 0000000000..57924d366b --- /dev/null +++ b/base/logging.h @@ -0,0 +1,59 @@ +#pragma once + +#ifdef _MSC_VER +#pragma warning (disable:4996) //strcpy may be dangerous +#endif + +// Logging +#ifdef _WIN32 + +inline void Crash() { __asm { int 3 }; } + +#else + +#ifdef ANDROID + +inline void Crash() { + char *p = (char *)1337; + *p = 1; +} +#else + +inline void Crash() { + asm("int $0x3"); +} +#endif + +#endif + +#ifdef ANDROID + +#include + +#ifndef APP_NAME +#define APP_NAME "Turboviking" +#endif + +#define ILOG(...) __android_log_print(ANDROID_LOG_INFO, APP_NAME, __VA_ARGS__); +#define WLOG(...) __android_log_print(ANDROID_LOG_WARN, APP_NAME, __VA_ARGS__); +#define ELOG(...) __android_log_print(ANDROID_LOG_ERROR, APP_NAME, __VA_ARGS__); +#define FLOG(...) { __android_log_print(ANDROID_LOG_ERROR, APP_NAME, __VA_ARGS__); Crash(); } + +#define MessageBox(a, b, c, d) __android_log_print(ANDROID_LOG_INFO, APP_NAME, "%s %s", (b), (c)); + +#else + +#include + +#define ILOG(...) {printf("I: %s:%i: ", __FILE__, __LINE__); printf("I: " __VA_ARGS__); printf("\n");} +#define WLOG(...) {printf("W: %s:%i: ", __FILE__, __LINE__); printf("W: " __VA_ARGS__); printf("\n");} +#define ELOG(...) {printf("E: %s:%i: ", __FILE__, __LINE__); printf("E: " __VA_ARGS__); printf("\n");} +#define FLOG(...) {printf("F: %s:%i: ", __FILE__, __LINE__); printf("F: " __VA_ARGS__); printf("\n"); Crash();} + +#ifndef MessageBox +#define MessageBox(a, b, c, d) printf("MSG: %s %s\n", (b), (c)); +#endif + +#endif + +#define CHECK(a) {if (!(a)) {FLOG("CHECK failed");}} diff --git a/base/scoped_ptr.h b/base/scoped_ptr.h new file mode 100644 index 0000000000..1d04d5e36e --- /dev/null +++ b/base/scoped_ptr.h @@ -0,0 +1,28 @@ +#pragma once + +template +class scoped_ptr { + public: + scoped_ptr() : ptr_(0) {} + scoped_ptr(T *p) : ptr_(p) {} + ~scoped_ptr() { + delete ptr_; + } + void reset(T *p) { + delete ptr_; + ptr_ = p; + } + T *release() { + T *p = ptr_; + ptr_ = 0; + return p; + } + T *operator->() { return ptr_; } + const T *operator->() const { return ptr_; } + + private: + // don't copy that floppy + scoped_ptr(const scoped_ptr &other); + void operator=(const scoped_ptr &other); + T *ptr_; +}; diff --git a/base/stats.h b/base/stats.h new file mode 100644 index 0000000000..5b45e7da2e --- /dev/null +++ b/base/stats.h @@ -0,0 +1,17 @@ +#pragma once + + +#define STATS_ENABLE + +#ifdef STATS_ENABLE + +void IncrementStat(const char *name); + +#define INCSTAT(name) IncrementStat(name); + +#else + +#define INCSTAT(name) + +#endif + diff --git a/base/stringutil.h b/base/stringutil.h new file mode 100644 index 0000000000..e5dfa87923 --- /dev/null +++ b/base/stringutil.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +class ITOA { +public: + char buffer[16]; + const char *p(int i) { + sprintf(buffer, "%i", i); + return &buffer[0]; + } +}; + +inline bool endsWith(const std::string &str, const std::string &what) { + return str.substr(str.size() - what.size()) == what; +} \ No newline at end of file diff --git a/base/timeutil.cpp b/base/timeutil.cpp new file mode 100644 index 0000000000..3ab3033547 --- /dev/null +++ b/base/timeutil.cpp @@ -0,0 +1,80 @@ +#include "base/basictypes.h" +#include "base/timeutil.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#endif +#include + +static double curtime = 0; +static float curtime_f = 0; + +#ifdef _WIN32 + +__int64 _frequency = 0; +__int64 _starttime = 0; + +double real_time_now(){ + if (_frequency == 0) { + QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency); + QueryPerformanceCounter((LARGE_INTEGER*)&_starttime); + curtime=0; + } + __int64 time; + QueryPerformanceCounter((LARGE_INTEGER*)&time); + return ((double) (time - _starttime) / (double) _frequency); +} + +#else + +double real_time_now() { + static time_t start; + struct timeval tv; + gettimeofday(&tv, NULL); + if (start == 0) { + start = tv.tv_sec; + } + tv.tv_sec -= start; + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; +} + +#endif + +void time_update() { + curtime = real_time_now(); + curtime_f = (float)curtime; + + //printf("curtime: %f %f\n", curtime, curtime_f); + // also smooth time. + //curtime+=float((double) (time-_starttime) / (double) _frequency); + //curtime*=0.5f; + //curtime+=1.0f/60.0f; + //lastTime=curtime; + //curtime_f = (float)curtime; +} + +float time_now() { + return curtime_f; +} + +double time_now_d() { + return curtime; +} + +int time_now_ms() { + return int(curtime*1000.0); +} + +void sleep_ms(int ms) { +#ifdef _WIN32 +#ifndef METRO + Sleep(ms); +#endif +#else + usleep(ms * 1000); +#endif +} + diff --git a/base/timeutil.h b/base/timeutil.h new file mode 100644 index 0000000000..388e9a09dc --- /dev/null +++ b/base/timeutil.h @@ -0,0 +1,19 @@ +#pragma once + +// http://linux.die.net/man/3/clock_gettime + +// This time implementation caches the time for max performance (call time_now() as much as you like). +// You need to call time_update() once per frame (or whenever you need the correct time right now). + +void time_update(); + +// Seconds. +float time_now(); +double time_now_d(); + +// Slower than the above cached time functions +double real_time_now(); + +int time_now_ms(); + +void sleep_ms(int ms); diff --git a/collision/CMakeLists.txt b/collision/CMakeLists.txt new file mode 100644 index 0000000000..00af58df88 --- /dev/null +++ b/collision/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SRCS + collision.cpp + ) + +add_library(collision STATIC ${SRCS}) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) diff --git a/collision/collision.cpp b/collision/collision.cpp new file mode 100644 index 0000000000..b4993562e9 --- /dev/null +++ b/collision/collision.cpp @@ -0,0 +1,288 @@ +#include "base/logging.h" +#include "math/lin/vec3.h" +#include "math/ray_intersections.h" +#include "collision/collision.h" + +#include + +// #define COLLISION_ENABLE_SCALING + +static std::string V(const Vec3 &v) { + char temp[100]; + sprintf(temp, "(%f %f %f)", v.x, v.y, v.z); + return std::string(temp); +} + +Collidable::Collidable() { +} + +Collidable::~Collidable() { +} + +void Collision::Init(const Vec3 &source, const Vec3 &dest, const Vec3 &size) { + this->size = size; + scale.Set(1.0f / size.x, 1.0f / size.y, 1.0f / size.z); + sourcePoint = source.scaledBy(scale); + + velocity = (dest - source).scaledBy(scale); + velocityLength = velocity.length(); + normalizedVelocity = velocity / velocityLength; + + stuck = false; + foundCollision = false; + nearestDistance = velocityLength; +} + +void Collision::BeginTransform(const Matrix4x4 &inverse, const Matrix4x4 &transform) { + this->transform = &transform; + untransformed_sourcePoint = sourcePoint; + untransformed_velocity = velocity; + + sourcePoint = sourcePoint * inverse; + velocity = velocity.rotatedBy(inverse); + + normalizedVelocity = untransformed_velocity.normalized(); +} + +void Collision::EndTransform() { + sourcePoint = untransformed_sourcePoint; + velocity = untransformed_velocity; + + normalizedVelocity = untransformed_velocity.normalized(); + this->transform = 0; +} + +bool Collision::registerCollision(float t, const Vec3 &polyIPoint) { + // Special handling for multiple contact points + if (t < 0.0f) { + return false; + } + Vec3 ipoint; + if (transform) ipoint = polyIPoint * (*transform); + else ipoint = polyIPoint; + + if (t >= -0.0001 && t <= 0.0001 && num_contact_points < MAX_CONTACT_POINTS) { + for (int i = 0; i < num_contact_points; i++) { + // Check if identical - can happen on dupe edges (which we'll later remove). + // TODO: Let's see if we can get rid of this fuzziness. + if (ipoint.distance2To(contact_points[i]) < 0.00001) + goto skip; + } + contact_points[num_contact_points++] = ipoint; + } +skip: + if ((t >= 0.0) && (t <= velocityLength) && (t <= this->nearestDistance)) { + // If we are hit we have a closest hit so far. We save the information + this->nearestDistance = t; + this->nearestPolygonIntersectionPoint = ipoint; + this->foundCollision = true; + //ILOG("Collision registered, t=%f", t); + return true; + } else { + // Already had a closer collision. + return false; + } +} + +bool Collision::Triangle(Vec3 p1, Vec3 p2, Vec3 p3) { + Vec3 pNormal = (p2 - p1) % (p3 - p1); + pNormal.normalize(); + return Triangle(p1, p2, p3, pNormal); +} + +bool Collision::Triangle(Vec3 p1, Vec3 p2, Vec3 p3, const Vec3 &pNormal) { +#ifdef COLLISION_ENABLE_SCALING + p1.scaleBy(scale); + p2.scaleBy(scale); + p3.scaleBy(scale); +#endif + + // Backface + // Early out optimization, lose a square root + if (dot(velocity, pNormal) >= 0.0f) + return false; + + // ILOG("normal. %s", V(pNormal).c_str()); + // find the plane intersection point + float t; + Vec3 polyIPoint, sIPoint = sourcePoint - pNormal; + if (pointPlaneDistance(sIPoint, p1, pNormal) > 0.0f) { + // Plane is embedded in ellipsoid. + // Find plane intersection point by shooting a ray from the sphere intersection + // point along the plane normal. + t = intersectRayPlanePerp(sIPoint, pNormal, p1); + polyIPoint = sIPoint + pNormal * t; // calculate plane intersection point + } else { + // shoot ray along the velocity vector + t = intersectRayPlane(sIPoint, normalizedVelocity, p1, pNormal); + if (t > velocityLength) { + // Did not hit plane. No point in doing triangle tests. + return false; + } + // calculate plane intersection point + polyIPoint = sIPoint + normalizedVelocity * t; + } + + if (isPointInTriangle(polyIPoint, p1, p2, p3)) { + // ILOG("Hit triangle! %s %s %s %s", V(polyIPoint).c_str(), V(p1).c_str(), V(p2).c_str(), V(p3).c_str()); + float depth2 = (polyIPoint - sourcePoint).length2(); + if (depth2 < 0.9999f) { + // Here we do the error checking to see if we got ourself stuck last frame + ELOG("Stuck in triangle! depth2 = %f", depth2); + // ILOG("%s %s %s", V(p1).c_str(), V(p2).c_str(), V(p3).c_str()); + stuck = true; + } + return registerCollision(t, polyIPoint); + } else { + // ILOG("Missed this triangle - returning"); + return false; + } +} + +bool Collision::EdgeLine(Vec3 p1, Vec3 p2) { + // This ellipsoid scaling stuff will result in the edge not being really cylindric. + // Should work for many purposes though. +#ifdef COLLISION_ENABLE_SCALING + p1.scaleBy(scale); + p2.scaleBy(scale); +#endif + float t = 1000000000.0; + Vec3 polyIPoint; + if (intersectCylinder(sourcePoint, normalizedVelocity, + p1, p2, &t, &polyIPoint)) { + polyIPoint = closestPointOnLine(p1, p2, polyIPoint); + return registerCollision(t, polyIPoint); + } else { + return false; + } +} + +bool Collision::Corner(Vec3 p1) { +#ifdef COLLISION_ENABLE_SCALING + p1.scaleBy(scale); +#endif + float t = intersectRayUnitSphere(sourcePoint, normalizedVelocity, p1); + if (t != -1.0f) { + return registerCollision(t, p1); + } else { + return false; + } +} + +bool Collision::Quad(const Vec3 &o, const Vec3 &dx, const Vec3 &dy) { + bool hit = Triangle(o, o + dx, o + dx + dy); + hit |= Triangle(o + dx + dy, o + dy, o); + return hit; +} + +bool Collide(Collision *collision, Collidable *scene, Vec3 *out) { + bool hit_anything = false; + collision->num_contact_points = 0; + collision->lastSafePosition = collision->sourcePoint; + // Process max 8 slides to have a limit if we get into an impossible situation. + int i = 0; + for (i = 0; i < 4; i++) { + // ILOG("Try %i! %s", i, V(collision->sourcePoint).c_str()); + scene->Collide(collision); + if (!collision->foundCollision) { + // Nothing left to do. + collision->sourcePoint += collision->velocity; + collision->velocity.setZero(); + break; + } + hit_anything = true; + if (collision->nearestDistance < 0.0f) { + ILOG("NegHit! %f %f", collision->velocityLength, collision->nearestDistance); + } else { + // ILOG("Hit! %f %f ", collision->velocityLength, collision->nearestDistance); + } + + Vec3 slidePlaneOrigin; + Vec3 slidePlaneNormal; + Vec3 newSourcePoint = collision->sourcePoint; + + float adjusted_hit_distance = collision->nearestDistance - 0.0001f; + if (adjusted_hit_distance < 0) adjusted_hit_distance = 0; + + // Two cases - one where we have multiple contact points, one where we don't. + if (collision->num_contact_points > 1) { + ILOG("Resolve %i-ary collision, velocityLength = %f", + collision->num_contact_points, collision->velocity.length()); + Vec3 avg_contact(0,0,0); + for (int i = 0; i < collision->num_contact_points; i++) { + ILOG("Contact point %s", V(collision->contact_points[i]).c_str()); + avg_contact += collision->contact_points[i]; + } + avg_contact /= (float)collision->num_contact_points; + + // The average of the contact points should be inside the sphere. + float dist = sqrt(avg_contact.distance2To(collision->sourcePoint)); + if (dist > 1.0) { + ILOG("Bogus contact points, for sure: %s vs %s, dist=%f", V(avg_contact).c_str(), V(collision->sourcePoint).c_str(), dist); + } + + Vec3 avg_contact_dir = avg_contact - collision->sourcePoint; + avg_contact_dir.normalize(); + avg_contact = collision->sourcePoint + avg_contact_dir; + + newSourcePoint += collision->normalizedVelocity * adjusted_hit_distance; + slidePlaneOrigin = avg_contact; + slidePlaneNormal = (newSourcePoint - slidePlaneOrigin).normalized(); + + // Push the ball out a bit along the normal to avoid re-collision. + newSourcePoint += slidePlaneNormal * 0.01f; + //collision->velocity += slidePlaneNormal * 0.01; + + //slidePlaneNormal = avg_contact_dir; + } else { + // LOG(INFO) << "Adjusting newsourcepoint to end of collision"; + newSourcePoint += collision->normalizedVelocity * adjusted_hit_distance; + // LOG(INFO) << "s: " << V(collision->sourcePoint); + // LOG(INFO) << "nsp: " << V(newSourcePoint); + // LOG(INFO) << "ip: " << V(collision->nearestPolygonIntersectionPoint); + + // Now we must calculate the sliding plane + slidePlaneOrigin = collision->nearestPolygonIntersectionPoint; + slidePlaneNormal = (newSourcePoint - slidePlaneOrigin).normalized(); + } + + // We now project the destination point onto the sliding plane + Vec3 destinationPoint = collision->sourcePoint + collision->velocity; + float l = intersectRayPlane(destinationPoint, slidePlaneNormal, + slidePlaneOrigin, -slidePlaneNormal); + + // We can now calculate a new destination point on the sliding plane + Vec3 newDestinationPoint = destinationPoint + slidePlaneNormal * l; + + // now we start over with the new position and velocity + + collision->sourcePoint = newSourcePoint; + // Generate the slide vector, which will become our new velocity vector + Vec3 slide = newDestinationPoint - slidePlaneOrigin; + slide += slidePlaneNormal * 0.0001f; + + // LOG(INFO) << "NDP: " << V(newDestinationPoint); + // ILOG("Slide: %s", V(slide).c_str()); + // Recompute to get ready! + collision->velocity = slide; + collision->velocityLength = collision->velocity.length(); + if (collision->velocityLength <= 0.0002) { + //ILOG("Zero-length velocity vector. Break."); + collision->velocity.setZero(); + break; + } else { + // ILOG("Velocity: %s", V(collision->velocity).c_str()); + } + collision->nearestDistance = collision->velocityLength; + collision->normalizedVelocity = collision->velocity / collision->velocityLength; + } + //ILOG("%i tries", i); + + *out = collision->sourcePoint.scaledBy(collision->size); + if (collision->stuck) { + //ILOG("Stuck - resetting"); + // *out = collision->lastSafePosition.scaledBy(collision->size); + } + return hit_anything; +} + diff --git a/collision/collision.h b/collision/collision.h new file mode 100644 index 0000000000..2ff5ba1d47 --- /dev/null +++ b/collision/collision.h @@ -0,0 +1,89 @@ +// Collision detection engine. You pass in a class representing a scene, +// and out comes a nice FPS-like "sliding physics" response. + +// By hrydgard@gmail.com, + + +#ifndef _COLLISION_H +#define _COLLISION_H + +#include "math/lin/vec3.h" +#include "math/lin/matrix4x4.h" + +class Collision; + +class Collidable { + public: + Collidable(); + virtual ~Collidable(); + // Apply the collision functions to collision as appropriate. + virtual void Collide(Collision *collision) = 0; +}; + +class Collision { + public: + void Init(const Vec3 &source, const Vec3 &dest, const Vec3 &size); + + // Collision functions + // These should be called from Collidable::Collide and nowhere else + // except the unit test. + + // To correctly collide with meshes, just apply these in sequence. You MUST + // collide against all external (convex) edges, otherwise you can get + // quite stuck. There's PLENTY of optimization to do here. + + bool Triangle(Vec3 p1, Vec3 p2, Vec3 p3); + bool Triangle(Vec3 p1, Vec3 p2, Vec3 p3, const Vec3 &normal); + bool EdgeLine(Vec3 p1, Vec3 p2); // Cylinder + bool Quad(const Vec3 &origin, const Vec3 &dX, const Vec3 &dY); + bool UnitCube(const Vec3 &origin); + bool Corner(Vec3 p1); // Sphere + bool Corners(Vec3 *p, int count); + + // There's an opportunity to provide hyper optimized versions of + // Edge for axis aligned edges. The cylinder intersection becomes trivial. + + // These cannot be nested! + void BeginTransform(const Matrix4x4 &inverse, const Matrix4x4 &transform); + void EndTransform(); + + private: + bool registerCollision(float t, const Vec3 &polyIPoint); + + public: + Vec3 sourcePoint; + + float velocityLength; + + Vec3 size; + Vec3 scale; + Vec3 velocity; // data about player movement + Vec3 normalizedVelocity; + + Vec3 lastSafePosition; // for error handling + bool stuck; + // data for collision response + bool foundCollision; + float nearestDistance; // nearest distance to hit + Vec3 nearestPolygonIntersectionPoint; // on polygon + + const Matrix4x4 *transform; + + // Saved state during transformed operation + Vec3 untransformed_sourcePoint; + Vec3 untransformed_velocity; + + enum { + MAX_CONTACT_POINTS = 10 + }; + Vec3 contact_points[MAX_CONTACT_POINTS]; + int num_contact_points; +}; + +// Sliding physics. +bool Collide(Collision *collision, Collidable *scene, Vec3 *out); + +// Bouncy physics +// void CollideBouncy( ... ) + +#endif // _COLLISION_H diff --git a/ext/etcpack/CMakeLists.txt b/ext/etcpack/CMakeLists.txt new file mode 100644 index 0000000000..806f4157b0 --- /dev/null +++ b/ext/etcpack/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 2.6) + +add_library(etcdec etcdec.cpp) +#if UNIX +add_definitions(-PIC) + +add_library(etcpack etcpack.cpp) + +add_definitions(-g) +add_definitions(-O2) +add_definitions(-Wall) +add_definitions(-DSDL) +add_definitions(-Wno-multichar) +# add_definitions(-fno-strict-aliasing) +add_definitions(-fstrict-aliasing) + +set(LIBS ${LIBS}) + +add_executable(etctool etctool.cpp image.cpp) +target_link_libraries(etctool etcdec etcpack) + +#add_executable(zimtool zimtool.cpp) +#target_link_libraries(zimtool png freetype z image file zip) + +#set(SRCS +# draw2d.cpp +# fbo.cpp +# glsl_program.cpp) +# +#set(SRCS ${SRCS}) + +#add_library(gfx_es2 STATIC ${SRCS}) + +#if(UNIX) +# add_definitions(-fPIC) +#endif(UNIX) diff --git a/ext/etcpack/README b/ext/etcpack/README new file mode 100644 index 0000000000..d3fbabb1eb --- /dev/null +++ b/ext/etcpack/README @@ -0,0 +1,7 @@ +This folder contains code adapted from etcpack by ERICSSON ///. + +The license comments have been left at the top of each file, but some code may have moved around. + +See LICENSE.TXT for details. It says that use of the code is limited to applications using the +code to compress textures for use with a Khronos-derived API. This game engine only supports +OpenGL ES 2.0, which certainly qualifies, so there is no issue there. diff --git a/ext/etcpack/etcdec.cpp b/ext/etcpack/etcdec.cpp new file mode 100644 index 0000000000..e19e48d54b --- /dev/null +++ b/ext/etcpack/etcdec.cpp @@ -0,0 +1,418 @@ +/** + +@~English +@page licensing Licensing + +@section etcdec etcdec.cxx License + +etcdec.cxx is made available under the terms and conditions of the following +License Agreement. + +SOFTWARE LICENSE AGREEMENT + +PLEASE REVIEW THE FOLLOWING TERMS AND CONDITIONS PRIOR TO USING THE +ERICSSON TEXTURE COMPRESSION CODEC SOFTWARE (THE "SOFTWARE"). THE USE +OF THE SOFTWARE IS SUBJECT TO THE TERMS AND CONDITIONS OF THE +FOLLOWING LICENSE AGREEMENT (THE "LICENSE AGREEMENT"). IF YOU DO NOT +ACCEPT SUCH TERMS AND CONDITIONS YOU MAY NOT USE THE SOFTWARE. + +Under the terms and conditions of the License Agreement, Licensee +hereby, receives a non-exclusive, non transferable, limited, free of +charge, perpetual and worldwide license, to copy, use, distribute and +modify the Software, but only for the purpose of developing, +manufacturing, selling, using and distributing products including the +Software, which products are used for (i) compression and/or +decompression to create content creation tools for usage with a +Khronos API, and/or (ii) compression and/or decompression for the +purpose of usage with a middleware API that is built on top of a +Khronos API, such as JCPs based on a Khronos API (in particular +"Mobile 3D Graphics API for J2ME" and its future versions and "Java +Bindings for OpenGL ES" and its future versions), and/or (iii) +compression and/or decompression to implement a Khronos specification. + +If Licensee institutes patent litigation against Ericsson or any +licensee of the Software for using the Software for making, +developing, manufacturing, selling, using and/or distributing products +within the scope of the Khronos framework, Ericsson shall have the +right to terminate this License Agreement with immediate +effect. However, should Licensee institute patent litigation against +any other licensee of the Software based on such licensee´s use of any +other software distributed together with the Software then Ericsson +shall have no right to terminate this License Agreement. + +The License Agreement does not transfer to Licensee any ownership to +any Ericsson or third party intellectual property rights. + +THE SOFTWARE IS PROVIDED "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF +ANY KIND, EXTENDS NO WARRANTIES OF ANY KIND, EITHER EXPRESS OR +IMPLIED; INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK +AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH THE +LICENSEE. SHOULD THE SOFTWARE PROVE DEFECTIVE, THE LICENSEE ASSUMES +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ERICSSON +MAKES NO WARRANTY THAT THE MANUFACTURE, SALE, DISTRIBUTION, LEASE, USE +OR IMPORTATION UNDER THE LICENSE AGREEMENT WILL BE FREE FROM +INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER INTELLECTUAL PROPERTY +RIGHTS OF OTHERS, AND THE VALIDITY OF THE LICENSE AND THE LICENSE +AGREEMENT IS SUBJECT TO LICENSEE'S SOLE RESPONSIBILITY TO MAKE SUCH +DETERMINATION AND ACQUIRE SUCH LICENSES AS MAY BE NECESSARY WITH +RESPECT TO PATENTS AND OTHER INTELLECTUAL PROPERTY OF THIRD PARTIES. + +IN NO EVENT WILL ERICSSON BE LIABLE TO THE LICENSEE FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY THE LICENSEE OR THIRD PARTIES OR A FAILURE OF THE +SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR +OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +Licensee acknowledges that "ERICSSON ///" is the corporate trademark +of Telefonaktiebolaget LM Ericsson and that both "Ericsson" and the +figure "///" are important features of the trade names of +Telefonaktiebolaget LM Ericsson. Nothing contained in these terms and +conditions shall be deemed to grant Licensee any right, title or +interest in the word "Ericsson" or the figure "///". + +The parties agree that this License Agreement based on these terms and +conditions is governed by the laws of Sweden, and in the event of any +dispute as a result of this License Agreement, the parties submit +themselves to the exclusive jurisdiction of the Swedish Courts. +*/ + +#include +#include + +#define CLAMP(ll,x,ul) (((x)<(ll)) ? (ll) : (((x)>(ul)) ? (ul) : (x))) +#define GETBITS(source, size, startpos) (( (source) >> ((startpos)-(size)+1) ) & ((1<<(size)) -1)) +#define GETBITSHIGH(source, size, startpos) (( (source) >> (((startpos)-32)-(size)+1) ) & ((1<<(size)) -1)) +#define RED(img,width,x,y) img[3*(y*width+x)+0] +#define GREEN(img,width,x,y) img[3*(y*width+x)+1] +#define BLUE(img,width,x,y) img[3*(y*width+x)+2] + +typedef unsigned char uint8; + +int unscramble[4] = {2, 3, 1, 0}; + +static const int compressParams[16][4] = {{-8, -2, 2, 8}, {-8, -2, 2, 8}, {-17, -5, 5, 17}, {-17, -5, 5, 17}, {-29, -9, 9, 29}, {-29, -9, 9, 29}, {-42, -13, 13, 42}, {-42, -13, 13, 42}, {-60, -18, 18, 60}, {-60, -18, 18, 60}, {-80, -24, 24, 80}, {-80, -24, 24, 80}, {-106, -33, 33, 106}, {-106, -33, 33, 106}, {-183, -47, 47, 183}, {-183, -47, 47, 183}}; + +void decompressBlockDiffFlip(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty) +{ + uint8 avg_color[3], enc_color1[3], enc_color2[3]; + char diff[3]; + int table; + int index,shift; + int r,g,b; + int diffbit; + int flipbit; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + int x,y; + + diffbit = (GETBITSHIGH(block_part1, 1, 33)); + flipbit = (GETBITSHIGH(block_part1, 1, 32)); + + if( !diffbit ) + { + + // We have diffbit = 0. + + // First decode left part of block. + avg_color[0]= GETBITSHIGH(block_part1, 4, 63); + avg_color[1]= GETBITSHIGH(block_part1, 4, 55); + avg_color[2]= GETBITSHIGH(block_part1, 4, 47); + + // Here, we should really multiply by 17 instead of 16. This can + // be done by just copying the four lower bits to the upper ones + // while keeping the lower bits. + avg_color[0] |= (avg_color[0] <<4); + avg_color[1] |= (avg_color[1] <<4); + avg_color[2] |= (avg_color[2] <<4); + + table = GETBITSHIGH(block_part1, 3, 39) << 1; + + + + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift = 0; + for(x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED(img,width,x,y) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN(img,width,x,y)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE(img,width,x,y) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + + + } + } + } + else + { + // We should flip + shift = 0; + for(x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED(img,width,x,y) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN(img,width,x,y)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE(img,width,x,y) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift+=2; + } + } + + // Now decode other part of block. + avg_color[0]= GETBITSHIGH(block_part1, 4, 59); + avg_color[1]= GETBITSHIGH(block_part1, 4, 51); + avg_color[2]= GETBITSHIGH(block_part1, 4, 43); + + // Here, we should really multiply by 17 instead of 16. This can + // be done by just copying the four lower bits to the upper ones + // while keeping the lower bits. + avg_color[0] |= (avg_color[0] <<4); + avg_color[1] |= (avg_color[1] <<4); + avg_color[2] |= (avg_color[2] <<4); + + table = GETBITSHIGH(block_part1, 3, 36) << 1; + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift=8; + for(x=startx+2; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED(img,width,x,y) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN(img,width,x,y)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE(img,width,x,y) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + + } + } + } + else + { + // We should flip + shift=2; + for(x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED(img,width,x,y) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN(img,width,x,y)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE(img,width,x,y) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + + } + shift += 2; + } + } + + } + else + { + // We have diffbit = 1. + +// 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 +// --------------------------------------------------------------------------------------------------- +// | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| +// | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | +// --------------------------------------------------------------------------------------------------- +// +// +// c) bit layout in bits 31 through 0 (in both cases) +// +// 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// -------------------------------------------------------------------------------------------------- +// | most significant pixel index bits | least significant pixel index bits | +// | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | +// -------------------------------------------------------------------------------------------------- + + + // First decode left part of block. + enc_color1[0]= GETBITSHIGH(block_part1, 5, 63); + enc_color1[1]= GETBITSHIGH(block_part1, 5, 55); + enc_color1[2]= GETBITSHIGH(block_part1, 5, 47); + + + // Expand from 5 to 8 bits + avg_color[0] = (enc_color1[0] <<3) | (enc_color1[0] >> 2); + avg_color[1] = (enc_color1[1] <<3) | (enc_color1[1] >> 2); + avg_color[2] = (enc_color1[2] <<3) | (enc_color1[2] >> 2); + + + table = GETBITSHIGH(block_part1, 3, 39) << 1; + + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift = 0; + for(x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED(img,width,x,y) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN(img,width,x,y)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE(img,width,x,y) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + + + } + } + } + else + { + // We should flip + shift = 0; + for(x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED(img,width,x,y) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN(img,width,x,y)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE(img,width,x,y) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift+=2; + } + } + + + // Now decode right part of block. + + + diff[0]= GETBITSHIGH(block_part1, 3, 58); + diff[1]= GETBITSHIGH(block_part1, 3, 50); + diff[2]= GETBITSHIGH(block_part1, 3, 42); + + enc_color2[0]= enc_color1[0] + diff[0]; + enc_color2[1]= enc_color1[1] + diff[1]; + enc_color2[2]= enc_color1[2] + diff[2]; + + // Extend sign bit to entire byte. + diff[0] = (diff[0] << 5); + diff[1] = (diff[1] << 5); + diff[2] = (diff[2] << 5); + diff[0] = diff[0] >> 5; + diff[1] = diff[1] >> 5; + diff[2] = diff[2] >> 5; + + // Calculale second color + enc_color2[0]= enc_color1[0] + diff[0]; + enc_color2[1]= enc_color1[1] + diff[1]; + enc_color2[2]= enc_color1[2] + diff[2]; + + // Expand from 5 to 8 bits + avg_color[0] = (enc_color2[0] <<3) | (enc_color2[0] >> 2); + avg_color[1] = (enc_color2[1] <<3) | (enc_color2[1] >> 2); + avg_color[2] = (enc_color2[2] <<3) | (enc_color2[2] >> 2); + + + table = GETBITSHIGH(block_part1, 3, 36) << 1; + pixel_indices_MSB = GETBITS(block_part2, 16, 31); + pixel_indices_LSB = GETBITS(block_part2, 16, 15); + + if( (flipbit) == 0 ) + { + // We should not flip + shift=8; + for(x=startx+2; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED(img,width,x,y) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN(img,width,x,y)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE(img,width,x,y) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + + } + } + } + else + { + // We should flip + shift=2; + for(x=startx; x> shift) & 1) << 1; + index |= ((pixel_indices_LSB >> shift) & 1); + shift++; + index=unscramble[index]; + + r=RED(img,width,x,y) =CLAMP(0,avg_color[0]+compressParams[table][index],255); + g=GREEN(img,width,x,y)=CLAMP(0,avg_color[1]+compressParams[table][index],255); + b=BLUE(img,width,x,y) =CLAMP(0,avg_color[2]+compressParams[table][index],255); + } + shift += 2; + } + } + } +} + +static int bswap(unsigned int x) { + return ((x & 0xFF000000) >> 24) | + ((x & 0x00FF0000) >> 8) | + ((x & 0x0000FF00) << 8) | + ((x & 0x000000FF) << 24); +} + +void DecompressBlock(const uint8 *compressed, uint8 *out, int out_width, int alpha=255) { + uint8 rgb[4*4*3]; + unsigned int block_part1, block_part2; + memcpy(&block_part1, compressed, 4); + memcpy(&block_part2, compressed + 4, 4); + block_part1 = bswap(block_part1); + block_part2 = bswap(block_part2); + decompressBlockDiffFlip(block_part1, block_part2, rgb, 4, 4, 0, 0); + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + out[(y * out_width + x) * 4 + 0] = rgb[(4 * y + x) * 3 + 0]; + out[(y * out_width + x) * 4 + 1] = rgb[(4 * y + x) * 3 + 1]; + out[(y * out_width + x) * 4 + 2] = rgb[(4 * y + x) * 3 + 2]; + out[(y * out_width + x) * 4 + 3] = alpha; + } + } +} diff --git a/ext/etcpack/etcdec.h b/ext/etcpack/etcdec.h new file mode 100644 index 0000000000..5a0ac6e5bb --- /dev/null +++ b/ext/etcpack/etcdec.h @@ -0,0 +1,9 @@ +#pragma once + +typedef unsigned char uint8; + +/* In etcdec.cxx */ +void decompressBlockDiffFlip(unsigned int block_part1, unsigned int block_part2, uint8 *img,int width,int height,int startx,int starty); + +// Writes RGBA output instead of RGB. +void DecompressBlock(const uint8 *compressed, uint8 *out, int out_width, int alpha=255); diff --git a/ext/etcpack/etcpack.cpp b/ext/etcpack/etcpack.cpp new file mode 100644 index 0000000000..3cf9feb7e0 --- /dev/null +++ b/ext/etcpack/etcpack.cpp @@ -0,0 +1,5730 @@ +// Modified by Henrik RydgÃ¥rd: +// +// * Moved many I/O and similar functions to etctool.cpp, this file +// should only concern itself with compression. +// * got rid of readCompress.. + + + +// etcpack.cxx v1.06 +// +// NO WARRANTY +// +// BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, ERICSSON MAKES NO +// REPRESENTATIONS OF ANY KIND, EXTENDS NO WARRANTIES OF ANY KIND; EITHER +// EXPRESS OR IMPLIED; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +// PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +// THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ERICSSON +// MAKES NO WARRANTY THAT THE MANUFACTURE, SALE, LEASE, USE OR +// IMPORTATION WILL BE FREE FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR +// OTHER INTELLECTUAL PROPERTY RIGHTS OF OTHERS, AND IT SHALL BE THE SOLE +// RESPONSIBILITY OF THE LICENSEE TO MAKE SUCH DETERMINATION AS IS +// NECESSARY WITH RESPECT TO THE ACQUISITION OF LICENSES UNDER PATENTS +// AND OTHER INTELLECTUAL PROPERTY OF THIRD PARTIES; +// +// IN NO EVENT WILL ERICSSON, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +// GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF +// THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO +// LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +// YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY +// OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGES. +// +// (C) Ericsson AB 2005. All Rights Reserved. +// + +#include +#include + +#include "etcpack.h" +#include "etcdec.h" + + + +#define CLAMP(ll,x,ul) (((x)<(ll)) ? (ll) : (((x)>(ul)) ? (ul) : (x))) +#define GETBITS(source, size, startpos) (( (source) >> ((startpos)-(size)+1) ) & ((1<<(size)) -1)) +#define GETBITSHIGH(source, size, startpos) (( (source) >> (((startpos)-32)-(size)+1) ) & ((1<<(size)) -1)) + +#define SQUARE(x) ((x)*(x)) +#define JAS_ROUND(x) (((x) < 0.0 ) ? ((int)((x)-0.5)) : ((int)((x)+0.5))) + +#define RED(img,width,x,y) img[3*(y*width+x)+0] +#define GREEN(img,width,x,y) img[3*(y*width+x)+1] +#define BLUE(img,width,x,y) img[3*(y*width+x)+2] + + +// SLOW SCAN RANGE IS -5 to 5 in all three colors +#define SLOW_SCAN_MIN (-5) +#define SLOW_SCAN_MAX (5) +#define SLOW_SCAN_RANGE ((SLOW_SCAN_MAX-(SLOW_SCAN_MIN)+1)) +#define SLOW_SCAN_OFFSET (-(SLOW_SCAN_MIN)) +// We need this to guarrantee that at least one try is valid +#define SLOW_TRY_MIN (-4 - SLOW_SCAN_MAX) +#define SLOW_TRY_MAX (3 - (SLOW_SCAN_MIN)) + + +// MEDIUM SCAN RANGE IS -3 to 3in all three colors +#define MEDIUM_SCAN_MIN (-3) +#define MEDIUM_SCAN_MAX (3) +#define MEDIUM_SCAN_RANGE ((MEDIUM_SCAN_MAX-(MEDIUM_SCAN_MIN)+1)) +#define MEDIUM_SCAN_OFFSET (-(MEDIUM_SCAN_MIN)) + +// We need this to guarrantee that at least one try is valid +#define MEDIUM_TRY_MIN (-4 - MEDIUM_SCAN_MAX) +#define MEDIUM_TRY_MAX (3 - (MEDIUM_SCAN_MIN)) + + +#define PUTBITS( dest, data, size, startpos) dest |= ( (data) & ((1<<(size))-1) ) << ((startpos)-(size)+1) +#define PUTBITSHIGH( dest, data, size, startpos) dest |= ( (data) & ((1<<(size))-1) ) << (((startpos)-32)-(size)+1) + +int scramble[4] = {3, 2, 0, 1}; + +static const int compressParamsEnc[16][4] = { + { -8, -2, 2, 8 }, + { -8, -2, 2, 8 }, + { -17, -5, 5, 17 }, + { -17, -5, 5, 17 }, + { -29, -9, 9, 29 }, + { -29, -9, 9, 29 }, + { -42, -13, 13, 42 }, + { -42, -13, 13, 42 }, + { -60, -18, 18, 60 }, + { -60, -18, 18, 60 }, + { -80, -24, 24, 80 }, + { -80, -24, 24, 80 }, + {-106, -33, 33,106 }, + {-106, -33, 33,106 }, + {-183, -47, 47,183 }, + {-183, -47, 47,183 }, +}; + +void computeAverageColor2x4noQuantFloat(uint8 *img,int width,int height,int startx,int starty,float *avg_color) { + int r=0,g=0,b=0; + for(int y=starty; y> 1), 1, i); + PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); + + i++; + + // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} + // so that first bit is sign bit and the other bit is size bit (4 or 12). + // This means that we have to scramble the bits before storing them. + + + sum_error+=min_error; + } + + } + + *pixel_indices_MSBp = pixel_indices_MSB; + *pixel_indices_LSBp = pixel_indices_LSB; + + return sum_error; +} + +float compressBlockWithTable2x4percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) +{ + uint8 orig[3],approx[3]; + unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; + float sum_error=0; + int q, i; + + double wR2 = PERCEPTUAL_WEIGHT_R_SQUARED; + double wG2 = PERCEPTUAL_WEIGHT_G_SQUARED; + double wB2 = PERCEPTUAL_WEIGHT_B_SQUARED; + + i = 0; + for(int x=startx; x> 1), 1, i); + PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); + + i++; + + // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} + // so that first bit is sign bit and the other bit is size bit (4 or 12). + // This means that we have to scramble the bits before storing them. + + + sum_error+=min_error; + } + + } + + *pixel_indices_MSBp = pixel_indices_MSB; + *pixel_indices_LSBp = pixel_indices_LSB; + + return sum_error; +} +int compressBlockWithTable4x2(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) +{ + uint8 orig[3],approx[3]; + unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; + int sum_error=0; + int q; + int i; + + + i = 0; + for(int x=startx; x> 1), 1, i); + PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); + i++; + + // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} + // so that first bit is sign bit and the other bit is size bit (4 or 12). + // This means that we have to scramble the bits before storing them. + + sum_error+=min_error; + } + i+=2; + + } + + *pixel_indices_MSBp = pixel_indices_MSB; + *pixel_indices_LSBp = pixel_indices_LSB; + + + return sum_error; +} + +float compressBlockWithTable4x2percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color,int table,unsigned int *pixel_indices_MSBp, unsigned int *pixel_indices_LSBp) +{ + uint8 orig[3],approx[3]; + unsigned int pixel_indices_MSB=0, pixel_indices_LSB=0, pixel_indices = 0; + float sum_error=0; + int q; + int i; + float wR2 = (float) PERCEPTUAL_WEIGHT_R_SQUARED; + float wG2 = (float) PERCEPTUAL_WEIGHT_G_SQUARED; + float wB2 = (float) PERCEPTUAL_WEIGHT_B_SQUARED; + + + i = 0; + for(int x=startx; x> 1), 1, i); + PUTBITS( pixel_indices_LSB, (pixel_indices & 1) , 1, i); + i++; + + // In order to simplify hardware, the table {-12, -4, 4, 12} is indexed {11, 10, 00, 01} + // so that first bit is sign bit and the other bit is size bit (4 or 12). + // This means that we have to scramble the bits before storing them. + + sum_error+=min_error; + } + i+=2; + + } + + *pixel_indices_MSBp = pixel_indices_MSB; + *pixel_indices_LSBp = pixel_indices_LSB; + + + return sum_error; +} + +int tryalltables_3bittable2x4(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) +{ + int min_error = 3*255*255*16; + int q; + int err; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + for(q=0;q<16;q+=2) // try all the 8 tables. + { + + err=compressBlockWithTable2x4(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); + + if(err> 1; + + } + } + return min_error; +} + +int tryalltables_3bittable2x4percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) +{ + float min_error = 3*255*255*16; + int q; + float err; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + for(q=0;q<16;q+=2) // try all the 8 tables. + { + + err=compressBlockWithTable2x4percep(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); + + if(err> 1; + + } + } + return (int) min_error; +} + +int tryalltables_3bittable4x2(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) +{ + int min_error = 3*255*255*16; + int q; + int err; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + for(q=0;q<16;q+=2) // try all the 8 tables. + { + + err=compressBlockWithTable4x2(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); + + if(err> 1; + } + } + + return min_error; +} +int tryalltables_3bittable4x2percep(uint8 *img,int width,int height,int startx,int starty,uint8 *avg_color, unsigned int &best_table,unsigned int &best_pixel_indices_MSB, unsigned int &best_pixel_indices_LSB) +{ + float min_error = 3*255*255*16; + int q; + float err; + unsigned int pixel_indices_MSB, pixel_indices_LSB; + + for(q=0;q<16;q+=2) // try all the 8 tables. + { + + err=compressBlockWithTable4x2percep(img,width,height,startx,starty,avg_color,q,&pixel_indices_MSB, &pixel_indices_LSB); + + if(err> 1; + } + } + + return (int) min_error; +} + +// The below code quantizes a float RGB value to RGB555. +// + +void quantize444ColorCombined(float *avg_col_in, int *enc_color, uint8 *avg_color) +{ + // Detta ar nummer tva + float dr, dg, db; + float kr, kg, kb; + float wR2, wG2, wB2; + uint8 low_color[3]; + uint8 high_color[3]; + float lowhightable[8]; + int q; + float kval = (float) (255.0/15.0); + + + // These are the values that we want to have: + float red_average, green_average, blue_average; + + int red_4bit_low, green_4bit_low, blue_4bit_low; + int red_4bit_high, green_4bit_high, blue_4bit_high; + + // These are the values that we approximate with: + int red_low, green_low, blue_low; + int red_high, green_high, blue_high; + + red_average = avg_col_in[0]; + green_average = avg_col_in[1]; + blue_average = avg_col_in[2]; + + // Find the 5-bit reconstruction levels red_low, red_high + // so that red_average is in interval [red_low, red_high]. + // (The same with green and blue.) + + red_4bit_low = (int) (red_average/kval); + green_4bit_low = (int) (green_average/kval); + blue_4bit_low = (int) (blue_average/kval); + + red_4bit_high = CLAMP(0, red_4bit_low + 1, 15); + green_4bit_high = CLAMP(0, green_4bit_low + 1, 15); + blue_4bit_high = CLAMP(0, blue_4bit_low + 1, 15); + + red_low = (red_4bit_low << 4) | (red_4bit_low >> 0); + green_low = (green_4bit_low << 4) | (green_4bit_low >> 0); + blue_low = (blue_4bit_low << 4) | (blue_4bit_low >> 0); + + red_high = (red_4bit_high << 4) | (red_4bit_high >> 0); + green_high = (green_4bit_high << 4) | (green_4bit_high >> 0); + blue_high = (blue_4bit_high << 4) | (blue_4bit_high >> 0); + + kr = (float)red_high - (float)red_low; + kg = (float)green_high - (float)green_low; + kb = (float)blue_high - (float)blue_low; + + // Note that dr, dg, and db are all negative. + dr = red_low - red_average; + dg = green_low - green_average; + db = blue_low - blue_average; + + // Use straight (nonperceptive) weights. + wR2 = (float) 1.0; + wG2 = (float) 1.0; + wB2 = (float) 1.0; + + lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + + float min_value = lowhightable[0]; + int min_index = 0; + + for(q = 1; q<8; q++) + { + if(lowhightable[q] < min_value) + { + min_value = lowhightable[q]; + min_index = q; + } + } + + low_color[0] = red_4bit_low; + low_color[1] = green_4bit_low; + low_color[2] = blue_4bit_low; + + high_color[0] = red_4bit_high; + high_color[1] = green_4bit_high; + high_color[2] = blue_4bit_high; + + switch(min_index) + { + case 0: + // Since the step size is always 17 in RGB444 format (15*17=255), + // kr = kg = kb = 17, which means that case 0 and case 7 will + // always have equal projected error. Choose the one that is + // closer to the desired color. + if(dr*dr + dg*dg + db*db > 3*8*8) + { + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + } + else + { + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + } + break; + case 1: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 2: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 3: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 4: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 5: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 6: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + case 7: + if(dr*dr + dg*dg + db*db > 3*8*8) + { + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + } + else + { + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + } + break; + } + + // Expand 5-bit encoded color to 8-bit color + avg_color[0] = (enc_color[0] << 3) | (enc_color[0] >> 2); + avg_color[1] = (enc_color[1] << 3) | (enc_color[1] >> 2); + avg_color[2] = (enc_color[2] << 3) | (enc_color[2] >> 2); +} + + +// The below code quantizes a float RGB value to RGB555. +// +void quantize555ColorCombined(float *avg_col_in, int *enc_color, uint8 *avg_color) +{ + float dr, dg, db; + float kr, kg, kb; + float wR2, wG2, wB2; + uint8 low_color[3]; + uint8 high_color[3]; + float lowhightable[8]; + int q; + float kval = (float) (255.0/31.0); + + + // These are the values that we want to have: + float red_average, green_average, blue_average; + + int red_5bit_low, green_5bit_low, blue_5bit_low; + int red_5bit_high, green_5bit_high, blue_5bit_high; + + // These are the values that we approximate with: + int red_low, green_low, blue_low; + int red_high, green_high, blue_high; + + red_average = avg_col_in[0]; + green_average = avg_col_in[1]; + blue_average = avg_col_in[2]; + + // Find the 5-bit reconstruction levels red_low, red_high + // so that red_average is in interval [red_low, red_high]. + // (The same with green and blue.) + + red_5bit_low = (int) (red_average/kval); + green_5bit_low = (int) (green_average/kval); + blue_5bit_low = (int) (blue_average/kval); + + red_5bit_high = CLAMP(0, red_5bit_low + 1, 31); + green_5bit_high = CLAMP(0, green_5bit_low + 1, 31); + blue_5bit_high = CLAMP(0, blue_5bit_low + 1, 31); + + red_low = (red_5bit_low << 3) | (red_5bit_low >> 2); + green_low = (green_5bit_low << 3) | (green_5bit_low >> 2); + blue_low = (blue_5bit_low << 3) | (blue_5bit_low >> 2); + + red_high = (red_5bit_high << 3) | (red_5bit_high >> 2); + green_high = (green_5bit_high << 3) | (green_5bit_high >> 2); + blue_high = (blue_5bit_high << 3) | (blue_5bit_high >> 2); + + kr = (float)red_high - (float)red_low; + kg = (float)green_high - (float)green_low; + kb = (float)blue_high - (float)blue_low; + + // Note that dr, dg, and db are all negative. + + dr = red_low - red_average; + dg = green_low - green_average; + db = blue_low - blue_average; + + // Use straight (nonperceptive) weights. + wR2 = (float) 1.0; + wG2 = (float) 1.0; + wB2 = (float) 1.0; + + lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + + + float min_value = lowhightable[0]; + int min_index = 0; + + for(q = 1; q<8; q++) + { + if(lowhightable[q] < min_value) + { + min_value = lowhightable[q]; + min_index = q; + } + } + + low_color[0] = red_5bit_low; + low_color[1] = green_5bit_low; + low_color[2] = blue_5bit_low; + + high_color[0] = red_5bit_high; + high_color[1] = green_5bit_high; + high_color[2] = blue_5bit_high; + + switch(min_index) + { + case 0: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 1: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 2: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 3: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 4: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 5: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 6: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + case 7: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + } + + // Expand 5-bit encoded color to 8-bit color + avg_color[0] = (enc_color[0] << 3) | (enc_color[0] >> 2); + avg_color[1] = (enc_color[1] << 3) | (enc_color[1] >> 2); + avg_color[2] = (enc_color[2] << 3) | (enc_color[2] >> 2); + +} + + +// The below code quantizes a float RGB value to RGB444. +// It is thus the same as the above function quantize444ColorCombined(), but it uses a +// weighted error metric instead. +// +void quantize444ColorCombinedPerceptual(float *avg_col_in, int *enc_color, uint8 *avg_color) +{ + float dr, dg, db; + float kr, kg, kb; + float wR2, wG2, wB2; + uint8 low_color[3]; + uint8 high_color[3]; + float lowhightable[8]; + int q; + float kval = (float) (255.0/15.0); + + + // These are the values that we want to have: + float red_average, green_average, blue_average; + + int red_4bit_low, green_4bit_low, blue_4bit_low; + int red_4bit_high, green_4bit_high, blue_4bit_high; + + // These are the values that we approximate with: + int red_low, green_low, blue_low; + int red_high, green_high, blue_high; + + red_average = avg_col_in[0]; + green_average = avg_col_in[1]; + blue_average = avg_col_in[2]; + + // Find the 5-bit reconstruction levels red_low, red_high + // so that red_average is in interval [red_low, red_high]. + // (The same with green and blue.) + + red_4bit_low = (int) (red_average/kval); + green_4bit_low = (int) (green_average/kval); + blue_4bit_low = (int) (blue_average/kval); + + red_4bit_high = CLAMP(0, red_4bit_low + 1, 15); + green_4bit_high = CLAMP(0, green_4bit_low + 1, 15); + blue_4bit_high = CLAMP(0, blue_4bit_low + 1, 15); + + red_low = (red_4bit_low << 4) | (red_4bit_low >> 0); + green_low = (green_4bit_low << 4) | (green_4bit_low >> 0); + blue_low = (blue_4bit_low << 4) | (blue_4bit_low >> 0); + + red_high = (red_4bit_high << 4) | (red_4bit_high >> 0); + green_high = (green_4bit_high << 4) | (green_4bit_high >> 0); + blue_high = (blue_4bit_high << 4) | (blue_4bit_high >> 0); + + low_color[0] = red_4bit_low; + low_color[1] = green_4bit_low; + low_color[2] = blue_4bit_low; + + high_color[0] = red_4bit_high; + high_color[1] = green_4bit_high; + high_color[2] = blue_4bit_high; + + kr = (float)red_high - (float)red_low; + kg = (float)green_high - (float)green_low; + kb = (float)blue_high- (float)blue_low; + + // Note that dr, dg, and db are all negative. + + dr = red_low - red_average; + dg = green_low - green_average; + db = blue_low - blue_average; + + // Perceptual weights to use + wR2 = (float) PERCEPTUAL_WEIGHT_R_SQUARED; + wG2 = (float) PERCEPTUAL_WEIGHT_G_SQUARED; + wB2 = (float) PERCEPTUAL_WEIGHT_B_SQUARED; + + lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + + + float min_value = lowhightable[0]; + int min_index = 0; + + for(q = 1; q<8; q++) + { + if(lowhightable[q] < min_value) + { + min_value = lowhightable[q]; + min_index = q; + } + } + + switch(min_index) + { + case 0: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 1: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 2: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 3: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 4: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 5: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 6: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + case 7: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + } + + // Expand encoded color to eight bits + avg_color[0] = (enc_color[0] << 4) | enc_color[0]; + avg_color[1] = (enc_color[1] << 4) | enc_color[1]; + avg_color[2] = (enc_color[2] << 4) | enc_color[2]; +} + + +// The below code quantizes a float RGB value to RGB555. +// It is thus the same as the above function quantize555ColorCombined(), but it uses a +// weighted error metric instead. +// +void quantize555ColorCombinedPerceptual(float *avg_col_in, int *enc_color, uint8 *avg_color) +{ + float dr, dg, db; + float kr, kg, kb; + float wR2, wG2, wB2; + uint8 low_color[3]; + uint8 high_color[3]; + float lowhightable[8]; + int q; + float kval = (float) (255.0/31.0); + + + // These are the values that we want to have: + float red_average, green_average, blue_average; + + int red_5bit_low, green_5bit_low, blue_5bit_low; + int red_5bit_high, green_5bit_high, blue_5bit_high; + + // These are the values that we approximate with: + int red_low, green_low, blue_low; + int red_high, green_high, blue_high; + + red_average = avg_col_in[0]; + green_average = avg_col_in[1]; + blue_average = avg_col_in[2]; + + // Find the 5-bit reconstruction levels red_low, red_high + // so that red_average is in interval [red_low, red_high]. + // (The same with green and blue.) + + red_5bit_low = (int) (red_average/kval); + green_5bit_low = (int) (green_average/kval); + blue_5bit_low = (int) (blue_average/kval); + + red_5bit_high = CLAMP(0, red_5bit_low + 1, 31); + green_5bit_high = CLAMP(0, green_5bit_low + 1, 31); + blue_5bit_high = CLAMP(0, blue_5bit_low + 1, 31); + + red_low = (red_5bit_low << 3) | (red_5bit_low >> 2); + green_low = (green_5bit_low << 3) | (green_5bit_low >> 2); + blue_low = (blue_5bit_low << 3) | (blue_5bit_low >> 2); + + red_high = (red_5bit_high << 3) | (red_5bit_high >> 2); + green_high = (green_5bit_high << 3) | (green_5bit_high >> 2); + blue_high = (blue_5bit_high << 3) | (blue_5bit_high >> 2); + + low_color[0] = red_5bit_low; + low_color[1] = green_5bit_low; + low_color[2] = blue_5bit_low; + + high_color[0] = red_5bit_high; + high_color[1] = green_5bit_high; + high_color[2] = blue_5bit_high; + + kr = (float)red_high - (float)red_low; + kg = (float)green_high - (float)green_low; + kb = (float)blue_high - (float)blue_low; + + // Note that dr, dg, and db are all negative. + + dr = red_low - red_average; + dg = green_low - green_average; + db = blue_low - blue_average; + + // Perceptual weights to use + wR2 = (float) PERCEPTUAL_WEIGHT_R_SQUARED; + wG2 = (float) PERCEPTUAL_WEIGHT_G_SQUARED; + wB2 = (float) PERCEPTUAL_WEIGHT_B_SQUARED; + + lowhightable[0] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[1] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+ 0) ); + lowhightable[2] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[3] = wR2*wG2*SQUARE( (dr+ 0) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[4] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+ 0) ) + wG2*wB2*SQUARE( (dg+kg) - (db+ 0) ); + lowhightable[5] = wR2*wG2*SQUARE( (dr+kr) - (dg+ 0) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+ 0) - (db+kb) ); + lowhightable[6] = wR2*wG2*SQUARE( (dr+ 0) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+ 0) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + lowhightable[7] = wR2*wG2*SQUARE( (dr+kr) - (dg+kg) ) + wR2*wB2*SQUARE( (dr+kr) - (db+kb) ) + wG2*wB2*SQUARE( (dg+kg) - (db+kb) ); + + + float min_value = lowhightable[0]; + int min_index = 0; + + for(q = 1; q<8; q++) + { + if(lowhightable[q] < min_value) + { + min_value = lowhightable[q]; + min_index = q; + } + } + + switch(min_index) + { + case 0: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 1: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = low_color[2]; + break; + case 2: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 3: + enc_color[0] = low_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 4: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = low_color[2]; + break; + case 5: + enc_color[0] = high_color[0]; + enc_color[1] = low_color[1]; + enc_color[2] = high_color[2]; + break; + case 6: + enc_color[0] = low_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + case 7: + enc_color[0] = high_color[0]; + enc_color[1] = high_color[1]; + enc_color[2] = high_color[2]; + break; + } + + // Expand 5-bit encoded color to 8-bit color + avg_color[0] = (enc_color[0] << 3) | (enc_color[0] >> 2); + avg_color[1] = (enc_color[1] << 3) | (enc_color[1] >> 2); + avg_color[2] = (enc_color[2] << 3) | (enc_color[2] >> 2); + +} + + +void compressBlockDiffFlipSlow(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + + + unsigned int compressed1_norm_diff, compressed2_norm_diff; + unsigned int compressed1_norm_444, compressed2_norm_444; + unsigned int compressed1_flip_diff, compressed2_flip_diff; + unsigned int compressed1_flip_444, compressed2_flip_444; + unsigned int best_err_norm_diff = 255*255*8*3; + unsigned int best_err_norm_444 = 255*255*8*3; + unsigned int best_err_flip_diff = 255*255*8*3; + unsigned int best_err_flip_444 = 255*255*8*3; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3], diff[3]; + int enc_base1[3], enc_base2[3]; + int enc_try1[3], enc_try2[3]; + int err; + unsigned int best_pixel_indices1_MSB=0; + unsigned int best_pixel_indices1_LSB=0; + unsigned int best_pixel_indices2_MSB=0; + unsigned int best_pixel_indices2_LSB=0; + + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + int minerr; + int dr1, dg1, db1, dr2, dg2, db2; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= SLOW_TRY_MIN) && (diff[0] <= SLOW_TRY_MAX) && (diff[1] >= SLOW_TRY_MIN) && (diff[1] <= SLOW_TRY_MAX) && (diff[2] >= SLOW_TRY_MIN) && (diff[2] <= SLOW_TRY_MAX) ) + { + diffbit = 1; + + enc_base1[0] = enc_color1[0]; + enc_base1[1] = enc_color1[1]; + enc_base1[2] = enc_color1[2]; + enc_base2[0] = enc_color2[0]; + enc_base2[1] = enc_color2[1]; + enc_base2[2] = enc_color2[2]; + + int err1[SLOW_SCAN_RANGE][SLOW_SCAN_RANGE][SLOW_SCAN_RANGE]; + int err2[SLOW_SCAN_RANGE][SLOW_SCAN_RANGE][SLOW_SCAN_RANGE]; + + // left part of block + for(dr1 = SLOW_SCAN_MIN; dr1> 2); + avg_color_quant1[1] = enc_try1[1] << 3 | (enc_try1[1] >> 2); + avg_color_quant1[2] = enc_try1[2] << 3 | (enc_try1[2] >> 2); + + // left part of block + err1[dr1+SLOW_SCAN_OFFSET][dg1+SLOW_SCAN_OFFSET][db1+SLOW_SCAN_OFFSET] = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + } + } + } + + // right part of block + for(dr2 = SLOW_SCAN_MIN; dr2> 2); + avg_color_quant2[1] = enc_try2[1] << 3 | (enc_try2[1] >> 2); + avg_color_quant2[2] = enc_try2[2] << 3 | (enc_try2[2] >> 2); + + // left part of block + err2[dr2+SLOW_SCAN_OFFSET][dg2+SLOW_SCAN_OFFSET][db2+SLOW_SCAN_OFFSET] = tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + } + } + } + + // Now see what combinations are both low in error and possible to + // encode differentially. + + minerr = 255*255*3*8*2; + + for(dr1 = SLOW_SCAN_MIN; dr1= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + // The diff is OK, calculate total error: + + err = err1[dr1+SLOW_SCAN_OFFSET][dg1+SLOW_SCAN_OFFSET][db1+SLOW_SCAN_OFFSET] + err2[dr2+SLOW_SCAN_OFFSET][dg2+SLOW_SCAN_OFFSET][db2+SLOW_SCAN_OFFSET]; + + if(err < minerr) + { + minerr = err; + + enc_color1[0] = enc_try1[0]; + enc_color1[1] = enc_try1[1]; + enc_color1[2] = enc_try1[2]; + enc_color2[0] = enc_try2[0]; + enc_color2[1] = enc_try2[1]; + enc_color2[2] = enc_try2[2]; + } + } + } + } + } + } + } + } + + best_err_norm_diff = minerr; + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + + // Pack bits into the first word. + + + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + compressed1_norm_diff = 0; + PUTBITSHIGH( compressed1_norm_diff, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm_diff, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm_diff, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm_diff, diff[2], 3, 42); + + + // left part of block + tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm_diff, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm_diff, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm_diff, 0, 1, 32); + + compressed2_norm_diff = 0; + PUTBITS( compressed2_norm_diff, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm_diff, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm_diff, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm_diff, (best_pixel_indices2_LSB ), 8, 15); + + } + // We should do this in any case... + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + + // Color for left block + + int besterr = 255*255*3*8; + int bestri = 0, bestgi = 0, bestbi = 0; + int ri, gi, bi; + + for(ri = 0; ri<15; ri++) + { + for(gi = 0; gi<15; gi++) + { + for(bi = 0; bi<15; bi++) + { + enc_color1[0] = ri; + enc_color1[1] = gi; + enc_color1[2] = bi; + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + + // left part of block + err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB,best_pixel_indices1_LSB); + + if(err= SLOW_TRY_MIN) && (diff[0] <= SLOW_TRY_MAX) && (diff[1] >= SLOW_TRY_MIN) && (diff[1] <= SLOW_TRY_MAX) && (diff[2] >= SLOW_TRY_MIN) && (diff[2] <= SLOW_TRY_MAX) ) + { + diffbit = 1; + + enc_base1[0] = enc_color1[0]; + enc_base1[1] = enc_color1[1]; + enc_base1[2] = enc_color1[2]; + enc_base2[0] = enc_color2[0]; + enc_base2[1] = enc_color2[1]; + enc_base2[2] = enc_color2[2]; + + int err1[SLOW_SCAN_RANGE][SLOW_SCAN_RANGE][SLOW_SCAN_RANGE]; + int err2[SLOW_SCAN_RANGE][SLOW_SCAN_RANGE][SLOW_SCAN_RANGE]; + + // upper part of block + for(dr1 = SLOW_SCAN_MIN; dr1> 2); + avg_color_quant1[1] = enc_try1[1] << 3 | (enc_try1[1] >> 2); + avg_color_quant1[2] = enc_try1[2] << 3 | (enc_try1[2] >> 2); + + // upper part of block + err1[dr1+SLOW_SCAN_OFFSET][dg1+SLOW_SCAN_OFFSET][db1+SLOW_SCAN_OFFSET] = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + } + } + } + + // lower part of block + for(dr2 = SLOW_SCAN_MIN; dr2> 2); + avg_color_quant2[1] = enc_try2[1] << 3 | (enc_try2[1] >> 2); + avg_color_quant2[2] = enc_try2[2] << 3 | (enc_try2[2] >> 2); + + // lower part of block + err2[dr2+SLOW_SCAN_OFFSET][dg2+SLOW_SCAN_OFFSET][db2+SLOW_SCAN_OFFSET] = tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + } + } + } + + // Now see what combinations are both low in error and possible to + // encode differentially. + + minerr = 255*255*3*8*2; + + for(dr1 = SLOW_SCAN_MIN; dr1= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + // The diff is OK, calculate total error: + + err = err1[dr1+SLOW_SCAN_OFFSET][dg1+SLOW_SCAN_OFFSET][db1+SLOW_SCAN_OFFSET] + err2[dr2+SLOW_SCAN_OFFSET][dg2+SLOW_SCAN_OFFSET][db2+SLOW_SCAN_OFFSET]; + + if(err < minerr) + { + minerr = err; + + enc_color1[0] = enc_try1[0]; + enc_color1[1] = enc_try1[1]; + enc_color1[2] = enc_try1[2]; + enc_color2[0] = enc_try2[0]; + enc_color2[1] = enc_try2[1]; + enc_color2[2] = enc_try2[2]; + } + } + } + } + } + } + } + } + + + flip_err = minerr; + + best_err_flip_diff = flip_err; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + // Pack bits into the first word. + + compressed1_flip_diff = 0; + PUTBITSHIGH( compressed1_flip_diff, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip_diff, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip_diff, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip_diff, diff[2], 3, 42); + + + + + // upper part of block + tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + + PUTBITSHIGH( compressed1_flip_diff, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip_diff, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip_diff, 1, 1, 32); + + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip_diff = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + } + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + + // Color for upper block + + int besterr = 255*255*3*8; + int bestri = 0, bestgi = 0, bestbi = 0; + int ri, gi, bi; + + for(ri = 0; ri<15; ri++) + { + for(gi = 0; gi<15; gi++) + { + for(bi = 0; bi<15; bi++) + { + enc_color1[0] = ri; + enc_color1[1] = gi; + enc_color1[2] = bi; + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + + // upper part of block + err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + if(err= MEDIUM_TRY_MIN) && (diff[0] <= MEDIUM_TRY_MAX) && (diff[1] >= MEDIUM_TRY_MIN) && (diff[1] <= MEDIUM_TRY_MAX) && (diff[2] >= MEDIUM_TRY_MIN) && (diff[2] <= MEDIUM_TRY_MAX) ) + { + diffbit = 1; + + enc_base1[0] = enc_color1[0]; + enc_base1[1] = enc_color1[1]; + enc_base1[2] = enc_color1[2]; + enc_base2[0] = enc_color2[0]; + enc_base2[1] = enc_color2[1]; + enc_base2[2] = enc_color2[2]; + + int err1[MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE]; + int err2[MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE]; + + // left part of block + for(dr1 = MEDIUM_SCAN_MIN; dr1> 2); + avg_color_quant1[1] = enc_try1[1] << 3 | (enc_try1[1] >> 2); + avg_color_quant1[2] = enc_try1[2] << 3 | (enc_try1[2] >> 2); + + // left part of block + err1[dr1+MEDIUM_SCAN_OFFSET][dg1+MEDIUM_SCAN_OFFSET][db1+MEDIUM_SCAN_OFFSET] = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + } + } + } + + // right part of block + for(dr2 = MEDIUM_SCAN_MIN; dr2> 2); + avg_color_quant2[1] = enc_try2[1] << 3 | (enc_try2[1] >> 2); + avg_color_quant2[2] = enc_try2[2] << 3 | (enc_try2[2] >> 2); + + // left part of block + err2[dr2+MEDIUM_SCAN_OFFSET][dg2+MEDIUM_SCAN_OFFSET][db2+MEDIUM_SCAN_OFFSET] = tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + } + } + } + + // Now see what combinations are both low in error and possible to + // encode differentially. + + minerr = 255*255*3*8*2; + + for(dr1 = MEDIUM_SCAN_MIN; dr1= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + // The diff is OK, calculate total error: + + err = err1[dr1+MEDIUM_SCAN_OFFSET][dg1+MEDIUM_SCAN_OFFSET][db1+MEDIUM_SCAN_OFFSET] + err2[dr2+MEDIUM_SCAN_OFFSET][dg2+MEDIUM_SCAN_OFFSET][db2+MEDIUM_SCAN_OFFSET]; + + if(err < minerr) + { + minerr = err; + + enc_color1[0] = enc_try1[0]; + enc_color1[1] = enc_try1[1]; + enc_color1[2] = enc_try1[2]; + enc_color2[0] = enc_try2[0]; + enc_color2[1] = enc_try2[1]; + enc_color2[2] = enc_try2[2]; + } + } + } + } + } + } + } + } + + best_err_norm_diff = minerr; + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + + // Pack bits into the first word. + + + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + compressed1_norm_diff = 0; + PUTBITSHIGH( compressed1_norm_diff, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm_diff, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm_diff, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm_diff, diff[2], 3, 42); + + + // left part of block + tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm_diff, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm_diff, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm_diff, 0, 1, 32); + + compressed2_norm_diff = 0; + PUTBITS( compressed2_norm_diff, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm_diff, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm_diff, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm_diff, (best_pixel_indices2_LSB ), 8, 15); + + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + + // Color for left block + + int besterr = 255*255*3*8; + int bestri = 0, bestgi = 0, bestbi = 0; + int ri, gi, bi; + + for(ri = 0; ri<15; ri++) + { + for(gi = 0; gi<15; gi++) + { + for(bi = 0; bi<15; bi++) + { + enc_color1[0] = ri; + enc_color1[1] = gi; + enc_color1[2] = bi; + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + + // left part of block + err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB,best_pixel_indices1_LSB); + + if(err= MEDIUM_TRY_MIN) && (diff[0] <= MEDIUM_TRY_MAX) && (diff[1] >= MEDIUM_TRY_MIN) && (diff[1] <= MEDIUM_TRY_MAX) && (diff[2] >= MEDIUM_TRY_MIN) && (diff[2] <= MEDIUM_TRY_MAX) ) + { + diffbit = 1; + + enc_base1[0] = enc_color1[0]; + enc_base1[1] = enc_color1[1]; + enc_base1[2] = enc_color1[2]; + enc_base2[0] = enc_color2[0]; + enc_base2[1] = enc_color2[1]; + enc_base2[2] = enc_color2[2]; + + int err1[MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE]; + int err2[MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE]; + + // upper part of block + for(dr1 = MEDIUM_SCAN_MIN; dr1> 2); + avg_color_quant1[1] = enc_try1[1] << 3 | (enc_try1[1] >> 2); + avg_color_quant1[2] = enc_try1[2] << 3 | (enc_try1[2] >> 2); + + // upper part of block + err1[dr1+MEDIUM_SCAN_OFFSET][dg1+MEDIUM_SCAN_OFFSET][db1+MEDIUM_SCAN_OFFSET] = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + } + } + } + + // lower part of block + for(dr2 = MEDIUM_SCAN_MIN; dr2> 2); + avg_color_quant2[1] = enc_try2[1] << 3 | (enc_try2[1] >> 2); + avg_color_quant2[2] = enc_try2[2] << 3 | (enc_try2[2] >> 2); + + // lower part of block + err2[dr2+MEDIUM_SCAN_OFFSET][dg2+MEDIUM_SCAN_OFFSET][db2+MEDIUM_SCAN_OFFSET] = tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + } + } + } + + // Now see what combinations are both low in error and possible to + // encode differentially. + + minerr = 255*255*3*8*2; + + for(dr1 = MEDIUM_SCAN_MIN; dr1= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + // The diff is OK, calculate total error: + + err = err1[dr1+MEDIUM_SCAN_OFFSET][dg1+MEDIUM_SCAN_OFFSET][db1+MEDIUM_SCAN_OFFSET] + err2[dr2+MEDIUM_SCAN_OFFSET][dg2+MEDIUM_SCAN_OFFSET][db2+MEDIUM_SCAN_OFFSET]; + + if(err < minerr) + { + minerr = err; + + enc_color1[0] = enc_try1[0]; + enc_color1[1] = enc_try1[1]; + enc_color1[2] = enc_try1[2]; + enc_color2[0] = enc_try2[0]; + enc_color2[1] = enc_try2[1]; + enc_color2[2] = enc_try2[2]; + } + } + } + } + } + } + } + } + + + flip_err = minerr; + + best_err_flip_diff = flip_err; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + // Pack bits into the first word. + + compressed1_flip_diff = 0; + PUTBITSHIGH( compressed1_flip_diff, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip_diff, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip_diff, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip_diff, diff[2], 3, 42); + + + + + // upper part of block + tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + + PUTBITSHIGH( compressed1_flip_diff, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip_diff, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip_diff, 1, 1, 32); + + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip_diff = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + + // Color for upper block + + int besterr = 255*255*3*8; + int bestri = 0, bestgi = 0, bestbi = 0; + int ri, gi, bi; + + for(ri = 0; ri<15; ri++) + { + for(gi = 0; gi<15; gi++) + { + for(bi = 0; bi<15; bi++) + { + enc_color1[0] = ri; + enc_color1[1] = gi; + enc_color1[2] = bi; + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + + // upper part of block + err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + if(err= SLOW_TRY_MIN) && (diff[0] <= SLOW_TRY_MAX) && (diff[1] >= SLOW_TRY_MIN) && (diff[1] <= SLOW_TRY_MAX) && (diff[2] >= SLOW_TRY_MIN) && (diff[2] <= SLOW_TRY_MAX) ) + { + diffbit = 1; + + enc_base1[0] = enc_color1[0]; + enc_base1[1] = enc_color1[1]; + enc_base1[2] = enc_color1[2]; + enc_base2[0] = enc_color2[0]; + enc_base2[1] = enc_color2[1]; + enc_base2[2] = enc_color2[2]; + + int err1[SLOW_SCAN_RANGE][SLOW_SCAN_RANGE][SLOW_SCAN_RANGE]; + int err2[SLOW_SCAN_RANGE][SLOW_SCAN_RANGE][SLOW_SCAN_RANGE]; + + // left part of block + for(dr1 = SLOW_SCAN_MIN; dr1> 2); + avg_color_quant1[1] = enc_try1[1] << 3 | (enc_try1[1] >> 2); + avg_color_quant1[2] = enc_try1[2] << 3 | (enc_try1[2] >> 2); + + // left part of block + err1[dr1+SLOW_SCAN_OFFSET][dg1+SLOW_SCAN_OFFSET][db1+SLOW_SCAN_OFFSET] = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + } + } + } + + // right part of block + for(dr2 = SLOW_SCAN_MIN; dr2> 2); + avg_color_quant2[1] = enc_try2[1] << 3 | (enc_try2[1] >> 2); + avg_color_quant2[2] = enc_try2[2] << 3 | (enc_try2[2] >> 2); + + // left part of block + err2[dr2+SLOW_SCAN_OFFSET][dg2+SLOW_SCAN_OFFSET][db2+SLOW_SCAN_OFFSET] = tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + } + } + } + + // Now see what combinations are both low in error and possible to + // encode differentially. + + minerr = 255*255*3*8*2; + + for(dr1 = SLOW_SCAN_MIN; dr1= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + // The diff is OK, calculate total error: + + err = err1[dr1+SLOW_SCAN_OFFSET][dg1+SLOW_SCAN_OFFSET][db1+SLOW_SCAN_OFFSET] + err2[dr2+SLOW_SCAN_OFFSET][dg2+SLOW_SCAN_OFFSET][db2+SLOW_SCAN_OFFSET]; + + if(err < minerr) + { + minerr = err; + + enc_color1[0] = enc_try1[0]; + enc_color1[1] = enc_try1[1]; + enc_color1[2] = enc_try1[2]; + enc_color2[0] = enc_try2[0]; + enc_color2[1] = enc_try2[1]; + enc_color2[2] = enc_try2[2]; + } + } + } + } + } + } + } + } + + best_err_norm_diff = minerr; + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + + // Pack bits into the first word. + + + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + compressed1_norm_diff = 0; + PUTBITSHIGH( compressed1_norm_diff, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm_diff, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm_diff, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm_diff, diff[2], 3, 42); + + + // left part of block + tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm_diff, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm_diff, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm_diff, 0, 1, 32); + + compressed2_norm_diff = 0; + PUTBITS( compressed2_norm_diff, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm_diff, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm_diff, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm_diff, (best_pixel_indices2_LSB ), 8, 15); + + + } + // We should do this in any case... + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + + // Color for left block + + int besterr = 255*255*3*8; + int bestri = 0, bestgi = 0, bestbi = 0; + int ri, gi, bi; + + for(ri = 0; ri<15; ri++) + { + for(gi = 0; gi<15; gi++) + { + for(bi = 0; bi<15; bi++) + { + enc_color1[0] = ri; + enc_color1[1] = gi; + enc_color1[2] = bi; + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + + // left part of block + err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB,best_pixel_indices1_LSB); + + if(err= SLOW_TRY_MIN) && (diff[0] <= SLOW_TRY_MAX) && (diff[1] >= SLOW_TRY_MIN) && (diff[1] <= SLOW_TRY_MAX) && (diff[2] >= SLOW_TRY_MIN) && (diff[2] <= SLOW_TRY_MAX) ) + { + diffbit = 1; + + enc_base1[0] = enc_color1[0]; + enc_base1[1] = enc_color1[1]; + enc_base1[2] = enc_color1[2]; + enc_base2[0] = enc_color2[0]; + enc_base2[1] = enc_color2[1]; + enc_base2[2] = enc_color2[2]; + + int err1[SLOW_SCAN_RANGE][SLOW_SCAN_RANGE][SLOW_SCAN_RANGE]; + int err2[SLOW_SCAN_RANGE][SLOW_SCAN_RANGE][SLOW_SCAN_RANGE]; + + // upper part of block + for(dr1 = SLOW_SCAN_MIN; dr1> 2); + avg_color_quant1[1] = enc_try1[1] << 3 | (enc_try1[1] >> 2); + avg_color_quant1[2] = enc_try1[2] << 3 | (enc_try1[2] >> 2); + + // upper part of block + err1[dr1+SLOW_SCAN_OFFSET][dg1+SLOW_SCAN_OFFSET][db1+SLOW_SCAN_OFFSET] = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + } + } + } + + // lower part of block + for(dr2 = SLOW_SCAN_MIN; dr2> 2); + avg_color_quant2[1] = enc_try2[1] << 3 | (enc_try2[1] >> 2); + avg_color_quant2[2] = enc_try2[2] << 3 | (enc_try2[2] >> 2); + + // lower part of block + err2[dr2+SLOW_SCAN_OFFSET][dg2+SLOW_SCAN_OFFSET][db2+SLOW_SCAN_OFFSET] = tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + } + } + } + + // Now see what combinations are both low in error and possible to + // encode differentially. + + minerr = 255*255*3*8*2; + + for(dr1 = SLOW_SCAN_MIN; dr1= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + // The diff is OK, calculate total error: + + err = err1[dr1+SLOW_SCAN_OFFSET][dg1+SLOW_SCAN_OFFSET][db1+SLOW_SCAN_OFFSET] + err2[dr2+SLOW_SCAN_OFFSET][dg2+SLOW_SCAN_OFFSET][db2+SLOW_SCAN_OFFSET]; + + if(err < minerr) + { + minerr = err; + + enc_color1[0] = enc_try1[0]; + enc_color1[1] = enc_try1[1]; + enc_color1[2] = enc_try1[2]; + enc_color2[0] = enc_try2[0]; + enc_color2[1] = enc_try2[1]; + enc_color2[2] = enc_try2[2]; + } + } + } + } + } + } + } + } + + + flip_err = minerr; + + best_err_flip_diff = flip_err; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + // Pack bits into the first word. + + compressed1_flip_diff = 0; + PUTBITSHIGH( compressed1_flip_diff, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip_diff, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip_diff, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip_diff, diff[2], 3, 42); + + + + + // upper part of block + tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + + PUTBITSHIGH( compressed1_flip_diff, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip_diff, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip_diff, 1, 1, 32); + + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip_diff = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + } + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + + // Color for upper block + + int besterr = 255*255*3*8; + int bestri = 0, bestgi = 0, bestbi = 0; + int ri, gi, bi; + + for(ri = 0; ri<15; ri++) + { + for(gi = 0; gi<15; gi++) + { + for(bi = 0; bi<15; bi++) + { + enc_color1[0] = ri; + enc_color1[1] = gi; + enc_color1[2] = bi; + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + + // upper part of block + err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + if(err= MEDIUM_TRY_MIN) && (diff[0] <= MEDIUM_TRY_MAX) && (diff[1] >= MEDIUM_TRY_MIN) && (diff[1] <= MEDIUM_TRY_MAX) && (diff[2] >= MEDIUM_TRY_MIN) && (diff[2] <= MEDIUM_TRY_MAX) ) + { + diffbit = 1; + + enc_base1[0] = enc_color1[0]; + enc_base1[1] = enc_color1[1]; + enc_base1[2] = enc_color1[2]; + enc_base2[0] = enc_color2[0]; + enc_base2[1] = enc_color2[1]; + enc_base2[2] = enc_color2[2]; + + int err1[MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE]; + int err2[MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE]; + + // left part of block + for(dr1 = MEDIUM_SCAN_MIN; dr1> 2); + avg_color_quant1[1] = enc_try1[1] << 3 | (enc_try1[1] >> 2); + avg_color_quant1[2] = enc_try1[2] << 3 | (enc_try1[2] >> 2); + + // left part of block + err1[dr1+MEDIUM_SCAN_OFFSET][dg1+MEDIUM_SCAN_OFFSET][db1+MEDIUM_SCAN_OFFSET] = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + } + } + } + + // right part of block + for(dr2 = MEDIUM_SCAN_MIN; dr2> 2); + avg_color_quant2[1] = enc_try2[1] << 3 | (enc_try2[1] >> 2); + avg_color_quant2[2] = enc_try2[2] << 3 | (enc_try2[2] >> 2); + + // left part of block + err2[dr2+MEDIUM_SCAN_OFFSET][dg2+MEDIUM_SCAN_OFFSET][db2+MEDIUM_SCAN_OFFSET] = tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + } + } + } + + // Now see what combinations are both low in error and possible to + // encode differentially. + + minerr = 255*255*3*8*2; + + for(dr1 = MEDIUM_SCAN_MIN; dr1= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + // The diff is OK, calculate total error: + + err = err1[dr1+MEDIUM_SCAN_OFFSET][dg1+MEDIUM_SCAN_OFFSET][db1+MEDIUM_SCAN_OFFSET] + err2[dr2+MEDIUM_SCAN_OFFSET][dg2+MEDIUM_SCAN_OFFSET][db2+MEDIUM_SCAN_OFFSET]; + + if(err < minerr) + { + minerr = err; + + enc_color1[0] = enc_try1[0]; + enc_color1[1] = enc_try1[1]; + enc_color1[2] = enc_try1[2]; + enc_color2[0] = enc_try2[0]; + enc_color2[1] = enc_try2[1]; + enc_color2[2] = enc_try2[2]; + } + } + } + } + } + } + } + } + + best_err_norm_diff = minerr; + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + + // Pack bits into the first word. + + + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + compressed1_norm_diff = 0; + PUTBITSHIGH( compressed1_norm_diff, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm_diff, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm_diff, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm_diff, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm_diff, diff[2], 3, 42); + + + // left part of block + tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm_diff, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm_diff, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm_diff, 0, 1, 32); + + compressed2_norm_diff = 0; + PUTBITS( compressed2_norm_diff, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm_diff, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm_diff, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm_diff, (best_pixel_indices2_LSB ), 8, 15); + + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + + // Color for left block + + int besterr = 255*255*3*8; + int bestri = 0, bestgi = 0, bestbi = 0; + int ri, gi, bi; + + for(ri = 0; ri<15; ri++) + { + for(gi = 0; gi<15; gi++) + { + for(bi = 0; bi<15; bi++) + { + enc_color1[0] = ri; + enc_color1[1] = gi; + enc_color1[2] = bi; + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + + // left part of block + err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB,best_pixel_indices1_LSB); + + if(err= MEDIUM_TRY_MIN) && (diff[0] <= MEDIUM_TRY_MAX) && (diff[1] >= MEDIUM_TRY_MIN) && (diff[1] <= MEDIUM_TRY_MAX) && (diff[2] >= MEDIUM_TRY_MIN) && (diff[2] <= MEDIUM_TRY_MAX) ) + { + diffbit = 1; + + enc_base1[0] = enc_color1[0]; + enc_base1[1] = enc_color1[1]; + enc_base1[2] = enc_color1[2]; + enc_base2[0] = enc_color2[0]; + enc_base2[1] = enc_color2[1]; + enc_base2[2] = enc_color2[2]; + + int err1[MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE]; + int err2[MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE][MEDIUM_SCAN_RANGE]; + + // upper part of block + for(dr1 = MEDIUM_SCAN_MIN; dr1> 2); + avg_color_quant1[1] = enc_try1[1] << 3 | (enc_try1[1] >> 2); + avg_color_quant1[2] = enc_try1[2] << 3 | (enc_try1[2] >> 2); + + // upper part of block + err1[dr1+MEDIUM_SCAN_OFFSET][dg1+MEDIUM_SCAN_OFFSET][db1+MEDIUM_SCAN_OFFSET] = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + } + } + } + + // lower part of block + for(dr2 = MEDIUM_SCAN_MIN; dr2> 2); + avg_color_quant2[1] = enc_try2[1] << 3 | (enc_try2[1] >> 2); + avg_color_quant2[2] = enc_try2[2] << 3 | (enc_try2[2] >> 2); + + // lower part of block + err2[dr2+MEDIUM_SCAN_OFFSET][dg2+MEDIUM_SCAN_OFFSET][db2+MEDIUM_SCAN_OFFSET] = tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + } + } + } + + // Now see what combinations are both low in error and possible to + // encode differentially. + + minerr = 255*255*3*8*2; + + for(dr1 = MEDIUM_SCAN_MIN; dr1= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + // The diff is OK, calculate total error: + + err = err1[dr1+MEDIUM_SCAN_OFFSET][dg1+MEDIUM_SCAN_OFFSET][db1+MEDIUM_SCAN_OFFSET] + err2[dr2+MEDIUM_SCAN_OFFSET][dg2+MEDIUM_SCAN_OFFSET][db2+MEDIUM_SCAN_OFFSET]; + + if(err < minerr) + { + minerr = err; + + enc_color1[0] = enc_try1[0]; + enc_color1[1] = enc_try1[1]; + enc_color1[2] = enc_try1[2]; + enc_color2[0] = enc_try2[0]; + enc_color2[1] = enc_try2[1]; + enc_color2[2] = enc_try2[2]; + } + } + } + } + } + } + } + } + + + flip_err = minerr; + + best_err_flip_diff = flip_err; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + // Pack bits into the first word. + + compressed1_flip_diff = 0; + PUTBITSHIGH( compressed1_flip_diff, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip_diff, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip_diff, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip_diff, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip_diff, diff[2], 3, 42); + + + + + // upper part of block + tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + + PUTBITSHIGH( compressed1_flip_diff, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip_diff, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip_diff, 1, 1, 32); + + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip_diff = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + + // Color for upper block + + int besterr = 255*255*3*8; + int bestri = 0, bestgi = 0, bestbi = 0; + int ri, gi, bi; + + for(ri = 0; ri<15; ri++) + { + for(gi = 0; gi<15; gi++) + { + for(bi = 0; bi<15; bi++) + { + enc_color1[0] = ri; + enc_color1[1] = gi; + enc_color1[2] = bi; + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + + // upper part of block + err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + if(err= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + norm_err = 0; + + // left part of block + norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + eps = (float) 0.0001; + + enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); + enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); + enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); + enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); + enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); + enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + + // Pack bits into the first word. + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + + // left part of block + norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + + } + + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); + + + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + eps = (float) 0.0001; + + enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); + enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); + enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); + enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); + enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); + enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + + } + + // Now lets see which is the best table to use. Only 8 tables are possible. + + if(norm_err <= flip_err) + { + + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + + } + else + { + + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + } +} + +void compressBlockDiffFlipCombined(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3], diff[3]; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + float eps; + + uint8 dummy[3]; + + quantize555ColorCombined(avg_color_float1, enc_color1, dummy); + quantize555ColorCombined(avg_color_float2, enc_color2, dummy); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + norm_err = 0; + + // left part of block + norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + eps = (float) 0.0001; + + uint8 dummy[3]; + quantize444ColorCombined(avg_color_float1, enc_color1, dummy); + quantize444ColorCombined(avg_color_float2, enc_color2, dummy); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + + // Pack bits into the first word. + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + + // left part of block + norm_err = tryalltables_3bittable2x4(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + + } + + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + quantize555ColorCombined(avg_color_float1, enc_color1, dummy); + quantize555ColorCombined(avg_color_float2, enc_color2, dummy); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); + + + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + eps = (float) 0.0001; + + uint8 dummy[3]; + quantize444ColorCombined(avg_color_float1, enc_color1, dummy); + quantize444ColorCombined(avg_color_float2, enc_color2, dummy); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + + } + + // Now lets see which is the best table to use. Only 8 tables are possible. + + if(norm_err <= flip_err) + { + + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + + } + else + { + + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + } +} + +void compressBlockDiffFlipAveragePerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3], diff[3]; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + + float eps; + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + norm_err = 0; + + // left part of block + norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + eps = (float) 0.0001; + + enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); + enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); + enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); + enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); + enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); + enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + + // Pack bits into the first word. + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + + // left part of block + norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + + } + + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + enc_color1[0] = int( JAS_ROUND(31.0*avg_color_float1[0]/255.0) ); + enc_color1[1] = int( JAS_ROUND(31.0*avg_color_float1[1]/255.0) ); + enc_color1[2] = int( JAS_ROUND(31.0*avg_color_float1[2]/255.0) ); + enc_color2[0] = int( JAS_ROUND(31.0*avg_color_float2[0]/255.0) ); + enc_color2[1] = int( JAS_ROUND(31.0*avg_color_float2[1]/255.0) ); + enc_color2[2] = int( JAS_ROUND(31.0*avg_color_float2[2]/255.0) ); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); + + + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + eps = (float) 0.0001; + + enc_color1[0] = int( ((float) avg_color_float1[0] / (17.0)) +0.5 + eps); + enc_color1[1] = int( ((float) avg_color_float1[1] / (17.0)) +0.5 + eps); + enc_color1[2] = int( ((float) avg_color_float1[2] / (17.0)) +0.5 + eps); + enc_color2[0] = int( ((float) avg_color_float2[0] / (17.0)) +0.5 + eps); + enc_color2[1] = int( ((float) avg_color_float2[1] / (17.0)) +0.5 + eps); + enc_color2[2] = int( ((float) avg_color_float2[2] / (17.0)) +0.5 + eps); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + + } + + // Now lets see which is the best table to use. Only 8 tables are possible. + + if(norm_err <= flip_err) + { + + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + + } + else + { + + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + } +} + +void compressBlockDiffFlipCombinedPerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2) +{ + + unsigned int compressed1_norm, compressed2_norm; + unsigned int compressed1_flip, compressed2_flip; + uint8 avg_color_quant1[3], avg_color_quant2[3]; + + float avg_color_float1[3],avg_color_float2[3]; + int enc_color1[3], enc_color2[3], diff[3]; + unsigned int best_table1=0, best_table2=0; + int diffbit; + + int norm_err=0; + int flip_err=0; + + // First try normal blocks 2x4: + + computeAverageColor2x4noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor2x4noQuantFloat(img,width,height,startx+2,starty,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + + float eps; + + uint8 dummy[3]; + + quantize555ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); + quantize555ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + // ETC1_RGB8_OES: + // + // a) bit layout in bits 63 through 32 if diffbit = 0 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // b) bit layout in bits 63 through 32 if diffbit = 1 + // + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | dcol 2 | base col1 | dcol 2 | base col 1 | dcol 2 | table | table |diff|flip| + // | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + // + // c) bit layout in bits 31 through 0 (in both cases) + // + // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // -------------------------------------------------------------------------------------------------- + // | most significant pixel index bits | least significant pixel index bits | + // | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a | + // -------------------------------------------------------------------------------------------------- + + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_norm, diff[0], 3, 58); + PUTBITSHIGH( compressed1_norm, diff[1], 3, 50); + PUTBITSHIGH( compressed1_norm, diff[2], 3, 42); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + norm_err = 0; + + // left part of block + norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + + eps = (float) 0.0001; + + quantize444ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); + quantize444ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + + // Pack bits into the first word. + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + compressed1_norm = 0; + PUTBITSHIGH( compressed1_norm, diffbit, 1, 33); + PUTBITSHIGH( compressed1_norm, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_norm, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_norm, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_norm, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_norm, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_norm, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + + // left part of block + norm_err = tryalltables_3bittable2x4percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + + // right part of block + norm_err += tryalltables_3bittable2x4percep(img,width,height,startx+2,starty,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_norm, best_table1, 3, 39); + PUTBITSHIGH( compressed1_norm, best_table2, 3, 36); + PUTBITSHIGH( compressed1_norm, 0, 1, 32); + + compressed2_norm = 0; + PUTBITS( compressed2_norm, (best_pixel_indices1_MSB ), 8, 23); + PUTBITS( compressed2_norm, (best_pixel_indices2_MSB ), 8, 31); + PUTBITS( compressed2_norm, (best_pixel_indices1_LSB ), 8, 7); + PUTBITS( compressed2_norm, (best_pixel_indices2_LSB ), 8, 15); + + + } + + // Now try flipped blocks 4x2: + + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty,avg_color_float1); + computeAverageColor4x2noQuantFloat(img,width,height,startx,starty+2,avg_color_float2); + + // First test if avg_color1 is similar enough to avg_color2 so that + // we can use differential coding of colors. + + + quantize555ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); + quantize555ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + if( (diff[0] >= -4) && (diff[0] <= 3) && (diff[1] >= -4) && (diff[1] <= 3) && (diff[2] >= -4) && (diff[2] <= 3) ) + { + diffbit = 1; + + // The difference to be coded: + + diff[0] = enc_color2[0]-enc_color1[0]; + diff[1] = enc_color2[1]-enc_color1[1]; + diff[2] = enc_color2[2]-enc_color1[2]; + + avg_color_quant1[0] = enc_color1[0] << 3 | (enc_color1[0] >> 2); + avg_color_quant1[1] = enc_color1[1] << 3 | (enc_color1[1] >> 2); + avg_color_quant1[2] = enc_color1[2] << 3 | (enc_color1[2] >> 2); + avg_color_quant2[0] = enc_color2[0] << 3 | (enc_color2[0] >> 2); + avg_color_quant2[1] = enc_color2[1] << 3 | (enc_color2[1] >> 2); + avg_color_quant2[2] = enc_color2[2] << 3 | (enc_color2[2] >> 2); + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 5, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 5, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 5, 47); + PUTBITSHIGH( compressed1_flip, diff[0], 3, 58); + PUTBITSHIGH( compressed1_flip, diff[1], 3, 50); + PUTBITSHIGH( compressed1_flip, diff[2], 3, 42); + + + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + + } + else + { + diffbit = 0; + // The difference is bigger than what fits in 555 plus delta-333, so we will have + // to deal with 444 444. + eps = (float) 0.0001; + + quantize444ColorCombinedPerceptual(avg_color_float1, enc_color1, dummy); + quantize444ColorCombinedPerceptual(avg_color_float2, enc_color2, dummy); + + avg_color_quant1[0] = enc_color1[0] << 4 | enc_color1[0]; + avg_color_quant1[1] = enc_color1[1] << 4 | enc_color1[1]; + avg_color_quant1[2] = enc_color1[2] << 4 | enc_color1[2]; + avg_color_quant2[0] = enc_color2[0] << 4 | enc_color2[0]; + avg_color_quant2[1] = enc_color2[1] << 4 | enc_color2[1]; + avg_color_quant2[2] = enc_color2[2] << 4 | enc_color2[2]; + + // 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 + // --------------------------------------------------------------------------------------------------- + // | base col1 | base col2 | base col1 | base col2 | base col1 | base col2 | table | table |diff|flip| + // | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit | + // --------------------------------------------------------------------------------------------------- + + + // Pack bits into the first word. + + compressed1_flip = 0; + PUTBITSHIGH( compressed1_flip, diffbit, 1, 33); + PUTBITSHIGH( compressed1_flip, enc_color1[0], 4, 63); + PUTBITSHIGH( compressed1_flip, enc_color1[1], 4, 55); + PUTBITSHIGH( compressed1_flip, enc_color1[2], 4, 47); + PUTBITSHIGH( compressed1_flip, enc_color2[0], 4, 59); + PUTBITSHIGH( compressed1_flip, enc_color2[1], 4, 51); + PUTBITSHIGH( compressed1_flip, enc_color2[2], 4, 43); + + unsigned int best_pixel_indices1_MSB; + unsigned int best_pixel_indices1_LSB; + unsigned int best_pixel_indices2_MSB; + unsigned int best_pixel_indices2_LSB; + + // upper part of block + flip_err = tryalltables_3bittable4x2percep(img,width,height,startx,starty,avg_color_quant1,best_table1,best_pixel_indices1_MSB, best_pixel_indices1_LSB); + // lower part of block + flip_err += tryalltables_3bittable4x2percep(img,width,height,startx,starty+2,avg_color_quant2,best_table2,best_pixel_indices2_MSB, best_pixel_indices2_LSB); + + PUTBITSHIGH( compressed1_flip, best_table1, 3, 39); + PUTBITSHIGH( compressed1_flip, best_table2, 3, 36); + PUTBITSHIGH( compressed1_flip, 1, 1, 32); + + best_pixel_indices1_MSB |= (best_pixel_indices2_MSB << 2); + best_pixel_indices1_LSB |= (best_pixel_indices2_LSB << 2); + + compressed2_flip = ((best_pixel_indices1_MSB & 0xffff) << 16) | (best_pixel_indices1_LSB & 0xffff); + + + } + + // Now lets see which is the best table to use. Only 8 tables are possible. + + if(norm_err <= flip_err) + { + + compressed1 = compressed1_norm | 0; + compressed2 = compressed2_norm; + + } + else + { + + compressed1 = compressed1_flip | 1; + compressed2 = compressed2_flip; + } +} + +double calcBlockErrorRGB(uint8 *img, uint8 *imgdec, int width, int height, int startx, int starty) +{ + int xx,yy; + double err; + + err = 0; + + for(xx = startx; xx< startx+4; xx++) + { + for(yy = starty; yy> 24) | + ((x & 0x00FF0000) >> 8) | + ((x & 0x0000FF00) << 8) | + ((x & 0x000000FF) << 24); +} + +void CompressBlock(const uint8 *in, int in_width, uint8 *output, int quality) { + uint8 rgb[4 * 4 * 3]; + for (int y = 0; y < 4; y++) { + for (int x = 0; x < 4; x++) { + rgb[(y * 4 + x) * 3 + 0] = in[(y * in_width + x) * 4 + 0]; + rgb[(y * 4 + x) * 3 + 1] = in[(y * in_width + x) * 4 + 1]; + rgb[(y * 4 + x) * 3 + 2] = in[(y * in_width + x) * 4 + 2]; + } + } + + uint8 imgdec[4 * 4 * 3]={0}; // temporary storage used by some of the functions. + unsigned int compressed1 = 0, compressed2 = 0; + switch (quality) { + case 0: + compressBlockDiffFlipFast(rgb, imgdec, 4, 4, 0, 0, compressed1, compressed2); + break; + case 1: + compressBlockDiffFlipFastPerceptual(rgb, imgdec, 4, 4, 0, 0, compressed1, compressed2); + break; + case 2: + compressBlockDiffFlipMedium(rgb, 4, 4, 0, 0, compressed1, compressed2); + break; + case 3: + compressBlockDiffFlipMediumPerceptual(rgb, 4, 4, 0, 0, compressed1, compressed2); + break; + case 4: + compressBlockDiffFlipSlow(rgb, 4, 4, 0, 0, compressed1, compressed2); + break; + case 5: + compressBlockDiffFlipSlowPerceptual(rgb, 4, 4, 0, 0, compressed1, compressed2); + break; + default: + fprintf(stderr, "ETC1: Compression level %i not defined", quality); + return; + } + compressed1 = bswap(compressed1); + compressed2 = bswap(compressed2); + memcpy(output, &compressed1, 4); + memcpy(output + 4, &compressed2, 4); + memset(rgb, 0, 4 * 4 * 3); +} diff --git a/ext/etcpack/etcpack.h b/ext/etcpack/etcpack.h new file mode 100644 index 0000000000..f8332ad17e --- /dev/null +++ b/ext/etcpack/etcpack.h @@ -0,0 +1,26 @@ +#pragma once + +#define PERCEPTUAL_WEIGHT_R_SQUARED 0.299 +#define PERCEPTUAL_WEIGHT_G_SQUARED 0.587 +#define PERCEPTUAL_WEIGHT_B_SQUARED 0.114 + +typedef unsigned char uint8; + +// These functions take RGB888. +void compressBlockDiffFlipSlow(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2); + +void compressBlockDiffFlipMedium(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2); + +void compressBlockDiffFlipFast(uint8 *img, uint8 *imgdec,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2); + +void compressBlockDiffFlipSlowPerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2); + +void compressBlockDiffFlipMediumPerceptual(uint8 *img,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2); + +void compressBlockDiffFlipFastPerceptual(uint8 *img, uint8 *imgdec,int width,int height,int startx,int starty, unsigned int &compressed1, unsigned int &compressed2); + + +// This one takes RGBA8888 and converts to RGB first. +// Writes 64 bits of compressed data to output[0..7]. +// Quality is 0 to 5. The odd numbers use perceptual metrics. +void CompressBlock(const uint8 *in, int in_width, uint8 *output, int quality); diff --git a/ext/etcpack/etctool.cpp b/ext/etcpack/etctool.cpp new file mode 100644 index 0000000000..6e7692826c --- /dev/null +++ b/ext/etcpack/etctool.cpp @@ -0,0 +1,1612 @@ +// Contains a lot of code from etcpack.cpp. +// Extracted by Henrik RydgÃ¥rd. + + +#include +#include +#include +#include + +#include "etcpack.h" +#include "etcdec.h" +#include "image.h" + +enum{FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T0, FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T1}; + +int orientation; + +int ktx_mode; + +typedef struct KTX_header_t +{ + uint8 identifier[12]; + unsigned int endianness; + unsigned int glType; + unsigned int glTypeSize; + unsigned int glFormat; + unsigned int glInternalFormat; + unsigned int glBaseInternalFormat; + unsigned int pixelWidth; + unsigned int pixelHeight; + unsigned int pixelDepth; + unsigned int numberOfArrayElements; + unsigned int numberOfFaces; + unsigned int numberOfMipmapLevels; + unsigned int bytesOfKeyValueData; +} +KTX_header; +#define KTX_IDENTIFIER_REF { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A } + +#define KTX_ENDIAN_REF (0x04030201) +#define KTX_ENDIAN_REF_REV (0x01020304) + +int ktx_identifier[] = KTX_IDENTIFIER_REF; + + +enum {GL_R=0x1903,GL_RG=0x8227,GL_RGB=0x1907,GL_RGBA=0x1908}; +enum {GL_ETC1_RGB8_OES=0x8d64}; + +#define ETC1_RGB_NO_MIPMAPS 0 +#define ETC1_RGBA_NO_MIPMAPS 1 +#define ETC1_RGB_MIPMAPS 2 +#define ETC1_RGBA_MIPMAPS 3 + +// The error metric Wr Wg Wb should be definied so that Wr^2 + Wg^2 + Wb^2 = 1. +// Hence it is easier to first define the squared values and derive the weights +// as their square-roots. + + +// Alternative weights +//#define PERCEPTUAL_WEIGHT_R_SQUARED 0.3086 +//#define PERCEPTUAL_WEIGHT_G_SQUARED 0.6094 +//#define PERCEPTUAL_WEIGHT_B_SQUARED 0.082 + +#define PERCEPTUAL_WEIGHT_R (sqrt(PERCEPTUAL_WEIGHT_R_SQUARED)) +#define PERCEPTUAL_WEIGHT_G (sqrt(PERCEPTUAL_WEIGHT_G_SQUARED)) +#define PERCEPTUAL_WEIGHT_B (sqrt(PERCEPTUAL_WEIGHT_B_SQUARED)) + + +double wR = PERCEPTUAL_WEIGHT_R; +double wG = PERCEPTUAL_WEIGHT_G; +double wB = PERCEPTUAL_WEIGHT_B; + +double wR2 = PERCEPTUAL_WEIGHT_R_SQUARED; +double wG2 = PERCEPTUAL_WEIGHT_G_SQUARED; +double wB2 = PERCEPTUAL_WEIGHT_B_SQUARED; + +void read_big_endian_2byte_word(unsigned short *blockadr, FILE *f) +{ + uint8 bytes[2]; + unsigned short block; + + fread(&bytes[0], 1, 1, f); + fread(&bytes[1], 1, 1, f); + + block = 0; + + block |= bytes[0]; + block = block << 8; + block |= bytes[1]; + + blockadr[0] = block; +} + +void read_big_endian_4byte_word(unsigned int *blockadr, FILE *f) +{ + uint8 bytes[4]; + unsigned int block; + + fread(&bytes[0], 1, 1, f); + fread(&bytes[1], 1, 1, f); + fread(&bytes[2], 1, 1, f); + fread(&bytes[3], 1, 1, f); + + block = 0; + + block |= bytes[0]; + block = block << 8; + block |= bytes[1]; + block = block << 8; + block |= bytes[2]; + block = block << 8; + block |= bytes[3]; + + blockadr[0] = block; +} + + + +bool fileExist(const char *filename) +{ + FILE *f=NULL; + if((f=fopen(filename,"rb"))!=NULL) + { + fclose(f); + return true; + } + return false; +} + +bool expandToWidthDivByFour(uint8 *&img, int width, int height, int &expandedwidth, int &expandedheight) +{ + int wdiv4; + int xx, yy; + uint8 *newimg; + + wdiv4 = width /4; + if( !(wdiv4 *4 == width) ) + { + expandedwidth = (wdiv4 + 1)*4; + expandedheight = height; + newimg=(uint8*)malloc(3*expandedwidth*expandedheight); + if(!newimg) + { + printf("Could not allocate memory to expand width\n"); + return false; + } + + // First copy image + for(yy = 0; yy> 8) & 0xff; + bytes[1] = (block >> 0) & 0xff; + + fwrite(&bytes[0],1,1,f); + fwrite(&bytes[1],1,1,f); +} + +void write_big_endian_4byte_word(unsigned int *blockadr, FILE *f) +{ + uint8 bytes[4]; + unsigned int block; + + block = blockadr[0]; + + bytes[0] = (block >> 24) & 0xff; + bytes[1] = (block >> 16) & 0xff; + bytes[2] = (block >> 8) & 0xff; + bytes[3] = (block >> 0) & 0xff; + + fwrite(&bytes[0],1,1,f); + fwrite(&bytes[1],1,1,f); + fwrite(&bytes[2],1,1,f); + fwrite(&bytes[3],1,1,f); +} + + +int find_pos_of_extension(const char *src) +{ + int q=strlen(src); + while(q>=0) // find file name extension + { + if(src[q]=='.') break; + q--; + } + if(q<0) + return -1; + else + return q; +} + +bool readSrcFile(const char *filename,uint8 *&img,int &width,int &height, int &expandedwidth, int &expandedheight) +{ + int w1,h1; + int wdiv4, hdiv4; + char str[255]; + + + // Delete temp file if it exists. + if(fileExist("tmp.ppm")) + { + sprintf(str, "del tmp.ppm\n"); + system(str); + } + + + int q = find_pos_of_extension(filename); + if(!strcmp(&filename[q],".ppm")) + { + // Already a .ppm file. Just copy. + sprintf(str,"copy %s tmp.ppm \n", filename); + printf("Copying source file %s to tmp.ppm\n", filename); + } + else + { + // Converting from other format to .ppm + // + // Use your favorite command line image converter program, + // for instance Image Magick. Just make sure the syntax can + // be written as below: + // + // C:\imconv source.jpg dest.ppm + // + sprintf(str,"imconv %s tmp.ppm\n", filename); + printf("Converting source file from %s to .ppm\n", filename); + } + // Execute system call + system(str); + + bool FLIP; + if(orientation == FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T0) + FLIP = false; + else if(orientation == FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T1) + FLIP = true; + else + { + printf("orientation error\n"); + exit(1); + } + + if(fReadPPM("tmp.ppm",w1,h1,img,FLIP)) + { + width=w1; + height=h1; + system("del tmp.ppm"); + + // Width must be divisible by 2 and height must be + // divisible by 4. Otherwise, we will not compress + // the image. + + wdiv4 = width / 4; + hdiv4 = height / 4; + + expandedwidth = width; + expandedheight = height; + + if( !(wdiv4 * 4 == width) ) + { + printf(" Width = %d is not divisible by four... ", width); + printf(" expanding image in x-dir... "); + if(expandToWidthDivByFour(img, width, height, expandedwidth, expandedheight)) + { + printf("OK.\n"); + } + else + { + printf("\n Error: could not expand image\n"); + return false; + } + } + if( !(hdiv4 * 4 == height)) + { + printf(" Height = %d is not divisible by four... ", height); + printf(" expanding image in y-dir..."); + if(expandToHeightDivByFour(img, expandedwidth, height, expandedwidth, expandedheight)) + { + printf("OK.\n"); + } + else + { + printf("\n Error: could not expand image\n"); + return false; + } + } + if(!(expandedwidth == width && expandedheight == height)) + printf("Active pixels: %dx%d. Expanded image: %dx%d\n",width,height,expandedwidth,expandedheight); + return true; + } + return false; + +} +bool readSrcFileNoExpand(const char *filename,uint8 *&img,int &width,int &height) +{ + int w1,h1; + char str[255]; + + + // Delete temp file if it exists. + if(fileExist("tmp.ppm")) + { + sprintf(str, "del tmp.ppm\n"); + system(str); + } + + + int q = find_pos_of_extension(filename); + if(!strcmp(&filename[q],".ppm")) + { + // Already a .ppm file. Just copy. + sprintf(str,"copy %s tmp.ppm \n", filename); + printf("Copying source file %s to tmp.ppm\n", filename); + } + else + { + // Converting from other format to .ppm + // + // Use your favorite command line image converter program, + // for instance Image Magick. Just make sure the syntax can + // be written as below: + // + // C:\imconv source.jpg dest.ppm + // + sprintf(str,"imconv %s tmp.ppm\n", filename); + printf("Converting source file from %s to .ppm\n", filename); + } + // Execute system call + system(str); + + // The current function is only used when comparing two ppm files --- we don't need to flip them. Hence reverse_y is false + if(fReadPPM("tmp.ppm",w1,h1,img,false)) + { + width=w1; + height=h1; + system("del tmp.ppm"); + + return true; + } + return false; + +} + + +void compressImageFile(uint8 *img,int width,int height,char *dstfile, int expandedwidth, int expandedheight, int action) +{ + FILE *f; + int x,y,w,h; + unsigned int block1, block2; + unsigned short wi, hi; + unsigned char magic[4]; + unsigned char version[2]; + unsigned short texture_type; + uint8 *imgdec; + + imgdec = (unsigned char*) malloc(expandedwidth*expandedheight*3); + if(!imgdec) + { + printf("Could not allocate decompression buffer --- exiting\n"); + exit(1); + } + + magic[0] = 'P'; magic[1] = 'K'; magic[2] = 'M'; magic[3] = ' '; + version[0] = '1'; version[1] = '0'; + texture_type = ETC1_RGB_NO_MIPMAPS; + + if((f=fopen(dstfile,"wb"))) + { + w=expandedwidth/4; w*=4; + h=expandedheight/4; h*=4; + wi = w; + hi = h; + + if(ktx_mode) + { + printf("Outputting to .kxt file...\n"); + //.ktx file: KTX header followed by compressed binary data. + KTX_header header; + //identifier + for(int i=0; i<12; i++) + { + header.identifier[i]=ktx_identifier[i]; + } + //endianess int.. if this comes out reversed, all of the other ints will too. + header.endianness=KTX_ENDIAN_REF; + + //these values are always 0/1 for compressed textures. + header.glType=0; + header.glTypeSize=1; + header.glFormat=0; + + header.pixelWidth=width; + header.pixelHeight=height; + header.pixelDepth=0; + + //we only support single non-mipmapped non-cubemap textures.. + header.numberOfArrayElements=0; + header.numberOfFaces=1; + header.numberOfMipmapLevels=1; + + //and no metadata.. + header.bytesOfKeyValueData=0; + + int halfbytes=1; + //header.glInternalFormat=? + //header.glBaseInternalFormat=? + if(texture_type==ETC1_RGB_NO_MIPMAPS) + { + header.glBaseInternalFormat=GL_RGB; + header.glInternalFormat=GL_ETC1_RGB8_OES; + } + else + { + printf("internal error: bad format!\n"); + exit(1); + } + //write header + fwrite(&header,sizeof(KTX_header),1,f); + + //write size of compressed data.. which depend on the expanded size.. + unsigned int imagesize=(w*h*halfbytes)/2; + fwrite(&imagesize,sizeof(int),1,f); + } + else + { + printf("outputting to .pkm file...\n"); + // Write magic number + fwrite(&magic[0], sizeof(unsigned char), 1, f); + fwrite(&magic[1], sizeof(unsigned char), 1, f); + fwrite(&magic[2], sizeof(unsigned char), 1, f); + fwrite(&magic[3], sizeof(unsigned char), 1, f); + + // Write version + fwrite(&version[0], sizeof(unsigned char), 1, f); + fwrite(&version[1], sizeof(unsigned char), 1, f); + + // Write texture type + write_big_endian_2byte_word(&texture_type, f); + + // Write binary header: the width and height as unsigned 16-bit words + write_big_endian_2byte_word(&wi, f); + write_big_endian_2byte_word(&hi, f); + + // Also write the active pixels. For instance, if we want to compress + // a 128 x 129 image, we have to extend it to 128 x 132 pixels. + // Then the wi and hi written above will be 128 and 132, but the + // additional information that we write below will be 128 and 129, + // to indicate that it is only the top 129 lines of data in the + // decompressed image that will be valid data, and the rest will + // be just garbage. + + unsigned short activew, activeh; + activew = width; + activeh = height; + + write_big_endian_2byte_word(&activew, f); + write_big_endian_2byte_word(&activeh, f); + } + + int totblocks = expandedheight/4 * expandedwidth/4; + int countblocks = 0; + + /// xxx + for(y=0;y.\n",dstfile); + } +} + +double calculatePSNR(uint8 *lossyimg, uint8 *origimg, int width, int height) +{ + // calculate Mean Square Error (MSE) + + int x,y; + double MSE; + double PSNR; + double err; + MSE = 0; + + // Note: This calculation of PSNR uses the formula + // + // PSNR = 10 * log_10 ( 255^2 / MSE ) + // + // where the MSE is calculated as + // + // 1/(N*M) * sum ( 1/3 * ((R' - R)^2 + (G' - G)^2 + (B' - B)^2) ) + // + // The reason for having the 1/3 factor is the following: + // Presume we have a grayscale image, that is acutally just the red component + // of a color image.. The squared error is then (R' - R)^2. + // Assume that we have a certain signal to noise ratio, say 30 dB. If we add + // another two components (say green and blue) with the same signal to noise + // ratio, we want the total signal to noise ratio be the same. For the + // squared error to remain constant we must divide by three after adding + // together the squared errors of the components. + + for(y=0;y.\n",srcfile); + return -1; + } +} + +void compressFile(char *srcfile,char *dstfile, int action) +{ + uint8 *srcimg; + int width,height; + int extendedwidth, extendedheight; + double PSNR; + + printf("\n"); + switch(action) + { + case 0: + printf("Using FAST compression mode and NONPERCEPTUAL error metric\n"); + break; + case 1: + printf("Using MEDIUM-speed compression mode and NONPERCEPTUAL error metric\n"); + break; + case 2: + printf("Using SLOW compression mode and NONPERCEPTUAL error metric\n"); + break; + case 3: + printf("Using FAST compression mode and PERCEPTUAL error metric\n"); + break; + case 4: + printf("Using MEDIUM-speed compression mode and PERCEPTUAL error metric\n"); + break; + case 5: + printf("Using SLOW compression mode and PERCEPTUAL error metric\n"); + break; + } + printf("Using the orientation that maps the first pixel in .ppm file to "); + if(orientation == FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T0) + printf("s=0, t=0.\n"); + else if(orientation == FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T1) + printf("s=0, t=1.\n"); + + if(readSrcFile(srcfile,srcimg,width,height,extendedwidth, extendedheight)) + { + printf("Compressing...\n"); + compressImageFile(srcimg,width,height,dstfile,extendedwidth, extendedheight, action); + PSNR = calculatePSNRfile(dstfile, srcimg); + free(srcimg); + printf("PSNR = %f\n",PSNR); + } +} + +double calculatePSNRTwoFiles(char *srcfile1,char *srcfile2) +{ + uint8 *srcimg1; + uint8 *srcimg2; + int width1, height1; + int width2, height2; + double PSNR = 0.0f; + double perceptually_weighted_PSNR; + + if(readSrcFileNoExpand(srcfile1,srcimg1,width1,height1)) + { + if(readSrcFileNoExpand(srcfile2,srcimg2,width2,height2)) + { + if((width1 == width2) && (height1 == height2)) + { + PSNR = calculatePSNR(srcimg1, srcimg2, width1, height1); + printf("PSNR = %f\n",PSNR); + perceptually_weighted_PSNR = calculatePerceptuallyWeightedPSNR(srcimg1, srcimg2, width1, height1); + printf("perceptually weighted PSNR = (%f)\n",perceptually_weighted_PSNR); + + } + else + { + printf("\n Width and height do no not match for image: width, height = (%d, %d) and (%d, %d)\n",width1,height1, width2, height2); + } + } + else + { + printf("Couldn't open file %s.\n",srcfile2); + } + } + else + { + printf("Couldn't open file %s.\n",srcfile1); + } + + return PSNR; +} + + +void uncompressFile(char *srcfile,char *dstfile) +{ + FILE *f; + int width,height; + unsigned int block_part1, block_part2; + uint8 *img, *newimg; + char str[300]; + unsigned short w, h; + int xx, yy; + unsigned char magic[4]; + unsigned char version[2]; + unsigned short texture_type; + int active_width; + int active_height; + int format; + + + + if(f=fopen(srcfile,"rb")) + { + if(ktx_mode) + { + //read ktx header.. + KTX_header header; + fread(&header,sizeof(KTX_header),1,f); + //read size parameter, which we don't actually need.. + unsigned int bitsize; + fread(&bitsize,sizeof(unsigned int),1,f); + + active_width = header.pixelWidth; + active_height = header.pixelHeight; + w = ((active_width+3)/4)*4; + h = ((active_height+3)/4)*4; + width=w; + height=h; + + if(header.glInternalFormat==GL_ETC1_RGB8_OES) + { + format=ETC1_RGB_NO_MIPMAPS; + } + else { + printf("ktx file has unknown glInternalFormat (not etc compressed)!\n"); + exit(1); + } + } + else + { + // Read magic nunmber + fread(&magic[0], sizeof(unsigned char), 1, f); + fread(&magic[1], sizeof(unsigned char), 1, f); + fread(&magic[2], sizeof(unsigned char), 1, f); + fread(&magic[3], sizeof(unsigned char), 1, f); + if(!(magic[0] == 'P' && magic[1] == 'K' && magic[2] == 'M' && magic[3] == ' ')) + { + printf("\n\n The file %s is not a .pkm file.\n",srcfile); + exit(1); + } + + // Read version + fread(&version[0], sizeof(unsigned char), 1, f); + fread(&version[1], sizeof(unsigned char), 1, f); + if(!(version[0] == '1' && version[1] == '0')) + { + printf("\n\n The file %s is not of version 1.0 but of version %c.%c.\n",srcfile, version[0], version[1]); + exit(1); + } + + // Read texture type + read_big_endian_2byte_word(&texture_type, f); + if(!(texture_type == ETC1_RGB_NO_MIPMAPS)) + { + printf("\n\n The file %s does not contain a ETC1_RGB_NO_MIPMAPS texture.\n", srcfile); + exit(1); + } + + // Read how many pixels the blocks make up + + read_big_endian_2byte_word(&w, f); + read_big_endian_2byte_word(&h, f); + width = w; + height = h; + + // Read how many pixels contain active data (the rest are just + // for making sure we have a 2*a x 4*b size). + + read_big_endian_2byte_word(&w, f); + read_big_endian_2byte_word(&h, f); + active_width = w; + active_height = h; + } + + printf("Width = %d, Height = %d\n",width, height); + printf("active pixel area: top left %d x %d area.\n",active_width, active_height); + + img=(uint8*)malloc(3*width*height); + if(!img) + { + printf("Error: could not allocate memory\n"); + exit(0); + } + + + for(int y=0;y.\n",srcfile); + } +} + +int determineAction(int argc,char *argv[],char *dst) +{ + char *src; + int q; + + enum {MODE_COMPRESS, MODE_UNCOMPRESS, MODE_PSNR}; + enum {SPEED_SLOW, SPEED_FAST, SPEED_MEDIUM}; + enum {METRIC_PERCEPTUAL, METRIC_NONPERCEPTUAL}; + + int mode = MODE_COMPRESS; + int speed = SPEED_FAST; + int metric = METRIC_PERCEPTUAL; + + // A bit hackish: First check for the orientation flag. When this flag is set, remove it from the string and proceed with the rest of the arguments as before. + + bool orientation_flag_found = false; + orientation = FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T0; + + for(q = 1; q < argc && !orientation_flag_found; q++) + { + src = argv[q]; + if(!strcmp(src, "-o")) + { + orientation_flag_found = true; + src = argv[q+1]; + if(!strcmp(src, "topleftmapsto_s0t0")) + { + orientation = FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T0; + } + else if(!strcmp(src, "bottomleftmapsto_s0t0")) + { + orientation = FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T1; + } + else + { + return -1; + } + // At this stage in the code we know we have a valid orientation argument. + // Now remove it from the argument list. + for(int xx=q+2; xx does not exist.\n",srcfile); + exit(0); + } + // 0: compress from .any to .pkm with SPEED_FAST, METRIC_NONPERCEPTUAL, + // 1: compress from .any to .pkm with SPEED_MEDIUM, METRIC_NONPERCEPTUAL, + // 2: compress from .any to .pkm with SPEED_SLOW, METRIC_NONPERCEPTUAL, + // 3: compress from .any to .pkm with SPEED_FAST, METRIC_PERCEPTUAL, + // 4: compress from .any to .pkm with SPEED_MEDIUM, METRIC_PERCEPTUAL, + // 5: compress from .any to .pkm with SPEED_SLOW, METRIC_PERCEPTUAL, + // 6: decompress from .pkm to .any + // 7: calculate PSNR between .any and .any + + if(action == 6) + { + printf("Uncompressing from .pkm file ...\n"); + printf("Using the orientation that maps the first pixel in .ppm file to "); + if(orientation == FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T0) + printf("s=0, t=0.\n"); + else if(orientation == FIRST_PIXEL_IN_PPM_FILE_MAPS_TO_S0T1) + printf("s=0, t=1.\n"); + + uncompressFile(srcfile,dstfile); + } + else if(action == 7) + { + printf("Calculating PSNR between files...\n"); + calculatePSNRTwoFiles(srcfile,dstfile); + } + else + { + compressFile(srcfile, dstfile, action); + } + } + } else { + printf("ETCPACK v1.06\n"); + printf("Usage: etcpack srcfile dstfile\n\nCompresses and decompresses images using the Ericsson Texture Compression (ETC) scheme.\n\n"); + printf(" -s {fast|medium|slow} Compression speed. Slow = best quality\n"); + printf(" (default: fast)\n"); + printf(" -e {perceptual|nonperceptual} Error metric: Perceptual (nicest) or \n"); + printf(" nonperceptual (highest PSNR)\n"); + printf(" (default: perceptual)\n"); + printf(" -o {topleftmapsto_s0t0| Orientation: Which pixel (top left or\n"); + printf(" bottomleftmapsto_s0t0} bottom left) that will map to texture\n"); + printf(" coordinates (s=0, t=0). \n"); + printf(" (default: topleftmapsto_s0t0.) For a \n"); + printf(" .ppm file this means that the first \n"); + printf(" pixel in the file will be mapped to \n"); + printf(" s=0, t=0 by default.\n"); + printf(" \n"); + printf("Examples: \n"); + printf(" etcpack img.ppm img.ktx Compresses img.ppm to img.ktx\n"); + printf(" etcpack img.ppm img.pkm Compresses img.ppm to img.pkm\n"); + printf(" etcpack img.ktx img_copy.ppm Decompresses img.ktx to img_copy.ppm\n"); + printf(" etcpack -s slow img.ppm img.ktx Compress using the slow mode.\n"); + printf(" etcpack -p orig.ppm copy.ppm Calculate PSNR between orig and copy\n\n"); + + } + return 0; + +} diff --git a/ext/etcpack/image.cpp b/ext/etcpack/image.cpp new file mode 100644 index 0000000000..61688632b5 --- /dev/null +++ b/ext/etcpack/image.cpp @@ -0,0 +1,212 @@ +// image.cxx +// +// NO WARRANTY +// +// BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, ERICSSON MAKES NO +// REPRESENTATIONS OF ANY KIND, EXTENDS NO WARRANTIES OF ANY KIND; EITHER +// EXPRESS OR IMPLIED; INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +// PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +// THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ERICSSON +// MAKES NO WARRANTY THAT THE MANUFACTURE, SALE, LEASE, USE OR +// IMPORTATION WILL BE FREE FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR +// OTHER INTELLECTUAL PROPERTY RIGHTS OF OTHERS, AND IT SHALL BE THE SOLE +// RESPONSIBILITY OF THE LICENSEE TO MAKE SUCH DETERMINATION AS IS +// NECESSARY WITH RESPECT TO THE ACQUISITION OF LICENSES UNDER PATENTS +// AND OTHER INTELLECTUAL PROPERTY OF THIRD PARTIES; +// +// IN NO EVENT WILL ERICSSON, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +// GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF +// THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO +// LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +// YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY +// OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGES. +// +// (C) Ericsson AB 2005. All Rights Reserved. +// +#include +#include +#include +#include "image.h" + +// Removes comments in a .ppm file +// (i.e., lines starting with #) +// +// Written by Jacob Strom +// +void removeComments(FILE *f1) +{ + int c; + + while((c = getc(f1)) == '#') + { + char line[1024]; + fgets(line, 1024, f1); + } + ungetc(c, f1); +} + + +// Removes white spaces in a .ppm file +// +// Written by Jacob Strom +// +void removeSpaces(FILE *f1) +{ + int c; + + c = getc(f1); + while(c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r') + { + c = getc(f1); + } + ungetc(c, f1); +} + +// fReadPPM +// +// Written by Jacob Strom +// +// reads a ppm file with P6 header (meaning binary, as opposed to P5, which is ascII) +// and returns the image in pixels. +// +// The header must look like this: +// +// P6 +// # Comments (not necessary) +// width height +// 255 +// +// after that follows RGBRGBRGB... +// +bool fReadPPM(const char *filename, int &width, int &height, unsigned char *&pixels, bool reverse_y) +{ + FILE *f1; + int mustbe255; + f1 = fopen(filename, "rb"); + + if(f1) + { + char line[255]; + + removeSpaces(f1); + removeComments(f1); + removeSpaces(f1); + + fscanf(f1, "%s", line); + + if(strcmp(line, "P6")!=0) + { + printf("Error: %s is not binary\n"); + printf("(Binary .ppm files start with P6).\n"); + fclose(f1); + return false; + } + removeSpaces(f1); + removeComments(f1); + removeSpaces(f1); + + fscanf(f1, "%d %d", &width, &height); + if( width<=0 || height <=0) + { + printf("Error: width or height negative. File: %s\n",filename); + fclose(f1); + return false; + } + + removeSpaces(f1); + removeComments(f1); + removeSpaces(f1); + + fscanf(f1, "%d", &mustbe255); + if( mustbe255!= 255 ) + { + printf("Error: Color resolution must be 255. File: %s\n",filename); + fclose(f1); + return false; + } + + // We need to remove the newline. + char c = 0; + while(c != '\n') + fscanf(f1, "%c", &c); + + + pixels = (unsigned char*) malloc(3*width*height); + + if(!pixels) + { + printf("Error: Could not allocate memory for image. File: %s\n", filename); + fclose(f1); + return false; + } + + if(reverse_y) + { + for(int yy = 0; yy header file. */ +#define HAVE_DLFCN_H 0 + +/* Define to 1 if you have the `fseeko' function. */ +#define HAVE_FSEEKO 0 + +/* Define to 1 if you have the `ftello' function. */ +#define HAVE_FTELLO 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 0 + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 0 + +/* Define to 1 if you have the `mkstemp' function. */ +#define HAVE_MKSTEMP 0 + +/* Define to 1 if you have the `MoveFileExA' function. */ +/* #undef HAVE_MOVEFILEEXA */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if `tm_zone' is a member of `struct tm'. */ +#define HAVE_STRUCT_TM_TM_ZONE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use + `HAVE_STRUCT_TM_TM_ZONE' instead. */ +#define HAVE_TM_ZONE 1 + +/* Define to 1 if you don't have `tm_zone' but do have the external array + `tzname'. */ +/* #undef HAVE_TZNAME */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Name of package */ +#define PACKAGE "libzip" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "libzip@nih.at" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libzip" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libzip 0.9.3" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libzip" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.9.3" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Version number of package */ +#define VERSION "0.9.3" diff --git a/ext/libzip/libzip.la b/ext/libzip/libzip.la new file mode 100644 index 0000000000..49b0cd7779 --- /dev/null +++ b/ext/libzip/libzip.la @@ -0,0 +1,41 @@ +# libzip.la - a libtool library file +# Generated by ltmain.sh (GNU libtool) 2.2.6b +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libzip.so.1' + +# Names of this library. +library_names='libzip.so.1.0.0 libzip.so.1 libzip.so' + +# The name of the static archive. +old_library='libzip.a' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='' + +# Libraries that this one depends upon. +dependency_libs=' -lz' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libzip. +current=1 +age=0 +revision=0 + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/usr/local/lib' diff --git a/ext/libzip/mkstemp.c b/ext/libzip/mkstemp.c new file mode 100644 index 0000000000..738a065a09 --- /dev/null +++ b/ext/libzip/mkstemp.c @@ -0,0 +1,140 @@ +/* $NiH: mkstemp.c,v 1.3 2006/04/23 14:51:45 wiz Exp $ */ + +/* Adapted from NetBSB libc by Dieter Baron */ + +/* NetBSD: gettemp.c,v 1.13 2003/12/05 00:57:36 uebayasi Exp */ + +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + + + +int +_zip_mkstemp(char *path) +{ + int fd; + char *start, *trv; + struct stat sbuf; + pid_t pid; + + /* To guarantee multiple calls generate unique names even if + the file is not created. 676 different possibilities with 7 + or more X's, 26 with 6 or less. */ + static char xtra[2] = "aa"; + int xcnt = 0; + + pid = getpid(); + + /* Move to end of path and count trailing X's. */ + for (trv = path; *trv; ++trv) + if (*trv == 'X') + xcnt++; + else + xcnt = 0; + + /* Use at least one from xtra. Use 2 if more than 6 X's. */ + if (*(trv - 1) == 'X') + *--trv = xtra[0]; + if (xcnt > 6 && *(trv - 1) == 'X') + *--trv = xtra[1]; + + /* Set remaining X's to pid digits with 0's to the left. */ + while (*--trv == 'X') { + *trv = (pid % 10) + '0'; + pid /= 10; + } + + /* update xtra for next call. */ + if (xtra[0] != 'z') + xtra[0]++; + else { + xtra[0] = 'a'; + if (xtra[1] != 'z') + xtra[1]++; + else + xtra[1] = 'a'; + } + + /* + * check the target directory; if you have six X's and it + * doesn't exist this runs for a *very* long time. + */ + for (start = trv + 1;; --trv) { + if (trv <= path) + break; + if (*trv == '/') { + *trv = '\0'; + if (stat(path, &sbuf)) + return (0); + if (!S_ISDIR(sbuf.st_mode)) { + errno = ENOTDIR; + return (0); + } + *trv = '/'; + break; + } + } + + for (;;) { + if ((fd=open(path, O_CREAT|O_EXCL|O_RDWR|O_BINARY, 0600)) >= 0) + return (fd); + if (errno != EEXIST) + return (0); + + /* tricky little algorithm for backward compatibility */ + for (trv = start;;) { + if (!*trv) + return (0); + if (*trv == 'z') + *trv++ = 'a'; + else { + if (isdigit((unsigned char)*trv)) + *trv = 'a'; + else + ++*trv; + break; + } + } + } + /*NOTREACHED*/ +} diff --git a/ext/libzip/zip.h b/ext/libzip/zip.h new file mode 100644 index 0000000000..48431a35c9 --- /dev/null +++ b/ext/libzip/zip.h @@ -0,0 +1,230 @@ +#ifndef _HAD_ZIP_H +#define _HAD_ZIP_H + +/* + zip.h -- exported declarations. + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#ifndef ZIP_EXTERN +#ifdef _MSC_VER +#define ZIP_EXTERN __declspec(dllexport) +#else +#define ZIP_EXTERN +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/* flags for zip_open */ + +#define ZIP_CREATE 1 +#define ZIP_EXCL 2 +#define ZIP_CHECKCONS 4 + + +/* flags for zip_name_locate, zip_fopen, zip_stat, ... */ + +#define ZIP_FL_NOCASE 1 /* ignore case on name lookup */ +#define ZIP_FL_NODIR 2 /* ignore directory component */ +#define ZIP_FL_COMPRESSED 4 /* read compressed data */ +#define ZIP_FL_UNCHANGED 8 /* use original data, ignoring changes */ +#define ZIP_FL_RECOMPRESS 16 /* force recompression of data */ + +/* archive global flags flags */ + +#define ZIP_AFL_TORRENT 1 /* torrent zipped */ + +/* libzip error codes */ + +#define ZIP_ER_OK 0 /* N No error */ +#define ZIP_ER_MULTIDISK 1 /* N Multi-disk zip archives not supported */ +#define ZIP_ER_RENAME 2 /* S Renaming temporary file failed */ +#define ZIP_ER_CLOSE 3 /* S Closing zip archive failed */ +#define ZIP_ER_SEEK 4 /* S Seek error */ +#define ZIP_ER_READ 5 /* S Read error */ +#define ZIP_ER_WRITE 6 /* S Write error */ +#define ZIP_ER_CRC 7 /* N CRC error */ +#define ZIP_ER_ZIPCLOSED 8 /* N Containing zip archive was closed */ +#define ZIP_ER_NOENT 9 /* N No such file */ +#define ZIP_ER_EXISTS 10 /* N File already exists */ +#define ZIP_ER_OPEN 11 /* S Can't open file */ +#define ZIP_ER_TMPOPEN 12 /* S Failure to create temporary file */ +#define ZIP_ER_ZLIB 13 /* Z Zlib error */ +#define ZIP_ER_MEMORY 14 /* N Malloc failure */ +#define ZIP_ER_CHANGED 15 /* N Entry has been changed */ +#define ZIP_ER_COMPNOTSUPP 16 /* N Compression method not supported */ +#define ZIP_ER_EOF 17 /* N Premature EOF */ +#define ZIP_ER_INVAL 18 /* N Invalid argument */ +#define ZIP_ER_NOZIP 19 /* N Not a zip archive */ +#define ZIP_ER_INTERNAL 20 /* N Internal error */ +#define ZIP_ER_INCONS 21 /* N Zip archive inconsistent */ +#define ZIP_ER_REMOVE 22 /* S Can't remove file */ +#define ZIP_ER_DELETED 23 /* N Entry has been deleted */ + + +/* type of system error value */ + +#define ZIP_ET_NONE 0 /* sys_err unused */ +#define ZIP_ET_SYS 1 /* sys_err is errno */ +#define ZIP_ET_ZLIB 2 /* sys_err is zlib error code */ + +/* compression methods */ + +#define ZIP_CM_DEFAULT -1 /* better of deflate or store */ +#define ZIP_CM_STORE 0 /* stored (uncompressed) */ +#define ZIP_CM_SHRINK 1 /* shrunk */ +#define ZIP_CM_REDUCE_1 2 /* reduced with factor 1 */ +#define ZIP_CM_REDUCE_2 3 /* reduced with factor 2 */ +#define ZIP_CM_REDUCE_3 4 /* reduced with factor 3 */ +#define ZIP_CM_REDUCE_4 5 /* reduced with factor 4 */ +#define ZIP_CM_IMPLODE 6 /* imploded */ +/* 7 - Reserved for Tokenizing compression algorithm */ +#define ZIP_CM_DEFLATE 8 /* deflated */ +#define ZIP_CM_DEFLATE64 9 /* deflate64 */ +#define ZIP_CM_PKWARE_IMPLODE 10 /* PKWARE imploding */ +/* 11 - Reserved by PKWARE */ +#define ZIP_CM_BZIP2 12 /* compressed using BZIP2 algorithm */ +/* 13 - Reserved by PKWARE */ +#define ZIP_CM_LZMA 14 /* LZMA (EFS) */ +/* 15-17 - Reserved by PKWARE */ +#define ZIP_CM_TERSE 18 /* compressed using IBM TERSE (new) */ +#define ZIP_CM_LZ77 19 /* IBM LZ77 z Architecture (PFS) */ +#define ZIP_CM_WAVPACK 97 /* WavPack compressed data */ +#define ZIP_CM_PPMD 98 /* PPMd version I, Rev 1 */ + +/* encryption methods */ + +#define ZIP_EM_NONE 0 /* not encrypted */ +#define ZIP_EM_TRAD_PKWARE 1 /* traditional PKWARE encryption */ +#if 0 /* Strong Encryption Header not parsed yet */ +#define ZIP_EM_DES 0x6601 /* strong encryption: DES */ +#define ZIP_EM_RC2_OLD 0x6602 /* strong encryption: RC2, version < 5.2 */ +#define ZIP_EM_3DES_168 0x6603 +#define ZIP_EM_3DES_112 0x6609 +#define ZIP_EM_AES_128 0x660e +#define ZIP_EM_AES_192 0x660f +#define ZIP_EM_AES_256 0x6610 +#define ZIP_EM_RC2 0x6702 /* strong encryption: RC2, version >= 5.2 */ +#define ZIP_EM_RC4 0x6801 +#endif +#define ZIP_EM_UNKNOWN 0xffff /* unknown algorithm */ + + + +enum zip_source_cmd { + ZIP_SOURCE_OPEN, /* prepare for reading */ + ZIP_SOURCE_READ, /* read data */ + ZIP_SOURCE_CLOSE, /* reading is done */ + ZIP_SOURCE_STAT, /* get meta information */ + ZIP_SOURCE_ERROR, /* get error information */ + ZIP_SOURCE_FREE /* cleanup and free resources */ +}; + +typedef ssize_t (*zip_source_callback)(void *state, void *data, + size_t len, enum zip_source_cmd cmd); + +struct zip_stat { + const char *name; /* name of the file */ + int index; /* index within archive */ + unsigned int crc; /* crc of file data */ + time_t mtime; /* modification time */ + off_t size; /* size of file (uncompressed) */ + off_t comp_size; /* size of file (compressed) */ + unsigned short comp_method; /* compression method used */ + unsigned short encryption_method; /* encryption method used */ +}; + +struct zip; +struct zip_file; +struct zip_source; + + + +ZIP_EXTERN int zip_add(struct zip *, const char *, struct zip_source *); +ZIP_EXTERN int zip_add_dir(struct zip *, const char *); +ZIP_EXTERN int zip_close(struct zip *); +ZIP_EXTERN int zip_delete(struct zip *, int); +ZIP_EXTERN void zip_error_clear(struct zip *); +ZIP_EXTERN void zip_error_get(struct zip *, int *, int *); +ZIP_EXTERN int zip_error_get_sys_type(int); +ZIP_EXTERN int zip_error_to_str(char *, size_t, int, int); +ZIP_EXTERN int zip_fclose(struct zip_file *); +ZIP_EXTERN void zip_file_error_clear(struct zip_file *); +ZIP_EXTERN void zip_file_error_get(struct zip_file *, int *, int *); +ZIP_EXTERN const char *zip_file_strerror(struct zip_file *); +ZIP_EXTERN struct zip_file *zip_fopen(struct zip *, const char *, int); +ZIP_EXTERN struct zip_file *zip_fopen_index(struct zip *, int, int); +ZIP_EXTERN ssize_t zip_fread(struct zip_file *, void *, size_t); +ZIP_EXTERN const char *zip_get_archive_comment(struct zip *, int *, int); +ZIP_EXTERN int zip_get_archive_flag(struct zip *, int, int); +ZIP_EXTERN const char *zip_get_file_comment(struct zip *, int, int *, int); +ZIP_EXTERN const char *zip_get_name(struct zip *, int, int); +ZIP_EXTERN int zip_get_num_files(struct zip *); +ZIP_EXTERN int zip_name_locate(struct zip *, const char *, int); +ZIP_EXTERN struct zip *zip_open(const char *, int, int *); +ZIP_EXTERN int zip_rename(struct zip *, int, const char *); +ZIP_EXTERN int zip_replace(struct zip *, int, struct zip_source *); +ZIP_EXTERN int zip_set_archive_comment(struct zip *, const char *, int); +ZIP_EXTERN int zip_set_archive_flag(struct zip *, int, int); +ZIP_EXTERN int zip_set_file_comment(struct zip *, int, const char *, int); +ZIP_EXTERN struct zip_source *zip_source_buffer(struct zip *, const void *, + off_t, int); +ZIP_EXTERN struct zip_source *zip_source_file(struct zip *, const char *, + off_t, off_t); +ZIP_EXTERN struct zip_source *zip_source_filep(struct zip *, FILE *, + off_t, off_t); +ZIP_EXTERN void zip_source_free(struct zip_source *); +ZIP_EXTERN struct zip_source *zip_source_function(struct zip *, + zip_source_callback, void *); +ZIP_EXTERN struct zip_source *zip_source_zip(struct zip *, struct zip *, + int, int, off_t, off_t); +ZIP_EXTERN int zip_stat(struct zip *, const char *, int, struct zip_stat *); +ZIP_EXTERN int zip_stat_index(struct zip *, int, int, struct zip_stat *); +ZIP_EXTERN void zip_stat_init(struct zip_stat *); +ZIP_EXTERN const char *zip_strerror(struct zip *); +ZIP_EXTERN int zip_unchange(struct zip *, int); +ZIP_EXTERN int zip_unchange_all(struct zip *); +ZIP_EXTERN int zip_unchange_archive(struct zip *); + +#ifdef __cplusplus +} +#endif + +#endif /* _HAD_ZIP_H */ diff --git a/ext/libzip/zip_add.c b/ext/libzip/zip_add.c new file mode 100644 index 0000000000..f6bdb2ab92 --- /dev/null +++ b/ext/libzip/zip_add.c @@ -0,0 +1,49 @@ +/* + zip_add.c -- add file via callback function + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_add(struct zip *za, const char *name, struct zip_source *source) +{ + if (name == NULL || source == NULL) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + return _zip_replace(za, -1, name, source); +} diff --git a/ext/libzip/zip_add_dir.c b/ext/libzip/zip_add_dir.c new file mode 100644 index 0000000000..493e28ddab --- /dev/null +++ b/ext/libzip/zip_add_dir.c @@ -0,0 +1,80 @@ +/* + zip_add_dir.c -- add directory + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_add_dir(struct zip *za, const char *name) +{ + int len, ret; + char *s; + struct zip_source *source; + + if (name == NULL) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + s = NULL; + len = strlen(name); + + if (name[len-1] != '/') { + if ((s=(char *)malloc(len+2)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + strcpy(s, name); + s[len] = '/'; + s[len+1] = '\0'; + } + + if ((source=zip_source_buffer(za, NULL, 0, 0)) == NULL) { + free(s); + return -1; + } + + ret = _zip_replace(za, -1, s ? s : name, source); + + free(s); + if (ret < 0) + zip_source_free(source); + + return ret; +} diff --git a/ext/libzip/zip_close.c b/ext/libzip/zip_close.c new file mode 100644 index 0000000000..c6885d96a9 --- /dev/null +++ b/ext/libzip/zip_close.c @@ -0,0 +1,679 @@ +/* + zip_close.c -- close zip archive and update changes + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include +#include +#include +#include +#include + +#include "zipint.h" + +static int add_data(struct zip *, struct zip_source *, struct zip_dirent *, + FILE *); +static int add_data_comp(zip_source_callback, void *, struct zip_stat *, + FILE *, struct zip_error *); +static int add_data_uncomp(struct zip *, zip_source_callback, void *, + struct zip_stat *, FILE *); +static void ch_set_error(struct zip_error *, zip_source_callback, void *); +static int copy_data(FILE *, off_t, FILE *, struct zip_error *); +static int write_cdir(struct zip *, struct zip_cdir *, FILE *); +static int _zip_cdir_set_comment(struct zip_cdir *, struct zip *); +static int _zip_changed(struct zip *, int *); +static char *_zip_create_temp_output(struct zip *, FILE **); +static int _zip_torrentzip_cmp(const void *, const void *); + + + +struct filelist { + int idx; + const char *name; +}; + + + +ZIP_EXTERN int +zip_close(struct zip *za) +{ + int survivors; + int i, j, error; + char *temp; + FILE *out; + mode_t mask; + struct zip_cdir *cd; + struct zip_dirent de; + struct filelist *filelist; + int reopen_on_error; + int new_torrentzip; + + reopen_on_error = 0; + + if (za == NULL) + return -1; + + if (!_zip_changed(za, &survivors)) { + _zip_free(za); + return 0; + } + + /* don't create zip files with no entries */ + if (survivors == 0) { + if (za->zn && za->zp) { + if (remove(za->zn) != 0) { + _zip_error_set(&za->error, ZIP_ER_REMOVE, errno); + return -1; + } + } + _zip_free(za); + return 0; + } + + if ((filelist=(struct filelist *)malloc(sizeof(filelist[0])*survivors)) + == NULL) + return -1; + + if ((cd=_zip_cdir_new(survivors, &za->error)) == NULL) { + free(filelist); + return -1; + } + + for (i=0; ientry[i]); + + /* archive comment is special for torrentzip */ + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) { + cd->comment = _zip_memdup(TORRENT_SIG "XXXXXXXX", + TORRENT_SIG_LEN + TORRENT_CRC_LEN, + &za->error); + if (cd->comment == NULL) { + _zip_cdir_free(cd); + free(filelist); + return -1; + } + cd->comment_len = TORRENT_SIG_LEN + TORRENT_CRC_LEN; + } + else if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, ZIP_FL_UNCHANGED) == 0) { + if (_zip_cdir_set_comment(cd, za) == -1) { + _zip_cdir_free(cd); + free(filelist); + return -1; + } + } + + if ((temp=_zip_create_temp_output(za, &out)) == NULL) { + _zip_cdir_free(cd); + free(filelist); + return -1; + } + + + /* create list of files with index into original archive */ + for (i=j=0; inentry; i++) { + if (za->entry[i].state == ZIP_ST_DELETED) + continue; + + filelist[j].idx = i; + filelist[j].name = zip_get_name(za, i, 0); + j++; + } + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) + qsort(filelist, survivors, sizeof(filelist[0]), + _zip_torrentzip_cmp); + + new_torrentzip = (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 1 + && zip_get_archive_flag(za, ZIP_AFL_TORRENT, + ZIP_FL_UNCHANGED) == 0); + error = 0; + for (j=0; jentry+i) || new_torrentzip) { + _zip_dirent_init(&de); + + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) + _zip_dirent_torrent_normalize(&de); + + /* use it as central directory entry */ + memcpy(cd->entry+j, &de, sizeof(cd->entry[j])); + + /* set/update file name */ + if (za->entry[i].ch_filename == NULL) { + if (za->entry[i].state == ZIP_ST_ADDED) { + de.filename = strdup("-"); + de.filename_len = 1; + cd->entry[j].filename = "-"; + cd->entry[j].filename_len = 1; + } + else { + de.filename = strdup(za->cdir->entry[i].filename); + de.filename_len = strlen(de.filename); + cd->entry[j].filename = za->cdir->entry[i].filename; + cd->entry[j].filename_len = de.filename_len; + } + } + } + else { + /* copy existing directory entries */ + if (fseeko(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0) { + _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + error = 1; + break; + } + if (_zip_dirent_read(&de, za->zp, NULL, NULL, 1, + &za->error) != 0) { + error = 1; + break; + } + memcpy(cd->entry+j, za->cdir->entry+i, sizeof(cd->entry[j])); + if (de.bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { + de.crc = za->cdir->entry[i].crc; + de.comp_size = za->cdir->entry[i].comp_size; + de.uncomp_size = za->cdir->entry[i].uncomp_size; + de.bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; + cd->entry[j].bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; + } + } + + if (za->entry[i].ch_filename) { + free(de.filename); + if ((de.filename=strdup(za->entry[i].ch_filename)) == NULL) { + error = 1; + break; + } + de.filename_len = strlen(de.filename); + cd->entry[j].filename = za->entry[i].ch_filename; + cd->entry[j].filename_len = de.filename_len; + } + + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0 + && za->entry[i].ch_comment_len != -1) { + /* as the rest of cd entries, its malloc/free is done by za */ + cd->entry[j].comment = za->entry[i].ch_comment; + cd->entry[j].comment_len = za->entry[i].ch_comment_len; + } + + cd->entry[j].offset = ftello(out); + + if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) { + struct zip_source *zs; + + zs = NULL; + if (!ZIP_ENTRY_DATA_CHANGED(za->entry+i)) { + if ((zs=zip_source_zip(za, za, i, ZIP_FL_RECOMPRESS, 0, -1)) + == NULL) { + error = 1; + break; + } + } + + if (add_data(za, zs ? zs : za->entry[i].source, &de, out) < 0) { + error = 1; + break; + } + cd->entry[j].last_mod = de.last_mod; + cd->entry[j].comp_method = de.comp_method; + cd->entry[j].comp_size = de.comp_size; + cd->entry[j].uncomp_size = de.uncomp_size; + cd->entry[j].crc = de.crc; + } + else { + if (_zip_dirent_write(&de, out, 1, &za->error) < 0) { + error = 1; + break; + } + /* we just read the local dirent, file is at correct position */ + if (copy_data(za->zp, cd->entry[j].comp_size, out, + &za->error) < 0) { + error = 1; + break; + } + } + + _zip_dirent_finalize(&de); + } + + free(filelist); + + if (!error) { + if (write_cdir(za, cd, out) < 0) + error = 1; + } + + /* pointers in cd entries are owned by za */ + cd->nentry = 0; + _zip_cdir_free(cd); + + if (error) { + _zip_dirent_finalize(&de); + fclose(out); + remove(temp); + free(temp); + return -1; + } + + if (fclose(out) != 0) { + _zip_error_set(&za->error, ZIP_ER_CLOSE, errno); + remove(temp); + free(temp); + return -1; + } + + if (za->zp) { + fclose(za->zp); + za->zp = NULL; + reopen_on_error = 1; + } + if (_zip_rename(temp, za->zn) != 0) { + _zip_error_set(&za->error, ZIP_ER_RENAME, errno); + remove(temp); + free(temp); + if (reopen_on_error) { + /* ignore errors, since we're already in an error case */ + za->zp = fopen(za->zn, "rb"); + } + return -1; + } + mask = umask(0); + umask(mask); + chmod(za->zn, 0666&~mask); + + _zip_free(za); + free(temp); + + return 0; +} + + + +static int +add_data(struct zip *za, struct zip_source *zs, struct zip_dirent *de, FILE *ft) +{ + off_t offstart, offend; + zip_source_callback cb; + void *ud; + struct zip_stat st; + + cb = zs->f; + ud = zs->ud; + + if (cb(ud, &st, sizeof(st), ZIP_SOURCE_STAT) < (ssize_t)sizeof(st)) { + ch_set_error(&za->error, cb, ud); + return -1; + } + + if (cb(ud, NULL, 0, ZIP_SOURCE_OPEN) < 0) { + ch_set_error(&za->error, cb, ud); + return -1; + } + + offstart = ftello(ft); + + if (_zip_dirent_write(de, ft, 1, &za->error) < 0) + return -1; + + if (st.comp_method != ZIP_CM_STORE) { + if (add_data_comp(cb, ud, &st, ft, &za->error) < 0) + return -1; + } + else { + if (add_data_uncomp(za, cb, ud, &st, ft) < 0) + return -1; + } + + if (cb(ud, NULL, 0, ZIP_SOURCE_CLOSE) < 0) { + ch_set_error(&za->error, cb, ud); + return -1; + } + + offend = ftello(ft); + + if (fseeko(ft, offstart, SEEK_SET) < 0) { + _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + return -1; + } + + + de->last_mod = st.mtime; + de->comp_method = st.comp_method; + de->crc = st.crc; + de->uncomp_size = st.size; + de->comp_size = st.comp_size; + + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) + _zip_dirent_torrent_normalize(de); + + if (_zip_dirent_write(de, ft, 1, &za->error) < 0) + return -1; + + if (fseeko(ft, offend, SEEK_SET) < 0) { + _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + return -1; + } + + return 0; +} + + + +static int +add_data_comp(zip_source_callback cb, void *ud, struct zip_stat *st,FILE *ft, + struct zip_error *error) +{ + char buf[BUFSIZE]; + ssize_t n; + + st->comp_size = 0; + while ((n=cb(ud, buf, sizeof(buf), ZIP_SOURCE_READ)) > 0) { + if (fwrite(buf, 1, n, ft) != (size_t)n) { + _zip_error_set(error, ZIP_ER_WRITE, errno); + return -1; + } + + st->comp_size += n; + } + if (n < 0) { + ch_set_error(error, cb, ud); + return -1; + } + + return 0; +} + + + +static int +add_data_uncomp(struct zip *za, zip_source_callback cb, void *ud, + struct zip_stat *st, FILE *ft) +{ + char b1[BUFSIZE], b2[BUFSIZE]; + int end, flush, ret; + ssize_t n; + size_t n2; + z_stream zstr; + int mem_level; + + st->comp_method = ZIP_CM_DEFLATE; + st->comp_size = st->size = 0; + st->crc = crc32(0, NULL, 0); + + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = NULL; + zstr.avail_in = 0; + zstr.avail_out = 0; + + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) + mem_level = TORRENT_MEM_LEVEL; + else + mem_level = MAX_MEM_LEVEL; + + /* -MAX_WBITS: undocumented feature of zlib to _not_ write a zlib header */ + deflateInit2(&zstr, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, mem_level, + Z_DEFAULT_STRATEGY); + + zstr.next_out = (Bytef *)b2; + zstr.avail_out = sizeof(b2); + zstr.next_in = NULL; + zstr.avail_in = 0; + + flush = 0; + end = 0; + while (!end) { + if (zstr.avail_in == 0 && !flush) { + if ((n=cb(ud, b1, sizeof(b1), ZIP_SOURCE_READ)) < 0) { + ch_set_error(&za->error, cb, ud); + deflateEnd(&zstr); + return -1; + } + if (n > 0) { + zstr.avail_in = n; + zstr.next_in = (Bytef *)b1; + st->size += n; + st->crc = crc32(st->crc, (Bytef *)b1, n); + } + else + flush = Z_FINISH; + } + + ret = deflate(&zstr, flush); + if (ret != Z_OK && ret != Z_STREAM_END) { + _zip_error_set(&za->error, ZIP_ER_ZLIB, ret); + return -1; + } + + if (zstr.avail_out != sizeof(b2)) { + n2 = sizeof(b2) - zstr.avail_out; + + if (fwrite(b2, 1, n2, ft) != n2) { + _zip_error_set(&za->error, ZIP_ER_WRITE, errno); + return -1; + } + + zstr.next_out = (Bytef *)b2; + zstr.avail_out = sizeof(b2); + st->comp_size += n2; + } + + if (ret == Z_STREAM_END) { + deflateEnd(&zstr); + end = 1; + } + } + + return 0; +} + + + +static void +ch_set_error(struct zip_error *error, zip_source_callback cb, void *ud) +{ + int e[2]; + + if ((cb(ud, e, sizeof(e), ZIP_SOURCE_ERROR)) < (ssize_t)sizeof(e)) { + error->zip_err = ZIP_ER_INTERNAL; + error->sys_err = 0; + } + else { + error->zip_err = e[0]; + error->sys_err = e[1]; + } +} + + + +static int +copy_data(FILE *fs, off_t len, FILE *ft, struct zip_error *error) +{ + char buf[BUFSIZE]; + int n, nn; + + if (len == 0) + return 0; + + while (len > 0) { + nn = len > sizeof(buf) ? sizeof(buf) : len; + if ((n=fread(buf, 1, nn, fs)) < 0) { + _zip_error_set(error, ZIP_ER_READ, errno); + return -1; + } + else if (n == 0) { + _zip_error_set(error, ZIP_ER_EOF, 0); + return -1; + } + + if (fwrite(buf, 1, n, ft) != (size_t)n) { + _zip_error_set(error, ZIP_ER_WRITE, errno); + return -1; + } + + len -= n; + } + + return 0; +} + + + +static int +write_cdir(struct zip *za, struct zip_cdir *cd, FILE *out) +{ + off_t offset; + uLong crc; + char buf[TORRENT_CRC_LEN+1]; + + if (_zip_cdir_write(cd, out, &za->error) < 0) + return -1; + + if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0) + return 0; + + + /* fix up torrentzip comment */ + + offset = ftello(out); + + if (_zip_filerange_crc(out, cd->offset, cd->size, &crc, &za->error) < 0) + return -1; + + snprintf(buf, sizeof(buf), "%08lX", (long)crc); + + if (fseeko(out, offset-TORRENT_CRC_LEN, SEEK_SET) < 0) { + _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + return -1; + } + + if (fwrite(buf, TORRENT_CRC_LEN, 1, out) != 1) { + _zip_error_set(&za->error, ZIP_ER_WRITE, errno); + return -1; + } + + return 0; +} + + + +static int +_zip_cdir_set_comment(struct zip_cdir *dest, struct zip *src) +{ + if (src->ch_comment_len != -1) { + dest->comment = _zip_memdup(src->ch_comment, + src->ch_comment_len, &src->error); + if (dest->comment == NULL) + return -1; + dest->comment_len = src->ch_comment_len; + } else { + if (src->cdir && src->cdir->comment) { + dest->comment = _zip_memdup(src->cdir->comment, + src->cdir->comment_len, &src->error); + if (dest->comment == NULL) + return -1; + dest->comment_len = src->cdir->comment_len; + } + } + + return 0; +} + + + +static int +_zip_changed(struct zip *za, int *survivorsp) +{ + int changed, i, survivors; + + changed = survivors = 0; + + if (za->ch_comment_len != -1 + || za->ch_flags != za->flags) + changed = 1; + + for (i=0; inentry; i++) { + if ((za->entry[i].state != ZIP_ST_UNCHANGED) + || (za->entry[i].ch_comment_len != -1)) + changed = 1; + if (za->entry[i].state != ZIP_ST_DELETED) + survivors++; + } + + *survivorsp = survivors; + + return changed; +} + + + +static char * +_zip_create_temp_output(struct zip *za, FILE **outp) +{ + char *temp; + int tfd; + FILE *tfp; + + if ((temp=(char *)malloc(strlen(za->zn)+8)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + sprintf(temp, "%s.XXXXXX", za->zn); + + if ((tfd=mkstemp(temp)) == -1) { + _zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return NULL; + } + + if ((tfp=fdopen(tfd, "r+b")) == NULL) { + _zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno); + close(tfd); + remove(temp); + free(temp); + return NULL; + } + + *outp = tfp; + return temp; +} + + + +static int +_zip_torrentzip_cmp(const void *a, const void *b) +{ + return strcasecmp(((const struct filelist *)a)->name, + ((const struct filelist *)b)->name); +} diff --git a/ext/libzip/zip_delete.c b/ext/libzip/zip_delete.c new file mode 100644 index 0000000000..a0545e4b9e --- /dev/null +++ b/ext/libzip/zip_delete.c @@ -0,0 +1,58 @@ +/* + zip_delete.c -- delete file from zip archive + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_delete(struct zip *za, int idx) +{ + if (idx < 0 || idx >= za->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + /* allow duplicate file names, because the file will + * be removed directly afterwards */ + if (_zip_unchange(za, idx, 1) != 0) + return -1; + + za->entry[idx].state = ZIP_ST_DELETED; + + return 0; +} + + diff --git a/ext/libzip/zip_dirent.c b/ext/libzip/zip_dirent.c new file mode 100644 index 0000000000..d15b376bad --- /dev/null +++ b/ext/libzip/zip_dirent.c @@ -0,0 +1,615 @@ +/* + zip_dirent.c -- read directory entry (local or central), clean dirent + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include +#include +#include +#include +#include + +#include "zipint.h" + +static time_t _zip_d2u_time(int, int); +static char *_zip_readfpstr(FILE *, unsigned int, int, struct zip_error *); +static char *_zip_readstr(unsigned char **, int, int, struct zip_error *); +static void _zip_u2d_time(time_t, unsigned short *, unsigned short *); +static void _zip_write2(unsigned short, FILE *); +static void _zip_write4(unsigned int, FILE *); + + + +void +_zip_cdir_free(struct zip_cdir *cd) +{ + int i; + + if (!cd) + return; + + for (i=0; inentry; i++) + _zip_dirent_finalize(cd->entry+i); + free(cd->comment); + free(cd->entry); + free(cd); +} + + + +int +_zip_cdir_grow(struct zip_cdir *cd, int nentry, struct zip_error *error) +{ + struct zip_dirent *entry; + + if (nentry < cd->nentry) { + _zip_error_set(error, ZIP_ER_INTERNAL, 0); + return -1; + } + + if ((entry=((struct zip_dirent *) + realloc(cd->entry, sizeof(*(cd->entry))*nentry))) == NULL) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + return -1; + } + + cd->nentry = nentry; + cd->entry = entry; + + return 0; +} + + + +struct zip_cdir * +_zip_cdir_new(int nentry, struct zip_error *error) +{ + struct zip_cdir *cd; + + if ((cd=(struct zip_cdir *)malloc(sizeof(*cd))) == NULL) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if ((cd->entry=(struct zip_dirent *)malloc(sizeof(*(cd->entry))*nentry)) + == NULL) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + free(cd); + return NULL; + } + + /* entries must be initialized by caller */ + + cd->nentry = nentry; + cd->size = cd->offset = 0; + cd->comment = NULL; + cd->comment_len = 0; + + return cd; +} + + + +int +_zip_cdir_write(struct zip_cdir *cd, FILE *fp, struct zip_error *error) +{ + int i; + + cd->offset = ftello(fp); + + for (i=0; inentry; i++) { + if (_zip_dirent_write(cd->entry+i, fp, 0, error) != 0) + return -1; + } + + cd->size = ftello(fp) - cd->offset; + + /* clearerr(fp); */ + fwrite(EOCD_MAGIC, 1, 4, fp); + _zip_write4(0, fp); + _zip_write2((unsigned short)cd->nentry, fp); + _zip_write2((unsigned short)cd->nentry, fp); + _zip_write4(cd->size, fp); + _zip_write4(cd->offset, fp); + _zip_write2(cd->comment_len, fp); + fwrite(cd->comment, 1, cd->comment_len, fp); + + if (ferror(fp)) { + _zip_error_set(error, ZIP_ER_WRITE, errno); + return -1; + } + + return 0; +} + + + +void +_zip_dirent_finalize(struct zip_dirent *zde) +{ + free(zde->filename); + zde->filename = NULL; + free(zde->extrafield); + zde->extrafield = NULL; + free(zde->comment); + zde->comment = NULL; +} + + + +void +_zip_dirent_init(struct zip_dirent *de) +{ + de->version_madeby = 0; + de->version_needed = 20; /* 2.0 */ + de->bitflags = 0; + de->comp_method = 0; + de->last_mod = 0; + de->crc = 0; + de->comp_size = 0; + de->uncomp_size = 0; + de->filename = NULL; + de->filename_len = 0; + de->extrafield = NULL; + de->extrafield_len = 0; + de->comment = NULL; + de->comment_len = 0; + de->disk_number = 0; + de->int_attrib = 0; + de->ext_attrib = 0; + de->offset = 0; +} + + + +/* _zip_dirent_read(zde, fp, bufp, left, localp, error): + Fills the zip directory entry zde. + + If bufp is non-NULL, data is taken from there and bufp is advanced + by the amount of data used; otherwise data is read from fp as needed. + + if leftp is non-NULL, no more bytes than specified by it are used, + and *leftp is reduced by the number of bytes used. + + If local != 0, it reads a local header instead of a central + directory entry. + + Returns 0 if successful. On error, error is filled in and -1 is + returned. + + XXX: leftp and file position undefined on error. +*/ + +int +_zip_dirent_read(struct zip_dirent *zde, FILE *fp, + unsigned char **bufp, unsigned int *leftp, int local, + struct zip_error *error) +{ + unsigned char buf[CDENTRYSIZE]; + unsigned char *cur; + unsigned short dostime, dosdate; + unsigned int size; + + if (local) + size = LENTRYSIZE; + else + size = CDENTRYSIZE; + + if (leftp && (*leftp < size)) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return -1; + } + + if (bufp) { + /* use data from buffer */ + cur = *bufp; + } + else { + /* read entry from disk */ + if ((fread(buf, 1, size, fp)version_madeby = _zip_read2(&cur); + else + zde->version_madeby = 0; + zde->version_needed = _zip_read2(&cur); + zde->bitflags = _zip_read2(&cur); + zde->comp_method = _zip_read2(&cur); + + /* convert to time_t */ + dostime = _zip_read2(&cur); + dosdate = _zip_read2(&cur); + zde->last_mod = _zip_d2u_time(dostime, dosdate); + + zde->crc = _zip_read4(&cur); + zde->comp_size = _zip_read4(&cur); + zde->uncomp_size = _zip_read4(&cur); + + zde->filename_len = _zip_read2(&cur); + zde->extrafield_len = _zip_read2(&cur); + + if (local) { + zde->comment_len = 0; + zde->disk_number = 0; + zde->int_attrib = 0; + zde->ext_attrib = 0; + zde->offset = 0; + } else { + zde->comment_len = _zip_read2(&cur); + zde->disk_number = _zip_read2(&cur); + zde->int_attrib = _zip_read2(&cur); + zde->ext_attrib = _zip_read4(&cur); + zde->offset = _zip_read4(&cur); + } + + zde->filename = NULL; + zde->extrafield = NULL; + zde->comment = NULL; + + size += zde->filename_len+zde->extrafield_len+zde->comment_len; + + if (leftp && (*leftp < size)) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return -1; + } + + if (bufp) { + if (zde->filename_len) { + zde->filename = _zip_readstr(&cur, zde->filename_len, 1, error); + if (!zde->filename) + return -1; + } + + if (zde->extrafield_len) { + zde->extrafield = _zip_readstr(&cur, zde->extrafield_len, 0, + error); + if (!zde->extrafield) + return -1; + } + + if (zde->comment_len) { + zde->comment = _zip_readstr(&cur, zde->comment_len, 0, error); + if (!zde->comment) + return -1; + } + } + else { + if (zde->filename_len) { + zde->filename = _zip_readfpstr(fp, zde->filename_len, 1, error); + if (!zde->filename) + return -1; + } + + if (zde->extrafield_len) { + zde->extrafield = _zip_readfpstr(fp, zde->extrafield_len, 0, + error); + if (!zde->extrafield) + return -1; + } + + if (zde->comment_len) { + zde->comment = _zip_readfpstr(fp, zde->comment_len, 0, error); + if (!zde->comment) + return -1; + } + } + + if (bufp) + *bufp = cur; + if (leftp) + *leftp -= size; + + return 0; +} + + + +/* _zip_dirent_torrent_normalize(de); + Set values suitable for torrentzip. +*/ + +void +_zip_dirent_torrent_normalize(struct zip_dirent *de) +{ + static struct tm torrenttime; + static time_t last_mod = 0; + + if (last_mod == 0) { +#ifdef HAVE_STRUCT_TM_TM_ZONE + time_t now; + struct tm *l; +#endif + + torrenttime.tm_sec = 0; + torrenttime.tm_min = 32; + torrenttime.tm_hour = 23; + torrenttime.tm_mday = 24; + torrenttime.tm_mon = 11; + torrenttime.tm_year = 96; + torrenttime.tm_wday = 0; + torrenttime.tm_yday = 0; + torrenttime.tm_isdst = 0; + +#ifdef HAVE_STRUCT_TM_TM_ZONE + time(&now); + l = localtime(&now); + torrenttime.tm_gmtoff = l->tm_gmtoff; + torrenttime.tm_zone = l->tm_zone; +#endif + + last_mod = mktime(&torrenttime); + } + + de->version_madeby = 0; + de->version_needed = 20; /* 2.0 */ + de->bitflags = 2; /* maximum compression */ + de->comp_method = ZIP_CM_DEFLATE; + de->last_mod = last_mod; + + de->disk_number = 0; + de->int_attrib = 0; + de->ext_attrib = 0; + de->offset = 0; + + free(de->extrafield); + de->extrafield = NULL; + de->extrafield_len = 0; + free(de->comment); + de->comment = NULL; + de->comment_len = 0; +} + + + +/* _zip_dirent_write(zde, fp, localp, error): + Writes zip directory entry zde to file fp. + + If localp != 0, it writes a local header instead of a central + directory entry. + + Returns 0 if successful. On error, error is filled in and -1 is + returned. +*/ + +int +_zip_dirent_write(struct zip_dirent *zde, FILE *fp, int localp, + struct zip_error *error) +{ + unsigned short dostime, dosdate; + + fwrite(localp ? LOCAL_MAGIC : CENTRAL_MAGIC, 1, 4, fp); + + if (!localp) + _zip_write2(zde->version_madeby, fp); + _zip_write2(zde->version_needed, fp); + _zip_write2(zde->bitflags, fp); + _zip_write2(zde->comp_method, fp); + + _zip_u2d_time(zde->last_mod, &dostime, &dosdate); + _zip_write2(dostime, fp); + _zip_write2(dosdate, fp); + + _zip_write4(zde->crc, fp); + _zip_write4(zde->comp_size, fp); + _zip_write4(zde->uncomp_size, fp); + + _zip_write2(zde->filename_len, fp); + _zip_write2(zde->extrafield_len, fp); + + if (!localp) { + _zip_write2(zde->comment_len, fp); + _zip_write2(zde->disk_number, fp); + _zip_write2(zde->int_attrib, fp); + _zip_write4(zde->ext_attrib, fp); + _zip_write4(zde->offset, fp); + } + + if (zde->filename_len) + fwrite(zde->filename, 1, zde->filename_len, fp); + + if (zde->extrafield_len) + fwrite(zde->extrafield, 1, zde->extrafield_len, fp); + + if (!localp) { + if (zde->comment_len) + fwrite(zde->comment, 1, zde->comment_len, fp); + } + + if (ferror(fp)) { + _zip_error_set(error, ZIP_ER_WRITE, errno); + return -1; + } + + return 0; +} + + + +static time_t +_zip_d2u_time(int dtime, int ddate) +{ + struct tm tm; + + memset(&tm, 0, sizeof(tm)); + + /* let mktime decide if DST is in effect */ + tm.tm_isdst = -1; + + tm.tm_year = ((ddate>>9)&127) + 1980 - 1900; + tm.tm_mon = ((ddate>>5)&15) - 1; + tm.tm_mday = ddate&31; + + tm.tm_hour = (dtime>>11)&31; + tm.tm_min = (dtime>>5)&63; + tm.tm_sec = (dtime<<1)&62; + + return mktime(&tm); +} + + + +unsigned short +_zip_read2(unsigned char **a) +{ + unsigned short ret; + + ret = (*a)[0]+((*a)[1]<<8); + *a += 2; + + return ret; +} + + + +unsigned int +_zip_read4(unsigned char **a) +{ + unsigned int ret; + + ret = ((((((*a)[3]<<8)+(*a)[2])<<8)+(*a)[1])<<8)+(*a)[0]; + *a += 4; + + return ret; +} + + + +static char * +_zip_readfpstr(FILE *fp, unsigned int len, int nulp, struct zip_error *error) +{ + char *r, *o; + + r = (char *)malloc(nulp ? len+1 : len); + if (!r) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if (fread(r, 1, len, fp)>8)&0xff, fp); + + return; +} + + + +static void +_zip_write4(unsigned int i, FILE *fp) +{ + putc(i&0xff, fp); + putc((i>>8)&0xff, fp); + putc((i>>16)&0xff, fp); + putc((i>>24)&0xff, fp); + + return; +} + + + +static void +_zip_u2d_time(time_t time, unsigned short *dtime, unsigned short *ddate) +{ + struct tm *tm; + + tm = localtime(&time); + *ddate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + + tm->tm_mday; + *dtime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + + ((tm->tm_sec)>>1); + + return; +} diff --git a/ext/libzip/zip_entry_free.c b/ext/libzip/zip_entry_free.c new file mode 100644 index 0000000000..c50c9434bd --- /dev/null +++ b/ext/libzip/zip_entry_free.c @@ -0,0 +1,52 @@ +/* + zip_entry_free.c -- free struct zip_entry + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +void +_zip_entry_free(struct zip_entry *ze) +{ + free(ze->ch_filename); + ze->ch_filename = NULL; + free(ze->ch_comment); + ze->ch_comment = NULL; + ze->ch_comment_len = -1; + + _zip_unchange_data(ze); +} diff --git a/ext/libzip/zip_entry_new.c b/ext/libzip/zip_entry_new.c new file mode 100644 index 0000000000..7059b1b060 --- /dev/null +++ b/ext/libzip/zip_entry_new.c @@ -0,0 +1,78 @@ +/* + zip_entry_new.c -- create and init struct zip_entry + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +struct zip_entry * +_zip_entry_new(struct zip *za) +{ + struct zip_entry *ze; + if (!za) { + ze = (struct zip_entry *)malloc(sizeof(struct zip_entry)); + if (!ze) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + } + else { + if (za->nentry >= za->nentry_alloc-1) { + za->nentry_alloc += 16; + za->entry = (struct zip_entry *)realloc(za->entry, + sizeof(struct zip_entry) + * za->nentry_alloc); + if (!za->entry) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + } + ze = za->entry+za->nentry; + } + + ze->state = ZIP_ST_UNCHANGED; + + ze->ch_filename = NULL; + ze->ch_comment = NULL; + ze->ch_comment_len = -1; + ze->source = NULL; + + if (za) + za->nentry++; + + return ze; +} diff --git a/ext/libzip/zip_err_str.c b/ext/libzip/zip_err_str.c new file mode 100644 index 0000000000..3fcdf1738a --- /dev/null +++ b/ext/libzip/zip_err_str.c @@ -0,0 +1,68 @@ +/* + This file was generated automatically by ./make_zip_err_str.sh + from ./zip.h; make changes there. + */ + +#include "zipint.h" + + + +const char * const _zip_err_str[] = { + "No error", + "Multi-disk zip archives not supported", + "Renaming temporary file failed", + "Closing zip archive failed", + "Seek error", + "Read error", + "Write error", + "CRC error", + "Containing zip archive was closed", + "No such file", + "File already exists", + "Can't open file", + "Failure to create temporary file", + "Zlib error", + "Malloc failure", + "Entry has been changed", + "Compression method not supported", + "Premature EOF", + "Invalid argument", + "Not a zip archive", + "Internal error", + "Zip archive inconsistent", + "Can't remove file", + "Entry has been deleted", +}; + +const int _zip_nerr_str = sizeof(_zip_err_str)/sizeof(_zip_err_str[0]); + +#define N ZIP_ET_NONE +#define S ZIP_ET_SYS +#define Z ZIP_ET_ZLIB + +const int _zip_err_type[] = { + N, + N, + S, + S, + S, + S, + S, + N, + N, + N, + N, + S, + S, + Z, + N, + N, + N, + N, + N, + N, + N, + N, + S, + N, +}; diff --git a/ext/libzip/zip_error.c b/ext/libzip/zip_error.c new file mode 100644 index 0000000000..aab7079456 --- /dev/null +++ b/ext/libzip/zip_error.c @@ -0,0 +1,101 @@ +/* + zip_error.c -- struct zip_error helper functions + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +void +_zip_error_clear(struct zip_error *err) +{ + err->zip_err = ZIP_ER_OK; + err->sys_err = 0; +} + + + +void +_zip_error_copy(struct zip_error *dst, struct zip_error *src) +{ + dst->zip_err = src->zip_err; + dst->sys_err = src->sys_err; +} + + + +void +_zip_error_fini(struct zip_error *err) +{ + free(err->str); + err->str = NULL; +} + + + +void +_zip_error_get(struct zip_error *err, int *zep, int *sep) +{ + if (zep) + *zep = err->zip_err; + if (sep) { + if (zip_error_get_sys_type(err->zip_err) != ZIP_ET_NONE) + *sep = err->sys_err; + else + *sep = 0; + } +} + + + +void +_zip_error_init(struct zip_error *err) +{ + err->zip_err = ZIP_ER_OK; + err->sys_err = 0; + err->str = NULL; +} + + + +void +_zip_error_set(struct zip_error *err, int ze, int se) +{ + if (err) { + err->zip_err = ze; + err->sys_err = se; + } +} diff --git a/ext/libzip/zip_error_clear.c b/ext/libzip/zip_error_clear.c new file mode 100644 index 0000000000..364383ebe9 --- /dev/null +++ b/ext/libzip/zip_error_clear.c @@ -0,0 +1,44 @@ +/* + zip_error_clear.c -- clear zip error + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN void +zip_error_clear(struct zip *za) +{ + _zip_error_clear(&za->error); +} diff --git a/ext/libzip/zip_error_get.c b/ext/libzip/zip_error_get.c new file mode 100644 index 0000000000..6d1c958c17 --- /dev/null +++ b/ext/libzip/zip_error_get.c @@ -0,0 +1,44 @@ +/* + zip_error_get.c -- get zip error + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN void +zip_error_get(struct zip *za, int *zep, int *sep) +{ + _zip_error_get(&za->error, zep, sep); +} diff --git a/ext/libzip/zip_error_get_sys_type.c b/ext/libzip/zip_error_get_sys_type.c new file mode 100644 index 0000000000..6c6f380740 --- /dev/null +++ b/ext/libzip/zip_error_get_sys_type.c @@ -0,0 +1,47 @@ +/* + zip_error_get_sys_type.c -- return type of system error code + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_error_get_sys_type(int ze) +{ + if (ze < 0 || ze >= _zip_nerr_str) + return 0; + + return _zip_err_type[ze]; +} diff --git a/ext/libzip/zip_error_strerror.c b/ext/libzip/zip_error_strerror.c new file mode 100644 index 0000000000..3d0951cfb1 --- /dev/null +++ b/ext/libzip/zip_error_strerror.c @@ -0,0 +1,90 @@ +/* + zip_error_sterror.c -- get string representation of struct zip_error + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include +#include +#include + +#include "zipint.h" + + + +const char * +_zip_error_strerror(struct zip_error *err) +{ + const char *zs, *ss; + char buf[128], *s; + + _zip_error_fini(err); + + if (err->zip_err < 0 || err->zip_err >= _zip_nerr_str) { + sprintf(buf, "Unknown error %d", err->zip_err); + zs = NULL; + ss = buf; + } + else { + zs = _zip_err_str[err->zip_err]; + + switch (_zip_err_type[err->zip_err]) { + case ZIP_ET_SYS: + ss = strerror(err->sys_err); + break; + + case ZIP_ET_ZLIB: + ss = zError(err->sys_err); + break; + + default: + ss = NULL; + } + } + + if (ss == NULL) + return zs; + else { + if ((s=(char *)malloc(strlen(ss) + + (zs ? strlen(zs)+2 : 0) + 1)) == NULL) + return _zip_err_str[ZIP_ER_MEMORY]; + + sprintf(s, "%s%s%s", + (zs ? zs : ""), + (zs ? ": " : ""), + ss); + err->str = s; + + return s; + } +} diff --git a/ext/libzip/zip_error_to_str.c b/ext/libzip/zip_error_to_str.c new file mode 100644 index 0000000000..bdba41a6cb --- /dev/null +++ b/ext/libzip/zip_error_to_str.c @@ -0,0 +1,70 @@ +/* + zip_error_to_str.c -- get string representation of zip error code + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include +#include +#include + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_error_to_str(char *buf, size_t len, int ze, int se) +{ + const char *zs, *ss; + + if (ze < 0 || ze >= _zip_nerr_str) + return snprintf(buf, len, "Unknown error %d", ze); + + zs = _zip_err_str[ze]; + + switch (_zip_err_type[ze]) { + case ZIP_ET_SYS: + ss = strerror(se); + break; + + case ZIP_ET_ZLIB: + ss = zError(se); + break; + + default: + ss = NULL; + } + + return snprintf(buf, len, "%s%s%s", + zs, (ss ? ": " : ""), (ss ? ss : "")); +} diff --git a/ext/libzip/zip_fclose.c b/ext/libzip/zip_fclose.c new file mode 100644 index 0000000000..8709362d6c --- /dev/null +++ b/ext/libzip/zip_fclose.c @@ -0,0 +1,71 @@ +/* + zip_fclose.c -- close file in zip archive + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_fclose(struct zip_file *zf) +{ + int i, ret; + + if (zf->zstr) + inflateEnd(zf->zstr); + free(zf->buffer); + free(zf->zstr); + + for (i=0; iza->nfile; i++) { + if (zf->za->file[i] == zf) { + zf->za->file[i] = zf->za->file[zf->za->nfile-1]; + zf->za->nfile--; + break; + } + } + + ret = 0; + if (zf->error.zip_err) + ret = zf->error.zip_err; + else if ((zf->flags & ZIP_ZF_CRC) && (zf->flags & ZIP_ZF_EOF)) { + /* if EOF, compare CRC */ + if (zf->crc_orig != zf->crc) + ret = ZIP_ER_CRC; + } + + free(zf); + return ret; +} diff --git a/ext/libzip/zip_file_error_clear.c b/ext/libzip/zip_file_error_clear.c new file mode 100644 index 0000000000..ef6fa10e4d --- /dev/null +++ b/ext/libzip/zip_file_error_clear.c @@ -0,0 +1,44 @@ +/* + zip_file_error_clear.c -- clear zip file error + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN void +zip_file_error_clear(struct zip_file *zf) +{ + _zip_error_clear(&zf->error); +} diff --git a/ext/libzip/zip_file_error_get.c b/ext/libzip/zip_file_error_get.c new file mode 100644 index 0000000000..f20e011fc7 --- /dev/null +++ b/ext/libzip/zip_file_error_get.c @@ -0,0 +1,44 @@ +/* + zip_file_error_get.c -- get zip file error + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN void +zip_file_error_get(struct zip_file *zf, int *zep, int *sep) +{ + _zip_error_get(&zf->error, zep, sep); +} diff --git a/ext/libzip/zip_file_get_offset.c b/ext/libzip/zip_file_get_offset.c new file mode 100644 index 0000000000..b96fd5e480 --- /dev/null +++ b/ext/libzip/zip_file_get_offset.c @@ -0,0 +1,74 @@ +/* + zip_file_get_offset.c -- get offset of file data in archive. + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include +#include +#include +#include +#include + +#include "zipint.h" + + + +/* _zip_file_get_offset(za, ze): + Returns the offset of the file data for entry ze. + + On error, fills in za->error and returns 0. +*/ + +unsigned int +_zip_file_get_offset(struct zip *za, int idx) +{ + struct zip_dirent de; + unsigned int offset; + + offset = za->cdir->entry[idx].offset; + + if (fseeko(za->zp, offset, SEEK_SET) != 0) { + _zip_error_set(&za->error, ZIP_ER_SEEK, errno); + return 0; + } + + if (_zip_dirent_read(&de, za->zp, NULL, NULL, 1, &za->error) != 0) + return 0; + + offset += LENTRYSIZE + de.filename_len + de.extrafield_len; + + _zip_dirent_finalize(&de); + + return offset; +} diff --git a/ext/libzip/zip_file_strerror.c b/ext/libzip/zip_file_strerror.c new file mode 100644 index 0000000000..9ba70f14ff --- /dev/null +++ b/ext/libzip/zip_file_strerror.c @@ -0,0 +1,44 @@ +/* + zip_file_sterror.c -- get string representation of zip file error + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN const char * +zip_file_strerror(struct zip_file *zf) +{ + return _zip_error_strerror(&zf->error); +} diff --git a/ext/libzip/zip_filerange_crc.c b/ext/libzip/zip_filerange_crc.c new file mode 100644 index 0000000000..4d1ad56692 --- /dev/null +++ b/ext/libzip/zip_filerange_crc.c @@ -0,0 +1,71 @@ +/* + zip_filerange_crc.c -- compute CRC32 for a range of a file + Copyright (C) 2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include + +#include "zipint.h" + + + + +int +_zip_filerange_crc(FILE *fp, off_t start, off_t len, uLong *crcp, + struct zip_error *errp) +{ + Bytef buf[BUFSIZE]; + size_t n; + + *crcp = crc32(0L, Z_NULL, 0); + + if (fseeko(fp, start, SEEK_SET) != 0) { + _zip_error_set(errp, ZIP_ER_SEEK, errno); + return -1; + } + + while (len > 0) { + n = len > BUFSIZE ? BUFSIZE : len; + if ((n=fread(buf, 1, n, fp)) <= 0) { + _zip_error_set(errp, ZIP_ER_READ, errno); + return -1; + } + + *crcp = crc32(*crcp, buf, n); + + len-= n; + } + + return 0; +} diff --git a/ext/libzip/zip_fopen.c b/ext/libzip/zip_fopen.c new file mode 100644 index 0000000000..6e2f724127 --- /dev/null +++ b/ext/libzip/zip_fopen.c @@ -0,0 +1,49 @@ +/* + zip_fopen.c -- open file in zip archive for reading + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN struct zip_file * +zip_fopen(struct zip *za, const char *fname, int flags) +{ + int idx; + + if ((idx=zip_name_locate(za, fname, flags)) < 0) + return NULL; + + return zip_fopen_index(za, idx, flags); +} diff --git a/ext/libzip/zip_fopen_index.c b/ext/libzip/zip_fopen_index.c new file mode 100644 index 0000000000..a330d697d2 --- /dev/null +++ b/ext/libzip/zip_fopen_index.c @@ -0,0 +1,216 @@ +/* + zip_fopen_index.c -- open file in zip archive for reading by index + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include +#include + +#include "zipint.h" + +static struct zip_file *_zip_file_new(struct zip *za); + + + +ZIP_EXTERN struct zip_file * +zip_fopen_index(struct zip *za, int fileno, int flags) +{ + int len, ret; + int zfflags; + struct zip_file *zf; + + if ((fileno < 0) || (fileno >= za->nentry)) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((flags & ZIP_FL_UNCHANGED) == 0 + && ZIP_ENTRY_DATA_CHANGED(za->entry+fileno)) { + _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); + return NULL; + } + + if (fileno >= za->cdir->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + zfflags = 0; + switch (za->cdir->entry[fileno].comp_method) { + case ZIP_CM_STORE: + zfflags |= ZIP_ZF_CRC; + break; + + case ZIP_CM_DEFLATE: + if ((flags & ZIP_FL_COMPRESSED) == 0) + zfflags |= ZIP_ZF_CRC | ZIP_ZF_DECOMP; + break; + default: + if ((flags & ZIP_FL_COMPRESSED) == 0) { + _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); + return NULL; + } + break; + } + + zf = _zip_file_new(za); + + zf->flags = zfflags; + /* zf->name = za->cdir->entry[fileno].filename; */ + zf->method = za->cdir->entry[fileno].comp_method; + zf->bytes_left = za->cdir->entry[fileno].uncomp_size; + zf->cbytes_left = za->cdir->entry[fileno].comp_size; + zf->crc_orig = za->cdir->entry[fileno].crc; + + if ((zf->fpos=_zip_file_get_offset(za, fileno)) == 0) { + zip_fclose(zf); + return NULL; + } + + if ((zf->flags & ZIP_ZF_DECOMP) == 0) + zf->bytes_left = zf->cbytes_left; + else { + if ((zf->buffer=(char *)malloc(BUFSIZE)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + zip_fclose(zf); + return NULL; + } + + len = _zip_file_fillbuf(zf->buffer, BUFSIZE, zf); + if (len <= 0) { + _zip_error_copy(&za->error, &zf->error); + zip_fclose(zf); + return NULL; + } + + if ((zf->zstr = (z_stream *)malloc(sizeof(z_stream))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + zip_fclose(zf); + return NULL; + } + zf->zstr->zalloc = Z_NULL; + zf->zstr->zfree = Z_NULL; + zf->zstr->opaque = NULL; + zf->zstr->next_in = (Bytef *)zf->buffer; + zf->zstr->avail_in = len; + + /* negative value to tell zlib that there is no header */ + if ((ret=inflateInit2(zf->zstr, -MAX_WBITS)) != Z_OK) { + _zip_error_set(&za->error, ZIP_ER_ZLIB, ret); + zip_fclose(zf); + return NULL; + } + } + + return zf; +} + + + +int +_zip_file_fillbuf(void *buf, size_t buflen, struct zip_file *zf) +{ + int i, j; + + if (zf->error.zip_err != ZIP_ER_OK) + return -1; + + if ((zf->flags & ZIP_ZF_EOF) || zf->cbytes_left <= 0 || buflen <= 0) + return 0; + + if (fseeko(zf->za->zp, zf->fpos, SEEK_SET) < 0) { + _zip_error_set(&zf->error, ZIP_ER_SEEK, errno); + return -1; + } + if (buflen < zf->cbytes_left) + i = buflen; + else + i = zf->cbytes_left; + + j = fread(buf, 1, i, zf->za->zp); + if (j == 0) { + _zip_error_set(&zf->error, ZIP_ER_EOF, 0); + j = -1; + } + else if (j < 0) + _zip_error_set(&zf->error, ZIP_ER_READ, errno); + else { + zf->fpos += j; + zf->cbytes_left -= j; + } + + return j; +} + + + +static struct zip_file * +_zip_file_new(struct zip *za) +{ + struct zip_file *zf, **file; + int n; + + if ((zf=(struct zip_file *)malloc(sizeof(struct zip_file))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if (za->nfile >= za->nfile_alloc-1) { + n = za->nfile_alloc + 10; + file = (struct zip_file **)realloc(za->file, + n*sizeof(struct zip_file *)); + if (file == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + free(zf); + return NULL; + } + za->nfile_alloc = n; + za->file = file; + } + + za->file[za->nfile++] = zf; + + zf->za = za; + _zip_error_init(&zf->error); + zf->flags = 0; + zf->crc = crc32(0L, Z_NULL, 0); + zf->crc_orig = 0; + zf->method = -1; + zf->bytes_left = zf->cbytes_left = 0; + zf->fpos = 0; + zf->buffer = NULL; + zf->zstr = NULL; + + return zf; +} diff --git a/ext/libzip/zip_fread.c b/ext/libzip/zip_fread.c new file mode 100644 index 0000000000..2a7fb42c78 --- /dev/null +++ b/ext/libzip/zip_fread.c @@ -0,0 +1,130 @@ +/* + zip_fread.c -- read from file + Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN ssize_t +zip_fread(struct zip_file *zf, void *outbuf, size_t toread) +{ + int ret; + size_t out_before, len; + int i; + + if (!zf) + return -1; + + if (zf->error.zip_err != 0) + return -1; + + if ((zf->flags & ZIP_ZF_EOF) || (toread == 0)) + return 0; + + if (zf->bytes_left == 0) { + zf->flags |= ZIP_ZF_EOF; + if (zf->flags & ZIP_ZF_CRC) { + if (zf->crc != zf->crc_orig) { + _zip_error_set(&zf->error, ZIP_ER_CRC, 0); + return -1; + } + } + return 0; + } + + if ((zf->flags & ZIP_ZF_DECOMP) == 0) { + ret = _zip_file_fillbuf(outbuf, toread, zf); + if (ret > 0) { + if (zf->flags & ZIP_ZF_CRC) + zf->crc = crc32(zf->crc, (Bytef *)outbuf, ret); + zf->bytes_left -= ret; + } + return ret; + } + + zf->zstr->next_out = (Bytef *)outbuf; + zf->zstr->avail_out = toread; + out_before = zf->zstr->total_out; + + /* endless loop until something has been accomplished */ + for (;;) { + ret = inflate(zf->zstr, Z_SYNC_FLUSH); + + switch (ret) { + case Z_STREAM_END: + if (zf->zstr->total_out == out_before) { + if (zf->crc != zf->crc_orig) { + _zip_error_set(&zf->error, ZIP_ER_CRC, 0); + return -1; + } + else + return 0; + } + + /* fallthrough */ + + case Z_OK: + len = zf->zstr->total_out - out_before; + if (len >= zf->bytes_left || len >= toread) { + if (zf->flags & ZIP_ZF_CRC) + zf->crc = crc32(zf->crc, (Bytef *)outbuf, len); + zf->bytes_left -= len; + return len; + } + break; + + case Z_BUF_ERROR: + if (zf->zstr->avail_in == 0) { + i = _zip_file_fillbuf(zf->buffer, BUFSIZE, zf); + if (i == 0) { + _zip_error_set(&zf->error, ZIP_ER_INCONS, 0); + return -1; + } + else if (i < 0) + return -1; + zf->zstr->next_in = (Bytef *)zf->buffer; + zf->zstr->avail_in = i; + continue; + } + /* fallthrough */ + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_STREAM_ERROR: + case Z_MEM_ERROR: + _zip_error_set(&zf->error, ZIP_ER_ZLIB, ret); + return -1; + } + } +} diff --git a/ext/libzip/zip_free.c b/ext/libzip/zip_free.c new file mode 100644 index 0000000000..76c3a9673f --- /dev/null +++ b/ext/libzip/zip_free.c @@ -0,0 +1,81 @@ +/* + zip_free.c -- free struct zip + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +/* _zip_free: + frees the space allocated to a zipfile struct, and closes the + corresponding file. */ + +void +_zip_free(struct zip *za) +{ + int i; + + if (za == NULL) + return; + + if (za->zn) + free(za->zn); + + if (za->zp) + fclose(za->zp); + + _zip_cdir_free(za->cdir); + + if (za->entry) { + for (i=0; inentry; i++) { + _zip_entry_free(za->entry+i); + } + free(za->entry); + } + + for (i=0; infile; i++) { + if (za->file[i]->error.zip_err == ZIP_ER_OK) { + _zip_error_set(&za->file[i]->error, ZIP_ER_ZIPCLOSED, 0); + za->file[i]->za = NULL; + } + } + + free(za->file); + + free(za); + + return; +} diff --git a/ext/libzip/zip_get_archive_comment.c b/ext/libzip/zip_get_archive_comment.c new file mode 100644 index 0000000000..669eb70216 --- /dev/null +++ b/ext/libzip/zip_get_archive_comment.c @@ -0,0 +1,60 @@ +/* + zip_get_archive_comment.c -- get archive comment + Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN const char * +zip_get_archive_comment(struct zip *za, int *lenp, int flags) +{ + if ((flags & ZIP_FL_UNCHANGED) + || (za->ch_comment_len == -1)) { + if (za->cdir) { + if (lenp != NULL) + *lenp = za->cdir->comment_len; + return za->cdir->comment; + } + else { + if (lenp != NULL) + *lenp = -1; + return NULL; + } + } + + if (lenp != NULL) + *lenp = za->ch_comment_len; + return za->ch_comment; +} diff --git a/ext/libzip/zip_get_archive_flag.c b/ext/libzip/zip_get_archive_flag.c new file mode 100644 index 0000000000..4733d92b58 --- /dev/null +++ b/ext/libzip/zip_get_archive_flag.c @@ -0,0 +1,48 @@ +/* + zip_get_archive_flag.c -- get archive global flag + Copyright (C) 2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_get_archive_flag(struct zip *za, int flag, int flags) +{ + int fl; + + fl = (flags & ZIP_FL_UNCHANGED) ? za->flags : za->ch_flags; + + return (fl & flag) ? 1 : 0; +} diff --git a/ext/libzip/zip_get_file_comment.c b/ext/libzip/zip_get_file_comment.c new file mode 100644 index 0000000000..adace72474 --- /dev/null +++ b/ext/libzip/zip_get_file_comment.c @@ -0,0 +1,58 @@ +/* + zip_get_file_comment.c -- get file comment + Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN const char * +zip_get_file_comment(struct zip *za, int idx, int *lenp, int flags) +{ + if (idx < 0 || idx >= za->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((flags & ZIP_FL_UNCHANGED) + || (za->entry[idx].ch_comment_len == -1)) { + if (lenp != NULL) + *lenp = za->cdir->entry[idx].comment_len; + return za->cdir->entry[idx].comment; + } + + if (lenp != NULL) + *lenp = za->entry[idx].ch_comment_len; + return za->entry[idx].ch_comment; +} diff --git a/ext/libzip/zip_get_name.c b/ext/libzip/zip_get_name.c new file mode 100644 index 0000000000..8f5050bd2e --- /dev/null +++ b/ext/libzip/zip_get_name.c @@ -0,0 +1,71 @@ +/* + zip_get_name.c -- get filename for a file in zip file + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN const char * +zip_get_name(struct zip *za, int idx, int flags) +{ + return _zip_get_name(za, idx, flags, &za->error); +} + + + +const char * +_zip_get_name(struct zip *za, int idx, int flags, struct zip_error *error) +{ + if (idx < 0 || idx >= za->nentry) { + _zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((flags & ZIP_FL_UNCHANGED) == 0) { + if (za->entry[idx].state == ZIP_ST_DELETED) { + _zip_error_set(error, ZIP_ER_DELETED, 0); + return NULL; + } + if (za->entry[idx].ch_filename) + return za->entry[idx].ch_filename; + } + + if (za->cdir == NULL || idx >= za->cdir->nentry) { + _zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return za->cdir->entry[idx].filename; +} diff --git a/ext/libzip/zip_get_num_files.c b/ext/libzip/zip_get_num_files.c new file mode 100644 index 0000000000..1d9baa3d65 --- /dev/null +++ b/ext/libzip/zip_get_num_files.c @@ -0,0 +1,47 @@ +/* + zip_get_num_files.c -- get number of files in archive + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_get_num_files(struct zip *za) +{ + if (za == NULL) + return -1; + + return za->nentry; +} diff --git a/ext/libzip/zip_memdup.c b/ext/libzip/zip_memdup.c new file mode 100644 index 0000000000..641125ed2d --- /dev/null +++ b/ext/libzip/zip_memdup.c @@ -0,0 +1,55 @@ +/* + zip_memdup.c -- internal zip function, "strdup" with len + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "zipint.h" + + + +void * +_zip_memdup(const void *mem, size_t len, struct zip_error *error) +{ + void *ret; + + ret = malloc(len); + if (!ret) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + memcpy(ret, mem, len); + + return ret; +} diff --git a/ext/libzip/zip_name_locate.c b/ext/libzip/zip_name_locate.c new file mode 100644 index 0000000000..baa6141f67 --- /dev/null +++ b/ext/libzip/zip_name_locate.c @@ -0,0 +1,88 @@ +/* + zip_name_locate.c -- get index by name + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_name_locate(struct zip *za, const char *fname, int flags) +{ + return _zip_name_locate(za, fname, flags, &za->error); +} + + + +int +_zip_name_locate(struct zip *za, const char *fname, int flags, + struct zip_error *error) +{ + int (*cmp)(const char *, const char *); + const char *fn, *p; + int i, n; + + if (fname == NULL) { + _zip_error_set(error, ZIP_ER_INVAL, 0); + return -1; + } + + cmp = (flags & ZIP_FL_NOCASE) ? strcasecmp : strcmp; + + n = (flags & ZIP_FL_UNCHANGED) ? za->cdir->nentry : za->nentry; + for (i=0; icdir->entry[i].filename; + else + fn = _zip_get_name(za, i, flags, error); + + /* newly added (partially filled) entry */ + if (fn == NULL) + continue; + + if (flags & ZIP_FL_NODIR) { + p = strrchr(fn, '/'); + if (p) + fn = p+1; + } + + if (cmp(fname, fn) == 0) + return i; + } + + _zip_error_set(error, ZIP_ER_NOENT, 0); + return -1; +} diff --git a/ext/libzip/zip_new.c b/ext/libzip/zip_new.c new file mode 100644 index 0000000000..3e8ccee644 --- /dev/null +++ b/ext/libzip/zip_new.c @@ -0,0 +1,70 @@ +/* + zip_new.c -- create and init struct zip + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +/* _zip_new: + creates a new zipfile struct, and sets the contents to zero; returns + the new struct. */ + +struct zip * +_zip_new(struct zip_error *error) +{ + struct zip *za; + + za = (struct zip *)malloc(sizeof(struct zip)); + if (!za) { + _zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + za->zn = NULL; + za->zp = NULL; + _zip_error_init(&za->error); + za->cdir = NULL; + za->ch_comment = NULL; + za->ch_comment_len = -1; + za->nentry = za->nentry_alloc = 0; + za->entry = NULL; + za->nfile = za->nfile_alloc = 0; + za->file = NULL; + za->flags = za->ch_flags = 0; + + return za; +} diff --git a/ext/libzip/zip_open.c b/ext/libzip/zip_open.c new file mode 100644 index 0000000000..a65974df95 --- /dev/null +++ b/ext/libzip/zip_open.c @@ -0,0 +1,567 @@ +/* + zip_open.c -- open zip archive + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include +#include +#include +#include +#include + +#include "zipint.h" + +static void set_error(int *, struct zip_error *, int); +static struct zip *_zip_allocate_new(const char *, int *); +static int _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *); +static void _zip_check_torrentzip(struct zip *); +static struct zip_cdir *_zip_find_central_dir(FILE *, int, int *, off_t); +static int _zip_file_exists(const char *, int, int *); +static int _zip_headercomp(struct zip_dirent *, int, + struct zip_dirent *, int); +static unsigned char *_zip_memmem(const unsigned char *, int, + const unsigned char *, int); +static struct zip_cdir *_zip_readcdir(FILE *, unsigned char *, unsigned char *, + int, int, struct zip_error *); + + + +ZIP_EXTERN struct zip * +zip_open(const char *fn, int flags, int *zep) +{ + FILE *fp; + struct zip *za; + struct zip_cdir *cdir; + int i; + off_t len; + + switch (_zip_file_exists(fn, flags, zep)) { + case -1: + return NULL; + case 0: + return _zip_allocate_new(fn, zep); + default: + break; + } + + if ((fp=fopen(fn, "rb")) == NULL) { + set_error(zep, NULL, ZIP_ER_OPEN); + return NULL; + } + + fseeko(fp, 0, SEEK_END); + len = ftello(fp); + + /* treat empty files as empty archives */ + if (len == 0) { + if ((za=_zip_allocate_new(fn, zep)) == NULL) + fclose(fp); + else + za->zp = fp; + return za; + } + + cdir = _zip_find_central_dir(fp, flags, zep, len); + if (cdir == NULL) { + fclose(fp); + return NULL; + } + + if ((za=_zip_allocate_new(fn, zep)) == NULL) { + _zip_cdir_free(cdir); + fclose(fp); + return NULL; + } + + za->cdir = cdir; + za->zp = fp; + + if ((za->entry=(struct zip_entry *)malloc(sizeof(*(za->entry)) + * cdir->nentry)) == NULL) { + set_error(zep, NULL, ZIP_ER_MEMORY); + _zip_free(za); + return NULL; + } + for (i=0; inentry; i++) + _zip_entry_new(za); + + _zip_check_torrentzip(za); + za->ch_flags = za->flags; + + return za; +} + + + +static void +set_error(int *zep, struct zip_error *err, int ze) +{ + int se; + + if (err) { + _zip_error_get(err, &ze, &se); + if (zip_error_get_sys_type(ze) == ZIP_ET_SYS) + errno = se; + } + + if (zep) + *zep = ze; +} + + + +/* _zip_readcdir: + tries to find a valid end-of-central-directory at the beginning of + buf, and then the corresponding central directory entries. + Returns a struct zip_cdir which contains the central directory + entries, or NULL if unsuccessful. */ + +static struct zip_cdir * +_zip_readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, int buflen, + int flags, struct zip_error *error) +{ + struct zip_cdir *cd; + unsigned char *cdp, **bufp; + int i, comlen, nentry; + unsigned int left; + + comlen = buf + buflen - eocd - EOCDLEN; + if (comlen < 0) { + /* not enough bytes left for comment */ + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return NULL; + } + + /* check for end-of-central-dir magic */ + if (memcmp(eocd, EOCD_MAGIC, 4) != 0) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return NULL; + } + + if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) { + _zip_error_set(error, ZIP_ER_MULTIDISK, 0); + return NULL; + } + + cdp = eocd + 8; + /* number of cdir-entries on this disk */ + i = _zip_read2(&cdp); + /* number of cdir-entries */ + nentry = _zip_read2(&cdp); + + if ((cd=_zip_cdir_new(nentry, error)) == NULL) + return NULL; + + cd->size = _zip_read4(&cdp); + cd->offset = _zip_read4(&cdp); + cd->comment = NULL; + cd->comment_len = _zip_read2(&cdp); + + if ((comlen < cd->comment_len) || (cd->nentry != i)) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + free(cd); + return NULL; + } + if ((flags & ZIP_CHECKCONS) && comlen != cd->comment_len) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + free(cd); + return NULL; + } + + if (cd->comment_len) { + if ((cd->comment=(char *)_zip_memdup(eocd+EOCDLEN, + cd->comment_len, error)) + == NULL) { + free(cd); + return NULL; + } + } + + if (cd->size < (unsigned int)(eocd-buf)) { + /* if buffer already read in, use it */ + cdp = eocd - cd->size; + bufp = &cdp; + } + else { + /* go to start of cdir and read it entry by entry */ + bufp = NULL; + clearerr(fp); + fseeko(fp, cd->offset, SEEK_SET); + /* possible consistency check: cd->offset = + len-(cd->size+cd->comment_len+EOCDLEN) ? */ + if (ferror(fp) || ((unsigned long)ftello(fp) != cd->offset)) { + /* seek error or offset of cdir wrong */ + if (ferror(fp)) + _zip_error_set(error, ZIP_ER_SEEK, errno); + else + _zip_error_set(error, ZIP_ER_NOZIP, 0); + free(cd); + return NULL; + } + } + + left = cd->size; + i=0; + do { + if (i == cd->nentry && left > 0) { + /* Infozip extension for more than 64k entries: + nentries wraps around, size indicates correct EOCD */ + _zip_cdir_grow(cd, cd->nentry+0x10000, error); + } + + if ((_zip_dirent_read(cd->entry+i, fp, bufp, &left, 0, error)) < 0) { + cd->nentry = i; + _zip_cdir_free(cd); + return NULL; + } + i++; + + } while (inentry); + + return cd; +} + + + +/* _zip_checkcons: + Checks the consistency of the central directory by comparing central + directory entries with local headers and checking for plausible + file and header offsets. Returns -1 if not plausible, else the + difference between the lowest and the highest fileposition reached */ + +static int +_zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) +{ + int i; + unsigned int min, max, j; + struct zip_dirent temp; + + if (cd->nentry) { + max = cd->entry[0].offset; + min = cd->entry[0].offset; + } + else + min = max = 0; + + for (i=0; inentry; i++) { + if (cd->entry[i].offset < min) + min = cd->entry[i].offset; + if (min > cd->offset) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return -1; + } + + j = cd->entry[i].offset + cd->entry[i].comp_size + + cd->entry[i].filename_len + LENTRYSIZE; + if (j > max) + max = j; + if (max > cd->offset) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return -1; + } + + if (fseeko(fp, cd->entry[i].offset, SEEK_SET) != 0) { + _zip_error_set(error, ZIP_ER_SEEK, 0); + return -1; + } + + if (_zip_dirent_read(&temp, fp, NULL, NULL, 1, error) == -1) + return -1; + + if (_zip_headercomp(cd->entry+i, 0, &temp, 1) != 0) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_dirent_finalize(&temp); + return -1; + } + _zip_dirent_finalize(&temp); + } + + return max - min; +} + + + +/* _zip_check_torrentzip: + check wether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */ + +static void +_zip_check_torrentzip(struct zip *za) +{ + uLong crc_got, crc_should; + char buf[8+1]; + char *end; + + if (za->zp == NULL || za->cdir == NULL) + return; + + if (za->cdir->comment_len != TORRENT_SIG_LEN+8 + || strncmp(za->cdir->comment, TORRENT_SIG, TORRENT_SIG_LEN) != 0) + return; + + memcpy(buf, za->cdir->comment+TORRENT_SIG_LEN, 8); + buf[8] = '\0'; + errno = 0; + crc_should = strtoul(buf, &end, 16); + if ((crc_should == UINT_MAX && errno != 0) || (end && *end)) + return; + + if (_zip_filerange_crc(za->zp, za->cdir->offset, za->cdir->size, + &crc_got, NULL) < 0) + return; + + if (crc_got == crc_should) + za->flags |= ZIP_AFL_TORRENT; +} + + + + +/* _zip_headercomp: + compares two headers h1 and h2; if they are local headers, set + local1p or local2p respectively to 1, else 0. Return 0 if they + are identical, -1 if not. */ + +static int +_zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2, + int local2p) +{ + if ((h1->version_needed != h2->version_needed) +#if 0 + /* some zip-files have different values in local + and global headers for the bitflags */ + || (h1->bitflags != h2->bitflags) +#endif + || (h1->comp_method != h2->comp_method) + || (h1->last_mod != h2->last_mod) + || (h1->filename_len != h2->filename_len) + || !h1->filename || !h2->filename + || strcmp(h1->filename, h2->filename)) + return -1; + + /* check that CRC and sizes are zero if data descriptor is used */ + if ((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local1p + && (h1->crc != 0 + || h1->comp_size != 0 + || h1->uncomp_size != 0)) + return -1; + if ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local2p + && (h2->crc != 0 + || h2->comp_size != 0 + || h2->uncomp_size != 0)) + return -1; + + /* check that CRC and sizes are equal if no data descriptor is used */ + if (((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local1p == 0) + && ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local2p == 0)) { + if ((h1->crc != h2->crc) + || (h1->comp_size != h2->comp_size) + || (h1->uncomp_size != h2->uncomp_size)) + return -1; + } + + if ((local1p == local2p) + && ((h1->extrafield_len != h2->extrafield_len) + || (h1->extrafield_len && h2->extrafield + && memcmp(h1->extrafield, h2->extrafield, + h1->extrafield_len)))) + return -1; + + /* if either is local, nothing more to check */ + if (local1p || local2p) + return 0; + + if ((h1->version_madeby != h2->version_madeby) + || (h1->disk_number != h2->disk_number) + || (h1->int_attrib != h2->int_attrib) + || (h1->ext_attrib != h2->ext_attrib) + || (h1->offset != h2->offset) + || (h1->comment_len != h2->comment_len) + || (h1->comment_len && h2->comment + && memcmp(h1->comment, h2->comment, h1->comment_len))) + return -1; + + return 0; +} + + + +static struct zip * +_zip_allocate_new(const char *fn, int *zep) +{ + struct zip *za; + struct zip_error error; + + if ((za=_zip_new(&error)) == NULL) { + set_error(zep, &error, 0); + return NULL; + } + + za->zn = strdup(fn); + if (!za->zn) { + _zip_free(za); + set_error(zep, NULL, ZIP_ER_MEMORY); + return NULL; + } + return za; +} + + + +static int +_zip_file_exists(const char *fn, int flags, int *zep) +{ + struct stat st; + + if (fn == NULL) { + set_error(zep, NULL, ZIP_ER_INVAL); + return -1; + } + + if (stat(fn, &st) != 0) { + if (flags & ZIP_CREATE) + return 0; + else { + set_error(zep, NULL, ZIP_ER_OPEN); + return -1; + } + } + else if ((flags & ZIP_EXCL)) { + set_error(zep, NULL, ZIP_ER_EXISTS); + return -1; + } + /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, + just like open() */ + + return 1; +} + + + +static struct zip_cdir * +_zip_find_central_dir(FILE *fp, int flags, int *zep, off_t len) +{ + struct zip_cdir *cdir, *cdirnew; + unsigned char *buf, *match; + int a, best, buflen, i; + struct zip_error zerr; + + i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END); + if (i == -1 && errno != EFBIG) { + /* seek before start of file on my machine */ + set_error(zep, NULL, ZIP_ER_SEEK); + return NULL; + } + + /* 64k is too much for stack */ + if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) { + set_error(zep, NULL, ZIP_ER_MEMORY); + return NULL; + } + + clearerr(fp); + buflen = fread(buf, 1, CDBUFSIZE, fp); + + if (ferror(fp)) { + set_error(zep, NULL, ZIP_ER_READ); + free(buf); + return NULL; + } + + best = -1; + cdir = NULL; + match = buf; + _zip_error_set(&zerr, ZIP_ER_NOZIP, 0); + + while ((match=_zip_memmem(match, buflen-(match-buf)-18, + (const unsigned char *)EOCD_MAGIC, 4))!=NULL) { + /* found match -- check, if good */ + /* to avoid finding the same match all over again */ + match++; + if ((cdirnew=_zip_readcdir(fp, buf, match-1, buflen, flags, + &zerr)) == NULL) + continue; + + if (cdir) { + if (best <= 0) + best = _zip_checkcons(fp, cdir, &zerr); + a = _zip_checkcons(fp, cdirnew, &zerr); + if (best < a) { + _zip_cdir_free(cdir); + cdir = cdirnew; + best = a; + } + else + _zip_cdir_free(cdirnew); + } + else { + cdir = cdirnew; + if (flags & ZIP_CHECKCONS) + best = _zip_checkcons(fp, cdir, &zerr); + else + best = 0; + } + cdirnew = NULL; + } + + free(buf); + + if (best < 0) { + set_error(zep, &zerr, 0); + _zip_cdir_free(cdir); + return NULL; + } + + return cdir; +} + + + +static unsigned char * +_zip_memmem(const unsigned char *big, int biglen, const unsigned char *little, + int littlelen) +{ + const unsigned char *p; + + if ((biglen < littlelen) || (littlelen == 0)) + return NULL; + p = big-1; + while ((p=(const unsigned char *) + memchr(p+1, little[0], (size_t)(big-(p+1)+biglen-littlelen+1))) + != NULL) { + if (memcmp(p+1, little+1, littlelen-1)==0) + return (unsigned char *)p; + } + + return NULL; +} diff --git a/ext/libzip/zip_rename.c b/ext/libzip/zip_rename.c new file mode 100644 index 0000000000..1d056bbe37 --- /dev/null +++ b/ext/libzip/zip_rename.c @@ -0,0 +1,65 @@ +/* + zip_rename.c -- rename file in zip archive + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_rename(struct zip *za, int idx, const char *name) +{ + const char *old_name; + int old_is_dir, new_is_dir; + + if (idx >= za->nentry || idx < 0 || name[0] == '\0') { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if ((old_name=zip_get_name(za, idx, 0)) == NULL) + return -1; + + new_is_dir = (name[strlen(name)-1] == '/'); + old_is_dir = (old_name[strlen(old_name)-1] == '/'); + + if (new_is_dir != old_is_dir) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + return _zip_set_name(za, idx, name); +} diff --git a/ext/libzip/zip_replace.c b/ext/libzip/zip_replace.c new file mode 100644 index 0000000000..6cdb80c503 --- /dev/null +++ b/ext/libzip/zip_replace.c @@ -0,0 +1,78 @@ +/* + zip_replace.c -- replace file via callback function + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_replace(struct zip *za, int idx, struct zip_source *source) +{ + if (idx < 0 || idx >= za->nentry || source == NULL) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (_zip_replace(za, idx, NULL, source) == -1) + return -1; + + return 0; +} + + + + +int +_zip_replace(struct zip *za, int idx, const char *name, + struct zip_source *source) +{ + if (idx == -1) { + if (_zip_entry_new(za) == NULL) + return -1; + + idx = za->nentry - 1; + } + + _zip_unchange_data(za->entry+idx); + + if (name && _zip_set_name(za, idx, name) != 0) + return -1; + + za->entry[idx].state = ((za->cdir == NULL || idx >= za->cdir->nentry) + ? ZIP_ST_ADDED : ZIP_ST_REPLACED); + za->entry[idx].source = source; + + return idx; +} diff --git a/ext/libzip/zip_set_archive_comment.c b/ext/libzip/zip_set_archive_comment.c new file mode 100644 index 0000000000..343fa5491c --- /dev/null +++ b/ext/libzip/zip_set_archive_comment.c @@ -0,0 +1,65 @@ +/* + zip_set_archive_comment.c -- set archive comment + Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_set_archive_comment(struct zip *za, const char *comment, int len) +{ + char *tmpcom; + + if (len < 0 || len > MAXCOMLEN + || (len > 0 && comment == NULL)) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (len > 0) { + if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL) + return -1; + } + else + tmpcom = NULL; + + free(za->ch_comment); + za->ch_comment = tmpcom; + za->ch_comment_len = len; + + return 0; +} diff --git a/ext/libzip/zip_set_archive_flag.c b/ext/libzip/zip_set_archive_flag.c new file mode 100644 index 0000000000..720e1f3508 --- /dev/null +++ b/ext/libzip/zip_set_archive_flag.c @@ -0,0 +1,49 @@ +/* + zip_get_archive_flag.c -- set archive global flag + Copyright (C) 2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_set_archive_flag(struct zip *za, int flag, int value) +{ + if (value) + za->ch_flags |= flag; + else + za->ch_flags &= ~flag; + + return 0; +} diff --git a/ext/libzip/zip_set_file_comment.c b/ext/libzip/zip_set_file_comment.c new file mode 100644 index 0000000000..bc1938e1df --- /dev/null +++ b/ext/libzip/zip_set_file_comment.c @@ -0,0 +1,66 @@ +/* + zip_set_file_comment.c -- set comment for file in archive + Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_set_file_comment(struct zip *za, int idx, const char *comment, int len) +{ + char *tmpcom; + + if (idx < 0 || idx >= za->nentry + || len < 0 || len > MAXCOMLEN + || (len > 0 && comment == NULL)) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (len > 0) { + if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL) + return -1; + } + else + tmpcom = NULL; + + free(za->entry[idx].ch_comment); + za->entry[idx].ch_comment = tmpcom; + za->entry[idx].ch_comment_len = len; + + return 0; +} diff --git a/ext/libzip/zip_set_name.c b/ext/libzip/zip_set_name.c new file mode 100644 index 0000000000..5c7da3d7c5 --- /dev/null +++ b/ext/libzip/zip_set_name.c @@ -0,0 +1,75 @@ +/* + zip_set_name.c -- rename helper function + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include + +#include "zipint.h" + + + +int +_zip_set_name(struct zip *za, int idx, const char *name) +{ + char *s; + int i; + + if (idx < 0 || idx >= za->nentry || name == NULL) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if ((i=_zip_name_locate(za, name, 0, NULL)) != -1 && i != idx) { + _zip_error_set(&za->error, ZIP_ER_EXISTS, 0); + return -1; + } + + /* no effective name change */ + if (i == idx) + return 0; + + if ((s=strdup(name)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + + if (za->entry[idx].state == ZIP_ST_UNCHANGED) + za->entry[idx].state = ZIP_ST_RENAMED; + + free(za->entry[idx].ch_filename); + za->entry[idx].ch_filename = s; + + return 0; +} diff --git a/ext/libzip/zip_source_buffer.c b/ext/libzip/zip_source_buffer.c new file mode 100644 index 0000000000..1e9121c560 --- /dev/null +++ b/ext/libzip/zip_source_buffer.c @@ -0,0 +1,157 @@ +/* + zip_source_buffer.c -- create zip data source from buffer + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include + +#include "zipint.h" + +struct read_data { + const char *buf, *data, *end; + time_t mtime; + int freep; +}; + +static ssize_t read_data(void *state, void *data, size_t len, + enum zip_source_cmd cmd); + + + +ZIP_EXTERN struct zip_source * +zip_source_buffer(struct zip *za, const void *data, off_t len, int freep) +{ + struct read_data *f; + struct zip_source *zs; + + if (za == NULL) + return NULL; + + if (len < 0 || (data == NULL && len > 0)) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((f=(struct read_data *)malloc(sizeof(*f))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + f->data = (const char *)data; + f->end = ((const char *)data)+len; + f->freep = freep; + f->mtime = time(NULL); + + if ((zs=zip_source_function(za, read_data, f)) == NULL) { + free(f); + return NULL; + } + + return zs; +} + + + +static ssize_t +read_data(void *state, void *data, size_t len, enum zip_source_cmd cmd) +{ + struct read_data *z; + char *buf; + size_t n; + + z = (struct read_data *)state; + buf = (char *)data; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + z->buf = z->data; + return 0; + + case ZIP_SOURCE_READ: + n = z->end - z->buf; + if (n > len) + n = len; + + if (n) { + memcpy(buf, z->buf, n); + z->buf += n; + } + + return n; + + case ZIP_SOURCE_CLOSE: + return 0; + + case ZIP_SOURCE_STAT: + { + struct zip_stat *st; + + if (len < sizeof(*st)) + return -1; + + st = (struct zip_stat *)data; + + zip_stat_init(st); + st->mtime = z->mtime; + st->size = z->end - z->data; + + return sizeof(*st); + } + + case ZIP_SOURCE_ERROR: + { + int *e; + + if (len < sizeof(int)*2) + return -1; + + e = (int *)data; + e[0] = e[1] = 0; + } + return sizeof(int)*2; + + case ZIP_SOURCE_FREE: + if (z->freep) { + free((void *)z->data); + z->data = NULL; + } + free(z); + return 0; + + default: + ; + } + + return -1; +} diff --git a/ext/libzip/zip_source_file.c b/ext/libzip/zip_source_file.c new file mode 100644 index 0000000000..eb1129a275 --- /dev/null +++ b/ext/libzip/zip_source_file.c @@ -0,0 +1,55 @@ +/* + zip_source_file.c -- create data source from file + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include + +#include "zipint.h" + + + +ZIP_EXTERN struct zip_source * +zip_source_file(struct zip *za, const char *fname, off_t start, off_t len) +{ + if (za == NULL) + return NULL; + + if (fname == NULL || start < 0 || len < -1) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + return _zip_source_file_or_p(za, fname, NULL, start, len); +} diff --git a/ext/libzip/zip_source_filep.c b/ext/libzip/zip_source_filep.c new file mode 100644 index 0000000000..5373934102 --- /dev/null +++ b/ext/libzip/zip_source_filep.c @@ -0,0 +1,216 @@ +/* + zip_source_filep.c -- create data source from FILE * + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include +#include +#include +#include + +#include "zipint.h" + +struct read_file { + char *fname; /* name of file to copy from */ + FILE *f; /* file to copy from */ + off_t off; /* start offset of */ + off_t len; /* lengt of data to copy */ + off_t remain; /* bytes remaining to be copied */ + int e[2]; /* error codes */ +}; + +static ssize_t read_file(void *state, void *data, size_t len, + enum zip_source_cmd cmd); + + + +ZIP_EXTERN struct zip_source * +zip_source_filep(struct zip *za, FILE *file, off_t start, off_t len) +{ + if (za == NULL) + return NULL; + + if (file == NULL || start < 0 || len < -1) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + return _zip_source_file_or_p(za, NULL, file, start, len); +} + + + +struct zip_source * +_zip_source_file_or_p(struct zip *za, const char *fname, FILE *file, + off_t start, off_t len) +{ + struct read_file *f; + struct zip_source *zs; + + if (file == NULL && fname == NULL) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((f=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + f->fname = NULL; + if (fname) { + if ((f->fname=strdup(fname)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + free(f); + return NULL; + } + } + f->f = file; + f->off = start; + f->len = (len ? len : -1); + + if ((zs=zip_source_function(za, read_file, f)) == NULL) { + free(f); + return NULL; + } + + return zs; +} + + + +static ssize_t +read_file(void *state, void *data, size_t len, enum zip_source_cmd cmd) +{ + struct read_file *z; + char *buf; + int i, n; + + z = (struct read_file *)state; + buf = (char *)data; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + if (z->fname) { + if ((z->f=fopen(z->fname, "rb")) == NULL) { + z->e[0] = ZIP_ER_OPEN; + z->e[1] = errno; + return -1; + } + } + + if (fseeko(z->f, z->off, SEEK_SET) < 0) { + z->e[0] = ZIP_ER_SEEK; + z->e[1] = errno; + return -1; + } + z->remain = z->len; + return 0; + + case ZIP_SOURCE_READ: + if (z->remain != -1) + n = len > z->remain ? z->remain : len; + else + n = len; + + if ((i=fread(buf, 1, n, z->f)) < 0) { + z->e[0] = ZIP_ER_READ; + z->e[1] = errno; + return -1; + } + + if (z->remain != -1) + z->remain -= i; + + return i; + + case ZIP_SOURCE_CLOSE: + if (z->fname) { + fclose(z->f); + z->f = NULL; + } + return 0; + + case ZIP_SOURCE_STAT: + { + struct zip_stat *st; + struct stat fst; + int err; + + if (len < sizeof(*st)) + return -1; + + if (z->f) + err = fstat(fileno(z->f), &fst); + else + err = stat(z->fname, &fst); + + if (err != 0) { + z->e[0] = ZIP_ER_READ; /* best match */ + z->e[1] = errno; + return -1; + } + + st = (struct zip_stat *)data; + + zip_stat_init(st); + st->mtime = fst.st_mtime; + if (z->len != -1) + st->size = z->len; + else if ((fst.st_mode&S_IFMT) == S_IFREG) + st->size = fst.st_size; + + return sizeof(*st); + } + + case ZIP_SOURCE_ERROR: + if (len < sizeof(int)*2) + return -1; + + memcpy(data, z->e, sizeof(int)*2); + return sizeof(int)*2; + + case ZIP_SOURCE_FREE: + free(z->fname); + if (z->f) + fclose(z->f); + free(z); + return 0; + + default: + ; + } + + return -1; +} diff --git a/ext/libzip/zip_source_free.c b/ext/libzip/zip_source_free.c new file mode 100644 index 0000000000..7a1632fbf9 --- /dev/null +++ b/ext/libzip/zip_source_free.c @@ -0,0 +1,51 @@ +/* + zip_source_free.c -- free zip data source + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +ZIP_EXTERN void +zip_source_free(struct zip_source *source) +{ + if (source == NULL) + return; + + (void)source->f(source->ud, NULL, 0, ZIP_SOURCE_FREE); + + free(source); +} diff --git a/ext/libzip/zip_source_function.c b/ext/libzip/zip_source_function.c new file mode 100644 index 0000000000..91b183cb6f --- /dev/null +++ b/ext/libzip/zip_source_function.c @@ -0,0 +1,59 @@ +/* + zip_source_function.c -- create zip data source from callback function + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +ZIP_EXTERN struct zip_source * +zip_source_function(struct zip *za, zip_source_callback zcb, void *ud) +{ + struct zip_source *zs; + + if (za == NULL) + return NULL; + + if ((zs=(struct zip_source *)malloc(sizeof(*zs))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + zs->f = zcb; + zs->ud = ud; + + return zs; +} diff --git a/ext/libzip/zip_source_zip.c b/ext/libzip/zip_source_zip.c new file mode 100644 index 0000000000..3eef552a23 --- /dev/null +++ b/ext/libzip/zip_source_zip.c @@ -0,0 +1,188 @@ +/* + zip_source_zip.c -- create data source from zip file + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include +#include + +#include "zipint.h" + +struct read_zip { + struct zip_file *zf; + struct zip_stat st; + off_t off, len; +}; + +static ssize_t read_zip(void *st, void *data, size_t len, + enum zip_source_cmd cmd); + + + +ZIP_EXTERN struct zip_source * +zip_source_zip(struct zip *za, struct zip *srcza, int srcidx, int flags, + off_t start, off_t len) +{ + struct zip_error error; + struct zip_source *zs; + struct read_zip *p; + + /* XXX: ZIP_FL_RECOMPRESS */ + + if (za == NULL) + return NULL; + + if (srcza == NULL || start < 0 || len < -1 || srcidx < 0 || srcidx >= srcza->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((flags & ZIP_FL_UNCHANGED) == 0 + && ZIP_ENTRY_DATA_CHANGED(srcza->entry+srcidx)) { + _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); + return NULL; + } + + if (len == 0) + len = -1; + + if (start == 0 && len == -1 && (flags & ZIP_FL_RECOMPRESS) == 0) + flags |= ZIP_FL_COMPRESSED; + else + flags &= ~ZIP_FL_COMPRESSED; + + if ((p=(struct read_zip *)malloc(sizeof(*p))) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return NULL; + } + + _zip_error_copy(&error, &srcza->error); + + if (zip_stat_index(srcza, srcidx, flags, &p->st) < 0 + || (p->zf=zip_fopen_index(srcza, srcidx, flags)) == NULL) { + free(p); + _zip_error_copy(&za->error, &srcza->error); + _zip_error_copy(&srcza->error, &error); + + return NULL; + } + p->off = start; + p->len = len; + + if ((flags & ZIP_FL_COMPRESSED) == 0) { + p->st.size = p->st.comp_size = len; + p->st.comp_method = ZIP_CM_STORE; + p->st.crc = 0; + } + + if ((zs=zip_source_function(za, read_zip, p)) == NULL) { + free(p); + return NULL; + } + + return zs; +} + + + +static ssize_t +read_zip(void *state, void *data, size_t len, enum zip_source_cmd cmd) +{ + struct read_zip *z; + char b[8192], *buf; + int i, n; + + z = (struct read_zip *)state; + buf = (char *)data; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + for (n=0; noff; n+= i) { + i = (z->off-n > sizeof(b) ? sizeof(b) : z->off-n); + if ((i=zip_fread(z->zf, b, i)) < 0) { + zip_fclose(z->zf); + z->zf = NULL; + return -1; + } + } + return 0; + + case ZIP_SOURCE_READ: + if (z->len != -1) + n = len > z->len ? z->len : len; + else + n = len; + + + if ((i=zip_fread(z->zf, buf, n)) < 0) + return -1; + + if (z->len != -1) + z->len -= i; + + return i; + + case ZIP_SOURCE_CLOSE: + return 0; + + case ZIP_SOURCE_STAT: + if (len < sizeof(z->st)) + return -1; + len = sizeof(z->st); + + memcpy(data, &z->st, len); + return len; + + case ZIP_SOURCE_ERROR: + { + int *e; + + if (len < sizeof(int)*2) + return -1; + + e = (int *)data; + zip_file_error_get(z->zf, e, e+1); + } + return sizeof(int)*2; + + case ZIP_SOURCE_FREE: + zip_fclose(z->zf); + free(z); + return 0; + + default: + ; + } + + return -1; +} diff --git a/ext/libzip/zip_stat.c b/ext/libzip/zip_stat.c new file mode 100644 index 0000000000..df1c0b50b9 --- /dev/null +++ b/ext/libzip/zip_stat.c @@ -0,0 +1,49 @@ +/* + zip_stat.c -- get information about file by name + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_stat(struct zip *za, const char *fname, int flags, struct zip_stat *st) +{ + int idx; + + if ((idx=zip_name_locate(za, fname, flags)) < 0) + return -1; + + return zip_stat_index(za, idx, flags, st); +} diff --git a/ext/libzip/zip_stat_index.c b/ext/libzip/zip_stat_index.c new file mode 100644 index 0000000000..60558e432b --- /dev/null +++ b/ext/libzip/zip_stat_index.c @@ -0,0 +1,90 @@ +/* + zip_stat_index.c -- get information about file by index + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_stat_index(struct zip *za, int index, int flags, struct zip_stat *st) +{ + const char *name; + + if (index < 0 || index >= za->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if ((name=zip_get_name(za, index, flags)) == NULL) + return -1; + + + if ((flags & ZIP_FL_UNCHANGED) == 0 + && ZIP_ENTRY_DATA_CHANGED(za->entry+index)) { + if (za->entry[index].source->f(za->entry[index].source->ud, + st, sizeof(*st), ZIP_SOURCE_STAT) < 0) { + _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); + return -1; + } + } + else { + if (za->cdir == NULL || index >= za->cdir->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + st->crc = za->cdir->entry[index].crc; + st->size = za->cdir->entry[index].uncomp_size; + st->mtime = za->cdir->entry[index].last_mod; + st->comp_size = za->cdir->entry[index].comp_size; + st->comp_method = za->cdir->entry[index].comp_method; + if (za->cdir->entry[index].bitflags & ZIP_GPBF_ENCRYPTED) { + if (za->cdir->entry[index].bitflags & ZIP_GPBF_STRONG_ENCRYPTION) { + /* XXX */ + st->encryption_method = ZIP_EM_UNKNOWN; + } + else + st->encryption_method = ZIP_EM_TRAD_PKWARE; + } + else + st->encryption_method = ZIP_EM_NONE; + /* st->bitflags = za->cdir->entry[index].bitflags; */ + } + + st->index = index; + st->name = name; + + return 0; +} diff --git a/ext/libzip/zip_stat_init.c b/ext/libzip/zip_stat_init.c new file mode 100644 index 0000000000..133ad304aa --- /dev/null +++ b/ext/libzip/zip_stat_init.c @@ -0,0 +1,51 @@ +/* + zip_stat_init.c -- initialize struct zip_stat. + Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN void +zip_stat_init(struct zip_stat *st) +{ + st->name = NULL; + st->index = -1; + st->crc = 0; + st->mtime = (time_t)-1; + st->size = -1; + st->comp_size = -1; + st->comp_method = ZIP_CM_STORE; + st->encryption_method = ZIP_EM_NONE; +} diff --git a/ext/libzip/zip_strerror.c b/ext/libzip/zip_strerror.c new file mode 100644 index 0000000000..9ebee144f9 --- /dev/null +++ b/ext/libzip/zip_strerror.c @@ -0,0 +1,44 @@ +/* + zip_sterror.c -- get string representation of zip error + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include "zipint.h" + + + +ZIP_EXTERN const char * +zip_strerror(struct zip *za) +{ + return _zip_error_strerror(&za->error); +} diff --git a/ext/libzip/zip_unchange.c b/ext/libzip/zip_unchange.c new file mode 100644 index 0000000000..ed0d4de65b --- /dev/null +++ b/ext/libzip/zip_unchange.c @@ -0,0 +1,82 @@ +/* + zip_unchange.c -- undo changes to file in zip archive + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_unchange(struct zip *za, int idx) +{ + return _zip_unchange(za, idx, 0); +} + + + +int +_zip_unchange(struct zip *za, int idx, int allow_duplicates) +{ + int i; + + if (idx < 0 || idx >= za->nentry) { + _zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (za->entry[idx].ch_filename) { + if (!allow_duplicates) { + i = _zip_name_locate(za, + _zip_get_name(za, idx, ZIP_FL_UNCHANGED, NULL), + 0, NULL); + if (i != -1 && i != idx) { + _zip_error_set(&za->error, ZIP_ER_EXISTS, 0); + return -1; + } + } + + free(za->entry[idx].ch_filename); + za->entry[idx].ch_filename = NULL; + } + + free(za->entry[idx].ch_comment); + za->entry[idx].ch_comment = NULL; + za->entry[idx].ch_comment_len = -1; + + _zip_unchange_data(za->entry+idx); + + return 0; +} diff --git a/ext/libzip/zip_unchange_all.c b/ext/libzip/zip_unchange_all.c new file mode 100644 index 0000000000..54a7a0d2e1 --- /dev/null +++ b/ext/libzip/zip_unchange_all.c @@ -0,0 +1,54 @@ +/* + zip_unchange.c -- undo changes to all files in zip archive + Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_unchange_all(struct zip *za) +{ + int ret, i; + + ret = 0; + for (i=0; inentry; i++) + ret |= _zip_unchange(za, i, 1); + + ret |= zip_unchange_archive(za); + + return ret; +} diff --git a/ext/libzip/zip_unchange_archive.c b/ext/libzip/zip_unchange_archive.c new file mode 100644 index 0000000000..8f9c024c36 --- /dev/null +++ b/ext/libzip/zip_unchange_archive.c @@ -0,0 +1,52 @@ +/* + zip_unchange_archive.c -- undo global changes to ZIP archive + Copyright (C) 2006-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + + + +ZIP_EXTERN int +zip_unchange_archive(struct zip *za) +{ + free(za->ch_comment); + za->ch_comment = NULL; + za->ch_comment_len = -1; + + za->ch_flags = za->flags; + + return 0; +} diff --git a/ext/libzip/zip_unchange_data.c b/ext/libzip/zip_unchange_data.c new file mode 100644 index 0000000000..6fe89f4fb2 --- /dev/null +++ b/ext/libzip/zip_unchange_data.c @@ -0,0 +1,53 @@ +/* + $NiH: zip_unchange_data.c,v 1.14 2004/11/30 23:02:47 wiz Exp $ + + zip_unchange_data.c -- undo helper function + Copyright (C) 1999, 2004 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + + +#include + +#include "zipint.h" + +void +_zip_unchange_data(struct zip_entry *ze) +{ + if (ze->source) { + (void)ze->source->f(ze->source->ud, NULL, 0, ZIP_SOURCE_FREE); + free(ze->source); + ze->source = NULL; + } + + ze->state = ze->ch_filename ? ZIP_ST_RENAMED : ZIP_ST_UNCHANGED; +} + diff --git a/ext/libzip/zipint.h b/ext/libzip/zipint.h new file mode 100644 index 0000000000..f5a81b1934 --- /dev/null +++ b/ext/libzip/zipint.h @@ -0,0 +1,264 @@ +#ifndef _HAD_ZIPINT_H +#define _HAD_ZIPINT_H + +/* + zipint.h -- internal declarations. + Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#ifdef _MSC_VER +#define ZIP_EXTERN __declspec(dllimport) +#endif + +#include "zip.h" +#include "config.h" + +#ifndef HAVE_MKSTEMP +int _zip_mkstemp(char *); +#define mkstemp _zip_mkstemp +#endif + +#ifdef HAVE_MOVEFILEEXA +#include +#define _zip_rename(s, t) \ + (!MoveFileExA((s), (t), \ + MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING)) +#else +#define _zip_rename rename +#endif + +#ifndef HAVE_FSEEKO +#define fseeko(s, o, w) (fseek((s), (long int)(o), (w))) +#endif +#ifndef HAVE_FTELLO +#define ftello(s) ((long)ftell((s))) +#endif + + + +#define CENTRAL_MAGIC "PK\1\2" +#define LOCAL_MAGIC "PK\3\4" +#define EOCD_MAGIC "PK\5\6" +#define DATADES_MAGIC "PK\7\8" +#define TORRENT_SIG "TORRENTZIPPED-" +#define TORRENT_SIG_LEN 14 +#define TORRENT_CRC_LEN 8 +#define TORRENT_MEM_LEVEL 8 +#define CDENTRYSIZE 46u +#define LENTRYSIZE 30 +#define MAXCOMLEN 65536 +#define EOCDLEN 22 +#define CDBUFSIZE (MAXCOMLEN+EOCDLEN) +#define BUFSIZE 8192 + + + +/* state of change of a file in zip archive */ + +enum zip_state { ZIP_ST_UNCHANGED, ZIP_ST_DELETED, ZIP_ST_REPLACED, + ZIP_ST_ADDED, ZIP_ST_RENAMED }; + +/* constants for struct zip_file's member flags */ + +#define ZIP_ZF_EOF 1 /* EOF reached */ +#define ZIP_ZF_DECOMP 2 /* decompress data */ +#define ZIP_ZF_CRC 4 /* compute and compare CRC */ + +/* directory entry: general purpose bit flags */ + +#define ZIP_GPBF_ENCRYPTED 0x0001 /* is encrypted */ +#define ZIP_GPBF_DATA_DESCRIPTOR 0x0008 /* crc/size after file data */ +#define ZIP_GPBF_STRONG_ENCRYPTION 0x0040 /* uses strong encryption */ + +/* error information */ + +struct zip_error { + int zip_err; /* libzip error code (ZIP_ER_*) */ + int sys_err; /* copy of errno (E*) or zlib error code */ + char *str; /* string representation or NULL */ +}; + +/* zip archive, part of API */ + +struct zip { + char *zn; /* file name */ + FILE *zp; /* file */ + struct zip_error error; /* error information */ + + unsigned int flags; /* archive global flags */ + unsigned int ch_flags; /* changed archive global flags */ + + struct zip_cdir *cdir; /* central directory */ + char *ch_comment; /* changed archive comment */ + int ch_comment_len; /* length of changed zip archive + * comment, -1 if unchanged */ + int nentry; /* number of entries */ + int nentry_alloc; /* number of entries allocated */ + struct zip_entry *entry; /* entries */ + int nfile; /* number of opened files within archive */ + int nfile_alloc; /* number of files allocated */ + struct zip_file **file; /* opened files within archive */ +}; + +/* file in zip archive, part of API */ + +struct zip_file { + struct zip *za; /* zip archive containing this file */ + struct zip_error error; /* error information */ + int flags; /* -1: eof, >0: error */ + + int method; /* compression method */ + off_t fpos; /* position within zip file (fread/fwrite) */ + unsigned long bytes_left; /* number of bytes left to read */ + unsigned long cbytes_left; /* number of bytes of compressed data left */ + + unsigned long crc; /* CRC so far */ + unsigned long crc_orig; /* CRC recorded in archive */ + + char *buffer; + z_stream *zstr; +}; + +/* zip archive directory entry (central or local) */ + +struct zip_dirent { + unsigned short version_madeby; /* (c) version of creator */ + unsigned short version_needed; /* (cl) version needed to extract */ + unsigned short bitflags; /* (cl) general purpose bit flag */ + unsigned short comp_method; /* (cl) compression method used */ + time_t last_mod; /* (cl) time of last modification */ + unsigned int crc; /* (cl) CRC-32 of uncompressed data */ + unsigned int comp_size; /* (cl) size of commpressed data */ + unsigned int uncomp_size; /* (cl) size of uncommpressed data */ + char *filename; /* (cl) file name (NUL-terminated) */ + unsigned short filename_len; /* (cl) length of filename (w/o NUL) */ + char *extrafield; /* (cl) extra field */ + unsigned short extrafield_len; /* (cl) length of extra field */ + char *comment; /* (c) file comment */ + unsigned short comment_len; /* (c) length of file comment */ + unsigned short disk_number; /* (c) disk number start */ + unsigned short int_attrib; /* (c) internal file attributes */ + unsigned int ext_attrib; /* (c) external file attributes */ + unsigned int offset; /* (c) offset of local header */ +}; + +/* zip archive central directory */ + +struct zip_cdir { + struct zip_dirent *entry; /* directory entries */ + int nentry; /* number of entries */ + + unsigned int size; /* size of central direcotry */ + unsigned int offset; /* offset of central directory in file */ + char *comment; /* zip archive comment */ + unsigned short comment_len; /* length of zip archive comment */ +}; + + + +struct zip_source { + zip_source_callback f; + void *ud; +}; + +/* entry in zip archive directory */ + +struct zip_entry { + enum zip_state state; + struct zip_source *source; + char *ch_filename; + char *ch_comment; + int ch_comment_len; +}; + + + +extern const char * const _zip_err_str[]; +extern const int _zip_nerr_str; +extern const int _zip_err_type[]; + + + +#define ZIP_ENTRY_DATA_CHANGED(x) \ + ((x)->state == ZIP_ST_REPLACED \ + || (x)->state == ZIP_ST_ADDED) + + + +int _zip_cdir_compute_crc(struct zip *, uLong *); +void _zip_cdir_free(struct zip_cdir *); +int _zip_cdir_grow(struct zip_cdir *, int, struct zip_error *); +struct zip_cdir *_zip_cdir_new(int, struct zip_error *); +int _zip_cdir_write(struct zip_cdir *, FILE *, struct zip_error *); + +void _zip_dirent_finalize(struct zip_dirent *); +void _zip_dirent_init(struct zip_dirent *); +int _zip_dirent_read(struct zip_dirent *, FILE *, unsigned char **, + unsigned int *, int, struct zip_error *); +void _zip_dirent_torrent_normalize(struct zip_dirent *); +int _zip_dirent_write(struct zip_dirent *, FILE *, int, struct zip_error *); + +void _zip_entry_free(struct zip_entry *); +void _zip_entry_init(struct zip *, int); +struct zip_entry *_zip_entry_new(struct zip *); + +void _zip_error_clear(struct zip_error *); +void _zip_error_copy(struct zip_error *, struct zip_error *); +void _zip_error_fini(struct zip_error *); +void _zip_error_get(struct zip_error *, int *, int *); +void _zip_error_init(struct zip_error *); +void _zip_error_set(struct zip_error *, int, int); +const char *_zip_error_strerror(struct zip_error *); + +int _zip_file_fillbuf(void *, size_t, struct zip_file *); +unsigned int _zip_file_get_offset(struct zip *, int); + +int _zip_filerange_crc(FILE *, off_t, off_t, uLong *, struct zip_error *); + +struct zip_source *_zip_source_file_or_p(struct zip *, const char *, FILE *, + off_t, off_t); + +void _zip_free(struct zip *); +const char *_zip_get_name(struct zip *, int, int, struct zip_error *); +int _zip_local_header_read(struct zip *, int); +void *_zip_memdup(const void *, size_t, struct zip_error *); +int _zip_name_locate(struct zip *, const char *, int, struct zip_error *); +struct zip *_zip_new(struct zip_error *); +unsigned short _zip_read2(unsigned char **); +unsigned int _zip_read4(unsigned char **); +int _zip_replace(struct zip *, int, const char *, struct zip_source *); +int _zip_set_name(struct zip *, int, const char *); +int _zip_unchange(struct zip *, int, int); +void _zip_unchange_data(struct zip_entry *); + +#endif /* zipint.h */ diff --git a/ext/rapidxml/license.txt b/ext/rapidxml/license.txt new file mode 100644 index 0000000000..140983180b --- /dev/null +++ b/ext/rapidxml/license.txt @@ -0,0 +1,52 @@ +Use of this software is granted under one of the following two licenses, +to be chosen freely by the user. + +1. Boost Software License - Version 1.0 - August 17th, 2003 +=============================================================================== + +Copyright (c) 2006, 2007 Marcin Kalicinski + +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. + +2. The MIT License +=============================================================================== + +Copyright (c) 2006, 2007 Marcin Kalicinski + +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 furnished 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, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/ext/rapidxml/manual.html b/ext/rapidxml/manual.html new file mode 100644 index 0000000000..2c422703f4 --- /dev/null +++ b/ext/rapidxml/manual.html @@ -0,0 +1,406 @@ +

RAPIDXML Manual

Version 1.13

Copyright (C) 2006, 2009 Marcin Kalicinski
See accompanying file license.txt for license information.

Table of Contents

1. What is RapidXml?
1.1 Dependencies And Compatibility
1.2 Character Types And Encodings
1.3 Error Handling
1.4 Memory Allocation
1.5 W3C Compliance
1.6 API Design
1.7 Reliability
1.8 Acknowledgements
2. Two Minute Tutorial
2.1 Parsing
2.2 Accessing The DOM Tree
2.3 Modifying The DOM Tree
2.4 Printing XML
3. Differences From Regular XML Parsers
3.1 Lifetime Of Source Text
3.2 Ownership Of Strings
3.3 Destructive Vs Non-Destructive Mode
4. Performance
4.1 Comparison With Other Parsers
5. Reference

1. What is RapidXml?

RapidXml is an attempt to create the fastest XML DOM parser possible, while retaining useability, portability and reasonable W3C compatibility. It is an in-situ parser written in C++, with parsing speed approaching that of strlen() function executed on the same data.

+ Entire parser is contained in a single header file, so no building or linking is neccesary. To use it you just need to copy rapidxml.hpp file to a convenient place (such as your project directory), and include it where needed. You may also want to use printing functions contained in header rapidxml_print.hpp.

1.1 Dependencies And Compatibility

RapidXml has no dependencies other than a very small subset of standard C++ library (<cassert>, <cstdlib>, <new> and <exception>, unless exceptions are disabled). It should compile on any reasonably conformant compiler, and was tested on Visual C++ 2003, Visual C++ 2005, Visual C++ 2008, gcc 3, gcc 4, and Comeau 4.3.3. Care was taken that no warnings are produced on these compilers, even with highest warning levels enabled.

1.2 Character Types And Encodings

RapidXml is character type agnostic, and can work both with narrow and wide characters. Current version does not fully support UTF-16 or UTF-32, so use of wide characters is somewhat incapacitated. However, it should succesfully parse wchar_t strings containing UTF-16 or UTF-32 if endianness of the data matches that of the machine. UTF-8 is fully supported, including all numeric character references, which are expanded into appropriate UTF-8 byte sequences (unless you enable parse_no_utf8 flag).

+ Note that RapidXml performs no decoding - strings returned by name() and value() functions will contain text encoded using the same encoding as source file. Rapidxml understands and expands the following character references: &apos; &amp; &quot; &lt; &gt; &#...; Other character references are not expanded.

1.3 Error Handling

By default, RapidXml uses C++ exceptions to report errors. If this behaviour is undesirable, RAPIDXML_NO_EXCEPTIONS can be defined to suppress exception code. See parse_error class and parse_error_handler() function for more information.

1.4 Memory Allocation

RapidXml uses a special memory pool object to allocate nodes and attributes, because direct allocation using new operator would be far too slow. Underlying memory allocations performed by the pool can be customized by use of memory_pool::set_allocator() function. See class memory_pool for more information.

1.5 W3C Compliance

RapidXml is not a W3C compliant parser, primarily because it ignores DOCTYPE declarations. There is a number of other, minor incompatibilities as well. Still, it can successfully parse and produce complete trees of all valid XML files in W3C conformance suite (over 1000 files specially designed to find flaws in XML processors). In destructive mode it performs whitespace normalization and character entity substitution for a small set of built-in entities.

1.6 API Design

RapidXml API is minimalistic, to reduce code size as much as possible, and facilitate use in embedded environments. Additional convenience functions are provided in separate headers: rapidxml_utils.hpp and rapidxml_print.hpp. Contents of these headers is not an essential part of the library, and is currently not documented (otherwise than with comments in code).

1.7 Reliability

RapidXml is very robust and comes with a large harness of unit tests. Special care has been taken to ensure stability of the parser no matter what source text is thrown at it. One of the unit tests produces 100,000 randomly corrupted variants of XML document, which (when uncorrupted) contains all constructs recognized by RapidXml. RapidXml passes this test when it correctly recognizes that errors have been introduced, and does not crash or loop indefinitely.

+ Another unit test puts RapidXml head-to-head with another, well estabilished XML parser, and verifies that their outputs match across a wide variety of small and large documents.

+ Yet another test feeds RapidXml with over 1000 test files from W3C compliance suite, and verifies that correct results are obtained. There are also additional tests that verify each API function separately, and test that various parsing modes work as expected.

1.8 Acknowledgements

I would like to thank Arseny Kapoulkine for his work on pugixml, which was an inspiration for this project. Additional thanks go to Kristen Wegner for creating pugxml, from which pugixml was derived. Janusz Wohlfeil kindly ran RapidXml speed tests on hardware that I did not have access to, allowing me to expand performance comparison table.

2. Two Minute Tutorial

2.1 Parsing

The following code causes RapidXml to parse a zero-terminated string named text:
using namespace rapidxml;
+xml_document<> doc;    // character type defaults to char
+doc.parse<0>(text);    // 0 means default parse flags
+
doc object is now a root of DOM tree containing representation of the parsed XML. Because all RapidXml interface is contained inside namespace rapidxml, users must either bring contents of this namespace into scope, or fully qualify all the names. Class xml_document represents a root of the DOM hierarchy. By means of public inheritance, it is also an xml_node and a memory_pool. Template parameter of xml_document::parse() function is used to specify parsing flags, with which you can fine-tune behaviour of the parser. Note that flags must be a compile-time constant.

2.2 Accessing The DOM Tree

To access the DOM tree, use methods of xml_node and xml_attribute classes:
cout << "Name of my first node is: " << doc.first_node()->name() << "\n";
+xml_node<> *node = doc.first_node("foobar");
+cout << "Node foobar has value " << node->value() << "\n";
+for (xml_attribute<> *attr = node->first_attribute();
+     attr; attr = attr->next_attribute())
+{
+    cout << "Node foobar has attribute " << attr->name() << " ";
+    cout << "with value " << attr->value() << "\n";
+}
+

2.3 Modifying The DOM Tree

DOM tree produced by the parser is fully modifiable. Nodes and attributes can be added/removed, and their contents changed. The below example creates a HTML document, whose sole contents is a link to google.com website:
xml_document<> doc;
+xml_node<> *node = doc.allocate_node(node_element, "a", "Google");
+doc.append_node(node);
+xml_attribute<> *attr = doc.allocate_attribute("href", "google.com");
+node->append_attribute(attr);
+
One quirk is that nodes and attributes do not own the text of their names and values. This is because normally they only store pointers to the source text. So, when assigning a new name or value to the node, care must be taken to ensure proper lifetime of the string. The easiest way to achieve it is to allocate the string from the xml_document memory pool. In the above example this is not necessary, because we are only assigning character constants. But the code below uses memory_pool::allocate_string() function to allocate node name (which will have the same lifetime as the document), and assigns it to a new node:
xml_document<> doc;
+char *node_name = doc.allocate_string(name);        // Allocate string and copy name into it
+xml_node<> *node = doc.allocate_node(node_element, node_name);  // Set node name to node_name
+
Check Reference section for description of the entire interface.

2.4 Printing XML

You can print xml_document and xml_node objects into an XML string. Use print() function or operator <<, which are defined in rapidxml_print.hpp header.
using namespace rapidxml;
+xml_document<> doc;    // character type defaults to char
+// ... some code to fill the document
+
+// Print to stream using operator <<
+std::cout << doc;   
+
+// Print to stream using print function, specifying printing flags
+print(std::cout, doc, 0);   // 0 means default printing flags
+
+// Print to string using output iterator
+std::string s;
+print(std::back_inserter(s), doc, 0);
+
+// Print to memory buffer using output iterator
+char buffer[4096];                      // You are responsible for making the buffer large enough!
+char *end = print(buffer, doc, 0);      // end contains pointer to character after last printed character
+*end = 0;                               // Add string terminator after XML
+

3. Differences From Regular XML Parsers

RapidXml is an in-situ parser, which allows it to achieve very high parsing speed. In-situ means that parser does not make copies of strings. Instead, it places pointers to the source text in the DOM hierarchy.

3.1 Lifetime Of Source Text

In-situ parsing requires that source text lives at least as long as the document object. If source text is destroyed, names and values of nodes in DOM tree will become destroyed as well. Additionally, whitespace processing, character entity translation, and zero-termination of strings require that source text be modified during parsing (but see non-destructive mode). This makes the text useless for further processing once it was parsed by RapidXml.

+ In many cases however, these are not serious issues.

3.2 Ownership Of Strings

Nodes and attributes produced by RapidXml do not own their name and value strings. They merely hold the pointers to them. This means you have to be careful when setting these values manually, by using xml_base::name(const Ch *) or xml_base::value(const Ch *) functions. Care must be taken to ensure that lifetime of the string passed is at least as long as lifetime of the node/attribute. The easiest way to achieve it is to allocate the string from memory_pool owned by the document. Use memory_pool::allocate_string() function for this purpose.

3.3 Destructive Vs Non-Destructive Mode

By default, the parser modifies source text during the parsing process. This is required to achieve character entity translation, whitespace normalization, and zero-termination of strings.

+ In some cases this behaviour may be undesirable, for example if source text resides in read only memory, or is mapped to memory directly from file. By using appropriate parser flags (parse_non_destructive), source text modifications can be disabled. However, because RapidXml does in-situ parsing, it obviously has the following side-effects:

4. Performance

RapidXml achieves its speed through use of several techniques:
  • In-situ parsing. When building DOM tree, RapidXml does not make copies of string data, such as node names and values. Instead, it stores pointers to interior of the source text.
  • Use of template metaprogramming techniques. This allows it to move much of the work to compile time. Through magic of the templates, C++ compiler generates a separate copy of parsing code for any combination of parser flags you use. In each copy, all possible decisions are made at compile time and all unused code is omitted.
  • Extensive use of lookup tables for parsing.
  • Hand-tuned C++ with profiling done on several most popular CPUs.
This results in a very small and fast code: a parser which is custom tailored to exact needs with each invocation.

4.1 Comparison With Other Parsers

The table below compares speed of RapidXml to some other parsers, and to strlen() function executed on the same data. On a modern CPU (as of 2007), you can expect parsing throughput to be close to 1 GB/s. As a rule of thumb, parsing speed is about 50-100x faster than Xerces DOM, 30-60x faster than TinyXml, 3-12x faster than pugxml, and about 5% - 30% faster than pugixml, the fastest XML parser I know of.
  • The test file is a real-world, 50kB large, moderately dense XML file.
  • All timing is done by using RDTSC instruction present in Pentium-compatible CPUs.
  • No profile-guided optimizations are used.
  • All parsers are running in their fastest modes.
  • The results are given in CPU cycles per character, so frequency of CPUs is irrelevant.
  • The results are minimum values from a large number of runs, to minimize effects of operating system activity, task switching, interrupt handling etc.
  • A single parse of the test file takes about 1/10th of a millisecond, so with large number of runs there is a good chance of hitting at least one no-interrupt streak, and obtaining undisturbed results.
Platform
Compiler
strlen() RapidXml pugixml 0.3 pugxml TinyXml
Pentium 4
MSVC 8.0
2.5
5.4
7.0
61.7
298.8
Pentium 4
gcc 4.1.1
0.8
6.1
9.5
67.0
413.2
Core 2
MSVC 8.0
1.0
4.5
5.0
24.6
154.8
Core 2
gcc 4.1.1
0.6
4.6
5.4
28.3
229.3
Athlon XP
MSVC 8.0
3.1
7.7
8.0
25.5
182.6
Athlon XP
gcc 4.1.1
0.9
8.2
9.2
33.7
265.2
Pentium 3
MSVC 8.0
2.0
6.3
7.0
30.9
211.9
Pentium 3
gcc 4.1.1
1.0
6.7
8.9
35.3
316.0
(*) All results are in CPU cycles per character of source text

5. Reference

This section lists all classes, functions, constants etc. and describes them in detail.
class + template + rapidxml::memory_pool
+ constructor + memory_pool()
+ destructor + ~memory_pool()
function allocate_node(node_type type, const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0)
function allocate_attribute(const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0)
function allocate_string(const Ch *source=0, std::size_t size=0)
function clone_node(const xml_node< Ch > *source, xml_node< Ch > *result=0)
function clear()
function set_allocator(alloc_func *af, free_func *ff)

class rapidxml::parse_error
+ constructor + parse_error(const char *what, void *where)
function what() const
function where() const

class + template + rapidxml::xml_attribute
+ constructor + xml_attribute()
function document() const
function previous_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function next_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const

class + template + rapidxml::xml_base
+ constructor + xml_base()
function name() const
function name_size() const
function value() const
function value_size() const
function name(const Ch *name, std::size_t size)
function name(const Ch *name)
function value(const Ch *value, std::size_t size)
function value(const Ch *value)
function parent() const

class + template + rapidxml::xml_document
+ constructor + xml_document()
function parse(Ch *text)
function clear()

class + template + rapidxml::xml_node
+ constructor + xml_node(node_type type)
function type() const
function document() const
function first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function last_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function previous_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function last_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
function type(node_type type)
function prepend_node(xml_node< Ch > *child)
function append_node(xml_node< Ch > *child)
function insert_node(xml_node< Ch > *where, xml_node< Ch > *child)
function remove_first_node()
function remove_last_node()
function remove_node(xml_node< Ch > *where)
function remove_all_nodes()
function prepend_attribute(xml_attribute< Ch > *attribute)
function append_attribute(xml_attribute< Ch > *attribute)
function insert_attribute(xml_attribute< Ch > *where, xml_attribute< Ch > *attribute)
function remove_first_attribute()
function remove_last_attribute()
function remove_attribute(xml_attribute< Ch > *where)
function remove_all_attributes()

namespace rapidxml
enum node_type
function parse_error_handler(const char *what, void *where)
function print(OutIt out, const xml_node< Ch > &node, int flags=0)
function print(std::basic_ostream< Ch > &out, const xml_node< Ch > &node, int flags=0)
function operator<<(std::basic_ostream< Ch > &out, const xml_node< Ch > &node)
+ constant + parse_no_data_nodes
+ constant + parse_no_element_values
+ constant + parse_no_string_terminators
+ constant + parse_no_entity_translation
+ constant + parse_no_utf8
+ constant + parse_declaration_node
+ constant + parse_comment_nodes
+ constant + parse_doctype_node
+ constant + parse_pi_nodes
+ constant + parse_validate_closing_tags
+ constant + parse_trim_whitespace
+ constant + parse_normalize_whitespace
+ constant + parse_default
+ constant + parse_non_destructive
+ constant + parse_fastest
+ constant + parse_full
+ constant + print_no_indenting


class + template + rapidxml::memory_pool

+ + Defined in rapidxml.hpp
+ Base class for + xml_document

Description

This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. In most cases, you will not need to use this class directly. However, if you need to create nodes manually or modify names/values of nodes, you are encouraged to use memory_pool of relevant xml_document to allocate the memory. Not only is this faster than allocating them by using new operator, but also their lifetime will be tied to the lifetime of document, possibly simplyfing memory management.

+ Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. You can also call allocate_string() function to allocate strings. Such strings can then be used as names or values of nodes without worrying about their lifetime. Note that there is no free() function -- all allocations are freed at once when clear() function is called, or when the pool is destroyed.

+ It is also possible to create a standalone memory_pool, and use it to allocate nodes, whose lifetime will not be tied to any document.

+ Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. Until static memory is exhausted, no dynamic memory allocations are done. When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, by using global new[] and delete[] operators. This behaviour can be changed by setting custom allocation routines. Use set_allocator() function to set them.

+ Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. This value defaults to the size of pointer on target architecture.

+ To obtain absolutely top performance from the parser, it is important that all nodes are allocated from a single, contiguous block of memory. Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT to obtain best wasted memory to performance compromise. To do it, define their values before rapidxml.hpp file is included.

Parameters

Ch
Character type of created nodes.

+ constructor + memory_pool::memory_pool

Synopsis

memory_pool(); +

Description

Constructs empty pool with default allocator functions.

+ destructor + memory_pool::~memory_pool

Synopsis

~memory_pool(); +

Description

Destroys pool and frees all the memory. This causes memory occupied by nodes allocated by the pool to be freed. Nodes allocated from the pool are no longer valid.

function memory_pool::allocate_node

Synopsis

xml_node<Ch>* allocate_node(node_type type, const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0); +

Description

Allocates a new node from the pool, and optionally assigns name and value to it. If the allocation request cannot be accomodated, this function will throw std::bad_alloc. If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function will call rapidxml::parse_error_handler() function.

Parameters

type
Type of node to create.
name
Name to assign to the node, or 0 to assign no name.
value
Value to assign to the node, or 0 to assign no value.
name_size
Size of name to assign, or 0 to automatically calculate size from name string.
value_size
Size of value to assign, or 0 to automatically calculate size from value string.

Returns

Pointer to allocated node. This pointer will never be NULL.

function memory_pool::allocate_attribute

Synopsis

xml_attribute<Ch>* allocate_attribute(const Ch *name=0, const Ch *value=0, std::size_t name_size=0, std::size_t value_size=0); +

Description

Allocates a new attribute from the pool, and optionally assigns name and value to it. If the allocation request cannot be accomodated, this function will throw std::bad_alloc. If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function will call rapidxml::parse_error_handler() function.

Parameters

name
Name to assign to the attribute, or 0 to assign no name.
value
Value to assign to the attribute, or 0 to assign no value.
name_size
Size of name to assign, or 0 to automatically calculate size from name string.
value_size
Size of value to assign, or 0 to automatically calculate size from value string.

Returns

Pointer to allocated attribute. This pointer will never be NULL.

function memory_pool::allocate_string

Synopsis

Ch* allocate_string(const Ch *source=0, std::size_t size=0); +

Description

Allocates a char array of given size from the pool, and optionally copies a given string to it. If the allocation request cannot be accomodated, this function will throw std::bad_alloc. If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function will call rapidxml::parse_error_handler() function.

Parameters

source
String to initialize the allocated memory with, or 0 to not initialize it.
size
Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated.

Returns

Pointer to allocated char array. This pointer will never be NULL.

function memory_pool::clone_node

Synopsis

xml_node<Ch>* clone_node(const xml_node< Ch > *source, xml_node< Ch > *result=0); +

Description

Clones an xml_node and its hierarchy of child nodes and attributes. Nodes and attributes are allocated from this memory pool. Names and values are not cloned, they are shared between the clone and the source. Result node can be optionally specified as a second parameter, in which case its contents will be replaced with cloned source node. This is useful when you want to clone entire document.

Parameters

source
Node to clone.
result
Node to put results in, or 0 to automatically allocate result node

Returns

Pointer to cloned node. This pointer will never be NULL.

function memory_pool::clear

Synopsis

void clear(); +

Description

Clears the pool. This causes memory occupied by nodes allocated by the pool to be freed. Any nodes or strings allocated from the pool will no longer be valid.

function memory_pool::set_allocator

Synopsis

void set_allocator(alloc_func *af, free_func *ff); +

Description

Sets or resets the user-defined memory allocation functions for the pool. This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. Allocation function must not return invalid pointer on failure. It should either throw, stop the program, or use longjmp() function to pass control to other place of program. If it returns invalid pointer, results are undefined.

+ User defined allocation functions must have the following forms:

+void *allocate(std::size_t size);
+void free(void *pointer);

Parameters

af
Allocation function, or 0 to restore default function
ff
Free function, or 0 to restore default function

class rapidxml::parse_error

+ + Defined in rapidxml.hpp

Description

Parse error exception. This exception is thrown by the parser when an error occurs. Use what() function to get human-readable error message. Use where() function to get a pointer to position within source text where error was detected.

+ If throwing exceptions by the parser is undesirable, it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. This function must be defined by the user.

+ This class derives from std::exception class.

+ constructor + parse_error::parse_error

Synopsis

parse_error(const char *what, void *where); +

Description

Constructs parse error.

function parse_error::what

Synopsis

virtual const char* what() const; +

Description

Gets human readable description of error.

Returns

Pointer to null terminated description of the error.

function parse_error::where

Synopsis

Ch* where() const; +

Description

Gets pointer to character data where error happened. Ch should be the same as char type of xml_document that produced the error.

Returns

Pointer to location within the parsed string where error occured.

class + template + rapidxml::xml_attribute

+ + Defined in rapidxml.hpp
+ Inherits from + xml_base

Description

Class representing attribute node of XML document. Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). Note that after parse, both name and value of attribute will point to interior of source text used for parsing. Thus, this text must persist in memory for the lifetime of attribute.

Parameters

Ch
Character type to use.

+ constructor + xml_attribute::xml_attribute

Synopsis

xml_attribute(); +

Description

Constructs an empty attribute with the specified type. Consider using memory_pool of appropriate xml_document if allocating attributes manually.

function xml_attribute::document

Synopsis

xml_document<Ch>* document() const; +

Description

Gets document of which attribute is a child.

Returns

Pointer to document that contains this attribute, or 0 if there is no parent document.

function xml_attribute::previous_attribute

Synopsis

xml_attribute<Ch>* previous_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets previous attribute, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

function xml_attribute::next_attribute

Synopsis

xml_attribute<Ch>* next_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets next attribute, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

class + template + rapidxml::xml_base

+ + Defined in rapidxml.hpp
+ Base class for + xml_attribute xml_node

Description

Base class for xml_node and xml_attribute implementing common functions: name(), name_size(), value(), value_size() and parent().

Parameters

Ch
Character type to use

+ constructor + xml_base::xml_base

Synopsis

xml_base(); +

function xml_base::name

Synopsis

Ch* name() const; +

Description

Gets name of the node. Interpretation of name depends on type of node. Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.

+ Use name_size() function to determine length of the name.

Returns

Name of node, or empty string if node has no name.

function xml_base::name_size

Synopsis

std::size_t name_size() const; +

Description

Gets size of node name, not including terminator character. This function works correctly irrespective of whether name is or is not zero terminated.

Returns

Size of node name, in characters.

function xml_base::value

Synopsis

Ch* value() const; +

Description

Gets value of node. Interpretation of value depends on type of node. Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse.

+ Use value_size() function to determine length of the value.

Returns

Value of node, or empty string if node has no value.

function xml_base::value_size

Synopsis

std::size_t value_size() const; +

Description

Gets size of node value, not including terminator character. This function works correctly irrespective of whether value is or is not zero terminated.

Returns

Size of node value, in characters.

function xml_base::name

Synopsis

void name(const Ch *name, std::size_t size); +

Description

Sets name of node to a non zero-terminated string. See Ownership Of Strings .

+ Note that node does not own its name or value, it only stores a pointer to it. It will not delete or otherwise free the pointer on destruction. It is reponsibility of the user to properly manage lifetime of the string. The easiest way to achieve it is to use memory_pool of the document to allocate the string - on destruction of the document the string will be automatically freed.

+ Size of name must be specified separately, because name does not have to be zero terminated. Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated).

Parameters

name
Name of node to set. Does not have to be zero terminated.
size
Size of name, in characters. This does not include zero terminator, if one is present.

function xml_base::name

Synopsis

void name(const Ch *name); +

Description

Sets name of node to a zero-terminated string. See also Ownership Of Strings and xml_node::name(const Ch *, std::size_t).

Parameters

name
Name of node to set. Must be zero terminated.

function xml_base::value

Synopsis

void value(const Ch *value, std::size_t size); +

Description

Sets value of node to a non zero-terminated string. See Ownership Of Strings .

+ Note that node does not own its name or value, it only stores a pointer to it. It will not delete or otherwise free the pointer on destruction. It is reponsibility of the user to properly manage lifetime of the string. The easiest way to achieve it is to use memory_pool of the document to allocate the string - on destruction of the document the string will be automatically freed.

+ Size of value must be specified separately, because it does not have to be zero terminated. Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated).

+ If an element has a child node of type node_data, it will take precedence over element value when printing. If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser.

Parameters

value
value of node to set. Does not have to be zero terminated.
size
Size of value, in characters. This does not include zero terminator, if one is present.

function xml_base::value

Synopsis

void value(const Ch *value); +

Description

Sets value of node to a zero-terminated string. See also Ownership Of Strings and xml_node::value(const Ch *, std::size_t).

Parameters

value
Vame of node to set. Must be zero terminated.

function xml_base::parent

Synopsis

xml_node<Ch>* parent() const; +

Description

Gets node parent.

Returns

Pointer to parent node, or 0 if there is no parent.

class + template + rapidxml::xml_document

+ + Defined in rapidxml.hpp
+ Inherits from + xml_node memory_pool

Description

This class represents root of the DOM hierarchy. It is also an xml_node and a memory_pool through public inheritance. Use parse() function to build a DOM tree from a zero-terminated XML text string. parse() function allocates memory for nodes and attributes by using functions of xml_document, which are inherited from memory_pool. To access root node of the document, use the document itself, as if it was an xml_node.

Parameters

Ch
Character type to use.

+ constructor + xml_document::xml_document

Synopsis

xml_document(); +

Description

Constructs empty XML document.

function xml_document::parse

Synopsis

void parse(Ch *text); +

Description

Parses zero-terminated XML string according to given flags. Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. The string must persist for the lifetime of the document. In case of error, rapidxml::parse_error exception will be thrown.

+ If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. Make sure that data is zero-terminated.

+ Document can be parsed into multiple times. Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool.

Parameters

text
XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser.

function xml_document::clear

Synopsis

void clear(); +

Description

Clears the document by deleting all nodes and clearing the memory pool. All nodes owned by document pool are destroyed.

class + template + rapidxml::xml_node

+ + Defined in rapidxml.hpp
+ Inherits from + xml_base
+ Base class for + xml_document

Description

Class representing a node of XML document. Each node may have associated name and value strings, which are available through name() and value() functions. Interpretation of name and value depends on type of the node. Type of node can be determined by using type() function.

+ Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. Thus, this text must persist in the memory for the lifetime of node.

Parameters

Ch
Character type to use.

+ constructor + xml_node::xml_node

Synopsis

xml_node(node_type type); +

Description

Constructs an empty node with the specified type. Consider using memory_pool of appropriate document to allocate nodes manually.

Parameters

type
Type of node to construct.

function xml_node::type

Synopsis

node_type type() const; +

Description

Gets type of node.

Returns

Type of node.

function xml_node::document

Synopsis

xml_document<Ch>* document() const; +

Description

Gets document of which node is a child.

Returns

Pointer to document that contains this node, or 0 if there is no parent document.

function xml_node::first_node

Synopsis

xml_node<Ch>* first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets first child node, optionally matching node name.

Parameters

name
Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found child, or 0 if not found.

function xml_node::last_node

Synopsis

xml_node<Ch>* last_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets last child node, optionally matching node name. Behaviour is undefined if node has no children. Use first_node() to test if node has children.

Parameters

name
Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found child, or 0 if not found.

function xml_node::previous_sibling

Synopsis

xml_node<Ch>* previous_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets previous sibling node, optionally matching node name. Behaviour is undefined if node has no parent. Use parent() to test if node has a parent.

Parameters

name
Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found sibling, or 0 if not found.

function xml_node::next_sibling

Synopsis

xml_node<Ch>* next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets next sibling node, optionally matching node name. Behaviour is undefined if node has no parent. Use parent() to test if node has a parent.

Parameters

name
Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found sibling, or 0 if not found.

function xml_node::first_attribute

Synopsis

xml_attribute<Ch>* first_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets first attribute of node, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

function xml_node::last_attribute

Synopsis

xml_attribute<Ch>* last_attribute(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const; +

Description

Gets last attribute of node, optionally matching attribute name.

Parameters

name
Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
name_size
Size of name, in characters, or 0 to have size calculated automatically from string
case_sensitive
Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters

Returns

Pointer to found attribute, or 0 if not found.

function xml_node::type

Synopsis

void type(node_type type); +

Description

Sets type of node.

Parameters

type
Type of node to set.

function xml_node::prepend_node

Synopsis

void prepend_node(xml_node< Ch > *child); +

Description

Prepends a new child node. The prepended child becomes the first child, and all existing children are moved one position back.

Parameters

child
Node to prepend.

function xml_node::append_node

Synopsis

void append_node(xml_node< Ch > *child); +

Description

Appends a new child node. The appended child becomes the last child.

Parameters

child
Node to append.

function xml_node::insert_node

Synopsis

void insert_node(xml_node< Ch > *where, xml_node< Ch > *child); +

Description

Inserts a new child node at specified place inside the node. All children after and including the specified node are moved one position back.

Parameters

where
Place where to insert the child, or 0 to insert at the back.
child
Node to insert.

function xml_node::remove_first_node

Synopsis

void remove_first_node(); +

Description

Removes first child node. If node has no children, behaviour is undefined. Use first_node() to test if node has children.

function xml_node::remove_last_node

Synopsis

void remove_last_node(); +

Description

Removes last child of the node. If node has no children, behaviour is undefined. Use first_node() to test if node has children.

function xml_node::remove_node

Synopsis

void remove_node(xml_node< Ch > *where); +

Description

Removes specified child from the node.

function xml_node::remove_all_nodes

Synopsis

void remove_all_nodes(); +

Description

Removes all child nodes (but not attributes).

function xml_node::prepend_attribute

Synopsis

void prepend_attribute(xml_attribute< Ch > *attribute); +

Description

Prepends a new attribute to the node.

Parameters

attribute
Attribute to prepend.

function xml_node::append_attribute

Synopsis

void append_attribute(xml_attribute< Ch > *attribute); +

Description

Appends a new attribute to the node.

Parameters

attribute
Attribute to append.

function xml_node::insert_attribute

Synopsis

void insert_attribute(xml_attribute< Ch > *where, xml_attribute< Ch > *attribute); +

Description

Inserts a new attribute at specified place inside the node. All attributes after and including the specified attribute are moved one position back.

Parameters

where
Place where to insert the attribute, or 0 to insert at the back.
attribute
Attribute to insert.

function xml_node::remove_first_attribute

Synopsis

void remove_first_attribute(); +

Description

Removes first attribute of the node. If node has no attributes, behaviour is undefined. Use first_attribute() to test if node has attributes.

function xml_node::remove_last_attribute

Synopsis

void remove_last_attribute(); +

Description

Removes last attribute of the node. If node has no attributes, behaviour is undefined. Use first_attribute() to test if node has attributes.

function xml_node::remove_attribute

Synopsis

void remove_attribute(xml_attribute< Ch > *where); +

Description

Removes specified attribute from node.

Parameters

where
Pointer to attribute to be removed.

function xml_node::remove_all_attributes

Synopsis

void remove_all_attributes(); +

Description

Removes all attributes of node.

enum node_type

Description

Enumeration listing all node types produced by the parser. Use xml_node::type() function to query node type.

Values

node_document
A document node. Name and value are empty.
node_element
An element node. Name contains element name. Value contains text of first data node.
node_data
A data node. Name is empty. Value contains data text.
node_cdata
A CDATA node. Name is empty. Value contains data text.
node_comment
A comment node. Name is empty. Value contains comment text.
node_declaration
A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes.
node_doctype
A DOCTYPE node. Name is empty. Value contains DOCTYPE text.
node_pi
A PI node. Name contains target. Value contains instructions.

function parse_error_handler

Synopsis

void rapidxml::parse_error_handler(const char *what, void *where); +

Description

When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function is called to notify user about the error. It must be defined by the user.

+ This function cannot return. If it does, the results are undefined.

+ A very simple definition might look like that: + void rapidxml::parse_error_handler(const char *what, void *where) + { + std::cout << "Parse error: " << what << "\n"; + std::abort(); + } +

Parameters

what
Human readable description of the error.
where
Pointer to character data where error was detected.

function print

Synopsis

OutIt rapidxml::print(OutIt out, const xml_node< Ch > &node, int flags=0); +

Description

Prints XML to given output iterator.

Parameters

out
Output iterator to print to.
node
Node to be printed. Pass xml_document to print entire document.
flags
Flags controlling how XML is printed.

Returns

Output iterator pointing to position immediately after last character of printed text.

function print

Synopsis

std::basic_ostream<Ch>& rapidxml::print(std::basic_ostream< Ch > &out, const xml_node< Ch > &node, int flags=0); +

Description

Prints XML to given output stream.

Parameters

out
Output stream to print to.
node
Node to be printed. Pass xml_document to print entire document.
flags
Flags controlling how XML is printed.

Returns

Output stream.

function operator<<

Synopsis

std::basic_ostream<Ch>& rapidxml::operator<<(std::basic_ostream< Ch > &out, const xml_node< Ch > &node); +

Description

Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.

Parameters

out
Output stream to print to.
node
Node to be printed.

Returns

Output stream.

+ constant + parse_no_data_nodes

Synopsis

const int parse_no_data_nodes + = 0x1; +

Description

Parse flag instructing the parser to not create data nodes. Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_no_element_values

Synopsis

const int parse_no_element_values + = 0x2; +

Description

Parse flag instructing the parser to not use text of first data node as a value of parent element. Can be combined with other flags by use of | operator. Note that child data nodes of element node take precendence over its value when printing. That is, if element has one or more child data nodes and a value, the value will be ignored. Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements.

+ See xml_document::parse() function.

+ constant + parse_no_string_terminators

Synopsis

const int parse_no_string_terminators + = 0x4; +

Description

Parse flag instructing the parser to not place zero terminators after strings in the source text. By default zero terminators are placed, modifying source text. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_no_entity_translation

Synopsis

const int parse_no_entity_translation + = 0x8; +

Description

Parse flag instructing the parser to not translate entities in the source text. By default entities are translated, modifying source text. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_no_utf8

Synopsis

const int parse_no_utf8 + = 0x10; +

Description

Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. By default, UTF-8 handling is enabled. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_declaration_node

Synopsis

const int parse_declaration_node + = 0x20; +

Description

Parse flag instructing the parser to create XML declaration node. By default, declaration node is not created. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_comment_nodes

Synopsis

const int parse_comment_nodes + = 0x40; +

Description

Parse flag instructing the parser to create comments nodes. By default, comment nodes are not created. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_doctype_node

Synopsis

const int parse_doctype_node + = 0x80; +

Description

Parse flag instructing the parser to create DOCTYPE node. By default, doctype node is not created. Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_pi_nodes

Synopsis

const int parse_pi_nodes + = 0x100; +

Description

Parse flag instructing the parser to create PI nodes. By default, PI nodes are not created. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_validate_closing_tags

Synopsis

const int parse_validate_closing_tags + = 0x200; +

Description

Parse flag instructing the parser to validate closing tag names. If not set, name inside closing tag is irrelevant to the parser. By default, closing tags are not validated. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_trim_whitespace

Synopsis

const int parse_trim_whitespace + = 0x400; +

Description

Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. By default, whitespace is not trimmed. This flag does not cause the parser to modify source text. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_normalize_whitespace

Synopsis

const int parse_normalize_whitespace + = 0x800; +

Description

Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. By default, whitespace is not normalized. If this flag is specified, source text will be modified. Can be combined with other flags by use of | operator.

+ See xml_document::parse() function.

+ constant + parse_default

Synopsis

const int parse_default + = 0; +

Description

Parse flags which represent default behaviour of the parser. This is always equal to 0, so that all other flags can be simply ored together. Normally there is no need to inconveniently disable flags by anding with their negated (~) values. This also means that meaning of each flag is a negation of the default setting. For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, and using the flag will disable it.

+ See xml_document::parse() function.

+ constant + parse_non_destructive

Synopsis

const int parse_non_destructive + = parse_no_string_terminators | parse_no_entity_translation; +

Description

A combination of parse flags that forbids any modifications of the source text. This also results in faster parsing. However, note that the following will occur:
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • entities will not be translated
  • whitespace will not be normalized
+See xml_document::parse() function.

+ constant + parse_fastest

Synopsis

const int parse_fastest + = parse_non_destructive | parse_no_data_nodes; +

Description

A combination of parse flags resulting in fastest possible parsing, without sacrificing important data.

+ See xml_document::parse() function.

+ constant + parse_full

Synopsis

const int parse_full + = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; +

Description

A combination of parse flags resulting in largest amount of data being extracted. This usually results in slowest parsing.

+ See xml_document::parse() function.

+ constant + print_no_indenting

Synopsis

const int print_no_indenting + = 0x1; +

Description

Printer flag instructing the printer to suppress indenting of XML. See print() function.

\ No newline at end of file diff --git a/ext/rapidxml/rapidxml.hpp b/ext/rapidxml/rapidxml.hpp new file mode 100644 index 0000000000..ae91e081d0 --- /dev/null +++ b/ext/rapidxml/rapidxml.hpp @@ -0,0 +1,2596 @@ +#ifndef RAPIDXML_HPP_INCLUDED +#define RAPIDXML_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation + +// If standard library is disabled, user must provide implementations of required functions and typedefs +#if !defined(RAPIDXML_NO_STDLIB) + #include // For std::size_t + #include // For assert + #include // For placement new +#endif + +// On MSVC, disable "conditional expression is constant" warning (level 4). +// This warning is almost impossible to avoid with certain types of templated code +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) // Conditional expression is constant +#endif + +/////////////////////////////////////////////////////////////////////////// +// RAPIDXML_PARSE_ERROR + +#if defined(RAPIDXML_NO_EXCEPTIONS) + +#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } + +namespace rapidxml +{ + //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, + //! this function is called to notify user about the error. + //! It must be defined by the user. + //!

+ //! This function cannot return. If it does, the results are undefined. + //!

+ //! A very simple definition might look like that: + //!

+    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
+    //! {
+    //!     std::cout << "Parse error: " << what << "\n";
+    //!     std::abort();
+    //! }
+    //! 
+ //! \param what Human readable description of the error. + //! \param where Pointer to character data where error was detected. + void parse_error_handler(const char *what, void *where); +} + +#else + +#include // For std::exception + +#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) + +namespace rapidxml +{ + + //! Parse error exception. + //! This exception is thrown by the parser when an error occurs. + //! Use what() function to get human-readable error message. + //! Use where() function to get a pointer to position within source text where error was detected. + //!

+ //! If throwing exceptions by the parser is undesirable, + //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. + //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. + //! This function must be defined by the user. + //!

+ //! This class derives from std::exception class. + class parse_error: public std::exception + { + + public: + + //! Constructs parse error + parse_error(const char *what, void *where) + : m_what(what) + , m_where(where) + { + } + + //! Gets human readable description of error. + //! \return Pointer to null terminated description of the error. + virtual const char *what() const throw() + { + return m_what; + } + + //! Gets pointer to character data where error happened. + //! Ch should be the same as char type of xml_document that produced the error. + //! \return Pointer to location within the parsed string where error occured. + template + Ch *where() const + { + return reinterpret_cast(m_where); + } + + private: + + const char *m_what; + void *m_where; + + }; +} + +#endif + +/////////////////////////////////////////////////////////////////////////// +// Pool sizes + +#ifndef RAPIDXML_STATIC_POOL_SIZE + // Size of static memory block of memory_pool. + // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. + #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_DYNAMIC_POOL_SIZE + // Size of dynamic memory block of memory_pool. + // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. + #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_ALIGNMENT + // Memory allocation alignment. + // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. + // All memory allocations for nodes, attributes and strings will be aligned to this value. + // This must be a power of 2 and at least 1, otherwise memory_pool will not work. + #define RAPIDXML_ALIGNMENT sizeof(void *) +#endif + +namespace rapidxml +{ + // Forward declarations + template class xml_node; + template class xml_attribute; + template class xml_document; + + //! Enumeration listing all node types produced by the parser. + //! Use xml_node::type() function to query node type. + enum node_type + { + node_document, //!< A document node. Name and value are empty. + node_element, //!< An element node. Name contains element name. Value contains text of first data node. + node_data, //!< A data node. Name is empty. Value contains data text. + node_cdata, //!< A CDATA node. Name is empty. Value contains data text. + node_comment, //!< A comment node. Name is empty. Value contains comment text. + node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. + node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. + node_pi //!< A PI node. Name contains target. Value contains instructions. + }; + + /////////////////////////////////////////////////////////////////////// + // Parsing flags + + //! Parse flag instructing the parser to not create data nodes. + //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_data_nodes = 0x1; + + //! Parse flag instructing the parser to not use text of first data node as a value of parent element. + //! Can be combined with other flags by use of | operator. + //! Note that child data nodes of element node take precendence over its value when printing. + //! That is, if element has one or more child data nodes and a value, the value will be ignored. + //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. + //!

+ //! See xml_document::parse() function. + const int parse_no_element_values = 0x2; + + //! Parse flag instructing the parser to not place zero terminators after strings in the source text. + //! By default zero terminators are placed, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_string_terminators = 0x4; + + //! Parse flag instructing the parser to not translate entities in the source text. + //! By default entities are translated, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_entity_translation = 0x8; + + //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. + //! By default, UTF-8 handling is enabled. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_utf8 = 0x10; + + //! Parse flag instructing the parser to create XML declaration node. + //! By default, declaration node is not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_declaration_node = 0x20; + + //! Parse flag instructing the parser to create comments nodes. + //! By default, comment nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_comment_nodes = 0x40; + + //! Parse flag instructing the parser to create DOCTYPE node. + //! By default, doctype node is not created. + //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_doctype_node = 0x80; + + //! Parse flag instructing the parser to create PI nodes. + //! By default, PI nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_pi_nodes = 0x100; + + //! Parse flag instructing the parser to validate closing tag names. + //! If not set, name inside closing tag is irrelevant to the parser. + //! By default, closing tags are not validated. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_validate_closing_tags = 0x200; + + //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. + //! By default, whitespace is not trimmed. + //! This flag does not cause the parser to modify source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_trim_whitespace = 0x400; + + //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. + //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. + //! By default, whitespace is not normalized. + //! If this flag is specified, source text will be modified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_normalize_whitespace = 0x800; + + // Compound flags + + //! Parse flags which represent default behaviour of the parser. + //! This is always equal to 0, so that all other flags can be simply ored together. + //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. + //! This also means that meaning of each flag is a negation of the default setting. + //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, + //! and using the flag will disable it. + //!

+ //! See xml_document::parse() function. + const int parse_default = 0; + + //! A combination of parse flags that forbids any modifications of the source text. + //! This also results in faster parsing. However, note that the following will occur: + //!
    + //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • + //!
  • entities will not be translated
  • + //!
  • whitespace will not be normalized
  • + //!
+ //! See xml_document::parse() function. + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; + + //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. + //!

+ //! See xml_document::parse() function. + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; + + //! A combination of parse flags resulting in largest amount of data being extracted. + //! This usually results in slowest parsing. + //!

+ //! See xml_document::parse() function. + const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; + + /////////////////////////////////////////////////////////////////////// + // Internals + + //! \cond internal + namespace internal + { + + // Struct that contains lookup tables for the parser + // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). + template + struct lookup_tables + { + static const unsigned char lookup_whitespace[256]; // Whitespace table + static const unsigned char lookup_node_name[256]; // Node name table + static const unsigned char lookup_text[256]; // Text table + static const unsigned char lookup_text_pure_no_ws[256]; // Text table + static const unsigned char lookup_text_pure_with_ws[256]; // Text table + static const unsigned char lookup_attribute_name[256]; // Attribute name table + static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes + static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes + static const unsigned char lookup_digits[256]; // Digits + static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters + }; + + // Find length of the string + template + inline std::size_t measure(const Ch *p) + { + const Ch *tmp = p; + while (*tmp) + ++tmp; + return tmp - p; + } + + // Compare strings for equality + template + inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) + { + if (size1 != size2) + return false; + if (case_sensitive) + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (*p1 != *p2) + return false; + } + else + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) + return false; + } + return true; + } + } + //! \endcond + + /////////////////////////////////////////////////////////////////////// + // Memory pool + + //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. + //! In most cases, you will not need to use this class directly. + //! However, if you need to create nodes manually or modify names/values of nodes, + //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. + //! Not only is this faster than allocating them by using new operator, + //! but also their lifetime will be tied to the lifetime of document, + //! possibly simplyfing memory management. + //!

+ //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. + //! You can also call allocate_string() function to allocate strings. + //! Such strings can then be used as names or values of nodes without worrying about their lifetime. + //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, + //! or when the pool is destroyed. + //!

+ //! It is also possible to create a standalone memory_pool, and use it + //! to allocate nodes, whose lifetime will not be tied to any document. + //!

+ //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. + //! Until static memory is exhausted, no dynamic memory allocations are done. + //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, + //! by using global new[] and delete[] operators. + //! This behaviour can be changed by setting custom allocation routines. + //! Use set_allocator() function to set them. + //!

+ //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. + //! This value defaults to the size of pointer on target architecture. + //!

+ //! To obtain absolutely top performance from the parser, + //! it is important that all nodes are allocated from a single, contiguous block of memory. + //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. + //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT + //! to obtain best wasted memory to performance compromise. + //! To do it, define their values before rapidxml.hpp file is included. + //! \param Ch Character type of created nodes. + template + class memory_pool + { + + public: + + //! \cond internal + typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory + typedef void (free_func)(void *); // Type of user-defined function used to free memory + //! \endcond + + //! Constructs empty pool with default allocator functions. + memory_pool() + : m_alloc_func(0) + , m_free_func(0) + { + init(); + } + + //! Destroys pool and frees all the memory. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Nodes allocated from the pool are no longer valid. + ~memory_pool() + { + clear(); + } + + //! Allocates a new node from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param type Type of node to create. + //! \param name Name to assign to the node, or 0 to assign no name. + //! \param value Value to assign to the node, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated node. This pointer will never be NULL. + xml_node *allocate_node(node_type type, + const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_node)); + xml_node *node = new(memory) xml_node(type); + if (name) + { + if (name_size > 0) + node->name(name, name_size); + else + node->name(name); + } + if (value) + { + if (value_size > 0) + node->value(value, value_size); + else + node->value(value); + } + return node; + } + + //! Allocates a new attribute from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param name Name to assign to the attribute, or 0 to assign no name. + //! \param value Value to assign to the attribute, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated attribute. This pointer will never be NULL. + xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_attribute)); + xml_attribute *attribute = new(memory) xml_attribute; + if (name) + { + if (name_size > 0) + attribute->name(name, name_size); + else + attribute->name(name); + } + if (value) + { + if (value_size > 0) + attribute->value(value, value_size); + else + attribute->value(value); + } + return attribute; + } + + //! Allocates a char array of given size from the pool, and optionally copies a given string to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param source String to initialize the allocated memory with, or 0 to not initialize it. + //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. + //! \return Pointer to allocated char array. This pointer will never be NULL. + Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) + { + assert(source || size); // Either source or size (or both) must be specified + if (size == 0) + size = internal::measure(source) + 1; + Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); + if (source) + for (std::size_t i = 0; i < size; ++i) + result[i] = source[i]; + return result; + } + + //! Clones an xml_node and its hierarchy of child nodes and attributes. + //! Nodes and attributes are allocated from this memory pool. + //! Names and values are not cloned, they are shared between the clone and the source. + //! Result node can be optionally specified as a second parameter, + //! in which case its contents will be replaced with cloned source node. + //! This is useful when you want to clone entire document. + //! \param source Node to clone. + //! \param result Node to put results in, or 0 to automatically allocate result node + //! \return Pointer to cloned node. This pointer will never be NULL. + xml_node *clone_node(const xml_node *source, xml_node *result = 0) + { + // Prepare result node + if (result) + { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } + else + result = allocate_node(source->type()); + + // Clone name and value + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); + + // Clone child nodes and attributes + for (xml_node *child = source->first_node(); child; child = child->next_sibling()) + result->append_node(clone_node(child)); + for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) + result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); + + return result; + } + + //! Clears the pool. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Any nodes or strings allocated from the pool will no longer be valid. + void clear() + { + while (m_begin != m_static_memory) + { + char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; + if (m_free_func) + m_free_func(m_begin); + else + delete[] m_begin; + m_begin = previous_begin; + } + init(); + } + + //! Sets or resets the user-defined memory allocation functions for the pool. + //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. + //! Allocation function must not return invalid pointer on failure. It should either throw, + //! stop the program, or use longjmp() function to pass control to other place of program. + //! If it returns invalid pointer, results are undefined. + //!

+ //! User defined allocation functions must have the following forms: + //!
+ //!
void *allocate(std::size_t size); + //!
void free(void *pointer); + //!

+ //! \param af Allocation function, or 0 to restore default function + //! \param ff Free function, or 0 to restore default function + void set_allocator(alloc_func *af, free_func *ff) + { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet + m_alloc_func = af; + m_free_func = ff; + } + + private: + + struct header + { + char *previous_begin; + }; + + void init() + { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory + sizeof(m_static_memory); + } + + char *align(char *ptr) + { + std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); + return ptr + alignment; + } + + char *allocate_raw(std::size_t size) + { + // Allocate + void *memory; + if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] + { + memory = m_alloc_func(size); + assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp + } + else + { + memory = new char[size]; +#ifdef RAPIDXML_NO_EXCEPTIONS + if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc + RAPIDXML_PARSE_ERROR("out of memory", 0); +#endif + } + return static_cast(memory); + } + + void *allocate_aligned(std::size_t size) + { + // Calculate aligned pointer + char *result = align(m_ptr); + + // If not enough memory left in current pool, allocate a new pool + if (result + size > m_end) + { + // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) + std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; + if (pool_size < size) + pool_size = size; + + // Allocate + std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation + char *raw_memory = allocate_raw(alloc_size); + + // Setup new pool in allocated memory + char *pool = align(raw_memory); + header *new_header = reinterpret_cast
(pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool + sizeof(header); + m_end = raw_memory + alloc_size; + + // Calculate aligned pointer again using new pool + result = align(m_ptr); + } + + // Update pool and return aligned pointer + m_ptr = result + size; + return result; + } + + char *m_begin; // Start of raw memory making up current pool + char *m_ptr; // First free byte in current pool + char *m_end; // One past last available byte in current pool + char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory + alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used + free_func *m_free_func; // Free function, or 0 if default is to be used + }; + + /////////////////////////////////////////////////////////////////////////// + // XML base + + //! Base class for xml_node and xml_attribute implementing common functions: + //! name(), name_size(), value(), value_size() and parent(). + //! \param Ch Character type to use + template + class xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + // Construct a base with empty name, value and parent + xml_base() + : m_name(0) + , m_value(0) + , m_parent(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets name of the node. + //! Interpretation of name depends on type of node. + //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use name_size() function to determine length of the name. + //! \return Name of node, or empty string if node has no name. + Ch *name() const + { + return m_name ? m_name : nullstr(); + } + + //! Gets size of node name, not including terminator character. + //! This function works correctly irrespective of whether name is or is not zero terminated. + //! \return Size of node name, in characters. + std::size_t name_size() const + { + return m_name ? m_name_size : 0; + } + + //! Gets value of node. + //! Interpretation of value depends on type of node. + //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use value_size() function to determine length of the value. + //! \return Value of node, or empty string if node has no value. + Ch *value() const + { + return m_value ? m_value : nullstr(); + } + + //! Gets size of node value, not including terminator character. + //! This function works correctly irrespective of whether value is or is not zero terminated. + //! \return Size of node value, in characters. + std::size_t value_size() const + { + return m_value ? m_value_size : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets name of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of name must be specified separately, because name does not have to be zero terminated. + //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! \param name Name of node to set. Does not have to be zero terminated. + //! \param size Size of name, in characters. This does not include zero terminator, if one is present. + void name(const Ch *name, std::size_t size) + { + m_name = const_cast(name); + m_name_size = size; + } + + //! Sets name of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). + //! \param name Name of node to set. Must be zero terminated. + void name(const Ch *name) + { + this->name(name, internal::measure(name)); + } + + //! Sets value of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of value must be specified separately, because it does not have to be zero terminated. + //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //!

+ //! If an element has a child node of type node_data, it will take precedence over element value when printing. + //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. + //! \param value value of node to set. Does not have to be zero terminated. + //! \param size Size of value, in characters. This does not include zero terminator, if one is present. + void value(const Ch *value, std::size_t size) + { + m_value = const_cast(value); + m_value_size = size; + } + + //! Sets value of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). + //! \param value Vame of node to set. Must be zero terminated. + void value(const Ch *value) + { + this->value(value, internal::measure(value)); + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets node parent. + //! \return Pointer to parent node, or 0 if there is no parent. + xml_node *parent() const + { + return m_parent; + } + + protected: + + // Return empty string + static Ch *nullstr() + { + static Ch zero = Ch('\0'); + return &zero; + } + + Ch *m_name; // Name of node, or 0 if no name + Ch *m_value; // Value of node, or 0 if no value + std::size_t m_name_size; // Length of node name, or undefined of no name + std::size_t m_value_size; // Length of node value, or undefined if no value + xml_node *m_parent; // Pointer to parent node, or 0 if none + + }; + + //! Class representing attribute node of XML document. + //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). + //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. + //! Thus, this text must persist in memory for the lifetime of attribute. + //! \param Ch Character type to use. + template + class xml_attribute: public xml_base + { + + friend class xml_node; + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty attribute with the specified type. + //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. + xml_attribute() + { + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which attribute is a child. + //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. + xml_document *document() const + { + if (xml_node *node = this->parent()) + { + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + else + return 0; + } + + //! Gets previous attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_prev_attribute : 0; + } + + //! Gets next attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_next_attribute : 0; + } + + private: + + xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero + xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML node + + //! Class representing a node of XML document. + //! Each node may have associated name and value strings, which are available through name() and value() functions. + //! Interpretation of name and value depends on type of the node. + //! Type of node can be determined by using type() function. + //!

+ //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. + //! Thus, this text must persist in the memory for the lifetime of node. + //! \param Ch Character type to use. + template + class xml_node: public xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty node with the specified type. + //! Consider using memory_pool of appropriate document to allocate nodes manually. + //! \param type Type of node to construct. + xml_node(node_type type) + : m_type(type) + , m_first_node(0) + , m_first_attribute(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets type of node. + //! \return Type of node. + node_type type() const + { + return m_type; + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which node is a child. + //! \return Pointer to document that contains this node, or 0 if there is no parent document. + xml_document *document() const + { + xml_node *node = const_cast *>(this); + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + + //! Gets first child node, optionally matching node name. + //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_first_node; child; child = child->next_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_first_node; + } + + //! Gets last child node, optionally matching node name. + //! Behaviour is undefined if node has no children. + //! Use first_node() to test if node has children. + //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(m_first_node); // Cannot query for last child if node has no children + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_last_node; child; child = child->previous_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_last_node; + } + + //! Gets previous sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_prev_sibling; + } + + //! Gets next sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_next_sibling; + } + + //! Gets first attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute; + } + + //! Gets last attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute ? m_last_attribute : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets type of node. + //! \param type Type of node to set. + void type(node_type type) + { + m_type = type; + } + + /////////////////////////////////////////////////////////////////////////// + // Node manipulation + + //! Prepends a new child node. + //! The prepended child becomes the first child, and all existing children are moved one position back. + //! \param child Node to prepend. + void prepend_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } + else + { + child->m_next_sibling = 0; + m_last_node = child; + } + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } + + //! Appends a new child node. + //! The appended child becomes the last child. + //! \param child Node to append. + void append_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } + else + { + child->m_prev_sibling = 0; + m_first_node = child; + } + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = 0; + } + + //! Inserts a new child node at specified place inside the node. + //! All children after and including the specified node are moved one position back. + //! \param where Place where to insert the child, or 0 to insert at the back. + //! \param child Node to insert. + void insert_node(xml_node *where, xml_node *child) + { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_document); + if (where == m_first_node) + prepend_node(child); + else if (where == 0) + append_node(child); + else + { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } + + //! Removes first child node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_first_node() + { + assert(first_node()); + xml_node *child = m_first_node; + m_first_node = child->m_next_sibling; + if (child->m_next_sibling) + child->m_next_sibling->m_prev_sibling = 0; + else + m_last_node = 0; + child->m_parent = 0; + } + + //! Removes last child of the node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_last_node() + { + assert(first_node()); + xml_node *child = m_last_node; + if (child->m_prev_sibling) + { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } + else + m_first_node = 0; + child->m_parent = 0; + } + + //! Removes specified child from the node + // \param where Pointer to child to be removed. + void remove_node(xml_node *where) + { + assert(where && where->parent() == this); + assert(first_node()); + if (where == m_first_node) + remove_first_node(); + else if (where == m_last_node) + remove_last_node(); + else + { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } + + //! Removes all child nodes (but not attributes). + void remove_all_nodes() + { + for (xml_node *node = first_node(); node; node = node->m_next_sibling) + node->m_parent = 0; + m_first_node = 0; + } + + //! Prepends a new attribute to the node. + //! \param attribute Attribute to prepend. + void prepend_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } + else + { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } + + //! Appends a new attribute to the node. + //! \param attribute Attribute to append. + void append_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } + else + { + attribute->m_prev_attribute = 0; + m_first_attribute = attribute; + } + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = 0; + } + + //! Inserts a new attribute at specified place inside the node. + //! All attributes after and including the specified attribute are moved one position back. + //! \param where Place where to insert the attribute, or 0 to insert at the back. + //! \param attribute Attribute to insert. + void insert_attribute(xml_attribute *where, xml_attribute *attribute) + { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + if (where == m_first_attribute) + prepend_attribute(attribute); + else if (where == 0) + append_attribute(attribute); + else + { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + + //! Removes first attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_first_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_first_attribute; + if (attribute->m_next_attribute) + { + attribute->m_next_attribute->m_prev_attribute = 0; + } + else + m_last_attribute = 0; + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + + //! Removes last attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_last_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_last_attribute; + if (attribute->m_prev_attribute) + { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } + else + m_first_attribute = 0; + attribute->m_parent = 0; + } + + //! Removes specified attribute from node. + //! \param where Pointer to attribute to be removed. + void remove_attribute(xml_attribute *where) + { + assert(first_attribute() && where->parent() == this); + if (where == m_first_attribute) + remove_first_attribute(); + else if (where == m_last_attribute) + remove_last_attribute(); + else + { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + + //! Removes all attributes of node. + void remove_all_attributes() + { + for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + attribute->m_parent = 0; + m_first_attribute = 0; + } + + private: + + /////////////////////////////////////////////////////////////////////////// + // Restrictions + + // No copying + xml_node(const xml_node &); + void operator =(const xml_node &); + + /////////////////////////////////////////////////////////////////////////// + // Data members + + // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. + // This is required for maximum performance, as it allows the parser to omit initialization of + // unneded/redundant values. + // + // The rules are as follows: + // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively + // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage + // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage + + node_type m_type; // Type of node; always valid + xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid + xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero + xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid + xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero + xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML document + + //! This class represents root of the DOM hierarchy. + //! It is also an xml_node and a memory_pool through public inheritance. + //! Use parse() function to build a DOM tree from a zero-terminated XML text string. + //! parse() function allocates memory for nodes and attributes by using functions of xml_document, + //! which are inherited from memory_pool. + //! To access root node of the document, use the document itself, as if it was an xml_node. + //! \param Ch Character type to use. + template + class xml_document: public xml_node, public memory_pool + { + + public: + + //! Constructs empty XML document + xml_document() + : xml_node(node_document) + { + } + + //! Parses zero-terminated XML string according to given flags. + //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. + //! The string must persist for the lifetime of the document. + //! In case of error, rapidxml::parse_error exception will be thrown. + //!

+ //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. + //! Make sure that data is zero-terminated. + //!

+ //! Document can be parsed into multiple times. + //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. + //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. + template + void parse(Ch *text) + { + assert(text); + + // Remove current contents + this->remove_all_nodes(); + this->remove_all_attributes(); + + // Parse BOM, if any + parse_bom(text); + + // Parse children + while (1) + { + // Skip whitespace before node + skip(text); + if (*text == 0) + break; + + // Parse and append new child + if (*text == Ch('<')) + { + ++text; // Skip '<' + if (xml_node *node = parse_node(text)) + this->append_node(node); + } + else + RAPIDXML_PARSE_ERROR("expected <", text); + } + + } + + //! Clears the document by deleting all nodes and clearing the memory pool. + //! All nodes owned by document pool are destroyed. + void clear() + { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool::clear(); + } + + private: + + /////////////////////////////////////////////////////////////////////// + // Internal character utility functions + + // Detect whitespace character + struct whitespace_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; + } + }; + + // Detect node name character + struct node_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; + } + }; + + // Detect attribute name character + struct attribute_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) + struct text_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_no_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_with_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; + } + }; + + // Detect attribute value character + template + struct attribute_value_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Detect attribute value character + template + struct attribute_value_pure_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Insert coded character, using UTF8 or 8-bit ASCII + template + static void insert_coded_character(Ch *&text, unsigned long code) + { + if (Flags & parse_no_utf8) + { + // Insert 8-bit ASCII character + // Todo: possibly verify that code is less than 256 and use replacement char otherwise? + text[0] = static_cast(code); + text += 1; + } + else + { + // Insert UTF8 sequence + if (code < 0x80) // 1 byte sequence + { + text[0] = static_cast(code); + text += 1; + } + else if (code < 0x800) // 2 byte sequence + { + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xC0); + text += 2; + } + else if (code < 0x10000) // 3 byte sequence + { + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xE0); + text += 3; + } + else if (code < 0x110000) // 4 byte sequence + { + text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xF0); + text += 4; + } + else // Invalid, only codes up to 0x10FFFF are allowed in Unicode + { + RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + + // Skip characters until predicate evaluates to true + template + static void skip(Ch *&text) + { + Ch *tmp = text; + while (StopPred::test(*tmp)) + ++tmp; + text = tmp; + } + + // Skip characters until predicate evaluates to true while doing the following: + // - replacing XML character entity references with proper characters (' & " < > &#...;) + // - condensing whitespace sequences to single space character + template + static Ch *skip_and_expand_character_refs(Ch *&text) + { + // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip + if (Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) + { + skip(text); + return text; + } + + // Use simple skip until first modification is detected + skip(text); + + // Use translation skip + Ch *src = text; + Ch *dest = src; + while (StopPred::test(*src)) + { + // If entity translation is enabled + if (!(Flags & parse_no_entity_translation)) + { + // Test if replacement is needed + if (src[0] == Ch('&')) + { + switch (src[1]) + { + + // & ' + case Ch('a'): + if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) + { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) + { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + break; + + // " + case Ch('q'): + if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) + { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + break; + + // > + case Ch('g'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + break; + + // < + case Ch('l'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + break; + + // &#...; - assumes ASCII + case Ch('#'): + if (src[2] == Ch('x')) + { + unsigned long code = 0; + src += 3; // Skip &#x + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 16 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + else + { + unsigned long code = 0; + src += 2; // Skip &# + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 10 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + if (*src == Ch(';')) + ++src; + else + RAPIDXML_PARSE_ERROR("expected ;", src); + continue; + + // Something else + default: + // Ignore, just copy '&' verbatim + break; + + } + } + } + + // If whitespace condensing is enabled + if (Flags & parse_normalize_whitespace) + { + // Test if condensing is needed + if (whitespace_pred::test(*src)) + { + *dest = Ch(' '); ++dest; // Put single space in dest + ++src; // Skip first whitespace char + // Skip remaining whitespace chars + while (whitespace_pred::test(*src)) + ++src; + continue; + } + } + + // No replacement, only copy character + *dest++ = *src++; + + } + + // Return new end + text = src; + return dest; + + } + + /////////////////////////////////////////////////////////////////////// + // Internal parsing functions + + // Parse BOM, if any + template + void parse_bom(Ch *&text) + { + // UTF-8? + if (static_cast(text[0]) == 0xEF && + static_cast(text[1]) == 0xBB && + static_cast(text[2]) == 0xBF) + { + text += 3; // Skup utf-8 bom + } + } + + // Parse XML declaration ( + xml_node *parse_xml_declaration(Ch *&text) + { + // If parsing of declaration is disabled + if (!(Flags & parse_declaration_node)) + { + // Skip until end of declaration + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + + // Create declaration + xml_node *declaration = this->allocate_node(node_declaration); + + // Skip whitespace before attributes or ?> + skip(text); + + // Parse declaration attributes + parse_node_attributes(text, declaration); + + // Skip ?> + if (text[0] != Ch('?') || text[1] != Ch('>')) + RAPIDXML_PARSE_ERROR("expected ?>", text); + text += 2; + + return declaration; + } + + // Parse XML comment (' + return 0; // Do not produce comment node + } + + // Remember value start + Ch *value = text; + + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create comment node + xml_node *comment = this->allocate_node(node_comment); + comment->value(value, text - value); + + // Place zero terminator after comment value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip '-->' + return comment; + } + + // Parse DOCTYPE + template + xml_node *parse_doctype(Ch *&text) + { + // Remember value start + Ch *value = text; + + // Skip to > + while (*text != Ch('>')) + { + // Determine character type + switch (*text) + { + + // If '[' encountered, scan for matching ending ']' using naive algorithm with depth + // This works for all W3C test files except for 2 most wicked + case Ch('['): + { + ++text; // Skip '[' + int depth = 1; + while (depth > 0) + { + switch (*text) + { + case Ch('['): ++depth; break; + case Ch(']'): --depth; break; + case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + ++text; + } + break; + } + + // Error on end of text + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Other character, skip it + default: + ++text; + + } + } + + // If DOCTYPE nodes enabled + if (Flags & parse_doctype_node) + { + // Create a new doctype node + xml_node *doctype = this->allocate_node(node_doctype); + doctype->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 1; // skip '>' + return doctype; + } + else + { + text += 1; // skip '>' + return 0; + } + + } + + // Parse PI + template + xml_node *parse_pi(Ch *&text) + { + // If creation of PI nodes is enabled + if (Flags & parse_pi_nodes) + { + // Create pi node + xml_node *pi = this->allocate_node(node_pi); + + // Extract PI target name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected PI target", text); + pi->name(name, text - name); + + // Skip whitespace between pi target and pi + skip(text); + + // Remember start of pi + Ch *value = text; + + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Set pi value (verbatim, no entity expansion or whitespace normalization) + pi->value(value, text - value); + + // Place zero terminator after name and value + if (!(Flags & parse_no_string_terminators)) + { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } + + text += 2; // Skip '?>' + return pi; + } + else + { + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + } + + // Parse and append data + // Return character that ends data. + // This is necessary because this character might have been overwritten by a terminating 0 + template + Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) + { + // Backup to contents start if whitespace trimming is disabled + if (!(Flags & parse_trim_whitespace)) + text = contents_start; + + // Skip until end of data + Ch *value = text, *end; + if (Flags & parse_normalize_whitespace) + end = skip_and_expand_character_refs(text); + else + end = skip_and_expand_character_refs(text); + + // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > + if (Flags & parse_trim_whitespace) + { + if (Flags & parse_normalize_whitespace) + { + // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end + if (*(end - 1) == Ch(' ')) + --end; + } + else + { + // Backup until non-whitespace character is found + while (whitespace_pred::test(*(end - 1))) + --end; + } + } + + // If characters are still left between end and value (this test is only necessary if normalization is enabled) + // Create new data node + if (!(Flags & parse_no_data_nodes)) + { + xml_node *data = this->allocate_node(node_data); + data->value(value, end - value); + node->append_node(data); + } + + // Add data to parent node if no data exists yet + if (!(Flags & parse_no_element_values)) + if (*node->value() == Ch('\0')) + node->value(value, end - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + Ch ch = *text; + *end = Ch('\0'); + return ch; // Return character that ends data; this is required because zero terminator overwritten it + } + + // Return character that ends data + return *text; + } + + // Parse CDATA + template + xml_node *parse_cdata(Ch *&text) + { + // If CDATA is disabled + if (Flags & parse_no_data_nodes) + { + // Skip until end of cdata + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; // Skip ]]> + return 0; // Do not produce CDATA node + } + + // Skip until end of cdata + Ch *value = text; + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create new cdata node + xml_node *cdata = this->allocate_node(node_cdata); + cdata->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip ]]> + return cdata; + } + + // Parse element node + template + xml_node *parse_element(Ch *&text) + { + // Create element node + xml_node *element = this->allocate_node(node_element); + + // Extract element name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected element name", text); + element->name(name, text - name); + + // Skip whitespace between element name and attributes or > + skip(text); + + // Parse attributes, if any + parse_node_attributes(text, element); + + // Determine ending type + if (*text == Ch('>')) + { + ++text; + parse_node_contents(text, element); + } + else if (*text == Ch('/')) + { + ++text; + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + } + else + RAPIDXML_PARSE_ERROR("expected >", text); + + // Place zero terminator after name + if (!(Flags & parse_no_string_terminators)) + element->name()[element->name_size()] = Ch('\0'); + + // Return parsed element + return element; + } + + // Determine node type, and parse it + template + xml_node *parse_node(Ch *&text) + { + // Parse proper node type + switch (text[0]) + { + + // <... + default: + // Parse and append element node + return parse_element(text); + + // (text); + } + else + { + // Parse PI + return parse_pi(text); + } + + // (text); + } + break; + + // (text); + } + break; + + // (text); + } + + } // switch + + // Attempt to skip other, unrecognized node types starting with ')) + { + if (*text == 0) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + ++text; // Skip '>' + return 0; // No node recognized + + } + } + + // Parse contents of the node - children, data etc. + template + void parse_node_contents(Ch *&text, xml_node *node) + { + // For all children and text + while (1) + { + // Skip whitespace between > and node contents + Ch *contents_start = text; // Store start of node contents before whitespace is skipped + skip(text); + Ch next_char = *text; + + // After data nodes, instead of continuing the loop, control jumps here. + // This is because zero termination inside parse_and_append_data() function + // would wreak havoc with the above code. + // Also, skipping whitespace after data nodes is unnecessary. + after_data_node: + + // Determine what comes next: node closing, child node, data node, or 0? + switch (next_char) + { + + // Node closing or child node + case Ch('<'): + if (text[1] == Ch('/')) + { + // Node closing + text += 2; // Skip '(text); + if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) + RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } + else + { + // No validation, just skip name + skip(text); + } + // Skip remaining whitespace after node name + skip(text); + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; // Skip '>' + return; // Node closed, finished parsing contents + } + else + { + // Child node + ++text; // Skip '<' + if (xml_node *child = parse_node(text)) + node->append_node(child); + } + break; + + // End of data - error + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Data node + default: + next_char = parse_and_append_data(node, text, contents_start); + goto after_data_node; // Bypass regular processing after data nodes + + } + } + } + + // Parse XML attributes of the node + template + void parse_node_attributes(Ch *&text, xml_node *node) + { + // For all attributes + while (attribute_name_pred::test(*text)) + { + // Extract attribute name + Ch *name = text; + ++text; // Skip first character of attribute name + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected attribute name", name); + + // Create new attribute + xml_attribute *attribute = this->allocate_attribute(); + attribute->name(name, text - name); + node->append_attribute(attribute); + + // Skip whitespace after attribute name + skip(text); + + // Skip = + if (*text != Ch('=')) + RAPIDXML_PARSE_ERROR("expected =", text); + ++text; + + // Add terminating zero after name + if (!(Flags & parse_no_string_terminators)) + attribute->name()[attribute->name_size()] = 0; + + // Skip whitespace after = + skip(text); + + // Skip quote and remember if it was ' or " + Ch quote = *text; + if (quote != Ch('\'') && quote != Ch('"')) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; + + // Extract attribute value and expand char refs in it + Ch *value = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes + if (quote == Ch('\'')) + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + else + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + + // Set attribute value + attribute->value(value, end - value); + + // Make sure that end quote is present + if (*text != quote) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; // Skip quote + + // Add terminating zero after value + if (!(Flags & parse_no_string_terminators)) + attribute->value()[attribute->value_size()] = 0; + + // Skip whitespace after attribute value + skip(text); + } + } + + }; + + //! \cond internal + namespace internal + { + + // Whitespace (space \n \r \t) + template + const unsigned char lookup_tables::lookup_whitespace[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + + // Node name (anything but space \n \r \t / > ? \0) + template + const unsigned char lookup_tables::lookup_node_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) (anything but < \0) + template + const unsigned char lookup_tables::lookup_text[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled + // (anything but < \0 &) + template + const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled + // (anything but < \0 & space \n \r \t) + template + const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute name (anything but space \n \r \t / < > = ? ! \0) + template + const unsigned char lookup_tables::lookup_attribute_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote (anything but ' \0) + template + const unsigned char lookup_tables::lookup_attribute_data_1[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote that does not require processing (anything but ' \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote (anything but " \0) + template + const unsigned char lookup_tables::lookup_attribute_data_2[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote that does not require processing (anything but " \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Digits (dec and hex, 255 denotes end of numeric character reference) + template + const unsigned char lookup_tables::lookup_digits[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F + }; + + // Upper case conversion + template + const unsigned char lookup_tables::lookup_upcase[256] = + { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F + }; + } + //! \endcond + +} + +// Undefine internal macros +#undef RAPIDXML_PARSE_ERROR + +// On MSVC, restore warnings state +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif diff --git a/ext/rapidxml/rapidxml_iterators.hpp b/ext/rapidxml/rapidxml_iterators.hpp new file mode 100644 index 0000000000..52ebc298aa --- /dev/null +++ b/ext/rapidxml/rapidxml_iterators.hpp @@ -0,0 +1,174 @@ +#ifndef RAPIDXML_ITERATORS_HPP_INCLUDED +#define RAPIDXML_ITERATORS_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_iterators.hpp This file contains rapidxml iterators + +#include "rapidxml.hpp" + +namespace rapidxml +{ + + //! Iterator of child nodes of xml_node + template + class node_iterator + { + + public: + + typedef typename xml_node value_type; + typedef typename xml_node &reference; + typedef typename xml_node *pointer; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + node_iterator() + : m_node(0) + { + } + + node_iterator(xml_node *node) + : m_node(node->first_node()) + { + } + + reference operator *() const + { + assert(m_node); + return *m_node; + } + + pointer operator->() const + { + assert(m_node); + return m_node; + } + + node_iterator& operator++() + { + assert(m_node); + m_node = m_node->next_sibling(); + return *this; + } + + node_iterator operator++(int) + { + node_iterator tmp = *this; + ++this; + return tmp; + } + + node_iterator& operator--() + { + assert(m_node && m_node->previous_sibling()); + m_node = m_node->previous_sibling(); + return *this; + } + + node_iterator operator--(int) + { + node_iterator tmp = *this; + ++this; + return tmp; + } + + bool operator ==(const node_iterator &rhs) + { + return m_node == rhs.m_node; + } + + bool operator !=(const node_iterator &rhs) + { + return m_node != rhs.m_node; + } + + private: + + xml_node *m_node; + + }; + + //! Iterator of child attributes of xml_node + template + class attribute_iterator + { + + public: + + typedef typename xml_attribute value_type; + typedef typename xml_attribute &reference; + typedef typename xml_attribute *pointer; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + attribute_iterator() + : m_attribute(0) + { + } + + attribute_iterator(xml_node *node) + : m_attribute(node->first_attribute()) + { + } + + reference operator *() const + { + assert(m_attribute); + return *m_attribute; + } + + pointer operator->() const + { + assert(m_attribute); + return m_attribute; + } + + attribute_iterator& operator++() + { + assert(m_attribute); + m_attribute = m_attribute->next_attribute(); + return *this; + } + + attribute_iterator operator++(int) + { + attribute_iterator tmp = *this; + ++this; + return tmp; + } + + attribute_iterator& operator--() + { + assert(m_attribute && m_attribute->previous_attribute()); + m_attribute = m_attribute->previous_attribute(); + return *this; + } + + attribute_iterator operator--(int) + { + attribute_iterator tmp = *this; + ++this; + return tmp; + } + + bool operator ==(const attribute_iterator &rhs) + { + return m_attribute == rhs.m_attribute; + } + + bool operator !=(const attribute_iterator &rhs) + { + return m_attribute != rhs.m_attribute; + } + + private: + + xml_attribute *m_attribute; + + }; + +} + +#endif diff --git a/ext/rapidxml/rapidxml_print.hpp b/ext/rapidxml/rapidxml_print.hpp new file mode 100644 index 0000000000..0ae2b14faa --- /dev/null +++ b/ext/rapidxml/rapidxml_print.hpp @@ -0,0 +1,421 @@ +#ifndef RAPIDXML_PRINT_HPP_INCLUDED +#define RAPIDXML_PRINT_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_print.hpp This file contains rapidxml printer implementation + +#include "rapidxml.hpp" + +// Only include streams if not disabled +#ifndef RAPIDXML_NO_STREAMS + #include + #include +#endif + +namespace rapidxml +{ + + /////////////////////////////////////////////////////////////////////// + // Printing flags + + const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. + + /////////////////////////////////////////////////////////////////////// + // Internal + + //! \cond internal + namespace internal + { + + /////////////////////////////////////////////////////////////////////////// + // Internal character operations + + // Copy characters from given range to given output iterator + template + inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) + { + while (begin != end) + *out++ = *begin++; + return out; + } + + // Copy characters from given range to given output iterator and expand + // characters into references (< > ' " &) + template + inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) + { + while (begin != end) + { + if (*begin == noexpand) + { + *out++ = *begin; // No expansion, copy character + } + else + { + switch (*begin) + { + case Ch('<'): + *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('>'): + *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('\''): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); + break; + case Ch('"'): + *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('&'): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); + break; + default: + *out++ = *begin; // No expansion, copy character + } + } + ++begin; // Step to next character + } + return out; + } + + // Fill given output iterator with repetitions of the same character + template + inline OutIt fill_chars(OutIt out, int n, Ch ch) + { + for (int i = 0; i < n; ++i) + *out++ = ch; + return out; + } + + // Find character + template + inline bool find_char(const Ch *begin, const Ch *end) + { + while (begin != end) + if (*begin++ == ch) + return true; + return false; + } + + /////////////////////////////////////////////////////////////////////////// + // Internal printing operations + + // Print node + template + inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print proper node type + switch (node->type()) + { + + // Document + case node_document: + out = print_children(out, node, flags, indent); + break; + + // Element + case node_element: + out = print_element_node(out, node, flags, indent); + break; + + // Data + case node_data: + out = print_data_node(out, node, flags, indent); + break; + + // CDATA + case node_cdata: + out = print_cdata_node(out, node, flags, indent); + break; + + // Declaration + case node_declaration: + out = print_declaration_node(out, node, flags, indent); + break; + + // Comment + case node_comment: + out = print_comment_node(out, node, flags, indent); + break; + + // Doctype + case node_doctype: + out = print_doctype_node(out, node, flags, indent); + break; + + // Pi + case node_pi: + out = print_pi_node(out, node, flags, indent); + break; + + // Unknown + default: + assert(0); + break; + } + + // If indenting not disabled, add line break after node + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + + // Return modified iterator + return out; + } + + // Print children of the node + template + inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent) + { + for (xml_node *child = node->first_node(); child; child = child->next_sibling()) + out = print_node(out, child, flags, indent); + return out; + } + + // Print attributes of the node + template + inline OutIt print_attributes(OutIt out, const xml_node *node, int flags) + { + for (xml_attribute *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + if (attribute->name() && attribute->value()) + { + // Print attribute name + *out = Ch(' '), ++out; + out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); + *out = Ch('='), ++out; + // Print attribute value using appropriate quote type + if (find_char(attribute->value(), attribute->value() + attribute->value_size())) + { + *out = Ch('\''), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); + *out = Ch('\''), ++out; + } + else + { + *out = Ch('"'), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); + *out = Ch('"'), ++out; + } + } + } + return out; + } + + // Print data node + template + inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_data); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + return out; + } + + // Print data node + template + inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_cdata); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'); ++out; + *out = Ch('!'); ++out; + *out = Ch('['); ++out; + *out = Ch('C'); ++out; + *out = Ch('D'); ++out; + *out = Ch('A'); ++out; + *out = Ch('T'); ++out; + *out = Ch('A'); ++out; + *out = Ch('['); ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch(']'); ++out; + *out = Ch(']'); ++out; + *out = Ch('>'); ++out; + return out; + } + + // Print element node + template + inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_element); + + // Print element name and attributes, if any + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + out = print_attributes(out, node, flags); + + // If node is childless + if (node->value_size() == 0 && !node->first_node()) + { + // Print childless node tag ending + *out = Ch('/'), ++out; + *out = Ch('>'), ++out; + } + else + { + // Print normal node tag ending + *out = Ch('>'), ++out; + + // Test if node contains a single data node only (and no other nodes) + xml_node *child = node->first_node(); + if (!child) + { + // If node has no children, only print its value without indenting + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + } + else if (child->next_sibling() == 0 && child->type() == node_data) + { + // If node has a sole data child, only print its value without indenting + out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); + } + else + { + // Print all children with full indenting + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + out = print_children(out, node, flags, indent + 1); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + } + + // Print node end + *out = Ch('<'), ++out; + *out = Ch('/'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch('>'), ++out; + } + return out; + } + + // Print declaration node + template + inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print declaration start + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + *out = Ch('x'), ++out; + *out = Ch('m'), ++out; + *out = Ch('l'), ++out; + + // Print attributes + out = print_attributes(out, node, flags); + + // Print declaration end + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + + return out; + } + + // Print comment node + template + inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_comment); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + *out = Ch('>'), ++out; + return out; + } + + // Print doctype node + template + inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_doctype); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('D'), ++out; + *out = Ch('O'), ++out; + *out = Ch('C'), ++out; + *out = Ch('T'), ++out; + *out = Ch('Y'), ++out; + *out = Ch('P'), ++out; + *out = Ch('E'), ++out; + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('>'), ++out; + return out; + } + + // Print pi node + template + inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_pi); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + return out; + } + + } + //! \endcond + + /////////////////////////////////////////////////////////////////////////// + // Printing + + //! Prints XML to given output iterator. + //! \param out Output iterator to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output iterator pointing to position immediately after last character of printed text. + template + inline OutIt print(OutIt out, const xml_node &node, int flags = 0) + { + return internal::print_node(out, &node, flags, 0); + } + +#ifndef RAPIDXML_NO_STREAMS + + //! Prints XML to given output stream. + //! \param out Output stream to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output stream. + template + inline std::basic_ostream &print(std::basic_ostream &out, const xml_node &node, int flags = 0) + { + print(std::ostream_iterator(out), node, flags); + return out; + } + + //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. + //! \param out Output stream to print to. + //! \param node Node to be printed. + //! \return Output stream. + template + inline std::basic_ostream &operator <<(std::basic_ostream &out, const xml_node &node) + { + return print(out, node); + } + +#endif + +} + +#endif diff --git a/ext/rapidxml/rapidxml_utils.hpp b/ext/rapidxml/rapidxml_utils.hpp new file mode 100644 index 0000000000..37c29535f4 --- /dev/null +++ b/ext/rapidxml/rapidxml_utils.hpp @@ -0,0 +1,122 @@ +#ifndef RAPIDXML_UTILS_HPP_INCLUDED +#define RAPIDXML_UTILS_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_utils.hpp This file contains high-level rapidxml utilities that can be useful +//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective. + +#include "rapidxml.hpp" +#include +#include +#include +#include + +namespace rapidxml +{ + + //! Represents data loaded from a file + template + class file + { + + public: + + //! Loads file into the memory. Data will be automatically destroyed by the destructor. + //! \param filename Filename to load. + file(const char *filename) + { + using namespace std; + + // Open stream + basic_ifstream stream(filename, ios::binary); + if (!stream) + throw runtime_error(string("cannot open file ") + filename); + stream.unsetf(ios::skipws); + + // Determine stream size + stream.seekg(0, ios::end); + size_t size = stream.tellg(); + stream.seekg(0); + + // Load data and add terminating 0 + m_data.resize(size + 1); + stream.read(&m_data.front(), static_cast(size)); + m_data[size] = 0; + } + + //! Loads file into the memory. Data will be automatically destroyed by the destructor + //! \param stream Stream to load from + file(std::basic_istream &stream) + { + using namespace std; + + // Load data and add terminating 0 + stream.unsetf(ios::skipws); + m_data.assign(istreambuf_iterator(stream), istreambuf_iterator()); + if (stream.fail() || stream.bad()) + throw runtime_error("error reading stream"); + m_data.push_back(0); + } + + //! Gets file data. + //! \return Pointer to data of file. + Ch *data() + { + return &m_data.front(); + } + + //! Gets file data. + //! \return Pointer to data of file. + const Ch *data() const + { + return &m_data.front(); + } + + //! Gets file data size. + //! \return Size of file data, in characters. + std::size_t size() const + { + return m_data.size(); + } + + private: + + std::vector m_data; // File data + + }; + + //! Counts children of node. Time complexity is O(n). + //! \return Number of children of node + template + inline std::size_t count_children(xml_node *node) + { + xml_node *child = node->first_node(); + std::size_t count = 0; + while (child) + { + ++count; + child = child->next_sibling(); + } + return count; + } + + //! Counts attributes of node. Time complexity is O(n). + //! \return Number of attributes of node + template + inline std::size_t count_attributes(xml_node *node) + { + xml_attribute *attr = node->first_attribute(); + std::size_t count = 0; + while (attr) + { + ++count; + attr = attr->next_attribute(); + } + return count; + } + +} + +#endif diff --git a/ext/sha1/CMakeLists.txt b/ext/sha1/CMakeLists.txt new file mode 100644 index 0000000000..b1ac75827e --- /dev/null +++ b/ext/sha1/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.6) + +#if(UNIX) +add_definitions(-PIC) +add_definitions(-g) +add_definitions(-O2) +add_definitions(-Wall) +#endif(UNIX) + +add_library(sha1 sha1.cpp) diff --git a/ext/sha1/SHA1.cpp b/ext/sha1/SHA1.cpp new file mode 100644 index 0000000000..f9c5cdf84c --- /dev/null +++ b/ext/sha1/SHA1.cpp @@ -0,0 +1,259 @@ +/* + 100% free public domain implementation of the SHA-1 algorithm + by Dominik Reichl + Web: http://www.dominik-reichl.de/ + + See header file for version history and test vectors. +*/ + +// If compiling with MFC, you might want to add #include "StdAfx.h" + +#define _CRT_SECURE_NO_WARNINGS +#include "SHA1.h" + +#ifdef SHA1_UTILITY_FUNCTIONS +#define SHA1_MAX_FILE_BUFFER (32 * 20 * 820) +#endif + +// Rotate _val32 by _nBits bits to the left +#ifndef ROL32 +#ifdef _MSC_VER +#define ROL32(_val32,_nBits) _rotl(_val32,_nBits) +#else +#define ROL32(_val32,_nBits) (((_val32)<<(_nBits))|((_val32)>>(32-(_nBits)))) +#endif +#endif + +#ifdef SHA1_LITTLE_ENDIAN +#define SHABLK0(i) (m_block->l[i] = \ + (ROL32(m_block->l[i],24) & 0xFF00FF00) | (ROL32(m_block->l[i],8) & 0x00FF00FF)) +#else +#define SHABLK0(i) (m_block->l[i]) +#endif + +#define SHABLK(i) (m_block->l[i&15] = ROL32(m_block->l[(i+13)&15] ^ \ + m_block->l[(i+8)&15] ^ m_block->l[(i+2)&15] ^ m_block->l[i&15],1)) + +// SHA-1 rounds +#define _R0(v,w,x,y,z,i) {z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5);w=ROL32(w,30);} +#define _R1(v,w,x,y,z,i) {z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5);w=ROL32(w,30);} +#define _R2(v,w,x,y,z,i) {z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5);w=ROL32(w,30);} +#define _R3(v,w,x,y,z,i) {z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5);w=ROL32(w,30);} +#define _R4(v,w,x,y,z,i) {z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5);w=ROL32(w,30);} + +#ifdef _MSC_VER +#pragma warning(push) +// Disable compiler warning 'Conditional expression is constant' +#pragma warning(disable: 4127) +#endif + +CSHA1::CSHA1() +{ + m_block = (SHA1_WORKSPACE_BLOCK*)m_workspace; + + Reset(); +} + +CSHA1::~CSHA1() +{ + Reset(); +} + +void CSHA1::Reset() +{ + // SHA1 initialization constants + m_state[0] = 0x67452301; + m_state[1] = 0xEFCDAB89; + m_state[2] = 0x98BADCFE; + m_state[3] = 0x10325476; + m_state[4] = 0xC3D2E1F0; + + m_count[0] = 0; + m_count[1] = 0; +} + +void CSHA1::Transform(UINT_32* pState, const UINT_8* pBuffer) +{ + UINT_32 a = pState[0], b = pState[1], c = pState[2], d = pState[3], e = pState[4]; + + memcpy(m_block, pBuffer, 64); + + // 4 rounds of 20 operations each, loop unrolled + _R0(a,b,c,d,e, 0); _R0(e,a,b,c,d, 1); _R0(d,e,a,b,c, 2); _R0(c,d,e,a,b, 3); + _R0(b,c,d,e,a, 4); _R0(a,b,c,d,e, 5); _R0(e,a,b,c,d, 6); _R0(d,e,a,b,c, 7); + _R0(c,d,e,a,b, 8); _R0(b,c,d,e,a, 9); _R0(a,b,c,d,e,10); _R0(e,a,b,c,d,11); + _R0(d,e,a,b,c,12); _R0(c,d,e,a,b,13); _R0(b,c,d,e,a,14); _R0(a,b,c,d,e,15); + _R1(e,a,b,c,d,16); _R1(d,e,a,b,c,17); _R1(c,d,e,a,b,18); _R1(b,c,d,e,a,19); + _R2(a,b,c,d,e,20); _R2(e,a,b,c,d,21); _R2(d,e,a,b,c,22); _R2(c,d,e,a,b,23); + _R2(b,c,d,e,a,24); _R2(a,b,c,d,e,25); _R2(e,a,b,c,d,26); _R2(d,e,a,b,c,27); + _R2(c,d,e,a,b,28); _R2(b,c,d,e,a,29); _R2(a,b,c,d,e,30); _R2(e,a,b,c,d,31); + _R2(d,e,a,b,c,32); _R2(c,d,e,a,b,33); _R2(b,c,d,e,a,34); _R2(a,b,c,d,e,35); + _R2(e,a,b,c,d,36); _R2(d,e,a,b,c,37); _R2(c,d,e,a,b,38); _R2(b,c,d,e,a,39); + _R3(a,b,c,d,e,40); _R3(e,a,b,c,d,41); _R3(d,e,a,b,c,42); _R3(c,d,e,a,b,43); + _R3(b,c,d,e,a,44); _R3(a,b,c,d,e,45); _R3(e,a,b,c,d,46); _R3(d,e,a,b,c,47); + _R3(c,d,e,a,b,48); _R3(b,c,d,e,a,49); _R3(a,b,c,d,e,50); _R3(e,a,b,c,d,51); + _R3(d,e,a,b,c,52); _R3(c,d,e,a,b,53); _R3(b,c,d,e,a,54); _R3(a,b,c,d,e,55); + _R3(e,a,b,c,d,56); _R3(d,e,a,b,c,57); _R3(c,d,e,a,b,58); _R3(b,c,d,e,a,59); + _R4(a,b,c,d,e,60); _R4(e,a,b,c,d,61); _R4(d,e,a,b,c,62); _R4(c,d,e,a,b,63); + _R4(b,c,d,e,a,64); _R4(a,b,c,d,e,65); _R4(e,a,b,c,d,66); _R4(d,e,a,b,c,67); + _R4(c,d,e,a,b,68); _R4(b,c,d,e,a,69); _R4(a,b,c,d,e,70); _R4(e,a,b,c,d,71); + _R4(d,e,a,b,c,72); _R4(c,d,e,a,b,73); _R4(b,c,d,e,a,74); _R4(a,b,c,d,e,75); + _R4(e,a,b,c,d,76); _R4(d,e,a,b,c,77); _R4(c,d,e,a,b,78); _R4(b,c,d,e,a,79); + + // Add the working vars back into state + pState[0] += a; + pState[1] += b; + pState[2] += c; + pState[3] += d; + pState[4] += e; + + // Wipe variables +#ifdef SHA1_WIPE_VARIABLES + a = b = c = d = e = 0; +#endif +} + +void CSHA1::Update(const UINT_8* pbData, UINT_32 uLen) +{ + UINT_32 j = ((m_count[0] >> 3) & 0x3F); + + if((m_count[0] += (uLen << 3)) < (uLen << 3)) + ++m_count[1]; // Overflow + + m_count[1] += (uLen >> 29); + + UINT_32 i; + if((j + uLen) > 63) + { + i = 64 - j; + memcpy(&m_buffer[j], pbData, i); + Transform(m_state, m_buffer); + + for( ; (i + 63) < uLen; i += 64) + Transform(m_state, &pbData[i]); + + j = 0; + } + else i = 0; + + if((uLen - i) != 0) + memcpy(&m_buffer[j], &pbData[i], uLen - i); +} + +#ifdef SHA1_UTILITY_FUNCTIONS +bool CSHA1::HashFile(const TCHAR* tszFileName) +{ + if(tszFileName == NULL) return false; + + FILE* fpIn = _tfopen(tszFileName, _T("rb")); + if(fpIn == NULL) return false; + + UINT_8* pbData = new UINT_8[SHA1_MAX_FILE_BUFFER]; + if(pbData == NULL) { fclose(fpIn); return false; } + + bool bSuccess = true; + while(true) + { + const size_t uRead = fread(pbData, 1, SHA1_MAX_FILE_BUFFER, fpIn); + + if(uRead > 0) + Update(pbData, static_cast(uRead)); + + if(uRead < SHA1_MAX_FILE_BUFFER) + { + if(feof(fpIn) == 0) bSuccess = false; + break; + } + } + + fclose(fpIn); + delete[] pbData; + return bSuccess; +} +#endif + +void CSHA1::Final() +{ + UINT_32 i; + + UINT_8 finalcount[8]; + for(i = 0; i < 8; ++i) + finalcount[i] = (UINT_8)((m_count[((i >= 4) ? 0 : 1)] + >> ((3 - (i & 3)) * 8) ) & 255); // Endian independent + + Update((UINT_8*)"\200", 1); + + while ((m_count[0] & 504) != 448) + Update((UINT_8*)"\0", 1); + + Update(finalcount, 8); // Cause a SHA1Transform() + + for(i = 0; i < 20; ++i) + m_digest[i] = (UINT_8)((m_state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF); + + // Wipe variables for security reasons +#ifdef SHA1_WIPE_VARIABLES + memset(m_buffer, 0, 64); + memset(m_state, 0, 20); + memset(m_count, 0, 8); + memset(finalcount, 0, 8); + Transform(m_state, m_buffer); +#endif +} + +#ifdef SHA1_UTILITY_FUNCTIONS +bool CSHA1::ReportHash(TCHAR* tszReport, REPORT_TYPE rtReportType) const +{ + if(tszReport == NULL) return false; + + TCHAR tszTemp[16]; + + if((rtReportType == REPORT_HEX) || (rtReportType == REPORT_HEX_SHORT)) + { + _sntprintf(tszTemp, 15, _T("%02X"), m_digest[0]); + _tcscpy(tszReport, tszTemp); + + const TCHAR* lpFmt = ((rtReportType == REPORT_HEX) ? _T(" %02X") : _T("%02X")); + for(size_t i = 1; i < 20; ++i) + { + _sntprintf(tszTemp, 15, lpFmt, m_digest[i]); + _tcscat(tszReport, tszTemp); + } + } + else if(rtReportType == REPORT_DIGIT) + { + _sntprintf(tszTemp, 15, _T("%u"), m_digest[0]); + _tcscpy(tszReport, tszTemp); + + for(size_t i = 1; i < 20; ++i) + { + _sntprintf(tszTemp, 15, _T(" %u"), m_digest[i]); + _tcscat(tszReport, tszTemp); + } + } + else return false; + + return true; +} +#endif + +#ifdef SHA1_STL_FUNCTIONS +bool CSHA1::ReportHashStl(std::basic_string& strOut, REPORT_TYPE rtReportType) const +{ + TCHAR tszOut[84]; + const bool bResult = ReportHash(tszOut, rtReportType); + if(bResult) strOut = tszOut; + return bResult; +} +#endif + +bool CSHA1::GetHash(UINT_8* pbDest20) const +{ + if(pbDest20 == NULL) return false; + memcpy(pbDest20, m_digest, 20); + return true; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/ext/sha1/sha1.h b/ext/sha1/sha1.h new file mode 100644 index 0000000000..77f39e6699 --- /dev/null +++ b/ext/sha1/sha1.h @@ -0,0 +1,263 @@ +/* + 100% free public domain implementation of the SHA-1 algorithm + by Dominik Reichl + Web: http://www.dominik-reichl.de/ + + Version 1.9 - 2011-11-10 + - Added Unicode test vectors. + - Improved support for hashing files using the HashFile method that + are larger than 4 GB. + - Improved file hashing performance (by using a larger buffer). + - Disabled unnecessary compiler warnings. + - Internal variables are now private. + + Version 1.8 - 2009-03-16 + - Converted project files to Visual Studio 2008 format. + - Added Unicode support for HashFile utility method. + - Added support for hashing files using the HashFile method that are + larger than 2 GB. + - HashFile now returns an error code instead of copying an error + message into the output buffer. + - GetHash now returns an error code and validates the input parameter. + - Added ReportHashStl STL utility method. + - Added REPORT_HEX_SHORT reporting mode. + - Improved Linux compatibility of test program. + + Version 1.7 - 2006-12-21 + - Fixed buffer underrun warning that appeared when compiling with + Borland C Builder (thanks to Rex Bloom and Tim Gallagher for the + patch). + - Breaking change: ReportHash writes the final hash to the start + of the buffer, i.e. it's not appending it to the string anymore. + - Made some function parameters const. + - Added Visual Studio 2005 project files to demo project. + + Version 1.6 - 2005-02-07 (thanks to Howard Kapustein for patches) + - You can set the endianness in your files, no need to modify the + header file of the CSHA1 class anymore. + - Aligned data support. + - Made support/compilation of the utility functions (ReportHash and + HashFile) optional (useful when bytes count, for example in embedded + environments). + + Version 1.5 - 2005-01-01 + - 64-bit compiler compatibility added. + - Made variable wiping optional (define SHA1_WIPE_VARIABLES). + - Removed unnecessary variable initializations. + - ROL32 improvement for the Microsoft compiler (using _rotl). + + Version 1.4 - 2004-07-22 + - CSHA1 now compiles fine with GCC 3.3 under MacOS X (thanks to Larry + Hastings). + + Version 1.3 - 2003-08-17 + - Fixed a small memory bug and made a buffer array a class member to + ensure correct working when using multiple CSHA1 class instances at + one time. + + Version 1.2 - 2002-11-16 + - Borlands C++ compiler seems to have problems with string addition + using sprintf. Fixed the bug which caused the digest report function + not to work properly. CSHA1 is now Borland compatible. + + Version 1.1 - 2002-10-11 + - Removed two unnecessary header file includes and changed BOOL to + bool. Fixed some minor bugs in the web page contents. + + Version 1.0 - 2002-06-20 + - First official release. + + ================ Test Vectors ================ + + SHA1("abc" in ANSI) = + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + SHA1("abc" in Unicode LE) = + 9F04F41A 84851416 2050E3D6 8C1A7ABB 441DC2B5 + + SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + in ANSI) = + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + SHA1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + in Unicode LE) = + 51D7D876 9AC72C40 9C5B0E3F 69C60ADC 9A039014 + + SHA1(A million repetitions of "a" in ANSI) = + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + SHA1(A million repetitions of "a" in Unicode LE) = + C4609560 A108A0C6 26AA7F2B 38A65566 739353C5 +*/ + +#ifndef ___SHA1_HDR___ +#define ___SHA1_HDR___ + +#if !defined(SHA1_UTILITY_FUNCTIONS) && !defined(SHA1_NO_UTILITY_FUNCTIONS) +#define SHA1_UTILITY_FUNCTIONS +#endif + +#if !defined(SHA1_STL_FUNCTIONS) && !defined(SHA1_NO_STL_FUNCTIONS) +#define SHA1_STL_FUNCTIONS +#if !defined(SHA1_UTILITY_FUNCTIONS) +#error STL functions require SHA1_UTILITY_FUNCTIONS. +#endif +#endif + +#include + +#ifdef SHA1_UTILITY_FUNCTIONS +#include +#include +#endif + +#ifdef SHA1_STL_FUNCTIONS +#include +#endif + +#ifdef _MSC_VER +#include +#endif + +// You can define the endian mode in your files without modifying the SHA-1 +// source files. Just #define SHA1_LITTLE_ENDIAN or #define SHA1_BIG_ENDIAN +// in your files, before including the SHA1.h header file. If you don't +// define anything, the class defaults to little endian. +#if !defined(SHA1_LITTLE_ENDIAN) && !defined(SHA1_BIG_ENDIAN) +#define SHA1_LITTLE_ENDIAN +#endif + +// If you want variable wiping, #define SHA1_WIPE_VARIABLES, if not, +// #define SHA1_NO_WIPE_VARIABLES. If you don't define anything, it +// defaults to wiping. +#if !defined(SHA1_WIPE_VARIABLES) && !defined(SHA1_NO_WIPE_VARIABLES) +#define SHA1_WIPE_VARIABLES +#endif + +#if defined(SHA1_HAS_TCHAR) +#include +#else +#ifdef _MSC_VER +#include +#else +#ifndef TCHAR +#define TCHAR char +#endif +#ifndef _T +#define _T(__x) (__x) +#define _tmain main +#define _tprintf printf +#define _getts gets +#define _tcslen strlen +#define _tfopen fopen +#define _tcscpy strcpy +#define _tcscat strcat +#define _sntprintf snprintf +#endif +#endif +#endif + +/////////////////////////////////////////////////////////////////////////// +// Define variable types + +#ifndef UINT_8 +#ifdef _MSC_VER // Compiling with Microsoft compiler +#define UINT_8 unsigned __int8 +#else // !_MSC_VER +#define UINT_8 unsigned char +#endif // _MSC_VER +#endif + +#ifndef UINT_32 +#ifdef _MSC_VER // Compiling with Microsoft compiler +#define UINT_32 unsigned __int32 +#else // !_MSC_VER +#if (ULONG_MAX == 0xFFFFFFFF) +#define UINT_32 unsigned long +#else +#define UINT_32 unsigned int +#endif +#endif // _MSC_VER +#endif // UINT_32 + +#ifndef INT_64 +#ifdef _MSC_VER // Compiling with Microsoft compiler +#define INT_64 __int64 +#else // !_MSC_VER +#define INT_64 long long +#endif // _MSC_VER +#endif // INT_64 + +#ifndef UINT_64 +#ifdef _MSC_VER // Compiling with Microsoft compiler +#define UINT_64 unsigned __int64 +#else // !_MSC_VER +#define UINT_64 unsigned long long +#endif // _MSC_VER +#endif // UINT_64 + +/////////////////////////////////////////////////////////////////////////// +// Declare SHA-1 workspace + +typedef union +{ + UINT_8 c[64]; + UINT_32 l[16]; +} SHA1_WORKSPACE_BLOCK; + +class CSHA1 +{ +public: +#ifdef SHA1_UTILITY_FUNCTIONS + // Different formats for ReportHash + enum REPORT_TYPE + { + REPORT_HEX = 0, + REPORT_DIGIT = 1, + REPORT_HEX_SHORT = 2 + }; +#endif + + // Constructor and destructor + CSHA1(); + ~CSHA1(); + + void Reset(); + + // Hash in binary data and strings + void Update(const UINT_8* pbData, UINT_32 uLen); + +#ifdef SHA1_UTILITY_FUNCTIONS + // Hash in file contents + bool HashFile(const TCHAR* tszFileName); +#endif + + // Finalize hash, call before using ReportHash(Stl) + void Final(); + +#ifdef SHA1_UTILITY_FUNCTIONS + bool ReportHash(TCHAR* tszReport, REPORT_TYPE rtReportType = REPORT_HEX) const; +#endif + +#ifdef SHA1_STL_FUNCTIONS + bool ReportHashStl(std::basic_string& strOut, REPORT_TYPE rtReportType = + REPORT_HEX) const; +#endif + + // Get the raw message digest (20 bytes) + bool GetHash(UINT_8* pbDest20) const; + +private: + // Private SHA-1 transformation + void Transform(UINT_32* pState, const UINT_8* pBuffer); + + // Member variables + UINT_32 m_state[5]; + UINT_32 m_count[2]; + UINT_32 m_reserved0[1]; // Memory alignment padding + UINT_8 m_buffer[64]; + UINT_8 m_digest[20]; + UINT_32 m_reserved1[3]; // Memory alignment padding + + UINT_8 m_workspace[64]; + SHA1_WORKSPACE_BLOCK* m_block; // SHA1 pointer to the byte array above +}; + +#endif // ___SHA1_HDR___ diff --git a/ext/stb_vorbis/CMakeLists.txt b/ext/stb_vorbis/CMakeLists.txt new file mode 100644 index 0000000000..128e155afc --- /dev/null +++ b/ext/stb_vorbis/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.6) + +#if(UNIX) +add_definitions(-PIC) +add_definitions(-g) +add_definitions(-O2) +add_definitions(-Wall) +#endif(UNIX) + +add_library(stb_vorbis stb_vorbis.c) diff --git a/ext/stb_vorbis/stb_vorbis.c b/ext/stb_vorbis/stb_vorbis.c new file mode 100644 index 0000000000..a51aecc5b7 --- /dev/null +++ b/ext/stb_vorbis/stb_vorbis.c @@ -0,0 +1,5045 @@ +// Ogg Vorbis I audio decoder -- version 0.99996 +// +// Written in April 2007 by Sean Barrett, sponsored by RAD Game Tools. +// +// Placed in the public domain April 2007 by the author: no copyright is +// claimed, and you may use it for any purpose you like. +// +// No warranty for any purpose is expressed or implied by the author (nor +// by RAD Game Tools). Report bugs and send enhancements to the author. +// +// Get the latest version and other information at: +// http://nothings.org/stb_vorbis/ + + +#pragma warning (disable:4996) + +// Todo: +// +// - seeking (note you can seek yourself using the pushdata API) +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// All of these limitations may be removed in future versions. + +#include "stb_vorbis.h" + + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +#define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +// STB_VORBIS_CODEBOOK_SHORTS +// The vorbis file format encodes VQ codebook floats as ax+b where a and +// b are floating point per-codebook constants, and x is a 16-bit int. +// Normally, stb_vorbis decodes them to floats rather than leaving them +// as 16-bit ints and computing ax+b while decoding. This is a speed/space +// tradeoff; you can save space by defining this flag. +#ifndef STB_VORBIS_CODEBOOK_SHORTS +#define STB_VORBIS_CODEBOOK_FLOATS +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT +#include +#include +#include +#include +#if !(defined(__APPLE__) || defined(MACOSX) || defined(macintosh) || defined(Macintosh)) +#include +#endif +#else +#define NULL 0 +#endif + +#ifndef _MSC_VER + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#ifdef STB_VORBIS_CODEBOOK_FLOATS +typedef float codetype; +#else +typedef uint16 codetype; +#endif + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 after_previous_page_start; + uint32 first_decoded_sample; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + uint32 first_audio_page_offset; + + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +extern int my_prof(int slot); +//#define stb_prof my_prof + +#ifndef stb_prof +#define stb_prof(x) 0 +#endif + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#ifdef dealloca +#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size)) +#else +#define temp_free(f,p) 0 +#endif +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is not a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, size_t sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+3)&~3; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=i<<24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1U << 14)) + if (n < (1U << 4)) return 0 + log2_4[n ]; + else if (n < (1U << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1U << 24)) + if (n < (1U << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1U << 29)) return 25 + log2_4[n >> 25]; + else if (n < (1U << 31)) return 30 + log2_4[n >> 30]; + else return 0; // signed n returns 0 +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1 << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { assert(0); return FALSE; } + res = available[z]; + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propogate availability up the tree + if (z != len[i]) { + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +static int uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + assert(pow((float) r+1, dim) > entries); + assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,y; +} Point; + +int point_compare(const void *p, const void *q) +{ + Point *a = (Point *) p; + Point *b = (Point *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n,i; + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0 || loc1 != ~0) { + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + ProbedPage p; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + p.page_start = f->first_audio_page_offset; + p.page_end = p.page_start + len; + p.after_previous_page_start = p.page_start; + p.first_decoded_sample = 0; + p.last_decoded_sample = loc0; + f->p_first = p; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + if (f->valid_bits < 0) return 0; + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +static int32 get_bits_signed(vorb *f, int n) +{ + uint32 z = get_bits(f, n); + if (z & (1 << (n-1))) + z += ~((1 << n) - 1); + return (int32) z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5, +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + assert(c->sorted_codewords || c->codewords); + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#ifndef STB_VORBIS_CODEBOOK_FLOATS + #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off] * c->delta_value + c->minimum_value) + #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off] * c->delta_value) + #define CODEBOOK_ELEMENT_BASE(c) (c->minimum_value) +#else + #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) + #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) + #define CODEBOOK_ELEMENT_BASE(c) (0) +#endif + +static int codebook_decode_start(vorb *f, Codebook *c, int len) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c,len); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c,len); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK +static int codebook_decode_deinterleave_repeat_2(vorb *f, Codebook *c, float **outputs, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*2 + effective > len * 2) { + effective = len*2 - (p_inter*2 - c_inter); + } + + { + z *= c->dimensions; + stb_prof(11); + if (c->sequence_p) { + // haven't optimized this case because I don't have any examples + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + outputs[c_inter][p_inter] += val; + if (++c_inter == 2) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + i=0; + if (c_inter == 1) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + outputs[c_inter][p_inter] += val; + c_inter = 0; ++p_inter; + ++i; + } + { + float *z0 = outputs[0]; + float *z1 = outputs[1]; + for (; i+1 < effective;) { + z0[p_inter] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + z1[p_inter] += CODEBOOK_ELEMENT_FAST(c,z+i+1) + last; + ++p_inter; + i += 2; + } + } + if (i < effective) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + outputs[c_inter][p_inter] += val; + if (++c_inter == 2) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} +#endif + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + LINE_OP(output[x], inverse_db_table[y]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y]); + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + stb_prof(2); + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + int len = ch * n; + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + stb_prof(3); + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + stb_prof(13); + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + stb_prof(5); + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + stb_prof(20); // accounts for X time + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat_2(f, book, residue_buffers, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + stb_prof(7); + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + stb_prof(8); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch == 1) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = 0, p_inter = z; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + stb_prof(22); + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + stb_prof(3); + } else { + z += r->part_size; + c_inter = 0; + p_inter = z; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + stb_prof(22); + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + stb_prof(3); + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + stb_prof(9); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + stb_prof(0); + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#else +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + //acc += x[j] * cos(M_PI / n * (i + 0.5) * (j + 0.5)); + buffer[i] = acc; + } + free(x); +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int k_off = -8; + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propogates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + assert(0); + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + draw_line(target, lx,ly, hx,hy, n2); + lx = hx, ly = hy; + } + } + if (lx < n2) + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + } + return TRUE; +} + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + int window_center; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + window_center = n >> 1; + + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + stb_prof(1); + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + stb_prof(0); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r,t; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + t = f->residue_types[r]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + +// INVERSE COUPLING + stb_prof(14); + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + stb_prof(15); + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + stb_prof(16); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + stb_prof(0); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet - (n-right_end); + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + right_end) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static void vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left; + if (vorbis_decode_packet(f, &len, &left, &right)) + vorbis_finish_frame(f, len, left, right); +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f, int end_page) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (end_page) + if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (end_page) + if (s < n-1) return error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f, TRUE)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + //assert(total != 0); + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values) { ++c->sorted_values; c->sorted_values[-1] = -1; } + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + c->lookup_values = lookup1_values(c->entries, c->dimensions); + } else { + c->lookup_values = c->entries * c->dimensions; + } + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + int z = sparse ? c->sorted_values[j] : j, div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + c->multiplicands[j*c->dimensions + k] = + #ifndef STB_VORBIS_CODEBOOK_FLOATS + mults[off]; + #else + mults[off]*c->delta_value + c->minimum_value; + // in this case (and this case only) we could pre-expand c->sequence_p, + // and throw away the decode logic for it; have to ALSO do + // it in the case below, but it can only be done if + // STB_VORBIS_CODEBOOK_FLOATS + // !STB_VORBIS_DIVIDES_IN_CODEBOOK + #endif + div *= c->lookup_values; + } + } + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + c->lookup_type = 2; + } + else +#endif + { + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + #ifndef STB_VORBIS_CODEBOOK_FLOATS + memcpy(c->multiplicands, mults, sizeof(c->multiplicands[0]) * c->lookup_values); + #else + for (j=0; j < (int) c->lookup_values; ++j) + c->multiplicands[j] = mults[j] * c->delta_value + c->minimum_value; + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + #endif + } + skip:; + + #ifdef STB_VORBIS_CODEBOOK_FLOATS + if (c->lookup_type == 2 && c->sequence_p) { + for (j=1; j < (int) c->lookup_values; ++j) + c->multiplicands[j] = c->multiplicands[j-1]; + c->sequence_p = 0; + } + #endif + } + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + Point p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].y = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].y; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low,hi; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(*f->residue_config)); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (get_bits(f,1)) + m->submaps = get_bits(f,4); + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels)-1); + m->chan[k].angle = get_bits(f, ilog(f->channels)-1); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + f->first_decode = TRUE; + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + + if (p->codebooks) { + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + for (i=0; i < p->channels; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, data, data_len); + } + + f->stream = data; + f->stream_end = data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f, FALSE)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return f->stream - data; + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return f->stream - data; + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return f->stream - data; +} + +stb_vorbis *stb_vorbis_open_pushdata( + unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = data; + p.stream_end = data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = f->stream - data; + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return f->stream - f->stream_start; + #ifndef STB_VORBIS_NO_STDIO + return ftell(f->f) - f->f_start; + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + +// seek is implemented with 'interpolation search'--this is like +// binary search, but we use the data values to estimate the likely +// location of the data item (plus a bit of a bias so when the +// estimation is wrong we don't waste overly much time) + +#define SAMPLE_unknown 0xffffffff + + +// ogg vorbis, in its insane infinite wisdom, only provides +// information about the sample at the END of the page. +// therefore we COULD have the data we need in the current +// page, and not know it. we could just use the end location +// as our only knowledge for bounds, seek back, and eventually +// the binary search finds it. or we can try to be smart and +// not waste time trying to locate more pages. we try to be +// smart, since this data is already in memory anyway, so +// doing needless I/O would be crazy! +static int vorbis_analyze_page(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + uint8 packet_type[255]; + int num_packet, packet_start, previous =0; + int i,len; + uint32 samples; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + assert(header[0] == 'O' && header[1] == 'g' && header[2] == 'g' && header[3] == 'S'); + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 16); + + if (header[5] & 4) { + // if this is the last page, it's not possible to work + // backwards to figure out the first sample! whoops! fuck. + z->first_decoded_sample = SAMPLE_unknown; + set_file_offset(f, z->page_start); + return 1; + } + + // scan through the frames to determine the sample-count of each one... + // our goal is the sample # of the first fully-decoded sample on the + // page, which is the first decoded sample of the 2nd page + + num_packet=0; + + packet_start = ((header[5] & 1) == 0); + + for (i=0; i < header[26]; ++i) { + if (packet_start) { + uint8 n,b,m; + if (lacing[i] == 0) goto bail; // trying to read from zero-length packet + n = get8(f); + // if bottom bit is non-zero, we've got corruption + if (n & 1) goto bail; + n >>= 1; + b = ilog(f->mode_count-1); + m = n >> b; + n &= (1 << b)-1; + if (n >= f->mode_count) goto bail; + if (num_packet == 0 && f->mode_config[n].blockflag) + previous = (m & 1); + packet_type[num_packet++] = f->mode_config[n].blockflag; + skip(f, lacing[i]-1); + } else + skip(f, lacing[i]); + packet_start = (lacing[i] < 255); + } + + // now that we know the sizes of all the pages, we can start determining + // how much sample data there is. + + samples = 0; + + // for the last packet, we step by its whole length, because the definition + // is that we encoded the end sample loc of the 'last packet completed', + // where 'completed' refers to packets being split, and we are left to guess + // what 'end sample loc' means. we assume it means ignoring the fact that + // the last half of the data is useless without windowing against the next + // packet... (so it's not REALLY complete in that sense) + if (num_packet > 1) + samples += f->blocksize[packet_type[num_packet-1]]; + + for (i=num_packet-2; i >= 1; --i) { + // now, for this packet, how many samples do we have that + // do not overlap the following packet? + if (packet_type[i] == 1) + if (packet_type[i+1] == 1) + samples += f->blocksize_1 >> 1; + else + samples += ((f->blocksize_1 - f->blocksize_0) >> 2) + (f->blocksize_0 >> 1); + else + samples += f->blocksize_0 >> 1; + } + // now, at this point, we've rewound to the very beginning of the + // _second_ packet. if we entirely discard the first packet after + // a seek, this will be exactly the right sample number. HOWEVER! + // we can't as easily compute this number for the LAST page. The + // only way to get the sample offset of the LAST page is to use + // the end loc from the previous page. But what that returns us + // is _exactly_ the place where we get our first non-overlapped + // sample. (I think. Stupid spec for being ambiguous.) So for + // consistency it's better to do that here, too. However, that + // will then require us to NOT discard all of the first frame we + // decode, in some cases, which means an even weirder frame size + // and extra code. what a fucking pain. + + // we're going to discard the first packet if we + // start the seek here, so we don't care about it. (we could actually + // do better; if the first packet is long, and the previous packet + // is short, there's actually data in the first half of the first + // packet that doesn't need discarding... but not worth paying the + // effort of tracking that of that here and in the seeking logic) + // except crap, if we infer it from the _previous_ packet's end + // location, we DO need to use that definition... and we HAVE to + // infer the start loc of the LAST packet from the previous packet's + // end location. fuck you, ogg vorbis. + + z->first_decoded_sample = z->last_decoded_sample - samples; + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; + + // restore file state to where we were + bail: + set_file_offset(f, z->page_start); + return 0; +} + +static int vorbis_seek_frame_from_page(stb_vorbis *f, uint32 page_start, uint32 first_sample, uint32 target_sample, int fine) +{ + int left_start, left_end, right_start, right_end, mode,i; + int frame=0; + uint32 frame_start; + int frames_to_skip, data_to_skip; + + // first_sample is the sample # of the first sample that doesn't + // overlap the previous page... note that this requires us to + // _partially_ discard the first packet! bleh. + set_file_offset(f, page_start); + + f->next_seg = -1; // force page resync + + frame_start = first_sample; + // frame start is where the previous packet's last decoded sample + // was, which corresponds to left_end... EXCEPT if the previous + // packet was long and this packet is short? Probably a bug here. + + + // now, we can start decoding frames... we'll only FAKE decode them, + // until we find the frame that contains our sample; then we'll rewind, + // and try again + for (;;) { + int start; + + if (!vorbis_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + + if (frame == 0) + start = left_end; + else + start = left_start; + + // the window starts at left_start; the last valid sample we generate + // before the next frame's window start is right_start-1 + if (target_sample < frame_start + right_start-start) + break; + + flush_packet(f); + if (f->eof) + return error(f, VORBIS_seek_failed); + + frame_start += right_start - start; + + ++frame; + } + + // ok, at this point, the sample we want is contained in frame #'frame' + + // to decode frame #'frame' normally, we have to decode the + // previous frame first... but if it's the FIRST frame of the page + // we can't. if it's the first frame, it means it falls in the part + // of the first frame that doesn't overlap either of the other frames. + // so, if we have to handle that case for the first frame, we might + // as well handle it for all of them, so: + if (target_sample > frame_start + (left_end - left_start)) { + // so what we want to do is go ahead and just immediately decode + // this frame, but then make it so the next get_frame_float() uses + // this already-decoded data? or do we want to go ahead and rewind, + // and leave a flag saying to skip the first N data? let's do that + frames_to_skip = frame; // if this is frame #1, skip 1 frame (#0) + data_to_skip = left_end - left_start; + } else { + // otherwise, we want to skip frames 0, 1, 2, ... frame-2 + // (which means frame-2+1 total frames) then decode frame-1, + // then leave frame pending + frames_to_skip = frame - 1; + assert(frames_to_skip >= 0); + data_to_skip = -1; + } + + set_file_offset(f, page_start); + f->next_seg = - 1; // force page resync + + for (i=0; i < frames_to_skip; ++i) { + maybe_start_packet(f); + flush_packet(f); + } + + if (data_to_skip >= 0) { + int i,j,n = f->blocksize_0 >> 1; + f->discard_samples_deferred = data_to_skip; + for (i=0; i < f->channels; ++i) + for (j=0; j < n; ++j) + f->previous_window[i][j] = 0; + f->previous_length = n; + frame_start += data_to_skip; + } else { + f->previous_length = 0; + vorbis_pump_first_frame(f); + } + + // at this point, the NEXT decoded frame will generate the desired sample + if (fine) { + // so if we're doing sample accurate streaming, we want to go ahead and decode it! + if (target_sample != frame_start) { + int n; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(target_sample > frame_start); + assert(f->channel_buffer_start + (int) (target_sample-frame_start) < f->channel_buffer_end); + f->channel_buffer_start += (target_sample - frame_start); + } + } + + return 0; +} + +static int vorbis_seek_base(stb_vorbis *f, unsigned int sample_number, int fine) +{ + ProbedPage p[2],q; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // do we know the location of the last page? + if (f->p_last.page_start == 0) { + uint32 z = stb_vorbis_stream_length_in_samples(f); + if (z == 0) return error(f, VORBIS_cant_find_last_page); + } + + p[0] = f->p_first; + p[1] = f->p_last; + + if (sample_number >= f->p_last.last_decoded_sample) + sample_number = f->p_last.last_decoded_sample-1; + + if (sample_number < f->p_first.last_decoded_sample) { + vorbis_seek_frame_from_page(f, p[0].page_start, 0, sample_number, fine); + return 0; + } else { + int attempts=0; + while (p[0].page_end < p[1].page_start) { + uint32 probe; + uint32 start_offset, end_offset; + uint32 start_sample, end_sample; + + // copy these into local variables so we can tweak them + // if any are unknown + start_offset = p[0].page_end; + end_offset = p[1].after_previous_page_start; // an address known to seek to page p[1] + start_sample = p[0].last_decoded_sample; + end_sample = p[1].last_decoded_sample; + + // currently there is no such tweaking logic needed/possible? + if (start_sample == SAMPLE_unknown || end_sample == SAMPLE_unknown) + return error(f, VORBIS_seek_failed); + + // now we want to lerp between these for the target samples... + + // step 1: we need to bias towards the page start... + if (start_offset + 4000 < end_offset) + end_offset -= 4000; + + // now compute an interpolated search loc + probe = start_offset + (int) floor((float) (end_offset - start_offset) / (end_sample - start_sample) * (sample_number - start_sample)); + + // next we need to bias towards binary search... + // code is a little wonky to allow for full 32-bit unsigned values + if (attempts >= 4) { + uint32 probe2 = start_offset + ((end_offset - start_offset) >> 1); + if (attempts >= 8) + probe = probe2; + else if (probe < probe2) + probe = probe + ((probe2 - probe) >> 1); + else + probe = probe2 + ((probe - probe2) >> 1); + } + ++attempts; + + set_file_offset(f, probe); + if (!vorbis_find_page(f, NULL, NULL)) return error(f, VORBIS_seek_failed); + if (!vorbis_analyze_page(f, &q)) return error(f, VORBIS_seek_failed); + q.after_previous_page_start = probe; + + // it's possible we've just found the last page again + if (q.page_start == p[1].page_start) { + p[1] = q; + continue; + } + + if (sample_number < q.last_decoded_sample) + p[1] = q; + else + p[0] = q; + } + + if (p[0].last_decoded_sample <= sample_number && sample_number < p[1].last_decoded_sample) { + vorbis_seek_frame_from_page(f, p[1].page_start, p[0].last_decoded_sample, sample_number, fine); + return 0; + } + return error(f, VORBIS_seek_failed); + } +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + return vorbis_seek_base(f, sample_number, FALSE); +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + return vorbis_seek_base(f, sample_number, TRUE); +} + +void stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { error(f, VORBIS_invalid_api_mixing); return; } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, (int unsigned *)&last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, (int unsigned *)&last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + f->p_last.first_decoded_sample = SAMPLE_unknown; + f->p_last.after_previous_page_start = previous_safe; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = ftell(file); + fseek(file, 0, SEEK_END); + len = ftell(file) - start; + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(char *filename, int *error, stb_vorbis_alloc *alloc) +{ + FILE *f = fopen(filename, "rb"); + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(unsigned char *data, int len, int *error, stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + vorbis_init(&p, alloc); + p.stream = data; + p.stream_end = data + len; + p.stream_start = p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i], samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(char *filename, int *channels, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(uint8 *mem, int len, int *channels, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + return data_len; +} +#endif + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +#endif // STB_VORBIS_HEADER_ONLY diff --git a/ext/stb_vorbis/stb_vorbis.h b/ext/stb_vorbis/stb_vorbis.h new file mode 100644 index 0000000000..16ccc10036 --- /dev/null +++ b/ext/stb_vorbis/stb_vorbis.h @@ -0,0 +1,329 @@ +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + unsigned char *datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(char *filename, int *channels, short **output); +#endif +extern int stb_vorbis_decode_memory(unsigned char *mem, int len, int *channels, short **output); +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(unsigned char *data, int len, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(char *filename, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// NOT WORKING YET +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern void stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0), but it +// actually works + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of samples per channel. the +// data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. Note that for interleaved data, you pass in the number of +// shorts (the size of your array), but the return value is the number of +// samples per channel, not the total number of samples. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + diff --git a/ext/vjson/CMakeLists.txt b/ext/vjson/CMakeLists.txt new file mode 100644 index 0000000000..97ea0d4876 --- /dev/null +++ b/ext/vjson/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.6) + +#if(UNIX) +add_definitions(-PIC) +add_definitions(-g) +add_definitions(-O2) +add_definitions(-Wall) +#endif(UNIX) + +add_library(vjson json.cpp block_allocator.cpp) diff --git a/ext/vjson/block_allocator.cpp b/ext/vjson/block_allocator.cpp new file mode 100644 index 0000000000..83bd02110c --- /dev/null +++ b/ext/vjson/block_allocator.cpp @@ -0,0 +1,50 @@ +#include +#include +#include "block_allocator.h" + +block_allocator::block_allocator(size_t blocksize): m_head(0), m_blocksize(blocksize) +{ +} + +block_allocator::~block_allocator() +{ + while (m_head) + { + block *temp = m_head->next; + ::free(m_head); + m_head = temp; + } +} + +void block_allocator::swap(block_allocator &rhs) +{ + std::swap(m_blocksize, rhs.m_blocksize); + std::swap(m_head, rhs.m_head); +} + +void *block_allocator::malloc(size_t size) +{ + if ((m_head && m_head->used + size > m_head->size) || !m_head) + { + // calc needed size for allocation + size_t alloc_size = std::max(sizeof(block) + size, m_blocksize); + + // create new block + char *buffer = (char *)::malloc(alloc_size); + block *b = reinterpret_cast(buffer); + b->size = alloc_size; + b->used = sizeof(block); + b->buffer = buffer; + b->next = m_head; + m_head = b; + } + + void *ptr = m_head->buffer + m_head->used; + m_head->used += size; + return ptr; +} + +void block_allocator::free() +{ + block_allocator(0).swap(*this); +} diff --git a/ext/vjson/block_allocator.h b/ext/vjson/block_allocator.h new file mode 100644 index 0000000000..90a81814f6 --- /dev/null +++ b/ext/vjson/block_allocator.h @@ -0,0 +1,35 @@ +#ifndef BLOCK_ALLOCATOR_H +#define BLOCK_ALLOCATOR_H + +class block_allocator +{ +public: + block_allocator(size_t blocksize); + ~block_allocator(); + + // exchange contents with rhs + void swap(block_allocator &rhs); + + // allocate memory + void *malloc(size_t size); + + // free all allocated blocks + void free(); + +private: + struct block + { + size_t size; + size_t used; + char *buffer; + block *next; + }; + + block *m_head; + size_t m_blocksize; + + block_allocator(const block_allocator &); + block_allocator &operator=(block_allocator &); +}; + +#endif diff --git a/ext/vjson/json.cpp b/ext/vjson/json.cpp new file mode 100644 index 0000000000..cc6dfd5ed7 --- /dev/null +++ b/ext/vjson/json.cpp @@ -0,0 +1,620 @@ +#include +#include +#include +#include "json.h" +#include "file/easy_file.h" + +// true if character represent a digit +#define IS_DIGIT(c) (c >= '0' && c <= '9') + +int json_value::numChildren() const { + int count = 0; + const json_value *c = first_child; + while (c) { + count++; + c = c->next_sibling; + } + return count; +} + +// only works right for first child. includes itself in count. +int json_value::numSiblings() const { + const json_value *s = next_sibling; + int count = 1; + while (s) { + count++; + s = s->next_sibling; + } + return count; +} + +const json_value *json_value::get(const char *child_name) const { + const json_value *c = first_child; + while (c) { + if (!strcmp(c->name, child_name)) { + return c; + } + c = c->next_sibling; + } + return 0; +} + +const json_value *json_value::get(const char *child_name, json_type type) const { + const json_value *v = get(child_name); + if (v && type == v->type) + return v; + else + return 0; +} + +const char *json_value::getString(const char *child_name) const { + return get(child_name, JSON_STRING)->string_value; +} + +const char *json_value::getString(const char *child_name, const char *default_value) const { + const json_value *val = get(child_name, JSON_STRING); + if (!val) + return default_value; + return val->string_value; +} + +bool json_value::getStringVector(std::vector *vec) const { + vec->clear(); + if (type == JSON_ARRAY) { + json_value *val = first_child; + while (val) { + if (val->type == JSON_STRING) { + vec->push_back(val->string_value); + } + } + return true; + } else { + return false; + } +} + +float json_value::getFloat(const char *child_name) const { + return get(child_name, JSON_FLOAT)->float_value; +} + +float json_value::getFloat(const char *child_name, float default_value) const { + const json_value *val = get(child_name, JSON_FLOAT); + if (!val) + return default_value; + return val->float_value; +} + +int json_value::getInt(const char *child_name) const { + return get(child_name, JSON_INT)->int_value; +} + +int json_value::getInt(const char *child_name, int default_value) const { + const json_value *val = get(child_name, JSON_INT); + if (!val) + return default_value; + return val->int_value; +} + +bool json_value::getBool(const char *child_name) const { + return get(child_name, JSON_BOOL)->int_value != 0 ? true : false; +} + +bool json_value::getBool(const char *child_name, bool default_value) const { + const json_value *val = get(child_name, JSON_BOOL); + if (!val) + return default_value; + return val->int_value != 0 ? true : false; +} + + +// convert string to integer +char *atoi(char *first, char *last, int *out) +{ + int sign = 1; + if (first != last) + { + if (*first == '-') + { + sign = -1; + ++first; + } + else if (*first == '+') + { + ++first; + } + } + + int result = 0; + for (; first != last && IS_DIGIT(*first); ++first) + { + result = 10 * result + (*first - '0'); + } + *out = result * sign; + + return first; +} + +// convert hexadecimal string to unsigned integer +char *hatoui(char *first, char *last, unsigned int *out) +{ + unsigned int result = 0; + for (; first != last; ++first) + { + int digit; + if (IS_DIGIT(*first)) + { + digit = *first - '0'; + } + else if (*first >= 'a' && *first <= 'f') + { + digit = *first - 'a' + 10; + } + else if (*first >= 'A' && *first <= 'F') + { + digit = *first - 'A' + 10; + } + else + { + break; + } + result = 16 * result + digit; + } + *out = result; + + return first; +} + +// convert string to floating point +char *atof(char *first, char *last, float *out) +{ + // sign + float sign = 1; + if (first != last) + { + if (*first == '-') + { + sign = -1; + ++first; + } + else if (*first == '+') + { + ++first; + } + } + + // integer part + float result = 0; + for (; first != last && IS_DIGIT(*first); ++first) + { + result = 10 * result + (*first - '0'); + } + + // fraction part + if (first != last && *first == '.') + { + ++first; + + float inv_base = 0.1f; + for (; first != last && IS_DIGIT(*first); ++first) + { + result += (*first - '0') * inv_base; + inv_base *= 0.1f; + } + } + + // result w\o exponent + result *= sign; + + // exponent + bool exponent_negative = false; + int exponent = 0; + if (first != last && (*first == 'e' || *first == 'E')) + { + ++first; + + if (*first == '-') + { + exponent_negative = true; + ++first; + } + else if (*first == '+') + { + ++first; + } + + for (; first != last && IS_DIGIT(*first); ++first) + { + exponent = 10 * exponent + (*first - '0'); + } + } + + if (exponent) + { + float power_of_ten = 10; + for (; exponent > 1; exponent--) + { + power_of_ten *= 10; + } + + if (exponent_negative) + { + result /= power_of_ten; + } + else + { + result *= power_of_ten; + } + } + + *out = result; + + return first; +} + +json_value *json_alloc(block_allocator *allocator) +{ + json_value *value = (json_value *)allocator->malloc(sizeof(json_value)); + memset(value, 0, sizeof(json_value)); + return value; +} + +void json_append(json_value *lhs, json_value *rhs) +{ + rhs->parent = lhs; + if (lhs->last_child) + { + lhs->last_child = lhs->last_child->next_sibling = rhs; + } + else + { + lhs->first_child = lhs->last_child = rhs; + } +} + +#define ERROR(it, desc)\ + *error_pos = it;\ + *error_desc = (char *)desc;\ + *error_line = 1 - escaped_newlines;\ + for (char *c = it; c != source; --c)\ + if (*c == '\n') ++*error_line;\ + return 0 + +#define CHECK_TOP() if (!top) {ERROR(it, "Unexpected character");} + +json_value *json_parse(char *source, char **error_pos, char **error_desc, int *error_line, block_allocator *allocator) +{ + json_value *root = 0; + json_value *top = 0; + + char *name = 0; + char *it = source; + + int escaped_newlines = 0; + + while (*it) + { + switch (*it) + { + case '{': + case '[': + { + // create new value + json_value *object = json_alloc(allocator); + + // name + object->name = name; + name = 0; + + // type + object->type = (*it == '{') ? JSON_OBJECT : JSON_ARRAY; + + // skip open character + ++it; + + // set top and root + if (top) + { + json_append(top, object); + } + else if (!root) + { + root = object; + } + else + { + ERROR(it, "Second root. Only one root allowed"); + } + top = object; + } + break; + + case '}': + case ']': + { + if (!top || top->type != ((*it == '}') ? JSON_OBJECT : JSON_ARRAY)) + { + ERROR(it, "Mismatch closing brace/bracket"); + } + + // skip close character + ++it; + + // set top + top = top->parent; + } + break; + + case ':': + if (!top || top->type != JSON_OBJECT) + { + ERROR(it, "Unexpected character"); + } + ++it; + break; + + case ',': + CHECK_TOP(); + ++it; + break; + + case '"': + { + CHECK_TOP(); + + // skip '"' character + ++it; + + char *first = it; + char *last = it; + while (*it) + { + if ((unsigned char)*it < '\x20') + { + ERROR(first, "Control characters not allowed in strings"); + } + else if (*it == '\\') + { + switch (it[1]) + { + case '"': + *last = '"'; + break; + case '\\': + *last = '\\'; + break; + case '/': + *last = '/'; + break; + case 'b': + *last = '\b'; + break; + case 'f': + *last = '\f'; + break; + case 'n': + *last = '\n'; + ++escaped_newlines; + break; + case 'r': + *last = '\r'; + break; + case 't': + *last = '\t'; + break; + case 'u': + { + unsigned int codepoint; + if (hatoui(it + 2, it + 6, &codepoint) != it + 6) + { + ERROR(it, "Bad unicode codepoint"); + } + + if (codepoint <= 0x7F) + { + *last = (char)codepoint; + } + else if (codepoint <= 0x7FF) + { + *last++ = (char)(0xC0 | (codepoint >> 6)); + *last = (char)(0x80 | (codepoint & 0x3F)); + } + else if (codepoint <= 0xFFFF) + { + *last++ = (char)(0xE0 | (codepoint >> 12)); + *last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F)); + *last = (char)(0x80 | (codepoint & 0x3F)); + } + } + it += 4; + break; + default: + ERROR(first, "Unrecognized escape sequence"); + } + + ++last; + it += 2; + } + else if (*it == '"') + { + *last = 0; + ++it; + break; + } + else + { + *last++ = *it++; + } + } + + if (!name && top->type == JSON_OBJECT) + { + // field name in object + name = first; + } + else + { + // new string value + json_value *object = json_alloc(allocator); + + object->name = name; + name = 0; + + object->type = JSON_STRING; + object->string_value = first; + + json_append(top, object); + } + } + break; + + case 'n': + case 't': + case 'f': + { + CHECK_TOP(); + + // new null/bool value + json_value *object = json_alloc(allocator); + + object->name = name; + name = 0; + + // null + if (it[0] == 'n' && it[1] == 'u' && it[2] == 'l' && it[3] == 'l') + { + object->type = JSON_NULL; + it += 4; + } + // true + else if (it[0] == 't' && it[1] == 'r' && it[2] == 'u' && it[3] == 'e') + { + object->type = JSON_BOOL; + object->int_value = 1; + it += 4; + } + // false + else if (it[0] == 'f' && it[1] == 'a' && it[2] == 'l' && it[3] == 's' && it[4] == 'e') + { + object->type = JSON_BOOL; + object->int_value = 0; + it += 5; + } + else + { + ERROR(it, "Unknown identifier"); + } + + json_append(top, object); + } + break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + CHECK_TOP(); + + // new number value + json_value *object = json_alloc(allocator); + + object->name = name; + name = 0; + + object->type = JSON_INT; + + char *first = it; + while (*it != '\x20' && *it != '\x9' && *it != '\xD' && *it != '\xA' && *it != ',' && *it != ']' && *it != '}') + { + if (*it == '.' || *it == 'e' || *it == 'E') + { + object->type = JSON_FLOAT; + } + ++it; + } + + if (object->type == JSON_INT && atoi(first, it, &object->int_value) != it) + { + ERROR(first, "Bad integer number"); + } + + if (object->type == JSON_FLOAT && atof(first, it, &object->float_value) != it) + { + ERROR(first, "Bad float number"); + } + + json_append(top, object); + } + break; + + default: + ERROR(it, "Unexpected character"); + } + + // skip white space + while (*it == '\x20' || *it == '\x9' || *it == '\xD' || *it == '\xA') + { + ++it; + } + } + + if (top) + { + ERROR(it, "Not all objects/arrays have been properly closed"); + } + + return root; +} + +#define IDENT(n) for (int i = 0; i < n; ++i) printf(" ") + +void json_print(json_value *value, int ident) +{ + IDENT(ident); + if (value->name) printf("\"%s\" = ", value->name); + switch(value->type) + { + case JSON_NULL: + printf("null\n"); + break; + case JSON_OBJECT: + case JSON_ARRAY: + printf(value->type == JSON_OBJECT ? "{\n" : "[\n"); + for (json_value *it = value->first_child; it; it = it->next_sibling) + { + json_print(it, ident + 1); + } + IDENT(ident); + printf(value->type == JSON_OBJECT ? "}\n" : "]\n"); + break; + case JSON_STRING: + printf("\"%s\"\n", value->string_value); + break; + case JSON_INT: + printf("%d\n", value->int_value); + break; + case JSON_FLOAT: + printf("%f\n", value->float_value); + break; + case JSON_BOOL: + printf(value->int_value ? "true\n" : "false\n"); + break; + } +} + +JsonReader::JsonReader(const std::string &filename) : alloc_(1 << 12), root_(0) { + size_t buf_size; + buffer_ = (char *)VFSReadFile(filename.c_str(), &buf_size); + if (buffer_) { + parse(); + } else { + ELOG("Failed to read json %s", filename.c_str()); + } +} diff --git a/ext/vjson/json.h b/ext/vjson/json.h new file mode 100644 index 0000000000..08c4ba8b7a --- /dev/null +++ b/ext/vjson/json.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include +#include "base/logging.h" +#include "block_allocator.h" +#include "file/vfs.h" + +enum json_type +{ + JSON_NULL, + JSON_OBJECT, + JSON_ARRAY, + JSON_STRING, + JSON_INT, + JSON_FLOAT, + JSON_BOOL, +}; + +struct json_value +{ + json_value() {} + + json_value *parent; + json_value *next_sibling; + json_value *first_child; + json_value *last_child; + + char *name; + union + { + char *string_value; + int int_value; + float float_value; + }; + + json_type type; + + int numChildren() const; + int numSiblings() const; // num siblings *after* this one only + const json_value *get(const char *child_name) const; + const json_value *get(const char *child_name, json_type type) const; + const char *getString(const char *child_name) const; + const char *getString(const char *child_name, const char *default_value) const; + bool getStringVector(std::vector *vec) const; + float getFloat(const char *child_name) const; + float getFloat(const char *child_name, float default_value) const; + int getInt(const char *child_name) const; + int getInt(const char *child_name, int default_value) const; + bool getBool(const char *child_name) const; + bool getBool(const char *child_name, bool default_value) const; + +private: + DISALLOW_COPY_AND_ASSIGN(json_value); +}; + +// low level interface +json_value *json_parse(char *source, char **error_pos, char **error_desc, int *error_line, block_allocator *allocator); +void json_print(json_value *value, int ident = 0); + + +// Easy-wrapper +class JsonReader { +public: + JsonReader(const std::string &filename); + JsonReader(const char *data, size_t size) : alloc_(1 << 12) { + buffer_ = (char *)malloc(size + 1); + memcpy(buffer_, data, size); + buffer_[size] = 0; + parse(); + } + + ~JsonReader() { + free(buffer_); + } + + void parse() { + char *error_pos; + char *error_desc; + int error_line; + root_ = json_parse((char *)buffer_, &error_pos, &error_desc, &error_line, &alloc_); + if (!root_) { + ELOG("Error at (%i): %s\n%s\n\n", error_line, error_desc, error_pos); + } + } + + bool ok() const { return root_ != 0; } + + json_value *root() { return root_; } + const json_value *root() const { return root_; } + + void print() { + json_print(root_); + } + +private: + char *buffer_; + block_allocator alloc_; + json_value *root_; + + DISALLOW_COPY_AND_ASSIGN(JsonReader); +}; diff --git a/ext/vjson/main.cpp b/ext/vjson/main.cpp new file mode 100644 index 0000000000..c0658c3ae0 --- /dev/null +++ b/ext/vjson/main.cpp @@ -0,0 +1,118 @@ +#include +#include +#include "json.h" + +void populate_sources(const char *filter, std::vector > &sources) +{ + char filename[256]; + for (int i = 1; i < 64; ++i) + { + sprintf(filename, filter, i); + FILE *fp = fopen(filename, "rb"); + if (fp) + { + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + fseek(fp, 0, SEEK_SET); + std::vector buffer(size + 1); + fread (&buffer[0], 1, size, fp); + fclose(fp); + sources.push_back(buffer); + } + else + { + break; + } + } + + printf("Loaded %d json files\n", sources.size()); +} + +#define IDENT(n) for (int i = 0; i < n; ++i) printf(" ") + +void print(json_value *value, int ident = 0) +{ + IDENT(ident); + if (value->name) printf("\"%s\" = ", value->name); + switch(value->type) + { + case JSON_NULL: + printf("null\n"); + break; + case JSON_OBJECT: + case JSON_ARRAY: + printf(value->type == JSON_OBJECT ? "{\n" : "[\n"); + for (json_value *it = value->first_child; it; it = it->next_sibling) + { + print(it, ident + 1); + } + IDENT(ident); + printf(value->type == JSON_OBJECT ? "}\n" : "]\n"); + break; + case JSON_STRING: + printf("\"%s\"\n", value->string_value); + break; + case JSON_INT: + printf("%d\n", value->int_value); + break; + case JSON_FLOAT: + printf("%f\n", value->float_value); + break; + case JSON_BOOL: + printf(value->int_value ? "true\n" : "false\n"); + break; + } +} + +bool parse(char *source) +{ + char *errorPos = 0; + char *errorDesc = 0; + int errorLine = 0; + block_allocator allocator(1 << 10); + + json_value *root = json_parse(source, &errorPos, &errorDesc, &errorLine, &allocator); + if (root) + { + print(root); + return true; + } + + printf("Error at line %d: %s\n%s\n\n", errorLine, errorDesc, errorPos); + return false; +} + +int main(int argc, char **argv) +{ + // Fail + printf("===FAIL===\n\n"); + std::vector > sources; + populate_sources("test/fail%d.json", sources); + int passed = 0; + for (size_t i = 0; i < sources.size(); ++i) + { + printf("Parsing %d\n", i + 1); + if (parse(&sources[i][0])) + { + ++passed; + } + } + printf("Passed %d from %d tests\n", passed, sources.size()); + + // Pass + sources.clear(); + printf("\n===PASS===\n\n"); + populate_sources("test/pass%d.json", sources); + passed = 0; + for (size_t i = 0; i < sources.size(); ++i) + { + printf("Parsing %d\n", i + 1); + if (parse(&sources[i][0])) + { + ++passed; + } + } + printf("Passed %d from %d tests\n", passed, sources.size()); + + return 0; +} diff --git a/file/CMakeLists.txt b/file/CMakeLists.txt new file mode 100644 index 0000000000..e0b552014a --- /dev/null +++ b/file/CMakeLists.txt @@ -0,0 +1,13 @@ +set(SRCS + easy_file.cpp + chunk_file.cpp + zip_read.cpp + file_util.cpp) + +set(SRCS ${SRCS}) + +add_library(file STATIC ${SRCS}) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) diff --git a/file/chunk_file.cpp b/file/chunk_file.cpp new file mode 100644 index 0000000000..9417a98ecc --- /dev/null +++ b/file/chunk_file.cpp @@ -0,0 +1,274 @@ +#include "base/logging.h" +#include "file/chunk_file.h" +#include "file/zip_read.h" + +//#define CHUNKDEBUG + +ChunkFile::ChunkFile(const char *filename, bool _read) { + data=0; + fn = filename; + fastMode=false; + numLevels=0; + read=_read; + pos=0; + didFail=false; + + fastMode = read ? true : false; + + if (fastMode) { + size_t size; + data = (uint8 *)VFSReadFile(filename, &size); + if (!data) { + ELOG("Chunkfile fail: %s", filename); + didFail = true; + return; + } + eof = size; + return; + } + + if (file.open(filename, FILE_WRITE)) { + didFail=false; + eof=file.fileSize(); + } else { + didFail=true; + return; + } +} + +ChunkFile::~ChunkFile() { + if (fastMode && data) + delete [] data; + else + file.close(); +} + +int ChunkFile::readInt() { + if (pos>8, id>>16, id>>24); +#endif + stack[numLevels]=temp; + seekTo(stack[numLevels].parentStartLocation); + return false; + } + + //descend into it + //pos was set inside the loop above + eof = stack[numLevels].startLocation + stack[numLevels].length; + numLevels++; +#ifdef CHUNKDEBUG + ILOG("Descended into %c%c%c%c", id, id>>8, id>>16, id>>24); +#endif + return true; + } else { +#ifndef DEMO_VERSION //if this is missing.. heheh + //write a chunk id, and prepare for filling in length later + writeInt(id); + writeInt(0); //will be filled in by Ascend + stack[numLevels].startLocation=pos; + numLevels++; + return true; +#else + return true; +#endif + } +} + +void ChunkFile::seekTo(int _pos) { + if (!fastMode) + file.seekBeg(_pos); + pos=_pos; +} + +//let's ascend out +void ChunkFile::ascend() { + if (read) { + //ascend, and restore information + numLevels--; + seekTo(stack[numLevels].parentStartLocation); + eof = stack[numLevels].parentEOF; +#ifdef CHUNKDEBUG + int id = stack[numLevels].ID; + ILOG("Ascended out of %c%c%c%c", id, id>>8, id>>16, id>>24); +#endif + } else { + numLevels--; + //now fill in the written length automatically + int posNow = pos; + seekTo(stack[numLevels].startLocation - 4); + writeInt(posNow-stack[numLevels].startLocation); + seekTo(posNow); + } +} + +//read a block +void ChunkFile::readData(void *what, int count) { + if (fastMode) + memcpy(what, data + pos, count); + else + file.read(what,count); + + pos+=count; + char temp[4]; //discarded + count &= 3; + if (count) { + count=4-count; + if (!fastMode) + file.read(temp,count); + pos+=count; + } +} + +//write a block +void ChunkFile::writeData(const void *what, int count) { + file.write(what, count); + pos+=count; + char temp[5]={0,0,0,0,0}; + count &= 3; + if (count) + { + count=4-count; + file.write(temp,count); + pos+=count; + } +} + +void ChunkFile::writeWString(String str) { + wchar_t *text; + int len=str.length(); +#ifdef UNICODE + text = str.getPointer(); +#else + text=new wchar_t[len+1]; + str.toUnicode(text); +#endif + writeInt(len); + writeData((char *)text,len*sizeof(wchar_t)); +#ifndef UNICODE + delete [] text; +#endif +} + +String ChunkFile::readWString() { + int len=readInt(); + wchar_t *text = new wchar_t[len+1]; + readData((char *)text,len*sizeof(wchar_t)); + text[len]=0; +#ifdef UNICODE + String s(text); + delete [] text; + return s; +#else + String temp; + temp.fromUnicode(text); + delete [] text; + return temp; +#endif +} + +static void toUnicode(const std::string &str, uint16 *t) { + for (int i=0; i<(int)str.size(); i++) { + *t++ = str[i]; + } + *t++ = '\0'; +} + +static std::string fromUnicode(const uint16 *src, int len) { + struct Local { + static int clamp(int i) { + return i>255?' ':i; + } + }; + + std::string str; + str.resize(len); + + for (int i=0; i + +#include "base/basictypes.h" +#include "base/LAMEString.h" +#include "file/easy_file.h" + +// TO REMEMBER WHEN USING: + +// EITHER a chunk contains ONLY data +// OR it contains ONLY other chunks +// otherwise the scheme breaks... +// hm.. or come to think about it, some data at the beginning of a chunk with subchunks WOULD work.. +// but not safely, so don't try +inline uint32 flipID(uint32 id) { + return ((id>>24)&0xFF) | ((id>>8)&0xFF00) | ((id<<8)&0xFF0000) | ((id<<24)&0xFF000000); +} + +class ChunkFile { +public: + ChunkFile(const char *filename, bool _read); + ~ChunkFile(); + + bool descend(uint32 id); + void ascend(); + + int readInt(); + void readInt(int &i) {i = readInt();} + void readData(void *data, int count); + String readWString(); + + void writeString(const std::string &str); + std::string readString(); + + void writeInt(int i); + void writeWString(String str); + void writeData(const void *data, int count); + + int getCurrentChunkSize(); + bool failed() const {return didFail;} + std::string filename() const { return fn; } + +private: + std::string fn; + LAMEFile file; + struct ChunkInfo { + int startLocation; + int parentStartLocation; + int parentEOF; + unsigned int ID; + int length; + }; + ChunkInfo stack[8]; + int numLevels; + + uint8 *data; + int pos,eof; + bool fastMode; + bool read; + bool didFail; + + void seekTo(int _pos); + int getPos() const {return pos;} +}; + diff --git a/file/easy_file.cpp b/file/easy_file.cpp new file mode 100644 index 0000000000..6256bead04 --- /dev/null +++ b/file/easy_file.cpp @@ -0,0 +1,96 @@ +#include + +#include "base/basictypes.h" +#include "base/LAMEString.h" +#include "file/easy_file.h" + +LAMEFile::LAMEFile() : file_(NULL) { + isOpen = false; +} + +LAMEFile::~LAMEFile() { } + +bool LAMEFile::open(const char *filename, eFileMode mode) { + //for easier access if we want to expand the path.. + // String temp=filename; + // temp = getRelativePath(temp); + + //it's time to open the file + file_ = fopen(filename, mode == FILE_READ ? "rb" : "wb"); + + if (!file_) { + isOpen = false; + } else { + isOpen = true; + if (mode == FILE_READ) { + fseek(file_, 0, SEEK_END); + size_ = ftell(file_); + fseek(file_, 0, SEEK_SET); + } + } + return isOpen; +} + +void LAMEFile::close() { + if (isOpen) { + //close the file and reset variables + fclose(file_); + file_ = NULL; + isOpen=false; + } +} + +int LAMEFile::fileSize() { + if (!isOpen) //of course + return 0; + else + return size_; +} + +std::string LAMEFile::readAll() { + std::string s; + size_t size = fileSize(); + s.resize(size); + read(&s[0], size); + return s; +} + +int LAMEFile::write(const void *data, int size) { + if (isOpen) { + return fwrite(data, 1, size, file_); //we return the number of bytes that actually got written + } else { + return 0; + } +} + +int LAMEFile::read(void *data, int size) { + if (isOpen) { + return fread(data, 1, size, file_); + } else { + return 0; + } +} + +int LAMEFile::readInt() { + int temp; + if (read(&temp, sizeof(int))) + return temp; + else + return 0; +} + +void LAMEFile::writeInt(int i) { + write(&i, sizeof(int)); +} + +char LAMEFile::readChar() { + char temp; + if (read(&temp, sizeof(char))) + return temp; + else + return 0; +} + +void LAMEFile::writeChar(char i) { + write(&i,sizeof(char)); +} diff --git a/file/easy_file.h b/file/easy_file.h new file mode 100644 index 0000000000..a0c4d24074 --- /dev/null +++ b/file/easy_file.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include + +#include "base/basictypes.h" + + +// Raw file paths, does not go through VFS. + +enum eFileMode { + FILE_READ=5, + FILE_WRITE=6 +}; + +class LAMEFile { +public: + LAMEFile(); + virtual ~LAMEFile(); + + bool open(const char *filename, eFileMode mode); + bool open(std::string filename, eFileMode mode) { + return open(filename.c_str(), mode); + } + void close(); + + void writeInt(int i); + void writeChar(char i); + int write(const void *data, int size); + void write(const std::string &str) { + write((void *)str.data(), str.size()); + } + + int readInt(); + char readChar(); + int read(void *data, int size); + + std::string readAll(); + + int fileSize(); + + void seekBeg(int pos) { + if (isOpen) fseek(file_,pos,SEEK_SET); + } + void seekEnd(int pos) { + if (isOpen) fseek(file_,pos,SEEK_END); + } + void seekCurrent(int pos) { + if (isOpen) fseek(file_,pos,SEEK_CUR); + } +private: + FILE *file_; + bool isOpen; + int size_; +}; diff --git a/file/file_util.cpp b/file/file_util.cpp new file mode 100644 index 0000000000..51e9684e07 --- /dev/null +++ b/file/file_util.cpp @@ -0,0 +1,125 @@ +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#include +#endif +#include +#include +#include "base/logging.h" +#include "base/basictypes.h" +#include "file/file_util.h" +#include + +bool WriteStringToFile(bool text_file, const std::string &str, const char *filename) +{ + FILE *f = fopen(filename, text_file ? "w" : "wb"); + if (!f) + return false; + size_t len = str.size(); + if (len != fwrite(str.data(), 1, str.size(), f)) + { + fclose(f); + return false; + } + fclose(f); + return true; +} + +// Overloaded GetSize, accepts FILE* +uint64_t GetSize(FILE *f) +{ + // can't use off_t here because it can be 32-bit + uint64_t pos = ftell(f); + if (fseek(f, 0, SEEK_END) != 0) { + return 0; + } + uint64_t size = ftell(f); + if ((size != pos) && (fseek(f, pos, SEEK_SET) != 0)) { + return 0; + } + return size; +} + +bool ReadFileToString(bool text_file, const char *filename, std::string &str) +{ + FILE *f = fopen(filename, text_file ? "r" : "rb"); + if (!f) + return false; + size_t len = (size_t)GetSize(f); + char *buf = new char[len + 1]; + buf[fread(buf, 1, len, f)] = 0; + str = std::string(buf, len); + fclose(f); + delete [] buf; + return true; +} + +#define DIR_SEP "/" + +#ifndef METRO + +size_t getFilesInDir(const char *directory, std::vector *files) +{ + size_t foundEntries = 0; +#ifdef _WIN32 + // Find the first file in the directory. + WIN32_FIND_DATA ffd; +#ifdef UNICODE + HANDLE hFind = FindFirstFile((std::wstring(directory) + "\\*").c_str(), &ffd); +#else + HANDLE hFind = FindFirstFile((std::string(directory) + "\\*").c_str(), &ffd); +#endif + if (hFind == INVALID_HANDLE_VALUE) + { + FindClose(hFind); + return foundEntries; + } + // windows loop + do + { + const std::string virtualName(ffd.cFileName); +#else + struct dirent dirent, *result = NULL; + + DIR *dirp = opendir(directory); + if (!dirp) + return 0; + + // non windows loop + while (!readdir_r(dirp, &dirent, &result) && result) + { + const std::string virtualName(result->d_name); +#endif + // check for "." and ".." + if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || + ((virtualName[0] == '.') && (virtualName[1] == '.') && + (virtualName[2] == '\0'))) + continue; + + files->push_back(std::string(directory) + virtualName); +#ifdef _WIN32 + } while (FindNextFile(hFind, &ffd) != 0); + FindClose(hFind); +#else + } + closedir(dirp); +#endif + return foundEntries; +} + +void deleteFile(const char *file) +{ +#ifdef _WIN32 + if (!DeleteFile(file)) { + ELOG("Error deleting %s: %i", file, GetLastError()); + } +#else + int err = unlink(file); + if (err) { + ELOG("Error unlinking %s: %i", file, err); + } +#endif +} +#endif \ No newline at end of file diff --git a/file/file_util.h b/file/file_util.h new file mode 100644 index 0000000000..06ed549824 --- /dev/null +++ b/file/file_util.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +bool writeStringToFile(bool text_file, const std::string &str, const char *filename); +bool readFileToString(bool text_file, const char *filename, std::string &str); + + +size_t getFilesInDir(const char *directory, std::vector *files); +void deleteFile(const char *file); \ No newline at end of file diff --git a/file/vfs.h b/file/vfs.h new file mode 100644 index 0000000000..552eb6dbc1 --- /dev/null +++ b/file/vfs.h @@ -0,0 +1,12 @@ +#include "base/basictypes.h" + +class AssetReader; +// Virtual file system. + +void VFSRegister(const char *prefix, AssetReader *reader); +void VFSShutdown(); + +// Use delete [] to release the memory. +// Always allocates an extra '\0' at the end, so that it +// can be used for text like shader sources. +uint8_t *VFSReadFile(const char *filename, size_t *size); diff --git a/file/zip_read.cpp b/file/zip_read.cpp new file mode 100644 index 0000000000..71b6e2617f --- /dev/null +++ b/file/zip_read.cpp @@ -0,0 +1,137 @@ +#include + +#ifndef _WIN32 +#include +#endif + +#include "base/basictypes.h" +#include "base/logging.h" +#include "file/zip_read.h" + +#ifndef _WIN32 +uint8_t *ReadFromZip(zip *archive, const char* filename, size_t *size) { + // Figure out the file size first. + struct zip_stat zstat; + zip_stat(archive, filename, ZIP_FL_NOCASE, &zstat); + + uint8_t *contents = new uint8_t[zstat.size + 1]; + + zip_file *file = zip_fopen(archive, filename, 0); + if (!file) { + ELOG("Error opening %s from ZIP", filename); + delete [] contents; + return 0; + } + zip_fread(file, contents, zstat.size); + zip_fclose(file); + contents[zstat.size] = 0; + + *size = zstat.size; + return contents; +} + +#endif + +// The return is non-const because - why not? +uint8_t *ReadLocalFile(const char *filename, size_t *size) { + FILE *file = fopen(filename, "rb"); + if (!file) { + return 0; + } + fseek(file, 0, SEEK_END); + size_t f_size = ftell(file); + fseek(file, 0, SEEK_SET); + uint8_t *contents = new uint8_t[f_size+1]; + fread(contents, 1, f_size, file); + fclose(file); + contents[f_size] = 0; + *size = f_size; + return contents; +} + +#ifndef _WIN32 + +ZipAssetReader::ZipAssetReader(const char *zip_file, const char *in_zip_path) { + zip_file_ = zip_open(zip_file, 0, NULL); + strcpy(in_zip_path_, in_zip_path); + if (!zip_file_) { + ELOG("Failed to open %s as a zip file", zip_file); + } + // This is not really necessary. + int numFiles = zip_get_num_files(zip_file_); + for (int i = 0; i < numFiles; i++) { + const char* name = zip_get_name(zip_file_, i, 0); + if (name == NULL) { + ELOG("Error reading zip file name at index %i : %s", i, zip_strerror(zip_file_)); + return; + } + // ILOG("File %i : %s\n", i, name); + } +} + +ZipAssetReader::~ZipAssetReader() { + zip_close(zip_file_); +} + +uint8_t *ZipAssetReader::ReadAsset(const char *path, size_t *size) { + char temp_path[256]; + strcpy(temp_path, in_zip_path_); + strcat(temp_path, path); + return ReadFromZip(zip_file_, temp_path, size); +} + +#endif + +uint8_t *DirectoryAssetReader::ReadAsset(const char *path, size_t *size) { + char new_path[256] = {0}; + // Check if it already contains the path + if (strlen(path) > strlen(path_) && 0 == memcmp(path, path_, strlen(path_))) { + } + else { + strcpy(new_path, path_); + } + strcat(new_path, path); + // ILOG("New path: %s", new_path); + return ReadLocalFile(new_path, size); +} + +struct VFSEntry { + const char *prefix; + AssetReader *reader; +}; + +static VFSEntry entries[16]; +static int num_entries = 0; + +void VFSRegister(const char *prefix, AssetReader *reader) { + entries[num_entries].prefix = prefix; + entries[num_entries].reader = reader; + ILOG("Registered VFS for %s", prefix); + num_entries++; +} + +void VFSShutdown() { + for (int i = 0; i < num_entries; i++) { + delete entries[i].reader; + } + num_entries = 0; +} + +uint8_t *VFSReadFile(const char *filename, size_t *size) { + int fn_len = strlen(filename); + for (int i = 0; i < num_entries; i++) { + int prefix_len = strlen(entries[i].prefix); + if (prefix_len >= fn_len) continue; + if (0 == memcmp(filename, entries[i].prefix, prefix_len)) { + // ILOG("Prefix match: %s (%s) -> %s", entries[i].prefix, filename, filename + prefix_len); + uint8_t *data = entries[i].reader->ReadAsset(filename + prefix_len, size); + if (data) + return data; + else + continue; + // Else try the other registered file systems. + } + } + ELOG("Missing filesystem for %s", filename); + return 0; +} diff --git a/file/zip_read.h b/file/zip_read.h new file mode 100644 index 0000000000..4194b5fd26 --- /dev/null +++ b/file/zip_read.h @@ -0,0 +1,45 @@ +#ifndef _WIN32 +#include +#endif + +#include + +#include "base/basictypes.h" +#include "file/vfs.h" + +// Direct readers. deallocate using delete []. +uint8_t *ReadLocalFile(const char *filename, size_t *size); + +class AssetReader { +public: + // use delete[] + virtual uint8_t *ReadAsset(const char *path, size_t *size) = 0; +}; + +#ifndef _WIN32 +uint8_t *ReadFromZip(zip *archive, const char* filename, size_t *size); +class ZipAssetReader : public AssetReader { +public: + ZipAssetReader(const char *zip_file, const char *in_zip_path); + ~ZipAssetReader(); + // use delete[] + virtual uint8_t *ReadAsset(const char *path, size_t *size); + +private: + zip *zip_file_; + char in_zip_path_[256]; +}; +#endif + +class DirectoryAssetReader : public AssetReader { +public: + DirectoryAssetReader(const char *path) { + strcpy(path_, path); + } + // use delete[] + virtual uint8_t *ReadAsset(const char *path, size_t *size); + +private: + char path_[512]; +}; + diff --git a/gfx/CMakeLists.txt b/gfx/CMakeLists.txt new file mode 100644 index 0000000000..97264f7d85 --- /dev/null +++ b/gfx/CMakeLists.txt @@ -0,0 +1,14 @@ +set(SRCS + gl_debug_log.cpp + gl_lost_manager.cpp + texture.cpp + texture_atlas.cpp + texture_gen.cpp) + +set(SRCS ${SRCS}) + +add_library(gfx STATIC ${SRCS}) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) diff --git a/gfx/gl_debug_log.cpp b/gfx/gl_debug_log.cpp new file mode 100644 index 0000000000..3bcfb80f4d --- /dev/null +++ b/gfx/gl_debug_log.cpp @@ -0,0 +1,66 @@ +#if defined(ANDROID) +#include +#include +typedef char GLchar; +#else +#include +#if defined(__APPLE__) +#include +#else +#include +#endif +#endif + +#include "base/logging.h" + +void glCheckzor(const char *file, int line) { + GLenum err = glGetError(); + if (err != GL_NO_ERROR) { + ELOG("GL error on line %i in %s: %i (%04x)", line, file, (int)err, (int)err); + } +} + +#ifndef ANDROID +#if 0 +void log_callback(GLenum source, GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + GLvoid* userParam) { + const char *src = "unknown"; + switch (source) { + case GL_DEBUG_SOURCE_API_GL_ARB: + src = "GL"; + break; + case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: + src = "GLSL"; + break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: + src = "X"; + break; + default: + break; + } + switch (type) { + case GL_DEBUG_TYPE_ERROR_ARB: + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: + ELOG("%s: %s", src, message); + break; + default: + ILOG("%s: %s", src, message); + break; + } +} +#endif +#endif + +void gl_log_enable() { +#ifndef ANDROID +#if 0 + glEnable(DEBUG_OUTPUT_SYNCHRONOUS_ARB); // TODO: Look into disabling, for more perf + glDebugMessageCallback(&log_callback, 0); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE); +#endif +#endif +} diff --git a/gfx/gl_debug_log.h b/gfx/gl_debug_log.h new file mode 100644 index 0000000000..115a393d1a --- /dev/null +++ b/gfx/gl_debug_log.h @@ -0,0 +1,24 @@ +#pragma once + +// New skool debugging +void gl_log_enable(); + +// Old skool debugging + +#ifndef ANDROID +//#define DEBUG_OPENGL +#endif + +#if defined(DEBUG_OPENGL) + +void glCheckzor(const char *file, int line); +#define GL_CHECK() glCheckzor(__FILE__, __LINE__) + +#else + +#define GL_CHECK() + +#endif + + + diff --git a/gfx/gl_lost_manager.cpp b/gfx/gl_lost_manager.cpp new file mode 100644 index 0000000000..0ed6854d92 --- /dev/null +++ b/gfx/gl_lost_manager.cpp @@ -0,0 +1,49 @@ +#include + +#include "base/logging.h" +#include "gfx/gl_lost_manager.h" + +std::list *holders; + +GfxResourceHolder::~GfxResourceHolder() {} + +void register_gl_resource_holder(GfxResourceHolder *holder) { + if (holders) { + holders->push_back(holder); + } else { + WLOG("GL resource holder not initialized, cannot register resource"); + } +} +void unregister_gl_resource_holder(GfxResourceHolder *holder) { + if (holders) { + holders->remove(holder); + } else { + WLOG("GL resource holder not initialized, cannot unregister resource"); + } +} + +void gl_lost() { + if (!holders) { + WLOG("GL resource holder not initialized, cannot process lost request"); + return; + } + for (std::list::iterator iter = holders->begin(); + iter != holders->end(); ++iter) { + (*iter)->GLLost(); + } +} + +void gl_lost_manager_init() { + if (holders) { + FLOG("Double GL lost manager init"); + } + holders = new std::list(); +} + +void gl_lost_manager_shutdown() { + if (!holders) { + FLOG("Lost manager already shutdown"); + } + delete holders; + holders = 0; +} diff --git a/gfx/gl_lost_manager.h b/gfx/gl_lost_manager.h new file mode 100644 index 0000000000..fbcc4165ce --- /dev/null +++ b/gfx/gl_lost_manager.h @@ -0,0 +1,16 @@ +#pragma once + +class GfxResourceHolder { + public: + virtual ~GfxResourceHolder(); + virtual void GLLost() = 0; +}; + +void gl_lost_manager_init(); +void gl_lost_manager_shutdown(); + +void register_gl_resource_holder(GfxResourceHolder *holder); +void unregister_gl_resource_holder(GfxResourceHolder *holder); + +// Notifies all objects about the loss. +void gl_lost(); diff --git a/gfx/texture.cpp b/gfx/texture.cpp new file mode 100644 index 0000000000..a36041392c --- /dev/null +++ b/gfx/texture.cpp @@ -0,0 +1,287 @@ +#ifdef ANDROID +#ifdef USE_GLES2 +#include +#include +#else +#include +#include +#endif +#else +#include +#if defined(__APPLE__) +#include +#else +#include +#endif +#endif + +#include +#include +#include + +#ifndef ANDROID +#include "image/png_load.h" +#include "ext/etcpack/etcdec.h" +#endif + +#include "image/zim_load.h" +#include "base/logging.h" +#include "texture.h" +#include "gfx/texture_gen.h" +#include "gfx/gl_debug_log.h" +#include "gfx/gl_lost_manager.h" + +Texture::Texture() : id_(0) { + register_gl_resource_holder(this); +} + +void Texture::Destroy() { + if (id_) { + glDeleteTextures(1, &id_); + id_ = 0; + } +} + +void Texture::GLLost() { + ILOG("Reloading lost texture %s", filename_.c_str()); + Load(filename_.c_str()); +} + +Texture::~Texture() { + unregister_gl_resource_holder(this); + Destroy(); +} + +static void SetTextureParameters(int zim_flags) { + GLenum wrap = GL_REPEAT; + if (zim_flags & ZIM_CLAMP) wrap = GL_CLAMP_TO_EDGE; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); + GL_CHECK(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if ((zim_flags & (ZIM_HAS_MIPS | ZIM_GEN_MIPS))) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + GL_CHECK(); +} + +bool Texture::Load(const char *filename) { + // hook for generated textures + if (!memcmp(filename, "gen:", 4)) { + // TODO + // return false; + int bpp, w, h; + bool clamp; + uint8_t *data = generateTexture(filename, bpp, w, h, clamp); + if (!data) + return false; + glGenTextures(1, &id_); + glBindTexture(GL_TEXTURE_2D, id_); + if (bpp == 1) { + + #ifdef ANDROID + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + #else + glTexImage2D(GL_TEXTURE_2D, 0, 1, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + #endif + } else { + FLOG("unsupported"); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + delete [] data; + return true; + } + + filename_ = filename; + // Currently contains many Rollerball-specific workarounds. + // They shouldn't really hurt anything else very much though. + int len = strlen(filename); + char fn[256]; + strcpy(fn, filename); + bool zim = false; + if (!strcmp("dds", &filename[len-3])) { + strcpy(&fn[len-3], "zim"); + zim = true; + } + if (!strcmp("6TX", &filename[len-3]) || !strcmp("6tx", &filename[len-3])) { + ILOG("Detected 6TX %s", filename); + strcpy(&fn[len-3], "zim"); + zim = true; + } + for (int i = 0; i < (int)strlen(fn); i++) { + if (fn[i] == '\\') fn[i] = '/'; + } + + if (fn[0] == 'm') fn[0] = 'M'; + const char *name = fn; + if (zim && 0==memcmp(name, "Media/textures/", strlen("Media/textures"))) name += strlen("Media/textures/"); + len = strlen(name); + #ifndef ANDROID + if (!strcmp("png", &name[len-3]) || + !strcmp("PNG", &name[len-3])) { + if (!LoadPNG(fn)) { + LoadXOR(); + return false; + } else { + return true; + } + } else + #endif + if (!strcmp("zim", &name[len-3])) { + if (!LoadZIM(name)) { + LoadXOR(); + return false; + } else { + return true; + } + } + LoadXOR(); + return false; +} + +#ifndef ANDROID +bool Texture::LoadPNG(const char *filename) { + unsigned char *image_data; + if (1 != pngLoad(filename, &width_, &height_, &image_data, false)) { + return false; + } + GL_CHECK(); + glGenTextures(1, &id_); + glBindTexture(GL_TEXTURE_2D, id_); + SetTextureParameters(ZIM_GEN_MIPS); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image_data); + glGenerateMipmap(GL_TEXTURE_2D); + GL_CHECK(); + free(image_data); + return true; +} +#endif + +bool Texture::LoadXOR() { + width_ = height_ = 256; + unsigned char *buf = new unsigned char[width_*height_*4]; + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + buf[(y*width_ + x)*4 + 0] = x^y; + buf[(y*width_ + x)*4 + 1] = x^y; + buf[(y*width_ + x)*4 + 2] = x^y; + buf[(y*width_ + x)*4 + 3] = 0xFF; + } + } + GL_CHECK(); + glGenTextures(1, &id_); + glBindTexture(GL_TEXTURE_2D, id_); + SetTextureParameters(ZIM_GEN_MIPS); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, + GL_RGBA, GL_UNSIGNED_BYTE, buf); + glGenerateMipmap(GL_TEXTURE_2D); + GL_CHECK(); + delete [] buf; + return true; +} + + +#ifndef ANDROID + +// Allocates using new[], doesn't free. +uint8_t *ETC1ToRGBA(uint8_t *etc1, int width, int height) { + uint8_t *rgba = new uint8_t[width * height * 4]; + memset(rgba, 0xFF, width * height * 4); + for (int y = 0; y < height; y += 4) { + for (int x = 0; x < width; x += 4) { + DecompressBlock(etc1 + ((y / 4) * width/4 + (x / 4)) * 8, + rgba + (y * width + x) * 4, width, 255); + } + } + return rgba; +} + +#endif + +bool Texture::LoadZIM(const char *filename) { + uint8_t *image_data[ZIM_MAX_MIP_LEVELS]; + int width[ZIM_MAX_MIP_LEVELS]; + int height[ZIM_MAX_MIP_LEVELS]; + + int flags; + int num_levels = ::LoadZIM(filename, &width[0], &height[0], &flags, &image_data[0]); + if (!num_levels) + return false; + width_ = width[0]; + height_ = height[0]; + int data_type = GL_UNSIGNED_BYTE; + int colors = GL_RGBA; + int storage = GL_RGBA; + bool compressed = false; + switch (flags & ZIM_FORMAT_MASK) { + case ZIM_RGBA8888: + data_type = GL_UNSIGNED_BYTE; + break; + case ZIM_RGBA4444: + data_type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case ZIM_RGB565: + data_type = GL_UNSIGNED_SHORT_5_6_5; + colors = GL_RGB; + storage = GL_RGB; + break; + case ZIM_ETC1: + compressed = true; + break; + } + + GL_CHECK(); + glGenTextures(1, &id_); + glBindTexture(GL_TEXTURE_2D, id_); + SetTextureParameters(flags); + + if (compressed) { + for (int l = 0; l < num_levels; l++) { + int data_w = width[l]; + int data_h = height[l]; + if (data_w < 4) data_w = 4; + if (data_h < 4) data_h = 4; +#if defined(ANDROID) + int compressed_image_bytes = data_w * data_h / 2; + glCompressedTexImage2D(GL_TEXTURE_2D, l, GL_ETC1_RGB8_OES, width[l], height[l], 0, compressed_image_bytes, image_data[l]); + GL_CHECK(); +#else + image_data[l] = ETC1ToRGBA(image_data[l], data_w, data_h); + glTexImage2D(GL_TEXTURE_2D, l, GL_RGBA, width[l], height[l], 0, + GL_RGBA, GL_UNSIGNED_BYTE, image_data[l]); +#endif + } + GL_CHECK(); +#if !defined(ANDROID) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, num_levels - 2); +#endif + } else { + for (int l = 0; l < num_levels; l++) { + glTexImage2D(GL_TEXTURE_2D, l, storage, width[l], height[l], 0, + colors, data_type, image_data[l]); + } + if (num_levels == 1 && (flags & ZIM_GEN_MIPS)) { + glGenerateMipmap(GL_TEXTURE_2D); + } + } + SetTextureParameters(flags); + + GL_CHECK(); + // Only free the top level, since the allocation is used for all of them. + delete [] image_data[0]; + return true; +} + +void Texture::Bind(int stage) { + GL_CHECK(); + if (stage != -1) + glActiveTexture(GL_TEXTURE0 + stage); + glBindTexture(GL_TEXTURE_2D, id_); + GL_CHECK(); +} diff --git a/gfx/texture.h b/gfx/texture.h new file mode 100644 index 0000000000..ffc7a05cb2 --- /dev/null +++ b/gfx/texture.h @@ -0,0 +1,44 @@ +#ifndef _TEXTURE_H +#define _TEXTURE_H + +#include + +#include "gfx/gl_lost_manager.h" + +class Texture : public GfxResourceHolder { + public: + Texture(); + ~Texture(); + + bool LoadZIM(const char *filename); +#ifndef ANDROID + bool LoadPNG(const char *filename); +#endif + bool LoadXOR(); // Loads a placeholder texture. + + // Deduces format from the filename. + // If loading fails, will load a 256x256 XOR texture. + // If filename begins with "gen:", will defer to texture_gen.cpp/h. + bool Load(const char *filename); + + void Bind(int stage = -1); + + void Destroy(); + + unsigned int Handle() const { + return id_; + } + + virtual void GLLost(); + std::string filename() const { return filename_; } + + private: + std::string filename_; +#ifdef METRO + ID3D11Texture2D *tex_; +#endif + unsigned int id_; + int width_, height_; +}; + +#endif diff --git a/gfx/texture_atlas.cpp b/gfx/texture_atlas.cpp new file mode 100644 index 0000000000..146aa615b4 --- /dev/null +++ b/gfx/texture_atlas.cpp @@ -0,0 +1,2 @@ +#include "gfx/texture_atlas.h" + diff --git a/gfx/texture_atlas.h b/gfx/texture_atlas.h new file mode 100644 index 0000000000..8dd9c4b746 --- /dev/null +++ b/gfx/texture_atlas.h @@ -0,0 +1,38 @@ +#pragma once + +struct AtlasChar { + // texcoords + float sx, sy, ex, ey; + // offset from the origin + float ox, oy; + // distance to move the origin forward + float wx; + // size in pixels + unsigned short pw, ph; +}; + +struct AtlasFont { + float padding; + float height; + float ascend; + float distslope; + AtlasChar chars[96]; +}; + +struct AtlasImage { + float u1, v1, u2, v2; + int w, h; +}; + +struct Atlas { + const char *filename; + const AtlasFont **fonts; + int num_fonts; + const AtlasImage *images; + int num_images; +}; + +enum { + PRINT_RIGHT = 1, + PRINT_CENTER = 2, +}; diff --git a/gfx/texture_dx11.cpp b/gfx/texture_dx11.cpp new file mode 100644 index 0000000000..bec73980c4 --- /dev/null +++ b/gfx/texture_dx11.cpp @@ -0,0 +1,272 @@ +#include "gfx/texture.h" + +#include + +#include +#include +#include + +#ifndef ANDROID +#include "image/png_load.h" +#include "ext/etcpack/etcdec.h" +#endif + +#include "image/zim_load.h" +#include "base/logging.h" +#include "texture.h" +#include "gfx/texture_gen.h" +#include "gfx/gl_debug_log.h" +#include "gfx/gl_lost_manager.h" + +Texture::Texture() : tex_(0) { + register_gl_resource_holder(this); + + + +} + +void Texture::Destroy() { + if (tex_) { + tex_->Release(); + tex_ = 0; + } +} + +void Texture::GLLost() { + ILOG("Reloading lost texture %s", filename_.c_str()); + Load(filename_.c_str()); +} + +Texture::~Texture() { + unregister_gl_resource_holder(this); + Destroy(); +} + +static void SetTextureParameters(int zim_flags) { +/* + GLenum wrap = GL_REPEAT; + if (zim_flags & ZIM_CLAMP) wrap = GL_CLAMP_TO_EDGE; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); + GL_CHECK(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if ((zim_flags & (ZIM_HAS_MIPS | ZIM_GEN_MIPS))) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + GL_CHECK();*/ +} + +bool Texture::Load(const char *filename) { + // hook for generated textures + if (!memcmp(filename, "gen:", 4)) { + // TODO + // return false; + tex_ = (LPVOID)generateTexture(filename); + if (tex_) { + this->filename_ = filename; + } + return true; + } + + filename_ = filename; + // Currently contains many Rollerball-specific workarounds. + // They shouldn't really hurt anything else very much though. + int len = strlen(filename); + char fn[256]; + strcpy(fn, filename); + bool zim = false; + if (!strcmp("dds", &filename[len-3])) { + strcpy(&fn[len-3], "zim"); + zim = true; + } + if (!strcmp("6TX", &filename[len-3]) || !strcmp("6tx", &filename[len-3])) { + ILOG("Detected 6TX %s", filename); + strcpy(&fn[len-3], "zim"); + zim = true; + } + for (int i = 0; i < (int)strlen(fn); i++) { + if (fn[i] == '\\') fn[i] = '/'; + } + + if (fn[0] == 'm') fn[0] = 'M'; + const char *name = fn; + if (zim && 0 == memcmp(name, "Media/textures/", strlen("Media/textures"))) name += strlen("Media/textures/"); + len = strlen(name); + #ifndef ANDROID + if (!strcmp("png", &name[len-3]) || + !strcmp("PNG", &name[len-3])) { + if (!LoadPNG(fn)) { + LoadXOR(); + return false; + } else { + return true; + } + } else + #endif + if (!strcmp("zim", &name[len-3])) { + if (!LoadZIM(name)) { + LoadXOR(); + return false; + } else { + return true; + } + } + LoadXOR(); + return false; +} + +#ifndef METRO +#ifndef ANDROID +bool Texture::LoadPNG(const char *filename) { + unsigned char *image_data; + if (1 != pngLoad(filename, &width_, &height_, &image_data, false)) { + return false; + } + GL_CHECK(); + glGenTextures(1, &id_); + glBindTexture(GL_TEXTURE_2D, id_); + SetTextureParameters(ZIM_GEN_MIPS); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, + GL_RGBA, GL_UNSIGNED_BYTE, image_data); + glGenerateMipmap(GL_TEXTURE_2D); + GL_CHECK(); + free(image_data); + return true; +} +#endif +#endif + +bool Texture::LoadXOR() { + width_ = height_ = 256; + unsigned char *buf = new unsigned char[width_*height_*4]; + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + buf[(y*width_ + x)*4 + 0] = x^y; + buf[(y*width_ + x)*4 + 1] = x^y; + buf[(y*width_ + x)*4 + 2] = x^y; + buf[(y*width_ + x)*4 + 3] = 0xFF; + } + } + GL_CHECK(); + ID3D11Device *ctx; + D3D11_TEXTURE2D_DESC desc; + desc.Width = width_; + desc.Height = height_; + desc.MipLevels = 0; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + if (FAILED(ctx->CreateTexture2D(&desc, 0, &tex_))) { + FLOG("Failed creating XOR texture"); + } + SetTextureParameters(ZIM_GEN_MIPS); + GL_CHECK(); + delete [] buf; + return true; +} + + +#ifndef ANDROID + +// Allocates using new[], doesn't free. +uint8_t *ETC1ToRGBA(uint8_t *etc1, int width, int height) { + uint8_t *rgba = new uint8_t[width * height * 4]; + memset(rgba, 0xFF, width * height * 4); + for (int y = 0; y < height; y += 4) { + for (int x = 0; x < width; x += 4) { + DecompressBlock(etc1 + ((y / 4) * width/4 + (x / 4)) * 8, + rgba + (y * width + x) * 4, width, 255); + } + } + return rgba; +} + +#endif + +bool Texture::LoadZIM(const char *filename) { + uint8_t *image_data[ZIM_MAX_MIP_LEVELS]; + int width[ZIM_MAX_MIP_LEVELS]; + int height[ZIM_MAX_MIP_LEVELS]; + + int flags; + int num_levels = ::LoadZIM(filename, &width[0], &height[0], &flags, &image_data[0]); + if (!num_levels) + return false; + width_ = width[0]; + height_ = height[0]; + int data_type = GL_UNSIGNED_BYTE; + int colors = GL_RGBA; + int storage = GL_RGBA; + bool compressed = false; + switch (flags & ZIM_FORMAT_MASK) { + case ZIM_RGBA8888: + data_type = GL_UNSIGNED_BYTE; + break; + case ZIM_RGBA4444: + data_type = DXGI_FORMAT_B4G4R4A4_UNORM; + break; + case ZIM_RGB565: + data_type = DXGI_FORMAT_B5G6R5_UNORM; + colors = GL_RGB; + storage = GL_RGB; + break; + case ZIM_ETC1: + compressed = true; + break; + } + + GL_CHECK(); + //glGenTextures(1, &id_); + //glBindTexture(GL_TEXTURE_2D, id_); + SetTextureParameters(flags); + + if (compressed) { + for (int l = 0; l < num_levels; l++) { + int data_w = width[l]; + int data_h = height[l]; + if (data_w < 4) data_w = 4; + if (data_h < 4) data_h = 4; +#if defined(ANDROID) + int compressed_image_bytes = data_w * data_h / 2; + glCompressedTexImage2D(GL_TEXTURE_2D, l, GL_ETC1_RGB8_OES, width[l], height[l], 0, compressed_image_bytes, image_data[l]); + GL_CHECK(); +#else + //image_data[l] = ETC1ToRGBA(image_data[l], data_w, data_h); + //glTexImage2D(GL_TEXTURE_2D, l, GL_RGBA, width[l], height[l], 0, + // GL_RGBA, GL_UNSIGNED_BYTE, image_data[l]); +#endif + } + GL_CHECK(); +#if !defined(ANDROID) + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, num_levels - 2); +#endif + } else { + for (int l = 0; l < num_levels; l++) { + //glTexImage2D(GL_TEXTURE_2D, l, storage, width[l], height[l], 0, + // colors, data_type, image_data[l]); + } + if (num_levels == 1 && (flags & ZIM_GEN_MIPS)) { + //glGenerateMipmap(GL_TEXTURE_2D); + } + } + SetTextureParameters(flags); + + GL_CHECK(); + // Only free the top level, since the allocation is used for all of them. + delete [] image_data[0]; + return true; +} + +void Texture::Bind(int stage) { + GL_CHECK(); + //if (stage != -1) + // glActiveTexture(GL_TEXTURE0 + stage); + // glBindTexture(GL_TEXTURE_2D, id_); + GL_CHECK(); +} diff --git a/gfx/texture_gen.cpp b/gfx/texture_gen.cpp new file mode 100644 index 0000000000..dd02e0079b --- /dev/null +++ b/gfx/texture_gen.cpp @@ -0,0 +1,54 @@ +// Minimal procedural texture generator to generate some usual textures like circles. +// "Gen" textures have filenames like this: "gen:256:256:vignette:0.1" +// +// These must be VERY VERY fast to not slow down loading. Could multicore this in the +// future. +// TODO: Rewrite to make more portable (should not use gfx api in here) + +#include +#include + +#include "base/basictypes.h" +#include "gfx/texture.h" + + +uint8_t *generateTexture(const char *filename, int &bpp, int &w, int &h, bool &clamp) { + char name_and_params[256]; + // security check :) + if (strlen(filename) > 200) + return 0; + sscanf(filename, "gen:%i:%i:%s", &w, &h, name_and_params); + + bool mip = false; + uint8_t *data; + if (!strcmp(name_and_params, "vignette")) { + bpp = 1; + data = new uint8_t[w*h]; + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; x++) { + float dx = (float)(x - w/2) / (w/2); + float dy = (float)(y - h/2) / (h/2); + float dist = sqrtf(dx * dx + dy * dy); + dist /= 1.414f; + float val = 1.0 - powf(dist, 1.4f); + data[y*w + x] = val * 255; + } + } + } else if (!strcmp(name_and_params, "circle")) { + bpp = 1; + // TODO + data = new uint8_t[w*h]; + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; x++) { + float dx = (float)(x - w/2) / (w/2); + float dy = (float)(y - h/2) / (h/2); + float dist = sqrtf(dx * dx + dy * dy); + dist /= 1.414f; + float val = 1.0 - powf(dist, 1.4f); + data[y*w + x] = val * 255; + } + } + } + + return data; +} diff --git a/gfx/texture_gen.h b/gfx/texture_gen.h new file mode 100644 index 0000000000..f5653b6f48 --- /dev/null +++ b/gfx/texture_gen.h @@ -0,0 +1,11 @@ +// Minimal procedural texture generator to generate some useful but unnecessary-to-store textures like circles. +// "Gen" textures have filenames like this: "gen:256:256:4444:vignette:0.1" + +#include "gfx/texture.h" + +// Returns an OpenGL ID for now. + +uint8_t *generateTexture(const char *filename, int &bpp, int &w, int &h, bool &clamp); + + + diff --git a/gfx_es1/CMakeLists.txt b/gfx_es1/CMakeLists.txt new file mode 100644 index 0000000000..f618a2598c --- /dev/null +++ b/gfx_es1/CMakeLists.txt @@ -0,0 +1,10 @@ +set(SRCS + draw_buffer.cpp +) +set(SRCS ${SRCS}) + +add_library(gfx_es1 STATIC ${SRCS}) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) diff --git a/gfx_es1/draw_buffer.cpp b/gfx_es1/draw_buffer.cpp new file mode 100644 index 0000000000..8c8b956933 --- /dev/null +++ b/gfx_es1/draw_buffer.cpp @@ -0,0 +1,287 @@ +// OpenGL ES 1.1 + +#ifdef ANDROID +#include +#else +#if defined(__APPLE__) +#include +#else +#include +#endif +#endif +#include +#include + +#include "gfx_es1/draw_buffer.h" +#include "gfx/texture_atlas.h" + +#include "main_atlas.h" + +LAMEBuffer buffer; +LAMEBuffer topbuffer; + +#define MAX_VERTS 16384 + +LAMEBuffer::LAMEBuffer() { + verts = new Vertex[MAX_VERTS]; + vcount = 0; + xoffset = 0; + yoffset = 0; + fontscalex = 0.37f; + fontscaley = 0.37f; +} + +LAMEBuffer::~LAMEBuffer() { + delete [] verts; + verts = 0; +} + +void LAMEBuffer::Setup() { + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glDisable(GL_TEXTURE_2D); +} + +void LAMEBuffer::Finish() { + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); +} + +// Draws what we have collected so far, so that we can change blend modes etc. +// TODO: Collect states and then blast at the end? +void LAMEBuffer::Flush() { + if (vcount > 0) { + glVertexPointer (3, GL_FLOAT, sizeof(Vertex), (void *)&verts[0].x); + glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(Vertex), (void *)&verts[0].rgba); + glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (void *)&verts[0].u); + glDrawArrays(GL_TRIANGLES, 0, vcount); + // printf("Drawing %i triangles\n", vcount / 3); + } + vcount = 0; +} + +void LAMEBuffer::V(float x, float y, uint32 color, float u, float v) { +#ifndef ANDROID + if (vcount >= MAX_VERTS) { + printf("Hit max # verts\n"); + } +#endif + verts[vcount].x = x + xoffset; + verts[vcount].y = y + yoffset; + verts[vcount].z = 0.0; + verts[vcount].rgba = color; + verts[vcount].u = u; + verts[vcount].v = v; + vcount++; +} + +void LAMEBuffer::rectFill(int x1, int y1, int x2, int y2, Color color) { + rectFillFaded(x1, y1, x2, y2, color, color); +} + +void LAMEBuffer::rectFillFaded(int x1, int y1, int x2, int y2, Color color1, Color color2) { + V(x1, y1, color1, 0, 0); + V(x2, y1, color1, 1, 0); + V(x2, y2, color2, 1, 1); + V(x1, y1, color1, 0, 0); + V(x2, y2, color2, 1, 1); + V(x1, y2, color2, 0, 1); +} + +void LAMEBuffer::MeasureImage(int atlas_image, float *w, float *h) { + const AtlasImage &image = atlas->images[atlas_image]; + *w = image.w; + *h = image.h; +} + +void LAMEBuffer::DrawImage(int atlas_image, float x, float y, Color color) { + const AtlasImage &image = atlas->images[atlas_image]; + float w = image.w; + float h = image.h; + DrawImageStretch(atlas_image, x, y, x + w, y + h, color); +} + +void LAMEBuffer::DrawImageCenter(int atlas_image, float x, float y, Color color) { + const AtlasImage &image = atlas->images[atlas_image]; + DrawImage(atlas_image, x - image.w/2, y - image.h/2, color); +} + +void LAMEBuffer::DrawImageStretch(int atlas_image, float x1, float y1, float x2, float y2, Color color) { + const AtlasImage &image = atlas->images[atlas_image]; + V(x1, y1, color, image.u1, image.v1); + V(x2, y1, color, image.u2, image.v1); + V(x2, y2, color, image.u2, image.v2); + V(x1, y1, color, image.u1, image.v1); + V(x2, y2, color, image.u2, image.v2); + V(x1, y2, color, image.u1, image.v2); +} + +void LAMEBuffer::DrawTexRect(float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, Color color) { + V(x1, y1, color, u1, v1); + V(x2, y1, color, u2, v1); + V(x2, y2, color, u2, v2); + V(x1, y1, color, u1, v1); + V(x2, y2, color, u2, v2); + V(x1, y2, color, u1, v2); +} + +void LAMEBuffer::DrawImage4Grid(int atlas_image, float x1, float y1, float x2, float y2, Color color, float corner_scale) { + const AtlasImage &image = atlas->images[atlas_image]; + + float um = (image.u2 - image.u1) * 0.5f; + float vm = (image.v2 - image.v1) * 0.5f; + float iw2 = (image.w * 0.5f) * corner_scale; + float ih2 = (image.h * 0.5f) * corner_scale; + float xa = x1 + iw2; + float xb = x2 - iw2; + float ya = y1 + ih2; + float yb = y2 - ih2; + float u1 = image.u1, v1 = image.v1, u2 = image.u2, v2 = image.v2; + // Top row + DrawTexRect(x1, y1, xa, ya, u1, v1, um, vm, color); + DrawTexRect(xa, y1, xb, ya, um, v1, um, vm, color); + DrawTexRect(xb, y1, x2, ya, um, v1, u2, vm, color); + // Middle row + DrawTexRect(x1, ya, xa, yb, u1, vm, um, vm, color); + DrawTexRect(xa, ya, xb, yb, um, vm, um, vm, color); + DrawTexRect(xb, ya, x2, yb, um, vm, u2, vm, color); + // Bottom row + DrawTexRect(x1, yb, xa, y2, u1, vm, um, v2, color); + DrawTexRect(xa, yb, xb, y2, um, vm, um, v2, color); + DrawTexRect(xb, yb, x2, y2, um, vm, u2, v2, color); +} + +void LAMEBuffer::DrawImage2GridH(int atlas_image, float x1, float y1, float x2, Color color, float corner_scale) { + const AtlasImage &image = atlas->images[atlas_image]; + float um = (image.u2 - image.u1) * 0.5f; + float iw2 = (image.w * 0.5f) * corner_scale; + float xa = x1 + iw2; + float xb = x2 - iw2; + float u1 = image.u1, v1 = image.v1, u2 = image.u2, v2 = image.v2; + float y2 = y1 + image.h; + DrawTexRect(x1, y1, xa, y2, u1, v1, um, v2, color); + DrawTexRect(xa, y1, xb, y2, um, v1, um, v2, color); + DrawTexRect(xb, y1, x2, y2, um, v1, u2, v2, color); +} + +void LAMEBuffer::MeasureText(int font, const char *text, float *w, float *h) { + const AtlasFont &atlasfont = *atlas->fonts[font]; + unsigned char cval; + float wacc = 0, maxh = 0; + while ((cval = *text++) != '\0') { + if (cval < 32) continue; + if (cval > 127) continue; + AtlasChar c = atlasfont.chars[cval - 32]; + wacc += c.wx * fontscalex; + maxh = 10.0; + } + *w = wacc; + *h = maxh; +} + +void LAMEBuffer::DrawText(int font, const char *text, float x, float y, Color color, int flags) { + const AtlasFont &atlasfont = *atlas->fonts[font]; + unsigned char cval; + if (flags) { + float w, h; + MeasureText(font, text, &w, &h); + if (flags & TEXT_HCENTER) x -= w / 2; + if (flags & TEXT_RIGHT) x -= w; + if (flags & TEXT_VCENTER) y -= h / 2; + } + float sx = x; + while ((cval = *text++) != '\0') { + if (cval == '\n') { + y += 10; + x = sx; + continue; + } + if (cval < 32) continue; + if (cval > 127) continue; + AtlasChar c = atlasfont.chars[cval - 32]; + float cx1 = x + c.ox * fontscalex; + float cy1 = y + c.oy * fontscaley; + float cx2 = x + (c.ox + c.pw) * fontscalex; + float cy2 = y + (c.oy + c.ph) * fontscaley; + V(cx1, cy1, color, c.sx, c.sy); + V(cx2, cy1, color, c.ex, c.sy); + V(cx2, cy2, color, c.ex, c.ey); + V(cx1, cy1, color, c.sx, c.sy); + V(cx2, cy2, color, c.ex, c.ey); + V(cx1, cy2, color, c.sx, c.ey); + x += c.wx * fontscalex; + } +} + + +void LAMEBuffer::hLine(int x1, int y, int x2, Color color) { + rectFill(x1, y, x2, y + 1, color | 0xFF000000); +} + +void LAMEBuffer::hLineDarken(int x1, int y, int x2) { + rectFill(x1, y, x2, y + 1, 0x80000000); +} + +void LAMEBuffer::vLine(int x, int y1, int y2, Color color) { + rectFill(x, y1, x + 1, y2, color); +} + +void LAMEBuffer::vLineAlpha50(int x, int y1, int y2, Color color) { + vLine(x, y1, y2, (color & 0x00FFFFFF) | 0x80); +} + +void LAMEBuffer::rect(int x1, int y1, int x2, int y2, Color color) { + hLine(x1, y1, x2, color); + hLine(x1, y2, x2, color); + vLine(x1, y1, y2, color); + vLine(x2, y1, y2, color); +} + +void LAMEBuffer::rectFillDarken(int x1, int y1, int x2, int y2) { + rectFill(x1, y1, x2, y2, 0x80000000); +} + +void LAMEBuffer::RotateSprite(int atlas_entry, float x, float y, float angle, float scale, Color color) { + // TODO - will be good for knobs + /* + float c = cos(angle + PI/4); + float s = sin(angle + PI/4); + + float x1 = x + c * scale; + float y1 = y + s * scale; + + float x2 = x + c * scale; + float y2 = y + s * scale; + + float x3 = x + c * scale; + float y3 = y + s * scale; + + V(x1, y1, color1, 0, 0); + V(x2, y1, color1, 1, 0); + V(x2, y2, color2, 1, 1); + V(x1, y1, color1, 0, 0); + V(x2, y2, color2, 1, 1); + V(x1, y2, color2, 0, 1); +*/ +} + +void LAMEBuffer::drawText(const TCHAR *text, int x, int y, Color color, int font) { + DrawText(UBUNTU24, text, x, y+3, color); +} +void LAMEBuffer::drawTextCenter(const TCHAR *text, int x, int y, Color color, int font) { + DrawText(UBUNTU24, text, x, y+3, color, TEXT_HCENTER); +} +void LAMEBuffer::drawTextShadow(const TCHAR *text, int x, int y, Color color, Color shadowColor, int font) { + DrawText(UBUNTU24, text, x, y+3, color); +} +void LAMEBuffer::drawTextShadowCenter(const TCHAR *text, int x, int y, Color color, Color shadowColor, int font) { + DrawText(UBUNTU24, text, x, y+3, color, TEXT_HCENTER); +} +void LAMEBuffer::drawTextContrastCenter(const TCHAR *text, int x, int y, Color color, Color shadowColor, int font) { + DrawText(UBUNTU24, text, x, y+3, color, TEXT_HCENTER); +} +void LAMEBuffer::drawTextContrast(const TCHAR *text, int x, int y, Color color, Color shadowColor, int font) { + DrawText(UBUNTU24, text, x, y+3, color); +} diff --git a/gfx_es1/draw_buffer.h b/gfx_es1/draw_buffer.h new file mode 100644 index 0000000000..8796f85447 --- /dev/null +++ b/gfx_es1/draw_buffer.h @@ -0,0 +1,122 @@ +#ifndef __LAMEBUFFER_H__ +#define __LAMEBUFFER_H__ + +#include "base/basictypes.h" +#include "base/color.h" + +class Atlas; + +enum { + TEXT_LEFT = 0, + TEXT_BASELINE = 0, + TEXT_TOP = 1, + TEXT_BOTTOM = 2, + TEXT_HCENTER = 4, + TEXT_VCENTER = 8, + TEXT_RIGHT = 16, +}; + +// OpenGL-based 2D primitive buffer. For GLES 1.1. +// Do not inherit from this class. +class LAMEBuffer { + public: + LAMEBuffer(); + ~LAMEBuffer(); + + void SetAtlas(const Atlas *_atlas) { + atlas = _atlas; + } + + void hLine(int x1, int y, int x2, Color color); + void hLineDarken(int x1, int y, int x2); + void vLine(int x, int y1, int y2, Color color); + void vLineAlpha50(int x, int y1, int y2, Color color); + + void rect(int x1, int y1, int x2, int y2, Color color); + void rectFill(int x1, int y1, int x2, int y2, Color color); + void rectRectFill(int x1, int y1, int x2, int y2, Color border, Color fill) { + rectFill(x1,y1,x2,y2,fill); + rect(x1,y1,x2,y2,border); + } + void rectFillFaded(int x1, int y1, int x2, int y2, Color color1, Color color2); + void rectFillDarkFaded(int x1, int y1, int x2, int y2, Color color) { + rectFillFaded(x1,y1,x2,y2,color, darkenColor(color)); + } + + void rectFillDarken(int x1, int y1, int x2, int y2); + + // New drawing APIs + void MeasureImage(int atlas_image, float *w, float *h); + void DrawImage(int atlas_image, float x, float y, Color color = COLOR(0xFFFFFF)); + void DrawImageCenter(int atlas_image, float x, float y, Color color = COLOR(0xFFFFFF)); + void DrawImageStretch(int atlas_image, float x1, float y1, float x2, float y2, Color color = COLOR(0xFFFFFF)); + void DrawTexRect(float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, Color color); + // Results in 18 triangles. Kind of expensive for a button. + void DrawImage4Grid(int atlas_image, float x1, float y1, float x2, float y2, Color color = COLOR(0xFFFFFF), float corner_scale = 1.0); + // This is only 6 triangles, much cheaper. + void DrawImage2GridH(int atlas_image, float x1, float y1, float x2, Color color = COLOR(0xFFFFFF), float scale = 1.0); + + void MeasureText(int font, const char *text, float *w, float *h); + void DrawText(int font, const char *text, float x, float y, Color color = 0xFFFFFFFF, int flags = 0); + + void RotateSprite(int atlas_entry, float x, float y, float angle, float scale, Color color); + + void drawText(const TCHAR *text, int x, int y, Color color = 0, int font=0); + void drawTextCenter(const TCHAR *text, int x, int y, Color color, int font=0); + void drawTextShadow(const TCHAR *text, int x, int y, Color color=0xffffffff, Color shadowColor=0xFF000000, int font=0); + void drawTextShadowCenter(const TCHAR *text, int x, int y, Color color=0xffffffff, Color shadowColor=0xFF000000, int font=0); + void drawTextContrastCenter(const TCHAR *text, int x, int y, Color color=0xffffffff, Color shadowColor=0xFF000000, int font=0); + void drawTextContrast(const TCHAR *text, int x, int y, Color color=0xffffffff, Color shadowColor=0xFF000000, int font=0); + + void SetFontScale(float xs, float ys) { + fontscalex = xs; + fontscaley = ys; + } + + // Offset management, for easier hierarchical drawing + void PushOffset(int xoff, int yoff) { + // TODO: Use a stack + xoffset = xoff; yoffset = yoff; + } + void PopOffset(int xoff, int yoff) { + xoffset = 0; yoffset = 0; + } + + // Only use in bunches of three. To draw triangles. + inline void V(float x, float y, uint32 color, float u, float v); + + // Call these around all Flush calls of drawbuffers. More than one flush call + // is fine. + static void Setup(); // Enables client state. + static void Finish(); // Disables client state + + // Draws what we have collected so far, so that we can change blend modes etc. + void Flush(); + + private: + const Atlas *atlas; + + float xoffset, yoffset; + + float fontscalex; + float fontscaley; + + struct Vertex { + float x, y, z; + uint32 rgba; + float u, v; + }; + int vcount; + Vertex *verts; +}; + +// For regular non blended drawing. No alpha, no alpha test. +extern LAMEBuffer buffer; + +// The two blend buffers could be combined using premultiplied alpha. +// But who cares. + +// For regular blended drawing. Standard alpha, no alpha test. +extern LAMEBuffer topbuffer; + +#endif //__LAMEBUFFER_H__ diff --git a/gfx_es2/CMakeLists.txt b/gfx_es2/CMakeLists.txt new file mode 100644 index 0000000000..d412efb163 --- /dev/null +++ b/gfx_es2/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SRCS + draw_buffer.cpp + fbo.cpp + glsl_program.cpp) + +set(SRCS ${SRCS}) + +add_library(gfx_es2 STATIC ${SRCS}) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) diff --git a/gfx_es2/draw_buffer.cpp b/gfx_es2/draw_buffer.cpp new file mode 100644 index 0000000000..6b92bf6b3e --- /dev/null +++ b/gfx_es2/draw_buffer.cpp @@ -0,0 +1,287 @@ +#ifdef ANDROID +#include +#include +#else +#include +#if defined(__APPLE__) +#include +#else +#include +#endif +#endif + +#include +#include "base/logging.h" +#include "math/math_util.h" +#include "gfx_es2/draw_buffer.h" +#include "gfx_es2/glsl_program.h" +#include "gfx/texture_atlas.h" +#include "gfx/gl_debug_log.h" + +DrawBuffer::DrawBuffer() : count_(0) { + // Enough? + verts_ = new Vertex[5000]; + fontscalex = 1.0f; + fontscaley = 1.0f; +} +DrawBuffer::~DrawBuffer() { + delete [] verts_; +} + +void DrawBuffer::Begin(DrawBufferMode dbmode) { + count_ = 0; + mode_ = dbmode; +} + +void DrawBuffer::End() { + // Currently does nothing, but call it! +} + +void DrawBuffer::Flush(const GLSLProgram *program, bool set_blend_state) { + if (count_ == 0) + return; + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + if (set_blend_state) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + glUniform1i(program->sampler0, 0); + glEnableVertexAttribArray(program->a_position); + glEnableVertexAttribArray(program->a_color); + if (program->a_texcoord0 != -1) + glEnableVertexAttribArray(program->a_texcoord0); + GL_CHECK(); + glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), &verts_[0].x); + glVertexAttribPointer(program->a_color, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), &verts_[0].r); + if (program->a_texcoord0 != -1) + glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), &verts_[0].u); + glDrawArrays(mode_ == DBMODE_LINES ? GL_LINES : GL_TRIANGLES, 0, count_); + GL_CHECK(); + glDisableVertexAttribArray(program->a_position); + glDisableVertexAttribArray(program->a_color); + if (program->a_texcoord0 != -1) + glDisableVertexAttribArray(program->a_texcoord0); + GL_CHECK(); + + count_ = 0; +} + +void DrawBuffer::V(float x, float y, float z, uint32 color, float u, float v) { + Vertex *vert = &verts_[count_++]; + vert->x = x; + vert->y = y; + vert->z = z; + // todo: speedup rgba here + vert->r = color & 0xFF; + vert->g = (color >> 8) & 0xFF; + vert->b = (color >> 16) & 0xFF; + vert->a = (color >> 24) & 0xFF; + vert->u = u; + vert->v = v; +} + +void DrawBuffer::Rect(float x, float y, float w, float h, uint32 color, int align) { + DoAlign(align, &x, &y, w, h); + RectVGradient(x, y, w, h, color, color); +} + +void DrawBuffer::RectVGradient(float x, float y, float w, float h, uint32 colorTop, uint32 colorBottom) { + V(x, y, 0, colorTop, 0, 0); + V(x + w, y, 0, colorTop, 1, 0); + V(x + w, y + h, 0, colorBottom, 1, 1); + V(x, y, 0, colorTop, 0, 0); + V(x + w, y + h, 0, colorBottom, 1, 1); + V(x, y + h, 0, colorBottom, 0, 1); +} + +void DrawBuffer::MultiVGradient(float x, float y, float w, float h, GradientStop *stops, int numStops) { + for (int i = 0; i < numStops - 1; i++) { + float t0 = stops[i].t, t1 = stops[i+1].t; + uint32_t c0 = stops[i].t, c1 = stops[i+1].t; + RectVGradient(x, y + h * t0, w, h * (t1 - t0), c0, c1); + } +} + +void DrawBuffer::Rect(float x, float y, float w, float h, + float u, float v, float uw, float uh, + uint32 color) { + V(x, y, 0, color, u, v); + V(x + w, y, 0, color, u + uw, v); + V(x + w, y + h, 0, color, u + uw, v + uh); + V(x, y, 0, color, u, v); + V(x + w, y + h, 0, color, u + uw, v + uh); + V(x, y + h, 0, color, u, v + uh); +} + +void DrawBuffer::MeasureImage(int atlas_image, float *w, float *h) { + const AtlasImage &image = atlas->images[atlas_image]; + *w = (float)image.w; + *h = (float)image.h; +} + +void DrawBuffer::DrawImage(int atlas_image, float x, float y, float scale, Color color, int align) { + const AtlasImage &image = atlas->images[atlas_image]; + float w = (float)image.w * scale; + float h = (float)image.h * scale; + if (align & ALIGN_HCENTER) x -= w / 2; + if (align & ALIGN_RIGHT) x -= w; + if (align & ALIGN_VCENTER) y -= h / 2; + if (align & ALIGN_BOTTOM) y -= h; + DrawImageStretch(atlas_image, x, y, x + w, y + h, color); +} + +void DrawBuffer::DrawImageStretch(int atlas_image, float x1, float y1, float x2, float y2, Color color) { + const AtlasImage &image = atlas->images[atlas_image]; + V(x1, y1, color, image.u1, image.v1); + V(x2, y1, color, image.u2, image.v1); + V(x2, y2, color, image.u2, image.v2); + V(x1, y1, color, image.u1, image.v1); + V(x2, y2, color, image.u2, image.v2); + V(x1, y2, color, image.u1, image.v2); +} + +// TODO: make into arc +void DrawBuffer::Circle(float xc, float yc, float radius, float thickness, int segments, float startAngle, uint32 color, float u_mul) { + float angleDelta = PI * 2 / segments; + float uDelta = 1.0f / segments; + float t2 = thickness / 2.0f; + float r1 = radius + t2; + float r2 = radius - t2; + for (int i = 0; i < segments + 1; i++) { + float angle1 = i * angleDelta; + float angle2 = (i + 1) * angleDelta; + float u1 = u_mul * i * uDelta; + float u2 = u_mul * (i + 1) * uDelta; + // TODO: get rid of one pair of cos/sin per loop, can reuse + float c1 = cosf(angle1), s1 = sinf(angle1), c2 = cosf(angle2), s2 = sinf(angle2); + const float x[4] = {c1 * r1 + xc, c2 * r1 + xc, c1 * r2 + xc, c2 * r2 + xc}; + const float y[4] = {s1 * r1 + yc, s2 * r1 + yc, s1 * r2 + yc, s2 * r2 + yc}; + V(x[0], y[0], color, u1, 0); + V(x[1], y[1], color, u2, 0); + V(x[2], y[2], color, u1, 1); + V(x[1], y[1], color, u2, 0); + V(x[3], y[3], color, u2, 1); + V(x[2], y[2], color, u1, 1); + } +} + +void DrawBuffer::DrawTexRect(float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, Color color) { + V(x1, y1, color, u1, v1); + V(x2, y1, color, u2, v1); + V(x2, y2, color, u2, v2); + V(x1, y1, color, u1, v1); + V(x2, y2, color, u2, v2); + V(x1, y2, color, u1, v2); +} + +void DrawBuffer::DrawImage4Grid(int atlas_image, float x1, float y1, float x2, float y2, Color color, float corner_scale) { + const AtlasImage &image = atlas->images[atlas_image]; + + float um = (image.u2 - image.u1) * 0.5f; + float vm = (image.v2 - image.v1) * 0.5f; + float iw2 = (image.w * 0.5f) * corner_scale; + float ih2 = (image.h * 0.5f) * corner_scale; + float xa = x1 + iw2; + float xb = x2 - iw2; + float ya = y1 + ih2; + float yb = y2 - ih2; + float u1 = image.u1, v1 = image.v1, u2 = image.u2, v2 = image.v2; + // Top row + DrawTexRect(x1, y1, xa, ya, u1, v1, um, vm, color); + DrawTexRect(xa, y1, xb, ya, um, v1, um, vm, color); + DrawTexRect(xb, y1, x2, ya, um, v1, u2, vm, color); + // Middle row + DrawTexRect(x1, ya, xa, yb, u1, vm, um, vm, color); + DrawTexRect(xa, ya, xb, yb, um, vm, um, vm, color); + DrawTexRect(xb, ya, x2, yb, um, vm, u2, vm, color); + // Bottom row + DrawTexRect(x1, yb, xa, y2, u1, vm, um, v2, color); + DrawTexRect(xa, yb, xb, y2, um, vm, um, v2, color); + DrawTexRect(xb, yb, x2, y2, um, vm, u2, v2, color); +} + +void DrawBuffer::DrawImage2GridH(int atlas_image, float x1, float y1, float x2, Color color, float corner_scale) { + const AtlasImage &image = atlas->images[atlas_image]; + float um = (image.u1 + image.u2) * 0.5f; + float iw2 = (image.w * 0.5f) * corner_scale; + float xa = x1 + iw2; + float xb = x2 - iw2; + float u1 = image.u1, v1 = image.v1, u2 = image.u2, v2 = image.v2; + float y2 = y1 + image.h; + DrawTexRect(x1, y1, xa, y2, u1, v1, um, v2, color); + DrawTexRect(xa, y1, xb, y2, um, v1, um, v2, color); + DrawTexRect(xb, y1, x2, y2, um, v1, u2, v2, color); +} + +void DrawBuffer::MeasureText(int font, const char *text, float *w, float *h) { + const AtlasFont &atlasfont = *atlas->fonts[font]; + unsigned char cval; + float wacc = 0, maxh = 0; + int lines = 1; + while ((cval = *text++) != '\0') { + if (cval < 32) continue; + if (cval > 127) continue; + if (cval == '\n') { + wacc = 0; + lines++; + } + AtlasChar c = atlasfont.chars[cval - 32]; + wacc += c.wx * fontscalex; + } + *w = wacc; + *h = atlasfont.height * fontscaley * lines; +} + +void DrawBuffer::DrawTextShadow(int font, const char *text, float x, float y, Color color, int flags) { + uint32_t alpha = (color >> 1) & 0xFF000000; + DrawText(font, text, x + 2, y + 2, alpha, flags); + DrawText(font, text, x, y, color, flags); +} + +void DrawBuffer::DoAlign(int align, float *x, float *y, float w, float h) { + if (align & ALIGN_HCENTER) *x -= w / 2; + if (align & ALIGN_RIGHT) *x -= w; + if (align & ALIGN_VCENTER) *y -= h / 2; + if (align & ALIGN_BOTTOM) *y -= h; +} + +void DrawBuffer::DrawText(int font, const char *text, float x, float y, Color color, int flags) { + const AtlasFont &atlasfont = *atlas->fonts[font]; + unsigned char cval; + float w, h; + MeasureText(font, text, &w, &h); + if (flags) { + DoAlign(flags, &x, &y, w, h); + } + y+=atlasfont.ascend*fontscaley; + float sx = x; + while ((cval = *text++) != '\0') { + if (cval == '\n') { + y += atlasfont.height * fontscaley; + x = sx; + continue; + } + if (cval < 32) continue; + if (cval > 127) continue; + AtlasChar c = atlasfont.chars[cval - 32]; + float cx1 = x + c.ox * fontscalex; + float cy1 = y + c.oy * fontscaley; + float cx2 = x + (c.ox + c.pw) * fontscalex; + float cy2 = y + (c.oy + c.ph) * fontscaley; + V(cx1, cy1, color, c.sx, c.sy); + V(cx2, cy1, color, c.ex, c.sy); + V(cx2, cy2, color, c.ex, c.ey); + V(cx1, cy1, color, c.sx, c.sy); + V(cx2, cy2, color, c.ex, c.ey); + V(cx1, cy2, color, c.sx, c.ey); + x += c.wx * fontscalex; + } +} + +void DrawBuffer::EnableBlend(bool enable) { + if (enable) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); +} \ No newline at end of file diff --git a/gfx_es2/draw_buffer.h b/gfx_es2/draw_buffer.h new file mode 100644 index 0000000000..89ea949f4e --- /dev/null +++ b/gfx_es2/draw_buffer.h @@ -0,0 +1,110 @@ +#pragma once + +#include "base/basictypes.h" +#include "base/color.h" + +struct Atlas; + +enum { + ALIGN_LEFT = 0, + ALIGN_RIGHT = 16, + ALIGN_TOP = 0, + ALIGN_BOTTOM = 1, + ALIGN_HCENTER = 4, + ALIGN_VCENTER = 8, + ALIGN_VBASELINE = 32, // text only, possibly not yet working + + ALIGN_TOPLEFT = ALIGN_TOP | ALIGN_LEFT, + ALIGN_TOPRIGHT = ALIGN_TOP | ALIGN_RIGHT, + ALIGN_BOTTOMLEFT = ALIGN_BOTTOM | ALIGN_LEFT, + ALIGN_BOTTOMRIGHT = ALIGN_BOTTOM | ALIGN_RIGHT, +}; + +struct GLSLProgram; + +enum DrawBufferMode { + DBMODE_NORMAL = 0, + DBMODE_LINES = 1 +}; + +struct GradientStop +{ + float t; + uint32_t color; +}; + +// Similar to QuadBuffer but only uses a vertex array that it keeps +// around. +class DrawBuffer { + public: + DrawBuffer(); + ~DrawBuffer(); + + void Begin(DrawBufferMode mode = DBMODE_NORMAL); + void End(); // Currently does nothing, but call it! + + int Count() const { return count_; } + + void Flush(const GLSLProgram *program, bool set_blend_state=true); + void Rect(float x, float y, float w, float h, uint32 color, int align = ALIGN_TOPLEFT); + void RectVGradient(float x, float y, float w, float h, uint32 colorTop, uint32 colorBottom); + + void MultiVGradient(float x, float y, float w, float h, GradientStop *stops, int numStops); + + void RectCenter(float x, float y, float w, float h, uint32 color) { + Rect(x - w/2, y - h/2, w, h, color); + } + void Rect(float x, float y, float w, float h, + float u, float v, float uw, float uh, uint32 color); + void V(float x, float y, float z, uint32 color, float u, float v); + void V(float x, float y, uint32 color, float u, float v) { + V(x, y, 0.0f, color, u, v); + } + + void Circle(float x, float y, float radius, float thickness, int segments, float startAngle, uint32 color, float u_mul); + + // New drawing APIs + + // Must call this before you use any functions with atlas_image etc. + void SetAtlas(const Atlas *_atlas) { + atlas = _atlas; + } + void MeasureImage(int atlas_image, float *w, float *h); + void DrawImage(int atlas_image, float x, float y, float scale, Color color = COLOR(0xFFFFFF), int align = ALIGN_TOPLEFT); + void DrawImageStretch(int atlas_image, float x1, float y1, float x2, float y2, Color color = COLOR(0xFFFFFF)); + void DrawTexRect(float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, Color color); + // Results in 18 triangles. Kind of expensive for a button. + void DrawImage4Grid(int atlas_image, float x1, float y1, float x2, float y2, Color color = COLOR(0xFFFFFF), float corner_scale = 1.0); + // This is only 6 triangles, much cheaper. + void DrawImage2GridH(int atlas_image, float x1, float y1, float x2, Color color = COLOR(0xFFFFFF), float scale = 1.0); + + void MeasureText(int font, const char *text, float *w, float *h); + void DrawText(int font, const char *text, float x, float y, Color color = 0xFFFFFFFF, int flags = 0); + void DrawTextShadow(int font, const char *text, float x, float y, Color color = 0xFFFFFFFF, int flags = 0); + + void RotateSprite(int atlas_entry, float x, float y, float angle, float scale, Color color); + void SetFontScale(float xs, float ys) { + fontscalex = xs; + fontscaley = ys; + } + + // Utility to avoid having to include gl.h just for this in UI code. + void EnableBlend(bool enable); + + private: + void DoAlign(int align, float *x, float *y, float w, float h); + struct Vertex { + float x, y, z; + uint8 r, g, b, a; + float u, v; + }; + + Vertex *verts_; + int count_; + DrawBufferMode mode_; + const Atlas *atlas; + + float fontscalex; + float fontscaley; +}; + diff --git a/gfx_es2/fbo.cpp b/gfx_es2/fbo.cpp new file mode 100644 index 0000000000..311abc3cd0 --- /dev/null +++ b/gfx_es2/fbo.cpp @@ -0,0 +1,90 @@ +#ifdef ANDROID +#include +#include +#else +#include +#if defined(__APPLE__) +#include +#else +#include +#endif +#endif + +#include "base/logging.h" +#include "gfx_es2/fbo.h" + +struct FBO { + GLuint handle; + GLuint color_texture; + GLuint z_stencil_buffer; + + int width; + int height; +}; + +FBO *fbo_create(int width, int height, int num_color_textures, bool z_stencil) { + FBO *fbo = new FBO(); + fbo->width = width; + fbo->height = height; + glGenFramebuffers(1, &fbo->handle); + glGenTextures(1, &fbo->color_texture); + glGenRenderbuffers(1, &fbo->z_stencil_buffer); + + // Create the surfaces. + glBindTexture(GL_TEXTURE_2D, fbo->color_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height); + + // Bind it all together + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo->handle); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + switch(status) { + case GL_FRAMEBUFFER_COMPLETE_EXT: + ILOG("Framebuffer verified complete."); + break; + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + ELOG("Framebuffer format not supported"); + break; + default: + FLOG("Other framebuffer error: %i", status); + break; + } + // Unbind state we don't need + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + return fbo; +} + +void fbo_unbind() { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +} + +void fbo_bind_as_render_target(FBO *fbo) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo->handle); +} + +void fbo_bind_for_read(FBO *fbo) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo->handle); +} + +void fbo_bind_color_as_texture(FBO *fbo, int color) { + glBindTexture(GL_TEXTURE_2D, fbo->color_texture); +} + +void fbo_destroy(FBO *fbo) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo->handle); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo->handle); + glDeleteTextures(1, &fbo->color_texture); + glDeleteRenderbuffers(1, &fbo->z_stencil_buffer); +} diff --git a/gfx_es2/fbo.h b/gfx_es2/fbo.h new file mode 100644 index 0000000000..648d255366 --- /dev/null +++ b/gfx_es2/fbo.h @@ -0,0 +1,20 @@ +#pragma once + +// Simple wrapper around FBO functionality. + +struct FBO; + +// Creates a simple FBO with a RGBA32 color buffer stored in a texture, and +// optionally an accompanying Z/stencil buffer. +// No mipmap support. +// num_color_textures must be 1 for now. +// you lose bound texture state. +FBO *fbo_create(int width, int height, int num_color_textures, bool z_stencil); + +// These functions should be self explanatory. +void fbo_bind_as_render_target(FBO *fbo); +// color must be 0, for now. +void fbo_bind_color_as_texture(FBO *fbo, int color); +void fbo_bind_for_read(FBO *fbo); +void fbo_unbind(); +void fbo_destroy(FBO *fbo); diff --git a/gfx_es2/glsl_program.cpp b/gfx_es2/glsl_program.cpp new file mode 100644 index 0000000000..20b340f3bb --- /dev/null +++ b/gfx_es2/glsl_program.cpp @@ -0,0 +1,203 @@ +#if defined(ANDROID) +#include +#include +typedef char GLchar; +#else +#include +#if defined(__APPLE__) +#include +#else +#include +#endif +#endif + +#include + +#include +#include +#include + +#include "base/logging.h" +#include "file/vfs.h" +#include "gfx_es2/glsl_program.h" + +static std::set active_programs; + +bool CompileShader(const char *source, GLuint shader, const char *filename) { + glShaderSource(shader, 1, &source, NULL); + glCompileShader(shader); + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { +#define MAX_INFO_LOG_SIZE 2048 + GLchar infoLog[MAX_INFO_LOG_SIZE]; + GLsizei len; + glGetShaderInfoLog(shader, MAX_INFO_LOG_SIZE, &len, infoLog); + infoLog[len] = '\0'; + ELOG("Error in shader compilation of %s!\n", filename); + ELOG("Info log: %s\n", infoLog); + ELOG("Shader source:\n%s\n", (const char *)source); + exit(1); + return false; + } + return true; +} + +GLSLProgram *glsl_create(const char *vshader, const char *fshader) { + GLSLProgram *program = new GLSLProgram(); + program->program_ = 0; + program->vsh_ = 0; + program->fsh_ = 0; + strcpy(program->name, vshader + strlen(vshader) - 16); + strcpy(program->vshader_filename, vshader); + strcpy(program->fshader_filename, fshader); + if (glsl_recompile(program)) { + active_programs.insert(program); + } + register_gl_resource_holder(program); + return program; +} + +bool glsl_up_to_date(GLSLProgram *program) { + struct stat vs, fs; + stat(program->vshader_filename, &vs); + stat(program->fshader_filename, &fs); + if (vs.st_mtime != program->vshader_mtime || + fs.st_mtime != program->fshader_mtime) { + return false; + } else { + return true; + } +} + +void glsl_refresh() { + ILOG("glsl_refresh()"); + for (std::set::const_iterator iter = active_programs.begin(); + iter != active_programs.end(); ++iter) { + if (!glsl_up_to_date(*iter)) { + glsl_recompile(*iter); + } + } +} + +bool glsl_recompile(GLSLProgram *program) { + struct stat vs, fs; + stat(program->vshader_filename, &vs); + stat(program->fshader_filename, &fs); + program->vshader_mtime = vs.st_mtime; + program->fshader_mtime = fs.st_mtime; + + size_t sz; + char *vsh_src = (char *)VFSReadFile(program->vshader_filename, &sz); + if (!vsh_src) { + ELOG("File missing: %s", vsh_src); + return false; + } + char *fsh_src = (char *)VFSReadFile(program->fshader_filename, &sz); + if (!fsh_src) { + ELOG("File missing: %s", fsh_src); + delete [] vsh_src; + return false; + } + + GLuint vsh = glCreateShader(GL_VERTEX_SHADER); + const GLchar *vsh_str = (const GLchar *)(vsh_src); + if (!CompileShader(vsh_str, vsh, program->vshader_filename)) { + return false; + } + delete [] vsh_src; + + const GLchar *fsh_str = (const GLchar *)(fsh_src); + GLuint fsh = glCreateShader(GL_FRAGMENT_SHADER); + if (!CompileShader(fsh_str, fsh, program->fshader_filename)) { + glDeleteShader(vsh); + return false; + } + delete [] fsh_src; + + GLuint prog = glCreateProgram(); + glAttachShader(prog, vsh); + glAttachShader(prog, fsh); + + glLinkProgram(prog); + + GLint linkStatus; + glGetProgramiv(prog, GL_LINK_STATUS, &linkStatus); + if (linkStatus != GL_TRUE) { + GLint bufLength = 0; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &bufLength); + if (bufLength) { + char* buf = new char[bufLength]; + glGetProgramInfoLog(prog, bufLength, NULL, buf); + FLOG("Could not link program:\n %s", buf); + delete [] buf; // we're dead! + } + glDeleteShader(vsh); + glDeleteShader(fsh); + return false; + } + + // Destroy the old program, if any. + if (program->program_) { + glDeleteProgram(program->program_); + } + + program->program_ = prog; + program->vsh_ = vsh; + program->fsh_ = vsh; + + program->sampler0 = glGetUniformLocation(program->program_, "sampler0"); + program->sampler1 = glGetUniformLocation(program->program_, "sampler1"); + + program->a_position = glGetAttribLocation(program->program_, "a_position"); + program->a_color = glGetAttribLocation(program->program_, "a_color"); + program->a_normal = glGetAttribLocation(program->program_, "a_normal"); + program->a_texcoord0 = glGetAttribLocation(program->program_, "a_texcoord0"); + program->a_texcoord1 = glGetAttribLocation(program->program_, "a_texcoord1"); + + program->u_worldviewproj = glGetUniformLocation(program->program_, "u_worldviewproj"); + program->u_world = glGetUniformLocation(program->program_, "u_world"); + program->u_viewproj = glGetUniformLocation(program->program_, "u_viewproj"); + program->u_fog = glGetUniformLocation(program->program_, "u_fog"); + program->u_sundir = glGetUniformLocation(program->program_, "u_sundir"); + program->u_camerapos = glGetUniformLocation(program->program_, "u_camerapos"); + + //ILOG("Shader compilation success: %s %s", + // program->vshader_filename, + // program->fshader_filename); + return true; +} + +void GLSLProgram::GLLost() { + ILOG("Restoring GLSL program %s/%s", this->vshader_filename, this->fshader_filename); + this->program_ = 0; + this->vsh_ = 0; + this->fsh_ = 0; + glsl_recompile(this); +} + + +int glsl_attrib_loc(const GLSLProgram *program, const char *name) { + return glGetAttribLocation(program->program_, name); +} + +int glsl_uniform_loc(const GLSLProgram *program, const char *name) { + return glGetUniformLocation(program->program_, name); +} + +void glsl_destroy(GLSLProgram *program) { + unregister_gl_resource_holder(program); + glDeleteShader(program->vsh_); + glDeleteShader(program->fsh_); + glDeleteProgram(program->program_); + active_programs.erase(program); + delete program; +} + +void glsl_bind(const GLSLProgram *program) { + glUseProgram(program->program_); +} + +void glsl_unbind() { + glUseProgram(0); +} diff --git a/gfx_es2/glsl_program.h b/gfx_es2/glsl_program.h new file mode 100644 index 0000000000..55d3531d2e --- /dev/null +++ b/gfx_es2/glsl_program.h @@ -0,0 +1,72 @@ +#ifndef _RENDER_UTIL +#define _RENDER_UTIL + +#ifdef ANDROID +#include +#include +#else +#include +#if defined(__APPLE__) +#include +#else +#include +#endif +#endif + +#include + +#include "gfx/gl_lost_manager.h" + +// Represent a compiled and linked vshader/fshader pair. +// A just-constructed object is valid but cannot be used as a shader program, meaning that +// yes, you can declare these as globals if you like. +struct GLSLProgram : public GfxResourceHolder { + char name[16]; + char vshader_filename[256]; + char fshader_filename[256]; + time_t vshader_mtime; + time_t fshader_mtime; + + // Locations to some common uniforms. Hardcoded for speed. + GLint sampler0; + GLint sampler1; + GLint u_worldviewproj; + GLint u_world; + GLint u_viewproj; + GLint u_fog; // rgb = color, a = density + GLint u_sundir; + GLint u_camerapos; + + GLint a_position; + GLint a_color; + GLint a_normal; + GLint a_texcoord0; + GLint a_texcoord1; + + // Private to the implementation, do not touch + GLuint vsh_; + GLuint fsh_; + GLuint program_; + + void GLLost(); +}; + +GLSLProgram *glsl_create(const char *vshader_file, const char *fshader_file); +void glsl_destroy(GLSLProgram *program); + +// If recompilation of the program fails, the program is untouched and error messages +// are logged and the function returns false. +bool glsl_recompile(GLSLProgram *program); +void glsl_bind(const GLSLProgram *program); +void glsl_unbind(); +int glsl_attrib_loc(const GLSLProgram *program, const char *name); +int glsl_uniform_loc(const GLSLProgram *program, const char *name); + +// Expensive, try to only call this once per second or so and only when developing. +// fstat-s all the source files of all the shaders to see if they +// should be recompiled, and recompiles them if so. +void glsl_refresh(); + +// Use glUseProgramObjectARB(NULL); to unset. + +#endif // _RENDER_UTIL diff --git a/gfx_es2/vertex_format.cpp b/gfx_es2/vertex_format.cpp new file mode 100644 index 0000000000..335029437d --- /dev/null +++ b/gfx_es2/vertex_format.cpp @@ -0,0 +1,71 @@ +#include "base/logging.h" +#include "gfx_es2/glsl_program.h" +#include "gfx_es2/vertex_format.h" + +static const GLuint formatLookup[16] = { + GL_FLOAT, + 0, //GL_HALF_FLOAT_EXT, + GL_UNSIGNED_SHORT, + GL_UNSIGNED_BYTE, + 0, //GL_UNSIGNED_INT_10_10_10_2, + 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void SetVertexFormat(const GLSLProgram *program, uint32_t vertexFormat) { + // First special case our favorites + if (vertexFormat == (POS_FLOAT | NRM_FLOAT | UV0_FLOAT)) { + const int vertexSize = 3*4 + 3*4 + 2*4; + glUniform1i(program->sampler0, 0); + glEnableVertexAttribArray(program->a_position); + glEnableVertexAttribArray(program->a_normal); + glEnableVertexAttribArray(program->a_texcoord0); + glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, vertexSize, (void *)0); + glVertexAttribPointer(program->a_normal, 3, GL_FLOAT, GL_FALSE, vertexSize, (void *)12); + glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, vertexSize, (void *)24); + return; + } + + // Then have generic code here. + + + int vertexSize = 0; + + FLOG("TODO: Write generic code."); + + if (vertexFormat & UV0_MASK) { + glUniform1i(program->sampler0, 0); + } + + glEnableVertexAttribArray(program->a_position); + glVertexAttribPointer(program->a_position, 3, GL_FLOAT, GL_FALSE, vertexSize, (void *)0); + if (vertexFormat & NRM_MASK) { + glEnableVertexAttribArray(program->a_normal); + glVertexAttribPointer(program->a_normal, 3, GL_FLOAT, GL_FALSE, vertexSize, (void *)12); + } + if (vertexFormat & UV0_MASK) { + glEnableVertexAttribArray(program->a_texcoord0); + glVertexAttribPointer(program->a_texcoord0, 2, GL_FLOAT, GL_FALSE, vertexSize, (void *)24); + } + if (vertexFormat & UV1_MASK) { + glEnableVertexAttribArray(program->a_texcoord1); + glVertexAttribPointer(program->a_texcoord1, 2, GL_FLOAT, GL_FALSE, vertexSize, (void *)24); + } + if (vertexFormat & RGBA_MASK) { + glEnableVertexAttribArray(program->a_color); + glVertexAttribPointer(program->a_color, 4, GL_FLOAT, GL_FALSE, vertexSize, (void *)28); + } +} + +// TODO: Save state so that we can get rid of this. +void UnsetVertexFormat(const GLSLProgram *program, uint32 vertexFormat) { + glDisableVertexAttribArray(program->a_position); + if (vertexFormat & NRM_MASK) + glDisableVertexAttribArray(program->a_normal); + if (vertexFormat & UV0_MASK) + glDisableVertexAttribArray(program->a_texcoord0); + if (vertexFormat & UV1_MASK) + glDisableVertexAttribArray(program->a_texcoord1); + if (vertexFormat & RGBA_MASK) + glDisableVertexAttribArray(program->a_color); +} diff --git a/gfx_es2/vertex_format.h b/gfx_es2/vertex_format.h new file mode 100644 index 0000000000..d4c844c0ba --- /dev/null +++ b/gfx_es2/vertex_format.h @@ -0,0 +1,49 @@ +#pragma once + +#include "base/basictypes.h" + +// Vertex format flags +enum VtxFmt { + POS_FLOAT = 1, + POS_FLOAT16 = 2, + POS_UINT16 = 3, + POS_UINT8 = 4, + POS_101010 = 5, + + NRM_FLOAT = 1 << 4, + NRM_FLOAT16 = 2 << 4, + NRM_SINT16 = 3 << 4, + NRM_UINT8 = 4 << 4, + NRM_101010 = 5 << 4, + + TANGENT_FLOAT = 1 << 8, + //.... + + UV0_NONE = 1 << 12, + UV0_FLOAT = 1 << 12, + // .... + UV1_NONE = 1 << 16, + UV1_FLOAT = 1 << 16, + + RGBA_NONE = 0 << 20, + RGBA_FLOAT = 1 << 20, + RGBA_FLOAT16 = 2 << 20, + RGBA_UINT16 = 3 << 20, + RGBA_UINT8 = 4 << 20, + RGBA_101010 = 5 << 20, + + POS_MASK = 0x0000000F, + NRM_MASK = 0x000000F0, + TANGENT_MASK = 0x00000F00, + UV0_MASK = 0x0000F000, + UV1_MASK = 0x000F0000, + RGBA_MASK = 0x00F00000, + + // Can add more here, such as a generic AUX or something. Don't know what to use it for though. Hardness for cloth sim? +}; + +struct GLSLProgram; + +// When calling this, the relevant vertex buffer must be bound to GL_ARRAY_BUFFER. +void SetVertexFormat(const GLSLProgram *program, uint32_t format); +void UnsetVertexFormat(const GLSLProgram *program, uint32_t format); diff --git a/image/CMakeLists.txt b/image/CMakeLists.txt new file mode 100644 index 0000000000..c835ef7134 --- /dev/null +++ b/image/CMakeLists.txt @@ -0,0 +1,14 @@ +set(SRCS + png_load.cpp + zim_load.cpp + zim_save.cpp +) + +set(SRCS ${SRCS}) + +add_library(image STATIC ${SRCS}) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) + diff --git a/image/build.scons b/image/build.scons new file mode 100644 index 0000000000..edc7163ec3 --- /dev/null +++ b/image/build.scons @@ -0,0 +1,15 @@ +# Bring in the environment to use for this component (boilerplate). +Import('env') + +# This is the list of input source files to the mandelbrot_gen library. Headers +# will be detected automatically. +inputs = ['png_load.cpp', + 'zim_load.cpp', + ] + +# Build these into a library. +env.ComponentLibrary('image', inputs) + +env.Append(LIBS = [ 'base', 'png', 'image', 'z', 'glog' ]) +inputs = ['zimagetool.cpp'] +env.ComponentProgram('zimagetool', inputs) diff --git a/image/png_load.cpp b/image/png_load.cpp new file mode 100644 index 0000000000..8be085d624 --- /dev/null +++ b/image/png_load.cpp @@ -0,0 +1,91 @@ +#include +#include +#include + +int pngLoad(const char *file, int *pwidth, + int *pheight, unsigned char **image_data_ptr, + bool flip) { + FILE *infile = fopen(file, "rb"); + if (!infile) { + printf("No such file: %s\n", file); + return 0; + } + /* Check for the 8-byte signature */ + char sig[8]; /* PNG signature array */ + int len = fread(sig, 1, 8, infile); + if (len != 8 || !png_check_sig((unsigned char *) sig, 8)) { + fclose(infile); + return 0; + } + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + fclose(infile); + return 4; /* out of memory */ + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); + fclose(infile); + return 4; /* out of memory */ + } + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(infile); + return 0; + } + + png_init_io(png_ptr, infile); + png_set_sig_bytes(png_ptr, 8); // we already checked the sig bytes + png_read_info(png_ptr, info_ptr); + int bit_depth=0; + int color_type=0; + png_uint_32 width=0, height=0; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); + *pwidth = (int)width; + *pheight = (int)height; + // Set up some transforms. Always load RGBA. + if (color_type & PNG_COLOR_MASK_ALPHA) { + // png_set_strip_alpha(png_ptr); + } + if (bit_depth > 8) { + png_set_strip_16(png_ptr); + } + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + png_set_gray_to_rgb(png_ptr); + } + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_set_palette_to_rgb(png_ptr); + } + if (color_type == PNG_COLOR_TYPE_RGB) { + png_set_filler(png_ptr, 255, PNG_FILLER_AFTER); + } + + // Update the png info struct. + png_read_update_info(png_ptr, info_ptr); + unsigned long rowbytes = png_get_rowbytes(png_ptr, info_ptr); + unsigned char *image_data = NULL; /* raw png image data */ + if ((image_data = (unsigned char *) malloc(rowbytes * height))==NULL) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return 4; + } + png_bytepp row_pointers = NULL; + if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + free(image_data); + image_data = NULL; + return 4; + } + if (flip) { + for (unsigned long i = 0; i < height; ++i) + row_pointers[height - 1 - i] = (png_byte *)(image_data + i*rowbytes); + } else { + for (unsigned long i = 0; i < height; ++i) + row_pointers[i] = (png_byte *)(image_data + i*rowbytes); + } + png_read_image(png_ptr, row_pointers); + free(row_pointers); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(infile); + *image_data_ptr = image_data; + return 1; +} diff --git a/image/png_load.h b/image/png_load.h new file mode 100644 index 0000000000..5a4ee08c54 --- /dev/null +++ b/image/png_load.h @@ -0,0 +1,11 @@ +#ifndef _PNG_LOAD_H +#define _PNG_LOAD_H + +// *image_data_ptr should be deleted with free() +// return value of 1 == success. +int pngLoad(const char *file, int *pwidth, + int *pheight, unsigned char **image_data_ptr, bool flip); + + + +#endif // _PNG_LOAD_H diff --git a/image/surface.h b/image/surface.h new file mode 100644 index 0000000000..39377b3864 --- /dev/null +++ b/image/surface.h @@ -0,0 +1,31 @@ +#ifndef _GFX_SURFACE +#define _GFX_SURFACE + +enum SurfaceFormats { + SURF_ARGB, + SURF_YUV, +}; + +struct cairo_surface_t; + +class Surface { + public: + Surface(int width, int height); + + // In case of YUV, U and V channels have half size, rounded UP. + int height() const { return height_; } + int width() const { return width_; } + int pitch() const { return pitch_; } + + int half_width() const { return (width_ + 1) >> 1; } + int half_height() const { return (height_ + 1) >> 1; } + + cairo_surface_t *CreateCairoSurface(); + private: + uint8 *data_; + int width_; + int height_; + int pitch_; +}; + +#endif diff --git a/image/zim_load.cpp b/image/zim_load.cpp new file mode 100644 index 0000000000..1cc2c406bf --- /dev/null +++ b/image/zim_load.cpp @@ -0,0 +1,139 @@ +#include +#include +#include + +#include "base/logging.h" +#include "zlib.h" +#include "image/zim_load.h" +#include "file/vfs.h" + +int ezuncompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, long nSrcLen) { + z_stream stream; + stream.next_in = (Bytef*)pSrc; + stream.avail_in = (uInt)nSrcLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != (uLong)nSrcLen) return Z_BUF_ERROR; + + uInt destlen = (uInt)*pnDestLen; + if ((uLong)destlen != (uLong)*pnDestLen) return Z_BUF_ERROR; + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + int err = inflateInit(&stream); + if (err != Z_OK) return err; + + int nExtraChunks = 0; + do { + stream.next_out = pDest; + stream.avail_out = destlen; + err = inflate(&stream, Z_FINISH); + if (err == Z_STREAM_END ) + break; + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + err = Z_DATA_ERROR; + if (err != Z_BUF_ERROR) { + inflateEnd(&stream); + return err; + } + nExtraChunks += 1; + } while (stream.avail_out == 0); + + *pnDestLen = stream.total_out; + + err = inflateEnd(&stream); + if (err != Z_OK) return err; + + return nExtraChunks ? Z_BUF_ERROR : Z_OK; +} + +static const char magic[5] = "ZIMG"; + +static unsigned int log2i(unsigned int val) { + unsigned int ret = -1; + while (val != 0) { + val >>= 1; ret++; + } + return ret; +} + +int LoadZIMPtr(uint8_t *zim, int datasize, int *width, int *height, int *flags, uint8 **image) { + if (zim[0] != 'Z' || zim[1] != 'I' || zim[2] != 'M' || zim[3] != 'G') { + ELOG("Not a ZIM file"); + return 0; + } + memcpy(width, zim + 4, 4); + memcpy(height, zim + 8, 4); + memcpy(flags, zim + 12, 4); + + int num_levels = 1; + int image_data_size[ZIM_MAX_MIP_LEVELS]; + if (*flags & ZIM_HAS_MIPS) { + num_levels = log2i(*width < *height ? *width : *height) + 1; + } + int total_data_size = 0; + for (int i = 0; i < num_levels; i++) { + if (i > 0) { + width[i] = width[i-1] / 2; + height[i] = height[i-1] / 2; + } + switch (*flags & ZIM_FORMAT_MASK) { + case ZIM_RGBA8888: + image_data_size[i] = width[i] * height[i] * 4; + break; + case ZIM_RGBA4444: + case ZIM_RGB565: + image_data_size[i] = width[i] * height[i] * 2; + break; + case ZIM_ETC1: + { + int data_width = width[i]; + int data_height = height[i]; + if (data_width < 4) data_width = 4; + if (data_height < 4) data_height = 4; + image_data_size[i] = data_width * data_height / 2; + break; + } + default: + ELOG("Invalid ZIM format %i", *flags & ZIM_FORMAT_MASK); + return 0; + } + total_data_size += image_data_size[i]; + } + + image[0] = (uint8 *)malloc(total_data_size); + for (int i = 1; i < num_levels; i++) { + image[i] = image[i-1] + image_data_size[i-1]; + } + + if (*flags & ZIM_ZLIB_COMPRESSED) { + long outlen = total_data_size; + if (Z_OK != ezuncompress(*image, &outlen, (unsigned char *)(zim + 16), datasize - 16)) { + free(*image); + *image = 0; + return 0; + } + if (outlen != total_data_size) { + ELOG("Wrong size data in ZIM: %i vs %i", (int)outlen, (int)total_data_size); + } + } else { + memcpy(*image, zim + 16, datasize - 16); + if (datasize - 16 != total_data_size) { + ELOG("Wrong size data in ZIM: %i vs %i", (int)(datasize-16), (int)total_data_size); + } + } + return num_levels; +} + +int LoadZIM(const char *filename, int *width, int *height, int *format, uint8_t **image) { + size_t size; + uint8_t *buffer = VFSReadFile(filename, &size); + if (!buffer) { + return 0; + } + int retval = LoadZIMPtr(buffer, size, width, height, format, image); + if (!retval) { + ELOG("Not a valid ZIM file: %s", filename); + } + delete [] buffer; + return retval; +} diff --git a/image/zim_load.h b/image/zim_load.h new file mode 100644 index 0000000000..b76fdc2ee3 --- /dev/null +++ b/image/zim_load.h @@ -0,0 +1,47 @@ +#pragma once + +#include "base/basictypes.h" + +// LoadZIM's responsibility: +// * Parse the ZIM format +// * Extract all mip levels so they can be uploaded to GPU +// +// * NOT convert formats to anything, except converting ETC1 to RGBA8888 when running on the PC + +// ZIM format: +// 4 byte ZIMG +// 4 byte width +// 4 byte height +// 4 byte flags +// The rest is zlibbed data. If multiple mips, they are zlibbed separately. + +// Defined flags: + +enum { + ZIM_RGBA8888 = 0, // Assumed format if no other format is set + ZIM_RGBA4444 = 1, // GL_UNSIGNED_SHORT_4_4_4_4 + ZIM_RGB565 = 2, // GL_UNSIGNED_SHORT_5_6_5 + ZIM_ETC1 = 3, + ZIM_RGB888 = 4, + ZIM_LUMINANCE_ALPHA = 5, + ZIM_LUMINANCE = 6, + ZIM_ALPHA = 7, + // There's space for plenty more formats. + ZIM_FORMAT_MASK = 15, + ZIM_HAS_MIPS = 16, // If set, assumes that a full mip chain is present. Mips are zlib-compressed individually and stored in sequence. Always half sized. + ZIM_GEN_MIPS = 32, // If set, the caller is advised to automatically generate mips. (maybe later, the ZIM lib will generate the mips for you). + ZIM_DITHER = 64, // If set, dithers during save if color reduction is necessary. + ZIM_CLAMP = 128, // Texture should default to clamp instead of wrap. + ZIM_ZLIB_COMPRESSED = 256, +}; + +// ZIM will only ever support up to 12 levels (4096x4096 max). +enum { + ZIM_MAX_MIP_LEVELS = 12, +}; + +// Delete the returned pointer using free() +// Watch out! If the image has mipmaps, multiple values will be written +// to width, height, and image, as if they were arrays, up to 12 (max texture size is 4096 which is 2^12). +int LoadZIM(const char *filename, int *width, int *height, int *flags, uint8_t **image); +int LoadZIMPtr(char *zim, int datasize, int *width, int *height, int *flags, uint8_t **image); diff --git a/image/zim_save.cpp b/image/zim_save.cpp new file mode 100644 index 0000000000..91305eed02 --- /dev/null +++ b/image/zim_save.cpp @@ -0,0 +1,247 @@ +#include +#include +#include "base/logging.h" +#include "ext/etcpack/etcpack.h" +#include "image/zim_save.h" +#include "zlib.h" + +static const char magic[5] = "ZIMG"; + +/*int num_levels = 1; + if (flags & ZIM_HAS_MIPS) { + num_levels = log2i(width > height ? width : height); + }*/ +static unsigned int log2i(unsigned int val) { + unsigned int ret = -1; + while (val != 0) { + val >>= 1; ret++; + } + return ret; +} + + +int ezcompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, long nSrcLen) { + z_stream stream; + int err; + + int nExtraChunks; + uInt destlen; + + stream.next_in = (Bytef*)pSrc; + stream.avail_in = (uInt)nSrcLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != nSrcLen) return Z_BUF_ERROR; +#endif + destlen = (uInt)*pnDestLen; + if ((uLong)destlen != (uLong)*pnDestLen) return Z_BUF_ERROR; + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, Z_DEFAULT_COMPRESSION); + if (err != Z_OK) return err; + nExtraChunks = 0; + do { + stream.next_out = pDest; + stream.avail_out = destlen; + err = deflate(&stream, Z_FINISH); + if (err == Z_STREAM_END ) + break; + if (err != Z_OK) { + deflateEnd(&stream); + return err; + } + nExtraChunks += 1; + } while (stream.avail_out == 0); + + *pnDestLen = stream.total_out; + + err = deflateEnd(&stream); + if (err != Z_OK) return err; + + return nExtraChunks ? Z_BUF_ERROR : Z_OK; +} + +inline int clamp16(int x) { if (x < 0) return 0; if (x > 15) return 15; return x; } +inline int clamp32(int x) { if (x < 0) return 0; if (x > 31) return 31; return x; } +inline int clamp64(int x) { if (x < 0) return 0; if (x > 63) return 63; return x; } + + +bool ispowerof2 (int x) { + if (!x || (x&(x-1))) + return false; + else + return true; +} + + + +void Convert(const uint8_t *image_data, int width, int height, int pitch, int flags, + uint8_t **data, int *data_size) { + // For 4444 and 565. Ordered dither matrix. looks really surprisingly good on cell phone screens at 4444. + int dith[16] = { + 1, 9, 3, 11, + 13, 5, 15, 7, + 4, 12, 2, 10, + 16, 8, 14, 6 + }; + if ((flags & ZIM_DITHER) == 0) { + for (int i = 0; i < 16; i++) { dith[i] = 8; } + } + switch (flags & ZIM_FORMAT_MASK) { + case ZIM_RGBA8888: + { + *data_size = width * height * 4; + *data = new uint8_t[width * height * 4]; + for (int y = 0; y < height; y++) { + memcpy((*data) + y * width * 4, image_data + y * pitch, width * 4); + } + break; + } + case ZIM_ETC1: { + // Check for power of 2 + if (!ispowerof2(width) || !ispowerof2(height)) { + FLOG("Image must have power of 2 dimensions, %ix%i just isn't that.", width, height); + } + // Convert RGBX to ETC1 before saving. + int blockw = width/4; + int blockh = height/4; + *data_size = blockw * blockh * 8; + *data = new uint8_t[*data_size]; +#pragma omp parallel for + for (int y = 0; y < blockh; y++) { + for (int x = 0; x < blockw; x++) { + CompressBlock(image_data + ((y * 4) * (pitch/4) + x * 4) * 4, width, + (*data) + (blockw * y + x) * 8, 1); + } + } + width = blockw * 4; + height = blockh * 4; + break; + } + case ZIM_RGBA4444: + { + *data_size = width * height * 2; + *data = new uint8_t[*data_size]; + uint16_t *dst = (uint16_t *)(*data); + int i = 0; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int dithval = dith[(x&3)+((y&0x3)<<2)] - 8; + int r = clamp16((image_data[i * 4] + dithval) >> 4); + int g = clamp16((image_data[i * 4 + 1] + dithval) >> 4); + int b = clamp16((image_data[i * 4 + 2] + dithval) >> 4); + int a = clamp16((image_data[i * 4 + 3] + dithval) >> 4); // really dither alpha? + // Note: GL_UNSIGNED_SHORT_4_4_4_4, not GL_UNSIGNED_SHORT_4_4_4_4_REV + *dst++ = (r << 12) | (g << 8) | (b << 4) | (a << 0); + i++; + } + } + break; + } + case ZIM_RGB565: + { + *data_size = width * height * 2; + *data = new uint8_t[*data_size]; + uint16_t *dst = (uint16_t *)(*data); + int i = 0; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int dithval = dith[(x&3)+((y&0x3)<<2)] - 8; + dithval = 0; + int r = clamp32((image_data[i * 4] + dithval/2) >> 3); + int g = clamp64((image_data[i * 4 + 1] + dithval/4) >> 2); + int b = clamp32((image_data[i * 4 + 2] + dithval/2) >> 3); + // Note: GL_UNSIGNED_SHORT_5_6_5, not GL_UNSIGNED_SHORT_5_6_5_REV + *dst++ = (r << 11) | (g << 5) | (b); + i++; + } + } + } + break; + + default: + ELOG("Unhandled ZIM format %i", flags & ZIM_FORMAT_MASK); + *data = 0; + *data_size = 0; + return; + } +} + +// Deletes the old buffer. +uint8_t *DownsampleBy2(const uint8_t *image, int width, int height, int pitch) { + uint8_t *out = new uint8_t[(width/2) * (height/2) * 4]; + + int degamma[256]; + int gamma[32768]; + for (int i =0; i < 256; i++) { + degamma[i] = powf((float)i / 255.0f, 1.0f/2.2f) * 8191.0f; + } + for (int i = 0; i < 32768; i++) { + gamma[i] = powf((float)i / 32764.0f, 2.2f) * 255.0f; + } + + // Let's start with something really stupid. + for (int y = 0; y < height; y+=2) { + for (int x = 0; x < width; x+=2) { + const uint8_t *tl = image + pitch * y + x*4; + const uint8_t *tr = tl + 4; + const uint8_t *bl = tl + pitch; + const uint8_t *br = bl + 4; + uint8_t *d = out + ((y/2) * ((width/2)) + x / 2) * 4; + for (int c = 0; c < 4; c++) { + d[c] = gamma[degamma[tl[c]] + degamma[tr[c]] + degamma[bl[c]] + degamma[br[c]]]; + } + } + } + return out; +} + +void SaveZIM(const char *filename, int width, int height, int pitch, int flags, const uint8_t *image_data) { + FILE *f = fopen(filename, "wb"); + fwrite(magic, 1, 4, f); + fwrite(&width, 1, 4, f); + fwrite(&height, 1, 4, f); + fwrite(&flags, 1, 4, f); + + int num_levels = 1; + if (flags & ZIM_HAS_MIPS) { + num_levels = log2i(width > height ? height : width) + 1; + } + for (int i = 0; i < num_levels; i++) { + uint8_t *data = 0; + int data_size; + Convert(image_data, width, height, pitch, flags, &data, &data_size); + if (flags & ZIM_ZLIB_COMPRESSED) { + long dest_len = data_size * 2; + uint8_t *dest = new uint8_t[dest_len]; + if (Z_OK == ezcompress(dest, &dest_len, data, data_size)) { + fwrite(dest, 1, dest_len, f); + } else { + ELOG("Zlib compression failed.\n"); + } + delete [] dest; + } else { + fwrite(data, 1, data_size, f); + } + delete [] data; + + if (i != num_levels - 1) { + uint8_t *smaller = DownsampleBy2(image_data, width, height, pitch); + if (i != 0) { + delete [] image_data; + } + image_data = smaller; + width /= 2; + height /= 2; + if ((flags & ZIM_FORMAT_MASK) == ZIM_ETC1) { + if (width < 4) width = 4; + if (height < 4) height = 4; + } + pitch = width * 4; + } + } + delete [] image_data; + fclose(f); +} diff --git a/image/zim_save.h b/image/zim_save.h new file mode 100644 index 0000000000..5b79b85149 --- /dev/null +++ b/image/zim_save.h @@ -0,0 +1,11 @@ +#pragma once + +#include "base/basictypes.h" +#include "image/zim_load.h" + +// SaveZIM's responsibility: +// * Write the ZIM format +// * Generate mipmaps if requested +// * Convert images to the requested format +// Input image is always 8888 RGBA. SaveZIM takes care of downsampling and mipmap generation. +void SaveZIM(const char *filename, int width, int height, int pitch, int format, const uint8_t *image); diff --git a/input/gesture_detector.cpp b/input/gesture_detector.cpp new file mode 100644 index 0000000000..44c92b614d --- /dev/null +++ b/input/gesture_detector.cpp @@ -0,0 +1,68 @@ +// Unfinished. +// TODO: +// Zoom gesture a la http://www.zdnet.com/blog/burnette/how-to-use-multi-touch-in-android-2-part-6-implementing-the-pinch-zoom-gesture/1847 + +#include "input/gesture_detector.h" + +namespace GestureDetector { + +struct Finger { + bool down; + float X; + float Y; + float lastX; + float lastY; + float downX; + float downY; + float deltaX; + float deltaY; + float smoothDeltaX; + float smoothDeltaY; +}; + +// State +#define MAX_FINGERS 4 + +static Finger fingers[MAX_FINGERS]; + +void update(const InputState &state) { + // Mouse / 1-finger-touch control. + if (state.mouse_buttons_down & 1) { + fingers[0].down = true; + fingers[0].downX = state.mouse_x; + fingers[0].downY = state.mouse_y; + } else { + fingers[0].down = false; + } + + fingers[0].lastX = fingers[0].X; + fingers[0].lastY = fingers[0].Y; + + // TODO: real multitouch +} + +bool down(int i, float *xdelta, float *ydelta) { + if (!fingers[i].down) { + return false; + } + *xdelta = fingers[i].downX; + *ydelta = fingers[i].downY; +} + +bool dragDistance(int i, float *xdelta, float *ydelta) { + if (!fingers[i].down) + return false; + + *xdelta = fingers[i].X - fingers[i].downX; + *ydelta = fingers[i].Y - fingers[i].downY; +} + +bool dragDelta(int i, float *xdelta, float *ydelta) { + if (!fingers[i].down) + return false; + + *xdelta = fingers[i].X - fingers[i].lastX; + *ydelta = fingers[i].Y - fingers[i].lastY; +} + +} \ No newline at end of file diff --git a/input/gesture_detector.h b/input/gesture_detector.h new file mode 100644 index 0000000000..d34bbc9159 --- /dev/null +++ b/input/gesture_detector.h @@ -0,0 +1,20 @@ +#include "input/input_state.h" + +// Mainly for detecting (multi-)touch gestures but also useable for left button mouse dragging etc. + +namespace GestureDetector +{ + void update(const InputState &state); + + bool down(int finger, float *xdown, float *ydown); + + // x/ydelta is difference from current location to the start of the drag. + // Returns true if button/finger is down, for convenience. + bool dragDistance(int finger, float *xdelta, float *ydelta); + + // x/ydelta is (smoothed?) difference from current location to the position from the last frame. + // Returns true if button/finger is down, for convenience. + bool dragDelta(int finger, float *xdelta, float *ydelta); + + +}; \ No newline at end of file diff --git a/input/input_state.h b/input/input_state.h new file mode 100644 index 0000000000..d663d52dbd --- /dev/null +++ b/input/input_state.h @@ -0,0 +1,53 @@ +#pragma once + +#include "math/lin/vec3.h" + +enum { + PAD_BUTTON_A = 1, + PAD_BUTTON_B = 2, + PAD_BUTTON_X = 4, + PAD_BUTTON_Y = 8, + PAD_BUTTON_LBUMPER = 16, + PAD_BUTTON_RBUMPER = 32, + PAD_BUTTON_START = 64, + PAD_BUTTON_BACK = 128, + PAD_BUTTON_UP = 256, + PAD_BUTTON_DOWN = 512, + PAD_BUTTON_LEFT = 1024, + PAD_BUTTON_RIGHT = 2048, +}; + +// Agglomeration of all possible inputs, and automatically computed +// deltas where applicable. +struct InputState { + // Gamepad style input + int pad_buttons; // bitfield + int pad_last_buttons; + int pad_buttons_down; // buttons just pressed this frame + int pad_buttons_up; // buttons just pressed last frame + float pad_lstick_x; + float pad_lstick_y; + float pad_rstick_x; + float pad_rstick_y; + float pad_ltrigger; + float pad_rtrigger; + + // Mouse/singletouch style input + volatile bool mouse_valid; + int mouse_x; + int mouse_y; + int mouse_buttons; + int mouse_buttons_down; + int mouse_buttons_up; + int mouse_last_buttons; + + // Accelerometer + bool accelerometer_valid; + Vec3 acc; +}; + +inline void UpdateInputState(InputState *input) { + input->pad_buttons_down = (input->pad_last_buttons ^ input->pad_buttons) & input->pad_buttons; + input->pad_buttons_up = (input->pad_last_buttons ^ input->pad_buttons) & input->pad_last_buttons; + input->pad_last_buttons = input->pad_buttons; +} diff --git a/json/CMakeLists.txt b/json/CMakeLists.txt new file mode 100644 index 0000000000..9c3ec2f57f --- /dev/null +++ b/json/CMakeLists.txt @@ -0,0 +1,15 @@ +set(SRCS + json_writer.cpp) + +set(SRCS ${SRCS}) + +add_library(jsonwriter STATIC ${SRCS}) + + + +add_executable(json_writer_test json_writer_test.cpp) +target_link_libraries(json_writer_test jsonwriter) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) diff --git a/json/json_writer.cpp b/json/json_writer.cpp new file mode 100644 index 0000000000..d626feb92d --- /dev/null +++ b/json/json_writer.cpp @@ -0,0 +1,116 @@ +#include "json/json_writer.h" + +JsonWriter::JsonWriter() { +} + +JsonWriter::~JsonWriter() { + +} + +void JsonWriter::begin() { + str_ << "{"; + stack_.push_back(StackEntry(DICT)); +} + +void JsonWriter::end() { + pop(); + str_ << "\n"; +} + +const char *JsonWriter::indent(int n) const { + static const char * const whitespace = " "; + return whitespace + (32 - n); +} + +const char *JsonWriter::indent() const { + int amount = (int)stack_.size() + 1; + amount *= 2; // 2-space indent. + return indent(amount); +} + +const char *JsonWriter::arrayIndent() const { + int amount = (int)stack_.size() + 1; + amount *= 2; // 2-space indent. + return stack_.back().first ? indent(amount) : ""; +} + +const char *JsonWriter::comma() const { + if (stack_.back().first) { + return ""; + } else { + return ","; + } +} + +const char *JsonWriter::arrayComma() const { + if (stack_.back().first) { + return "\n"; + } else { + return ", "; + } +} + +void JsonWriter::pushDict(const char *name) { + str_ << comma() << "\n" << indent() << "\"" << name << "\": {"; + stack_.push_back(StackEntry(DICT)); +} + +void JsonWriter::pushArray(const char *name) { + str_ << comma() << "\n" << indent() << "\"" << name << "\": ["; + stack_.push_back(StackEntry(ARRAY)); +} + +void JsonWriter::writeBool(bool value) { + str_ << arrayComma() << arrayIndent() << (value ? "true" : "false"); + stack_.back().first = false; +} + +void JsonWriter::writeBool(const char *name, bool value) { + str_ << comma() << "\n" << indent() << "\"" << name << "\": " << (value ? "true" : "false"); + stack_.back().first = false; +} + +void JsonWriter::writeInt(int value) { + str_ << arrayComma() << arrayIndent() << value; + stack_.back().first = false; +} + +void JsonWriter::writeInt(const char *name, int value) { + str_ << comma() << "\n" << indent() << "\"" << name << "\": " << value; + stack_.back().first = false; +} + +void JsonWriter::writeFloat(double value) { + str_ << arrayComma() << arrayIndent() << value; + stack_.back().first = false; +} + +void JsonWriter::writeFloat(const char *name, double value) { + str_ << comma() << "\n" << indent() << "\"" << name << "\": " << value; + stack_.back().first = false; +} + +void JsonWriter::writeString(const char *value) { + str_ << arrayComma() << arrayIndent() << "\"" << value << "\""; + stack_.back().first = false; +} + +void JsonWriter::writeString(const char *name, const char *value) { + str_ << comma() << "\n" << indent() << "\"" << name << "\": \"" << value << "\""; + stack_.back().first = false; +} + +void JsonWriter::pop() { + BlockType type = stack_.back().type; + stack_.pop_back(); + switch (type) { + case ARRAY: + str_ << "\n" << indent() << "]"; + break; + case DICT: + str_ << "\n" << indent() << "}"; + break; + } + if (stack_.size() > 0) + stack_.back().first = false; +} diff --git a/json/json_writer.h b/json/json_writer.h new file mode 100644 index 0000000000..5c714c15d4 --- /dev/null +++ b/json/json_writer.h @@ -0,0 +1,55 @@ +// Minimal-state ultra-minimal JSON writer. Consumes almost no memory +// apart from the string being built-up, which could easily be replaced +// with a file stream (although I've chosen not to do that just yet). +// +// Zero dependencies apart from stdlib. +// Public domain. + +#include +#include +#include + +#include "base/basictypes.h" + +class JsonWriter { + public: + JsonWriter(); + ~JsonWriter(); + void begin(); + void end(); + void pushDict(const char *name); + void pushArray(const char *name); + void writeBool(bool value); + void writeBool(const char *name, bool value); + void writeInt(int value); + void writeInt(const char *name, int value); + void writeFloat(double value); + void writeFloat(const char *name, double value); + void writeString(const char *value); + void writeString(const char *name, const char *value); + void pop(); + + std::string str() const { + return str_.str(); + } + + private: + const char *indent(int n) const; + const char *comma() const; + const char *arrayComma() const; + const char *indent() const; + const char *arrayIndent() const; + enum BlockType { + ARRAY, + DICT, + }; + struct StackEntry { + StackEntry(BlockType t) : type(t), first(true) {} + BlockType type; + bool first; + }; + std::vector stack_; + std::ostringstream str_; + + DISALLOW_COPY_AND_ASSIGN(JsonWriter); +}; diff --git a/json/json_writer_test.cpp b/json/json_writer_test.cpp new file mode 100644 index 0000000000..46875ac12b --- /dev/null +++ b/json/json_writer_test.cpp @@ -0,0 +1,25 @@ +#include "json/json_writer.h" +#include + +int main() { + JsonWriter j; + j.begin(); + j.pushDict("settings"); + j.writeInt("volume", 50); + j.writeInt("sound", 60); + j.pop(); + j.pushDict("user"); + j.writeFloat("level", 1.5); + j.writeString("name", "hello"); + j.pop(); + j.writeInt("outer", 3); + j.pushArray("ints"); + j.writeInt(3); + j.writeInt(4); + j.writeInt(6); + j.pop(); + j.writeString("yo!", "yo"); + j.end(); + std::cout << j.str(); + return 0; +} diff --git a/math/CMakeLists.txt b/math/CMakeLists.txt new file mode 100644 index 0000000000..b05acac409 --- /dev/null +++ b/math/CMakeLists.txt @@ -0,0 +1,13 @@ +set(SRCS + lin/matrix4x4.cpp + lin/vec3.cpp + lin/quat.cpp + ) + +set(SRCS ${SRCS}) + +add_library(lin STATIC ${SRCS}) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) diff --git a/math/build.scons b/math/build.scons new file mode 100644 index 0000000000..17d6c4ff4c --- /dev/null +++ b/math/build.scons @@ -0,0 +1,8 @@ +Import('env') + +inputs = [ + 'math_util.cc', +] + +env.Append(LIBS = ['gflags', 'glog', ]) +env.ComponentLibrary('math_util', inputs, COMPONENT_STATIC=True) diff --git a/math/compression.h b/math/compression.h new file mode 100644 index 0000000000..8200e52ea9 --- /dev/null +++ b/math/compression.h @@ -0,0 +1,21 @@ +#pragma once + +// WARNING : Do not use these with floating point data, especially not float16... + +template +inline void delta(T *data, int length) { + T prev = data[0]; + for (int i = 1; i < length; i++) { + T temp = data[i] - prev; + prev = data[i]; + data[i] = temp; + } +} + +template +inline void dedelta(T *data, int length) { + for (int i = 1; i < length; i++) { + data[i] += data[i - 1]; + } +} + diff --git a/math/lin/aabb.cpp b/math/lin/aabb.cpp new file mode 100644 index 0000000000..4e73b512a0 --- /dev/null +++ b/math/lin/aabb.cpp @@ -0,0 +1,272 @@ +#include "math/lin/aabb.h" +#include "math/lin/ray.h" +#include "math/lin/plane.h" + +#define NUMDIM 3 +#define RIGHT 0 +#define LEFT 1 +#define MIDDLE 2 + +namespace lin { + +static const float flt_plus_inf = -logf(0); // let's keep C and C++ compilers happy. + +AABB::AABB() : minB(0,0,0),maxB(0,0,0) { + +} + +void AABB::Add(const Vec3 &pt) { + for (int i=0; i<3; i++) + { + if (pt[i] < minB[i]) + minB[i] = pt[i]; + if (pt[i] > maxB[i]) + maxB[i] = pt[i]; + } +} + +bool AABB::Contains(const Vec3 &pt) const { + for (int i=0; i<3; i++) + { + if (pt[i] < minB[i]) + return false; + if (pt[i] > maxB[i]) + return false; + } + return true; +} + + +bool AABB::IntersectRay(const Ray &ray, float &tnear, float &tfar) const { + float tNear=-flt_plus_inf, tFar=flt_plus_inf; +//For each pair of planes P associated with X, Y, and Z do: +//(example using X planes) + for (int i=0; i<3; i++) + { + float min = minB[i]; + float max = maxB[i]; + + if (ray.dir[i] == 0.0f) //parallell! ARGH! + if (ray.origin[i] < min || ray.origin[i] > max) + return false; + //Intersect with slab + float T1 = (min - ray.origin[i]) * ray.invdir[i]; + float T2 = (max - ray.origin[i]) * ray.invdir[i]; + //Swap if necessary + if (T1 > T2) + { + float temp=T1; T1=T2; T2=temp; + } + //Save closest/farthest hits + if (T1 > tNear) tNear = T1; + if (T2 < tFar) tFar = T2; + } + + if (tNear > tFar) + return false; //missed the box + else + { + if (tFar < 0) + return false; //behind camera + tnear = tNear; + tfar = tFar; + } + return true; +} + + +// Possible orientation of the splitting plane in the interior node of the kd-tree, +// ”No axis” denotes a leaf. +enum Axes +{ + Xaxis, + Yaxis, + Zaxis, + Noaxis +}; + + + +int AABB::GetShortestAxis() const +{ + Vec3 delta = maxB-minB; + if (delta.yfabs(delta.x)) + { + if (fabs(delta.z)>fabs(delta.y)) + return 2; + else + return 1; + } + else + { + if (fabs(delta.z)>fabs(delta.x)) + return 2; + else + return 0; + } +} + + + +inline void FINDMINMAX(float x0, float x1, float x2, float &min, float &max ) +{ + min = max = x0; + if(x1max) + max=x1; + if(x2max) + max=x2; +} + +// X-tests +#define AXISTEST_X01( a, b, fa, fb ) \ + p0 = a * v0[1] - b * v0[2], p2 = a * v2[1] - b * v2[2]; \ + if (p0 < p2) { min = p0; max = p2;} else { min = p2; max = p0; } \ + rad = fa * a_BoxHalfsize[1] + fb * a_BoxHalfsize[2]; \ + if (min > rad || max < -rad) return 0; + +#define AXISTEST_X2( a, b, fa, fb ) \ + p0 = a * v0[1] - b * v0[2], p1 = a * v1[1] - b * v1[2]; \ + if (p0 < p1) { min = p0; max = p1; } else { min = p1; max = p0;} \ + rad = fa * a_BoxHalfsize[1] + fb * a_BoxHalfsize[2]; \ + if(min>rad || max<-rad) return 0; +// Y-tests +#define AXISTEST_Y02( a, b, fa, fb ) \ + p0 = -a * v0[0] + b * v0[2], p2 = -a * v2[0] + b * v2[2]; \ + if(p0 < p2) { min = p0; max = p2; } else { min = p2; max = p0; } \ + rad = fa * a_BoxHalfsize[0] + fb * a_BoxHalfsize[2]; \ + if (min > rad || max < -rad) return 0; +#define AXISTEST_Y1( a, b, fa, fb ) \ + p0 = -a * v0[0] + b * v0[2], p1 = -a * v1[0] + b * v1[2]; \ + if (p0 < p1) { min = p0; max = p1; } else { min = p1; max = p0; } \ + rad = fa * a_BoxHalfsize[0] + fb * a_BoxHalfsize[2]; \ + if (min > rad || max < -rad) return 0; +// Z-tests +#define AXISTEST_Z12( a, b, fa, fb ) \ + p1 = a * v1[0] - b * v1[1], p2 = a * v2[0] - b * v2[1]; \ + if(p2 < p1) { min = p2; max = p1; } else { min = p1; max = p2; } \ + rad = fa * a_BoxHalfsize[0] + fb * a_BoxHalfsize[1]; \ + if (min > rad || max < -rad) return 0; +#define AXISTEST_Z0( a, b, fa, fb ) \ + p0 = a * v0[0] - b * v0[1], p1 = a * v1[0] - b * v1[1]; \ + if(p0 < p1) { min = p0; max = p1; } else { min = p1; max = p0; } \ + rad = fa * a_BoxHalfsize[0] + fb * a_BoxHalfsize[1]; \ + if (min > rad || max < -rad) return 0; + +bool PlaneBoxOverlap(const Vec3& a_Normal, const Vec3& a_Vert, const Vec3& a_MaxBox ) +{ + Vec3 vmin, vmax; + for (int q = 0; q < 3; q++) + { + float v = a_Vert[q]; + if (a_Normal[q] > 0.0f) + { + vmin[q] = -a_MaxBox[q] - v; + vmax[q] = a_MaxBox[q] - v; + } + else + { + vmin[q] = a_MaxBox[q] - v; + vmax[q] = -a_MaxBox[q] - v; + } + } + if (( a_Normal * vmin) > 0.0f) + return false; + if (( a_Normal * vmax) >= 0.0f) + return true; + + return false; +} + + +bool AABB::IntersectsTriangle(const Vec3& a_V0, const Vec3& a_V1, const Vec3& a_V2 ) const +{ + Vec3 a_BoxCentre = GetMidpoint(); + Vec3 a_BoxHalfsize = GetExtents() / 2.0f; + Vec3 v0, v1, v2, normal, e0, e1, e2; + float min, max, p0, p1, p2, rad, fex, fey, fez; + v0 = a_V0 - a_BoxCentre; + v1 = a_V1 - a_BoxCentre; + v2 = a_V2 - a_BoxCentre; + e0 = v1 - v0, e1 = v2 - v1, e2 = v0 - v2; + fex = fabs(e0[0]); + fey = fabs(e0[1]); + fez = fabs(e0[2]); + AXISTEST_X01( e0[2], e0[1], fez, fey ); + AXISTEST_Y02( e0[2], e0[0], fez, fex ); + AXISTEST_Z12( e0[1], e0[0], fey, fex ); + fex = fabs(e1[0]); + fey = fabs(e1[1]); + fez = fabs(e1[2]); + AXISTEST_X01( e1[2], e1[1], fez, fey ); + AXISTEST_Y02( e1[2], e1[0], fez, fex ); + AXISTEST_Z0 ( e1[1], e1[0], fey, fex ); + fex = fabs(e2[0]); + fey = fabs(e2[1]); + fez = fabs(e2[2]); + AXISTEST_X2 ( e2[2], e2[1], fez, fey ); + AXISTEST_Y1 ( e2[2], e2[0], fez, fex ); + AXISTEST_Z12( e2[1], e2[0], fey, fex ); + FINDMINMAX( v0[0], v1[0], v2[0], min, max ); + if (min > a_BoxHalfsize[0] || max < -a_BoxHalfsize[0]) + return false; + FINDMINMAX( v0[1], v1[1], v2[1], min, max ); + if (min > a_BoxHalfsize[1] || max < -a_BoxHalfsize[1]) + return false; + FINDMINMAX( v0[2], v1[2], v2[2], min, max ); + if (min > a_BoxHalfsize[2] || max < -a_BoxHalfsize[2]) + return false; + normal = e0 % e1; + if (!PlaneBoxOverlap( normal, v0, a_BoxHalfsize )) + return false; + return true; +} + + +bool AABB::BehindPlane(const Plane &plane) const { + if (plane.Distance(minB) > 0) + return false; + if (plane.Distance(maxB) > 0) + return false; + + if (plane.Distance(maxB.x, minB.y, minB.z) > 0) + return false; + if (plane.Distance(maxB.x, maxB.y, minB.z) > 0) + return false; + if (plane.Distance(maxB.x, minB.y, maxB.z) > 0) + return false; + + if (plane.Distance(minB.x, maxB.y, minB.z) > 0) + return false; + if (plane.Distance(minB.x, minB.y, maxB.z) > 0) + return false; + if (plane.Distance(minB.x, maxB.y, maxB.z) > 0) + return false; + + return true; +} + +} // namespace lin diff --git a/math/lin/aabb.h b/math/lin/aabb.h new file mode 100644 index 0000000000..22f2a7b80d --- /dev/null +++ b/math/lin/aabb.h @@ -0,0 +1,47 @@ +#ifndef _MATH_LIN_AABB +#define _MATH_LIN_AABB + +#include "math/lin/vec3.h" + +namespace lin { + +class Ray; +class Plane; + +struct AABB { + Vec3 minB; + int pad; + Vec3 maxB; + int pad2; + + AABB(); + bool Contains(const Vec3 &pt) const; + bool IntersectRay(const Ray &ray, float &tnear, float &tfar) const; + + // Doesn't currently work. + bool IntersectRay2(const Ray &ray, float &tnear, float &tfar) const; + + bool IntersectsTriangle(const Vec3& a_V0, const Vec3& a_V1, const Vec3& a_V2) const; + void Add(const Vec3 &pt); + int GetShortestAxis() const; + int GetLongestAxis() const; + + float GetSize(int axis) const { + return maxB[axis] - minB[axis]; + } + float GetMidpoint(int axis) const { + return (minB[axis] + maxB[axis]) / 2; + } + Vec3 GetMidpoint() const { + return (minB + maxB) / 2; + } + Vec3 GetExtents() const { + return maxB - minB; + } + + bool BehindPlane(const Plane &plane) const; +}; + +} // namespace lin + +#endif // _MATH_LIN_AABB diff --git a/math/lin/build.scons b/math/lin/build.scons new file mode 100644 index 0000000000..41b0cf46c2 --- /dev/null +++ b/math/lin/build.scons @@ -0,0 +1,12 @@ +Import('env') + +inputs = [ + 'aabb.cc', + 'matrix4x4.cc', + 'vec3.cc', + 'quat.cc', + 'ray_intersections.cc', +] + +env.Append(LIBS = ['gflags', 'glog', ]) +env.ComponentLibrary('lin', inputs, COMPONENT_STATIC=True) diff --git a/math/lin/matrix4x4.cpp b/math/lin/matrix4x4.cpp new file mode 100644 index 0000000000..a2e7f32c93 --- /dev/null +++ b/math/lin/matrix4x4.cpp @@ -0,0 +1,276 @@ +#include "math/lin/matrix4x4.h" + +#include + +#include "math/lin/vec3.h" +#include "math/lin/quat.h" + +// See http://code.google.com/p/oolongengine/source/browse/trunk/Oolong+Engine2/Math/neonmath/neon_matrix_impl.cpp?spec=svn143&r=143 when we need speed +// no wait. http://code.google.com/p/math-neon/ + +void matrix_mul_4x4(Matrix4x4 &res, const Matrix4x4 &inA, const Matrix4x4 &inB) { + res.xx = inA.xx*inB.xx + inA.xy*inB.yx + inA.xz*inB.zx + inA.xw*inB.wx; + res.xy = inA.xx*inB.xy + inA.xy*inB.yy + inA.xz*inB.zy + inA.xw*inB.wy; + res.xz = inA.xx*inB.xz + inA.xy*inB.yz + inA.xz*inB.zz + inA.xw*inB.wz; + res.xw = inA.xx*inB.xw + inA.xy*inB.yw + inA.xz*inB.zw + inA.xw*inB.ww; + + res.yx = inA.yx*inB.xx + inA.yy*inB.yx + inA.yz*inB.zx + inA.yw*inB.wx; + res.yy = inA.yx*inB.xy + inA.yy*inB.yy + inA.yz*inB.zy + inA.yw*inB.wy; + res.yz = inA.yx*inB.xz + inA.yy*inB.yz + inA.yz*inB.zz + inA.yw*inB.wz; + res.yw = inA.yx*inB.xw + inA.yy*inB.yw + inA.yz*inB.zw + inA.yw*inB.ww; + + res.zx = inA.zx*inB.xx + inA.zy*inB.yx + inA.zz*inB.zx + inA.zw*inB.wx; + res.zy = inA.zx*inB.xy + inA.zy*inB.yy + inA.zz*inB.zy + inA.zw*inB.wy; + res.zz = inA.zx*inB.xz + inA.zy*inB.yz + inA.zz*inB.zz + inA.zw*inB.wz; + res.zw = inA.zx*inB.xw + inA.zy*inB.yw + inA.zz*inB.zw + inA.zw*inB.ww; + + res.wx = inA.wx*inB.xx + inA.wy*inB.yx + inA.wz*inB.zx + inA.ww*inB.wx; + res.wy = inA.wx*inB.xy + inA.wy*inB.yy + inA.wz*inB.zy + inA.ww*inB.wy; + res.wz = inA.wx*inB.xz + inA.wy*inB.yz + inA.wz*inB.zz + inA.ww*inB.wz; + res.ww = inA.wx*inB.xw + inA.wy*inB.yw + inA.wz*inB.zw + inA.ww*inB.ww; +} + +Matrix4x4 Matrix4x4::simpleInverse() const { + Matrix4x4 out; + out.xx = xx; + out.xy = yx; + out.xz = zx; + + out.yx = xy; + out.yy = yy; + out.yz = zy; + + out.zx = xz; + out.zy = yz; + out.zz = zz; + + out.wx = -(xx * wx + xy * wy + xz * wz); + out.wy = -(yx * wx + yy * wy + yz * wz); + out.wz = -(zx * wx + zy * wy + zz * wz); + + out.xw = 0.0f; + out.yw = 0.0f; + out.zw = 0.0f; + out.ww = 1.0f; + + return out; +} +Matrix4x4 Matrix4x4::transpose() const +{ + Matrix4x4 out; + out.xx = xx;out.xy = yx;out.xz = zx;out.xw = wx; + out.yx = xy;out.yy = yy;out.yz = zy;out.yw = wy; + out.zx = xz;out.zy = yz;out.zz = zz;out.zw = wz; + out.wx = xw;out.wy = yw;out.wz = zw;out.ww = ww; + return out; +} + +Matrix4x4 Matrix4x4::operator * (const Matrix4x4 &other) const +{ + Matrix4x4 temp; + matrix_mul_4x4(temp, *this, other); + return temp; +} + +Matrix4x4 Matrix4x4::inverse() const { + Matrix4x4 temp; + float dW = 1.0f / (xx*(yy*zz - yz*zy) - xy*(yx*zz - yz*zx) - xz*(yy*zx - yx*zy)); + + temp.xx = (yy*zz - yz*zy) * dW; + temp.xy = (xz*zy - xy*zz) * dW; + temp.xz = (xy*yz - xz*yy) * dW; + temp.xw = xw; + + temp.yx = (yz*zx - yx*zz) * dW; + temp.yy = (xx*zz - xz*zx) * dW; + temp.yz = (xz*yx - xx*zx) * dW; + temp.yw = yw; + + temp.zx = (yx*zy - yy*zx) * dW; + temp.zy = (xy*zx - xx*zy) * dW; + temp.zz = (xx*yy - xy*yx) * dW; + temp.zw = zw; + + temp.wx = (yy*(zx*wz - zz*wx) + yz*(zy*wx - zx*wy) - yx*(zy*wz - zz*wy)) * dW; + temp.wy = (xx*(zy*wz - zz*wy) + xy*(zz*wx - zx*wz) + xz*(zx*wy - zy*wx)) * dW; + temp.wz = (xy*(yx*wz - yz*wx) + xz*(yy*wx - yx*wy) - xx*(yy*wz - yz*wy)) * dW; + temp.ww = ww; + + return temp; +} + +void Matrix4x4::setViewLookAt(const Vec3 &vFrom, const Vec3 &vAt, const Vec3 &vWorldUp) { + Vec3 vView = vFrom - vAt; // OpenGL, sigh... + vView.normalize(); + float DotProduct = vWorldUp * vView; + Vec3 vUp = vWorldUp - vView * DotProduct; + float Length = vUp.length(); + + if (1e-6f > Length) { + // EMERGENCY + vUp = Vec3(0.0f, 1.0f, 0.0f) - vView * vView.y; + // If we still have near-zero length, resort to a different axis. + Length = vUp.length(); + if (1e-6f > Length) + { + vUp = Vec3(0.0f, 0.0f, 1.0f) - vView * vView.z; + Length = vUp.length(); + if (1e-6f > Length) + return; + } + } + vUp.normalize(); + Vec3 vRight = vUp % vView; + empty(); + + xx = vRight.x; xy = vUp.x; xz=vView.x; + yx = vRight.y; yy = vUp.y; yz=vView.y; + zx = vRight.z; zy = vUp.z; zz=vView.z; + + wx = -vFrom * vRight; + wy = -vFrom * vUp; + wz = -vFrom * vView; + ww = 1.0f; +} + +void Matrix4x4::setViewLookAtD3D(const Vec3 &vFrom, const Vec3 &vAt, const Vec3 &vWorldUp) { + Vec3 vView = vAt - vFrom; + vView.normalize(); + float DotProduct = vWorldUp * vView; + Vec3 vUp = vWorldUp - vView * DotProduct; + float Length = vUp.length(); + + if (1e-6f > Length) { + // EMERGENCY + vUp = Vec3(0.0f, 1.0f, 0.0f) - vView * vView.y; + // If we still have near-zero length, resort to a different axis. + Length = vUp.length(); + if (1e-6f > Length) + { + vUp = Vec3(0.0f, 0.0f, 1.0f) - vView * vView.z; + Length = vUp.length(); + if (1e-6f > Length) + return; + } + } + vUp.normalize(); + Vec3 vRight = vUp % vView; + empty(); + + xx = vRight.x; xy = vUp.x; xz=vView.x; + yx = vRight.y; yy = vUp.y; yz=vView.y; + zx = vRight.z; zy = vUp.z; zz=vView.z; + + wx = -vFrom * vRight; + wy = -vFrom * vUp; + wz = -vFrom * vView; + ww = 1.0f; +} + + +void Matrix4x4::setViewFrame(const Vec3 &pos, const Vec3 &vRight, const Vec3 &vView, const Vec3 &vUp) { + xx = vRight.x; xy = vUp.x; xz=vView.x; xw = 0.0f; + yx = vRight.y; yy = vUp.y; yz=vView.y; yw = 0.0f; + zx = vRight.z; zy = vUp.z; zz=vView.z; zw = 0.0f; + + wx = -pos * vRight; + wy = -pos * vUp; + wz = -pos * vView; + ww = 1.0f; +} + +//YXZ euler angles +void Matrix4x4::setRotation(float x,float y, float z) +{ + setRotationY(y); + Matrix4x4 temp; + temp.setRotationX(x); + *this *= temp; + temp.setRotationZ(z); + *this *= temp; +} + +void Matrix4x4::setProjection(float near, float far, float fov_horiz, float aspect) { + // Now OpenGL style. + empty(); + + float xFac = tanf(fov_horiz * 3.14f/360); + float yFac = xFac * aspect; + xx = 1.0f / xFac; + yy = 1.0f / yFac; + zz = -(far+near)/(far-near); + zw = -1.0f; + wz = -(2*far*near)/(far-near); +} + +void Matrix4x4::setProjectionD3D(float near_plane, float far_plane, float fov_horiz, float aspect) { + empty(); + float Q, f; + + f = fov_horiz*0.5f; + Q = far_plane / (far_plane - near_plane); + + xx = (float)(1.0f / tanf(f));; + yy = (float)(1.0f / tanf(f*aspect)); + zz = Q; + wz = -Q * near_plane; + zw = 1.0f; +} + +void Matrix4x4::setOrtho(float left, float right, float bottom, float top, float near, float far) { + setIdentity(); + xx = 2.0f / (right - left); + yy = 2.0f / (top - bottom); + zz = 2.0f / (far - near); + wx = -(right + left) / (right - left); + wy = -(top + bottom) / (top - bottom); + wz = -(far + near) / (far - near); +} + +// This is a D3D style matrix. +void Matrix4x4::setProjectionInf(const float near_plane, const float fov_horiz, const float aspect) { + empty(); + float f = fov_horiz*0.5f; + xx = 1.0f / tanf(f); + yy = 1.0f / tanf(f*aspect); + zz = 1; + wz = -near_plane; + zw = 1.0f; +} + +void Matrix4x4::setRotationAxisAngle(const Vec3 &axis, float angle) { + Quaternion quat; + quat.setRotation(axis, angle); + quat.toMatrix(this); +} + +// from a (Position, Rotation, Scale) vec3 quat vec3 tuple +Matrix4x4 Matrix4x4::fromPRS(const Vec3 &positionv, const Quaternion &rotv, const Vec3 &scalev) { + Matrix4x4 newM; + newM.setIdentity(); + Matrix4x4 rot, scale; + rotv.toMatrix(&rot); + scale.setScaling(scalev); + newM = rot * scale; + newM.wx = positionv.x; + newM.wy = positionv.y; + newM.wz = positionv.z; + return newM; +} +#if _MSC_VER +#define snprintf _snprintf +#endif +void Matrix4x4::toText(char *buffer, int len) const { + snprintf(buffer, len, "%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", + xx,xy,xz,xw, + yx,yy,yz,yw, + zx,zy,zz,zw, + wx,wy,wz,ww); + buffer[len - 1] = '\0'; +} + +void Matrix4x4::print() const { + char buffer[256]; + toText(buffer, 256); + puts(buffer); +} diff --git a/math/lin/matrix4x4.h b/math/lin/matrix4x4.h new file mode 100644 index 0000000000..b64b31a28d --- /dev/null +++ b/math/lin/matrix4x4.h @@ -0,0 +1,141 @@ +#ifndef _MATH_LIN_MATRIX4X4_H +#define _MATH_LIN_MATRIX4X4_H + +#include "math/lin/vec3.h" + +class Quaternion; + +class Matrix4x4 { + public: + float xx, xy, xz, xw; + float yx, yy, yz, yw; + float zx, zy, zz, zw; + float wx, wy, wz, ww; + + const Vec3 right() const {return Vec3(xx, xy, xz);} + const Vec3 up() const {return Vec3(yx, yy, yz);} + const Vec3 front() const {return Vec3(zx, zy, zz);} + const Vec3 move() const {return Vec3(wx, wy, wz);} + + void setRight(const Vec3 &v) { + xx = v.x; xy = v.y; xz = v.z; + } + void setUp(const Vec3 &v) { + yx = v.x; yy = v.y; yz = v.z; + } + void setFront(const Vec3 &v) { + zx = v.x; zy = v.y; zz = v.z; + } + void setMove(const Vec3 &v) { + wx = v.x; wy = v.y; wz = v.z; + } + + + const float &operator[](int i) const { + return *(((const float *)this) + i); + } + float &operator[](int i) { + return *(((float *)this) + i); + } + Matrix4x4 operator * (const Matrix4x4 &other) const ; + void operator *= (const Matrix4x4 &other) { + *this = *this * other; + } + const float *getReadPtr() const { + return (const float *)this; + } + void empty() { + memset(this, 0, 16 * sizeof(float)); + } + void setScaling(const float f) { + empty(); + xx=yy=zz=f; ww=1.0f; + } + void setScaling(const Vec3 f) { + empty(); + xx=f.x; + yy=f.y; + zz=f.z; + ww=1.0f; + } + + void setIdentity() { + setScaling(1.0f); + } + void setTranslation(const Vec3 &trans) { + setIdentity(); + wx = trans.x; + wy = trans.y; + wz = trans.z; + } + Matrix4x4 inverse() const; + Matrix4x4 simpleInverse() const; + Matrix4x4 transpose() const; + + void setRotationX(const float a) { + empty(); + float c=cosf(a); + float s=sinf(a); + xx = 1.0f; + yy = c; yz = s; + zy = -s; zz = c; + ww = 1.0f; + } + void setRotationY(const float a) { + empty(); + float c=cosf(a); + float s=sinf(a); + xx = c; xz = -s; + yy = 1.0f; + zx = s; zz = c ; + ww = 1.0f; + } + void setRotationZ(const float a) { + empty(); + float c=cosf(a); + float s=sinf(a); + xx = c; xy = s; + yx = -s; yy = c; + zz = 1.0f; + ww = 1.0f; + } + void setRotationAxisAngle(const Vec3 &axis, float angle); + + + void setRotation(float x,float y, float z); + void setProjection(float near_plane, float far_plane, float fov_horiz, float aspect = 0.75f); + void setProjectionD3D(float near_plane, float far_plane, float fov_horiz, float aspect = 0.75f); + void setProjectionInf(float near_plane, float fov_horiz, float aspect = 0.75f); + void setOrtho(float left, float right, float bottom, float top, float near, float far); + void setShadow(float Lx, float Ly, float Lz, float Lw) { + float Pa=0; + float Pb=1; + float Pc=0; + float Pd=0; + //P = normalize(Plane); + float d = (Pa*Lx + Pb*Ly + Pc*Lz + Pd*Lw); + + xx=Pa * Lx + d; xy=Pa * Ly; xz=Pa * Lz; xw=Pa * Lw; + yx=Pb * Lx; yy=Pb * Ly + d; yz=Pb * Lz; yw=Pb * Lw; + zx=Pc * Lx; zy=Pc * Ly; zz=Pc * Lz + d; zw=Pc * Lw; + wx=Pd * Lx; wy=Pd * Ly; wz=Pd * Lz; ww=Pd * Lw + d; + } + + void setViewLookAt(const Vec3 &from, const Vec3 &at, const Vec3 &worldup); + void setViewLookAtD3D(const Vec3 &from, const Vec3 &at, const Vec3 &worldup); + void setViewFrame(const Vec3 &pos, const Vec3 &right, const Vec3 &forward, const Vec3 &up); + void stabilizeOrtho() { + /* + front().normalize(); + right().normalize(); + up() = front() % right(); + right() = up() % front(); + */ + } + void toText(char *buffer, int len) const; + void print() const; + static Matrix4x4 fromPRS(const Vec3 &position, const Quaternion &normal, const Vec3 &scale); +}; + +#endif // _MATH_LIN_MATRIX4X4_H + diff --git a/math/lin/matrix_neon.s b/math/lin/matrix_neon.s new file mode 100644 index 0000000000..a663ffe956 --- /dev/null +++ b/math/lin/matrix_neon.s @@ -0,0 +1,106 @@ +@ +@ NEON matrix multiplication examples +@ + +.syntax unified + +@ +@ matrix_mul_float: +@ Calculate 4x4 (matrix 0) * (matrix 1) and store to result 4x4 matrix. +@ matrix 0, matrix 1 and result pointers can be the same, +@ ie. my_matrix = my_matrix * my_matrix is possible. +@ +@ r0 = pointer to 4x4 result matrix, single precision floats, column major order +@ r1 = pointer to 4x4 matrix 0, single precision floats, column major order +@ r2 = pointer to 4x4 matrix 1, single precision floats, column major order +@ + + .global matrix_mul_float +matrix_mul_float: + vld1.32 {d16-d19}, [r1]! @ load first eight elements of matrix 0 + vld1.32 {d20-d23}, [r1]! @ load second eight elements of matrix 0 + vld1.32 {d0-d3}, [r2]! @ load first eight elements of matrix 1 + vld1.32 {d4-d7}, [r2]! @ load second eight elements of matrix 1 + + vmul.f32 q12, q8, d0[0] @ rslt col0 = (mat0 col0) * (mat1 col0 elt0) + vmul.f32 q13, q8, d2[0] @ rslt col1 = (mat0 col0) * (mat1 col1 elt0) + vmul.f32 q14, q8, d4[0] @ rslt col2 = (mat0 col0) * (mat1 col2 elt0) + vmul.f32 q15, q8, d6[0] @ rslt col3 = (mat0 col0) * (mat1 col3 elt0) + + vmla.f32 q12, q9, d0[1] @ rslt col0 += (mat0 col1) * (mat1 col0 elt1) + vmla.f32 q13, q9, d2[1] @ rslt col1 += (mat0 col1) * (mat1 col1 elt1) + vmla.f32 q14, q9, d4[1] @ rslt col2 += (mat0 col1) * (mat1 col2 elt1) + vmla.f32 q15, q9, d6[1] @ rslt col3 += (mat0 col1) * (mat1 col3 elt1) + + vmla.f32 q12, q10, d1[0] @ rslt col0 += (mat0 col2) * (mat1 col0 elt2) + vmla.f32 q13, q10, d3[0] @ rslt col1 += (mat0 col2) * (mat1 col1 elt2) + vmla.f32 q14, q10, d5[0] @ rslt col2 += (mat0 col2) * (mat1 col2 elt2) + vmla.f32 q15, q10, d7[0] @ rslt col3 += (mat0 col2) * (mat1 col2 elt2) + + vmla.f32 q12, q11, d1[1] @ rslt col0 += (mat0 col3) * (mat1 col0 elt3) + vmla.f32 q13, q11, d3[1] @ rslt col1 += (mat0 col3) * (mat1 col1 elt3) + vmla.f32 q14, q11, d5[1] @ rslt col2 += (mat0 col3) * (mat1 col2 elt3) + vmla.f32 q15, q11, d7[1] @ rslt col3 += (mat0 col3) * (mat1 col3 elt3) + + vst1.32 {d24-d27}, [r0]! @ store first eight elements of result + vst1.32 {d28-d31}, [r0]! @ store second eight elements of result + + mov pc, lr @ return to caller + + + + + +@ Macro: mul_col_s16 +@ +@ Multiply a four s16 element column of a matrix by the columns of a second matrix +@ to give a column of results. Elements are assumed to be in Q1.14 format. +@ Inputs: col_d - d register containing a column of the matrix +@ Outputs: res_d - d register containing the column of results +@ Corrupts: register q12 +@ Assumes: the second matrix columns are in registers d16-d19 in column major order +@ + + .macro mul_col_s16 res_d, col_d + vmull.s16 q12, d16, \col_d[0] @ multiply col element 0 by matrix col 0 + vmlal.s16 q12, d17, \col_d[1] @ multiply-acc col element 1 by matrix col 1 + vmlal.s16 q12, d18, \col_d[2] @ multiply-acc col element 2 by matrix col 2 + vmlal.s16 q12, d19, \col_d[3] @ multiply-acc col element 3 by matrix col 3 + vqrshrn.s32 \res_d, q12, #14 @ shift right and narrow accumulator into + @ Q1.14 fixed point format, with saturation + .endm + +@ +@ matrix_mul_fixed: +@ Calculate 4x4 (matrix 0) * (matrix 1) and store to result 4x4 matrix. +@ matrix 0, matrix 1 and result pointers can be the same, +@ ie. my_matrix = my_matrix * my_matrix is possible +@ +@ r0 = pointer to 4x4 result matrix, Q1.14 fixed point, column major order +@ r1 = pointer to 4x4 matrix 0, Q1.14 fixed point, column major order +@ r2 = pointer to 4x4 matrix 1, Q1.14 fixed point, column major order +@ + + .global matrix_mul_fixed +matrix_mul_fixed: + vld1.16 {d16-d19}, [r1] @ load sixteen elements of matrix 0 + vld1.16 {d0-d3}, [r2] @ load sixteen elements of matrix 1 + + mul_col_s16 d4, d0 @ matrix 0 * matrix 1 col 0 + mul_col_s16 d5, d1 @ matrix 0 * matrix 1 col 1 + mul_col_s16 d6, d2 @ matrix 0 * matrix 1 col 2 + mul_col_s16 d7, d3 @ matrix 0 * matrix 1 col 3 + + vst1.16 {d4-d7}, [r0] @ store sixteen elements of result + + mov pc, lr @ return to caller + + + + + + + + + + diff --git a/math/lin/plane.cpp b/math/lin/plane.cpp new file mode 100644 index 0000000000..31b52fa00d --- /dev/null +++ b/math/lin/plane.cpp @@ -0,0 +1,13 @@ +#include "math/lin/matrix4x4.h" +#include "math/lin/plane.h" + +namespace lin { + +Plane Plane::TransformByIT(const Matrix4x4 &m, Plane *out) { + out->x = x * m.xx + y * m.yx + z * m.zx + d * m.wx; + out->y = x * m.xy + y * m.yy + z * m.zy + d * m.wy; + out->z = x * m.xz + y * m.yz + z * m.zz + d * m.wz; + out->d = x * m.xw + y * m.yw + z * m.zw + d * m.ww; +} + +} diff --git a/math/lin/plane.h b/math/lin/plane.h new file mode 100644 index 0000000000..02c7ad22da --- /dev/null +++ b/math/lin/plane.h @@ -0,0 +1,41 @@ +#ifndef _PLANE_H +#define _PLANE_H + +#include "math/lin/vec3.h" + +namespace lin { + +class Matrix4x4; + +class Plane { + public: + float x, y, z, d; + Plane() {} + Plane(float x_, float y_, float z_, float d_) + : x(x_), y(y_), z(z_), d(d_) { } + ~Plane() {} + + float Distance(const Vec3 &v) const { + return x * v.x + y * v.y + z * v.z + d; + } + + float Distance(float px, float py, float pz) const { + return x * px + y * py + z * pz + d; + } + + void Normalize() { + float inv_length = 1.0 * sqrtf(x * x + y * y + z * z); + x *= inv_length; + y *= inv_length; + z *= inv_length; + d *= inv_length; + } + + // Matrix is the inverse transpose of the wanted transform. + // out cannot be equal to this. + Plane TransformByIT(const Matrix4x4 &matrix, Plane *out); +}; + +} + +#endif diff --git a/math/lin/quat.cpp b/math/lin/quat.cpp new file mode 100644 index 0000000000..b869aa6b87 --- /dev/null +++ b/math/lin/quat.cpp @@ -0,0 +1,124 @@ +#include "math/lin/quat.h" +#include "math/lin/matrix4x4.h" + +void Quaternion::toMatrix(Matrix4x4 *out) const { + Matrix4x4 temp; + temp.setIdentity(); + float ww, xx, yy, zz, wx, wy, wz, xy, xz, yz; + ww = w*w; xx = x*x; yy = y*y; zz = z*z; + wx = w*x*2; wy = w*y*2; wz = w*z*2; + xy = x*y*2; xz = x*z*2; yz = y*z*2; + + temp.xx = ww + xx - yy - zz; + temp.xy = xy + wz; + temp.xz = xz - wy; + + temp.yx = xy - wz; + temp.yy = ww - xx + yy - zz; + temp.yz = yz + wx; + + temp.zx = xz + wy; + temp.zy = yz - wx; + temp.zz = ww - xx - yy + zz; + + *out = temp; +} + +Quaternion Quaternion::fromMatrix(Matrix4x4 &m) +{ + // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes + // article "Quaternion Calculus and Fast Animation". + Quaternion q(0,0,0,1); + /* + float fTrace = m[0][0] + m[1][1] + m[2][2]; + float fRoot; + + if( fTrace > 0.0 ) + { + fRoot = sqrtf( fTrace + 1.0f ); + + q.w = 0.5f * fRoot; + + fRoot = 0.5f / fRoot; + + q.x = ( m[2][1] - m[1][2] ) * fRoot; + q.y = ( m[0][2] - m[2][0] ) * fRoot; + q.z = ( m[1][0] - m[0][1] ) * fRoot; + } + else + { + int iNext[3] = { 1, 2, 0 }; + + int i = 0; + if( m[1][1] > m[0][0] ) + i = 1; + + if( m[2][2] > m[i][i] ) + i = 2; + + int j = iNext[i]; + int k = iNext[j]; + + fRoot = sqrtf( m[i][i] - m[j][j] - m[k][k] + 1.0f ); + + float *apfQuat = &q.x; + + apfQuat[i] = 0.5f * fRoot; + + fRoot = 0.5f / fRoot; + + q.w = ( m[k][j] - m[j][k] ) * fRoot; + + apfQuat[j] = ( m[j][i] + m[i][j] ) * fRoot; + apfQuat[k] = ( m[k][i] + m[i][k] ) * fRoot; + } + q.normalize(); */ + return q; +}; + +Quaternion Quaternion::slerp(const Quaternion &to, const float a) const { + Quaternion to2; + float angle, cos_angle, scale_from, scale_to, sin_angle; + + cos_angle = (x * to.x) + (y * to.y) + (z * to.z) + (w * to.w); //4D dot product + + if (cos_angle < 0.0f) + { + cos_angle = -cos_angle; + to2.w = -to.w; to2.x = -to.x; to2.y = -to.y; to2.z = -to.z; + } + else + { + to2 = to; + } + + if ((1.0f - fabsf(cos_angle)) > 0.00001f) + { + /* spherical linear interpolation (SLERP) */ + angle = acosf(cos_angle); + sin_angle = sinf(angle); + scale_from = sinf((1.0f - a) * angle) / sin_angle; + scale_to = sinf(a * angle) / sin_angle; + } + else + { + /* to prevent divide-by-zero, resort to linear interpolation */ + // This is okay in 99% of cases anyway, maybe should be the default? + scale_from = 1.0f - a; + scale_to = a; + } + + return Quaternion( + scale_from*x + scale_to*to2.x, + scale_from*y + scale_to*to2.y, + scale_from*z + scale_to*to2.z, + scale_from*w + scale_to*to2.w + ); +} + +Quaternion Quaternion::multiply(const Quaternion &q) const { + return Quaternion((w * q.x) + (x * q.w) + (y * q.z) - (z * q.y), + (w * q.y) + (y * q.w) + (z * q.x) - (x * q.z), + (w * q.z) + (z * q.w) + (x * q.y) - (y * q.x), + (w * q.w) - (x * q.x) - (y * q.y) - (z * q.z)); +} diff --git a/math/lin/quat.h b/math/lin/quat.h new file mode 100644 index 0000000000..2bedd59733 --- /dev/null +++ b/math/lin/quat.h @@ -0,0 +1,92 @@ +#ifndef _MATH_LIN_QUAT_H +#define _MATH_LIN_QUAT_H + +#include "math/lin/vec3.h" + +class Matrix4x4; + +class Quaternion +{ +public: + float x,y,z,w; + + Quaternion() { } + Quaternion(const float _x, const float _y, const float _z, const float _w) { + x=_x; y=_y; z=_z; w=_w; + } + void setIdentity() + { + x=y=z=0; w=1.0f; + } + void setXRotation(const float r) { w = cosf(r / 2); x = sinf(r / 2); y = z = 0; } + void setYRotation(const float r) { w = cosf(r / 2); y = sinf(r / 2); x = z = 0; } + void setZRotation(const float r) { w = cosf(r / 2); z = sinf(r / 2); x = y = 0; } + void toMatrix(Matrix4x4 *out) const; + static Quaternion fromMatrix(Matrix4x4 &m); + + Quaternion operator *(Quaternion &q) const + { + return Quaternion( + (w * q.w) - (x * q.x) - (y * q.y) - (z * q.z), + (w * q.x) + (x * q.w) + (y * q.z) - (z * q.y), + (w * q.y) + (y * q.w) + (z * q.x) - (x * q.z), + (w * q.z) + (z * q.w) + (x * q.y) - (y * q.x) + ); + } + Quaternion operator -() + { + return Quaternion(-x,-y,-z,-w); + } + void setRotation(Vec3 axis, float angle) + { + axis /= axis.length(); + angle *= .5f; + float sine = sinf(angle); + w = cosf(angle); + x = sine * axis.x; + y = sine * axis.y; + z = sine * axis.z; + } + void toAxisAngle(Vec3 &v, float &angle) + { + normalize(); + if (w==1.0f && x==0.0f && y==0.0f && z==0.0f) + { + v = Vec3(0,1,0); + angle = 0.0f; + return; + } + float cos_a = w; + angle = acosf(cos_a) * 2; + float sin_a = sqrtf( 1.0f - cos_a * cos_a ); + if (fabsf(sin_a) < 0.00005f) sin_a = 1; + float inv_sin_a=1.0f/sin_a; + v.x = x * inv_sin_a; + v.y = y * inv_sin_a; + v.z = z * inv_sin_a; + } + enum { + QUAT_SHORT, + QUAT_LONG, + QUAT_CW, + QUAT_CCW + }; + Quaternion slerp(const Quaternion &to, const float a) const; + Quaternion multiply(const Quaternion &q) const; + float &operator [] (int i) { + return *((&x) + i); + } + const float operator [] (int i) const { + return *((&x) + i); + } + //not sure about this, maybe mag is supposed to sqrt + float magnitude() const { + return x*x + y*y + z*z + w*w; + } + void normalize() { + float f = 1.0f/sqrtf(magnitude()); + x*=f; y*=f; z*=f; w*=f; + } +}; + +#endif // _MATH_LIN_QUAT_H diff --git a/math/lin/ray.h b/math/lin/ray.h new file mode 100644 index 0000000000..5c9a5e91e0 --- /dev/null +++ b/math/lin/ray.h @@ -0,0 +1,20 @@ +#ifndef _MATH_LIN_RAY_H +#define _MATH_LIN_RAY_H + +#include "vec3.h" + +namespace lin { + +/* __declspec(align(16)) */ struct Ray +{ + Vec3 origin; + int pad; + Vec3 dir; + int pad2; + Vec3 invdir; + int pad3; +}; + +} // namespace lin + +#endif // _MATH_LIN_RAY_H diff --git a/math/lin/vec3.cpp b/math/lin/vec3.cpp new file mode 100644 index 0000000000..bf35ea5118 --- /dev/null +++ b/math/lin/vec3.cpp @@ -0,0 +1,28 @@ +#include + +#include "math/lin/vec3.h" +#include "math/lin/matrix4x4.h" + +Vec3 Vec3::operator *(const Matrix4x4 &m) const { + return Vec3(x*m.xx + y*m.yx + z*m.zx + m.wx, + x*m.xy + y*m.yy + z*m.zy + m.wy, + x*m.xz + y*m.yz + z*m.zz + m.wz); +} +Vec4 Vec3::multiply4D(const Matrix4x4 &m) const { + return Vec4(x*m.xx + y*m.yx + z*m.zx + m.wx, + x*m.xy + y*m.yy + z*m.zy + m.wy, + x*m.xz + y*m.yz + z*m.zz + m.wz, + x*m.xw + y*m.yw + z*m.zw + m.ww); +} +Vec4 Vec4::multiply4D(Matrix4x4 &m) const { + return Vec4(x*m.xx + y*m.yx + z*m.zx + w*m.wx, + x*m.xy + y*m.yy + z*m.zy + w*m.wy, + x*m.xz + y*m.yz + z*m.zz + w*m.wz, + x*m.xw + y*m.yw + z*m.zw + w*m.ww); +} + +Vec3 Vec3::rotatedBy(const Matrix4x4 &m) const { + return Vec3(x*m.xx + y*m.yx + z*m.zx, + x*m.xy + y*m.yy + z*m.zy, + x*m.xz + y*m.yz + z*m.zz); +} diff --git a/math/lin/vec3.h b/math/lin/vec3.h new file mode 100644 index 0000000000..92badf89f9 --- /dev/null +++ b/math/lin/vec3.h @@ -0,0 +1,158 @@ +#ifndef _MATH_LIN_VEC3 +#define _MATH_LIN_VEC3 + +#include +#include // memset + +inline float sqr(float f) {return f*f;} +inline float sqr_signed(float f) {return f<0 ? -f*f : f*f;} + +#ifndef PI +#define PI (3.141592654f) +#endif +#define PI_INV (0.318309886f) + +class Matrix4x4; +// class Quaternion; + +// Hm, doesn't belong in this file. +class Vec4 { +public: + float x,y,z,w; + Vec4(){} + Vec4(float a, float b, float c, float d) {x=a;y=b;z=c;w=d;} + Vec4 multiply4D(Matrix4x4 &m) const; +}; + +class Vec3 { +public: + float x,y,z; + + Vec3() { } + explicit Vec3(float f) {x=y=z=f;} + + Vec3(const float _x, const float _y, const float _z) { + x=_x; y=_y; z=_z; + } + void Set(float _x, float _y, float _z) { + x=_x; y=_y; z=_z; + } + Vec3 operator + (const Vec3 &other) const { + return Vec3(x+other.x, y+other.y, z+other.z); + } + void operator += (const Vec3 &other) { + x+=other.x; y+=other.y; z+=other.z; + } + Vec3 operator -(const Vec3 &v) const { + return Vec3(x-v.x,y-v.y,z-v.z); + } + void operator -= (const Vec3 &other) + { + x-=other.x; y-=other.y; z-=other.z; + } + Vec3 operator -() const { + return Vec3(-x,-y,-z); + } + + Vec3 operator * (const float f) const { + return Vec3(x*f,y*f,z*f); + } + Vec3 operator / (const float f) const { + float invf = (1.0f/f); + return Vec3(x*invf,y*invf,z*invf); + } + void operator /= (const float f) + { + *this = *this / f; + } + float operator * (const Vec3 &other) const { + return x*other.x + y*other.y + z*other.z; + } + void operator *= (const float f) { + *this = *this * f; + } + void scaleBy(const Vec3 &other) { + x *= other.x; y *= other.y; z *= other.z; + } + Vec3 scaledBy(const Vec3 &other) const { + return Vec3(x*other.x, y*other.y, z*other.z); + } + Vec3 scaledByInv(const Vec3 &other) const { + return Vec3(x/other.x, y/other.y, z/other.z); + } + Vec3 operator *(const Matrix4x4 &m) const; + void operator *=(const Matrix4x4 &m) { + *this = *this * m; + } + Vec4 multiply4D(const Matrix4x4 &m) const; + Vec3 rotatedBy(const Matrix4x4 &m) const; + Vec3 operator %(const Vec3 &v) const { + return Vec3(y*v.z-z*v.y, z*v.x-x*v.z, x*v.y-y*v.x); + } + float length2() const { + return x*x + y*y + z*z; + } + float length() const { + return sqrtf(length2()); + } + void setLength(const float l) { + (*this) *= l/length(); + } + Vec3 withLength(const float l) const { + return (*this) * l / length(); + } + float distance2To(const Vec3 &other) const { + return Vec3(other-(*this)).length2(); + } + Vec3 normalized() const { + return (*this) / length(); + } + float normalize() { //returns the previous length, is often useful + float len = length(); + (*this) = (*this)/len; + return len; + } + /* + float &operator [] (int i) { //allow vector[2] = 3 (vector.z=3) + return *((&x) + i); + } + const float operator [] (const int i) const { + return *((&x) + i); + }*/ + bool operator == (const Vec3 &other) const { + if (x==other.x && y==other.y && z==other.z) + return true; + else + return false; + } + Vec3 lerp(const Vec3 &other, const float t) const { + return (*this)*(1-t) + other*t; + } + void setZero() { + memset((void *)this,0,sizeof(float)*3); + } +}; + +inline Vec3 operator * (const float f, const Vec3 &v) {return v * f;} + +// In new code, prefer these to the operators. + +inline float dot(const Vec3 &a, const Vec3 &b) { + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +inline Vec3 cross(const Vec3 &a, const Vec3 &b) { + return a % b; +} + +inline float sqr(const Vec3 &v) { + return dot(v, v); +} + +class AABBox { +public: + Vec3 min; + Vec3 max; +}; + +#endif // _MATH_LIN_VEC3 diff --git a/math/math_util.cc b/math/math_util.cc new file mode 100644 index 0000000000..9454df7750 --- /dev/null +++ b/math/math_util.cc @@ -0,0 +1,19 @@ +#include "math/math_util.h" +#include +#include + +/* +static unsigned int randSeed = 22222; // Change this for different random sequences. + +void SetSeed(unsigned int seed) { + randSeed = seed * 382792592; +} + +unsigned int GenerateRandomNumber() { + randSeed = (randSeed * 196314165) + 907633515; + randSeed ^= _rotl(randSeed, 13); + return randSeed; +}*/ + +#include + diff --git a/math/math_util.h b/math/math_util.h new file mode 100644 index 0000000000..573a7e864d --- /dev/null +++ b/math/math_util.h @@ -0,0 +1,86 @@ +#ifndef _MATHUTILS_H +#define _MATHUTILS_H + +#include +#include + +typedef unsigned short float16; + +// This ain't a 1.5.10 float16, no sire, it's a stupid hack format where we chop 16 bits off a float. +// This choice is subject to change. +inline float16 FloatToFloat16(float x) { + int ix; + memcpy(&ix, &x, sizeof(float)); + return ix >> 16; +} + +inline float Float16ToFloat(float16 ix) { + float x; + memcpy(&x, &ix, sizeof(float)); + return x; +} + + +#define PI 3.141592653589793f + +// The stuff in this file is from all over the web, esp. dspmusic.org. I think it's all public domain. +// In any case, very little of it is used anywhere at the moment. + +inline float sine(float t,float f,float ph,float fm) { + return sinf((t*f+ph)*2*PI + 0.5f*PI*fm*(1 - sqrt(f*2))); +} + +//fb := feedback (0 to 1) (1 max saw) + +inline float saw(float t,float f,float ph, float fm, float fb = 1.0f) +{ + return sine(t,f,ph,fb*sine(t-1.0f,f,ph,fm)); +} + +// pm := pulse mod (0 to 1) (1 max pulse) +// pw := pulse width (0 to 1) (1 square) +inline float pulse(float t,float f,float ph,float fm,float fb,float pm,float pw) { + return saw(t,f,ph,fm,fb) - saw(t,f,ph+0.5f*pw,fm,fb) * pm; +} + +// Calculate pseudo-random 32 bit number based on linear congruential method. +void SetSeed(unsigned int seed); +unsigned int GenerateRandomNumber(); +inline float GenerateRandomFloat01() { + return (float)((double)GenerateRandomNumber() / 0xFFFFFFFF); +} +inline float GenerateRandomSignedFloat() { + return (float)((double)GenerateRandomNumber() / 0x80000000) - 1.0f; +} + + +inline float GaussRand() +{ + float R1 = GenerateRandomFloat01(); + float R2 = GenerateRandomFloat01(); + + float X = sqrtf( -2.0f * logf(R1)) * cosf(2.0f * PI * R2); + if (X > 4.0f) X = 4.0f; + if (X < -4.0f) X = -4.0f; + return X; +} + +// Accuracy unknown +inline double atan_fast(double x) { + return (x / (1.0 + 0.28 * (x * x))); +} + + +// linear -> dB conversion +inline float lin2dB(float lin) { + const float LOG_2_DB = 8.6858896380650365530225783783321f; // 20 / ln( 10 ) + return log(lin) * LOG_2_DB; +} + +// dB -> linear conversion +inline float dB2lin(float dB) { + const float DB_2_LOG = 0.11512925464970228420089957273422f; // ln( 10 ) / 20 + return exp(dB * DB_2_LOG); +} + +#endif diff --git a/native.vcxproj b/native.vcxproj new file mode 100644 index 0000000000..a03612b9a5 --- /dev/null +++ b/native.vcxproj @@ -0,0 +1,183 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {E8B58922-9827-493D-81E0-4B6E6BD77171} + Win32Proj + native + + + + StaticLibrary + true + MultiByte + + + StaticLibrary + false + false + MultiByte + + + + + + + + + + + + + D:\dev\LIB;C:\DXSDK\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; + + + D:\dev\LIB;C:\DXSDK\Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib + + + D:\dev\LIB;C:\DXSDK\include;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; + + + D:\dev\LIB;C:\DXSDK\Lib\x86;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + ..\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;..\libpng;%(AdditionalIncludeDirectories); + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + ..\zlib;..\native;..\RollerballGL;..\glew;..\SDL\include;..\libpng;%(AdditionalIncludeDirectories); + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/native.vcxproj.filters b/native.vcxproj.filters new file mode 100644 index 0000000000..d0b324b401 --- /dev/null +++ b/native.vcxproj.filters @@ -0,0 +1,299 @@ + + + + + + file + + + + + + + gfx + + + image + + + image + + + image + + + image + + + math + + + math + + + math + + + gfx + + + gfx + + + gfx + + + ext + + + audio + + + audio + + + profiler + + + input + + + file + + + file + + + file + + + file + + + collision + + + base + + + base + + + base + + + base + + + gfx + + + gfx + + + math + + + math + + + ext + + + base + + + base + + + ext + + + ext + + + gfx + + + audio + + + input + + + base + + + ext + + + base + + + gfx + + + json + + + file + + + math + + + gfx + + + + + gfx + + + image + + + image + + + image + + + math + + + math + + + math + + + gfx + + + gfx + + + gfx + + + ext + + + audio + + + audio + + + profiler + + + file + + + file + + + file + + + collision + + + base + + + gfx + + + gfx + + + math + + + math + + + ext + + + base + + + base + + + ext + + + ext + + + ext + + + gfx + + + audio + + + input + + + ext + + + base + + + gfx + + + json + + + file + + + gfx + + + + + {9da89505-72a1-40e6-86e5-705372db1608} + + + {828bddaf-63e5-4311-985b-bf377f86ff00} + + + {f3f83007-7c3b-473f-a2f7-423c5087fbfa} + + + {5c54b5e2-1bb2-4783-9df6-de4ff7105570} + + + {4ea45f6c-de87-44a9-95b2-e1ec7e3601d0} + + + {c57b41dd-cc33-46ac-b479-19676455ae54} + + + {4ba4474c-b80f-40da-95e8-2de57eb1b327} + + + {49afd06e-eb44-41ac-b038-e109e444a834} + + + {a52d761c-4c72-4261-be17-50d071bd5cc8} + + + {95e41110-57a2-497f-a938-b4c8e9dd6a27} + + + {cda27e9f-5763-4bf2-b6bc-85a377f7020e} + + + \ No newline at end of file diff --git a/profiler/CMakeLists.txt b/profiler/CMakeLists.txt new file mode 100644 index 0000000000..f3f9673dac --- /dev/null +++ b/profiler/CMakeLists.txt @@ -0,0 +1,11 @@ +set(SRCS + profiler.cpp +) + +# LGUIFileList.cpp + +add_library(profiler STATIC ${SRCS}) + +if(UNIX) + add_definitions(-fPIC) +endif(UNIX) diff --git a/profiler/profiler.cpp b/profiler/profiler.cpp new file mode 100644 index 0000000000..28d400cb99 --- /dev/null +++ b/profiler/profiler.cpp @@ -0,0 +1,84 @@ +// UNFINISHED but kinda working + +#include +#include +#include + +#include + +#include "base/logging.h" +#include "gfx_es2/draw_buffer.h" +#include "base/timeutil.h" + +using namespace std; + + +struct Section { + const char *name; + int level; + double start; + double end; +}; +static vector
current_frame; + +static double frame_start; +#define NUM_LEVELS 16 +static Section cur_section[NUM_LEVELS]; + +static int level; + +void _profiler_init() { + level = 0; +} + +void _profiler_enter(const char *section_name) { + cur_section[level].name = section_name; + cur_section[level].start = real_time_now(); + level++; +} + +void _profiler_leave(const char *section_name) { + --level; + cur_section[level].end = real_time_now(); + if (strcmp(section_name, cur_section[level].name)) { + FLOG("Can't enter %s when %s is active, only one at a time!", + section_name, cur_section[level].name); + } + cur_section[level].level = level; + current_frame.push_back(cur_section[level]); +} + +void _profiler_begin_frame() { + frame_start = real_time_now(); +} + +void _profiler_end_frame() { + current_frame.clear(); +} + +void _profiler_log() { + const char *spaces = " "; + ILOG("Profiler output ===================="); + for (int i = 0; i < (int)current_frame.size(); i++) { + double start = current_frame[i].start - frame_start; + double elapsed = current_frame[i].end - current_frame[i].start; + double start_ms = (start*1000); + double elapsed_ms = (elapsed*1000); + ILOG("%s%s: %0.3f ms", spaces + 15-current_frame[i].level, current_frame[i].name, elapsed_ms); + } +} + +void _profiler_draw(DrawBuffer *draw2d, int font) { + uint32_t colors[4] = { 0xFFc0a030, 0xFF30a0c0, 0xFF30C050, 0xFFc02080 }; + for (int i = 0; i < (int)current_frame.size(); i++) { + const Section §ion = current_frame[i]; + double start = section.start - frame_start; + double elapsed = section.end - current_frame[i].start; + + uint32_t color = colors[i&3]; + float y1 = i * 32, y2 = (i+1)*32; + float x1 = start / 0.0166666; + float x2 = (start + elapsed) / 0.01666666; + draw2d->Rect(x1, y1, x2, y2, color); + } +} diff --git a/profiler/profiler.h b/profiler/profiler.h new file mode 100644 index 0000000000..2eb52c6586 --- /dev/null +++ b/profiler/profiler.h @@ -0,0 +1,37 @@ +#pragma once + +// #define USE_PROFILER + +#ifdef USE_PROFILER + +class DrawBuffer; + +void _profiler_init(); +void _profiler_begin_frame(); +void _profiler_end_frame(); + +void _profiler_log(); +void _profiler_draw(DrawBuffer *draw2d, int font); + +void _profiler_enter(const char *section); +void _profiler_leave(const char *section); + +#define PROFILER_INIT() _profiler_init(); +#define PROFILER_ENTER(section) _profiler_enter(section); +#define PROFILER_LEAVE(section) _profiler_leave(section); +#define PROFILER_LOG() _profiler_log(); +#define PROFILER_DRAW(draw, font) _profiler_draw(draw, font); +#define PROFILER_BEGIN_FRAME() _profiler_begin_frame(); +#define PROFILER_END_FRAME() _profiler_end_frame(); + +#else + +#define PROFILER_INIT() +#define PROFILER_ENTER(section) +#define PROFILER_LEAVE(section) +#define PROFILER_LOG() +#define PROFILER_DRAW(draw, font) +#define PROFILER_BEGIN_FRAME() +#define PROFILER_END_FRAME() + +#endif diff --git a/util/bits/bits.cc b/util/bits/bits.cc new file mode 100644 index 0000000000..5267078abe --- /dev/null +++ b/util/bits/bits.cc @@ -0,0 +1,40 @@ +#include "util/bits/bits.h" + +namespace bits { + +// See http://graphics.stanford.edu/~seander/bithacks.html + +static const unsigned char BitsSetTable256[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + +int CountBits8(uint8 v) { + return BitsSetTable256[v]; +} + +int CountBits16(uint16 v) { + return BitsSetTable256[v & 0xFF] + BitsSetTable256[v >> 8]; +} + +int CountBits32(uint32 v) { + v = v - ((v >> 1) & 0x55555555); // reuse input as temporary + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp + return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count +} + +} // namespace bits diff --git a/util/bits/bits.h b/util/bits/bits.h new file mode 100644 index 0000000000..5afab5a8fe --- /dev/null +++ b/util/bits/bits.h @@ -0,0 +1,28 @@ +#ifndef _UTIL_BITS_BITS +#define _UTil_BITS_BITS + +#include "base/basictypes.h" + +namespace bits { + +int CountBits8(uint8 v); +int CountBits16(uint16 v); +int CountBits32(uint32 v); + +// where mask is 0, the result is a. +// where mask is 1, the result is b. +inline uint32 MixBits(uint32 a, uint32 b, uint32 mask) { + return a ^ ((a ^ b) & mask); +} + +inline uint32 ComputeParity(uint32 v) { + v ^= v >> 16; + v ^= v >> 8; + v ^= v >> 4; + v &= 0xf; + return (0x6996 >> v) & 1; +} + +} // namespace bits + +#endif diff --git a/util/bits/hamming.h b/util/bits/hamming.h new file mode 100644 index 0000000000..82217a8376 --- /dev/null +++ b/util/bits/hamming.h @@ -0,0 +1,27 @@ +#ifndef _UTIL_BITS_HAMMING +#define _UTIL_BITS_HAMMING + +#include + +#include "base/logging.h" + +inline int Hamming(const std::string &a, const std::string &b) { + CHECK_EQ(a.size(), b.size()); + int hamming = 0; + for (size_t i = 0; i < a.size(); i++) + hamming += a[i] == b[i]; + return hamming; +} + +inline int Hamming4(const std::string &a, const std::string &b) { + CHECK_EQ(a.size(), b.size()); + int hamming = 0; + const uint32 *au = (const uint32 *)a.data(); + const uint32 *bu = (const uint32 *)b.data(); + for (size_t i = 0; i < a.size() / 4; i++) + hamming += au[i] == bu[i]; + return hamming * 4; +} + + +#endif // _UTIL_BITS_HAMMING diff --git a/util/bits/varint.cc b/util/bits/varint.cc new file mode 100644 index 0000000000..8cd94d052c --- /dev/null +++ b/util/bits/varint.cc @@ -0,0 +1,35 @@ +#include "util/bits/varint.h" + +namespace varint { + +void Encode32(uint32 value, char **dest) { + // Simple varint + char *p = *dest; + while (value > 127) { + *p++ = (value & 127); + value >>= 7; + } + *p++ = value | 0x80; + *dest = p; +} + +uint32 Decode32(const char **ptr) { + uint32 value = 0; + const char *p = *ptr; + while (true) { + uint8 b = *p++; + if (b & 0x80) { + *ptr = p; + return value | (b & 0x7F); + } else { + value |= *p++; + value <<= 7; + } + } + *ptr = p; + return value; +} + +} // namespace varint + + diff --git a/util/bits/varint.h b/util/bits/varint.h new file mode 100644 index 0000000000..aa7d3e8788 --- /dev/null +++ b/util/bits/varint.h @@ -0,0 +1,13 @@ +#ifndef _UTIL_BITS_VARINT +#define _UTIL_BITS_VARINT + +#include "base/base.h" + +namespace varint { + +void Encode32(uint32 value, char **dest); +uint32 Decode32(const char **ptr); + +} // namespace varint + +#endif // _UTIL_BITS_VARINT diff --git a/util/build.scons b/util/build.scons new file mode 100644 index 0000000000..1f668a08f7 --- /dev/null +++ b/util/build.scons @@ -0,0 +1,20 @@ +# Bring in the environment to use for this component (boilerplate). +Import('env') + +# This is the list of input source files to the mandelbrot_gen library. Headers +# will be detected automatically. +inputs = ['hash/hash.cc', + 'hash/minhash.cc'] + +# Build these into a library. +env.ComponentLibrary('hash', inputs) + +inputs = ['bits/varint.cc', + 'bits/bits.cc' ] + +env.ComponentLibrary('bits', inputs) + +inputs = ['random/perlin.cc', + ] + +env.ComponentLibrary('random', inputs) diff --git a/util/hash/hash.cc b/util/hash/hash.cc new file mode 100644 index 0000000000..264450dabe --- /dev/null +++ b/util/hash/hash.cc @@ -0,0 +1,65 @@ +#include "base/base.h" +#include "util/hash/hash.h" + +namespace hash { + +// uint32_t +// WARNING - may read one more byte! +// Implementation from Wikipedia. +uint32 HashFletcher(const uint8 *data_uint8, size_t length) { + const uint16 *data = (const uint16 *)data_uint8; + size_t len = (length + 1) / 2; + uint32 sum1 = 0xffff, sum2 = 0xffff; + + while (len) { + size_t tlen = len > 360 ? 360 : len; + len -= tlen; + + do { + sum1 += *data++; + sum2 += sum1; + } while (--tlen); + + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + } + + /* Second reduction step to reduce sums to 16 bits */ + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + return sum2 << 16 | sum1; +} + +// Implementation from Wikipedia +// Slightly slower than Fletcher above, but slighly more reliable. +#define MOD_ADLER 65521 +// data: Pointer to the data to be summed; len is in bytes +uint32 HashAdler32(const uint8 *data, size_t len) { + uint32 a = 1, b = 0; + while (len) { + size_t tlen = len > 5550 ? 5550 : len; + len -= tlen; + do { + a += *data++; + b += a; + } while (--tlen); + + a = (a & 0xffff) + (a >> 16) * (65536 - MOD_ADLER); + b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); + } + + // It can be shown that a <= 0x1013a here, so a single subtract will do. + if (a >= MOD_ADLER) { + a -= MOD_ADLER; + } + + // It can be shown that b can reach 0xfff87 here. + b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); + + if (b >= MOD_ADLER) { + b -= MOD_ADLER; + } + return (b << 16) | a; +} + +} // namespace hash diff --git a/util/hash/hash.h b/util/hash/hash.h new file mode 100644 index 0000000000..3f80714185 --- /dev/null +++ b/util/hash/hash.h @@ -0,0 +1,30 @@ +#ifndef _UTIL_HASH_HASH_H +#define _UTIL_HASH_HASH_H + +#include + +#include "base/basictypes.h" + +namespace hash { + +uint32 Fletcher(const uint8 *data_u8, size_t length); // FAST. Length & 1 == 0. +uint32 Adler32(const uint8 *data, size_t len); // Fairly accurate, slightly slower + +// WTF is this for? +class ConsistentHasher { + public: + ConsistentHasher() { + // TODO: really need a better seed here. + uint32 orig_seed = rand(); + seed = Fletcher((const uint8 *)&orig_seed, 4); + } + uint32 Hash(uint32 value) { + return value ^ seed; + } + private: + uint32 seed; +}; + +} // namespace hash + +#endif diff --git a/util/random/perlin.cc b/util/random/perlin.cc new file mode 100644 index 0000000000..d0ee8a9fa4 --- /dev/null +++ b/util/random/perlin.cc @@ -0,0 +1,72 @@ +#include "util/random/perlin.h" + +#include + +// This should go in the const section of the binary. +static const int p[512] = { + 151,160,137,91,90,15, + 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, + 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, + 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, + 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, + 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, + 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, + 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, + 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, + 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, + 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, + 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, + 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180, + 151,160,137,91,90,15, + 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, + 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, + 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, + 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, + 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, + 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, + 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, + 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, + 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, + 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, + 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, + 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180, +}; + +inline float fade(float t) { + return t * t * t * (t * (t * 6 - 15) + 10); +} + +inline float lerp(float t, float a, float b) { + return a + t * (b - a); +} + +inline float grad(int hash, float x, float y, float z) { + int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE + float u = h < 8 ? x : y; // INTO 12 GRADIENT DIRECTIONS. + float v = h < 4 ? y : (h == 12 || h == 14 ? x : z); + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); +} + +float Noise(double z, double y, double x) { + double fx = floor(x), fy = floor(y), fz = floor(z); + int X = (int)fx & 255, // FIND UNIT CUBE THAT + Y = (int)fy & 255, // CONTAINS POINT. + Z = (int)fz & 255; + x -= fx; // FIND RELATIVE X,Y,Z + y -= fy; // OF POINT IN CUBE. + z -= fz; + float u = fade(x); // COMPUTE FADE CURVES + float v = fade(y); // FOR EACH OF X,Y,Z. + float w = fade(z); + int A = p[X ]+Y, B = p[X+1]+Y; + int AA = p[A]+Z, AB = p[A+1]+Z; // HASH COORDINATES OF + int BA = p[B]+Z, BB = p[B+1]+Z; // THE 8 CUBE CORNERS, + return lerp(w, lerp(v, lerp(u, grad(p[AA ], x , y , z ), // AND ADD + grad(p[BA ], x-1, y , z )), // BLENDED + lerp(u, grad(p[AB ], x , y-1, z ), // RESULTS + grad(p[BB ], x-1, y-1, z ))), // FROM 8 + lerp(v, lerp(u, grad(p[AA+1], x , y , z-1 ), // CORNERS + grad(p[BA+1], x-1, y , z-1 )), // OF CUBE + lerp(u, grad(p[AB+1], x , y-1, z-1 ), + grad(p[BB+1], x-1, y-1, z-1 )))); +} diff --git a/util/random/perlin.h b/util/random/perlin.h new file mode 100644 index 0000000000..ace8c521d3 --- /dev/null +++ b/util/random/perlin.h @@ -0,0 +1,8 @@ +#ifndef _PERLIN_H +#define _PERLIN_H + +// Implementation of "Improved Noise" +// http://mrl.nyu.edu/~perlin/noise/ +float Noise(double x, double y, double z); + +#endif // _PERLIN_H diff --git a/util/random/rng.h b/util/random/rng.h new file mode 100644 index 0000000000..28369196a6 --- /dev/null +++ b/util/random/rng.h @@ -0,0 +1,26 @@ +#ifndef _RNG_H +#define _RNG_H + +#include "base/basictypes.h" + +// George Marsaglia-style random number generator. +class GMRng { + public: + void Init(int seed) { + m_w = seed ^ (seed << 16); + if (!m_w) m_w = 1337; + m_z = ~seed; + if (!m_z) m_z = 31337; + } + uint32 R32() { + m_z = 36969 * (m_z & 65535) + (m_z >> 16); + m_w = 18000 * (m_w & 65535) + (m_w >> 16); + return (m_z << 16) + m_w; + } + + private: + uint32 m_w; + uint32 m_z; +}; + +#endif