mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2024-12-24 21:55:42 +00:00
75e7b137d6
Also simplified .zga texture and .aqs savegame loading. Dropped support for "crunched" .sav files (those were never used), and the corresponding crunch functions. To be done: Temp files are still used when compressing files, will address this in a later commit.
1085 lines
24 KiB
C++
1085 lines
24 KiB
C++
/*
|
|
Copyright (C) 2007, 2010 - Bit-Blot
|
|
|
|
This file is part of Aquaria.
|
|
|
|
Aquaria is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
#include "Base.h"
|
|
#include "Core.h"
|
|
|
|
#ifdef BBGE_BUILD_WINDOWS
|
|
#include <shellapi.h>
|
|
#endif
|
|
|
|
#if defined(BBGE_BUILD_UNIX)
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#endif
|
|
|
|
#if defined(BBGE_BUILD_MACOSX)
|
|
#include <Carbon/Carbon.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
|
|
Vector getDirVector(Direction dir)
|
|
{
|
|
switch(dir)
|
|
{
|
|
case DIR_DOWN:
|
|
return Vector(0, 1);
|
|
break;
|
|
case DIR_UP:
|
|
return Vector(0, -1);
|
|
break;
|
|
case DIR_LEFT:
|
|
return Vector(-1, 0);
|
|
break;
|
|
case DIR_RIGHT:
|
|
return Vector(1, 0);
|
|
break;
|
|
}
|
|
return Vector(0,0);
|
|
}
|
|
|
|
Direction getOppositeDir(Direction dir)
|
|
{
|
|
switch(dir)
|
|
{
|
|
case DIR_DOWN:
|
|
return DIR_UP;
|
|
break;
|
|
case DIR_UP:
|
|
return DIR_DOWN;
|
|
break;
|
|
case DIR_LEFT:
|
|
return DIR_RIGHT;
|
|
break;
|
|
case DIR_RIGHT:
|
|
return DIR_LEFT;
|
|
break;
|
|
}
|
|
|
|
return DIR_NONE;
|
|
}
|
|
|
|
Direction getNextDirClockwise(Direction dir)
|
|
{
|
|
switch(dir)
|
|
{
|
|
case DIR_DOWN:
|
|
return DIR_LEFT;
|
|
break;
|
|
case DIR_UP:
|
|
return DIR_RIGHT;
|
|
break;
|
|
case DIR_LEFT:
|
|
return DIR_UP;
|
|
break;
|
|
case DIR_RIGHT:
|
|
return DIR_DOWN;
|
|
break;
|
|
}
|
|
return DIR_NONE;
|
|
}
|
|
|
|
void sizePowerOf2Texture(int &v)
|
|
{
|
|
int p = 8, use=0;
|
|
do
|
|
{
|
|
use = 1 << p;
|
|
p++;
|
|
}
|
|
while(v > use);
|
|
|
|
v = use;
|
|
}
|
|
|
|
int randAngle360()
|
|
{
|
|
return rand()%360;
|
|
}
|
|
|
|
int randRange(int n1, int n2)
|
|
{
|
|
int r = rand()%(n2-1);
|
|
r += n1;
|
|
return r;
|
|
}
|
|
|
|
std::string removeSpaces(const std::string &input)
|
|
{
|
|
std::string result;
|
|
for (int i = 0; i < input.size(); i++)
|
|
{
|
|
if (input[i] != ' ')
|
|
{
|
|
result.push_back(input[i]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
unsigned hash(const std::string &string)
|
|
{
|
|
unsigned hash = 5381;
|
|
|
|
for (int i = 0; i < string.size(); i++)
|
|
hash = ((hash << 5) + hash) + string[i];
|
|
|
|
return hash;
|
|
}
|
|
|
|
/* hash * 33 + c */
|
|
|
|
std::string splitCamelCase(const std::string &input)
|
|
{
|
|
std::string result;
|
|
int last = 0;
|
|
for (int i = 0; i < input.size(); i++)
|
|
{
|
|
if (last == 1)
|
|
{
|
|
if (input[i] >= 'A' && input[i] <= 'Z')
|
|
{
|
|
result += ' ';
|
|
}
|
|
}
|
|
|
|
result += input[i];
|
|
|
|
if (input[i] >= 'A' && input[i] <= 'Z')
|
|
{
|
|
last = 2;
|
|
}
|
|
else
|
|
last = 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string numToZeroString(int num, int zeroes)
|
|
{
|
|
std::ostringstream num_os;
|
|
num_os << num;
|
|
|
|
std::ostringstream os;
|
|
|
|
if (num_os.str().size() <= zeroes)
|
|
{
|
|
for (int j = 0; j < zeroes - num_os.str().size(); j++)
|
|
{
|
|
os << "0";
|
|
}
|
|
os << num;
|
|
}
|
|
return os.str();
|
|
}
|
|
|
|
bool isVectorInRect(const Vector &vec, const Vector &coord1, const Vector &coord2)
|
|
{
|
|
return (vec.x > coord1.x && vec.x < coord2.x && vec.y > coord1.y && vec.y < coord2.y);
|
|
}
|
|
|
|
void stringToUpper(std::string &s)
|
|
{
|
|
for (int i = 0; i < s.size(); i++)
|
|
{
|
|
if (s[i] >= 'a' && s[i] <= 'z')
|
|
{
|
|
s[i] = s[i]-'a' + 'A';
|
|
}
|
|
}
|
|
}
|
|
|
|
void stringToLower(std::string &s)
|
|
{
|
|
for (int i = 0; i < s.size(); i++)
|
|
{
|
|
if (s[i] >= 'A' && s[i] <= 'Z')
|
|
{
|
|
s[i] = s[i]-'A' + 'a';
|
|
}
|
|
}
|
|
}
|
|
|
|
void stringToLowerUserData(std::string &s)
|
|
{
|
|
// don't lowercase the userdata path.
|
|
const std::string userdata = core->getUserDataFolder();
|
|
const size_t len = userdata.length();
|
|
const bool match = (s.length() > len) &&
|
|
((s[len] == '/') || (s[len] == '\\')) &&
|
|
(userdata.compare(0, len, s, 0, len) == 0);
|
|
if (!match)
|
|
stringToLower(s);
|
|
else
|
|
{
|
|
s = s.substr(len);
|
|
stringToLower(s);
|
|
s = userdata + s;
|
|
}
|
|
}
|
|
|
|
#ifndef HAVE_STRCASECMP
|
|
int nocasecmp(const std::string &s1, const std::string &s2)
|
|
{
|
|
std::string::const_iterator it1=s1.begin();
|
|
std::string::const_iterator it2=s2.begin();
|
|
|
|
//stop when either string's end has been reached
|
|
while ( (it1!=s1.end()) && (it2!=s2.end()) )
|
|
{
|
|
if(::toupper(*it1) != ::toupper(*it2)) //letters differ?
|
|
// return -1 to indicate smaller than, 1 otherwise
|
|
return (::toupper(*it1) < ::toupper(*it2)) ? -1 : 1;
|
|
//proceed to the next character in each string
|
|
++it1;
|
|
++it2;
|
|
}
|
|
size_t size1=s1.size(), size2=s2.size();// cache lengths
|
|
//return -1,0 or 1 according to strings' lengths
|
|
if (size1==size2)
|
|
return 0;
|
|
return (size1<size2) ? -1 : 1;
|
|
}
|
|
#endif // #if !HAVE_STRCASECMP
|
|
|
|
std::string upperCase(const std::string &s1)
|
|
{
|
|
std::string ret;
|
|
std::string::const_iterator it1=s1.begin();
|
|
while (it1 != s1.end())
|
|
{
|
|
ret += ::toupper(*it1);
|
|
++it1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool exists(const std::string &f, bool makeFatal)
|
|
{
|
|
/*
|
|
if (!PHYSFS_exists(f.c_str()))
|
|
{
|
|
*/
|
|
/*
|
|
std::ostringstream os;
|
|
os << "checking to see if [" << f << "] exists";
|
|
debugLog(os.str());
|
|
*/
|
|
|
|
FILE *file = fopen(core->adjustFilenameCase(f).c_str(), "rb");
|
|
if (!file)
|
|
{
|
|
if (makeFatal)
|
|
{
|
|
errorLog(std::string("Could not open [" + f + "]"));
|
|
exit(0);
|
|
}
|
|
return false;
|
|
}
|
|
fclose(file);
|
|
//}
|
|
return true;
|
|
}
|
|
|
|
void drawCircle(float radius, int stepSize)
|
|
{
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
glBegin(GL_POLYGON);
|
|
{
|
|
for(int i=0;i < 360; i+=stepSize) {
|
|
const float degInRad = i*PI/180.0f;
|
|
glVertex3f(cosf(degInRad)*radius, sinf(degInRad)*radius,0.0);
|
|
}
|
|
}
|
|
glEnd();
|
|
|
|
glEnable(GL_CULL_FACE);
|
|
#endif
|
|
}
|
|
|
|
void fatalError(const std::string &message)
|
|
{
|
|
msg(message);
|
|
exit(0);
|
|
}
|
|
|
|
std::string parseCommand(const std::string &line, const std::string &command)
|
|
{
|
|
int stringPos = line.find(command);
|
|
if (stringPos != std::string::npos)
|
|
{
|
|
return line.substr((command.length()), line.length());
|
|
}
|
|
return "";
|
|
}
|
|
|
|
void glColor3_256(int r, int g, int b)
|
|
{
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
glColor4f(float(r)/256.0f, float(g)/256.0f, float(b)/256.0f, 1.0f);
|
|
#endif
|
|
}
|
|
|
|
bool chance(int perc)
|
|
{
|
|
if (perc == 100) return true;
|
|
if (perc == 0) return false;
|
|
return ((rand()%100) <= perc);
|
|
}
|
|
|
|
bool chancef(float p)
|
|
{
|
|
if (p >= 1) return true;
|
|
if (p <= 0) return false;
|
|
return ((rand()%100) <= p*100);
|
|
}
|
|
|
|
/*
|
|
PHYSFS_file *openRead(const std::string &f)
|
|
{
|
|
|
|
PHYSFS_file *file = PHYSFS_openRead(f.c_str());
|
|
if (!file)
|
|
{
|
|
errorLog ("Could not open [" + f + "]");
|
|
exit(0);
|
|
}
|
|
|
|
return file;
|
|
}
|
|
|
|
|
|
void pfread(void *buffer, PHYSFS_uint32 size, PHYSFS_uint32 objs, PHYSFS_file *handle)
|
|
{
|
|
PHYSFS_read(handle, buffer, size, objs);
|
|
}
|
|
|
|
void pfseek(PHYSFS_file *handle,PHYSFS_uint64 byte,int origin)
|
|
{
|
|
if (origin == SEEK_CUR)
|
|
{
|
|
byte += PHYSFS_tell(handle);
|
|
}
|
|
PHYSFS_seek(handle,byte);
|
|
}
|
|
|
|
void pfclose(PHYSFS_file *handle)
|
|
{
|
|
PHYSFS_close(handle);
|
|
}
|
|
|
|
std::string pLoadStream(const std::string &filename)
|
|
{
|
|
PHYSFS_file *f = openRead(filename.c_str());
|
|
int len = PHYSFS_fileLength(f);
|
|
std::string s;
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
char p;
|
|
PHYSFS_read(f, &p, sizeof(char), 1);
|
|
s += p;
|
|
}
|
|
//std::istringstream is(s);
|
|
|
|
PHYSFS_close(f);
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
void pSaveStream(const std::string &filename, std::ostringstream &os)
|
|
{
|
|
PHYSFS_file *f = PHYSFS_openWrite(filename.c_str());
|
|
//int size = os.str().size();
|
|
//PHYSFS_write(f, (void*)size, sizeof(int), 1);
|
|
PHYSFS_write(f, (void*)os.str().c_str(), sizeof(char)*os.str().size(), 1);
|
|
PHYSFS_close(f);
|
|
}
|
|
*/
|
|
|
|
void errorLog(const std::string &s)
|
|
{
|
|
if (core)
|
|
{
|
|
core->errorLog(s);
|
|
}
|
|
else
|
|
{
|
|
//msg("Core Not Initialized");
|
|
//MessageBox(0, s.c_str(), "ErrorLog (Core Not Initalized)", MB_OK);
|
|
}
|
|
}
|
|
|
|
void debugLog(const std::string &s)
|
|
{
|
|
if (core)
|
|
core->debugLog(s);
|
|
else
|
|
{
|
|
//MessageBox(0, s.c_str(), "DebugLog (Core Not Initalized)", MB_OK);
|
|
}
|
|
}
|
|
|
|
|
|
// Read the given file into memory and return a pointer to the allocated
|
|
// buffer. The buffer will be null-terminated, like a C string; you can
|
|
// also obtain the data length by passing a pointer to an unsigned long
|
|
// as the (optional) second parameter. The buffer should be freed with
|
|
// delete[] when no longer needed.
|
|
char *readFile(std::string path, unsigned long *size_ret)
|
|
{
|
|
FILE *f = fopen(path.c_str(), "rb");
|
|
if (!f)
|
|
return NULL;
|
|
|
|
long fileSize;
|
|
if (fseek(f, 0, SEEK_END) != 0
|
|
|| (fileSize = ftell(f)) < 0
|
|
|| fseek(f, 0, SEEK_SET) != 0)
|
|
{
|
|
debugLog(path + ": Failed to get file size");
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
char *buffer = new char[fileSize + 1];
|
|
if (!buffer)
|
|
{
|
|
std::ostringstream os;
|
|
os << path << ": Not enough memory for file ("
|
|
<< (fileSize+1) << " bytes)";
|
|
debugLog(os.str());
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
long bytesRead = fread(buffer, 1, fileSize, f);
|
|
if (bytesRead != fileSize)
|
|
{
|
|
std::ostringstream os;
|
|
os << path << ": Failed to read file (only got "
|
|
<< bytesRead << " of " << fileSize << " bytes)";
|
|
debugLog(os.str());
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
fclose(f);
|
|
if (size_ret)
|
|
*size_ret = fileSize;
|
|
buffer[fileSize] = 0;
|
|
return buffer;
|
|
}
|
|
|
|
/*
|
|
void pForEachFile(std::string path, std::string type, void callback(const std::string &filename, int param), int param)
|
|
{
|
|
char **rc = PHYSFS_enumerateFiles(path.c_str());
|
|
char **i;
|
|
|
|
for (i = rc; *i != NULL; i++)
|
|
{
|
|
std::string s(*i);
|
|
int p=0;
|
|
if ((p=s.find('.'))!=std::string::npos)
|
|
{
|
|
std::string ext = s.susbtr(p, s.getLength2D());
|
|
if (ext == type)
|
|
{
|
|
callback(fielnameafhghaha
|
|
}
|
|
}
|
|
}
|
|
|
|
PHYSFS_freeList(rc);
|
|
}
|
|
*/
|
|
|
|
void doSingleFile(const std::string &path, const std::string &type, std::string filename, void callback(const std::string &filename, int param), int param)
|
|
{
|
|
if (filename.size()>4)
|
|
{
|
|
std::string search = filename;
|
|
stringToLower(search);
|
|
std::string filetype = filename.substr(search.size()-4, search.size());
|
|
//stringToUpper(filetype);
|
|
//debugLog("comparing: " + filetype + " and: " + type);
|
|
//if (filetype==type)
|
|
debugLog("checking:" + search + " for type:" + type);
|
|
if (search.find(type)!=std::string::npos)
|
|
{
|
|
debugLog("callback");
|
|
callback(path+filename, param);
|
|
}
|
|
else
|
|
{
|
|
debugLog("not the same");
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string stripEndlineForUnix(const std::string &in)
|
|
{
|
|
std::string out;
|
|
for (int i = 0; i < in.size(); i++)
|
|
{
|
|
if (int(in[i]) != 13)
|
|
{
|
|
out+= in[i];
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
void forEachFile(std::string path, std::string type, void callback(const std::string &filename, intptr_t param), intptr_t param)
|
|
{
|
|
if (path.empty()) return;
|
|
|
|
stringToLowerUserData(path);
|
|
stringToLower(type);
|
|
//HACK: MAC:
|
|
debugLog("forEachFile - path: " + path + " type: " + type);
|
|
|
|
#if defined(BBGE_BUILD_UNIX)
|
|
DIR *dir=0;
|
|
dir = opendir(path.c_str());
|
|
if (dir)
|
|
{
|
|
dirent *file=0;
|
|
while ( (file=readdir(dir)) != NULL )
|
|
{
|
|
if (file->d_name && strlen(file->d_name) > 4)
|
|
{
|
|
debugLog(file->d_name);
|
|
char *extension=strrchr(file->d_name,'.');
|
|
if (extension)
|
|
{
|
|
debugLog(extension);
|
|
if (extension!=NULL)
|
|
{
|
|
if (strcasecmp(extension,type.c_str())==0)
|
|
{
|
|
callback(path + std::string(file->d_name), param);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
closedir(dir);
|
|
}
|
|
else
|
|
{
|
|
debugLog("FAILED TO OPEN DIR");
|
|
}
|
|
#endif
|
|
|
|
#ifdef BBGE_BUILD_WINDOWS
|
|
BOOL fFinished;
|
|
HANDLE hList;
|
|
TCHAR szDir[MAX_PATH+1];
|
|
WIN32_FIND_DATA FileData;
|
|
|
|
int end = path.size()-1;
|
|
if (path[end] != '/')
|
|
path[end] += '/';
|
|
|
|
// Get the proper directory path
|
|
// \\ %s\\*
|
|
|
|
|
|
|
|
if (type.find('.')==std::string::npos)
|
|
{
|
|
type = "." + type;
|
|
}
|
|
|
|
|
|
//std::string add = "%s*" + type;
|
|
|
|
//sprintf(szDir, "%s*", path.c_str());
|
|
sprintf(szDir, "%s\\*", path.c_str());
|
|
|
|
stringToUpper(type);
|
|
|
|
// Get the first file
|
|
hList = FindFirstFile(szDir, &FileData);
|
|
if (hList == INVALID_HANDLE_VALUE)
|
|
{
|
|
//printf("No files found\n\n");
|
|
debugLog("No files of type " + type + " found in path " + path);
|
|
}
|
|
else
|
|
{
|
|
// Traverse through the directory structure
|
|
fFinished = FALSE;
|
|
while (!fFinished)
|
|
{
|
|
// Check the object is a directory or not
|
|
//printf("%*s%s\n", indent, "", FileData.cFileName);
|
|
std::string filename = FileData.cFileName;
|
|
//debugLog("found: " + filename);
|
|
if (filename.size()>4)
|
|
{
|
|
|
|
std::string filetype = filename.substr(filename.size()-4, filename.size());
|
|
stringToUpper(filetype);
|
|
//debugLog("comparing: " + filetype + " and: " + type);
|
|
if (filetype==type)
|
|
{
|
|
callback(path+filename, param);
|
|
}
|
|
}
|
|
|
|
|
|
if (!FindNextFile(hList, &FileData))
|
|
{
|
|
/*
|
|
if (GetLastError() == ERROR_NO_MORE_FILES)
|
|
{
|
|
fFinished = TRUE;
|
|
}
|
|
*/
|
|
fFinished = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
FindClose(hList);
|
|
#endif
|
|
}
|
|
|
|
std::vector<std::string> getFileList(std::string path, std::string type, int param)
|
|
{
|
|
std::vector<std::string> list;
|
|
|
|
#ifdef BBGE_BUILD_WINDOWS
|
|
BOOL fFinished;
|
|
HANDLE hList;
|
|
TCHAR szDir[MAX_PATH+1];
|
|
WIN32_FIND_DATA FileData;
|
|
|
|
// Get the proper directory path
|
|
sprintf(szDir, "%s\\*", path.c_str());
|
|
|
|
|
|
// Get the first file
|
|
hList = FindFirstFile(szDir, &FileData);
|
|
if (hList == INVALID_HANDLE_VALUE)
|
|
{
|
|
printf("No files found\n\n");
|
|
}
|
|
else
|
|
{
|
|
// Traverse through the directory structure
|
|
fFinished = FALSE;
|
|
while (!fFinished)
|
|
{
|
|
// Check the object is a directory or not
|
|
//printf("%*s%s\n", indent, "", FileData.cFileName);
|
|
std::string filename = FileData.cFileName;
|
|
if (filename.size()>4 && filename.substr(filename.size()-4, filename.size())==type)
|
|
{
|
|
//callback(path+filename, param);
|
|
list.push_back (filename);
|
|
}
|
|
|
|
|
|
if (!FindNextFile(hList, &FileData))
|
|
{
|
|
/*
|
|
if (GetLastError() == ERROR_NO_MORE_FILES)
|
|
{
|
|
fFinished = TRUE;
|
|
}
|
|
*/
|
|
fFinished = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
FindClose(hList);
|
|
#endif
|
|
|
|
return list;
|
|
}
|
|
|
|
std::string msg(const std::string &message)
|
|
{
|
|
core->msg(message);
|
|
return message;
|
|
}
|
|
|
|
void msgVector(const std::string &name, const Vector &vec)
|
|
{
|
|
std::ostringstream os;
|
|
os << name << ": (" << vec.x <<", " << vec.y << ", " << vec.z << ")";
|
|
msg (os.str());
|
|
}
|
|
|
|
Vector getNearestPointOnLine(Vector a, Vector b, Vector c)
|
|
{
|
|
Vector nearest;
|
|
/// Local Variables ///////////////////////////////////////////////////////////
|
|
long dot_ta,dot_tb;
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// SEE IF a IS THE NEAREST POINT - ANGLE IS OBTUSE
|
|
dot_ta = (c.x - a.x)*(b.x - a.x) + (c.y - a.y)*(b.y - a.y);
|
|
if (dot_ta <= 0) // IT IS OFF THE a VERTEX
|
|
{
|
|
nearest.x = a.x;
|
|
nearest.y = a.y;
|
|
return nearest;
|
|
}
|
|
dot_tb = (c.x - b.x)*(a.x - b.x) + (c.y - b.y)*(a.y - b.y);
|
|
// SEE IF b IS THE NEAREST POINT - ANGLE IS OBTUSE
|
|
if (dot_tb <= 0)
|
|
{
|
|
nearest.x = b.x;
|
|
nearest.y = b.y;
|
|
return nearest;
|
|
}
|
|
// FIND THE REAL NEAREST POINT ON THE LINE SEGMENT - BASED ON RATIO
|
|
nearest.x = a.x + ((b.x - a.x) * dot_ta)/(dot_ta + dot_tb);
|
|
nearest.y = a.y + ((b.y - a.y) * dot_ta)/(dot_ta + dot_tb);
|
|
return nearest;
|
|
}
|
|
|
|
/*
|
|
bool isTouchingLine(Vector lineStart, Vector lineEnd, Vector point, int radius)
|
|
{
|
|
Vector p = getNearestPointOnLine(lineStart, lineEnd, point);
|
|
Vector diff = p - point;
|
|
std::ostringstream os;
|
|
os << "s(" << lineStart.x << ", " << lineStart.y << ") e(";
|
|
os << lineEnd.x << ", " << lineEnd.y << ") - p(" << point.x << ", " << point.y << ")";
|
|
debugLog(os.str());
|
|
return (diff.getSquaredLength2D() < sqr(radius));
|
|
}
|
|
*/
|
|
|
|
bool isTouchingLine(Vector lineStart, Vector lineEnd, Vector point, int radius, Vector *closestP)
|
|
{
|
|
Vector dir = lineEnd - lineStart;
|
|
Vector diff = point - lineStart;
|
|
Vector closest;
|
|
if (!dir.isZero()) {
|
|
float t = diff.dot2D(dir) / dir.dot2D(dir);
|
|
if (t < 0.0f)
|
|
t = 0.0f;
|
|
if (t > 1.0f)
|
|
t = 1.0f;
|
|
closest = lineStart + t * dir;
|
|
} else {
|
|
closest = lineStart;
|
|
}
|
|
Vector d = point - closest;
|
|
float distsqr = d.dot2D(d);
|
|
if (closestP)
|
|
(*closestP) = closest;
|
|
return distsqr <= radius*radius;
|
|
}
|
|
|
|
|
|
GLuint generateEmptyTexture(int quality) // Create An Empty Texture
|
|
{
|
|
GLuint txtnumber=0; // Texture ID
|
|
unsigned char *data; // Stored Data
|
|
|
|
// Create Storage Space For Texture Data (128x128x4)
|
|
int size = (quality * quality) * 4;
|
|
data = new unsigned char[size];
|
|
|
|
memset(data, 0, size); // Clear Storage Memory
|
|
|
|
#ifdef BBGE_BUILD_OPENGL
|
|
glGenTextures(1, &txtnumber); // Create 1 Texture
|
|
glBindTexture(GL_TEXTURE_2D, txtnumber); // Bind The Texture
|
|
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_RGBA, quality, quality, 0,
|
|
GL_RGBA, GL_UNSIGNED_BYTE, data); // Build Texture Using Information In data
|
|
#endif
|
|
|
|
delete [] data; // Release data
|
|
|
|
return txtnumber; // Return The Texture ID
|
|
}
|
|
|
|
Vector randVector(float mag)
|
|
{
|
|
float angle = (rand() / (float)RAND_MAX) * 2.0f * PI;
|
|
float x = sinf(angle), y = cosf(angle);
|
|
return Vector(x*mag, y*mag);
|
|
}
|
|
|
|
float lerp(const float &v1, const float &v2, float dt, int lerpType)
|
|
{
|
|
switch(lerpType)
|
|
{
|
|
case LERP_EASE:
|
|
{
|
|
// ease in and out
|
|
return v1*(2*(dt*dt*dt)-3*sqr(dt)+1) + v2*(3*sqr(dt) - 2*(dt*dt*dt));
|
|
}
|
|
case LERP_EASEIN:
|
|
{
|
|
float t = 1 - dt;
|
|
return (v2-v1)*(sinf(-t*PI_HALF)+1)+v1;
|
|
}
|
|
case LERP_EASEOUT:
|
|
{
|
|
return (v2-v1)*-sinf(-dt*PI_HALF)+v1;
|
|
}
|
|
}
|
|
|
|
return (v2-v1)*dt+v1;
|
|
}
|
|
|
|
|
|
#include <zlib.h>
|
|
#include <assert.h>
|
|
|
|
#define CHUNK 16384
|
|
|
|
/* Compress from file source to file dest until EOF on source.
|
|
def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
|
|
allocated for processing, Z_STREAM_ERROR if an invalid compression
|
|
level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
|
|
version of the library linked do not match, or Z_ERRNO if there is
|
|
an error reading or writing the files. */
|
|
int packFile(const std::string &sourcef, const std::string &destf, int level)
|
|
{
|
|
FILE *source = fopen(core->adjustFilenameCase(sourcef).c_str(), "rb");
|
|
FILE *dest = fopen(core->adjustFilenameCase(destf).c_str(), "wb");
|
|
|
|
if (!source || !dest)
|
|
return 0;
|
|
|
|
int ret, flush;
|
|
unsigned have;
|
|
z_stream strm;
|
|
unsigned char in[CHUNK];
|
|
unsigned char out[CHUNK];
|
|
|
|
/* allocate deflate state */
|
|
strm.zalloc = Z_NULL;
|
|
strm.zfree = Z_NULL;
|
|
strm.opaque = Z_NULL;
|
|
ret = deflateInit(&strm, level);
|
|
if (ret != Z_OK)
|
|
return ret;
|
|
|
|
/* compress until end of file */
|
|
do {
|
|
strm.avail_in = fread(in, 1, CHUNK, source);
|
|
if (ferror(source)) {
|
|
(void)deflateEnd(&strm);
|
|
return Z_ERRNO;
|
|
}
|
|
flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
|
|
strm.next_in = in;
|
|
|
|
/* run deflate() on input until output buffer not full, finish
|
|
compression if all of source has been read in */
|
|
do {
|
|
strm.avail_out = CHUNK;
|
|
strm.next_out = out;
|
|
ret = deflate(&strm, flush); /* no bad return value */
|
|
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
|
|
have = CHUNK - strm.avail_out;
|
|
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
|
|
(void)deflateEnd(&strm);
|
|
return Z_ERRNO;
|
|
}
|
|
} while (strm.avail_out == 0);
|
|
assert(strm.avail_in == 0); /* all input will be used */
|
|
|
|
/* done when last data in file processed */
|
|
} while (flush != Z_FINISH);
|
|
assert(ret == Z_STREAM_END); /* stream will be complete */
|
|
|
|
/* clean up and return */
|
|
(void)deflateEnd(&strm);
|
|
|
|
fclose(source);
|
|
fclose(dest);
|
|
|
|
return Z_OK;
|
|
}
|
|
|
|
/* Decompress from file source to file dest until stream ends or EOF.
|
|
inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
|
|
allocated for processing, Z_DATA_ERROR if the deflate data is
|
|
invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
|
|
the version of the library linked do not match, or Z_ERRNO if there
|
|
is an error reading or writing the files. */
|
|
int unpackFile(const std::string &sourcef, const std::string &destf)
|
|
{
|
|
FILE *source = fopen(core->adjustFilenameCase(sourcef).c_str(), "rb");
|
|
FILE *dest = fopen(core->adjustFilenameCase(destf).c_str(), "wb");
|
|
|
|
if (!source || !dest)
|
|
return 0;
|
|
|
|
int ret;
|
|
unsigned have;
|
|
z_stream strm;
|
|
unsigned char in[CHUNK];
|
|
unsigned char out[CHUNK];
|
|
|
|
/* allocate inflate state */
|
|
strm.zalloc = Z_NULL;
|
|
strm.zfree = Z_NULL;
|
|
strm.opaque = Z_NULL;
|
|
strm.avail_in = 0;
|
|
strm.next_in = Z_NULL;
|
|
ret = inflateInit(&strm);
|
|
if (ret != Z_OK)
|
|
return ret;
|
|
|
|
/* decompress until deflate stream ends or end of file */
|
|
do {
|
|
strm.avail_in = fread(in, 1, CHUNK, source);
|
|
if (ferror(source)) {
|
|
(void)inflateEnd(&strm);
|
|
return Z_ERRNO;
|
|
}
|
|
if (strm.avail_in == 0)
|
|
break;
|
|
strm.next_in = in;
|
|
|
|
/* run inflate() on input until output buffer not full */
|
|
do {
|
|
strm.avail_out = CHUNK;
|
|
strm.next_out = out;
|
|
ret = inflate(&strm, Z_NO_FLUSH);
|
|
assert(ret != Z_STREAM_ERROR); /* state not clobbered */
|
|
switch (ret) {
|
|
case Z_NEED_DICT:
|
|
ret = Z_DATA_ERROR; /* and fall through */
|
|
case Z_DATA_ERROR:
|
|
case Z_MEM_ERROR:
|
|
(void)inflateEnd(&strm);
|
|
return ret;
|
|
}
|
|
have = CHUNK - strm.avail_out;
|
|
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
|
|
(void)inflateEnd(&strm);
|
|
return Z_ERRNO;
|
|
}
|
|
} while (strm.avail_out == 0);
|
|
|
|
/* done when inflate() says it's done */
|
|
} while (ret != Z_STREAM_END);
|
|
|
|
/* clean up and return */
|
|
(void)inflateEnd(&strm);
|
|
|
|
fclose(source);
|
|
fclose(dest);
|
|
|
|
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
|
|
}
|
|
|
|
void openURL(const std::string &url)
|
|
{
|
|
#ifdef BBGE_BUILD_WINDOWS
|
|
ShellExecute(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
|
#endif
|
|
#if defined(BBGE_BUILD_MACOSX)
|
|
CFStringRef str = CFStringCreateWithCString (0, url.c_str(), 0);
|
|
CFURLRef ref = CFURLCreateWithString(kCFAllocatorDefault, str, NULL);
|
|
LSOpenCFURLRef(ref, 0);
|
|
CFRelease(ref);
|
|
CFRelease(str);
|
|
#elif BBGE_BUILD_UNIX
|
|
std::string cmd("PATH=$PATH:. xdg-open '");
|
|
cmd += url;
|
|
cmd += "'";
|
|
if (system(cmd.c_str()) != 0)
|
|
debugLog("system(xdg_open '" + url + "') failed");
|
|
#endif
|
|
}
|
|
|
|
void clamp256Col(int *r)
|
|
{
|
|
if (*r > 255) *r = 255;
|
|
if (*r < 0) *r = 0;
|
|
}
|
|
|
|
Vector colorRGB(int r, int g, int b)
|
|
{
|
|
Vector c;
|
|
|
|
clamp256Col(&r);
|
|
clamp256Col(&g);
|
|
clamp256Col(&b);
|
|
|
|
c.x = float(r)/255.0f;
|
|
c.y = float(g)/255.0f;
|
|
c.z = float(b)/255.0f;
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
std::string underscoresToSpaces(const std::string &str)
|
|
{
|
|
std::string s = str;
|
|
for (int i = 0; i < s.size(); i++)
|
|
if (s[i] == '_') s[i] = ' ';
|
|
return s;
|
|
}
|
|
|
|
std::string spacesToUnderscores(const std::string &str)
|
|
{
|
|
std::string s = str;
|
|
for (int i = 0; i < s.size(); i++)
|
|
if (s[i] == ' ') s[i] = '_';
|
|
return s;
|
|
}
|
|
|
|
|
|
|
|
#include "DeflateCompressor.h"
|
|
|
|
char *readCompressedFile(std::string path, unsigned long *size_ret)
|
|
{
|
|
unsigned long size = 0;
|
|
char *buf = readFile(path, &size);
|
|
ZlibCompressor z; // allocates with new[] by default
|
|
z.init(buf, size, ByteBuffer::TAKE_OVER);
|
|
z.Compressed(true);
|
|
z.Decompress();
|
|
if(!z.Compressed())
|
|
{
|
|
if (size_ret)
|
|
*size_ret = z.size();
|
|
z.wpos(z.size());
|
|
z << '\0'; // be sure the buffer is null-terminated
|
|
buf = (char*)z.ptr();
|
|
z._setPtr(NULL);
|
|
return buf;
|
|
}
|
|
return NULL;
|
|
}
|