diff --git a/extras/amalgamator/juce_AmalgamatorMain.cpp b/extras/amalgamator/juce_AmalgamatorMain.cpp
new file mode 100644
index 0000000000..792218c831
--- /dev/null
+++ b/extras/amalgamator/juce_AmalgamatorMain.cpp
@@ -0,0 +1,340 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library - "Jules' Utility Class Extensions"
+ Copyright 2004-7 by Raw Material Software ltd.
+
+ ------------------------------------------------------------------------------
+
+ JUCE can be redistributed and/or modified 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.
+
+ JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+
+ ------------------------------------------------------------------------------
+
+ If you'd like to release a closed-source product which uses JUCE, commercial
+ licenses are also available: visit www.rawmaterialsoftware.com/juce for
+ more information.
+
+ ==============================================================================
+*/
+
+#include "juce_AppConfig.h"
+#include "../../juce_amalgamated.h"
+
+
+//==============================================================================
+static bool matchesWildcard (const String& filename, const StringArray& wildcards)
+{
+ for (int i = wildcards.size(); --i >= 0;)
+ if (filename.matchesWildcard (wildcards[i], true))
+ return true;
+
+ return false;
+}
+
+static bool canFileBeReincluded (const File& f)
+{
+ String content (f.loadFileAsString());
+
+ for (;;)
+ {
+ content = content.trimStart();
+
+ if (content.startsWith (T("//")))
+ content = content.fromFirstOccurrenceOf (T("\n"), false, false);
+ else if (content.startsWith (T("/*")))
+ content = content.fromFirstOccurrenceOf (T("*/"), false, false);
+ else
+ break;
+ }
+
+ StringArray lines;
+ lines.addLines (content);
+ lines.trim();
+ lines.removeEmptyStrings();
+
+ const String l1 (lines[0].removeCharacters (T(" \t")).trim());
+ const String l2 (lines[1].removeCharacters (T(" \t")).trim());
+
+ if (l1.replace (T("#ifndef"), T("#define")) == l2)
+ return false;
+
+ return true;
+}
+
+//==============================================================================
+static bool parseFile (const File& rootFolder,
+ const File& newTargetFile,
+ StringArray& dest,
+ const File& file,
+ StringArray& alreadyIncludedFiles,
+ const StringArray& includesToIgnore,
+ const StringArray& wildcards,
+ const bool isOuterFile,
+ const bool stripUnnecessaryStuff)
+{
+ printf ("reading: " + file.getFileName() + "\n");
+
+ if (! file.exists())
+ {
+ printf ("!! ERROR - file doesn't exist!");
+ return false;
+ }
+
+ String content (file.loadFileAsString());
+
+ if (stripUnnecessaryStuff && ! isOuterFile)
+ {
+ if (content.startsWith (T("/*")))
+ content = content.fromFirstOccurrenceOf (T("*/"), false, false).trimStart();
+
+ content = content.replace (T("\r\n\r\n\r\n"), T("\r\n\r\n"));
+ }
+
+ StringArray lines;
+ lines.addLines (content);
+ while (lines[0].trim().isEmpty())
+ lines.remove (0);
+
+ for (int i = 0; i < lines.size(); ++i)
+ {
+ String line (lines[i]);
+
+ if ((! isOuterFile) && line.contains (T("//================================================================")))
+ line = String::empty;
+
+ if (line.trimStart().startsWithChar (T('#'))
+ && line.removeCharacters (T(" \t")).startsWithIgnoreCase (T("#include\"")))
+ {
+ const int endOfInclude = line.indexOfChar (line.indexOfChar (T('\"')) + 1, T('\"')) + 1;
+ const String lineUpToEndOfInclude (line.substring (0, endOfInclude));
+ const String lineAfterInclude (line.substring (endOfInclude));
+
+ const String filename (line.fromFirstOccurrenceOf (T("\""), false, false)
+ .upToLastOccurrenceOf (T("\""), false, false));
+ const File targetFile (file.getSiblingFile (filename));
+
+ if (targetFile.exists()
+ && targetFile.isAChildOf (rootFolder))
+ {
+ if (matchesWildcard (filename.replaceCharacter (T('\\'), T('/')), wildcards)
+ && ! includesToIgnore.contains (targetFile.getFileName()))
+ {
+ if (! alreadyIncludedFiles.contains (targetFile.getFullPathName()))
+ {
+ if (! canFileBeReincluded (targetFile))
+ alreadyIncludedFiles.add (targetFile.getFullPathName());
+
+ dest.add (String::empty);
+ dest.add (T("/********* Start of inlined file: ")
+ + targetFile.getFileName()
+ + T(" *********/"));
+
+ if (! parseFile (rootFolder, newTargetFile,
+ dest, targetFile, alreadyIncludedFiles, includesToIgnore,
+ wildcards, false, stripUnnecessaryStuff))
+ {
+ return false;
+ }
+
+ dest.add (T("/********* End of inlined file: ")
+ + targetFile.getFileName()
+ + T(" *********/"));
+ dest.add (String::empty);
+
+ line = lineAfterInclude;
+ }
+ else
+ {
+ if (stripUnnecessaryStuff)
+ line = String::empty;
+ else
+ line = T("/* ") + lineUpToEndOfInclude + T(" */") + lineAfterInclude;
+ }
+ }
+ else
+ {
+ line = lineUpToEndOfInclude.upToFirstOccurrenceOf (T("\""), true, false)
+ + targetFile.getRelativePathFrom (newTargetFile.getParentDirectory())
+ .replaceCharacter (T('\\'), T('/'))
+ + T("\"")
+ + lineAfterInclude;
+ }
+ }
+ }
+
+ dest.add (line.trimEnd());
+ }
+
+ return true;
+}
+
+//==============================================================================
+static bool munge (const File& templateFile, const File& targetFile, const String& wildcard,
+ const bool stripUnnecessaryStuff, StringArray& alreadyIncludedFiles,
+ const StringArray& includesToIgnore)
+{
+ if (! templateFile.existsAsFile())
+ {
+ printf (" The template file doesn't exist!\n\n");
+ return false;
+ }
+
+ StringArray lines, wildcards;
+ wildcards.addTokens (wildcard, T(";,"), T("'\""));
+ wildcards.trim();
+ wildcards.removeEmptyStrings();
+
+ if (! parseFile (targetFile.getParentDirectory(),
+ targetFile,
+ lines, templateFile,
+ alreadyIncludedFiles,
+ includesToIgnore,
+ wildcards,
+ true, stripUnnecessaryStuff))
+ {
+ return false;
+ }
+
+ //lines.trim();
+ //lines.removeEmptyStrings();
+ printf ("\nwriting: " + targetFile.getFullPathName() + "...\n\n");
+
+ for (int i = 0; i < lines.size() - 2; ++i)
+ {
+ if (lines[i].isEmpty() && lines[i + 1].isEmpty())
+ {
+ lines.remove (i + 1);
+ --i;
+ }
+ }
+
+ MemoryBlock newData, oldData;
+ const String newText (lines.joinIntoString (T("\n")) + T("\n"));
+ newData.append ((const char*) newText, (int) strlen ((const char*) newText));
+ targetFile.loadFileAsData (oldData);
+
+ if (oldData == newData)
+ {
+ printf ("(No need to write - new file is identical)\n\n");
+ return true;
+ }
+
+ if (! targetFile.replaceWithData (newData.getData(), newData.getSize()))
+ {
+ printf ("\n!! ERROR - couldn't write to the target file: " + targetFile.getFullPathName() + "\n\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void findAllFilesIncludedIn (const File& hppTemplate, StringArray& alreadyIncludedFiles)
+{
+ StringArray lines;
+ lines.addLines (hppTemplate.loadFileAsString());
+
+ for (int i = 0; i < lines.size(); ++i)
+ {
+ String line (lines[i]);
+
+ if (line.removeCharacters (T(" \t")).startsWithIgnoreCase (T("#include\"")))
+ {
+ const String filename (line.fromFirstOccurrenceOf (T("\""), false, false)
+ .upToLastOccurrenceOf (T("\""), false, false));
+ const File targetFile (hppTemplate.getSiblingFile (filename));
+
+ if (! alreadyIncludedFiles.contains (targetFile.getFullPathName()))
+ {
+ alreadyIncludedFiles.add (targetFile.getFullPathName());
+
+ if (targetFile.getFileName().containsIgnoreCase (T("juce_")) && targetFile.exists())
+ findAllFilesIncludedIn (targetFile, alreadyIncludedFiles);
+ }
+ }
+ }
+}
+
+//==============================================================================
+static void mungeJuce (const File& juceFolder)
+{
+ if (! juceFolder.isDirectory())
+ {
+ printf (" The folder supplied must be the root of your Juce directory!\n\n");
+ return;
+ }
+
+ const File hppTemplate (juceFolder.getChildFile (T("src/juce_amalgamated_template.h")));
+ const File cppTemplate (juceFolder.getChildFile (T("src/juce_amalgamated_template.cpp")));
+
+ const File hppTarget (juceFolder.getChildFile (T("juce_amalgamated.h")));
+ const File cppTarget (juceFolder.getChildFile (T("juce_amalgamated.cpp")));
+
+ StringArray alreadyIncludedFiles, includesToIgnore;
+
+ if (! munge (hppTemplate, hppTarget,
+ "*.h", true, alreadyIncludedFiles, includesToIgnore))
+ {
+ return;
+ }
+
+ findAllFilesIncludedIn (hppTemplate, alreadyIncludedFiles);
+ includesToIgnore.add (hppTarget.getFileName());
+
+ munge (cppTemplate, cppTarget,
+ "*.cpp;*.c;*.h;*.mm;*.m", true, alreadyIncludedFiles,
+ includesToIgnore);
+}
+
+//==============================================================================
+int main (int argc, char* argv[])
+{
+ // If you're running a command-line app, you need to initialise juce manually
+ // before calling any Juce functionality..
+ initialiseJuce_NonGUI();
+
+ printf ("\n The C++ Amalgamator! Copyright 2008 by Julian Storer - www.rawmaterialsoftware.com\n\n");
+
+ if (argc == 4)
+ {
+ const File templateFile (File::getCurrentWorkingDirectory().getChildFile (argv[1]));
+ const File targetFile (File::getCurrentWorkingDirectory().getChildFile (argv[2]));
+ const String wildcard (String (argv[3]).unquoted());
+ StringArray alreadyIncludedFiles, includesToIgnore;
+
+ munge (templateFile, targetFile, wildcard, false, alreadyIncludedFiles, includesToIgnore);
+ }
+ else if (argc == 2)
+ {
+ const File juceFolder (File::getCurrentWorkingDirectory().getChildFile (argv[1]));
+ mungeJuce (juceFolder);
+ }
+ else
+ {
+ printf (" Usage: amalgamator TemplateFile TargetFile \"FileToReplaceWildcard\"\n\n");
+ printf (" amalgamator will run through a C++ file and replace any\n"
+ " #include statements with the contents of the file they refer to.\n"
+ " It'll only do this for files that are within the same parent\n"
+ " directory as the target file, and will ignore include statements\n"
+ " that use '<>' instead of quotes. It'll also only include a file once,\n"
+ " ignoring any repeated instances of it.\n\n"
+ " The wildcard lets you specify what kind of files will be replaced, so\n"
+ " \"*.cpp;*.h\" would replace only includes that reference a .cpp or .h file.\n\n"
+ " Or: just run 'amalgamator YourJuceDirectory' to rebuild the juce files."
+ );
+ }
+
+ return 0;
+}
+
diff --git a/extras/amalgamator/juce_AppConfig.h b/extras/amalgamator/juce_AppConfig.h
new file mode 100644
index 0000000000..fa05cdb520
--- /dev/null
+++ b/extras/amalgamator/juce_AppConfig.h
@@ -0,0 +1,28 @@
+
+/*
+ This file contains settings that you might want to explicitly apply to
+ the your build.
+
+ Most of these are turned on or off by default, but you can override
+ that setting here by un-commenting it and giving it a 1 or 0 value.
+*/
+
+#define JUCE_ONLY_BUILD_CORE_LIBRARY 1
+//#define JUCE_FORCE_DEBUG 1
+//#define JUCE_LOG_ASSERTIONS 1
+//#define JUCE_ASIO 1
+//#define JUCE_ALSA 1
+//#define JUCE_QUICKTIME 1
+//#define JUCE_OPENGL 1
+//#define JUCE_USE_FLAC 1
+//#define JUCE_USE_OGGVORBIS 1
+//#define JUCE_USE_CDBURNER 1
+//#define JUCE_ENABLE_REPAINT_DEBUGGING 1
+//#define JUCE_USE_XINERAMA 1
+//#define JUCE_USE_XSHM 1
+//#define JUCE_PLUGINHOST_VST 1
+//#define JUCE_PLUGINHOST_AU 1
+//#define JUCE_BUILD_GUI_CLASSES 1
+//#define JUCE_CHECK_MEMORY_LEAKS 1
+//#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1
+//#define JUCE_STRINGS_ARE_UNICODE 1
diff --git a/extras/amalgamator/juce_LibrarySource.cpp b/extras/amalgamator/juce_LibrarySource.cpp
new file mode 100644
index 0000000000..5567cbfd82
--- /dev/null
+++ b/extras/amalgamator/juce_LibrarySource.cpp
@@ -0,0 +1,12 @@
+
+/*
+ This file includes the entire juce source tree via the amalgamated file.
+
+ You could add the amalgamated file directly to your project, but doing it
+ like this allows you to put your app's config settings in the
+ juce_AppConfig.h file and have them applied to both the juce headers and
+ the source code.
+*/
+
+#include "juce_AppConfig.h"
+#include "../../juce_amalgamated.cpp"
diff --git a/extras/amalgamator/vc8/Amalgamator.sln b/extras/amalgamator/vc8/Amalgamator.sln
new file mode 100644
index 0000000000..7d092650e9
--- /dev/null
+++ b/extras/amalgamator/vc8/Amalgamator.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Amalgamator", "Amalgamator.vcproj", "{E616D10E-A3BF-4227-9512-186086D297A6}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E616D10E-A3BF-4227-9512-186086D297A6}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E616D10E-A3BF-4227-9512-186086D297A6}.Debug|Win32.Build.0 = Debug|Win32
+ {E616D10E-A3BF-4227-9512-186086D297A6}.Release|Win32.ActiveCfg = Release|Win32
+ {E616D10E-A3BF-4227-9512-186086D297A6}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/extras/amalgamator/vc8/Amalgamator.vcproj b/extras/amalgamator/vc8/Amalgamator.vcproj
new file mode 100644
index 0000000000..c3bc7ebc8b
--- /dev/null
+++ b/extras/amalgamator/vc8/Amalgamator.vcproj
@@ -0,0 +1,193 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp
new file mode 100644
index 0000000000..1453c1c0f9
--- /dev/null
+++ b/juce_amalgamated.cpp
@@ -0,0 +1,259886 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library - "Jules' Utility Class Extensions"
+ Copyright 2004-7 by Raw Material Software ltd.
+
+ ------------------------------------------------------------------------------
+
+ JUCE can be redistributed and/or modified 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.
+
+ JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+
+ ------------------------------------------------------------------------------
+
+ If you'd like to release a closed-source product which uses JUCE, commercial
+ licenses are also available: visit www.rawmaterialsoftware.com/juce for
+ more information.
+
+ ==============================================================================
+*/
+
+/*
+ This monolithic file contains the entire Juce source tree!
+
+ To build an app which uses Juce, all you need to do is to add this
+ file to your project, and include juce.h in your own cpp files.
+
+*/
+
+//==============================================================================
+#ifdef _WIN32
+
+/********* Start of inlined file: win32_headers.h *********/
+#ifndef __WIN32_HEADERS_JUCEHEADER__
+#define __WIN32_HEADERS_JUCEHEADER__
+
+#ifndef STRICT
+ #define STRICT 1
+#endif
+#define WIN32_LEAN_AND_MEAN
+
+// don't want to get told about microsoft's mistakes..
+#ifdef _MSC_VER
+ #pragma warning (push)
+ #pragma warning (disable : 4100 4201)
+#endif
+
+// use Platform SDK as win2000 unless this is disabled
+#ifndef DISABLE_TRANSPARENT_WINDOWS
+ #define _WIN32_WINNT 0x0500
+#endif
+
+#define _UNICODE 1
+#define UNICODE 1
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#undef PACKED
+
+#ifdef _MSC_VER
+ #pragma warning (pop)
+#endif
+
+#endif // __WIN32_HEADERS_JUCEHEADER__
+/********* End of inlined file: win32_headers.h *********/
+
+ #include
+ #include
+ #include
+
+ #if JUCE_QUICKTIME
+ #include
+ #include
+ #include
+ #include
+ #include
+ #undef TARGET_OS_MAC // quicktime sets these, but they confuse some of the 3rd party libs
+ #undef MACOS
+ #endif
+
+#elif defined (LINUX)
+
+#else
+ #include
+ #include
+ #define __Point__
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+
+ #include
+ #include
+ #include
+ #include
+#endif
+
+//==============================================================================
+#define DONT_SET_USING_JUCE_NAMESPACE 1
+
+#include "juce_amalgamated.h"
+
+#define NO_DUMMY_DECL
+
+#if (defined(_MSC_VER) && (_MSC_VER <= 1200))
+ #pragma warning (disable: 4309 4305)
+#endif
+
+//==============================================================================
+
+/********* Start of inlined file: juce_FileLogger.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+FileLogger::FileLogger (const File& logFile_,
+ const String& welcomeMessage,
+ const int maxInitialFileSizeBytes)
+ : logFile (logFile_)
+{
+ if (maxInitialFileSizeBytes >= 0)
+ trimFileSize (maxInitialFileSizeBytes);
+
+ if (! logFile_.exists())
+ {
+ // do this so that the parent directories get created..
+ logFile_.create();
+ }
+
+ logStream = logFile_.createOutputStream (256);
+ jassert (logStream != 0);
+
+ String welcome;
+ welcome << "\r\n**********************************************************\r\n"
+ << welcomeMessage
+ << "\r\nLog started: " << Time::getCurrentTime().toString (true, true)
+ << "\r\n";
+
+ logMessage (welcome);
+}
+
+FileLogger::~FileLogger()
+{
+ deleteAndZero (logStream);
+}
+
+void FileLogger::logMessage (const String& message)
+{
+ if (logStream != 0)
+ {
+ Logger::outputDebugString (message);
+
+ const ScopedLock sl (logLock);
+ (*logStream) << message << T("\r\n");
+ logStream->flush();
+ }
+}
+
+void FileLogger::trimFileSize (int maxFileSizeBytes) const
+{
+ if (maxFileSizeBytes <= 0)
+ {
+ logFile.deleteFile();
+ }
+ else
+ {
+ const int64 fileSize = logFile.getSize();
+
+ if (fileSize > maxFileSizeBytes)
+ {
+ FileInputStream* const in = logFile.createInputStream();
+ jassert (in != 0);
+
+ if (in != 0)
+ {
+ in->setPosition (fileSize - maxFileSizeBytes);
+ String content;
+
+ {
+ MemoryBlock contentToSave;
+ contentToSave.setSize (maxFileSizeBytes + 4);
+ contentToSave.fillWith (0);
+
+ in->read (contentToSave.getData(), maxFileSizeBytes);
+ delete in;
+
+ content = contentToSave.toString();
+ }
+
+ int newStart = 0;
+
+ while (newStart < fileSize
+ && content[newStart] != '\n'
+ && content[newStart] != '\r')
+ ++newStart;
+
+ logFile.deleteFile();
+ logFile.appendText (content.substring (newStart), false, false);
+ }
+ }
+ }
+}
+
+FileLogger* FileLogger::createDefaultAppLogger (const String& logFileSubDirectoryName,
+ const String& logFileName,
+ const String& welcomeMessage,
+ const int maxInitialFileSizeBytes)
+{
+#if JUCE_MAC
+ File logFile ("~/Library/Logs");
+ logFile = logFile.getChildFile (logFileName);
+
+#else
+ File logFile (File::getSpecialLocation (File::userApplicationDataDirectory));
+
+ if (logFile.isDirectory())
+ {
+ logFile = logFile.getChildFile (logFileSubDirectoryName)
+ .getChildFile (logFileName);
+ }
+#endif
+
+ return new FileLogger (logFile, welcomeMessage, maxInitialFileSizeBytes);
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_FileLogger.cpp *********/
+
+/********* Start of inlined file: juce_Logger.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+Logger::Logger()
+{
+}
+
+Logger::~Logger()
+{
+}
+
+static Logger* currentLogger = 0;
+
+void Logger::setCurrentLogger (Logger* const newLogger,
+ const bool deleteOldLogger)
+{
+ Logger* const oldLogger = currentLogger;
+ currentLogger = newLogger;
+
+ if (deleteOldLogger && (oldLogger != 0))
+ delete oldLogger;
+}
+
+void Logger::writeToLog (const String& message)
+{
+ if (currentLogger != 0)
+ currentLogger->logMessage (message);
+ else
+ outputDebugString (message);
+}
+
+#if JUCE_LOG_ASSERTIONS
+void JUCE_API juce_LogAssertion (const char* filename, const int lineNum) throw()
+{
+ String m ("JUCE Assertion failure in ");
+ m << filename << ", line " << lineNum;
+
+ Logger::writeToLog (m);
+}
+#endif
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_Logger.cpp *********/
+
+/********* Start of inlined file: juce_Random.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+Random::Random (const int64 seedValue) throw()
+ : seed (seedValue)
+{
+}
+
+Random::~Random() throw()
+{
+}
+
+void Random::setSeed (const int64 newSeed) throw()
+{
+ seed = newSeed;
+}
+
+int Random::nextInt() throw()
+{
+ seed = (seed * literal64bit (0x5deece66d) + 11) & literal64bit (0xffffffffffff);
+
+ return (int) (seed >> 16);
+}
+
+int Random::nextInt (const int maxValue) throw()
+{
+ jassert (maxValue > 0);
+ return (nextInt() & 0x7fffffff) % maxValue;
+}
+
+int64 Random::nextInt64() throw()
+{
+ return (((int64) nextInt()) << 32) | (int64) (uint64) (uint32) nextInt();
+}
+
+bool Random::nextBool() throw()
+{
+ return (nextInt() & 0x80000000) != 0;
+}
+
+float Random::nextFloat() throw()
+{
+ return ((uint32) nextInt()) / (float) 0xffffffff;
+}
+
+double Random::nextDouble() throw()
+{
+ return ((uint32) nextInt()) / (double) 0xffffffff;
+}
+
+static Random sysRand (1);
+
+Random& Random::getSystemRandom() throw()
+{
+ return sysRand;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_Random.cpp *********/
+
+/********* Start of inlined file: juce_RelativeTime.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+RelativeTime::RelativeTime (const double seconds_) throw()
+ : seconds (seconds_)
+{
+}
+
+RelativeTime::RelativeTime (const RelativeTime& other) throw()
+ : seconds (other.seconds)
+{
+}
+
+RelativeTime::~RelativeTime() throw()
+{
+}
+
+const RelativeTime RelativeTime::milliseconds (const int milliseconds) throw()
+{
+ return RelativeTime (milliseconds * 0.001);
+}
+
+const RelativeTime RelativeTime::milliseconds (const int64 milliseconds) throw()
+{
+ return RelativeTime (milliseconds * 0.001);
+}
+
+const RelativeTime RelativeTime::minutes (const double numberOfMinutes) throw()
+{
+ return RelativeTime (numberOfMinutes * 60.0);
+}
+
+const RelativeTime RelativeTime::hours (const double numberOfHours) throw()
+{
+ return RelativeTime (numberOfHours * (60.0 * 60.0));
+}
+
+const RelativeTime RelativeTime::days (const double numberOfDays) throw()
+{
+ return RelativeTime (numberOfDays * (60.0 * 60.0 * 24.0));
+}
+
+const RelativeTime RelativeTime::weeks (const double numberOfWeeks) throw()
+{
+ return RelativeTime (numberOfWeeks * (60.0 * 60.0 * 24.0 * 7.0));
+}
+
+int64 RelativeTime::inMilliseconds() const throw()
+{
+ return (int64)(seconds * 1000.0);
+}
+
+double RelativeTime::inMinutes() const throw()
+{
+ return seconds / 60.0;
+}
+
+double RelativeTime::inHours() const throw()
+{
+ return seconds / (60.0 * 60.0);
+}
+
+double RelativeTime::inDays() const throw()
+{
+ return seconds / (60.0 * 60.0 * 24.0);
+}
+
+double RelativeTime::inWeeks() const throw()
+{
+ return seconds / (60.0 * 60.0 * 24.0 * 7.0);
+}
+
+const String RelativeTime::getDescription (const String& returnValueForZeroTime) const throw()
+{
+ if (seconds < 0.001 && seconds > -0.001)
+ return returnValueForZeroTime;
+
+ String result;
+
+ if (seconds < 0)
+ result = T("-");
+
+ int fieldsShown = 0;
+ int n = abs ((int) inWeeks());
+ if (n > 0)
+ {
+ result << n << ((n == 1) ? TRANS(" week ")
+ : TRANS(" weeks "));
+ ++fieldsShown;
+ }
+
+ n = abs ((int) inDays()) % 7;
+ if (n > 0)
+ {
+ result << n << ((n == 1) ? TRANS(" day ")
+ : TRANS(" days "));
+ ++fieldsShown;
+ }
+
+ if (fieldsShown < 2)
+ {
+ n = abs ((int) inHours()) % 24;
+ if (n > 0)
+ {
+ result << n << ((n == 1) ? TRANS(" hr ")
+ : TRANS(" hrs "));
+ ++fieldsShown;
+ }
+
+ if (fieldsShown < 2)
+ {
+ n = abs ((int) inMinutes()) % 60;
+ if (n > 0)
+ {
+ result << n << ((n == 1) ? TRANS(" min ")
+ : TRANS(" mins "));
+ ++fieldsShown;
+ }
+
+ if (fieldsShown < 2)
+ {
+ n = abs ((int) inSeconds()) % 60;
+ if (n > 0)
+ {
+ result << n << ((n == 1) ? TRANS(" sec ")
+ : TRANS(" secs "));
+ ++fieldsShown;
+ }
+
+ if (fieldsShown < 1)
+ {
+ n = abs ((int) inMilliseconds()) % 1000;
+ if (n > 0)
+ {
+ result << n << TRANS(" ms");
+ ++fieldsShown;
+ }
+ }
+ }
+ }
+ }
+
+ return result.trimEnd();
+}
+
+const RelativeTime& RelativeTime::operator= (const RelativeTime& other) throw()
+{
+ seconds = other.seconds;
+ return *this;
+}
+
+bool RelativeTime::operator== (const RelativeTime& other) const throw()
+{
+ return seconds == other.seconds;
+}
+
+bool RelativeTime::operator!= (const RelativeTime& other) const throw()
+{
+ return seconds != other.seconds;
+}
+
+bool RelativeTime::operator> (const RelativeTime& other) const throw()
+{
+ return seconds > other.seconds;
+}
+
+bool RelativeTime::operator< (const RelativeTime& other) const throw()
+{
+ return seconds < other.seconds;
+}
+
+bool RelativeTime::operator>= (const RelativeTime& other) const throw()
+{
+ return seconds >= other.seconds;
+}
+
+bool RelativeTime::operator<= (const RelativeTime& other) const throw()
+{
+ return seconds <= other.seconds;
+}
+
+const RelativeTime RelativeTime::operator+ (const RelativeTime& timeToAdd) const throw()
+{
+ return RelativeTime (seconds + timeToAdd.seconds);
+}
+
+const RelativeTime RelativeTime::operator- (const RelativeTime& timeToSubtract) const throw()
+{
+ return RelativeTime (seconds - timeToSubtract.seconds);
+}
+
+const RelativeTime RelativeTime::operator+ (const double secondsToAdd) const throw()
+{
+ return RelativeTime (seconds + secondsToAdd);
+}
+
+const RelativeTime RelativeTime::operator- (const double secondsToSubtract) const throw()
+{
+ return RelativeTime (seconds - secondsToSubtract);
+}
+
+const RelativeTime& RelativeTime::operator+= (const RelativeTime& timeToAdd) throw()
+{
+ seconds += timeToAdd.seconds;
+ return *this;
+}
+
+const RelativeTime& RelativeTime::operator-= (const RelativeTime& timeToSubtract) throw()
+{
+ seconds -= timeToSubtract.seconds;
+ return *this;
+}
+
+const RelativeTime& RelativeTime::operator+= (const double secondsToAdd) throw()
+{
+ seconds += secondsToAdd;
+ return *this;
+}
+
+const RelativeTime& RelativeTime::operator-= (const double secondsToSubtract) throw()
+{
+ seconds -= secondsToSubtract;
+ return *this;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_RelativeTime.cpp *********/
+
+/********* Start of inlined file: juce_SystemStats.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+void juce_initialiseStrings();
+
+const String SystemStats::getJUCEVersion() throw()
+{
+ return "JUCE v" + String (JUCE_MAJOR_VERSION) + "." + String (JUCE_MINOR_VERSION);
+}
+
+static bool juceInitialisedNonGUI = false;
+
+void JUCE_PUBLIC_FUNCTION initialiseJuce_NonGUI()
+{
+ if (! juceInitialisedNonGUI)
+ {
+#ifdef JUCE_DEBUG
+ // Some simple test code to keep an eye on things and make sure these functions
+ // work ok on all platforms. Let me know if any of these assertions fail!
+ int n = 1;
+ atomicIncrement (n);
+ jassert (atomicIncrementAndReturn (n) == 3);
+ atomicDecrement (n);
+ jassert (atomicDecrementAndReturn (n) == 1);
+
+ jassert (swapByteOrder ((uint32) 0x11223344) == 0x44332211);
+
+ // quick test to make sure the run-time lib doesn't crash on freeing a null-pointer.
+ SystemStats* nullPointer = 0;
+ juce_free (nullPointer);
+ delete[] nullPointer;
+ delete nullPointer;
+#endif
+ // Now the real initialisation..
+
+ juceInitialisedNonGUI = true;
+
+ DBG (SystemStats::getJUCEVersion());
+ juce_initialiseStrings();
+ SystemStats::initialiseStats();
+ Random::getSystemRandom().setSeed (Time::currentTimeMillis());
+ }
+}
+
+#if JUCE_WIN32
+ // This is imported from the sockets code..
+ typedef int (__stdcall juce_CloseWin32SocketLibCall) (void);
+ extern juce_CloseWin32SocketLibCall* juce_CloseWin32SocketLib;
+#endif
+
+#if JUCE_DEBUG
+ extern void juce_CheckForDanglingStreams();
+#endif
+
+void JUCE_PUBLIC_FUNCTION shutdownJuce_NonGUI()
+{
+ if (juceInitialisedNonGUI)
+ {
+#if JUCE_WIN32
+ // need to shut down sockets if they were used..
+ if (juce_CloseWin32SocketLib != 0)
+ (*juce_CloseWin32SocketLib)();
+#endif
+
+ LocalisedStrings::setCurrentMappings (0);
+ Thread::stopAllThreads (3000);
+
+#if JUCE_DEBUG
+ juce_CheckForDanglingStreams();
+#endif
+
+ juceInitialisedNonGUI = false;
+ }
+}
+
+#ifdef JUCE_DLL
+
+void* juce_Malloc (const int size)
+{
+ return malloc (size);
+}
+
+void* juce_Calloc (const int size)
+{
+ return calloc (1, size);
+}
+
+void* juce_Realloc (void* const block, const int size)
+{
+ return realloc (block, size);
+}
+
+void juce_Free (void* const block)
+{
+ free (block);
+}
+
+#if defined (JUCE_DEBUG) && JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS
+
+void* juce_DebugMalloc (const int size, const char* file, const int line)
+{
+ return _malloc_dbg (size, _NORMAL_BLOCK, file, line);
+}
+
+void* juce_DebugCalloc (const int size, const char* file, const int line)
+{
+ return _calloc_dbg (1, size, _NORMAL_BLOCK, file, line);
+}
+
+void* juce_DebugRealloc (void* const block, const int size, const char* file, const int line)
+{
+ return _realloc_dbg (block, size, _NORMAL_BLOCK, file, line);
+}
+
+void juce_DebugFree (void* const block)
+{
+ _free_dbg (block, _NORMAL_BLOCK);
+}
+
+#endif
+#endif
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_SystemStats.cpp *********/
+
+/********* Start of inlined file: juce_Time.cpp *********/
+#ifdef _MSC_VER
+ #pragma warning (disable: 4514)
+ #pragma warning (push)
+#endif
+
+#ifndef JUCE_WIN32
+ #include
+#else
+ #include
+#endif
+
+#include
+
+BEGIN_JUCE_NAMESPACE
+
+#ifdef _MSC_VER
+ #pragma warning (pop)
+
+ #ifdef _INC_TIME_INL
+ #define USE_NEW_SECURE_TIME_FNS
+ #endif
+#endif
+
+static void millisToLocal (const int64 millis, struct tm& result) throw()
+{
+ const int64 seconds = millis / 1000;
+
+ if (seconds < literal64bit (86400) || seconds >= literal64bit (2145916800))
+ {
+ // use extended maths for dates beyond 1970 to 2037..
+ const int timeZoneAdjustment = 31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000);
+ const int64 jdm = seconds + timeZoneAdjustment + literal64bit (210866803200);
+
+ const int days = (int) (jdm / literal64bit (86400));
+ const int a = 32044 + days;
+ const int b = (4 * a + 3) / 146097;
+ const int c = a - (b * 146097) / 4;
+ const int d = (4 * c + 3) / 1461;
+ const int e = c - (d * 1461) / 4;
+ const int m = (5 * e + 2) / 153;
+
+ result.tm_mday = e - (153 * m + 2) / 5 + 1;
+ result.tm_mon = m + 2 - 12 * (m / 10);
+ result.tm_year = b * 100 + d - 6700 + (m / 10);
+ result.tm_wday = (days + 1) % 7;
+ result.tm_yday = -1;
+
+ int t = (int) (jdm % literal64bit (86400));
+ result.tm_hour = t / 3600;
+ t %= 3600;
+ result.tm_min = t / 60;
+ result.tm_sec = t % 60;
+ result.tm_isdst = -1;
+ }
+ else
+ {
+ time_t now = (time_t) (seconds);
+
+#if JUCE_WIN32
+ #ifdef USE_NEW_SECURE_TIME_FNS
+ if (now >= 0 && now <= 0x793406fff)
+ localtime_s (&result, &now);
+ else
+ zeromem (&result, sizeof (result));
+ #else
+ result = *localtime (&now);
+ #endif
+#else
+ // more thread-safe
+ localtime_r (&now, &result);
+#endif
+ }
+}
+
+Time::Time() throw()
+ : millisSinceEpoch (0)
+{
+}
+
+Time::Time (const Time& other) throw()
+ : millisSinceEpoch (other.millisSinceEpoch)
+{
+}
+
+Time::Time (const int64 ms) throw()
+ : millisSinceEpoch (ms)
+{
+}
+
+Time::Time (const int year,
+ const int month,
+ const int day,
+ const int hours,
+ const int minutes,
+ const int seconds,
+ const int milliseconds) throw()
+{
+ jassert (year > 100); // year must be a 4-digit version
+
+ if (year < 1971 || year >= 2038)
+ {
+ // use extended maths for dates beyond 1970 to 2037..
+ const int timeZoneAdjustment = 31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000);
+ const int a = (13 - month) / 12;
+ const int y = year + 4800 - a;
+ const int jd = day + (153 * (month + 12 * a - 2) + 2) / 5
+ + (y * 365) + (y / 4) - (y / 100) + (y / 400)
+ - 32045;
+
+ const int64 s = ((int64) jd) * literal64bit (86400) - literal64bit (210866803200);
+
+ millisSinceEpoch = 1000 * (s + (hours * 3600 + minutes * 60 + seconds - timeZoneAdjustment))
+ + milliseconds;
+ }
+ else
+ {
+ struct tm t;
+ t.tm_year = year - 1900;
+ t.tm_mon = month;
+ t.tm_mday = day;
+ t.tm_hour = hours;
+ t.tm_min = minutes;
+ t.tm_sec = seconds;
+ t.tm_isdst = -1;
+
+ millisSinceEpoch = 1000 * (int64) mktime (&t);
+
+ if (millisSinceEpoch < 0)
+ millisSinceEpoch = 0;
+ else
+ millisSinceEpoch += milliseconds;
+ }
+}
+
+Time::~Time() throw()
+{
+}
+
+const Time& Time::operator= (const Time& other) throw()
+{
+ millisSinceEpoch = other.millisSinceEpoch;
+ return *this;
+}
+
+int64 Time::currentTimeMillis() throw()
+{
+ static uint32 lastCounterResult = 0xffffffff;
+ static int64 correction = 0;
+
+ const uint32 now = getMillisecondCounter();
+
+ // check the counter hasn't wrapped (also triggered the first time this function is called)
+ if (now < lastCounterResult)
+ {
+ // double-check it's actually wrapped, in case multi-cpu machines have timers that drift a bit.
+ if (lastCounterResult == 0xffffffff || now < lastCounterResult - 10)
+ {
+ // get the time once using normal library calls, and store the difference needed to
+ // turn the millisecond counter into a real time.
+#if JUCE_WIN32
+ struct _timeb t;
+ #ifdef USE_NEW_SECURE_TIME_FNS
+ _ftime_s (&t);
+ #else
+ _ftime (&t);
+ #endif
+ correction = (((int64) t.time) * 1000 + t.millitm) - now;
+#else
+ struct timeval tv;
+ struct timezone tz;
+ gettimeofday (&tv, &tz);
+ correction = (((int64) tv.tv_sec) * 1000 + tv.tv_usec / 1000) - now;
+#endif
+ }
+ }
+
+ lastCounterResult = now;
+
+ return correction + now;
+}
+
+uint32 juce_millisecondsSinceStartup() throw();
+static uint32 lastMSCounterValue = 0;
+
+uint32 Time::getMillisecondCounter() throw()
+{
+ const uint32 now = juce_millisecondsSinceStartup();
+
+ if (now < lastMSCounterValue)
+ {
+ // in multi-threaded apps this might be called concurrently, so
+ // make sure that our last counter value only increases and doesn't
+ // go backwards..
+ if (now < lastMSCounterValue - 1000)
+ lastMSCounterValue = now;
+ }
+ else
+ {
+ lastMSCounterValue = now;
+ }
+
+ return now;
+}
+
+uint32 Time::getApproximateMillisecondCounter() throw()
+{
+ jassert (lastMSCounterValue != 0);
+ return lastMSCounterValue;
+}
+
+void Time::waitForMillisecondCounter (const uint32 targetTime) throw()
+{
+ for (;;)
+ {
+ const uint32 now = getMillisecondCounter();
+
+ if (now >= targetTime)
+ break;
+
+ const int toWait = targetTime - now;
+
+ if (toWait > 2)
+ {
+ Thread::sleep (jmin (20, toWait >> 1));
+ }
+ else
+ {
+ // xxx should consider using mutex_pause on the mac as it apparently
+ // makes it seem less like a spinlock and avoids lowering the thread pri.
+ for (int i = 10; --i >= 0;)
+ Thread::yield();
+ }
+ }
+}
+
+double Time::highResolutionTicksToSeconds (const int64 ticks) throw()
+{
+ return ticks / (double) getHighResolutionTicksPerSecond();
+}
+
+int64 Time::secondsToHighResolutionTicks (const double seconds) throw()
+{
+ return (int64) (seconds * (double) getHighResolutionTicksPerSecond());
+}
+
+const Time JUCE_CALLTYPE Time::getCurrentTime() throw()
+{
+ return Time (currentTimeMillis());
+}
+
+const String Time::toString (const bool includeDate,
+ const bool includeTime,
+ const bool includeSeconds,
+ const bool use24HourClock) const throw()
+{
+ String result;
+
+ if (includeDate)
+ {
+ result << getDayOfMonth() << ' '
+ << getMonthName (true) << ' '
+ << getYear();
+
+ if (includeTime)
+ result << ' ';
+ }
+
+ if (includeTime)
+ {
+ if (includeSeconds)
+ {
+ result += String::formatted (T("%d:%02d:%02d "),
+ (use24HourClock) ? getHours()
+ : getHoursInAmPmFormat(),
+ getMinutes(),
+ getSeconds());
+ }
+ else
+ {
+ result += String::formatted (T("%d.%02d"),
+ (use24HourClock) ? getHours()
+ : getHoursInAmPmFormat(),
+ getMinutes());
+ }
+
+ if (! use24HourClock)
+ result << (isAfternoon() ? "pm" : "am");
+ }
+
+ return result.trimEnd();
+}
+
+const String Time::formatted (const tchar* const format) const throw()
+{
+ tchar buffer[80];
+
+ struct tm t;
+ millisToLocal (millisSinceEpoch, t);
+
+ if (CharacterFunctions::ftime (buffer, 79, format, &t) <= 0)
+ {
+ int bufferSize = 128;
+
+ for (;;)
+ {
+ MemoryBlock mb (bufferSize * sizeof (tchar));
+ tchar* const b = (tchar*) mb.getData();
+
+ if (CharacterFunctions::ftime (b, bufferSize, format, &t) > 0)
+ return String (b);
+
+ bufferSize += 128;
+ }
+ }
+
+ return String (buffer);
+}
+
+int Time::getYear() const throw()
+{
+ struct tm t;
+ millisToLocal (millisSinceEpoch, t);
+ return t.tm_year + 1900;
+}
+
+int Time::getMonth() const throw()
+{
+ struct tm t;
+ millisToLocal (millisSinceEpoch, t);
+ return t.tm_mon;
+}
+
+int Time::getDayOfMonth() const throw()
+{
+ struct tm t;
+ millisToLocal (millisSinceEpoch, t);
+ return t.tm_mday;
+}
+
+int Time::getDayOfWeek() const throw()
+{
+ struct tm t;
+ millisToLocal (millisSinceEpoch, t);
+ return t.tm_wday;
+}
+
+int Time::getHours() const throw()
+{
+ struct tm t;
+ millisToLocal (millisSinceEpoch, t);
+ return t.tm_hour;
+}
+
+int Time::getHoursInAmPmFormat() const throw()
+{
+ const int hours = getHours();
+
+ if (hours == 0)
+ return 12;
+ else if (hours <= 12)
+ return hours;
+ else
+ return hours - 12;
+}
+
+bool Time::isAfternoon() const throw()
+{
+ return getHours() >= 12;
+}
+
+static int extendedModulo (const int64 value, const int modulo) throw()
+{
+ return (int) (value >= 0 ? (value % modulo)
+ : (value - ((value / modulo) + 1) * modulo));
+}
+
+int Time::getMinutes() const throw()
+{
+ return extendedModulo (millisSinceEpoch / 60000, 60);
+}
+
+int Time::getSeconds() const throw()
+{
+ return extendedModulo (millisSinceEpoch / 1000, 60);
+}
+
+int Time::getMilliseconds() const throw()
+{
+ return extendedModulo (millisSinceEpoch, 1000);
+}
+
+bool Time::isDaylightSavingTime() const throw()
+{
+ struct tm t;
+ millisToLocal (millisSinceEpoch, t);
+ return t.tm_isdst != 0;
+}
+
+const String Time::getTimeZone() const throw()
+{
+ String zone[2];
+
+#if JUCE_WIN32
+ _tzset();
+
+ #ifdef USE_NEW_SECURE_TIME_FNS
+ {
+ char name [128];
+ size_t length;
+
+ for (int i = 0; i < 2; ++i)
+ {
+ zeromem (name, sizeof (name));
+ _get_tzname (&length, name, 127, i);
+ zone[i] = name;
+ }
+ }
+ #else
+ const char** const zonePtr = (const char**) _tzname;
+ zone[0] = zonePtr[0];
+ zone[1] = zonePtr[1];
+ #endif
+#else
+ tzset();
+ const char** const zonePtr = (const char**) tzname;
+ zone[0] = zonePtr[0];
+ zone[1] = zonePtr[1];
+#endif
+
+ if (isDaylightSavingTime())
+ {
+ zone[0] = zone[1];
+
+ if (zone[0].length() > 3
+ && zone[0].containsIgnoreCase (T("daylight"))
+ && zone[0].contains (T("GMT")))
+ zone[0] = "BST";
+ }
+
+ return zone[0].substring (0, 3);
+}
+
+const String Time::getMonthName (const bool threeLetterVersion) const throw()
+{
+ return getMonthName (getMonth(), threeLetterVersion);
+}
+
+const String Time::getWeekdayName (const bool threeLetterVersion) const throw()
+{
+ return getWeekdayName (getDayOfWeek(), threeLetterVersion);
+}
+
+const String Time::getMonthName (int monthNumber,
+ const bool threeLetterVersion) throw()
+{
+ const char* const shortMonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ const char* const longMonthNames[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
+
+ monthNumber %= 12;
+
+ return TRANS (threeLetterVersion ? shortMonthNames [monthNumber]
+ : longMonthNames [monthNumber]);
+}
+
+const String Time::getWeekdayName (int day,
+ const bool threeLetterVersion) throw()
+{
+ const char* const shortDayNames[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+ const char* const longDayNames[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
+
+ day %= 7;
+
+ return TRANS (threeLetterVersion ? shortDayNames [day]
+ : longDayNames [day]);
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_Time.cpp *********/
+
+/********* Start of inlined file: juce_BitArray.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+BitArray::BitArray() throw()
+ : numValues (4),
+ highestBit (-1),
+ negative (false)
+{
+ values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1));
+}
+
+BitArray::BitArray (const int value) throw()
+ : numValues (4),
+ highestBit (31),
+ negative (value < 0)
+{
+ values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1));
+ values[0] = abs (value);
+ highestBit = getHighestBit();
+}
+
+BitArray::BitArray (int64 value) throw()
+ : numValues (4),
+ highestBit (63),
+ negative (value < 0)
+{
+ values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1));
+
+ if (value < 0)
+ value = -value;
+
+ values[0] = (unsigned int) value;
+ values[1] = (unsigned int) (value >> 32);
+ highestBit = getHighestBit();
+}
+
+BitArray::BitArray (const unsigned int value) throw()
+ : numValues (4),
+ highestBit (31),
+ negative (false)
+{
+ values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1));
+ values[0] = value;
+ highestBit = getHighestBit();
+}
+
+BitArray::BitArray (const BitArray& other) throw()
+ : numValues (jmax (4, (other.highestBit >> 5) + 1)),
+ highestBit (other.getHighestBit()),
+ negative (other.negative)
+{
+ const int bytes = sizeof (unsigned int) * (numValues + 1);
+ values = (unsigned int*) juce_malloc (bytes);
+ memcpy (values, other.values, bytes);
+}
+
+BitArray::~BitArray() throw()
+{
+ juce_free (values);
+}
+
+const BitArray& BitArray::operator= (const BitArray& other) throw()
+{
+ if (this != &other)
+ {
+ juce_free (values);
+
+ highestBit = other.getHighestBit();
+ numValues = jmax (4, (highestBit >> 5) + 1);
+ negative = other.negative;
+ const int memSize = sizeof (unsigned int) * (numValues + 1);
+ values = (unsigned int*)juce_malloc (memSize);
+ memcpy (values, other.values, memSize);
+ }
+
+ return *this;
+}
+
+// result == 0 = the same
+// result < 0 = this number is smaller
+// result > 0 = this number is bigger
+int BitArray::compare (const BitArray& other) const throw()
+{
+ if (isNegative() == other.isNegative())
+ {
+ const int absComp = compareAbsolute (other);
+ return isNegative() ? -absComp : absComp;
+ }
+ else
+ {
+ return isNegative() ? -1 : 1;
+ }
+}
+
+int BitArray::compareAbsolute (const BitArray& other) const throw()
+{
+ const int h1 = getHighestBit();
+ const int h2 = other.getHighestBit();
+
+ if (h1 > h2)
+ return 1;
+ else if (h1 < h2)
+ return -1;
+
+ for (int i = (h1 >> 5) + 1; --i >= 0;)
+ if (values[i] != other.values[i])
+ return (values[i] > other.values[i]) ? 1 : -1;
+
+ return 0;
+}
+
+bool BitArray::operator== (const BitArray& other) const throw()
+{
+ return compare (other) == 0;
+}
+
+bool BitArray::operator!= (const BitArray& other) const throw()
+{
+ return compare (other) != 0;
+}
+
+bool BitArray::operator[] (const int bit) const throw()
+{
+ return bit >= 0 && bit <= highestBit
+ && ((values [bit >> 5] & (1 << (bit & 31))) != 0);
+}
+
+bool BitArray::isEmpty() const throw()
+{
+ return getHighestBit() < 0;
+}
+
+void BitArray::clear() throw()
+{
+ if (numValues > 16)
+ {
+ juce_free (values);
+ numValues = 4;
+ values = (unsigned int*) juce_calloc (sizeof (unsigned int) * (numValues + 1));
+ }
+ else
+ {
+ zeromem (values, sizeof (unsigned int) * (numValues + 1));
+ }
+
+ highestBit = -1;
+ negative = false;
+}
+
+void BitArray::setBit (const int bit) throw()
+{
+ if (bit >= 0)
+ {
+ if (bit > highestBit)
+ {
+ ensureSize (bit >> 5);
+ highestBit = bit;
+ }
+
+ values [bit >> 5] |= (1 << (bit & 31));
+ }
+}
+
+void BitArray::setBit (const int bit,
+ const bool shouldBeSet) throw()
+{
+ if (shouldBeSet)
+ setBit (bit);
+ else
+ clearBit (bit);
+}
+
+void BitArray::clearBit (const int bit) throw()
+{
+ if (bit >= 0 && bit <= highestBit)
+ values [bit >> 5] &= ~(1 << (bit & 31));
+}
+
+void BitArray::setRange (int startBit,
+ int numBits,
+ const bool shouldBeSet) throw()
+{
+ while (--numBits >= 0)
+ setBit (startBit++, shouldBeSet);
+}
+
+void BitArray::insertBit (const int bit,
+ const bool shouldBeSet) throw()
+{
+ if (bit >= 0)
+ shiftBits (1, bit);
+
+ setBit (bit, shouldBeSet);
+}
+
+void BitArray::andWith (const BitArray& other) throw()
+{
+ // this operation will only work with the absolute values
+ jassert (isNegative() == other.isNegative());
+
+ int n = numValues;
+
+ while (n > other.numValues)
+ values[--n] = 0;
+
+ while (--n >= 0)
+ values[n] &= other.values[n];
+
+ if (other.highestBit < highestBit)
+ highestBit = other.highestBit;
+
+ highestBit = getHighestBit();
+}
+
+void BitArray::orWith (const BitArray& other) throw()
+{
+ if (other.highestBit < 0)
+ return;
+
+ // this operation will only work with the absolute values
+ jassert (isNegative() == other.isNegative());
+
+ ensureSize (other.highestBit >> 5);
+
+ int n = (other.highestBit >> 5) + 1;
+
+ while (--n >= 0)
+ values[n] |= other.values[n];
+
+ if (other.highestBit > highestBit)
+ highestBit = other.highestBit;
+
+ highestBit = getHighestBit();
+}
+
+void BitArray::xorWith (const BitArray& other) throw()
+{
+ if (other.highestBit < 0)
+ return;
+
+ // this operation will only work with the absolute values
+ jassert (isNegative() == other.isNegative());
+
+ ensureSize (other.highestBit >> 5);
+
+ int n = (other.highestBit >> 5) + 1;
+
+ while (--n >= 0)
+ values[n] ^= other.values[n];
+
+ if (other.highestBit > highestBit)
+ highestBit = other.highestBit;
+
+ highestBit = getHighestBit();
+}
+
+void BitArray::add (const BitArray& other) throw()
+{
+ if (other.isNegative())
+ {
+ BitArray o (other);
+ o.negate();
+ subtract (o);
+ return;
+ }
+
+ if (isNegative())
+ {
+ if (compareAbsolute (other) < 0)
+ {
+ BitArray temp (*this);
+ temp.negate();
+ *this = other;
+ subtract (temp);
+ }
+ else
+ {
+ negate();
+ subtract (other);
+ negate();
+ }
+
+ return;
+ }
+
+ if (other.highestBit > highestBit)
+ highestBit = other.highestBit;
+
+ ++highestBit;
+
+ const int numInts = (highestBit >> 5) + 1;
+ ensureSize (numInts);
+
+ int64 remainder = 0;
+
+ for (int i = 0; i <= numInts; ++i)
+ {
+ if (i < numValues)
+ remainder += values[i];
+
+ if (i < other.numValues)
+ remainder += other.values[i];
+
+ values[i] = (unsigned int) remainder;
+ remainder >>= 32;
+ }
+
+ jassert (remainder == 0);
+ highestBit = getHighestBit();
+}
+
+void BitArray::subtract (const BitArray& other) throw()
+{
+ if (other.isNegative())
+ {
+ BitArray o (other);
+ o.negate();
+ add (o);
+ return;
+ }
+
+ if (! isNegative())
+ {
+ if (compareAbsolute (other) < 0)
+ {
+ BitArray temp (*this);
+ *this = other;
+ subtract (temp);
+ negate();
+ return;
+ }
+ }
+ else
+ {
+ negate();
+ add (other);
+ negate();
+ return;
+ }
+
+ const int numInts = (highestBit >> 5) + 1;
+ const int maxOtherInts = (other.highestBit >> 5) + 1;
+ int64 amountToSubtract = 0;
+
+ for (int i = 0; i <= numInts; ++i)
+ {
+ if (i <= maxOtherInts)
+ amountToSubtract += (int64)other.values[i];
+
+ if (values[i] >= amountToSubtract)
+ {
+ values[i] = (unsigned int) (values[i] - amountToSubtract);
+ amountToSubtract = 0;
+ }
+ else
+ {
+ const int64 n = ((int64) values[i] + (((int64) 1) << 32)) - amountToSubtract;
+ values[i] = (unsigned int) n;
+ amountToSubtract = 1;
+ }
+ }
+}
+
+void BitArray::multiplyBy (const BitArray& other) throw()
+{
+ BitArray total;
+ highestBit = getHighestBit();
+ const bool wasNegative = isNegative();
+ setNegative (false);
+
+ for (int i = 0; i <= highestBit; ++i)
+ {
+ if (operator[](i))
+ {
+ BitArray n (other);
+ n.setNegative (false);
+ n.shiftBits (i);
+ total.add (n);
+ }
+ }
+
+ *this = total;
+ negative = wasNegative ^ other.isNegative();
+}
+
+void BitArray::divideBy (const BitArray& divisor, BitArray& remainder) throw()
+{
+ jassert (this != &remainder); // (can't handle passing itself in to get the remainder)
+
+ const int divHB = divisor.getHighestBit();
+ const int ourHB = getHighestBit();
+
+ if (divHB < 0 || ourHB < 0)
+ {
+ // division by zero
+ remainder.clear();
+ clear();
+ }
+ else
+ {
+ remainder = *this;
+ remainder.setNegative (false);
+ const bool wasNegative = isNegative();
+ clear();
+
+ BitArray temp (divisor);
+ temp.setNegative (false);
+
+ int leftShift = ourHB - divHB;
+ temp.shiftBits (leftShift);
+
+ while (leftShift >= 0)
+ {
+ if (remainder.compareAbsolute (temp) >= 0)
+ {
+ remainder.subtract (temp);
+ setBit (leftShift);
+ }
+
+ if (--leftShift >= 0)
+ temp.shiftBits (-1);
+ }
+
+ negative = wasNegative ^ divisor.isNegative();
+ remainder.setNegative (wasNegative);
+ }
+}
+
+void BitArray::modulo (const BitArray& divisor) throw()
+{
+ BitArray remainder;
+ divideBy (divisor, remainder);
+ *this = remainder;
+}
+
+static const BitArray simpleGCD (BitArray* m, BitArray* n) throw()
+{
+ while (! m->isEmpty())
+ {
+ if (n->compareAbsolute (*m) > 0)
+ swapVariables (m, n);
+
+ m->subtract (*n);
+ }
+
+ return *n;
+}
+
+const BitArray BitArray::findGreatestCommonDivisor (BitArray n) const throw()
+{
+ BitArray m (*this);
+
+ while (! n.isEmpty())
+ {
+ if (abs (m.getHighestBit() - n.getHighestBit()) <= 16)
+ return simpleGCD (&m, &n);
+
+ BitArray temp1 (m), temp2;
+ temp1.divideBy (n, temp2);
+
+ m = n;
+ n = temp2;
+ }
+
+ return m;
+}
+
+void BitArray::exponentModulo (const BitArray& exponent,
+ const BitArray& modulus) throw()
+{
+ BitArray exp (exponent);
+ exp.modulo (modulus);
+
+ BitArray value (*this);
+ value.modulo (modulus);
+
+ clear();
+ setBit (0);
+
+ while (! exp.isEmpty())
+ {
+ if (exp [0])
+ {
+ multiplyBy (value);
+ this->modulo (modulus);
+ }
+
+ value.multiplyBy (value);
+ value.modulo (modulus);
+
+ exp.shiftBits (-1);
+ }
+}
+
+void BitArray::inverseModulo (const BitArray& modulus) throw()
+{
+ const BitArray one (1);
+
+ if (modulus == one || modulus.isNegative())
+ {
+ clear();
+ return;
+ }
+
+ if (isNegative() || compareAbsolute (modulus) >= 0)
+ this->modulo (modulus);
+
+ if (*this == one)
+ return;
+
+ if (! (*this)[0])
+ {
+ // not invertible
+ clear();
+ return;
+ }
+
+ BitArray a1 (modulus);
+ BitArray a2 (*this);
+ BitArray b1 (modulus);
+ BitArray b2 (1);
+
+ while (a2 != one)
+ {
+ BitArray temp1, temp2, multiplier (a1);
+ multiplier.divideBy (a2, temp1);
+
+ temp1 = a2;
+ temp1.multiplyBy (multiplier);
+ temp2 = a1;
+ temp2.subtract (temp1);
+ a1 = a2;
+ a2 = temp2;
+
+ temp1 = b2;
+ temp1.multiplyBy (multiplier);
+ temp2 = b1;
+ temp2.subtract (temp1);
+ b1 = b2;
+ b2 = temp2;
+ }
+
+ while (b2.isNegative())
+ b2.add (modulus);
+
+ b2.modulo (modulus);
+ *this = b2;
+}
+
+void BitArray::shiftBits (int bits, const int startBit) throw()
+{
+ if (highestBit < 0)
+ return;
+
+ if (startBit > 0)
+ {
+ if (bits < 0)
+ {
+ // right shift
+ for (int i = startBit; i <= highestBit; ++i)
+ setBit (i, operator[] (i - bits));
+
+ highestBit = getHighestBit();
+ }
+ else if (bits > 0)
+ {
+ // left shift
+ for (int i = highestBit + 1; --i >= startBit;)
+ setBit (i + bits, operator[] (i));
+
+ while (--bits >= 0)
+ clearBit (bits + startBit);
+ }
+ }
+ else
+ {
+ if (bits < 0)
+ {
+ // right shift
+ bits = -bits;
+
+ if (bits > highestBit)
+ {
+ clear();
+ }
+ else
+ {
+ const int wordsToMove = bits >> 5;
+ int top = 1 + (highestBit >> 5) - wordsToMove;
+ highestBit -= bits;
+
+ if (wordsToMove > 0)
+ {
+ int i;
+ for (i = 0; i < top; ++i)
+ values [i] = values [i + wordsToMove];
+
+ for (i = 0; i < wordsToMove; ++i)
+ values [top + i] = 0;
+
+ bits &= 31;
+ }
+
+ if (bits != 0)
+ {
+ const int invBits = 32 - bits;
+
+ --top;
+ for (int i = 0; i < top; ++i)
+ values[i] = (values[i] >> bits) | (values [i + 1] << invBits);
+
+ values[top] = (values[top] >> bits);
+ }
+
+ highestBit = getHighestBit();
+ }
+ }
+ else if (bits > 0)
+ {
+ // left shift
+ ensureSize (((highestBit + bits) >> 5) + 1);
+
+ const int wordsToMove = bits >> 5;
+ int top = 1 + (highestBit >> 5);
+ highestBit += bits;
+
+ if (wordsToMove > 0)
+ {
+ int i;
+ for (i = top; --i >= 0;)
+ values [i + wordsToMove] = values [i];
+
+ for (i = 0; i < wordsToMove; ++i)
+ values [i] = 0;
+
+ bits &= 31;
+ }
+
+ if (bits != 0)
+ {
+ const int invBits = 32 - bits;
+
+ for (int i = top + 1 + wordsToMove; --i > wordsToMove;)
+ values[i] = (values[i] << bits) | (values [i - 1] >> invBits);
+
+ values [wordsToMove] = values [wordsToMove] << bits;
+ }
+
+ highestBit = getHighestBit();
+ }
+ }
+}
+
+int BitArray::getBitRangeAsInt (const int startBit, int numBits) const throw()
+{
+ if (numBits > 32)
+ {
+ jassertfalse
+ numBits = 32;
+ }
+
+ if (startBit == 0)
+ {
+ if (numBits < 32)
+ return values[0] & ((1 << numBits) - 1);
+
+ return values[0];
+ }
+
+ int n = 0;
+ for (int i = numBits; --i >= 0;)
+ {
+ n <<= 1;
+
+ if (operator[] (startBit + i))
+ n |= 1;
+ }
+
+ return n;
+}
+
+void BitArray::setBitRangeAsInt (const int startBit, int numBits, unsigned int valueToSet) throw()
+{
+ if (numBits > 32)
+ {
+ jassertfalse
+ numBits = 32;
+ }
+
+ for (int i = 0; i < numBits; ++i)
+ {
+ setBit (startBit + i, (valueToSet & 1) != 0);
+ valueToSet >>= 1;
+ }
+}
+
+void BitArray::fillBitsRandomly (int startBit, int numBits) throw()
+{
+ highestBit = jmax (highestBit, startBit + numBits);
+ ensureSize (((startBit + numBits) >> 5) + 1);
+
+ while ((startBit & 31) != 0 && numBits > 0)
+ {
+ setBit (startBit++, Random::getSystemRandom().nextBool());
+
+ --numBits;
+ }
+
+ while (numBits >= 32)
+ {
+ values [startBit >> 5] = (unsigned int) Random::getSystemRandom().nextInt();
+
+ startBit += 32;
+ numBits -= 32;
+ }
+
+ while (--numBits >= 0)
+ {
+ setBit (startBit + numBits, Random::getSystemRandom().nextBool());
+ }
+
+ highestBit = getHighestBit();
+}
+
+void BitArray::createRandomNumber (const BitArray& maximumValue) throw()
+{
+ clear();
+
+ do
+ {
+ fillBitsRandomly (0, maximumValue.getHighestBit() + 1);
+ }
+ while (compare (maximumValue) >= 0);
+}
+
+bool BitArray::isNegative() const throw()
+{
+ return negative && ! isEmpty();
+}
+
+void BitArray::setNegative (const bool neg) throw()
+{
+ negative = neg;
+}
+
+void BitArray::negate() throw()
+{
+ negative = (! negative) && ! isEmpty();
+}
+
+int BitArray::countNumberOfSetBits() const throw()
+{
+ int total = 0;
+
+ for (int i = (highestBit >> 5) + 1; --i >= 0;)
+ {
+ unsigned int n = values[i];
+
+ if (n == 0xffffffff)
+ {
+ total += 32;
+ }
+ else
+ {
+ while (n != 0)
+ {
+ total += (n & 1);
+ n >>= 1;
+ }
+ }
+ }
+
+ return total;
+}
+
+int BitArray::getHighestBit() const throw()
+{
+ for (int i = highestBit + 1; --i >= 0;)
+ if ((values [i >> 5] & (1 << (i & 31))) != 0)
+ return i;
+
+ return -1;
+}
+
+int BitArray::findNextSetBit (int i) const throw()
+{
+ for (; i <= highestBit; ++i)
+ if ((values [i >> 5] & (1 << (i & 31))) != 0)
+ return i;
+
+ return -1;
+}
+
+int BitArray::findNextClearBit (int i) const throw()
+{
+ for (; i <= highestBit; ++i)
+ if ((values [i >> 5] & (1 << (i & 31))) == 0)
+ break;
+
+ return i;
+}
+
+void BitArray::ensureSize (const int numVals) throw()
+{
+ if (numVals + 2 >= numValues)
+ {
+ int oldSize = numValues;
+ numValues = ((numVals + 2) * 3) / 2;
+ values = (unsigned int*) juce_realloc (values, sizeof (unsigned int) * numValues + 4);
+
+ while (oldSize < numValues)
+ values [oldSize++] = 0;
+ }
+}
+
+const String BitArray::toString (const int base) const throw()
+{
+ String s;
+ BitArray v (*this);
+
+ if (base == 2 || base == 8 || base == 16)
+ {
+ const int bits = (base == 2) ? 1 : (base == 8 ? 3 : 4);
+ static const tchar* const hexDigits = T("0123456789abcdef");
+
+ for (;;)
+ {
+ const int remainder = v.getBitRangeAsInt (0, bits);
+
+ v.shiftBits (-bits);
+
+ if (remainder == 0 && v.isEmpty())
+ break;
+
+ s = String::charToString (hexDigits [remainder]) + s;
+ }
+ }
+ else if (base == 10)
+ {
+ const BitArray ten (10);
+ BitArray remainder;
+
+ for (;;)
+ {
+ v.divideBy (ten, remainder);
+
+ if (remainder.isEmpty() && v.isEmpty())
+ break;
+
+ s = String (remainder.getBitRangeAsInt (0, 8)) + s;
+ }
+ }
+ else
+ {
+ jassertfalse // can't do the specified base
+ return String::empty;
+ }
+
+ if (s.isEmpty())
+ return T("0");
+
+ return isNegative() ? T("-") + s : s;
+}
+
+void BitArray::parseString (const String& text,
+ const int base) throw()
+{
+ clear();
+ const tchar* t = (const tchar*) text;
+
+ if (base == 2 || base == 8 || base == 16)
+ {
+ const int bits = (base == 2) ? 1 : (base == 8 ? 3 : 4);
+
+ for (;;)
+ {
+ const tchar c = *t++;
+ const int digit = CharacterFunctions::getHexDigitValue (c);
+
+ if (((unsigned int) digit) < (unsigned int) base)
+ {
+ shiftBits (bits);
+ add (digit);
+ }
+ else if (c == 0)
+ {
+ break;
+ }
+ }
+ }
+ else if (base == 10)
+ {
+ const BitArray ten ((unsigned int) 10);
+
+ for (;;)
+ {
+ const tchar c = *t++;
+
+ if (c >= T('0') && c <= T('9'))
+ {
+ multiplyBy (ten);
+ add ((int) (c - T('0')));
+ }
+ else if (c == 0)
+ {
+ break;
+ }
+ }
+ }
+
+ setNegative (text.trimStart().startsWithChar (T('-')));
+}
+
+const MemoryBlock BitArray::toMemoryBlock() const throw()
+{
+ const int numBytes = (getHighestBit() + 7) >> 3;
+ MemoryBlock mb (numBytes);
+
+ for (int i = 0; i < numBytes; ++i)
+ mb[i] = (uint8) getBitRangeAsInt (i << 3, 8);
+
+ return mb;
+}
+
+void BitArray::loadFromMemoryBlock (const MemoryBlock& data) throw()
+{
+ clear();
+
+ for (int i = data.getSize(); --i >= 0;)
+ this->setBitRangeAsInt (i << 3, 8, data [i]);
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_BitArray.cpp *********/
+
+/********* Start of inlined file: juce_MemoryBlock.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+MemoryBlock::MemoryBlock() throw()
+ : data (0),
+ size (0)
+{
+}
+
+MemoryBlock::MemoryBlock (const int initialSize,
+ const bool initialiseToZero) throw()
+{
+ if (initialSize > 0)
+ {
+ size = initialSize;
+
+ if (initialiseToZero)
+ data = (char*) juce_calloc (initialSize);
+ else
+ data = (char*) juce_malloc (initialSize);
+ }
+ else
+ {
+ data = 0;
+ size = 0;
+ }
+}
+
+MemoryBlock::MemoryBlock (const MemoryBlock& other) throw()
+ : data (0),
+ size (other.size)
+{
+ if (size > 0)
+ {
+ jassert (other.data != 0);
+ data = (char*) juce_malloc (size);
+ memcpy (data, other.data, size);
+ }
+}
+
+MemoryBlock::MemoryBlock (const void* const dataToInitialiseFrom,
+ const int sizeInBytes) throw()
+ : data (0),
+ size (jmax (0, sizeInBytes))
+{
+ jassert (sizeInBytes >= 0);
+
+ if (size > 0)
+ {
+ jassert (dataToInitialiseFrom != 0); // non-zero size, but a zero pointer passed-in?
+
+ data = (char*) juce_malloc (size);
+
+ if (dataToInitialiseFrom != 0)
+ memcpy (data, dataToInitialiseFrom, size);
+ }
+}
+
+MemoryBlock::~MemoryBlock() throw()
+{
+ jassert (size >= 0); // should never happen
+ jassert (size == 0 || data != 0); // non-zero size but no data allocated?
+
+ juce_free (data);
+}
+
+const MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) throw()
+{
+ if (this != &other)
+ {
+ setSize (other.size, false);
+ memcpy (data, other.data, size);
+ }
+
+ return *this;
+}
+
+bool MemoryBlock::operator== (const MemoryBlock& other) const throw()
+{
+ return (size == other.size)
+ && (memcmp (data, other.data, size) == 0);
+}
+
+bool MemoryBlock::operator!= (const MemoryBlock& other) const throw()
+{
+ return ! operator== (other);
+}
+
+// this will resize the block to this size
+void MemoryBlock::setSize (const int newSize,
+ const bool initialiseToZero) throw()
+{
+ if (size != newSize)
+ {
+ if (newSize <= 0)
+ {
+ juce_free (data);
+ data = 0;
+ size = 0;
+ }
+ else
+ {
+ if (data != 0)
+ {
+ data = (char*) juce_realloc (data, newSize);
+
+ if (initialiseToZero && (newSize > size))
+ zeromem (data + size, newSize - size);
+ }
+ else
+ {
+ if (initialiseToZero)
+ data = (char*) juce_calloc (newSize);
+ else
+ data = (char*) juce_malloc (newSize);
+ }
+
+ size = newSize;
+ }
+ }
+}
+
+void MemoryBlock::ensureSize (const int minimumSize,
+ const bool initialiseToZero) throw()
+{
+ if (size < minimumSize)
+ setSize (minimumSize, initialiseToZero);
+}
+
+void MemoryBlock::fillWith (const uint8 value) throw()
+{
+ memset (data, (int) value, size);
+}
+
+void MemoryBlock::append (const void* const srcData,
+ const int numBytes) throw()
+{
+ if (numBytes > 0)
+ {
+ const int oldSize = size;
+ setSize (size + numBytes);
+ memcpy (data + oldSize, srcData, numBytes);
+ }
+}
+
+void MemoryBlock::copyFrom (const void* const src, int offset, int num) throw()
+{
+ const char* d = (const char*) src;
+
+ if (offset < 0)
+ {
+ d -= offset;
+ num -= offset;
+ offset = 0;
+ }
+
+ if (offset + num > size)
+ num = size - offset;
+
+ if (num > 0)
+ memcpy (data + offset, d, num);
+}
+
+void MemoryBlock::copyTo (void* const dst, int offset, int num) const throw()
+{
+ char* d = (char*) dst;
+
+ if (offset < 0)
+ {
+ zeromem (d, -offset);
+ d -= offset;
+
+ num += offset;
+ offset = 0;
+ }
+
+ if (offset + num > size)
+ {
+ const int newNum = size - offset;
+ zeromem (d + newNum, num - newNum);
+ num = newNum;
+ }
+
+ if (num > 0)
+ memcpy (d, data + offset, num);
+}
+
+void MemoryBlock::removeSection (int startByte, int numBytesToRemove) throw()
+{
+ if (startByte < 0)
+ {
+ numBytesToRemove += startByte;
+ startByte = 0;
+ }
+
+ if (startByte + numBytesToRemove >= size)
+ {
+ setSize (startByte);
+ }
+ else if (numBytesToRemove > 0)
+ {
+ memmove (data + startByte,
+ data + startByte + numBytesToRemove,
+ size - (startByte + numBytesToRemove));
+
+ setSize (size - numBytesToRemove);
+ }
+}
+
+const String MemoryBlock::toString() const throw()
+{
+ return String (data, size);
+}
+
+int MemoryBlock::getBitRange (const int bitRangeStart, int numBits) const throw()
+{
+ int res = 0;
+
+ int byte = bitRangeStart >> 3;
+ int offsetInByte = bitRangeStart & 7;
+ int bitsSoFar = 0;
+
+ while (numBits > 0 && byte < size)
+ {
+ const int bitsThisTime = jmin (numBits, 8 - offsetInByte);
+ const int mask = (0xff >> (8 - bitsThisTime)) << offsetInByte;
+
+ res |= (((data[byte] & mask) >> offsetInByte) << bitsSoFar);
+
+ bitsSoFar += bitsThisTime;
+ numBits -= bitsThisTime;
+ ++byte;
+ offsetInByte = 0;
+ }
+
+ return res;
+}
+
+void MemoryBlock::setBitRange (const int bitRangeStart, int numBits, int bitsToSet) throw()
+{
+ int byte = bitRangeStart >> 3;
+ int offsetInByte = bitRangeStart & 7;
+ unsigned int mask = ~((((unsigned int)0xffffffff) << (32 - numBits)) >> (32 - numBits));
+
+ while (numBits > 0 && byte < size)
+ {
+ const int bitsThisTime = jmin (numBits, 8 - offsetInByte);
+
+ const unsigned int tempMask = (mask << offsetInByte) | ~((((unsigned int)0xffffffff) >> offsetInByte) << offsetInByte);
+ const unsigned int tempBits = bitsToSet << offsetInByte;
+
+ data[byte] = (char)((data[byte] & tempMask) | tempBits);
+
+ ++byte;
+ numBits -= bitsThisTime;
+ bitsToSet >>= bitsThisTime;
+ mask >>= bitsThisTime;
+ offsetInByte = 0;
+ }
+}
+
+void MemoryBlock::loadFromHexString (const String& hex) throw()
+{
+ ensureSize (hex.length() >> 1);
+ char* dest = data;
+ int i = 0;
+
+ for (;;)
+ {
+ int byte = 0;
+
+ for (int loop = 2; --loop >= 0;)
+ {
+ byte <<= 4;
+
+ for (;;)
+ {
+ const tchar c = hex [i++];
+
+ if (c >= T('0') && c <= T('9'))
+ {
+ byte |= c - T('0');
+ break;
+ }
+ else if (c >= T('a') && c <= T('z'))
+ {
+ byte |= c - (T('a') - 10);
+ break;
+ }
+ else if (c >= T('A') && c <= T('Z'))
+ {
+ byte |= c - (T('A') - 10);
+ break;
+ }
+ else if (c == 0)
+ {
+ setSize ((int) (dest - data));
+ return;
+ }
+ }
+ }
+
+ *dest++ = (char) byte;
+ }
+}
+
+static const char* const encodingTable
+ = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+";
+
+const String MemoryBlock::toBase64Encoding() const throw()
+{
+ const int numChars = ((size << 3) + 5) / 6;
+
+ String destString (size); // store the length, followed by a '.', and then the data.
+ const int initialLen = destString.length();
+ destString.preallocateStorage (initialLen + 2 + numChars);
+
+ tchar* d = const_cast (((const tchar*) destString) + initialLen);
+ *d++ = T('.');
+
+ for (int i = 0; i < numChars; ++i)
+ *d++ = encodingTable [getBitRange (i * 6, 6)];
+
+ *d++ = 0;
+
+ return destString;
+}
+
+bool MemoryBlock::fromBase64Encoding (const String& s) throw()
+{
+ const int startPos = s.indexOfChar (T('.')) + 1;
+
+ if (startPos <= 0)
+ return false;
+
+ const int numBytesNeeded = s.substring (0, startPos - 1).getIntValue();
+
+ setSize (numBytesNeeded, true);
+
+ const int numChars = s.length() - startPos;
+ const tchar* const srcChars = ((const tchar*) s) + startPos;
+
+ for (int i = 0; i < numChars; ++i)
+ {
+ const char c = (char) srcChars[i];
+
+ for (int j = 0; j < 64; ++j)
+ {
+ if (encodingTable[j] == c)
+ {
+ setBitRange (i * 6, 6, j);
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_MemoryBlock.cpp *********/
+
+/********* Start of inlined file: juce_PropertySet.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+PropertySet::PropertySet (const bool ignoreCaseOfKeyNames) throw()
+ : properties (ignoreCaseOfKeyNames),
+ fallbackProperties (0),
+ ignoreCaseOfKeys (ignoreCaseOfKeyNames)
+{
+}
+
+PropertySet::PropertySet (const PropertySet& other) throw()
+ : properties (other.properties),
+ fallbackProperties (other.fallbackProperties),
+ ignoreCaseOfKeys (other.ignoreCaseOfKeys)
+{
+}
+
+const PropertySet& PropertySet::operator= (const PropertySet& other) throw()
+{
+ properties = other.properties;
+ fallbackProperties = other.fallbackProperties;
+ ignoreCaseOfKeys = other.ignoreCaseOfKeys;
+
+ propertyChanged();
+ return *this;
+}
+
+PropertySet::~PropertySet()
+{
+}
+
+void PropertySet::clear()
+{
+ const ScopedLock sl (lock);
+
+ if (properties.size() > 0)
+ {
+ properties.clear();
+ propertyChanged();
+ }
+}
+
+const String PropertySet::getValue (const String& keyName,
+ const String& defaultValue) const throw()
+{
+ const ScopedLock sl (lock);
+
+ const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
+
+ if (index >= 0)
+ return properties.getAllValues() [index];
+
+ return fallbackProperties != 0 ? fallbackProperties->getValue (keyName, defaultValue)
+ : defaultValue;
+}
+
+int PropertySet::getIntValue (const String& keyName,
+ const int defaultValue) const throw()
+{
+ const ScopedLock sl (lock);
+ const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
+
+ if (index >= 0)
+ return properties.getAllValues() [index].getIntValue();
+
+ return fallbackProperties != 0 ? fallbackProperties->getIntValue (keyName, defaultValue)
+ : defaultValue;
+}
+
+double PropertySet::getDoubleValue (const String& keyName,
+ const double defaultValue) const throw()
+{
+ const ScopedLock sl (lock);
+ const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
+
+ if (index >= 0)
+ return properties.getAllValues()[index].getDoubleValue();
+
+ return fallbackProperties != 0 ? fallbackProperties->getDoubleValue (keyName, defaultValue)
+ : defaultValue;
+}
+
+bool PropertySet::getBoolValue (const String& keyName,
+ const bool defaultValue) const throw()
+{
+ const ScopedLock sl (lock);
+ const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
+
+ if (index >= 0)
+ return properties.getAllValues() [index].getIntValue() != 0;
+
+ return fallbackProperties != 0 ? fallbackProperties->getBoolValue (keyName, defaultValue)
+ : defaultValue;
+}
+
+XmlElement* PropertySet::getXmlValue (const String& keyName) const
+{
+ XmlDocument doc (getValue (keyName));
+
+ return doc.getDocumentElement();
+}
+
+void PropertySet::setValue (const String& keyName,
+ const String& value) throw()
+{
+ jassert (keyName.isNotEmpty()); // shouldn't use an empty key name!
+
+ if (keyName.isNotEmpty())
+ {
+ const ScopedLock sl (lock);
+
+ const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
+
+ if (index < 0 || properties.getAllValues() [index] != value)
+ {
+ properties.set (keyName, value);
+ propertyChanged();
+ }
+ }
+}
+
+void PropertySet::removeValue (const String& keyName) throw()
+{
+ if (keyName.isNotEmpty())
+ {
+ const ScopedLock sl (lock);
+ const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys);
+
+ if (index >= 0)
+ {
+ properties.remove (keyName);
+ propertyChanged();
+ }
+ }
+}
+
+void PropertySet::setValue (const String& keyName, const tchar* const value) throw()
+{
+ setValue (keyName, String (value));
+}
+
+void PropertySet::setValue (const String& keyName, const int value) throw()
+{
+ setValue (keyName, String (value));
+}
+
+void PropertySet::setValue (const String& keyName, const double value) throw()
+{
+ setValue (keyName, String (value));
+}
+
+void PropertySet::setValue (const String& keyName, const bool value) throw()
+{
+ setValue (keyName, String ((value) ? T("1") : T("0")));
+}
+
+void PropertySet::setValue (const String& keyName, const XmlElement* const xml)
+{
+ setValue (keyName, (xml == 0) ? String::empty
+ : xml->createDocument (String::empty, true));
+}
+
+bool PropertySet::containsKey (const String& keyName) const throw()
+{
+ const ScopedLock sl (lock);
+ return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys);
+}
+
+void PropertySet::setFallbackPropertySet (PropertySet* fallbackProperties_) throw()
+{
+ const ScopedLock sl (lock);
+ fallbackProperties = fallbackProperties_;
+}
+
+XmlElement* PropertySet::createXml (const String& nodeName) const throw()
+{
+ const ScopedLock sl (lock);
+ XmlElement* const xml = new XmlElement (nodeName);
+
+ for (int i = 0; i < properties.getAllKeys().size(); ++i)
+ {
+ XmlElement* const e = new XmlElement (T("VALUE"));
+
+ e->setAttribute (T("name"), properties.getAllKeys()[i]);
+ e->setAttribute (T("val"), properties.getAllValues()[i]);
+
+ xml->addChildElement (e);
+ }
+
+ return xml;
+}
+
+void PropertySet::restoreFromXml (const XmlElement& xml) throw()
+{
+ const ScopedLock sl (lock);
+ clear();
+
+ forEachXmlChildElementWithTagName (xml, e, T("VALUE"))
+ {
+ if (e->hasAttribute (T("name"))
+ && e->hasAttribute (T("val")))
+ {
+ properties.set (e->getStringAttribute (T("name")),
+ e->getStringAttribute (T("val")));
+ }
+ }
+
+ if (properties.size() > 0)
+ propertyChanged();
+}
+
+void PropertySet::propertyChanged()
+{
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_PropertySet.cpp *********/
+
+/********* Start of inlined file: juce_BlowFish.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+static const uint32 initialPValues [18] =
+{
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b
+};
+
+static const uint32 initialSValues [4 * 256] =
+{
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
+};
+
+BlowFish::BlowFish (const uint8* keyData, int keyBytes)
+{
+ p = (uint32*) juce_malloc (18 * sizeof (uint32));
+ memcpy (p, initialPValues, sizeof (p));
+
+ int i, j;
+ for (i = 4; --i >= 0;)
+ {
+ s[i] = (uint32*) juce_malloc (256 * sizeof (uint32));
+ memcpy (s[i], initialSValues + i * 256, 256 * sizeof (uint32));
+ }
+
+ j = 0;
+
+ for (i = 0; i < 18; ++i)
+ {
+ uint32 d = 0;
+
+ for (int k = 0; k < 4; ++k)
+ {
+ d = (d << 8) | keyData[j];
+
+ if (++j >= keyBytes)
+ j = 0;
+ }
+
+ p[i] = initialPValues[i] ^ d;
+ }
+
+ uint32 l = 0, r = 0;
+
+ for (i = 0; i < 18; i += 2)
+ {
+ encrypt (l, r);
+
+ p[i] = l;
+ p[i + 1] = r;
+ }
+
+ for (i = 0; i < 4; ++i)
+ {
+ for (j = 0; j < 256; j += 2)
+ {
+ encrypt (l, r);
+
+ s[i][j] = l;
+ s[i][j + 1] = r;
+ }
+ }
+}
+
+BlowFish::~BlowFish()
+{
+ juce_free (p);
+
+ for (int i = 4; --i >= 0;)
+ juce_free (s[i]);
+}
+
+uint32 BlowFish::F (uint32 x) const
+{
+ uint16 a, b, c, d;
+ uint32 y;
+
+ d = (uint16) (x & 0xff);
+ x >>= 8;
+ c = (uint16) (x & 0xff);
+ x >>= 8;
+ b = (uint16) (x & 0xff);
+ x >>= 8;
+ a = (uint16) (x & 0xff);
+
+ y = s[0][a] + s[1][b];
+ y = y ^ s[2][c];
+ y = y + s[3][d];
+
+ return y;
+}
+
+void BlowFish::encrypt (uint32& data1,
+ uint32& data2) const
+{
+ uint32 l = data1;
+ uint32 r = data2;
+
+ for (int i = 0; i < 16; ++i)
+ {
+ l = l ^ p[i];
+ r = F (l) ^ r;
+
+ const uint32 temp = l;
+ l = r;
+ r = temp;
+ }
+
+ const uint32 temp = l;
+ l = r;
+ r = temp;
+
+ r = r ^ p[16];
+ l = l ^ p[17];
+
+ data1 = l;
+ data2 = r;
+}
+
+void BlowFish::decrypt (uint32& data1,
+ uint32& data2) const
+{
+ uint32 l = data1;
+ uint32 r = data2;
+
+ for (int i = 17; i > 1; --i)
+ {
+ l =l ^ p[i];
+ r = F (l) ^ r;
+
+ const uint32 temp = l;
+ l = r;
+ r = temp;
+ }
+
+ const uint32 temp = l;
+ l = r;
+ r = temp;
+
+ r = r ^ p[1];
+ l = l ^ p[0];
+
+ data1 = l;
+ data2 = r;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_BlowFish.cpp *********/
+
+/********* Start of inlined file: juce_MD5.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+MD5::MD5()
+{
+ zeromem (result, sizeof (result));
+}
+
+MD5::MD5 (const MD5& other)
+{
+ memcpy (result, other.result, sizeof (result));
+}
+
+const MD5& MD5::operator= (const MD5& other)
+{
+ memcpy (result, other.result, sizeof (result));
+ return *this;
+}
+
+MD5::MD5 (const MemoryBlock& data)
+{
+ ProcessContext context;
+ context.processBlock ((const uint8*) data.getData(), data.getSize());
+ context.finish (result);
+}
+
+MD5::MD5 (const char* data, const int numBytes)
+{
+ ProcessContext context;
+ context.processBlock ((const uint8*) data, numBytes);
+ context.finish (result);
+}
+
+MD5::MD5 (const String& text)
+{
+ ProcessContext context;
+
+ const int len = text.length();
+ const juce_wchar* const t = text;
+
+ for (int i = 0; i < len; ++i)
+ {
+ // force the string into integer-sized unicode characters, to try to make it
+ // get the same results on all platforms + compilers.
+ uint32 unicodeChar = (uint32) t[i];
+ swapIfBigEndian (unicodeChar);
+
+ context.processBlock ((const uint8*) &unicodeChar,
+ sizeof (unicodeChar));
+ }
+
+ context.finish (result);
+}
+
+void MD5::processStream (InputStream& input, int numBytesToRead)
+{
+ ProcessContext context;
+
+ if (numBytesToRead < 0)
+ numBytesToRead = INT_MAX;
+
+ while (numBytesToRead > 0)
+ {
+ char tempBuffer [512];
+ const int bytesRead = input.read (tempBuffer, jmin (numBytesToRead, sizeof (tempBuffer)));
+
+ if (bytesRead <= 0)
+ break;
+
+ numBytesToRead -= bytesRead;
+
+ context.processBlock ((const uint8*) tempBuffer, bytesRead);
+ }
+
+ context.finish (result);
+}
+
+MD5::MD5 (InputStream& input, int numBytesToRead)
+{
+ processStream (input, numBytesToRead);
+}
+
+MD5::MD5 (const File& file)
+{
+ FileInputStream* const fin = file.createInputStream();
+
+ if (fin != 0)
+ {
+ processStream (*fin, -1);
+ delete fin;
+ }
+ else
+ {
+ zeromem (result, sizeof (result));
+ }
+}
+
+MD5::~MD5()
+{
+}
+
+MD5::ProcessContext::ProcessContext()
+{
+ state[0] = 0x67452301;
+ state[1] = 0xefcdab89;
+ state[2] = 0x98badcfe;
+ state[3] = 0x10325476;
+
+ count[0] = 0;
+ count[1] = 0;
+}
+
+void MD5::ProcessContext::processBlock (const uint8* const data, int dataSize)
+{
+ int bufferPos = ((count[0] >> 3) & 0x3F);
+
+ count[0] += (dataSize << 3);
+
+ if (count[0] < ((uint32) dataSize << 3))
+ count[1]++;
+
+ count[1] += (dataSize >> 29);
+
+ const int spaceLeft = 64 - bufferPos;
+
+ int i = 0;
+
+ if (dataSize >= spaceLeft)
+ {
+ memcpy (buffer + bufferPos, data, spaceLeft);
+
+ transform (buffer);
+
+ i = spaceLeft;
+
+ while (i < dataSize - 63)
+ {
+ transform (data + i);
+ i += 64;
+ }
+
+ bufferPos = 0;
+ }
+
+ memcpy (buffer + bufferPos, data + i, dataSize - i);
+}
+
+static void encode (uint8* const output,
+ const uint32* const input,
+ const int numBytes)
+{
+ uint32* const o = (uint32*) output;
+
+ for (int i = 0; i < (numBytes >> 2); ++i)
+ o[i] = swapIfBigEndian (input [i]);
+}
+
+static void decode (uint32* const output,
+ const uint8* const input,
+ const int numBytes)
+{
+ for (int i = 0; i < (numBytes >> 2); ++i)
+ output[i] = littleEndianInt ((const char*) input + (i << 2));
+}
+
+void MD5::ProcessContext::finish (uint8* const result)
+{
+ unsigned char encodedLength[8];
+ encode (encodedLength, count, 8);
+
+ // Pad out to 56 mod 64.
+ const int index = (uint32) ((count[0] >> 3) & 0x3f);
+
+ const int paddingLength = (index < 56) ? (56 - index)
+ : (120 - index);
+
+ uint8 paddingBuffer [64];
+ zeromem (paddingBuffer, paddingLength);
+ paddingBuffer [0] = 0x80;
+ processBlock (paddingBuffer, paddingLength);
+
+ processBlock (encodedLength, 8);
+
+ encode (result, state, 16);
+
+ zeromem (buffer, sizeof (buffer));
+}
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static inline uint32 F (const uint32 x, const uint32 y, const uint32 z) { return (x & y) | (~x & z); }
+static inline uint32 G (const uint32 x, const uint32 y, const uint32 z) { return (x & z) | (y & ~z); }
+static inline uint32 H (const uint32 x, const uint32 y, const uint32 z) { return x ^ y ^ z; }
+static inline uint32 I (const uint32 x, const uint32 y, const uint32 z) { return y ^ (x | ~z); }
+
+static inline uint32 rotateLeft (const uint32 x, const uint32 n) { return (x << n) | (x >> (32 - n)); }
+
+static inline void FF (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac)
+{
+ a += F (b, c, d) + x + ac;
+ a = rotateLeft (a, s) + b;
+}
+
+static inline void GG (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac)
+{
+ a += G (b, c, d) + x + ac;
+ a = rotateLeft (a, s) + b;
+}
+
+static inline void HH (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac)
+{
+ a += H (b, c, d) + x + ac;
+ a = rotateLeft (a, s) + b;
+}
+
+static inline void II (uint32& a, const uint32 b, const uint32 c, const uint32 d, const uint32 x, const uint32 s, const uint32 ac)
+{
+ a += I (b, c, d) + x + ac;
+ a = rotateLeft (a, s) + b;
+}
+
+void MD5::ProcessContext::transform (const uint8* const buffer)
+{
+ uint32 a = state[0];
+ uint32 b = state[1];
+ uint32 c = state[2];
+ uint32 d = state[3];
+ uint32 x[16];
+
+ decode (x, buffer, 64);
+
+ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ zeromem (x, sizeof (x));
+}
+
+const MemoryBlock MD5::getRawChecksumData() const
+{
+ return MemoryBlock (result, 16);
+}
+
+const String MD5::toHexString() const
+{
+ return String::toHexString (result, 16, 0);
+}
+
+bool MD5::operator== (const MD5& other) const
+{
+ return memcmp (result, other.result, 16) == 0;
+}
+
+bool MD5::operator!= (const MD5& other) const
+{
+ return ! operator== (other);
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_MD5.cpp *********/
+
+/********* Start of inlined file: juce_Primes.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+static void createSmallSieve (const int numBits, BitArray& result) throw()
+{
+ result.setBit (numBits);
+ result.clearBit (numBits); // to enlarge the array
+
+ result.setBit (0);
+ int n = 2;
+
+ do
+ {
+ for (int i = n + n; i < numBits; i += n)
+ result.setBit (i);
+
+ n = result.findNextClearBit (n + 1);
+ }
+ while (n <= (numBits >> 1));
+}
+
+static void bigSieve (const BitArray& base,
+ const int numBits,
+ BitArray& result,
+ const BitArray& smallSieve,
+ const int smallSieveSize) throw()
+{
+ jassert (! base[0]); // must be even!
+
+ result.setBit (numBits);
+ result.clearBit (numBits); // to enlarge the array
+
+ int index = smallSieve.findNextClearBit (0);
+
+ do
+ {
+ const int prime = (index << 1) + 1;
+
+ BitArray r (base);
+ BitArray remainder;
+ r.divideBy (prime, remainder);
+
+ int i = prime - remainder.getBitRangeAsInt (0, 32);
+
+ if (r.isEmpty())
+ i += prime;
+
+ if ((i & 1) == 0)
+ i += prime;
+
+ i = (i - 1) >> 1;
+
+ while (i < numBits)
+ {
+ result.setBit (i);
+ i += prime;
+ }
+
+ index = smallSieve.findNextClearBit (index + 1);
+ }
+ while (index < smallSieveSize);
+}
+
+static bool findCandidate (const BitArray& base,
+ const BitArray& sieve,
+ const int numBits,
+ BitArray& result,
+ const int certainty) throw()
+{
+ for (int i = 0; i < numBits; ++i)
+ {
+ if (! sieve[i])
+ {
+ result = base;
+ result.add (BitArray ((unsigned int) ((i << 1) + 1)));
+
+ if (Primes::isProbablyPrime (result, certainty))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const BitArray Primes::createProbablePrime (const int bitLength,
+ const int certainty) throw()
+{
+ BitArray smallSieve;
+ const int smallSieveSize = 15000;
+ createSmallSieve (smallSieveSize, smallSieve);
+
+ BitArray p;
+ p.fillBitsRandomly (0, bitLength);
+ p.setBit (bitLength - 1);
+ p.clearBit (0);
+
+ const int searchLen = jmax (1024, (bitLength / 20) * 64);
+
+ while (p.getHighestBit() < bitLength)
+ {
+ p.add (2 * searchLen);
+
+ BitArray sieve;
+ bigSieve (p, searchLen, sieve,
+ smallSieve, smallSieveSize);
+
+ BitArray candidate;
+
+ if (findCandidate (p, sieve, searchLen, candidate, certainty))
+ return candidate;
+ }
+
+ jassertfalse
+ return BitArray();
+}
+
+static bool passesMillerRabin (const BitArray& n, int iterations) throw()
+{
+ const BitArray one (1);
+ const BitArray two (2);
+
+ BitArray nMinusOne (n);
+ nMinusOne.subtract (one);
+
+ BitArray d (nMinusOne);
+ const int s = d.findNextSetBit (0);
+ d.shiftBits (-s);
+
+ BitArray smallPrimes;
+ int numBitsInSmallPrimes = 0;
+
+ for (;;)
+ {
+ numBitsInSmallPrimes += 256;
+ createSmallSieve (numBitsInSmallPrimes, smallPrimes);
+
+ const int numPrimesFound = numBitsInSmallPrimes - smallPrimes.countNumberOfSetBits();
+
+ if (numPrimesFound > iterations + 1)
+ break;
+ }
+
+ int smallPrime = 2;
+
+ while (--iterations >= 0)
+ {
+ smallPrime = smallPrimes.findNextClearBit (smallPrime + 1);
+
+ BitArray r (smallPrime);
+ //r.createRandomNumber (nMinusOne);
+ r.exponentModulo (d, n);
+
+ if (! (r == one || r == nMinusOne))
+ {
+ for (int j = 0; j < s; ++j)
+ {
+ r.exponentModulo (two, n);
+
+ if (r == nMinusOne)
+ break;
+ }
+
+ if (r != nMinusOne)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Primes::isProbablyPrime (const BitArray& number,
+ const int certainty) throw()
+{
+ if (! number[0])
+ return false;
+
+ if (number.getHighestBit() <= 10)
+ {
+ const int num = number.getBitRangeAsInt (0, 10);
+
+ for (int i = num / 2; --i > 1;)
+ if (num % i == 0)
+ return false;
+
+ return true;
+ }
+ else
+ {
+ const BitArray screen (2 * 3 * 5 * 7 * 11 * 13 * 17 * 19 * 23);
+
+ if (number.findGreatestCommonDivisor (screen) != BitArray (1))
+ return false;
+
+ return passesMillerRabin (number, certainty);
+ }
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_Primes.cpp *********/
+
+/********* Start of inlined file: juce_RSAKey.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+RSAKey::RSAKey() throw()
+{
+}
+
+RSAKey::RSAKey (const String& s) throw()
+{
+ if (s.containsChar (T(',')))
+ {
+ part1.parseString (s.upToFirstOccurrenceOf (T(","), false, false), 16);
+ part2.parseString (s.fromFirstOccurrenceOf (T(","), false, false), 16);
+ }
+ else
+ {
+ // the string needs to be two hex numbers, comma-separated..
+ jassertfalse;
+ }
+}
+
+RSAKey::~RSAKey() throw()
+{
+}
+
+const String RSAKey::toString() const throw()
+{
+ return part1.toString (16) + T(",") + part2.toString (16);
+}
+
+bool RSAKey::applyToValue (BitArray& value) const throw()
+{
+ if (part1.isEmpty() || part2.isEmpty()
+ || value.compare (0) <= 0)
+ {
+ jassertfalse // using an uninitialised key
+ value.clear();
+ return false;
+ }
+
+ BitArray result;
+
+ while (! value.isEmpty())
+ {
+ result.multiplyBy (part2);
+
+ BitArray remainder;
+ value.divideBy (part2, remainder);
+
+ remainder.exponentModulo (part1, part2);
+
+ result.add (remainder);
+ }
+
+ value = result;
+
+ return true;
+}
+
+static const BitArray findBestCommonDivisor (const BitArray& p,
+ const BitArray& q) throw()
+{
+ const BitArray one (1);
+
+ // try 3, 5, 9, 17, etc first because these only contain 2 bits and so
+ // are fast to divide + multiply
+ for (int i = 2; i <= 65536; i *= 2)
+ {
+ const BitArray e (1 + i);
+
+ if (e.findGreatestCommonDivisor (p) == one
+ && e.findGreatestCommonDivisor (q) == one)
+ {
+ return e;
+ }
+ }
+
+ BitArray e (4);
+
+ while (! (e.findGreatestCommonDivisor (p) == one
+ && e.findGreatestCommonDivisor (q) == one))
+ {
+ e.add (one);
+ }
+
+ return e;
+}
+
+void RSAKey::createKeyPair (RSAKey& publicKey,
+ RSAKey& privateKey,
+ const int numBits) throw()
+{
+ jassert (numBits > 16); // not much point using less than this..
+
+ BitArray p (Primes::createProbablePrime (numBits / 2, 30));
+ BitArray q (Primes::createProbablePrime (numBits - numBits / 2, 30));
+
+ BitArray n (p);
+ n.multiplyBy (q); // n = pq
+
+ const BitArray one (1);
+ p.subtract (one);
+ q.subtract (one);
+
+ BitArray m (p);
+ m.multiplyBy (q); // m = (p - 1)(q - 1)
+
+ const BitArray e (findBestCommonDivisor (p, q));
+
+ BitArray d (e);
+ d.inverseModulo (m);
+
+ publicKey.part1 = e;
+ publicKey.part2 = n;
+
+ privateKey.part1 = d;
+ privateKey.part2 = n;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_RSAKey.cpp *********/
+
+/********* Start of inlined file: juce_InputStream.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+char InputStream::readByte()
+{
+ char temp = 0;
+ read (&temp, 1);
+ return temp;
+}
+
+bool InputStream::readBool()
+{
+ return readByte() != 0;
+}
+
+short InputStream::readShort()
+{
+ char temp [2];
+
+ if (read (temp, 2) == 2)
+ return (short) littleEndianShort (temp);
+ else
+ return 0;
+}
+
+short InputStream::readShortBigEndian()
+{
+ char temp [2];
+
+ if (read (temp, 2) == 2)
+ return (short) bigEndianShort (temp);
+ else
+ return 0;
+}
+
+int InputStream::readInt()
+{
+ char temp [4];
+
+ if (read (temp, 4) == 4)
+ return (int) littleEndianInt (temp);
+ else
+ return 0;
+}
+
+int InputStream::readIntBigEndian()
+{
+ char temp [4];
+
+ if (read (temp, 4) == 4)
+ return (int) bigEndianInt (temp);
+ else
+ return 0;
+}
+
+int InputStream::readCompressedInt()
+{
+ int num = 0;
+
+ if (! isExhausted())
+ {
+ unsigned char numBytes = readByte();
+ const bool negative = (numBytes & 0x80) != 0;
+ numBytes &= 0x7f;
+
+ if (numBytes <= 4)
+ {
+ if (read (&num, numBytes) != numBytes)
+ return 0;
+
+ if (negative)
+ num = -num;
+ }
+ }
+
+ return num;
+}
+
+int64 InputStream::readInt64()
+{
+ char temp [8];
+
+ if (read (temp, 8) == 8)
+ return (int64) swapIfBigEndian (*(uint64*)temp);
+ else
+ return 0;
+}
+
+int64 InputStream::readInt64BigEndian()
+{
+ char temp [8];
+
+ if (read (temp, 8) == 8)
+ return (int64) swapIfLittleEndian (*(uint64*)temp);
+ else
+ return 0;
+}
+
+float InputStream::readFloat()
+{
+ union { int asInt; float asFloat; } n;
+ n.asInt = readInt();
+ return n.asFloat;
+}
+
+float InputStream::readFloatBigEndian()
+{
+ union { int asInt; float asFloat; } n;
+ n.asInt = readIntBigEndian();
+ return n.asFloat;
+}
+
+double InputStream::readDouble()
+{
+ union { int64 asInt; double asDouble; } n;
+ n.asInt = readInt64();
+ return n.asDouble;
+}
+
+double InputStream::readDoubleBigEndian()
+{
+ union { int64 asInt; double asDouble; } n;
+ n.asInt = readInt64BigEndian();
+ return n.asDouble;
+}
+
+const String InputStream::readString()
+{
+ const int tempBufferSize = 256;
+ uint8 temp [tempBufferSize];
+ int i = 0;
+
+ while ((temp [i++] = readByte()) != 0)
+ {
+ if (i == tempBufferSize)
+ {
+ // too big for our quick buffer, so read it in blocks..
+ String result (String::fromUTF8 (temp, i));
+ i = 0;
+
+ for (;;)
+ {
+ if ((temp [i++] = readByte()) == 0)
+ {
+ result += String::fromUTF8 (temp, i - 1);
+ break;
+ }
+ else if (i == tempBufferSize)
+ {
+ result += String::fromUTF8 (temp, i);
+ i = 0;
+ }
+ }
+
+ return result;
+ }
+ }
+
+ return String::fromUTF8 (temp, i - 1);
+}
+
+const String InputStream::readNextLine()
+{
+ String s;
+ const int maxChars = 256;
+ tchar buffer [maxChars];
+ int charsInBuffer = 0;
+
+ while (! isExhausted())
+ {
+ const uint8 c = readByte();
+ const int64 lastPos = getPosition();
+
+ if (c == '\n')
+ {
+ break;
+ }
+ else if (c == '\r')
+ {
+ if (readByte() != '\n')
+ setPosition (lastPos);
+
+ break;
+ }
+
+ buffer [charsInBuffer++] = c;
+
+ if (charsInBuffer == maxChars)
+ {
+ s.append (buffer, maxChars);
+ charsInBuffer = 0;
+ }
+ }
+
+ if (charsInBuffer > 0)
+ s.append (buffer, charsInBuffer);
+
+ return s;
+}
+
+int InputStream::readIntoMemoryBlock (MemoryBlock& block,
+ int numBytes)
+{
+ const int64 totalLength = getTotalLength();
+
+ if (totalLength >= 0)
+ {
+ const int totalBytesRemaining = (int) jmin ((int64) 0x7fffffff,
+ totalLength - getPosition());
+
+ if (numBytes < 0)
+ numBytes = totalBytesRemaining;
+ else if (numBytes > 0)
+ numBytes = jmin (numBytes, totalBytesRemaining);
+ else
+ return 0;
+ }
+
+ const int originalBlockSize = block.getSize();
+ int totalBytesRead = 0;
+
+ if (numBytes > 0)
+ {
+ // know how many bytes we want, so we can resize the block first..
+ block.setSize (originalBlockSize + numBytes, false);
+ totalBytesRead = read (((char*) block.getData()) + originalBlockSize, numBytes);
+ }
+ else
+ {
+ // read until end of stram..
+ const int chunkSize = 32768;
+
+ for (;;)
+ {
+ block.ensureSize (originalBlockSize + totalBytesRead + chunkSize, false);
+
+ const int bytesJustIn = read (((char*) block.getData())
+ + originalBlockSize
+ + totalBytesRead,
+ chunkSize);
+
+ if (bytesJustIn == 0)
+ break;
+
+ totalBytesRead += bytesJustIn;
+ }
+ }
+
+ // trim off any excess left at the end
+ block.setSize (originalBlockSize + totalBytesRead, false);
+ return totalBytesRead;
+}
+
+const String InputStream::readEntireStreamAsString()
+{
+ MemoryBlock mb;
+ const int size = readIntoMemoryBlock (mb);
+
+ return String::createStringFromData ((const char*) mb.getData(), size);
+}
+
+void InputStream::skipNextBytes (int64 numBytesToSkip)
+{
+ if (numBytesToSkip > 0)
+ {
+ const int skipBufferSize = (int) jmin (numBytesToSkip, (int64) 16384);
+ MemoryBlock temp (skipBufferSize);
+
+ while ((numBytesToSkip > 0) && ! isExhausted())
+ {
+ numBytesToSkip -= read (temp.getData(), (int) jmin (numBytesToSkip, (int64) skipBufferSize));
+ }
+ }
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_InputStream.cpp *********/
+
+/********* Start of inlined file: juce_OutputStream.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+#if JUCE_DEBUG
+static CriticalSection activeStreamLock;
+static VoidArray activeStreams;
+
+void juce_CheckForDanglingStreams()
+{
+ /*
+ It's always a bad idea to leak any object, but if you're leaking output
+ streams, then there's a good chance that you're failing to flush a file
+ to disk properly, which could result in corrupted data and other similar
+ nastiness..
+ */
+ jassert (activeStreams.size() == 0);
+};
+#endif
+
+OutputStream::OutputStream() throw()
+{
+#if JUCE_DEBUG
+ activeStreamLock.enter();
+ activeStreams.add (this);
+ activeStreamLock.exit();
+#endif
+}
+
+OutputStream::~OutputStream()
+{
+#if JUCE_DEBUG
+ activeStreamLock.enter();
+ activeStreams.removeValue (this);
+ activeStreamLock.exit();
+#endif
+}
+
+void OutputStream::writeBool (bool b)
+{
+ writeByte ((b) ? (char) 1
+ : (char) 0);
+}
+
+void OutputStream::writeByte (char byte)
+{
+ write (&byte, 1);
+}
+
+void OutputStream::writeShort (short value)
+{
+ const unsigned short v = swapIfBigEndian ((unsigned short) value);
+ write (&v, 2);
+}
+
+void OutputStream::writeShortBigEndian (short value)
+{
+ const unsigned short v = swapIfLittleEndian ((unsigned short) value);
+ write (&v, 2);
+}
+
+void OutputStream::writeInt (int value)
+{
+ const unsigned int v = swapIfBigEndian ((unsigned int) value);
+ write (&v, 4);
+}
+
+void OutputStream::writeIntBigEndian (int value)
+{
+ const unsigned int v = swapIfLittleEndian ((unsigned int) value);
+ write (&v, 4);
+}
+
+void OutputStream::writeCompressedInt (int value)
+{
+ unsigned int un = (value < 0) ? (unsigned int) -value
+ : (unsigned int) value;
+ unsigned int tn = un;
+
+ int numSigBytes = 0;
+
+ do
+ {
+ tn >>= 8;
+ numSigBytes++;
+
+ } while (tn & 0xff);
+
+ if (value < 0)
+ numSigBytes |= 0x80;
+
+ writeByte ((char) numSigBytes);
+ write (&un, numSigBytes);
+}
+
+void OutputStream::writeInt64 (int64 value)
+{
+ const uint64 v = swapIfBigEndian ((uint64) value);
+ write (&v, 8);
+}
+
+void OutputStream::writeInt64BigEndian (int64 value)
+{
+ const uint64 v = swapIfLittleEndian ((uint64) value);
+ write (&v, 8);
+}
+
+void OutputStream::writeFloat (float value)
+{
+ union { int asInt; float asFloat; } n;
+ n.asFloat = value;
+ writeInt (n.asInt);
+}
+
+void OutputStream::writeFloatBigEndian (float value)
+{
+ union { int asInt; float asFloat; } n;
+ n.asFloat = value;
+ writeIntBigEndian (n.asInt);
+}
+
+void OutputStream::writeDouble (double value)
+{
+ union { int64 asInt; double asDouble; } n;
+ n.asDouble = value;
+ writeInt64 (n.asInt);
+}
+
+void OutputStream::writeDoubleBigEndian (double value)
+{
+ union { int64 asInt; double asDouble; } n;
+ n.asDouble = value;
+ writeInt64BigEndian (n.asInt);
+}
+
+void OutputStream::writeString (const String& text)
+{
+ const int numBytes = text.copyToUTF8 (0);
+ uint8* const temp = (uint8*) juce_malloc (numBytes);
+
+ text.copyToUTF8 (temp);
+ write (temp, numBytes); // (numBytes includes the terminating null).
+
+ juce_free (temp);
+}
+
+void OutputStream::printf (const char* pf, ...)
+{
+ unsigned int bufSize = 256;
+ char* buf = (char*) juce_malloc (bufSize);
+
+ for (;;)
+ {
+ va_list list;
+ va_start (list, pf);
+
+ const int num = CharacterFunctions::vprintf (buf, bufSize, pf, list);
+
+ if (num > 0)
+ {
+ write (buf, num);
+ break;
+ }
+ else if (num == 0)
+ {
+ break;
+ }
+
+ juce_free (buf);
+ bufSize += 256;
+ buf = (char*) juce_malloc (bufSize);
+ }
+
+ juce_free (buf);
+}
+
+OutputStream& OutputStream::operator<< (const int number)
+{
+ const String s (number);
+ write ((const char*) s, s.length());
+ return *this;
+}
+
+OutputStream& OutputStream::operator<< (const double number)
+{
+ const String s (number);
+ write ((const char*) s, s.length());
+ return *this;
+}
+
+OutputStream& OutputStream::operator<< (const char character)
+{
+ writeByte (character);
+ return *this;
+}
+
+OutputStream& OutputStream::operator<< (const char* const text)
+{
+ write (text, (int) strlen (text));
+ return *this;
+}
+
+OutputStream& OutputStream::operator<< (const juce_wchar* const text)
+{
+ const String s (text);
+ write ((const char*) s, s.length());
+ return *this;
+}
+
+OutputStream& OutputStream::operator<< (const String& text)
+{
+ write ((const char*) text,
+ text.length());
+
+ return *this;
+}
+
+void OutputStream::writeText (const String& text,
+ const bool asUnicode,
+ const bool writeUnicodeHeaderBytes)
+{
+ if (asUnicode)
+ {
+ if (writeUnicodeHeaderBytes)
+ write ("\x0ff\x0fe", 2);
+
+ const juce_wchar* src = (const juce_wchar*) text;
+ bool lastCharWasReturn = false;
+
+ while (*src != 0)
+ {
+ if (*src == L'\n' && ! lastCharWasReturn)
+ writeShort ((short) L'\r');
+
+ lastCharWasReturn = (*src == L'\r');
+ writeShort ((short) *src++);
+ }
+ }
+ else
+ {
+ const char* src = (const char*) text;
+ const char* t = src;
+
+ for (;;)
+ {
+ if (*t == '\n')
+ {
+ if (t > src)
+ write (src, (int) (t - src));
+
+ write ("\r\n", 2);
+ src = t + 1;
+ }
+ else if (*t == '\r')
+ {
+ if (t[1] == '\n')
+ ++t;
+ }
+ else if (*t == 0)
+ {
+ if (t > src)
+ write (src, (int) (t - src));
+
+ break;
+ }
+
+ ++t;
+ }
+ }
+}
+
+int OutputStream::writeFromInputStream (InputStream& source,
+ int numBytesToWrite)
+{
+ if (numBytesToWrite < 0)
+ numBytesToWrite = 0x7fffffff;
+
+ int numWritten = 0;
+
+ while (numBytesToWrite > 0 && ! source.isExhausted())
+ {
+ char buffer [8192];
+
+ const int num = source.read (buffer, jmin (numBytesToWrite, sizeof (buffer)));
+
+ if (num == 0)
+ break;
+
+ write (buffer, num);
+
+ numBytesToWrite -= num;
+ numWritten += num;
+ }
+
+ return numWritten;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_OutputStream.cpp *********/
+
+/********* Start of inlined file: juce_DirectoryIterator.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile,
+ bool* isDirectory, bool* isHidden, int64* fileSize,
+ Time* modTime, Time* creationTime, bool* isReadOnly) throw();
+bool juce_findFileNext (void* handle, String& resultFile,
+ bool* isDirectory, bool* isHidden, int64* fileSize,
+ Time* modTime, Time* creationTime, bool* isReadOnly) throw();
+void juce_findFileClose (void* handle) throw();
+
+DirectoryIterator::DirectoryIterator (const File& directory,
+ bool isRecursive,
+ const String& wc,
+ const int whatToLookFor_) throw()
+ : wildCard (wc),
+ index (-1),
+ whatToLookFor (whatToLookFor_),
+ subIterator (0)
+{
+ // you have to specify the type of files you're looking for!
+ jassert ((whatToLookFor_ & (File::findFiles | File::findDirectories)) != 0);
+ jassert (whatToLookFor_ > 0 && whatToLookFor_ <= 7);
+
+ String path (directory.getFullPathName());
+ if (! path.endsWithChar (File::separator))
+ path += File::separator;
+
+ String filename;
+ bool isDirectory, isHidden;
+
+ void* const handle = juce_findFileStart (path,
+ isRecursive ? T("*") : wc,
+ filename, &isDirectory, &isHidden, 0, 0, 0, 0);
+
+ if (handle != 0)
+ {
+ do
+ {
+ if (! filename.containsOnly (T(".")))
+ {
+ bool addToList = false;
+
+ if (isDirectory)
+ {
+ if (isRecursive
+ && ((whatToLookFor_ & File::ignoreHiddenFiles) == 0
+ || ! isHidden))
+ {
+ dirsFound.add (new File (path + filename, 0));
+ }
+
+ addToList = (whatToLookFor_ & File::findDirectories) != 0;
+ }
+ else
+ {
+ addToList = (whatToLookFor_ & File::findFiles) != 0;
+ }
+
+ // if it's recursive, we're not relying on the OS iterator
+ // to do the wildcard match, so do it now..
+ if (isRecursive && addToList)
+ addToList = filename.matchesWildcard (wc, true);
+
+ if (addToList && (whatToLookFor_ & File::ignoreHiddenFiles) != 0)
+ addToList = ! isHidden;
+
+ if (addToList)
+ filesFound.add (new File (path + filename, 0));
+ }
+
+ } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0));
+
+ juce_findFileClose (handle);
+ }
+}
+
+DirectoryIterator::~DirectoryIterator() throw()
+{
+ if (subIterator != 0)
+ delete subIterator;
+}
+
+bool DirectoryIterator::next() throw()
+{
+ if (subIterator != 0)
+ {
+ if (subIterator->next())
+ return true;
+
+ deleteAndZero (subIterator);
+ }
+
+ if (index >= filesFound.size() + dirsFound.size() - 1)
+ return false;
+
+ ++index;
+
+ if (index >= filesFound.size())
+ {
+ subIterator = new DirectoryIterator (*(dirsFound [index - filesFound.size()]),
+ true, wildCard, whatToLookFor);
+ return next();
+ }
+
+ return true;
+}
+
+const File DirectoryIterator::getFile() const throw()
+{
+ if (subIterator != 0)
+ return subIterator->getFile();
+
+ const File* const f = filesFound [index];
+
+ return (f != 0) ? *f
+ : File::nonexistent;
+}
+
+float DirectoryIterator::getEstimatedProgress() const throw()
+{
+ if (filesFound.size() + dirsFound.size() == 0)
+ {
+ return 0.0f;
+ }
+ else
+ {
+ const float detailedIndex = (subIterator != 0) ? index + subIterator->getEstimatedProgress()
+ : (float) index;
+
+ return detailedIndex / (filesFound.size() + dirsFound.size());
+ }
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_DirectoryIterator.cpp *********/
+
+/********* Start of inlined file: juce_File.cpp *********/
+#ifdef _MSC_VER
+ #pragma warning (disable: 4514)
+ #pragma warning (push)
+#endif
+
+#ifndef JUCE_WIN32
+ #include
+#endif
+
+BEGIN_JUCE_NAMESPACE
+
+#ifdef _MSC_VER
+ #pragma warning (pop)
+#endif
+
+void* juce_fileOpen (const String& path, bool forWriting) throw();
+void juce_fileClose (void* handle) throw();
+int juce_fileWrite (void* handle, const void* buffer, int size) throw();
+int64 juce_fileGetPosition (void* handle) throw();
+int64 juce_fileSetPosition (void* handle, int64 pos) throw();
+void juce_fileFlush (void* handle) throw();
+
+bool juce_fileExists (const String& fileName, const bool dontCountDirectories) throw();
+bool juce_isDirectory (const String& fileName) throw();
+int64 juce_getFileSize (const String& fileName) throw();
+bool juce_canWriteToFile (const String& fileName) throw();
+bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) throw();
+
+void juce_getFileTimes (const String& fileName, int64& modificationTime, int64& accessTime, int64& creationTime) throw();
+bool juce_setFileTimes (const String& fileName, int64 modificationTime, int64 accessTime, int64 creationTime) throw();
+
+bool juce_deleteFile (const String& fileName) throw();
+bool juce_copyFile (const String& source, const String& dest) throw();
+bool juce_moveFile (const String& source, const String& dest) throw();
+
+// this must also create all paths involved in the directory.
+void juce_createDirectory (const String& fileName) throw();
+
+bool juce_launchFile (const String& fileName, const String& parameters) throw();
+
+const StringArray juce_getFileSystemRoots() throw();
+const String juce_getVolumeLabel (const String& filenameOnVolume, int& volumeSerialNumber) throw();
+
+// starts a directory search operation with a wildcard, returning a handle for
+// use in calls to juce_findFileNext.
+// juce_firstResultFile gets the name of the file (not the whole pathname) and
+// the other pointers, if non-null, are set based on the properties of the file.
+void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile,
+ bool* isDirectory, bool* isHidden, int64* fileSize, Time* modTime,
+ Time* creationTime, bool* isReadOnly) throw();
+
+// returns false when no more files are found
+bool juce_findFileNext (void* handle, String& resultFile,
+ bool* isDirectory, bool* isHidden, int64* fileSize,
+ Time* modTime, Time* creationTime, bool* isReadOnly) throw();
+
+void juce_findFileClose (void* handle) throw();
+
+static const String parseAbsolutePath (String path) throw()
+{
+ if (path.isEmpty())
+ return String::empty;
+
+#if JUCE_WIN32
+ // Windows..
+ path = path.replaceCharacter (T('/'), T('\\')).unquoted();
+
+ if (path.startsWithChar (File::separator))
+ {
+ if (path[1] != File::separator)
+ {
+ jassertfalse // using a filename that starts with a slash is a bit dodgy on
+ // Windows, because it needs a drive letter, which in this case
+ // we'll take from the CWD.. but this is a bit of an assumption that
+ // could be wrong..
+
+ path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path;
+ }
+ }
+ else if (path.indexOfChar (T(':')) < 0)
+ {
+ if (path.isEmpty())
+ return String::empty;
+
+ jassertfalse // using a partial filename is a bad way to initialise a file, because
+ // we don't know what directory to put it in.
+ // Here we'll assume it's in the CWD, but this might not be what was
+ // intended..
+
+ return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
+ }
+#else
+ // Mac or Linux..
+ path = path.replaceCharacter (T('\\'), T('/')).unquoted();
+
+ if (path.startsWithChar (T('~')))
+ {
+ const char* homeDir = 0;
+
+ if (path[1] == File::separator || path[1] == 0)
+ {
+ // expand a name of the form "~/abc"
+ path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName()
+ + path.substring (1);
+ }
+ else
+ {
+ // expand a name of type "~dave/abc"
+ const String userName (path.substring (1)
+ .upToFirstOccurrenceOf (T("/"), false, false));
+
+ struct passwd* const pw = getpwnam (userName);
+ if (pw != 0)
+ {
+ String home (homeDir);
+
+ if (home.endsWithChar (File::separator))
+ home [home.length() - 1] = 0;
+
+ path = String (pw->pw_dir)
+ + path.substring (userName.length());
+ }
+ }
+ }
+ else if (! path.startsWithChar (File::separator))
+ {
+ while (path.startsWith (T("./")))
+ path = path.substring (2);
+
+ if (path.isEmpty())
+ return String::empty;
+
+ jassertfalse // using a partial filename is a bad way to initialise a file, because
+ // we don't know what directory to put it in.
+ // Here we'll assume it's in the CWD, but this might not be what was
+ // intended..
+
+ return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName();
+ }
+#endif
+
+ int len = path.length();
+ while (--len > 0 && path [len] == File::separator)
+ path [len] = 0;
+
+ return path;
+}
+
+const File File::nonexistent;
+
+File::File (const String& fullPathName) throw()
+ : fullPath (parseAbsolutePath (fullPathName))
+{
+}
+
+File::File (const String& path, int) throw()
+ : fullPath (path)
+{
+}
+
+File::File (const File& other) throw()
+ : fullPath (other.fullPath)
+{
+}
+
+const File& File::operator= (const String& newPath) throw()
+{
+ fullPath = parseAbsolutePath (newPath);
+ return *this;
+}
+
+const File& File::operator= (const File& other) throw()
+{
+ fullPath = other.fullPath;
+ return *this;
+}
+
+#if JUCE_LINUX
+ #define NAMES_ARE_CASE_SENSITIVE 1
+#endif
+
+bool File::areFileNamesCaseSensitive()
+{
+#if NAMES_ARE_CASE_SENSITIVE
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool File::operator== (const File& other) const throw()
+{
+ // case-insensitive on Windows, but not on linux.
+#if NAMES_ARE_CASE_SENSITIVE
+ return fullPath == other.fullPath;
+#else
+ return fullPath.equalsIgnoreCase (other.fullPath);
+#endif
+}
+
+bool File::operator!= (const File& other) const throw()
+{
+ return ! operator== (other);
+}
+
+bool File::exists() const throw()
+{
+ return juce_fileExists (fullPath, false);
+}
+
+bool File::existsAsFile() const throw()
+{
+ return juce_fileExists (fullPath, true);
+}
+
+bool File::isDirectory() const throw()
+{
+ return juce_isDirectory (fullPath);
+}
+
+bool File::hasWriteAccess() const throw()
+{
+ if (exists())
+ return juce_canWriteToFile (fullPath);
+
+#ifndef JUCE_WIN32
+ else if ((! isDirectory()) && fullPath.containsChar (separator))
+ return getParentDirectory().hasWriteAccess();
+ else
+ return false;
+#else
+ // on windows, it seems that even read-only directories can still be written into,
+ // so checking the parent directory's permissions would return the wrong result..
+ else
+ return true;
+#endif
+}
+
+bool File::setReadOnly (const bool shouldBeReadOnly,
+ const bool applyRecursively) const throw()
+{
+ bool worked = true;
+
+ if (applyRecursively && isDirectory())
+ {
+ OwnedArray subFiles;
+ findChildFiles (subFiles, File::findFilesAndDirectories, false);
+
+ for (int i = subFiles.size(); --i >= 0;)
+ worked = subFiles[i]->setReadOnly (shouldBeReadOnly, true) && worked;
+ }
+
+ return juce_setFileReadOnly (fullPath, shouldBeReadOnly) && worked;
+}
+
+bool File::deleteFile() const throw()
+{
+ return (! exists())
+ || juce_deleteFile (fullPath);
+}
+
+bool File::deleteRecursively() const throw()
+{
+ bool worked = true;
+
+ if (isDirectory())
+ {
+ OwnedArray subFiles;
+ findChildFiles (subFiles, File::findFilesAndDirectories, false);
+
+ for (int i = subFiles.size(); --i >= 0;)
+ worked = subFiles[i]->deleteRecursively() && worked;
+ }
+
+ return deleteFile() && worked;
+}
+
+bool File::moveFileTo (const File& newFile) const throw()
+{
+ if (newFile.fullPath == fullPath)
+ return true;
+
+#if ! NAMES_ARE_CASE_SENSITIVE
+ if (*this != newFile)
+#endif
+ if (! newFile.deleteFile())
+ return false;
+
+ return juce_moveFile (fullPath, newFile.fullPath);
+}
+
+bool File::copyFileTo (const File& newFile) const throw()
+{
+ if (*this == newFile)
+ return true;
+
+ if (! newFile.deleteFile())
+ return false;
+
+ return juce_copyFile (fullPath, newFile.fullPath);
+}
+
+bool File::copyDirectoryTo (const File& newDirectory) const throw()
+{
+ if (isDirectory() && newDirectory.createDirectory())
+ {
+ OwnedArray subFiles;
+ findChildFiles (subFiles, File::findFiles, false);
+
+ int i;
+ for (i = 0; i < subFiles.size(); ++i)
+ if (! subFiles[i]->copyFileTo (newDirectory.getChildFile (subFiles[i]->getFileName())))
+ return false;
+
+ subFiles.clear();
+ findChildFiles (subFiles, File::findDirectories, false);
+
+ for (i = 0; i < subFiles.size(); ++i)
+ if (! subFiles[i]->copyDirectoryTo (newDirectory.getChildFile (subFiles[i]->getFileName())))
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+const String File::getPathUpToLastSlash() const throw()
+{
+ const int lastSlash = fullPath.lastIndexOfChar (separator);
+
+ if (lastSlash > 0)
+ return fullPath.substring (0, lastSlash);
+ else if (lastSlash == 0)
+ return separatorString;
+ else
+ return fullPath;
+}
+
+const File File::getParentDirectory() const throw()
+{
+ return File (getPathUpToLastSlash());
+}
+
+const String File::getFileName() const throw()
+{
+ return fullPath.substring (fullPath.lastIndexOfChar (separator) + 1);
+}
+
+int File::hashCode() const throw()
+{
+ return fullPath.hashCode();
+}
+
+int64 File::hashCode64() const throw()
+{
+ return fullPath.hashCode64();
+}
+
+const String File::getFileNameWithoutExtension() const throw()
+{
+ const int lastSlash = fullPath.lastIndexOfChar (separator) + 1;
+ const int lastDot = fullPath.lastIndexOfChar (T('.'));
+
+ if (lastDot > lastSlash)
+ return fullPath.substring (lastSlash, lastDot);
+ else
+ return fullPath.substring (lastSlash);
+}
+
+bool File::isAChildOf (const File& potentialParent) const throw()
+{
+ const String ourPath (getPathUpToLastSlash());
+
+#if NAMES_ARE_CASE_SENSITIVE
+ if (potentialParent.fullPath == ourPath)
+#else
+ if (potentialParent.fullPath.equalsIgnoreCase (ourPath))
+#endif
+ {
+ return true;
+ }
+ else if (potentialParent.fullPath.length() >= ourPath.length())
+ {
+ return false;
+ }
+ else
+ {
+ return getParentDirectory().isAChildOf (potentialParent);
+ }
+}
+
+bool File::isAbsolutePath (const String& path) throw()
+{
+ return path.startsWithChar (T('/')) || path.startsWithChar (T('\\'))
+#if JUCE_WIN32
+ || (path.isNotEmpty() && ((const String&) path)[1] == T(':'));
+#else
+ || path.startsWithChar (T('~'));
+#endif
+}
+
+const File File::getChildFile (String relativePath) const throw()
+{
+ if (isAbsolutePath (relativePath))
+ {
+ // the path is really absolute..
+ return File (relativePath);
+ }
+ else
+ {
+ // it's relative, so remove any ../ or ./ bits at the start.
+ String path (fullPath);
+
+ if (relativePath[0] == T('.'))
+ {
+#if JUCE_WIN32
+ relativePath = relativePath.replaceCharacter (T('/'), T('\\')).trimStart();
+#else
+ relativePath = relativePath.replaceCharacter (T('\\'), T('/')).trimStart();
+#endif
+ while (relativePath[0] == T('.'))
+ {
+ if (relativePath[1] == T('.'))
+ {
+ if (relativePath [2] == 0 || relativePath[2] == separator)
+ {
+ const int lastSlash = path.lastIndexOfChar (separator);
+ if (lastSlash > 0)
+ path = path.substring (0, lastSlash);
+
+ relativePath = relativePath.substring (3);
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if (relativePath[1] == separator)
+ {
+ relativePath = relativePath.substring (2);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ if (! path.endsWithChar (separator))
+ path += separator;
+
+ return File (path + relativePath);
+ }
+}
+
+const File File::getSiblingFile (const String& fileName) const throw()
+{
+ return getParentDirectory().getChildFile (fileName);
+}
+
+int64 File::getSize() const throw()
+{
+ return juce_getFileSize (fullPath);
+}
+
+const String File::descriptionOfSizeInBytes (const int64 bytes)
+{
+ if (bytes == 1)
+ {
+ return "1 byte";
+ }
+ else if (bytes < 1024)
+ {
+ return String ((int) bytes) + " bytes";
+ }
+ else if (bytes < 1024 * 1024)
+ {
+ return String (bytes / 1024.0, 1) + " KB";
+ }
+ else if (bytes < 1024 * 1024 * 1024)
+ {
+ return String (bytes / (1024.0 * 1024.0), 1) + " MB";
+ }
+ else
+ {
+ return String (bytes / (1024.0 * 1024.0 * 1024.0), 1) + " GB";
+ }
+}
+
+bool File::create() const throw()
+{
+ if (! exists())
+ {
+ const File parentDir (getParentDirectory());
+
+ if (parentDir == *this || ! parentDir.createDirectory())
+ return false;
+
+ void* const fh = juce_fileOpen (fullPath, true);
+
+ if (fh == 0)
+ return false;
+
+ juce_fileClose (fh);
+ }
+
+ return true;
+}
+
+bool File::createDirectory() const throw()
+{
+ if (! isDirectory())
+ {
+ const File parentDir (getParentDirectory());
+
+ if (parentDir == *this || ! parentDir.createDirectory())
+ return false;
+
+ String dir (fullPath);
+
+ while (dir.endsWithChar (separator))
+ dir [dir.length() - 1] = 0;
+
+ juce_createDirectory (dir);
+
+ return isDirectory();
+ }
+
+ return true;
+}
+
+const Time File::getCreationTime() const throw()
+{
+ int64 m, a, c;
+ juce_getFileTimes (fullPath, m, a, c);
+ return Time (c);
+}
+
+bool File::setCreationTime (const Time& t) const throw()
+{
+ return juce_setFileTimes (fullPath, 0, 0, t.toMilliseconds());
+}
+
+const Time File::getLastModificationTime() const throw()
+{
+ int64 m, a, c;
+ juce_getFileTimes (fullPath, m, a, c);
+ return Time (m);
+}
+
+bool File::setLastModificationTime (const Time& t) const throw()
+{
+ return juce_setFileTimes (fullPath, t.toMilliseconds(), 0, 0);
+}
+
+const Time File::getLastAccessTime() const throw()
+{
+ int64 m, a, c;
+ juce_getFileTimes (fullPath, m, a, c);
+ return Time (a);
+}
+
+bool File::setLastAccessTime (const Time& t) const throw()
+{
+ return juce_setFileTimes (fullPath, 0, t.toMilliseconds(), 0);
+}
+
+bool File::loadFileAsData (MemoryBlock& destBlock) const throw()
+{
+ if (! existsAsFile())
+ return false;
+
+ FileInputStream in (*this);
+ return getSize() == in.readIntoMemoryBlock (destBlock);
+}
+
+const String File::loadFileAsString() const throw()
+{
+ if (! existsAsFile())
+ return String::empty;
+
+ FileInputStream in (*this);
+ return in.readEntireStreamAsString();
+}
+
+static inline bool fileTypeMatches (const int whatToLookFor,
+ const bool isDir,
+ const bool isHidden)
+{
+ return (whatToLookFor & (isDir ? File::findDirectories
+ : File::findFiles)) != 0
+ && ((! isHidden)
+ || (whatToLookFor & File::ignoreHiddenFiles) == 0);
+}
+
+int File::findChildFiles (OwnedArray& results,
+ const int whatToLookFor,
+ const bool searchRecursively,
+ const String& wildCardPattern) const throw()
+{
+ // you have to specify the type of files you're looking for!
+ jassert ((whatToLookFor & (findFiles | findDirectories)) != 0);
+
+ int total = 0;
+
+ // find child files or directories in this directory first..
+ if (isDirectory())
+ {
+ String path (fullPath);
+ if (! path.endsWithChar (separator))
+ path += separator;
+
+ String filename;
+ bool isDirectory, isHidden;
+
+ void* const handle = juce_findFileStart (path,
+ wildCardPattern,
+ filename,
+ &isDirectory, &isHidden,
+ 0, 0, 0, 0);
+
+ if (handle != 0)
+ {
+ do
+ {
+ if (fileTypeMatches (whatToLookFor, isDirectory, isHidden)
+ && ! filename.containsOnly (T(".")))
+ {
+ results.add (new File (path + filename, 0));
+ ++total;
+ }
+
+ } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0));
+
+ juce_findFileClose (handle);
+ }
+ }
+ else
+ {
+ // trying to search for files inside a non-directory?
+ //jassertfalse
+ }
+
+ // and recurse down if required.
+ if (searchRecursively)
+ {
+ OwnedArray subDirectories;
+ findChildFiles (subDirectories, File::findDirectories, false);
+
+ for (int i = 0; i < subDirectories.size(); ++i)
+ {
+ total += subDirectories.getUnchecked(i)
+ ->findChildFiles (results,
+ whatToLookFor,
+ true,
+ wildCardPattern);
+ }
+ }
+
+ return total;
+}
+
+int File::getNumberOfChildFiles (const int whatToLookFor,
+ const String& wildCardPattern) const throw()
+{
+ // you have to specify the type of files you're looking for!
+ jassert (whatToLookFor > 0 && whatToLookFor <= 3);
+
+ int count = 0;
+
+ if (isDirectory())
+ {
+ String filename;
+ bool isDirectory, isHidden;
+
+ void* const handle = juce_findFileStart (fullPath,
+ wildCardPattern,
+ filename,
+ &isDirectory, &isHidden,
+ 0, 0, 0, 0);
+
+ if (handle != 0)
+ {
+ do
+ {
+ if (fileTypeMatches (whatToLookFor, isDirectory, isHidden)
+ && ! filename.containsOnly (T(".")))
+ {
+ ++count;
+ }
+
+ } while (juce_findFileNext (handle, filename, &isDirectory, &isHidden, 0, 0, 0, 0));
+
+ juce_findFileClose (handle);
+ }
+ }
+ else
+ {
+ // trying to search for files inside a non-directory?
+ jassertfalse
+ }
+
+ return count;
+}
+
+const File File::getNonexistentChildFile (const String& prefix_,
+ const String& suffix,
+ bool putNumbersInBrackets) const throw()
+{
+ File f (getChildFile (prefix_ + suffix));
+
+ if (f.exists())
+ {
+ int num = 2;
+ String prefix (prefix_);
+
+ // remove any bracketed numbers that may already be on the end..
+ if (prefix.trim().endsWithChar (T(')')))
+ {
+ putNumbersInBrackets = true;
+
+ const int openBracks = prefix.lastIndexOfChar (T('('));
+ const int closeBracks = prefix.lastIndexOfChar (T(')'));
+
+ if (openBracks > 0
+ && closeBracks > openBracks
+ && prefix.substring (openBracks + 1, closeBracks).containsOnly (T("0123456789")))
+ {
+ num = prefix.substring (openBracks + 1, closeBracks).getIntValue() + 1;
+ prefix = prefix.substring (0, openBracks);
+ }
+ }
+
+ // also use brackets if it ends in a digit.
+ putNumbersInBrackets = putNumbersInBrackets
+ || CharacterFunctions::isDigit (prefix.getLastCharacter());
+
+ do
+ {
+ if (putNumbersInBrackets)
+ f = getChildFile (prefix + T('(') + String (num++) + T(')') + suffix);
+ else
+ f = getChildFile (prefix + String (num++) + suffix);
+
+ } while (f.exists());
+ }
+
+ return f;
+}
+
+const File File::getNonexistentSibling (const bool putNumbersInBrackets) const throw()
+{
+ if (exists())
+ {
+ return getParentDirectory()
+ .getNonexistentChildFile (getFileNameWithoutExtension(),
+ getFileExtension(),
+ putNumbersInBrackets);
+ }
+ else
+ {
+ return *this;
+ }
+}
+
+const String File::getFileExtension() const throw()
+{
+ String ext;
+
+ if (! isDirectory())
+ {
+ const int indexOfDot = fullPath.lastIndexOfChar (T('.'));
+
+ if (indexOfDot > fullPath.lastIndexOfChar (separator))
+ ext = fullPath.substring (indexOfDot);
+ }
+
+ return ext;
+}
+
+bool File::hasFileExtension (const String& possibleSuffix) const throw()
+{
+ if (possibleSuffix.isEmpty())
+ return fullPath.lastIndexOfChar (T('.')) <= fullPath.lastIndexOfChar (separator);
+
+ if (fullPath.endsWithIgnoreCase (possibleSuffix))
+ {
+ if (possibleSuffix.startsWithChar (T('.')))
+ return true;
+
+ const int dotPos = fullPath.length() - possibleSuffix.length() - 1;
+
+ if (dotPos >= 0)
+ return fullPath [dotPos] == T('.');
+ }
+
+ return false;
+}
+
+const File File::withFileExtension (const String& newExtension) const throw()
+{
+ if (fullPath.isEmpty())
+ return File::nonexistent;
+
+ String filePart (getFileName());
+
+ int i = filePart.lastIndexOfChar (T('.'));
+ if (i < 0)
+ i = filePart.length();
+
+ String newExt (newExtension);
+
+ if (newExt.isNotEmpty() && ! newExt.startsWithChar (T('.')))
+ newExt = T(".") + newExt;
+
+ return getSiblingFile (filePart.substring (0, i) + newExt);
+}
+
+bool File::startAsProcess (const String& parameters) const throw()
+{
+ return exists()
+ && juce_launchFile (fullPath, parameters);
+}
+
+FileInputStream* File::createInputStream() const throw()
+{
+ if (existsAsFile())
+ return new FileInputStream (*this);
+ else
+ return 0;
+}
+
+FileOutputStream* File::createOutputStream (const int bufferSize) const throw()
+{
+ FileOutputStream* const out = new FileOutputStream (*this, bufferSize);
+
+ if (out->failedToOpen())
+ {
+ delete out;
+ return 0;
+ }
+ else
+ {
+ return out;
+ }
+}
+
+bool File::appendData (const void* const dataToAppend,
+ const int numberOfBytes) const throw()
+{
+ if (numberOfBytes > 0)
+ {
+ FileOutputStream* const out = createOutputStream();
+
+ if (out == 0)
+ return false;
+
+ out->write (dataToAppend, numberOfBytes);
+ delete out;
+ }
+
+ return true;
+}
+
+bool File::replaceWithData (const void* const dataToWrite,
+ const int numberOfBytes) const throw()
+{
+ jassert (numberOfBytes >= 0); // a negative number of bytes??
+
+ if (numberOfBytes <= 0)
+ return deleteFile();
+
+ const File tempFile (getSiblingFile (T(".") + getFileName()).getNonexistentSibling (false));
+
+ if (tempFile.appendData (dataToWrite, numberOfBytes)
+ && tempFile.moveFileTo (*this))
+ {
+ return true;
+ }
+
+ tempFile.deleteFile();
+ return false;
+}
+
+bool File::appendText (const String& text,
+ const bool asUnicode,
+ const bool writeUnicodeHeaderBytes) const throw()
+{
+ FileOutputStream* const out = createOutputStream();
+
+ if (out != 0)
+ {
+ out->writeText (text, asUnicode, writeUnicodeHeaderBytes);
+ delete out;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool File::printf (const tchar* pf, ...) const throw()
+{
+ va_list list;
+ va_start (list, pf);
+
+ String text;
+ text.vprintf (pf, list);
+
+ return appendData ((const char*) text, text.length());
+}
+
+bool File::replaceWithText (const String& textToWrite,
+ const bool asUnicode,
+ const bool writeUnicodeHeaderBytes) const throw()
+{
+ const File tempFile (getSiblingFile (T(".") + getFileName()).getNonexistentSibling (false));
+
+ if (tempFile.appendText (textToWrite, asUnicode, writeUnicodeHeaderBytes)
+ && tempFile.moveFileTo (*this))
+ {
+ return true;
+ }
+
+ tempFile.deleteFile();
+ return false;
+}
+
+const String File::createLegalPathName (const String& original) throw()
+{
+ String s (original);
+ String start;
+
+ if (s[1] == T(':'))
+ {
+ start = s.substring (0, 2);
+ s = s.substring (2);
+ }
+
+ return start + s.removeCharacters (T("\"#@,;:<>*^|?"))
+ .substring (0, 1024);
+}
+
+const String File::createLegalFileName (const String& original) throw()
+{
+ String s (original.removeCharacters (T("\"#@,;:<>*^|?\\/")));
+
+ const int maxLength = 128; // only the length of the filename, not the whole path
+ const int len = s.length();
+
+ if (len > maxLength)
+ {
+ const int lastDot = s.lastIndexOfChar (T('.'));
+
+ if (lastDot > jmax (0, len - 12))
+ {
+ s = s.substring (0, maxLength - (len - lastDot))
+ + s.substring (lastDot);
+ }
+ else
+ {
+ s = s.substring (0, maxLength);
+ }
+ }
+
+ return s;
+}
+
+const String File::getRelativePathFrom (const File& dir) const throw()
+{
+ String thisPath (fullPath);
+
+ {
+ int len = thisPath.length();
+ while (--len >= 0 && thisPath [len] == File::separator)
+ thisPath [len] = 0;
+ }
+
+ String dirPath ((dir.existsAsFile()) ? dir.getParentDirectory().getFullPathName()
+ : dir.fullPath);
+
+ if (! dirPath.endsWithChar (separator))
+ dirPath += separator;
+
+ const int len = jmin (thisPath.length(), dirPath.length());
+ int commonBitLength = 0;
+
+ for (int i = 0; i < len; ++i)
+ {
+#if NAMES_ARE_CASE_SENSITIVE
+ if (thisPath[i] != dirPath[i])
+#else
+ if (CharacterFunctions::toLowerCase (thisPath[i])
+ != CharacterFunctions::toLowerCase (dirPath[i]))
+#endif
+ {
+ break;
+ }
+
+ ++commonBitLength;
+ }
+
+ while (commonBitLength > 0 && thisPath [commonBitLength - 1] != File::separator)
+ --commonBitLength;
+
+ // if the only common bit is the root, then just return the full path..
+#if JUCE_WIN32
+ if (commonBitLength <= 0
+ || (commonBitLength == 1 && thisPath [1] == File::separator)
+ || (commonBitLength <= 3 && thisPath [1] == T(':')))
+#else
+ if (commonBitLength <= 0
+ || (commonBitLength == 1 && thisPath [1] == File::separator))
+#endif
+ return fullPath;
+
+ thisPath = thisPath.substring (commonBitLength);
+ dirPath = dirPath.substring (commonBitLength);
+
+ while (dirPath.isNotEmpty())
+ {
+#if JUCE_WIN32
+ thisPath = T("..\\") + thisPath;
+#else
+ thisPath = T("../") + thisPath;
+#endif
+
+ const int sep = dirPath.indexOfChar (separator);
+
+ if (sep >= 0)
+ dirPath = dirPath.substring (sep + 1);
+ else
+ dirPath = String::empty;
+ }
+
+ return thisPath;
+}
+
+void File::findFileSystemRoots (OwnedArray& destArray) throw()
+{
+ const StringArray roots (juce_getFileSystemRoots());
+
+ for (int i = 0; i < roots.size(); ++i)
+ destArray.add (new File (roots[i]));
+}
+
+const String File::getVolumeLabel() const throw()
+{
+ int serialNum;
+ return juce_getVolumeLabel (fullPath, serialNum);
+}
+
+int File::getVolumeSerialNumber() const throw()
+{
+ int serialNum;
+ juce_getVolumeLabel (fullPath, serialNum);
+
+ return serialNum;
+}
+
+const File File::createTempFile (const String& fileNameEnding) throw()
+{
+ String tempName (T("temp"));
+ static int tempNum = 0;
+ tempName << tempNum++ << fileNameEnding;
+
+ const File tempFile (getSpecialLocation (tempDirectory)
+ .getChildFile (tempName));
+
+ if (tempFile.exists())
+ return createTempFile (fileNameEnding);
+ else
+ return tempFile;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_File.cpp *********/
+
+/********* Start of inlined file: juce_FileInputStream.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+void* juce_fileOpen (const String& path, bool forWriting) throw();
+void juce_fileClose (void* handle) throw();
+int juce_fileRead (void* handle, void* buffer, int size) throw();
+int64 juce_fileSetPosition (void* handle, int64 pos) throw();
+
+FileInputStream::FileInputStream (const File& f)
+ : file (f),
+ currentPosition (0),
+ needToSeek (true)
+{
+ totalSize = f.getSize();
+
+ fileHandle = juce_fileOpen (f.getFullPathName(), false);
+}
+
+FileInputStream::~FileInputStream()
+{
+ juce_fileClose (fileHandle);
+}
+
+int64 FileInputStream::getTotalLength()
+{
+ return totalSize;
+}
+
+int FileInputStream::read (void* buffer, int bytesToRead)
+{
+ int num = 0;
+
+ if (needToSeek)
+ {
+ if (juce_fileSetPosition (fileHandle, currentPosition) < 0)
+ return 0;
+
+ needToSeek = false;
+ }
+
+ num = juce_fileRead (fileHandle, buffer, bytesToRead);
+ currentPosition += num;
+
+ return num;
+}
+
+bool FileInputStream::isExhausted()
+{
+ return currentPosition >= totalSize;
+}
+
+int64 FileInputStream::getPosition()
+{
+ return currentPosition;
+}
+
+bool FileInputStream::setPosition (int64 pos)
+{
+ pos = jlimit ((int64) 0, totalSize, pos);
+
+ needToSeek |= (currentPosition != pos);
+ currentPosition = pos;
+
+ return true;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_FileInputStream.cpp *********/
+
+/********* Start of inlined file: juce_FileOutputStream.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+void* juce_fileOpen (const String& path, bool forWriting) throw();
+void juce_fileClose (void* handle) throw();
+int juce_fileWrite (void* handle, const void* buffer, int size) throw();
+void juce_fileFlush (void* handle) throw();
+int64 juce_fileGetPosition (void* handle) throw();
+int64 juce_fileSetPosition (void* handle, int64 pos) throw();
+
+FileOutputStream::FileOutputStream (const File& f,
+ const int bufferSize_)
+ : file (f),
+ bufferSize (bufferSize_),
+ bytesInBuffer (0)
+{
+ fileHandle = juce_fileOpen (f.getFullPathName(), true);
+
+ if (fileHandle != 0)
+ {
+ currentPosition = juce_fileGetPosition (fileHandle);
+
+ if (currentPosition < 0)
+ {
+ jassertfalse
+ juce_fileClose (fileHandle);
+ fileHandle = 0;
+ }
+ }
+
+ buffer = (char*) juce_malloc (jmax (bufferSize_, 16));
+}
+
+FileOutputStream::~FileOutputStream()
+{
+ flush();
+
+ juce_fileClose (fileHandle);
+ juce_free (buffer);
+}
+
+int64 FileOutputStream::getPosition()
+{
+ return currentPosition;
+}
+
+bool FileOutputStream::setPosition (int64 newPosition)
+{
+ if (newPosition != currentPosition)
+ {
+ flush();
+ currentPosition = juce_fileSetPosition (fileHandle, newPosition);
+ }
+
+ return newPosition == currentPosition;
+}
+
+void FileOutputStream::flush()
+{
+ if (bytesInBuffer > 0)
+ {
+ juce_fileWrite (fileHandle, buffer, bytesInBuffer);
+ bytesInBuffer = 0;
+ }
+
+ juce_fileFlush (fileHandle);
+}
+
+bool FileOutputStream::write (const void* const src, const int numBytes)
+{
+ if (bytesInBuffer + numBytes < bufferSize)
+ {
+ memcpy (buffer + bytesInBuffer, src, numBytes);
+ bytesInBuffer += numBytes;
+ currentPosition += numBytes;
+ }
+ else
+ {
+ if (bytesInBuffer > 0)
+ {
+ // flush the reservoir
+ const bool wroteOk = (juce_fileWrite (fileHandle, buffer, bytesInBuffer) == bytesInBuffer);
+ bytesInBuffer = 0;
+
+ if (! wroteOk)
+ return false;
+ }
+
+ if (numBytes < bufferSize)
+ {
+ memcpy (buffer + bytesInBuffer, src, numBytes);
+ bytesInBuffer += numBytes;
+ currentPosition += numBytes;
+ }
+ else
+ {
+ const int bytesWritten = juce_fileWrite (fileHandle, src, numBytes);
+ currentPosition += bytesWritten;
+
+ return bytesWritten == numBytes;
+ }
+ }
+
+ return true;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_FileOutputStream.cpp *********/
+
+/********* Start of inlined file: juce_FileSearchPath.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+FileSearchPath::FileSearchPath()
+{
+}
+
+FileSearchPath::FileSearchPath (const String& path)
+{
+ init (path);
+}
+
+FileSearchPath::FileSearchPath (const FileSearchPath& other)
+ : directories (other.directories)
+{
+}
+
+FileSearchPath::~FileSearchPath()
+{
+}
+
+const FileSearchPath& FileSearchPath::operator= (const String& path)
+{
+ init (path);
+ return *this;
+}
+
+void FileSearchPath::init (const String& path)
+{
+ directories.clear();
+ directories.addTokens (path, T(";"), T("\""));
+ directories.trim();
+ directories.removeEmptyStrings();
+
+ for (int i = directories.size(); --i >= 0;)
+ directories.set (i, directories[i].unquoted());
+}
+
+int FileSearchPath::getNumPaths() const
+{
+ return directories.size();
+}
+
+const File FileSearchPath::operator[] (const int index) const
+{
+ return File (directories [index]);
+}
+
+const String FileSearchPath::toString() const
+{
+ StringArray directories2 (directories);
+ for (int i = directories2.size(); --i >= 0;)
+ if (directories2[i].containsChar (T(';')))
+ directories2.set (i, directories2[i].quoted());
+
+ return directories2.joinIntoString (T(";"));
+}
+
+void FileSearchPath::add (const File& dir, const int insertIndex)
+{
+ directories.insert (insertIndex, dir.getFullPathName());
+}
+
+void FileSearchPath::addIfNotAlreadyThere (const File& dir)
+{
+ for (int i = 0; i < directories.size(); ++i)
+ if (File (directories[i]) == dir)
+ return;
+
+ add (dir);
+}
+
+void FileSearchPath::remove (const int index)
+{
+ directories.remove (index);
+}
+
+void FileSearchPath::addPath (const FileSearchPath& other)
+{
+ for (int i = 0; i < other.getNumPaths(); ++i)
+ addIfNotAlreadyThere (other[i]);
+}
+
+void FileSearchPath::removeRedundantPaths()
+{
+ for (int i = directories.size(); --i >= 0;)
+ {
+ const File d1 (directories[i]);
+
+ for (int j = directories.size(); --j >= 0;)
+ {
+ const File d2 (directories[j]);
+
+ if ((i != j) && (d1.isAChildOf (d2) || d1 == d2))
+ {
+ directories.remove (i);
+ break;
+ }
+ }
+ }
+}
+
+void FileSearchPath::removeNonExistentPaths()
+{
+ for (int i = directories.size(); --i >= 0;)
+ if (! File (directories[i]).isDirectory())
+ directories.remove (i);
+}
+
+int FileSearchPath::findChildFiles (OwnedArray& results,
+ const int whatToLookFor,
+ const bool searchRecursively,
+ const String& wildCardPattern) const
+{
+ int total = 0;
+
+ for (int i = 0; i < directories.size(); ++i)
+ total += operator[] (i).findChildFiles (results,
+ whatToLookFor,
+ searchRecursively,
+ wildCardPattern);
+
+ return total;
+}
+
+bool FileSearchPath::isFileInPath (const File& fileToCheck,
+ const bool checkRecursively) const
+{
+ for (int i = directories.size(); --i >= 0;)
+ {
+ const File d (directories[i]);
+
+ if (checkRecursively)
+ {
+ if (fileToCheck.isAChildOf (d))
+ return true;
+ }
+ else
+ {
+ if (fileToCheck.getParentDirectory() == d)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_FileSearchPath.cpp *********/
+
+/********* Start of inlined file: juce_NamedPipe.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+NamedPipe::NamedPipe()
+ : internal (0)
+{
+}
+
+NamedPipe::~NamedPipe()
+{
+ close();
+}
+
+bool NamedPipe::openExisting (const String& pipeName)
+{
+ currentPipeName = pipeName;
+ return openInternal (pipeName, false);
+}
+
+bool NamedPipe::createNewPipe (const String& pipeName)
+{
+ currentPipeName = pipeName;
+ return openInternal (pipeName, true);
+}
+
+bool NamedPipe::isOpen() const throw()
+{
+ return internal != 0;
+}
+
+const String NamedPipe::getName() const throw()
+{
+ return currentPipeName;
+}
+
+// other methods for this class are implemented in the platform-specific files
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_NamedPipe.cpp *********/
+
+/********* Start of inlined file: juce_Socket.cpp *********/
+#ifdef _WIN32
+
+ #include
+
+ #ifdef _MSC_VER
+ #pragma warning (disable : 4127 4389 4018)
+ #endif
+
+#else
+ #ifndef LINUX
+ #include
+ #endif
+
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+#endif
+
+BEGIN_JUCE_NAMESPACE
+
+#if JUCE_WIN32
+
+typedef int (__stdcall juce_CloseWin32SocketLibCall) (void);
+juce_CloseWin32SocketLibCall* juce_CloseWin32SocketLib = 0;
+
+static void initWin32Sockets()
+{
+ static CriticalSection lock;
+ const ScopedLock sl (lock);
+
+ if (juce_CloseWin32SocketLib == 0)
+ {
+ WSADATA wsaData;
+ const WORD wVersionRequested = MAKEWORD (1, 1);
+ WSAStartup (wVersionRequested, &wsaData);
+
+ juce_CloseWin32SocketLib = &WSACleanup;
+ }
+}
+
+#endif
+
+static bool resetSocketOptions (const int handle, const bool isDatagram) throw()
+{
+ if (handle <= 0)
+ return false;
+
+ const int sndBufSize = 65536;
+ const int rcvBufSize = 65536;
+ const int one = 1;
+
+ return setsockopt (handle, SOL_SOCKET, SO_RCVBUF, (const char*) &rcvBufSize, sizeof (int)) == 0
+ && setsockopt (handle, SOL_SOCKET, SO_SNDBUF, (const char*) &sndBufSize, sizeof (int)) == 0
+ && (isDatagram || (setsockopt (handle, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof (int)) == 0));
+}
+
+static bool bindSocketToPort (const int handle, const int port) throw()
+{
+ if (handle == 0 || port <= 0)
+ return false;
+
+ struct sockaddr_in servTmpAddr;
+ zerostruct (servTmpAddr);
+ servTmpAddr.sin_family = PF_INET;
+ servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY);
+ servTmpAddr.sin_port = htons ((uint16) port);
+
+ return bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) >= 0;
+}
+
+static int readSocket (const int handle,
+ void* const destBuffer, const int maxBytesToRead,
+ bool volatile& connected) throw()
+{
+ int bytesRead = 0;
+
+ while (bytesRead < maxBytesToRead)
+ {
+ int bytesThisTime;
+
+#if JUCE_WIN32
+ bytesThisTime = recv (handle, ((char*) destBuffer) + bytesRead, maxBytesToRead - bytesRead, 0);
+#else
+ while ((bytesThisTime = ::read (handle, ((char*) destBuffer) + bytesRead, maxBytesToRead - bytesRead)) < 0
+ && errno == EINTR
+ && connected)
+ {
+ }
+#endif
+
+ if (bytesThisTime <= 0 || ! connected)
+ {
+ if (bytesRead == 0)
+ bytesRead = -1;
+
+ break;
+ }
+
+ bytesRead += bytesThisTime;
+ }
+
+ return bytesRead;
+}
+
+static int waitForReadiness (const int handle, const bool forReading,
+ const int timeoutMsecs) throw()
+{
+ struct timeval timeout;
+ struct timeval* timeoutp;
+
+ if (timeoutMsecs >= 0)
+ {
+ timeout.tv_sec = timeoutMsecs / 1000;
+ timeout.tv_usec = (timeoutMsecs % 1000) * 1000;
+ timeoutp = &timeout;
+ }
+ else
+ {
+ timeoutp = 0;
+ }
+
+ fd_set rset, wset;
+ FD_ZERO (&rset);
+ FD_SET (handle, &rset);
+ FD_ZERO (&wset);
+ FD_SET (handle, &wset);
+
+ fd_set* const prset = forReading ? &rset : 0;
+ fd_set* const pwset = forReading ? 0 : &wset;
+
+#if JUCE_WIN32
+ if (select (handle + 1, prset, pwset, 0, timeoutp) < 0)
+ return -1;
+#else
+ {
+ int result;
+ while ((result = select (handle + 1, prset, pwset, 0, timeoutp)) < 0
+ && errno == EINTR)
+ {
+ }
+
+ if (result < 0)
+ return -1;
+ }
+#endif
+
+ {
+ int opt;
+
+#if defined (JUCE_LINUX) || (defined (JUCE_MAC) && ! MACOS_10_2_OR_EARLIER)
+ socklen_t len = sizeof (opt);
+#else
+ int len = sizeof (opt);
+#endif
+
+ if (getsockopt (handle, SOL_SOCKET, SO_ERROR, (char*) &opt, &len) < 0
+ || opt != 0)
+ return -1;
+ }
+
+ if ((forReading && FD_ISSET (handle, &rset))
+ || ((! forReading) && FD_ISSET (handle, &wset)))
+ return 1;
+
+ return 0;
+}
+
+static bool setSocketBlockingState (const int handle, const bool shouldBlock) throw()
+{
+#if JUCE_WIN32
+ u_long nonBlocking = shouldBlock ? 0 : 1;
+
+ if (ioctlsocket (handle, FIONBIO, &nonBlocking) != 0)
+ return false;
+#else
+ int socketFlags = fcntl (handle, F_GETFL, 0);
+
+ if (socketFlags == -1)
+ return false;
+
+ if (shouldBlock)
+ socketFlags &= ~O_NONBLOCK;
+ else
+ socketFlags |= O_NONBLOCK;
+
+ if (fcntl (handle, F_SETFL, socketFlags) != 0)
+ return false;
+#endif
+
+ return true;
+}
+
+static bool connectSocket (int volatile& handle,
+ const bool isDatagram,
+ void** serverAddress,
+ const String& hostName,
+ const int portNumber,
+ const int timeOutMillisecs) throw()
+{
+ struct hostent* const hostEnt = gethostbyname (hostName);
+
+ if (hostEnt == 0)
+ return false;
+
+ struct in_addr targetAddress;
+ memcpy (&targetAddress.s_addr,
+ *(hostEnt->h_addr_list),
+ sizeof (targetAddress.s_addr));
+
+ struct sockaddr_in servTmpAddr;
+ zerostruct (servTmpAddr);
+ servTmpAddr.sin_family = PF_INET;
+ servTmpAddr.sin_addr = targetAddress;
+ servTmpAddr.sin_port = htons ((uint16) portNumber);
+
+ if (handle < 0)
+ handle = (int) socket (AF_INET, isDatagram ? SOCK_DGRAM : SOCK_STREAM, 0);
+
+ if (handle < 0)
+ return false;
+
+ if (isDatagram)
+ {
+ *serverAddress = new struct sockaddr_in();
+ *((struct sockaddr_in*) *serverAddress) = servTmpAddr;
+
+ return true;
+ }
+
+ setSocketBlockingState (handle, false);
+
+ const int result = ::connect (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in));
+
+ if (result < 0)
+ {
+#if JUCE_WIN32
+ if (result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
+#else
+ if (errno == EINPROGRESS)
+#endif
+ {
+ if (waitForReadiness (handle, false, timeOutMillisecs) != 1)
+ {
+ setSocketBlockingState (handle, true);
+ return false;
+ }
+ }
+ }
+
+ setSocketBlockingState (handle, true);
+ resetSocketOptions (handle, false);
+
+ return true;
+}
+
+StreamingSocket::StreamingSocket()
+ : portNumber (0),
+ handle (-1),
+ connected (false),
+ isListener (false)
+{
+#if JUCE_WIN32
+ initWin32Sockets();
+#endif
+}
+
+StreamingSocket::StreamingSocket (const String& hostName_,
+ const int portNumber_,
+ const int handle_)
+ : hostName (hostName_),
+ portNumber (portNumber_),
+ handle (handle_),
+ connected (true),
+ isListener (false)
+{
+#if JUCE_WIN32
+ initWin32Sockets();
+#endif
+
+ resetSocketOptions (handle_, false);
+}
+
+StreamingSocket::~StreamingSocket()
+{
+ close();
+}
+
+int StreamingSocket::read (void* destBuffer, const int maxBytesToRead)
+{
+ return (connected && ! isListener) ? readSocket (handle, destBuffer, maxBytesToRead, connected)
+ : -1;
+}
+
+int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite)
+{
+ if (isListener || ! connected)
+ return -1;
+
+#if JUCE_WIN32
+ return send (handle, (const char*) sourceBuffer, numBytesToWrite, 0);
+#else
+ int result;
+
+ while ((result = ::write (handle, sourceBuffer, numBytesToWrite)) < 0
+ && errno == EINTR)
+ {
+ }
+
+ return result;
+#endif
+}
+
+int StreamingSocket::waitUntilReady (const bool readyForReading,
+ const int timeoutMsecs) const
+{
+ return connected ? waitForReadiness (handle, readyForReading, timeoutMsecs)
+ : -1;
+}
+
+bool StreamingSocket::bindToPort (const int port)
+{
+ return bindSocketToPort (handle, port);
+}
+
+bool StreamingSocket::connect (const String& remoteHostName,
+ const int remotePortNumber,
+ const int timeOutMillisecs)
+{
+ if (isListener)
+ {
+ jassertfalse // a listener socket can't connect to another one!
+ return false;
+ }
+
+ if (connected)
+ close();
+
+ hostName = remoteHostName;
+ portNumber = remotePortNumber;
+ isListener = false;
+
+ connected = connectSocket (handle, false, 0, remoteHostName,
+ remotePortNumber, timeOutMillisecs);
+
+ if (! (connected && resetSocketOptions (handle, false)))
+ {
+ close();
+ return false;
+ }
+
+ return true;
+}
+
+void StreamingSocket::close()
+{
+#if JUCE_WIN32
+ closesocket (handle);
+ connected = false;
+#else
+ if (connected)
+ {
+ connected = false;
+
+ if (isListener)
+ {
+ // need to do this to interrupt the accept() function..
+ StreamingSocket temp;
+ temp.connect ("localhost", portNumber, 1000);
+ }
+ }
+
+ ::close (handle);
+#endif
+
+ hostName = String::empty;
+ portNumber = 0;
+ handle = -1;
+ isListener = false;
+}
+
+bool StreamingSocket::createListener (const int newPortNumber)
+{
+ if (connected)
+ close();
+
+ hostName = "listener";
+ portNumber = newPortNumber;
+ isListener = true;
+
+ struct sockaddr_in servTmpAddr;
+ zerostruct (servTmpAddr);
+ servTmpAddr.sin_family = PF_INET;
+ servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY);
+ servTmpAddr.sin_port = htons ((uint16) portNumber);
+
+ handle = (int) socket (AF_INET, SOCK_STREAM, 0);
+
+ if (handle < 0)
+ return false;
+
+ const int reuse = 1;
+ setsockopt (handle, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof (reuse));
+
+ if (bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) < 0
+ || listen (handle, SOMAXCONN) < 0)
+ {
+ close();
+ return false;
+ }
+
+ connected = true;
+ return true;
+}
+
+StreamingSocket* StreamingSocket::waitForNextConnection() const
+{
+ jassert (isListener || ! connected); // to call this method, you first have to use createListener() to
+ // prepare this socket as a listener.
+
+ if (connected && isListener)
+ {
+ struct sockaddr address;
+
+#if defined (JUCE_LINUX) || (defined (JUCE_MAC) && ! MACOS_10_2_OR_EARLIER)
+ socklen_t len = sizeof (sockaddr);
+#else
+ int len = sizeof (sockaddr);
+#endif
+ const int newSocket = (int) accept (handle, &address, &len);
+
+ if (newSocket >= 0 && connected)
+ return new StreamingSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr),
+ portNumber, newSocket);
+ }
+
+ return 0;
+}
+
+bool StreamingSocket::isLocal() const throw()
+{
+ return hostName == T("127.0.0.1");
+}
+
+DatagramSocket::DatagramSocket (const int localPortNumber)
+ : portNumber (0),
+ handle (-1),
+ connected (false),
+ serverAddress (0)
+{
+#if JUCE_WIN32
+ initWin32Sockets();
+#endif
+
+ handle = (int) socket (AF_INET, SOCK_DGRAM, 0);
+ bindToPort (localPortNumber);
+}
+
+DatagramSocket::DatagramSocket (const String& hostName_, const int portNumber_,
+ const int handle_, const int localPortNumber)
+ : hostName (hostName_),
+ portNumber (portNumber_),
+ handle (handle_),
+ connected (true),
+ serverAddress (0)
+{
+#if JUCE_WIN32
+ initWin32Sockets();
+#endif
+
+ resetSocketOptions (handle_, true);
+ bindToPort (localPortNumber);
+}
+
+DatagramSocket::~DatagramSocket()
+{
+ close();
+
+ delete ((struct sockaddr_in*) serverAddress);
+ serverAddress = 0;
+}
+
+void DatagramSocket::close()
+{
+#if JUCE_WIN32
+ closesocket (handle);
+ connected = false;
+#else
+ connected = false;
+ ::close (handle);
+#endif
+
+ hostName = String::empty;
+ portNumber = 0;
+ handle = -1;
+}
+
+bool DatagramSocket::bindToPort (const int port)
+{
+ return bindSocketToPort (handle, port);
+}
+
+bool DatagramSocket::connect (const String& remoteHostName,
+ const int remotePortNumber,
+ const int timeOutMillisecs)
+{
+ if (connected)
+ close();
+
+ hostName = remoteHostName;
+ portNumber = remotePortNumber;
+
+ connected = connectSocket (handle, true, &serverAddress,
+ remoteHostName, remotePortNumber,
+ timeOutMillisecs);
+
+ if (! (connected && resetSocketOptions (handle, true)))
+ {
+ close();
+ return false;
+ }
+
+ return true;
+}
+
+DatagramSocket* DatagramSocket::waitForNextConnection() const
+{
+ struct sockaddr address;
+
+#if defined (JUCE_LINUX) || (defined (JUCE_MAC) && ! MACOS_10_2_OR_EARLIER)
+ socklen_t len = sizeof (sockaddr);
+#else
+ int len = sizeof (sockaddr);
+#endif
+
+ while (waitUntilReady (true, -1) == 1)
+ {
+ char buf[1];
+
+ if (recvfrom (handle, buf, 0, 0, &address, &len) > 0)
+ {
+ return new DatagramSocket (inet_ntoa (((struct sockaddr_in*) &address)->sin_addr),
+ ntohs (((struct sockaddr_in*) &address)->sin_port),
+ -1, -1);
+ }
+ }
+
+ return 0;
+}
+
+int DatagramSocket::waitUntilReady (const bool readyForReading,
+ const int timeoutMsecs) const
+{
+ return connected ? waitForReadiness (handle, readyForReading, timeoutMsecs)
+ : -1;
+}
+
+int DatagramSocket::read (void* destBuffer, const int maxBytesToRead)
+{
+ return connected ? readSocket (handle, destBuffer, maxBytesToRead, connected)
+ : -1;
+}
+
+int DatagramSocket::write (const void* sourceBuffer, const int numBytesToWrite)
+{
+ // You need to call connect() first to set the server address..
+ jassert (serverAddress != 0 && connected);
+
+ return connected ? sendto (handle, (const char*) sourceBuffer,
+ numBytesToWrite, 0,
+ (const struct sockaddr*) serverAddress,
+ sizeof (struct sockaddr_in))
+ : -1;
+}
+
+bool DatagramSocket::isLocal() const throw()
+{
+ return hostName == T("127.0.0.1");
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_Socket.cpp *********/
+
+/********* Start of inlined file: juce_URL.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+URL::URL() throw()
+{
+}
+
+URL::URL (const String& url_)
+ : url (url_)
+{
+ int i = url.indexOfChar (T('?'));
+
+ if (i >= 0)
+ {
+ do
+ {
+ const int nextAmp = url.indexOfChar (i + 1, T('&'));
+ const int equalsPos = url.indexOfChar (i + 1, T('='));
+
+ if (equalsPos > i + 1)
+ {
+ if (nextAmp < 0)
+ {
+ parameters.set (removeEscapeChars (url.substring (i + 1, equalsPos)),
+ removeEscapeChars (url.substring (equalsPos + 1)));
+ }
+ else if (nextAmp > 0 && equalsPos < nextAmp)
+ {
+ parameters.set (removeEscapeChars (url.substring (i + 1, equalsPos)),
+ removeEscapeChars (url.substring (equalsPos + 1, nextAmp)));
+ }
+ }
+
+ i = nextAmp;
+ }
+ while (i >= 0);
+
+ url = url.upToFirstOccurrenceOf (T("?"), false, false);
+ }
+}
+
+URL::URL (const URL& other)
+ : url (other.url),
+ parameters (other.parameters),
+ filesToUpload (other.filesToUpload),
+ mimeTypes (other.mimeTypes)
+{
+}
+
+const URL& URL::operator= (const URL& other)
+{
+ url = other.url;
+ parameters = other.parameters;
+ filesToUpload = other.filesToUpload;
+ mimeTypes = other.mimeTypes;
+
+ return *this;
+}
+
+URL::~URL() throw()
+{
+}
+
+static const String getMangledParameters (const StringPairArray& parameters)
+{
+ String p;
+
+ for (int i = 0; i < parameters.size(); ++i)
+ {
+ if (i > 0)
+ p += T("&");
+
+ p << URL::addEscapeChars (parameters.getAllKeys() [i])
+ << T("=")
+ << URL::addEscapeChars (parameters.getAllValues() [i]);
+ }
+
+ return p;
+}
+
+const String URL::toString (const bool includeGetParameters) const
+{
+ if (includeGetParameters && parameters.size() > 0)
+ return url + T("?") + getMangledParameters (parameters);
+ else
+ return url;
+}
+
+bool URL::isWellFormed() const
+{
+ //xxx TODO
+ return url.isNotEmpty();
+}
+
+bool URL::isProbablyAWebsiteURL (const String& possibleURL)
+{
+ return (possibleURL.containsChar (T('.'))
+ && (! possibleURL.containsChar (T('@')))
+ && (! possibleURL.endsWithChar (T('.')))
+ && (possibleURL.startsWithIgnoreCase (T("www."))
+ || possibleURL.startsWithIgnoreCase (T("http:"))
+ || possibleURL.startsWithIgnoreCase (T("ftp:"))
+ || possibleURL.endsWithIgnoreCase (T(".com"))
+ || possibleURL.endsWithIgnoreCase (T(".net"))
+ || possibleURL.endsWithIgnoreCase (T(".org"))
+ || possibleURL.endsWithIgnoreCase (T(".co.uk")))
+ || possibleURL.startsWithIgnoreCase (T("file:")));
+}
+
+bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress)
+{
+ const int atSign = possibleEmailAddress.indexOfChar (T('@'));
+
+ return atSign > 0
+ && possibleEmailAddress.lastIndexOfChar (T('.')) > (atSign + 1)
+ && (! possibleEmailAddress.endsWithChar (T('.')));
+}
+
+void* juce_openInternetFile (const String& url,
+ const String& headers,
+ const MemoryBlock& optionalPostData,
+ const bool isPost,
+ URL::OpenStreamProgressCallback* callback,
+ void* callbackContext);
+
+void juce_closeInternetFile (void* handle);
+int juce_readFromInternetFile (void* handle, void* dest, int bytesToRead);
+int juce_seekInInternetFile (void* handle, int newPosition);
+
+class WebInputStream : public InputStream
+{
+public:
+
+ WebInputStream (const URL& url,
+ const bool isPost_,
+ URL::OpenStreamProgressCallback* const progressCallback_,
+ void* const progressCallbackContext_)
+ : position (0),
+ finished (false),
+ isPost (isPost_),
+ progressCallback (progressCallback_),
+ progressCallbackContext (progressCallbackContext_)
+ {
+ server = url.toString (! isPost);
+
+ if (isPost_)
+ createHeadersAndPostData (url);
+
+ handle = juce_openInternetFile (server, headers, postData, isPost,
+ progressCallback_, progressCallbackContext_);
+ }
+
+ ~WebInputStream()
+ {
+ juce_closeInternetFile (handle);
+ }
+
+ bool isError() const throw()
+ {
+ return handle == 0;
+ }
+
+ int64 getTotalLength()
+ {
+ return -1;
+ }
+
+ bool isExhausted()
+ {
+ return finished;
+ }
+
+ int read (void* dest, int bytes)
+ {
+ if (finished || isError())
+ {
+ return 0;
+ }
+ else
+ {
+ const int bytesRead = juce_readFromInternetFile (handle, dest, bytes);
+ position += bytesRead;
+
+ if (bytesRead == 0)
+ finished = true;
+
+ return bytesRead;
+ }
+ }
+
+ int64 getPosition()
+ {
+ return position;
+ }
+
+ bool setPosition (int64 wantedPos)
+ {
+ if (wantedPos != position)
+ {
+ finished = false;
+
+ const int actualPos = juce_seekInInternetFile (handle, (int) wantedPos);
+
+ if (actualPos == wantedPos)
+ {
+ position = wantedPos;
+ }
+ else
+ {
+ if (wantedPos < position)
+ {
+ juce_closeInternetFile (handle);
+
+ position = 0;
+ finished = false;
+
+ handle = juce_openInternetFile (server, headers, postData, isPost,
+ progressCallback, progressCallbackContext);
+ }
+
+ skipNextBytes (wantedPos - position);
+ }
+ }
+
+ return true;
+ }
+
+ juce_UseDebuggingNewOperator
+
+private:
+ String server, headers;
+ MemoryBlock postData;
+ int64 position;
+ bool finished;
+ const bool isPost;
+ void* handle;
+ URL::OpenStreamProgressCallback* const progressCallback;
+ void* const progressCallbackContext;
+
+ void createHeadersAndPostData (const URL& url)
+ {
+ if (url.getFilesToUpload().size() > 0)
+ {
+ // need to upload some files, so do it as multi-part...
+ String boundary (String::toHexString (Random::getSystemRandom().nextInt64()));
+
+ headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n";
+
+ appendUTF8ToPostData ("--" + boundary);
+
+ int i;
+ for (i = 0; i < url.getParameters().size(); ++i)
+ {
+ String s;
+ s << "\r\nContent-Disposition: form-data; name=\""
+ << url.getParameters().getAllKeys() [i]
+ << "\"\r\n\r\n"
+ << url.getParameters().getAllValues() [i]
+ << "\r\n--"
+ << boundary;
+
+ appendUTF8ToPostData (s);
+ }
+
+ for (i = 0; i < url.getFilesToUpload().size(); ++i)
+ {
+ const File f (url.getFilesToUpload().getAllValues() [i]);
+ const String paramName (url.getFilesToUpload().getAllKeys() [i]);
+
+ String s;
+ s << "\r\nContent-Disposition: form-data; name=\""
+ << paramName
+ << "\"; filename=\""
+ << f.getFileName()
+ << "\"\r\n";
+
+ const String mimeType (url.getMimeTypesOfUploadFiles()
+ .getValue (paramName, String::empty));
+
+ if (mimeType.isNotEmpty())
+ s << "Content-Type: " << mimeType << "\r\n";
+
+ s << "Content-Transfer-Encoding: binary\r\n\r\n";
+
+ appendUTF8ToPostData (s);
+
+ f.loadFileAsData (postData);
+
+ s = "\r\n--" + boundary;
+
+ appendUTF8ToPostData (s);
+ }
+
+ appendUTF8ToPostData ("--\r\n");
+ }
+ else
+ {
+ // just a short text attachment, so use simple url encoding..
+ const String params (getMangledParameters (url.getParameters()));
+
+ headers = "Content-Type: application/x-www-form-urlencoded\r\nContent-length: "
+ + String ((int) strlen (params.toUTF8()))
+ + "\r\n";
+
+ appendUTF8ToPostData (params);
+ }
+ }
+
+ void appendUTF8ToPostData (const String& text) throw()
+ {
+ postData.append (text.toUTF8(),
+ (int) strlen (text.toUTF8()));
+ }
+
+ WebInputStream (const WebInputStream&);
+ const WebInputStream& operator= (const WebInputStream&);
+};
+
+InputStream* URL::createInputStream (const bool usePostCommand,
+ OpenStreamProgressCallback* const progressCallback,
+ void* const progressCallbackContext) const
+{
+ WebInputStream* wi = new WebInputStream (*this, usePostCommand,
+ progressCallback, progressCallbackContext);
+
+ if (wi->isError())
+ {
+ delete wi;
+ wi = 0;
+ }
+
+ return wi;
+}
+
+bool URL::readEntireBinaryStream (MemoryBlock& destData,
+ const bool usePostCommand) const
+{
+ InputStream* const in = createInputStream (usePostCommand);
+
+ if (in != 0)
+ {
+ in->readIntoMemoryBlock (destData, -1);
+ delete in;
+
+ return true;
+ }
+
+ return false;
+}
+
+const String URL::readEntireTextStream (const bool usePostCommand) const
+{
+ String result;
+ InputStream* const in = createInputStream (usePostCommand);
+
+ if (in != 0)
+ {
+ result = in->readEntireStreamAsString();
+ delete in;
+ }
+
+ return result;
+}
+
+XmlElement* URL::readEntireXmlStream (const bool usePostCommand) const
+{
+ XmlDocument doc (readEntireTextStream (usePostCommand));
+ return doc.getDocumentElement();
+}
+
+const URL URL::withParameter (const String& parameterName,
+ const String& parameterValue) const
+{
+ URL u (*this);
+ u.parameters.set (parameterName, parameterValue);
+ return u;
+}
+
+const URL URL::withFileToUpload (const String& parameterName,
+ const File& fileToUpload,
+ const String& mimeType) const
+{
+ URL u (*this);
+ u.filesToUpload.set (parameterName, fileToUpload.getFullPathName());
+ u.mimeTypes.set (parameterName, mimeType);
+ return u;
+}
+
+const StringPairArray& URL::getParameters() const throw()
+{
+ return parameters;
+}
+
+const StringPairArray& URL::getFilesToUpload() const throw()
+{
+ return filesToUpload;
+}
+
+const StringPairArray& URL::getMimeTypesOfUploadFiles() const throw()
+{
+ return mimeTypes;
+}
+
+const String URL::removeEscapeChars (const String& s)
+{
+ const int len = s.length();
+ uint8* const resultUTF8 = (uint8*) juce_calloc (len * 4);
+ uint8* r = resultUTF8;
+
+ for (int i = 0; i < len; ++i)
+ {
+ char c = (char) s[i];
+ if (c == 0)
+ break;
+
+ if (c == '+')
+ {
+ c = ' ';
+ }
+ else if (c == '%')
+ {
+ c = (char) s.substring (i + 1, i + 3).getHexValue32();
+ i += 2;
+ }
+
+ *r++ = c;
+ }
+
+ const String stringResult (String::fromUTF8 (resultUTF8));
+ juce_free (resultUTF8);
+ return stringResult;
+}
+
+const String URL::addEscapeChars (const String& s)
+{
+ String result;
+ result.preallocateStorage (s.length() + 8);
+ const char* utf8 = s.toUTF8();
+
+ while (*utf8 != 0)
+ {
+ const char c = *utf8++;
+
+ if (c == ' ')
+ {
+ result += T('+');
+ }
+ else if (CharacterFunctions::isLetterOrDigit (c)
+ || CharacterFunctions::indexOfChar ("_-$.*!'(),", c, false) >= 0)
+ {
+ result << c;
+ }
+ else
+ {
+ const int v = (int) (uint8) c;
+
+ if (v < 0x10)
+ result << T("%0");
+ else
+ result << T('%');
+
+ result << String::toHexString (v);
+ }
+ }
+
+ return result;
+}
+
+extern bool juce_launchFile (const String& fileName,
+ const String& parameters) throw();
+
+bool URL::launchInDefaultBrowser() const
+{
+ String u (toString (true));
+
+ if (u.contains (T("@")) && ! u.contains (T(":")))
+ u = "mailto:" + u;
+
+ return juce_launchFile (u, String::empty);
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_URL.cpp *********/
+
+/********* Start of inlined file: juce_BufferedInputStream.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+BufferedInputStream::BufferedInputStream (InputStream* const source_,
+ const int bufferSize_,
+ const bool deleteSourceWhenDestroyed_) throw()
+ : source (source_),
+ deleteSourceWhenDestroyed (deleteSourceWhenDestroyed_),
+ bufferSize (jmax (256, bufferSize_)),
+ position (source_->getPosition()),
+ lastReadPos (0),
+ bufferOverlap (128)
+{
+ const int sourceSize = (int) source_->getTotalLength();
+ if (sourceSize >= 0)
+ bufferSize = jmin (jmax (32, sourceSize), bufferSize);
+
+ bufferStart = position;
+ buffer = (char*) juce_malloc (bufferSize);
+}
+
+BufferedInputStream::~BufferedInputStream() throw()
+{
+ if (deleteSourceWhenDestroyed)
+ delete source;
+
+ juce_free (buffer);
+}
+
+int64 BufferedInputStream::getTotalLength()
+{
+ return source->getTotalLength();
+}
+
+int64 BufferedInputStream::getPosition()
+{
+ return position;
+}
+
+bool BufferedInputStream::setPosition (int64 newPosition)
+{
+ position = jmax ((int64) 0, newPosition);
+ return true;
+}
+
+bool BufferedInputStream::isExhausted()
+{
+ return (position >= lastReadPos)
+ && source->isExhausted();
+}
+
+void BufferedInputStream::ensureBuffered()
+{
+ const int64 bufferEndOverlap = lastReadPos - bufferOverlap;
+
+ if (position < bufferStart || position >= bufferEndOverlap)
+ {
+ int bytesRead;
+
+ if (position < lastReadPos
+ && position >= bufferEndOverlap
+ && position >= bufferStart)
+ {
+ const int bytesToKeep = (int) (lastReadPos - position);
+ memmove (buffer, buffer + position - bufferStart, bytesToKeep);
+
+ bufferStart = position;
+
+ bytesRead = source->read (buffer + bytesToKeep,
+ bufferSize - bytesToKeep);
+
+ lastReadPos += bytesRead;
+ bytesRead += bytesToKeep;
+ }
+ else
+ {
+ bufferStart = position;
+ source->setPosition (bufferStart);
+ bytesRead = source->read (buffer, bufferSize);
+ lastReadPos = bufferStart + bytesRead;
+ }
+
+ while (bytesRead < bufferSize)
+ buffer [bytesRead++] = 0;
+ }
+}
+
+int BufferedInputStream::read (void* destBuffer, int maxBytesToRead)
+{
+ if (position >= bufferStart
+ && position + maxBytesToRead < lastReadPos)
+ {
+ memcpy (destBuffer, buffer + (position - bufferStart), maxBytesToRead);
+ position += maxBytesToRead;
+
+ return maxBytesToRead;
+ }
+ else
+ {
+ int bytesRead = 0;
+
+ while (maxBytesToRead > 0)
+ {
+ ensureBuffered();
+
+ if (isExhausted())
+ break;
+
+ const int bytesAvailable = jmin (maxBytesToRead, (int) (lastReadPos - position));
+ memcpy (destBuffer, buffer + (position - bufferStart), bytesAvailable);
+ maxBytesToRead -= bytesAvailable;
+ bytesRead += bytesAvailable;
+ position += bytesAvailable;
+ destBuffer = (void*) (((char*) destBuffer) + bytesAvailable);
+ }
+
+ return bytesRead;
+ }
+}
+
+const String BufferedInputStream::readString()
+{
+ if (position >= bufferStart
+ && position < lastReadPos)
+ {
+ const int maxChars = (int) (lastReadPos - position);
+
+ const char* const src = buffer + (position - bufferStart);
+
+ for (int i = 0; i < maxChars; ++i)
+ {
+ if (src[i] == 0)
+ {
+ position += i + 1;
+
+ return String::fromUTF8 ((const uint8*) src, i);
+ }
+ }
+ }
+
+ return InputStream::readString();
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_BufferedInputStream.cpp *********/
+
+/********* Start of inlined file: juce_FileInputSource.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+FileInputSource::FileInputSource (const File& file_) throw()
+ : file (file_)
+{
+}
+
+FileInputSource::~FileInputSource()
+{
+}
+
+InputStream* FileInputSource::createInputStream()
+{
+ return file.createInputStream();
+}
+
+InputStream* FileInputSource::createInputStreamFor (const String& relatedItemPath)
+{
+ return file.getSiblingFile (relatedItemPath).createInputStream();
+}
+
+int64 FileInputSource::hashCode() const
+{
+ return file.hashCode();
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_FileInputSource.cpp *********/
+
+/********* Start of inlined file: juce_MemoryInputStream.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+MemoryInputStream::MemoryInputStream (const void* const sourceData,
+ const int sourceDataSize,
+ const bool keepInternalCopy) throw()
+ : data ((const char*) sourceData),
+ dataSize (sourceDataSize),
+ position (0)
+{
+ if (keepInternalCopy)
+ {
+ internalCopy.append (data, sourceDataSize);
+ data = (const char*) internalCopy.getData();
+ }
+}
+
+MemoryInputStream::~MemoryInputStream() throw()
+{
+}
+
+int64 MemoryInputStream::getTotalLength()
+{
+ return dataSize;
+}
+
+int MemoryInputStream::read (void* buffer, int howMany)
+{
+ const int num = jmin (howMany, dataSize - position);
+ memcpy (buffer, data + position, num);
+ position += num;
+ return num;
+}
+
+bool MemoryInputStream::isExhausted()
+{
+ return (position >= dataSize);
+}
+
+bool MemoryInputStream::setPosition (int64 pos)
+{
+ position = (int) jlimit ((int64) 0, (int64) dataSize, pos);
+
+ return true;
+}
+
+int64 MemoryInputStream::getPosition()
+{
+ return position;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_MemoryInputStream.cpp *********/
+
+/********* Start of inlined file: juce_MemoryOutputStream.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+MemoryOutputStream::MemoryOutputStream (const int initialSize,
+ const int blockSizeToIncreaseBy,
+ MemoryBlock* const memoryBlockToWriteTo) throw()
+ : data (memoryBlockToWriteTo),
+ position (0),
+ size (0),
+ blockSize (jmax (16, blockSizeToIncreaseBy)),
+ ownsMemoryBlock (memoryBlockToWriteTo == 0)
+{
+ if (memoryBlockToWriteTo == 0)
+ data = new MemoryBlock (initialSize);
+ else
+ memoryBlockToWriteTo->setSize (initialSize, false);
+}
+
+MemoryOutputStream::~MemoryOutputStream() throw()
+{
+ if (ownsMemoryBlock)
+ delete data;
+ else
+ flush();
+}
+
+void MemoryOutputStream::flush()
+{
+ if (! ownsMemoryBlock)
+ data->setSize (size, false);
+}
+
+void MemoryOutputStream::reset() throw()
+{
+ position = 0;
+ size = 0;
+}
+
+bool MemoryOutputStream::write (const void* buffer, int howMany)
+{
+ int storageNeeded = position + howMany + 1;
+ storageNeeded = storageNeeded - (storageNeeded % blockSize) + blockSize;
+
+ data->ensureSize (storageNeeded);
+ data->copyFrom (buffer, position, howMany);
+ position += howMany;
+ size = jmax (size, position);
+
+ return true;
+}
+
+const char* MemoryOutputStream::getData() throw()
+{
+ if (data->getSize() > size)
+ ((char*) data->getData()) [size] = 0;
+
+ return (const char*) data->getData();
+}
+
+int MemoryOutputStream::getDataSize() const throw()
+{
+ return size;
+}
+
+int64 MemoryOutputStream::getPosition()
+{
+ return position;
+}
+
+bool MemoryOutputStream::setPosition (int64 newPosition)
+{
+ if (newPosition <= size)
+ {
+ // ok to seek backwards
+ position = jlimit (0, size, (int) newPosition);
+ return true;
+ }
+ else
+ {
+ // trying to make it bigger isn't a good thing to do..
+ return false;
+ }
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_MemoryOutputStream.cpp *********/
+
+/********* Start of inlined file: juce_SubregionStream.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+SubregionStream::SubregionStream (InputStream* const sourceStream,
+ const int64 startPositionInSourceStream_,
+ const int64 lengthOfSourceStream_,
+ const bool deleteSourceWhenDestroyed_) throw()
+ : source (sourceStream),
+ deleteSourceWhenDestroyed (deleteSourceWhenDestroyed_),
+ startPositionInSourceStream (startPositionInSourceStream_),
+ lengthOfSourceStream (lengthOfSourceStream_)
+{
+ setPosition (0);
+}
+
+SubregionStream::~SubregionStream() throw()
+{
+ if (deleteSourceWhenDestroyed)
+ delete source;
+}
+
+int64 SubregionStream::getTotalLength()
+{
+ const int64 srcLen = source->getTotalLength() - startPositionInSourceStream;
+
+ return (lengthOfSourceStream >= 0) ? jmin (lengthOfSourceStream, srcLen)
+ : srcLen;
+}
+
+int64 SubregionStream::getPosition()
+{
+ return source->getPosition() - startPositionInSourceStream;
+}
+
+bool SubregionStream::setPosition (int64 newPosition)
+{
+ return source->setPosition (jmax ((int64) 0, newPosition + startPositionInSourceStream));
+}
+
+int SubregionStream::read (void* destBuffer, int maxBytesToRead)
+{
+ if (lengthOfSourceStream < 0)
+ {
+ return source->read (destBuffer, maxBytesToRead);
+ }
+ else
+ {
+ maxBytesToRead = (int) jmin ((int64) maxBytesToRead, lengthOfSourceStream - getPosition());
+
+ if (maxBytesToRead <= 0)
+ return 0;
+
+ return source->read (destBuffer, maxBytesToRead);
+ }
+}
+
+bool SubregionStream::isExhausted()
+{
+ if (lengthOfSourceStream >= 0)
+ return (getPosition() >= lengthOfSourceStream) || source->isExhausted();
+ else
+ return source->isExhausted();
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_SubregionStream.cpp *********/
+
+/********* Start of inlined file: juce_PerformanceCounter.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+PerformanceCounter::PerformanceCounter (const String& name_,
+ int runsPerPrintout,
+ const File& loggingFile)
+ : name (name_),
+ numRuns (0),
+ runsPerPrint (runsPerPrintout),
+ totalTime (0),
+ outputFile (loggingFile)
+{
+ if (outputFile != File::nonexistent)
+ {
+ String s ("**** Counter for \"");
+ s << name_ << "\" started at: "
+ << Time::getCurrentTime().toString (true, true)
+ << "\r\n";
+
+ outputFile.appendText (s, false, false);
+ }
+}
+
+PerformanceCounter::~PerformanceCounter()
+{
+ printStatistics();
+}
+
+void PerformanceCounter::start()
+{
+ started = Time::getHighResolutionTicks();
+}
+
+void PerformanceCounter::stop()
+{
+ const int64 now = Time::getHighResolutionTicks();
+
+ totalTime += 1000.0 * Time::highResolutionTicksToSeconds (now - started);
+
+ if (++numRuns == runsPerPrint)
+ printStatistics();
+}
+
+void PerformanceCounter::printStatistics()
+{
+ if (numRuns > 0)
+ {
+ String s ("Performance count for \"");
+ s << name << "\" - average over " << numRuns << " run(s) = ";
+
+ const int micros = (int) (totalTime * (1000.0 / numRuns));
+
+ if (micros > 10000)
+ s << (micros/1000) << " millisecs";
+ else
+ s << micros << " microsecs";
+
+ s << ", total = " << String (totalTime / 1000, 5) << " seconds";
+
+ Logger::outputDebugString (s);
+
+ s << "\r\n";
+
+ if (outputFile != File::nonexistent)
+ outputFile.appendText (s, false, false);
+
+ numRuns = 0;
+ totalTime = 0;
+ }
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_PerformanceCounter.cpp *********/
+
+/********* Start of inlined file: juce_Uuid.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+Uuid::Uuid()
+{
+ // do some serious mixing up of our MAC addresses and different types of time info,
+ // plus a couple of passes of pseudo-random numbers over the whole thing.
+ SystemStats::getMACAddresses (value.asInt64, 2);
+
+ int i;
+ for (i = 16; --i >= 0;)
+ {
+ Random r (Time::getHighResolutionTicks()
+ + Random::getSystemRandom().nextInt()
+ + value.asInt [i & 3]);
+
+ value.asBytes[i] ^= (uint8) r.nextInt();
+ }
+
+ value.asInt64 [0] ^= Time::getHighResolutionTicks();
+ value.asInt64 [1] ^= Time::currentTimeMillis();
+
+ for (i = 4; --i >= 0;)
+ {
+ Random r (Time::getHighResolutionTicks() ^ value.asInt[i]);
+ value.asInt[i] ^= r.nextInt();
+ }
+}
+
+Uuid::~Uuid() throw()
+{
+}
+
+Uuid::Uuid (const Uuid& other)
+ : value (other.value)
+{
+}
+
+Uuid& Uuid::operator= (const Uuid& other)
+{
+ if (this != &other)
+ value = other.value;
+
+ return *this;
+}
+
+bool Uuid::operator== (const Uuid& other) const
+{
+ return memcmp (value.asBytes, other.value.asBytes, 16) == 0;
+}
+
+bool Uuid::operator!= (const Uuid& other) const
+{
+ return ! operator== (other);
+}
+
+bool Uuid::isNull() const throw()
+{
+ return (value.asInt64 [0] == 0) && (value.asInt64 [1] == 0);
+}
+
+const String Uuid::toString() const
+{
+ return String::toHexString (value.asBytes, 16, 0);
+}
+
+Uuid::Uuid (const String& uuidString)
+{
+ operator= (uuidString);
+}
+
+Uuid& Uuid::operator= (const String& uuidString)
+{
+ int destIndex = 0;
+ int i = 0;
+
+ for (;;)
+ {
+ int byte = 0;
+
+ for (int loop = 2; --loop >= 0;)
+ {
+ byte <<= 4;
+
+ for (;;)
+ {
+ const tchar c = uuidString [i++];
+
+ if (c >= T('0') && c <= T('9'))
+ {
+ byte |= c - T('0');
+ break;
+ }
+ else if (c >= T('a') && c <= T('z'))
+ {
+ byte |= c - (T('a') - 10);
+ break;
+ }
+ else if (c >= T('A') && c <= T('Z'))
+ {
+ byte |= c - (T('A') - 10);
+ break;
+ }
+ else if (c == 0)
+ {
+ while (destIndex < 16)
+ value.asBytes [destIndex++] = 0;
+
+ return *this;
+ }
+ }
+ }
+
+ value.asBytes [destIndex++] = (uint8) byte;
+ }
+}
+
+Uuid::Uuid (const uint8* const rawData)
+{
+ operator= (rawData);
+}
+
+Uuid& Uuid::operator= (const uint8* const rawData)
+{
+ if (rawData != 0)
+ memcpy (value.asBytes, rawData, 16);
+ else
+ zeromem (value.asBytes, 16);
+
+ return *this;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_Uuid.cpp *********/
+
+/********* Start of inlined file: juce_ZipFile.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+struct ZipEntryInfo
+{
+ ZipFile::ZipEntry entry;
+ int streamOffset;
+ int compressedSize;
+ bool compressed;
+};
+
+class ZipInputStream : public InputStream
+{
+public:
+
+ ZipInputStream (ZipFile& file_,
+ ZipEntryInfo& zei) throw()
+ : file (file_),
+ zipEntryInfo (zei),
+ pos (0),
+ headerSize (0)
+ {
+ char buffer [30];
+
+ if (file.source->setPosition (zei.streamOffset)
+ && file.source->read (buffer, 30) == 30
+ && littleEndianInt (buffer) == 0x04034b50)
+ {
+ headerSize = 30 + littleEndianShort (buffer + 26)
+ + littleEndianShort (buffer + 28);
+ }
+
+ if (! file_.isFromCustomStream)
+ {
+ source = file_.sourceFile.createInputStream();
+ }
+ else
+ {
+ source = 0;
+#ifdef JUCE_DEBUG
+ file_.numOpenStreams++;
+#endif
+ }
+ }
+
+ ~ZipInputStream() throw()
+ {
+#ifdef JUCE_DEBUG
+ if (source == 0)
+ file.numOpenStreams--;
+#endif
+
+ delete source;
+ }
+
+ int64 getTotalLength() throw()
+ {
+ return zipEntryInfo.compressedSize;
+ }
+
+ int read (void* buffer, int howMany) throw()
+ {
+ if (headerSize <= 0)
+ return 0;
+
+ howMany = (int) jmin ((int64) howMany, zipEntryInfo.compressedSize - pos);
+ int num;
+
+ if (source != 0)
+ {
+ source->setPosition (pos + zipEntryInfo.streamOffset + headerSize);
+ num = source->read (buffer, howMany);
+ }
+ else
+ {
+ const ScopedLock sl (file.lock);
+
+ file.source->setPosition (pos + zipEntryInfo.streamOffset + headerSize);
+ num = file.source->read (buffer, howMany);
+ }
+
+ pos += num;
+ return num;
+ }
+
+ bool isExhausted() throw()
+ {
+ return pos >= zipEntryInfo.compressedSize;
+ }
+
+ int64 getPosition() throw()
+ {
+ return pos;
+ }
+
+ bool setPosition (int64 newPos) throw()
+ {
+ pos = jlimit ((int64) 0, (int64) zipEntryInfo.compressedSize, newPos);
+ return true;
+ }
+
+private:
+
+ ZipFile& file;
+ ZipEntryInfo zipEntryInfo;
+ int64 pos;
+ int headerSize;
+ InputStream* source;
+
+ ZipInputStream (const ZipInputStream&);
+ const ZipInputStream& operator= (const ZipInputStream&);
+};
+
+ZipFile::ZipFile (InputStream* const source_,
+ const bool deleteStreamWhenDestroyed_) throw()
+ : source (source_),
+ isFromCustomStream (true),
+ deleteStreamWhenDestroyed (deleteStreamWhenDestroyed_)
+#ifdef JUCE_DEBUG
+ , numOpenStreams (0)
+#endif
+{
+ init();
+}
+
+ZipFile::ZipFile (const File& file)
+ : sourceFile (file),
+ isFromCustomStream (false),
+ deleteStreamWhenDestroyed (true)
+#ifdef JUCE_DEBUG
+ , numOpenStreams (0)
+#endif
+{
+ source = file.createInputStream();
+ init();
+}
+
+ZipFile::~ZipFile() throw()
+{
+ for (int i = entries.size(); --i >= 0;)
+ {
+ ZipEntryInfo* const zei = (ZipEntryInfo*) entries [i];
+ delete zei;
+ }
+
+ if (deleteStreamWhenDestroyed && (source != 0))
+ delete source;
+
+#ifdef JUCE_DEBUG
+ // If you hit this assertion, it means you've created a stream to read
+ // one of the items in the zipfile, but you've forgotten to delete that
+ // stream object before deleting the file.. Streams can't be kept open
+ // after the file is deleted because they need to share the input
+ // stream that the file uses to read itself.
+ jassert (numOpenStreams == 0);
+#endif
+}
+
+int ZipFile::getNumEntries() const throw()
+{
+ return entries.size();
+}
+
+const ZipFile::ZipEntry* ZipFile::getEntry (const int index) const throw()
+{
+ ZipEntryInfo* const zei = (ZipEntryInfo*) entries [index];
+
+ return (zei != 0) ? &(zei->entry)
+ : 0;
+}
+
+int ZipFile::getIndexOfFileName (const String& fileName) const throw()
+{
+ for (int i = 0; i < entries.size(); ++i)
+ if (((ZipEntryInfo*) entries.getUnchecked (i))->entry.filename == fileName)
+ return i;
+
+ return -1;
+}
+
+const ZipFile::ZipEntry* ZipFile::getEntry (const String& fileName) const throw()
+{
+ return getEntry (getIndexOfFileName (fileName));
+}
+
+InputStream* ZipFile::createStreamForEntry (const int index)
+{
+ ZipEntryInfo* const zei = (ZipEntryInfo*) entries[index];
+
+ InputStream* stream = 0;
+
+ if (zei != 0)
+ {
+ stream = new ZipInputStream (*this, *zei);
+
+ if (zei->compressed)
+ {
+ stream = new GZIPDecompressorInputStream (stream, true, true,
+ zei->entry.uncompressedSize);
+
+ // (much faster to unzip in big blocks using a buffer..)
+ stream = new BufferedInputStream (stream, 32768, true);
+ }
+ }
+
+ return stream;
+}
+
+class ZipFilenameComparator
+{
+public:
+ static int compareElements (const void* const first, const void* const second) throw()
+ {
+ return ((const ZipEntryInfo*) first)->entry.filename
+ .compare (((const ZipEntryInfo*) second)->entry.filename);
+ }
+};
+
+void ZipFile::sortEntriesByFilename()
+{
+ ZipFilenameComparator sorter;
+ entries.sort (sorter);
+}
+
+void ZipFile::init()
+{
+ if (source != 0)
+ {
+ numEntries = 0;
+ int pos = findEndOfZipEntryTable();
+ int size = (int) (source->getTotalLength() - pos);
+
+ source->setPosition (pos);
+ MemoryBlock headerData;
+
+ if (source->readIntoMemoryBlock (headerData, size) != size)
+ return;
+
+ pos = 0;
+
+ for (int i = 0; i < numEntries; ++i)
+ {
+ if (pos + 46 > size)
+ break;
+
+ const char* const buffer = ((const char*) headerData.getData()) + pos;
+
+ const int fileNameLen = littleEndianShort (buffer + 28);
+
+ if (pos + 46 + fileNameLen > size)
+ break;
+
+ ZipEntryInfo* const zei = new ZipEntryInfo();
+ zei->entry.filename = String (buffer + 46, fileNameLen);
+
+ const int time = littleEndianShort (buffer + 12);
+ const int date = littleEndianShort (buffer + 14);
+
+ const int year = 1980 + (date >> 9);
+ const int month = ((date >> 5) & 15) - 1;
+ const int day = date & 31;
+ const int hours = time >> 11;
+ const int minutes = (time >> 5) & 63;
+ const int seconds = (time & 31) << 1;
+
+ zei->entry.fileTime = Time (year, month, day, hours, minutes, seconds);
+
+ zei->compressed = littleEndianShort (buffer + 10) != 0;
+ zei->compressedSize = littleEndianInt (buffer + 20);
+ zei->entry.uncompressedSize = littleEndianInt (buffer + 24);
+
+ zei->streamOffset = littleEndianInt (buffer + 42);
+ entries.add (zei);
+
+ pos += 46 + fileNameLen
+ + littleEndianShort (buffer + 30)
+ + littleEndianShort (buffer + 32);
+ }
+ }
+}
+
+int ZipFile::findEndOfZipEntryTable()
+{
+ BufferedInputStream in (source, 8192, false);
+
+ in.setPosition (in.getTotalLength());
+ int64 pos = in.getPosition();
+
+ char buffer [32];
+ zeromem (buffer, sizeof (buffer));
+
+ while (pos > 0)
+ {
+ in.setPosition (pos - 22);
+ pos = in.getPosition();
+ memcpy (buffer + 22, buffer, 4);
+
+ if (in.read (buffer, 22) != 22)
+ return 0;
+
+ for (int i = 0; i < 22; ++i)
+ {
+ if (littleEndianInt (buffer + i) == 0x06054b50)
+ {
+ in.setPosition (pos + i);
+ in.read (buffer, 22);
+ numEntries = littleEndianShort (buffer + 10);
+
+ return littleEndianInt (buffer + 16);
+ }
+ }
+ }
+
+ return 0;
+}
+
+void ZipFile::uncompressTo (const File& targetDirectory,
+ const bool shouldOverwriteFiles)
+{
+ for (int i = 0; i < entries.size(); ++i)
+ {
+ const ZipEntryInfo& zei = *(ZipEntryInfo*) entries[i];
+
+ const File targetFile (targetDirectory.getChildFile (zei.entry.filename));
+
+ if (zei.entry.filename.endsWithChar (T('/')))
+ {
+ targetFile.createDirectory(); // (entry is a directory, not a file)
+ }
+ else
+ {
+ InputStream* const in = createStreamForEntry (i);
+
+ if (in != 0)
+ {
+ if (shouldOverwriteFiles)
+ targetFile.deleteFile();
+
+ if ((! targetFile.exists())
+ && targetFile.getParentDirectory().createDirectory())
+ {
+ FileOutputStream* const out = targetFile.createOutputStream();
+
+ if (out != 0)
+ {
+ out->writeFromInputStream (*in, -1);
+ delete out;
+
+ targetFile.setCreationTime (zei.entry.fileTime);
+ targetFile.setLastModificationTime (zei.entry.fileTime);
+ targetFile.setLastAccessTime (zei.entry.fileTime);
+ }
+ }
+
+ delete in;
+ }
+ }
+ }
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_ZipFile.cpp *********/
+
+/********* Start of inlined file: juce_CharacterFunctions.cpp *********/
+#ifdef _MSC_VER
+ #pragma warning (disable: 4514 4996)
+ #pragma warning (push)
+#endif
+
+#include
+#include
+#include
+
+#ifdef _MSC_VER
+ #pragma warning (pop)
+#endif
+
+BEGIN_JUCE_NAMESPACE
+
+int CharacterFunctions::length (const char* const s) throw()
+{
+ return (int) strlen (s);
+}
+
+int CharacterFunctions::length (const juce_wchar* const s) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ int n = 0;
+ while (s[n] != 0)
+ ++n;
+
+ return n;
+#else
+ return (int) wcslen (s);
+#endif
+}
+
+void CharacterFunctions::copy (char* dest, const char* src, const int maxChars) throw()
+{
+ strncpy (dest, src, maxChars);
+}
+
+void CharacterFunctions::copy (juce_wchar* dest, const juce_wchar* src, int maxChars) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ while (--maxChars >= 0 && *src != 0)
+ *dest++ = *src++;
+
+ *dest = 0;
+#else
+ wcsncpy (dest, src, maxChars);
+#endif
+}
+
+void CharacterFunctions::copy (juce_wchar* dest, const char* src, const int maxChars) throw()
+{
+ mbstowcs (dest, src, maxChars);
+}
+
+void CharacterFunctions::copy (char* dest, const juce_wchar* src, const int maxChars) throw()
+{
+ wcstombs (dest, src, maxChars);
+}
+
+void CharacterFunctions::append (char* dest, const char* src) throw()
+{
+ strcat (dest, src);
+}
+
+void CharacterFunctions::append (juce_wchar* dest, const juce_wchar* src) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ while (*dest != 0)
+ ++dest;
+
+ while (*src != 0)
+ *dest++ = *src++;
+
+ *dest = 0;
+#else
+ wcscat (dest, src);
+#endif
+}
+
+int CharacterFunctions::compare (const char* const s1, const char* const s2) throw()
+{
+ return strcmp (s1, s2);
+}
+
+int CharacterFunctions::compare (const juce_wchar* s1, const juce_wchar* s2) throw()
+{
+ jassert (s1 != 0 && s2 != 0);
+
+#if MACOS_10_2_OR_EARLIER
+ for (;;)
+ {
+ if (*s1 != *s2)
+ {
+ const int diff = *s1 - *s2;
+
+ if (diff != 0)
+ return diff < 0 ? -1 : 1;
+ }
+ else if (*s1 == 0)
+ break;
+
+ ++s1;
+ ++s2;
+ }
+
+ return 0;
+#else
+ return wcscmp (s1, s2);
+#endif
+}
+
+int CharacterFunctions::compare (const char* const s1, const char* const s2, const int maxChars) throw()
+{
+ jassert (s1 != 0 && s2 != 0);
+
+ return strncmp (s1, s2, maxChars);
+}
+
+int CharacterFunctions::compare (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw()
+{
+ jassert (s1 != 0 && s2 != 0);
+
+#if MACOS_10_2_OR_EARLIER
+ while (--maxChars >= 0)
+ {
+ if (*s1 != *s2)
+ return (*s1 < *s2) ? -1 : 1;
+ else if (*s1 == 0)
+ break;
+
+ ++s1;
+ ++s2;
+ }
+
+ return 0;
+#else
+ return wcsncmp (s1, s2, maxChars);
+#endif
+}
+
+int CharacterFunctions::compareIgnoreCase (const char* const s1, const char* const s2) throw()
+{
+ jassert (s1 != 0 && s2 != 0);
+
+#if JUCE_WIN32
+ return stricmp (s1, s2);
+#else
+ return strcasecmp (s1, s2);
+#endif
+}
+
+int CharacterFunctions::compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2) throw()
+{
+ jassert (s1 != 0 && s2 != 0);
+
+#if JUCE_WIN32
+ return _wcsicmp (s1, s2);
+#else
+ for (;;)
+ {
+ if (*s1 != *s2)
+ {
+ const int diff = toUpperCase (*s1) - toUpperCase (*s2);
+
+ if (diff != 0)
+ return diff < 0 ? -1 : 1;
+ }
+ else if (*s1 == 0)
+ break;
+
+ ++s1;
+ ++s2;
+ }
+
+ return 0;
+#endif
+}
+
+int CharacterFunctions::compareIgnoreCase (const char* const s1, const char* const s2, const int maxChars) throw()
+{
+ jassert (s1 != 0 && s2 != 0);
+
+#if JUCE_WIN32
+ return strnicmp (s1, s2, maxChars);
+#else
+ return strncasecmp (s1, s2, maxChars);
+#endif
+}
+
+int CharacterFunctions::compareIgnoreCase (const juce_wchar* s1, const juce_wchar* s2, int maxChars) throw()
+{
+ jassert (s1 != 0 && s2 != 0);
+
+#if JUCE_WIN32
+ return _wcsnicmp (s1, s2, maxChars);
+#else
+ while (--maxChars >= 0)
+ {
+ if (*s1 != *s2)
+ {
+ const int diff = toUpperCase (*s1) - toUpperCase (*s2);
+
+ if (diff != 0)
+ return diff < 0 ? -1 : 1;
+ }
+ else if (*s1 == 0)
+ break;
+
+ ++s1;
+ ++s2;
+ }
+
+ return 0;
+#endif
+}
+
+const char* CharacterFunctions::find (const char* const haystack, const char* const needle) throw()
+{
+ return strstr (haystack, needle);
+}
+
+const juce_wchar* CharacterFunctions::find (const juce_wchar* haystack, const juce_wchar* const needle) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ while (*haystack != 0)
+ {
+ const juce_wchar* s1 = haystack;
+ const juce_wchar* s2 = needle;
+
+ for (;;)
+ {
+ if (*s2 == 0)
+ return haystack;
+
+ if (*s1 != *s2 || *s2 == 0)
+ break;
+
+ ++s1;
+ ++s2;
+ }
+
+ ++haystack;
+ }
+
+ return 0;
+#else
+ return wcsstr (haystack, needle);
+#endif
+}
+
+int CharacterFunctions::indexOfChar (const char* const haystack, const char needle, const bool ignoreCase) throw()
+{
+ if (haystack != 0)
+ {
+ int i = 0;
+
+ if (ignoreCase)
+ {
+ const char n1 = toLowerCase (needle);
+ const char n2 = toUpperCase (needle);
+
+ if (n1 != n2) // if the char is the same in upper/lower case, fall through to the normal search
+ {
+ while (haystack[i] != 0)
+ {
+ if (haystack[i] == n1 || haystack[i] == n2)
+ return i;
+
+ ++i;
+ }
+
+ return -1;
+ }
+
+ jassert (n1 == needle);
+ }
+
+ while (haystack[i] != 0)
+ {
+ if (haystack[i] == needle)
+ return i;
+
+ ++i;
+ }
+ }
+
+ return -1;
+}
+
+int CharacterFunctions::indexOfChar (const juce_wchar* const haystack, const juce_wchar needle, const bool ignoreCase) throw()
+{
+ if (haystack != 0)
+ {
+ int i = 0;
+
+ if (ignoreCase)
+ {
+ const juce_wchar n1 = toLowerCase (needle);
+ const juce_wchar n2 = toUpperCase (needle);
+
+ if (n1 != n2) // if the char is the same in upper/lower case, fall through to the normal search
+ {
+ while (haystack[i] != 0)
+ {
+ if (haystack[i] == n1 || haystack[i] == n2)
+ return i;
+
+ ++i;
+ }
+
+ return -1;
+ }
+
+ jassert (n1 == needle);
+ }
+
+ while (haystack[i] != 0)
+ {
+ if (haystack[i] == needle)
+ return i;
+
+ ++i;
+ }
+ }
+
+ return -1;
+}
+
+int CharacterFunctions::indexOfCharFast (const char* const haystack, const char needle) throw()
+{
+ jassert (haystack != 0);
+
+ int i = 0;
+ while (haystack[i] != 0)
+ {
+ if (haystack[i] == needle)
+ return i;
+
+ ++i;
+ }
+
+ return -1;
+}
+
+int CharacterFunctions::indexOfCharFast (const juce_wchar* const haystack, const juce_wchar needle) throw()
+{
+ jassert (haystack != 0);
+
+ int i = 0;
+ while (haystack[i] != 0)
+ {
+ if (haystack[i] == needle)
+ return i;
+
+ ++i;
+ }
+
+ return -1;
+}
+
+int CharacterFunctions::getIntialSectionContainingOnly (const char* const text, const char* const allowedChars) throw()
+{
+ return allowedChars == 0 ? 0 : (int) strspn (text, allowedChars);
+}
+
+int CharacterFunctions::getIntialSectionContainingOnly (const juce_wchar* const text, const juce_wchar* const allowedChars) throw()
+{
+ if (allowedChars == 0)
+ return 0;
+
+ int i = 0;
+
+ for (;;)
+ {
+ if (indexOfCharFast (allowedChars, text[i]) < 0)
+ break;
+
+ ++i;
+ }
+
+ return i;
+}
+
+int CharacterFunctions::ftime (char* const dest, const int maxChars, const char* const format, const struct tm* const tm) throw()
+{
+ return (int) strftime (dest, maxChars, format, tm);
+}
+
+int CharacterFunctions::ftime (juce_wchar* const dest, const int maxChars, const juce_wchar* const format, const struct tm* const tm) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ const String formatTemp (format);
+ size_t num = strftime ((char*) dest, maxChars, (const char*) formatTemp, tm);
+ String temp ((char*) dest);
+ temp.copyToBuffer (dest, num);
+ dest [num] = 0;
+ return (int) num;
+#else
+ return (int) wcsftime (dest, maxChars, format, tm);
+#endif
+}
+
+int CharacterFunctions::getIntValue (const char* const s) throw()
+{
+ return atoi (s);
+}
+
+int CharacterFunctions::getIntValue (const juce_wchar* s) throw()
+{
+#if JUCE_WIN32
+ return _wtoi (s);
+#else
+ int v = 0;
+
+ while (isWhitespace (*s))
+ ++s;
+
+ const bool isNeg = *s == T('-');
+ if (isNeg)
+ ++s;
+
+ for (;;)
+ {
+ const wchar_t c = *s++;
+
+ if (c >= T('0') && c <= T('9'))
+ v = v * 10 + (int) (c - T('0'));
+ else
+ break;
+ }
+
+ return isNeg ? -v : v;
+#endif
+}
+
+int64 CharacterFunctions::getInt64Value (const char* s) throw()
+{
+#if JUCE_LINUX
+ return atoll (s);
+#elif defined (JUCE_WIN32)
+ return _atoi64 (s);
+#else
+ int64 v = 0;
+
+ while (isWhitespace (*s))
+ ++s;
+
+ const bool isNeg = *s == T('-');
+ if (isNeg)
+ ++s;
+
+ for (;;)
+ {
+ const char c = *s++;
+
+ if (c >= '0' && c <= '9')
+ v = v * 10 + (int64) (c - '0');
+ else
+ break;
+ }
+
+ return isNeg ? -v : v;
+#endif
+}
+
+int64 CharacterFunctions::getInt64Value (const juce_wchar* s) throw()
+{
+#if JUCE_WIN32
+ return _wtoi64 (s);
+#else
+ int64 v = 0;
+
+ while (isWhitespace (*s))
+ ++s;
+
+ const bool isNeg = *s == T('-');
+ if (isNeg)
+ ++s;
+
+ for (;;)
+ {
+ const juce_wchar c = *s++;
+
+ if (c >= T('0') && c <= T('9'))
+ v = v * 10 + (int64) (c - T('0'));
+ else
+ break;
+ }
+
+ return isNeg ? -v : v;
+#endif
+}
+
+double CharacterFunctions::getDoubleValue (const char* const s) throw()
+{
+ return atof (s);
+}
+
+double CharacterFunctions::getDoubleValue (const juce_wchar* const s) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ String temp (s);
+ return atof ((const char*) temp);
+#else
+ wchar_t* endChar;
+ return wcstod (s, &endChar);
+#endif
+}
+
+char CharacterFunctions::toUpperCase (const char character) throw()
+{
+ return (char) toupper (character);
+}
+
+juce_wchar CharacterFunctions::toUpperCase (const juce_wchar character) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ return toupper ((char) character);
+#else
+ return towupper (character);
+#endif
+}
+
+void CharacterFunctions::toUpperCase (char* s) throw()
+{
+#if JUCE_WIN32
+ strupr (s);
+#else
+ while (*s != 0)
+ {
+ *s = toUpperCase (*s);
+ ++s;
+ }
+#endif
+}
+
+void CharacterFunctions::toUpperCase (juce_wchar* s) throw()
+{
+#if JUCE_WIN32
+ _wcsupr (s);
+#else
+ while (*s != 0)
+ {
+ *s = toUpperCase (*s);
+ ++s;
+ }
+#endif
+}
+
+bool CharacterFunctions::isUpperCase (const char character) throw()
+{
+ return isupper (character) != 0;
+}
+
+bool CharacterFunctions::isUpperCase (const juce_wchar character) throw()
+{
+#if JUCE_WIN32
+ return iswupper (character) != 0;
+#else
+ return toLowerCase (character) != character;
+#endif
+}
+
+char CharacterFunctions::toLowerCase (const char character) throw()
+{
+ return (char) tolower (character);
+}
+
+juce_wchar CharacterFunctions::toLowerCase (const juce_wchar character) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ return tolower ((char) character);
+#else
+ return towlower (character);
+#endif
+}
+
+void CharacterFunctions::toLowerCase (char* s) throw()
+{
+#if JUCE_WIN32
+ strlwr (s);
+#else
+ while (*s != 0)
+ {
+ *s = toLowerCase (*s);
+ ++s;
+ }
+#endif
+}
+
+void CharacterFunctions::toLowerCase (juce_wchar* s) throw()
+{
+#if JUCE_WIN32
+ _wcslwr (s);
+#else
+ while (*s != 0)
+ {
+ *s = toLowerCase (*s);
+ ++s;
+ }
+#endif
+}
+
+bool CharacterFunctions::isLowerCase (const char character) throw()
+{
+ return islower (character) != 0;
+}
+
+bool CharacterFunctions::isLowerCase (const juce_wchar character) throw()
+{
+#if JUCE_WIN32
+ return iswlower (character) != 0;
+#else
+ return toUpperCase (character) != character;
+#endif
+}
+
+bool CharacterFunctions::isWhitespace (const char character) throw()
+{
+ return character == T(' ') || (character <= 13 && character >= 9);
+}
+
+bool CharacterFunctions::isWhitespace (const juce_wchar character) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ return isWhitespace ((char) character);
+#else
+ return iswspace (character) != 0;
+#endif
+}
+
+bool CharacterFunctions::isDigit (const char character) throw()
+{
+ return (character >= '0' && character <= '9');
+}
+
+bool CharacterFunctions::isDigit (const juce_wchar character) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ return isdigit ((char) character) != 0;
+#else
+ return iswdigit (character) != 0;
+#endif
+}
+
+bool CharacterFunctions::isLetter (const char character) throw()
+{
+ return (character >= 'a' && character <= 'z')
+ || (character >= 'A' && character <= 'Z');
+}
+
+bool CharacterFunctions::isLetter (const juce_wchar character) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ return isLetter ((char) character);
+#else
+ return iswalpha (character) != 0;
+#endif
+}
+
+bool CharacterFunctions::isLetterOrDigit (const char character) throw()
+{
+ return (character >= 'a' && character <= 'z')
+ || (character >= 'A' && character <= 'Z')
+ || (character >= '0' && character <= '9');
+}
+
+bool CharacterFunctions::isLetterOrDigit (const juce_wchar character) throw()
+{
+#if MACOS_10_2_OR_EARLIER
+ return isLetterOrDigit ((char) character);
+#else
+ return iswalnum (character) != 0;
+#endif
+}
+
+int CharacterFunctions::getHexDigitValue (const tchar digit) throw()
+{
+ if (digit >= T('0') && digit <= T('9'))
+ return digit - T('0');
+ else if (digit >= T('a') && digit <= T('f'))
+ return digit - (T('a') - 10);
+ else if (digit >= T('A') && digit <= T('F'))
+ return digit - (T('A') - 10);
+
+ return -1;
+}
+
+int CharacterFunctions::printf (char* const dest, const int maxLength, const char* const format, ...) throw()
+{
+ va_list list;
+ va_start (list, format);
+ return vprintf (dest, maxLength, format, list);
+}
+
+int CharacterFunctions::printf (juce_wchar* const dest, const int maxLength, const juce_wchar* const format, ...) throw()
+{
+ va_list list;
+ va_start (list, format);
+ return vprintf (dest, maxLength, format, list);
+}
+
+int CharacterFunctions::vprintf (char* const dest, const int maxLength, const char* const format, va_list& args) throw()
+{
+#if JUCE_WIN32
+ return (int) _vsnprintf (dest, maxLength, format, args);
+#else
+ return (int) vsnprintf (dest, maxLength, format, args);
+#endif
+}
+
+int CharacterFunctions::vprintf (juce_wchar* const dest, const int maxLength, const juce_wchar* const format, va_list& args) throw()
+{
+#if MACOS_10_3_OR_EARLIER
+ const String formatTemp (format);
+ size_t num = vprintf ((char*) dest, maxLength, formatTemp, args);
+ String temp ((char*) dest);
+ temp.copyToBuffer (dest, num);
+ dest [num] = 0;
+ return (int) num;
+#elif defined (JUCE_WIN32)
+ return (int) _vsnwprintf (dest, maxLength, format, args);
+#else
+ return (int) vswprintf (dest, maxLength, format, args);
+#endif
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_CharacterFunctions.cpp *********/
+
+/********* Start of inlined file: juce_LocalisedStrings.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+LocalisedStrings::LocalisedStrings (const String& fileContents) throw()
+{
+ loadFromText (fileContents);
+}
+
+LocalisedStrings::LocalisedStrings (const File& fileToLoad) throw()
+{
+ loadFromText (fileToLoad.loadFileAsString());
+}
+
+LocalisedStrings::~LocalisedStrings() throw()
+{
+}
+
+const String LocalisedStrings::translate (const String& text) const throw()
+{
+ return translations.getValue (text, text);
+}
+
+static int findCloseQuote (const String& text, int startPos) throw()
+{
+ tchar lastChar = 0;
+
+ for (;;)
+ {
+ const tchar c = text [startPos];
+
+ if (c == 0 || (c == T('"') && lastChar != T('\\')))
+ break;
+
+ lastChar = c;
+ ++startPos;
+ }
+
+ return startPos;
+}
+
+static const String unescapeString (const String& s) throw()
+{
+ return s.replace (T("\\\""), T("\""))
+ .replace (T("\\\'"), T("\'"))
+ .replace (T("\\t"), T("\t"))
+ .replace (T("\\r"), T("\r"))
+ .replace (T("\\n"), T("\n"));
+}
+
+void LocalisedStrings::loadFromText (const String& fileContents) throw()
+{
+ StringArray lines;
+ lines.addLines (fileContents);
+
+ for (int i = 0; i < lines.size(); ++i)
+ {
+ String line (lines[i].trim());
+
+ if (line.startsWithChar (T('"')))
+ {
+ int closeQuote = findCloseQuote (line, 1);
+
+ const String originalText (unescapeString (line.substring (1, closeQuote)));
+
+ if (originalText.isNotEmpty())
+ {
+ const int openingQuote = findCloseQuote (line, closeQuote + 1);
+ closeQuote = findCloseQuote (line, openingQuote + 1);
+
+ const String newText (unescapeString (line.substring (openingQuote + 1, closeQuote)));
+
+ if (newText.isNotEmpty())
+ translations.set (originalText, newText);
+ }
+ }
+ else if (line.startsWithIgnoreCase (T("language:")))
+ {
+ languageName = line.substring (9).trim();
+ }
+ else if (line.startsWithIgnoreCase (T("countries:")))
+ {
+ countryCodes.addTokens (line.substring (10).trim(), true);
+ countryCodes.trim();
+ countryCodes.removeEmptyStrings();
+ }
+ }
+}
+
+static CriticalSection currentMappingsLock;
+static LocalisedStrings* currentMappings = 0;
+
+void LocalisedStrings::setCurrentMappings (LocalisedStrings* newTranslations) throw()
+{
+ const ScopedLock sl (currentMappingsLock);
+
+ delete currentMappings;
+ currentMappings = newTranslations;
+}
+
+LocalisedStrings* LocalisedStrings::getCurrentMappings() throw()
+{
+ return currentMappings;
+}
+
+const String LocalisedStrings::translateWithCurrentMappings (const String& text) throw()
+{
+ const ScopedLock sl (currentMappingsLock);
+
+ if (currentMappings != 0)
+ return currentMappings->translate (text);
+
+ return text;
+}
+
+const String LocalisedStrings::translateWithCurrentMappings (const char* text) throw()
+{
+ return translateWithCurrentMappings (String (text));
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_LocalisedStrings.cpp *********/
+
+/********* Start of inlined file: juce_String.cpp *********/
+#ifdef _MSC_VER
+ #pragma warning (disable: 4514)
+ #pragma warning (push)
+#endif
+#include
+
+#if JUCE_MSVC
+ #include
+#endif
+
+BEGIN_JUCE_NAMESPACE
+
+#ifdef _MSC_VER
+ #pragma warning (pop)
+#endif
+
+static const char* const emptyCharString = "\0\0\0\0JUCE";
+static const int safeEmptyStringRefCount = 0x3fffffff;
+String::InternalRefCountedStringHolder String::emptyString = { safeEmptyStringRefCount, 0, { 0 } };
+static tchar decimalPoint = T('.');
+
+void juce_initialiseStrings()
+{
+ decimalPoint = String::fromUTF8 ((const uint8*) localeconv()->decimal_point) [0];
+}
+
+void String::deleteInternal() throw()
+{
+ if (atomicDecrementAndReturn (text->refCount) == 0)
+ juce_free (text);
+}
+
+void String::createInternal (const int numChars) throw()
+{
+ jassert (numChars > 0);
+
+ text = (InternalRefCountedStringHolder*) juce_malloc (sizeof (InternalRefCountedStringHolder)
+ + numChars * sizeof (tchar));
+ text->refCount = 1;
+ text->allocatedNumChars = numChars;
+ text->text[0] = 0;
+}
+
+void String::createInternal (const tchar* const t, const tchar* const textEnd) throw()
+{
+ jassert (*(textEnd - 1) == 0); // must have a null terminator
+
+ const int numChars = (int) (textEnd - t);
+ createInternal (numChars - 1);
+ memcpy (text->text, t, numChars * sizeof (tchar));
+}
+
+void String::appendInternal (const tchar* const newText,
+ const int numExtraChars) throw()
+{
+ if (numExtraChars > 0)
+ {
+ const int oldLen = CharacterFunctions::length (text->text);
+ const int newTotalLen = oldLen + numExtraChars;
+
+ if (text->refCount > 1)
+ {
+ // it's in use by other strings as well, so we need to make a private copy before messing with it..
+ InternalRefCountedStringHolder* const newTextHolder
+ = (InternalRefCountedStringHolder*) juce_malloc (sizeof (InternalRefCountedStringHolder)
+ + newTotalLen * sizeof (tchar));
+ newTextHolder->refCount = 1;
+ newTextHolder->allocatedNumChars = newTotalLen;
+
+ memcpy (newTextHolder->text, text->text, oldLen * sizeof (tchar));
+ memcpy (newTextHolder->text + oldLen, newText, numExtraChars * sizeof (tchar));
+
+ InternalRefCountedStringHolder* const old = text;
+ text = newTextHolder;
+
+ if (atomicDecrementAndReturn (old->refCount) == 0)
+ juce_free (old);
+ }
+ else
+ {
+ // no other strings using it, so just expand it if needed..
+ if (newTotalLen > text->allocatedNumChars)
+ {
+ text = (InternalRefCountedStringHolder*)
+ juce_realloc (text, sizeof (InternalRefCountedStringHolder)
+ + newTotalLen * sizeof (tchar));
+
+ text->allocatedNumChars = newTotalLen;
+ }
+
+ memcpy (text->text + oldLen, newText, numExtraChars * sizeof (tchar));
+ }
+
+ text->text [newTotalLen] = 0;
+ }
+}
+
+void String::dupeInternalIfMultiplyReferenced() throw()
+{
+ if (text->refCount > 1)
+ {
+ InternalRefCountedStringHolder* const old = text;
+ const int len = old->allocatedNumChars;
+
+ InternalRefCountedStringHolder* const newTextHolder
+ = (InternalRefCountedStringHolder*) juce_malloc (sizeof (InternalRefCountedStringHolder)
+ + len * sizeof (tchar));
+
+ newTextHolder->refCount = 1;
+ newTextHolder->allocatedNumChars = len;
+
+ memcpy (newTextHolder->text, old->text, (len + 1) * sizeof (tchar));
+
+ text = newTextHolder;
+
+ if (atomicDecrementAndReturn (old->refCount) == 0)
+ juce_free (old);
+ }
+}
+
+const String String::empty;
+
+String::String() throw()
+ : text (&emptyString)
+{
+}
+
+String::String (const String& other) throw()
+ : text (other.text)
+{
+ atomicIncrement (text->refCount);
+}
+
+String::String (const int numChars,
+ const int /*dummyVariable*/) throw()
+{
+ createInternal (numChars);
+}
+
+String::String (const char* const t) throw()
+{
+ if (t != 0 && *t != 0)
+ {
+ const int len = CharacterFunctions::length (t);
+ createInternal (len);
+
+#if JUCE_STRINGS_ARE_UNICODE
+ CharacterFunctions::copy (text->text, t, len + 1);
+#else
+ memcpy (text->text, t, len + 1);
+#endif
+ }
+ else
+ {
+ text = &emptyString;
+ emptyString.refCount = safeEmptyStringRefCount;
+ }
+}
+
+String::String (const juce_wchar* const t) throw()
+{
+ if (t != 0 && *t != 0)
+ {
+ const int len = CharacterFunctions::length (t);
+ createInternal (len);
+
+#if JUCE_STRINGS_ARE_UNICODE
+ memcpy (text->text, t, (len + 1) * sizeof (tchar));
+#else
+ CharacterFunctions::copy (text->text, t, len + 1);
+#endif
+ }
+ else
+ {
+ text = &emptyString;
+ emptyString.refCount = safeEmptyStringRefCount;
+ }
+}
+
+String::String (const char* const t,
+ const int maxChars) throw()
+{
+ int i;
+ for (i = 0; i < maxChars; ++i)
+ if (t[i] == 0)
+ break;
+
+ if (i > 0)
+ {
+ createInternal (i);
+
+#if JUCE_STRINGS_ARE_UNICODE
+ CharacterFunctions::copy (text->text, t, i);
+#else
+ memcpy (text->text, t, i);
+#endif
+
+ text->text [i] = 0;
+ }
+ else
+ {
+ text = &emptyString;
+ emptyString.refCount = safeEmptyStringRefCount;
+ }
+}
+
+String::String (const juce_wchar* const t,
+ const int maxChars) throw()
+{
+ int i;
+ for (i = 0; i < maxChars; ++i)
+ if (t[i] == 0)
+ break;
+
+ if (i > 0)
+ {
+ createInternal (i);
+
+#if JUCE_STRINGS_ARE_UNICODE
+ memcpy (text->text, t, i * sizeof (tchar));
+#else
+ CharacterFunctions::copy (text->text, t, i);
+#endif
+ text->text [i] = 0;
+ }
+ else
+ {
+ text = &emptyString;
+ emptyString.refCount = safeEmptyStringRefCount;
+ }
+}
+
+const String String::charToString (const tchar character) throw()
+{
+ tchar temp[2];
+ temp[0] = character;
+ temp[1] = 0;
+
+ return String (temp);
+}
+
+// pass in a pointer to the END of a buffer..
+static tchar* int64ToCharString (tchar* t, const int64 n) throw()
+{
+ *--t = 0;
+ int64 v = (n >= 0) ? n : -n;
+
+ do
+ {
+ *--t = (tchar) (T('0') + (int) (v % 10));
+ v /= 10;
+
+ } while (v > 0);
+
+ if (n < 0)
+ *--t = T('-');
+
+ return t;
+}
+
+static tchar* intToCharString (tchar* t, const int n) throw()
+{
+ if (n == (int) 0x80000000) // (would cause an overflow)
+ return int64ToCharString (t, n);
+
+ *--t = 0;
+ int v = abs (n);
+
+ do
+ {
+ *--t = (tchar) (T('0') + (v % 10));
+ v /= 10;
+
+ } while (v > 0);
+
+ if (n < 0)
+ *--t = T('-');
+
+ return t;
+}
+
+static tchar* uintToCharString (tchar* t, unsigned int v) throw()
+{
+ *--t = 0;
+
+ do
+ {
+ *--t = (tchar) (T('0') + (v % 10));
+ v /= 10;
+
+ } while (v > 0);
+
+ return t;
+}
+
+String::String (const int number) throw()
+{
+ tchar buffer [16];
+ tchar* const end = buffer + 16;
+
+ createInternal (intToCharString (end, number), end);
+}
+
+String::String (const unsigned int number) throw()
+{
+ tchar buffer [16];
+ tchar* const end = buffer + 16;
+
+ createInternal (uintToCharString (end, number), end);
+}
+
+String::String (const short number) throw()
+{
+ tchar buffer [16];
+ tchar* const end = buffer + 16;
+
+ createInternal (intToCharString (end, (int) number), end);
+}
+
+String::String (const unsigned short number) throw()
+{
+ tchar buffer [16];
+ tchar* const end = buffer + 16;
+
+ createInternal (uintToCharString (end, (unsigned int) number), end);
+}
+
+String::String (const int64 number) throw()
+{
+ tchar buffer [32];
+ tchar* const end = buffer + 32;
+
+ createInternal (int64ToCharString (end, number), end);
+}
+
+String::String (const uint64 number) throw()
+{
+ tchar buffer [32];
+ tchar* const end = buffer + 32;
+ tchar* t = end;
+
+ *--t = 0;
+ int64 v = number;
+
+ do
+ {
+ *--t = (tchar) (T('0') + (int) (v % 10));
+ v /= 10;
+
+ } while (v > 0);
+
+ createInternal (t, end);
+}
+
+// a double-to-string routine that actually uses the number of dec. places you asked for
+// without resorting to exponent notation if the number's too big or small (which is what printf does).
+void String::doubleToStringWithDecPlaces (double n, int numDecPlaces) throw()
+{
+ const int bufSize = 80;
+ tchar buffer [bufSize];
+ int len;
+ tchar* t;
+
+ if (numDecPlaces > 0 && n > -1.0e20 && n < 1.0e20)
+ {
+ int64 v = (int64) (pow (10.0, numDecPlaces) * fabs (n) + 0.5);
+
+ t = buffer + bufSize;
+ *--t = (tchar) 0;
+
+ while (numDecPlaces >= 0 || v > 0)
+ {
+ if (numDecPlaces == 0)
+ *--t = decimalPoint;
+
+ *--t = (tchar) (T('0') + (v % 10));
+
+ v /= 10;
+ --numDecPlaces;
+ }
+
+ if (n < 0)
+ *--t = T('-');
+
+ len = (int) ((buffer + bufSize) - t);
+ }
+ else
+ {
+ len = CharacterFunctions::printf (buffer, bufSize, T("%.9g"), n) + 1;
+ t = buffer;
+ }
+
+ if (len > 1)
+ {
+ jassert (len < numElementsInArray (buffer));
+
+ createInternal (len - 1);
+ memcpy (text->text, t, len * sizeof (tchar));
+ }
+ else
+ {
+ jassert (*t == 0);
+ text = &emptyString;
+ emptyString.refCount = safeEmptyStringRefCount;
+ }
+}
+
+String::String (const float number,
+ const int numberOfDecimalPlaces) throw()
+{
+ doubleToStringWithDecPlaces ((double) number,
+ numberOfDecimalPlaces);
+}
+
+String::String (const double number,
+ const int numberOfDecimalPlaces) throw()
+{
+ doubleToStringWithDecPlaces (number,
+ numberOfDecimalPlaces);
+}
+
+String::~String() throw()
+{
+ if (atomicDecrementAndReturn (text->refCount) == 0)
+ juce_free (text);
+}
+
+void String::preallocateStorage (const int numChars) throw()
+{
+ if (numChars > text->allocatedNumChars)
+ {
+ dupeInternalIfMultiplyReferenced();
+
+ text = (InternalRefCountedStringHolder*) juce_realloc (text, sizeof (InternalRefCountedStringHolder)
+ + numChars * sizeof (tchar));
+ text->allocatedNumChars = numChars;
+ }
+}
+
+#if JUCE_STRINGS_ARE_UNICODE
+String::operator const char*() const throw()
+{
+ if (isEmpty())
+ {
+ return (const char*) emptyCharString;
+ }
+ else
+ {
+ String* const mutableThis = const_cast (this);
+
+ mutableThis->dupeInternalIfMultiplyReferenced();
+ int len = CharacterFunctions::length (text->text) + 1;
+ mutableThis->text = (InternalRefCountedStringHolder*)
+ juce_realloc (text, sizeof (InternalRefCountedStringHolder)
+ + (len * sizeof (juce_wchar) + len));
+ char* otherCopy = (char*) (text->text + len);
+ --len;
+
+ CharacterFunctions::copy (otherCopy, text->text, len);
+ otherCopy [len] = 0;
+ return otherCopy;
+ }
+}
+
+#else
+
+String::operator const juce_wchar*() const throw()
+{
+ if (isEmpty())
+ {
+ return (const juce_wchar*) emptyCharString;
+ }
+ else
+ {
+ String* const mutableThis = const_cast (this);
+
+ mutableThis->dupeInternalIfMultiplyReferenced();
+ int len = CharacterFunctions::length (text->text) + 1;
+ mutableThis->text = (InternalRefCountedStringHolder*)
+ juce_realloc (text, sizeof (InternalRefCountedStringHolder)
+ + (len * sizeof (juce_wchar) + len));
+
+ juce_wchar* otherCopy = (juce_wchar*) (text->text + len);
+ --len;
+
+ CharacterFunctions::copy (otherCopy, text->text, len);
+ otherCopy [len] = 0;
+ return otherCopy;
+ }
+}
+
+#endif
+
+void String::copyToBuffer (char* const destBuffer,
+ const int maxCharsToCopy) const throw()
+{
+ const int len = jmin (maxCharsToCopy, length());
+
+#if JUCE_STRINGS_ARE_UNICODE
+ CharacterFunctions::copy (destBuffer, text->text, len);
+#else
+ memcpy (destBuffer, text->text, len * sizeof (tchar));
+#endif
+
+ destBuffer [len] = 0;
+}
+
+void String::copyToBuffer (juce_wchar* const destBuffer,
+ const int maxCharsToCopy) const throw()
+{
+ const int len = jmin (maxCharsToCopy, length());
+
+#if JUCE_STRINGS_ARE_UNICODE
+ memcpy (destBuffer, text->text, len * sizeof (juce_wchar));
+#else
+ CharacterFunctions::copy (destBuffer, text->text, len);
+#endif
+
+ destBuffer [len] = 0;
+}
+
+int String::length() const throw()
+{
+ return CharacterFunctions::length (text->text);
+}
+
+int String::hashCode() const throw()
+{
+ const tchar* t = text->text;
+ int result = 0;
+
+ while (*t != (tchar) 0)
+ result = 31 * result + *t++;
+
+ return result;
+}
+
+int64 String::hashCode64() const throw()
+{
+ const tchar* t = text->text;
+ int64 result = 0;
+
+ while (*t != (tchar) 0)
+ result = 101 * result + *t++;
+
+ return result;
+}
+
+const String& String::operator= (const tchar* const otherText) throw()
+{
+ if (otherText != 0 && *otherText != 0)
+ {
+ const int otherLen = CharacterFunctions::length (otherText);
+
+ if (otherLen > 0)
+ {
+ // avoid resizing the memory block if the string is
+ // shrinking..
+ if (text->refCount > 1
+ || otherLen > text->allocatedNumChars
+ || otherLen <= (text->allocatedNumChars >> 1))
+ {
+ deleteInternal();
+ createInternal (otherLen);
+ }
+
+ memcpy (text->text, otherText, (otherLen + 1) * sizeof (tchar));
+
+ return *this;
+ }
+ }
+
+ deleteInternal();
+ text = &emptyString;
+ emptyString.refCount = safeEmptyStringRefCount;
+
+ return *this;
+}
+
+const String& String::operator= (const String& other) throw()
+{
+ if (this != &other)
+ {
+ atomicIncrement (other.text->refCount);
+
+ if (atomicDecrementAndReturn (text->refCount) == 0)
+ juce_free (text);
+
+ text = other.text;
+ }
+
+ return *this;
+}
+
+bool String::operator== (const String& other) const throw()
+{
+ return text == other.text
+ || CharacterFunctions::compare (text->text, other.text->text) == 0;
+}
+
+bool String::operator== (const tchar* const t) const throw()
+{
+ return t != 0 ? CharacterFunctions::compare (text->text, t) == 0
+ : isEmpty();
+}
+
+bool String::equalsIgnoreCase (const tchar* t) const throw()
+{
+ return t != 0 ? CharacterFunctions::compareIgnoreCase (text->text, t) == 0
+ : isEmpty();
+}
+
+bool String::equalsIgnoreCase (const String& other) const throw()
+{
+ return text == other.text
+ || CharacterFunctions::compareIgnoreCase (text->text, other.text->text) == 0;
+}
+
+bool String::operator!= (const String& other) const throw()
+{
+ return text != other.text
+ && CharacterFunctions::compare (text->text, other.text->text) != 0;
+}
+
+bool String::operator!= (const tchar* const t) const throw()
+{
+ return t != 0 ? (CharacterFunctions::compare (text->text, t) != 0)
+ : isNotEmpty();
+}
+
+bool String::operator> (const String& other) const throw()
+{
+ return compare (other) > 0;
+}
+
+bool String::operator< (const tchar* const other) const throw()
+{
+ return compare (other) < 0;
+}
+
+bool String::operator>= (const String& other) const throw()
+{
+ return compare (other) >= 0;
+}
+
+bool String::operator<= (const tchar* const other) const throw()
+{
+ return compare (other) <= 0;
+}
+
+int String::compare (const tchar* const other) const throw()
+{
+ return other != 0 ? CharacterFunctions::compare (text->text, other)
+ : isEmpty();
+}
+
+int String::compareIgnoreCase (const tchar* const other) const throw()
+{
+ return other != 0 ? CharacterFunctions::compareIgnoreCase (text->text, other)
+ : isEmpty();
+}
+
+int String::compareLexicographically (const tchar* other) const throw()
+{
+ if (other == 0)
+ return isEmpty();
+
+ const tchar* s1 = text->text;
+ while (*s1 != 0 && ! CharacterFunctions::isLetterOrDigit (*s1))
+ ++s1;
+
+ while (*other != 0 && ! CharacterFunctions::isLetterOrDigit (*other))
+ ++other;
+
+ return CharacterFunctions::compareIgnoreCase (s1, other);
+}
+
+const String String::operator+ (const String& other) const throw()
+{
+ if (*(other.text->text) == 0)
+ return *this;
+
+ if (isEmpty())
+ return other;
+
+ const int len = CharacterFunctions::length (text->text);
+ const int otherLen = CharacterFunctions::length (other.text->text);
+
+ String result (len + otherLen, (int) 0);
+ memcpy (result.text->text, text->text, len * sizeof (tchar));
+ memcpy (result.text->text + len, other.text->text, otherLen * sizeof (tchar));
+ result.text->text [len + otherLen] = 0;
+
+ return result;
+}
+
+const String String::operator+ (const tchar* const textToAppend) const throw()
+{
+ if (textToAppend == 0 || *textToAppend == 0)
+ return *this;
+
+ const int len = CharacterFunctions::length (text->text);
+ const int otherLen = CharacterFunctions::length (textToAppend);
+
+ String result (len + otherLen, (int) 0);
+ memcpy (result.text->text, text->text, len * sizeof (tchar));
+ memcpy (result.text->text + len, textToAppend, otherLen * sizeof (tchar));
+ result.text->text [len + otherLen] = 0;
+
+ return result;
+}
+
+const String String::operator+ (const tchar characterToAppend) const throw()
+{
+ if (characterToAppend == 0)
+ return *this;
+
+ const int len = CharacterFunctions::length (text->text);
+ String result ((int) (len + 1), (int) 0);
+
+ memcpy (result.text->text, text->text, len * sizeof (tchar));
+ result.text->text[len] = characterToAppend;
+ result.text->text[len + 1] = 0;
+
+ return result;
+}
+
+const String JUCE_PUBLIC_FUNCTION operator+ (const char* const string1,
+ const String& string2) throw()
+{
+ String s (string1);
+ s += string2;
+ return s;
+}
+
+const String JUCE_PUBLIC_FUNCTION operator+ (const juce_wchar* const string1,
+ const String& string2) throw()
+{
+ String s (string1);
+ s += string2;
+ return s;
+}
+
+const String& String::operator+= (const tchar* const t) throw()
+{
+ if (t != 0)
+ appendInternal (t, CharacterFunctions::length (t));
+
+ return *this;
+}
+
+const String& String::operator+= (const String& other) throw()
+{
+ if (isEmpty())
+ operator= (other);
+ else
+ appendInternal (other.text->text,
+ CharacterFunctions::length (other.text->text));
+
+ return *this;
+}
+
+const String& String::operator+= (const char ch) throw()
+{
+ char asString[2];
+ asString[0] = ch;
+ asString[1] = 0;
+
+#if JUCE_STRINGS_ARE_UNICODE
+ operator+= (String (asString));
+#else
+ appendInternal (asString, 1);
+#endif
+
+ return *this;
+}
+
+const String& String::operator+= (const juce_wchar ch) throw()
+{
+ juce_wchar asString[2];
+ asString[0] = ch;
+ asString[1] = 0;
+
+#if JUCE_STRINGS_ARE_UNICODE
+ appendInternal (asString, 1);
+#else
+ operator+= (String (asString));
+#endif
+
+ return *this;
+}
+
+void String::append (const tchar* const other,
+ const int howMany) throw()
+{
+ if (howMany > 0)
+ {
+ int i;
+ for (i = 0; i < howMany; ++i)
+ if (other[i] == 0)
+ break;
+
+ appendInternal (other, i);
+ }
+}
+
+String& String::operator<< (const int number) throw()
+{
+ tchar buffer [64];
+ tchar* const end = buffer + 64;
+ const tchar* const t = intToCharString (end, number);
+ appendInternal (t, (int) (end - t) - 1);
+
+ return *this;
+}
+
+String& String::operator<< (const unsigned int number) throw()
+{
+ tchar buffer [64];
+ tchar* const end = buffer + 64;
+ const tchar* const t = uintToCharString (end, number);
+ appendInternal (t, (int) (end - t) - 1);
+
+ return *this;
+}
+
+String& String::operator<< (const short number) throw()
+{
+ tchar buffer [64];
+ tchar* const end = buffer + 64;
+ const tchar* const t = intToCharString (end, (int) number);
+ appendInternal (t, (int) (end - t) - 1);
+
+ return *this;
+}
+
+String& String::operator<< (const double number) throw()
+{
+ operator+= (String (number));
+ return *this;
+}
+
+String& String::operator<< (const float number) throw()
+{
+ operator+= (String (number));
+ return *this;
+}
+
+String& String::operator<< (const char character) throw()
+{
+ operator+= (character);
+ return *this;
+}
+
+String& String::operator<< (const juce_wchar character) throw()
+{
+ operator+= (character);
+ return *this;
+}
+
+String& String::operator<< (const char* const t) throw()
+{
+#if JUCE_STRINGS_ARE_UNICODE
+ operator+= (String (t));
+#else
+ operator+= (t);
+#endif
+ return *this;
+}
+
+String& String::operator<< (const juce_wchar* const t) throw()
+{
+#if JUCE_STRINGS_ARE_UNICODE
+ operator+= (t);
+#else
+ operator+= (String (t));
+#endif
+ return *this;
+}
+
+String& String::operator<< (const String& t) throw()
+{
+ operator+= (t);
+ return *this;
+}
+
+int String::indexOfChar (const tchar character) const throw()
+{
+ const tchar* t = text->text;
+
+ for (;;)
+ {
+ if (*t == character)
+ return (int) (t - text->text);
+
+ if (*t++ == 0)
+ return -1;
+ }
+}
+
+int String::lastIndexOfChar (const tchar character) const throw()
+{
+ for (int i = CharacterFunctions::length (text->text); --i >= 0;)
+ if (text->text[i] == character)
+ return i;
+
+ return -1;
+}
+
+int String::indexOf (const tchar* const t) const throw()
+{
+ const tchar* const r = CharacterFunctions::find (text->text, t);
+ return (r == 0) ? -1
+ : (int) (r - text->text);
+}
+
+int String::indexOfChar (const int startIndex,
+ const tchar character) const throw()
+{
+ if (startIndex >= 0 && startIndex >= CharacterFunctions::length (text->text))
+ return -1;
+
+ const tchar* t = text->text + jmax (0, startIndex);
+
+ for (;;)
+ {
+ if (*t == character)
+ return (int) (t - text->text);
+
+ if (*t++ == 0)
+ return -1;
+ }
+}
+
+int String::indexOfAnyOf (const tchar* const charactersToLookFor,
+ const int startIndex,
+ const bool ignoreCase) const throw()
+{
+ if (charactersToLookFor == 0
+ || (startIndex >= 0 && startIndex >= CharacterFunctions::length (text->text)))
+ return -1;
+
+ const tchar* t = text->text + jmax (0, startIndex);
+
+ while (*t != 0)
+ {
+ if (CharacterFunctions::indexOfChar (charactersToLookFor, *t, ignoreCase) >= 0)
+ return (int) (t - text->text);
+
+ ++t;
+ }
+
+ return -1;
+}
+
+int String::indexOf (const int startIndex,
+ const tchar* const other) const throw()
+{
+ if (other == 0 || startIndex >= CharacterFunctions::length (text->text))
+ return -1;
+
+ const tchar* const found = CharacterFunctions::find (text->text + jmax (0, startIndex),
+ other);
+
+ return (found == 0) ? -1
+ : (int) (found - text->text);
+}
+
+int String::indexOfIgnoreCase (const tchar* const other) const throw()
+{
+ if (other != 0 && *other != 0)
+ {
+ const int len = CharacterFunctions::length (other);
+ const int end = CharacterFunctions::length (text->text) - len;
+
+ for (int i = 0; i <= end; ++i)
+ if (CharacterFunctions::compareIgnoreCase (text->text + i, other, len) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+int String::indexOfIgnoreCase (const int startIndex,
+ const tchar* const other) const throw()
+{
+ if (other != 0 && *other != 0)
+ {
+ const int len = CharacterFunctions::length (other);
+ const int end = length() - len;
+
+ for (int i = jmax (0, startIndex); i <= end; ++i)
+ if (CharacterFunctions::compareIgnoreCase (text->text + i, other, len) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+int String::lastIndexOf (const tchar* const other) const throw()
+{
+ if (other != 0 && *other != 0)
+ {
+ const int len = CharacterFunctions::length (other);
+ int i = length() - len;
+
+ if (i >= 0)
+ {
+ const tchar* n = text->text + i;
+
+ while (i >= 0)
+ {
+ if (CharacterFunctions::compare (n--, other, len) == 0)
+ return i;
+
+ --i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int String::lastIndexOfIgnoreCase (const tchar* const other) const throw()
+{
+ if (other != 0 && *other != 0)
+ {
+ const int len = CharacterFunctions::length (other);
+ int i = length() - len;
+
+ if (i >= 0)
+ {
+ const tchar* n = text->text + i;
+
+ while (i >= 0)
+ {
+ if (CharacterFunctions::compareIgnoreCase (n--, other, len) == 0)
+ return i;
+
+ --i;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int String::lastIndexOfAnyOf (const tchar* const charactersToLookFor,
+ const bool ignoreCase) const throw()
+{
+ for (int i = CharacterFunctions::length (text->text); --i >= 0;)
+ if (CharacterFunctions::indexOfChar (charactersToLookFor, text->text [i], ignoreCase) >= 0)
+ return i;
+
+ return -1;
+}
+
+bool String::contains (const tchar* const other) const throw()
+{
+ return indexOf (other) >= 0;
+}
+
+bool String::containsChar (const tchar character) const throw()
+{
+ return indexOfChar (character) >= 0;
+}
+
+bool String::containsIgnoreCase (const tchar* const t) const throw()
+{
+ return indexOfIgnoreCase (t) >= 0;
+}
+
+int String::indexOfWholeWord (const tchar* const word) const throw()
+{
+ if (word != 0 && *word != 0)
+ {
+ const int wordLen = CharacterFunctions::length (word);
+ const int end = length() - wordLen;
+ const tchar* t = text->text;
+
+ for (int i = 0; i <= end; ++i)
+ {
+ if (CharacterFunctions::compare (t, word, wordLen) == 0
+ && (i == 0 || ! CharacterFunctions::isLetterOrDigit (* (t - 1)))
+ && ! CharacterFunctions::isLetterOrDigit (t [wordLen]))
+ {
+ return i;
+ }
+
+ ++t;
+ }
+ }
+
+ return -1;
+}
+
+int String::indexOfWholeWordIgnoreCase (const tchar* const word) const throw()
+{
+ if (word != 0 && *word != 0)
+ {
+ const int wordLen = CharacterFunctions::length (word);
+ const int end = length() - wordLen;
+ const tchar* t = text->text;
+
+ for (int i = 0; i <= end; ++i)
+ {
+ if (CharacterFunctions::compareIgnoreCase (t, word, wordLen) == 0
+ && (i == 0 || ! CharacterFunctions::isLetterOrDigit (* (t - 1)))
+ && ! CharacterFunctions::isLetterOrDigit (t [wordLen]))
+ {
+ return i;
+ }
+
+ ++t;
+ }
+ }
+
+ return -1;
+}
+
+bool String::containsWholeWord (const tchar* const wordToLookFor) const throw()
+{
+ return indexOfWholeWord (wordToLookFor) >= 0;
+}
+
+bool String::containsWholeWordIgnoreCase (const tchar* const wordToLookFor) const throw()
+{
+ return indexOfWholeWordIgnoreCase (wordToLookFor) >= 0;
+}
+
+static int indexOfMatch (const tchar* const wildcard,
+ const tchar* const test,
+ const bool ignoreCase) throw()
+{
+ int start = 0;
+
+ while (test [start] != 0)
+ {
+ int i = 0;
+
+ for (;;)
+ {
+ const tchar wc = wildcard [i];
+ const tchar c = test [i + start];
+
+ if (wc == c
+ || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (c))
+ || (wc == T('?') && c != 0))
+ {
+ if (wc == 0)
+ return start;
+
+ ++i;
+ }
+ else
+ {
+ if (wc == T('*') && (wildcard [i + 1] == 0
+ || indexOfMatch (wildcard + i + 1,
+ test + start + i,
+ ignoreCase) >= 0))
+ {
+ return start;
+ }
+
+ break;
+ }
+ }
+
+ ++start;
+ }
+
+ return -1;
+}
+
+bool String::matchesWildcard (const tchar* wildcard, const bool ignoreCase) const throw()
+{
+ int i = 0;
+
+ for (;;)
+ {
+ const tchar wc = wildcard [i];
+ const tchar c = text->text [i];
+
+ if (wc == c
+ || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (c))
+ || (wc == T('?') && c != 0))
+ {
+ if (wc == 0)
+ return true;
+
+ ++i;
+ }
+ else
+ {
+ return wc == T('*') && (wildcard [i + 1] == 0
+ || indexOfMatch (wildcard + i + 1,
+ text->text + i,
+ ignoreCase) >= 0);
+ }
+ }
+}
+
+void String::printf (const tchar* const pf, ...) throw()
+{
+ va_list list;
+ va_start (list, pf);
+
+ vprintf (pf, list);
+}
+
+const String String::formatted (const tchar* const pf, ...) throw()
+{
+ va_list list;
+ va_start (list, pf);
+
+ String result;
+ result.vprintf (pf, list);
+ return result;
+}
+
+void String::vprintf (const tchar* const pf, va_list& args) throw()
+{
+ tchar stackBuf [256];
+ unsigned int bufSize = 256;
+ tchar* buf = stackBuf;
+
+ deleteInternal();
+
+ do
+ {
+#if JUCE_LINUX && JUCE_64BIT
+ va_list tempArgs;
+ va_copy (tempArgs, args);
+ const int num = CharacterFunctions::vprintf (buf, bufSize - 1, pf, tempArgs);
+ va_end (tempArgs);
+#else
+ const int num = CharacterFunctions::vprintf (buf, bufSize - 1, pf, args);
+#endif
+
+ if (num > 0)
+ {
+ createInternal (num);
+ memcpy (text->text, buf, (num + 1) * sizeof (tchar));
+ break;
+ }
+ else if (num == 0)
+ {
+ text = &emptyString;
+ emptyString.refCount = safeEmptyStringRefCount;
+ break;
+ }
+
+ if (buf != stackBuf)
+ juce_free (buf);
+
+ bufSize += 256;
+ buf = (tchar*) juce_malloc (bufSize * sizeof (tchar));
+ }
+ while (bufSize < 65536); // this is a sanity check to avoid situations where vprintf repeatedly
+ // returns -1 because of an error rather than because it needs more space.
+
+ if (buf != stackBuf)
+ juce_free (buf);
+}
+
+const String String::repeatedString (const tchar* const stringToRepeat,
+ int numberOfTimesToRepeat) throw()
+{
+ const int len = CharacterFunctions::length (stringToRepeat);
+ String result ((int) (len * numberOfTimesToRepeat + 1), (int) 0);
+
+ tchar* n = result.text->text;
+ n[0] = 0;
+
+ while (--numberOfTimesToRepeat >= 0)
+ {
+ CharacterFunctions::append (n, stringToRepeat);
+ n += len;
+ }
+
+ return result;
+}
+
+const String String::replaceSection (int index,
+ int numCharsToReplace,
+ const tchar* const stringToInsert) const throw()
+{
+ if (index < 0)
+ {
+ // a negative index to replace from?
+ jassertfalse
+ index = 0;
+ }
+
+ if (numCharsToReplace < 0)
+ {
+ // replacing a negative number of characters?
+ numCharsToReplace = 0;
+ jassertfalse;
+ }
+
+ const int len = length();
+
+ if (index + numCharsToReplace > len)
+ {
+ if (index > len)
+ {
+ // replacing beyond the end of the string?
+ index = len;
+ jassertfalse
+ }
+
+ numCharsToReplace = len - index;
+ }
+
+ const int newStringLen = (stringToInsert != 0) ? CharacterFunctions::length (stringToInsert) : 0;
+ const int newTotalLen = len + newStringLen - numCharsToReplace;
+
+ String result (newTotalLen, (int) 0);
+
+ memcpy (result.text->text,
+ text->text,
+ index * sizeof (tchar));
+
+ if (newStringLen > 0)
+ memcpy (result.text->text + index,
+ stringToInsert,
+ newStringLen * sizeof (tchar));
+
+ const int endStringLen = newTotalLen - (index + newStringLen);
+
+ if (endStringLen > 0)
+ memcpy (result.text->text + (index + newStringLen),
+ text->text + (index + numCharsToReplace),
+ endStringLen * sizeof (tchar));
+
+ result.text->text [newTotalLen] = 0;
+
+ return result;
+}
+
+const String String::replace (const tchar* const stringToReplace,
+ const tchar* const stringToInsert,
+ const bool ignoreCase) const throw()
+{
+ const int stringToReplaceLen = CharacterFunctions::length (stringToReplace);
+ const int stringToInsertLen = CharacterFunctions::length (stringToInsert);
+
+ int i = 0;
+ String result (*this);
+
+ while ((i = (ignoreCase ? result.indexOfIgnoreCase (i, stringToReplace)
+ : result.indexOf (i, stringToReplace))) >= 0)
+ {
+ result = result.replaceSection (i, stringToReplaceLen, stringToInsert);
+ i += stringToInsertLen;
+ }
+
+ return result;
+}
+
+const String String::replaceCharacter (const tchar charToReplace,
+ const tchar charToInsert) const throw()
+{
+ const int index = indexOfChar (charToReplace);
+
+ if (index < 0)
+ return *this;
+
+ String result (*this);
+ result.dupeInternalIfMultiplyReferenced();
+
+ tchar* t = result.text->text + index;
+
+ while (*t != 0)
+ {
+ if (*t == charToReplace)
+ *t = charToInsert;
+
+ ++t;
+ }
+
+ return result;
+}
+
+const String String::replaceCharacters (const String& charactersToReplace,
+ const tchar* const charactersToInsertInstead) const throw()
+{
+ String result (*this);
+ result.dupeInternalIfMultiplyReferenced();
+
+ tchar* t = result.text->text;
+ const int len2 = CharacterFunctions::length (charactersToInsertInstead);
+
+ // the two strings passed in are supposed to be the same length!
+ jassert (len2 == charactersToReplace.length());
+
+ while (*t != 0)
+ {
+ const int index = charactersToReplace.indexOfChar (*t);
+
+ if (((unsigned int) index) < (unsigned int) len2)
+ *t = charactersToInsertInstead [index];
+
+ ++t;
+ }
+
+ return result;
+}
+
+bool String::startsWith (const tchar* const other) const throw()
+{
+ return other != 0
+ && CharacterFunctions::compare (text->text, other, CharacterFunctions::length (other)) == 0;
+}
+
+bool String::startsWithIgnoreCase (const tchar* const other) const throw()
+{
+ return other != 0
+ && CharacterFunctions::compareIgnoreCase (text->text, other, CharacterFunctions::length (other)) == 0;
+}
+
+bool String::startsWithChar (const tchar character) const throw()
+{
+ return text->text[0] == character;
+}
+
+bool String::endsWithChar (const tchar character) const throw()
+{
+ return text->text[0] != 0
+ && text->text [length() - 1] == character;
+}
+
+bool String::endsWith (const tchar* const other) const throw()
+{
+ if (other == 0)
+ return false;
+
+ const int thisLen = length();
+ const int otherLen = CharacterFunctions::length (other);
+
+ return thisLen >= otherLen
+ && CharacterFunctions::compare (text->text + thisLen - otherLen, other) == 0;
+}
+
+bool String::endsWithIgnoreCase (const tchar* const other) const throw()
+{
+ if (other == 0)
+ return false;
+
+ const int thisLen = length();
+ const int otherLen = CharacterFunctions::length (other);
+
+ return thisLen >= otherLen
+ && CharacterFunctions::compareIgnoreCase (text->text + thisLen - otherLen, other) == 0;
+}
+
+const String String::toUpperCase() const throw()
+{
+ String result (*this);
+ result.dupeInternalIfMultiplyReferenced();
+ CharacterFunctions::toUpperCase (result.text->text);
+ return result;
+}
+
+const String String::toLowerCase() const throw()
+{
+ String result (*this);
+ result.dupeInternalIfMultiplyReferenced();
+ CharacterFunctions::toLowerCase (result.text->text);
+ return result;
+}
+
+tchar& String::operator[] (const int index) throw()
+{
+ jassert (((unsigned int) index) <= (unsigned int) length());
+
+ dupeInternalIfMultiplyReferenced();
+
+ return text->text [index];
+}
+
+tchar String::getLastCharacter() const throw()
+{
+ return (isEmpty()) ? ((tchar) 0)
+ : text->text [CharacterFunctions::length (text->text) - 1];
+}
+
+const String String::substring (int start, int end) const throw()
+{
+ if (start < 0)
+ start = 0;
+ else if (end <= start)
+ return empty;
+
+ int len = 0;
+ const tchar* const t = text->text;
+
+ while (len <= end && t [len] != 0)
+ ++len;
+
+ if (end >= len)
+ {
+ if (start == 0)
+ return *this;
+
+ end = len;
+ }
+
+ return String (text->text + start,
+ end - start);
+}
+
+const String String::substring (const int start) const throw()
+{
+ if (start <= 0)
+ return *this;
+
+ const int len = CharacterFunctions::length (text->text);
+
+ if (start >= len)
+ return empty;
+ else
+ return String (text->text + start,
+ len - start);
+}
+
+const String String::dropLastCharacters (const int numberToDrop) const throw()
+{
+ return String (text->text,
+ jmax (0, CharacterFunctions::length (text->text) - numberToDrop));
+}
+
+const String String::fromFirstOccurrenceOf (const tchar* const sub,
+ const bool includeSubString,
+ const bool ignoreCase) const throw()
+{
+ const int i = ignoreCase ? indexOf (sub)
+ : indexOfIgnoreCase (sub);
+
+ if (i < 0)
+ return empty;
+ else
+ return substring ((includeSubString) ? i : i + CharacterFunctions::length (sub));
+}
+
+const String String::fromLastOccurrenceOf (const tchar* const sub,
+ const bool includeSubString,
+ const bool ignoreCase) const throw()
+{
+ const int i = ignoreCase ? lastIndexOf (sub)
+ : lastIndexOfIgnoreCase (sub);
+
+ if (i < 0)
+ return *this;
+ else
+ return substring ((includeSubString) ? i : i + CharacterFunctions::length (sub));
+}
+
+const String String::upToFirstOccurrenceOf (const tchar* const sub,
+ const bool includeSubString,
+ const bool ignoreCase) const throw()
+{
+ const int i = ignoreCase ? indexOfIgnoreCase (sub)
+ : indexOf (sub);
+
+ if (i < 0)
+ return *this;
+ else
+ return substring (0, (includeSubString) ? i + CharacterFunctions::length (sub) : i);
+}
+
+const String String::upToLastOccurrenceOf (const tchar* const sub,
+ const bool includeSubString,
+ const bool ignoreCase) const throw()
+{
+ const int i = ignoreCase ? lastIndexOfIgnoreCase (sub)
+ : lastIndexOf (sub);
+ if (i < 0)
+ return *this;
+
+ return substring (0, (includeSubString) ? i + CharacterFunctions::length (sub) : i);
+}
+
+bool String::isQuotedString() const throw()
+{
+ const String trimmed (trimStart());
+
+ return trimmed[0] == T('"')
+ || trimmed[0] == T('\'');
+}
+
+const String String::unquoted() const throw()
+{
+ String s (*this);
+
+ if (s[0] == T('"') || s[0] == T('\''))
+ s = s.substring (1);
+
+ const int lastCharIndex = s.length() - 1;
+
+ if (lastCharIndex >= 0
+ && (s [lastCharIndex] == T('"') || s[lastCharIndex] == T('\'')))
+ s [lastCharIndex] = 0;
+
+ return s;
+}
+
+const String String::quoted (const tchar quoteCharacter) const throw()
+{
+ if (isEmpty())
+ return charToString (quoteCharacter) + quoteCharacter;
+
+ String t (*this);
+
+ if (! t.startsWithChar (quoteCharacter))
+ t = charToString (quoteCharacter) + t;
+
+ if (! t.endsWithChar (quoteCharacter))
+ t += quoteCharacter;
+
+ return t;
+}
+
+const String String::trim() const throw()
+{
+ if (isEmpty())
+ return empty;
+
+ int start = 0;
+
+ while (CharacterFunctions::isWhitespace (text->text [start]))
+ ++start;
+
+ const int len = CharacterFunctions::length (text->text);
+ int end = len - 1;
+
+ while ((end >= start) && CharacterFunctions::isWhitespace (text->text [end]))
+ --end;
+
+ ++end;
+
+ if (end <= start)
+ return empty;
+ else if (start > 0 || end < len)
+ return String (text->text + start, end - start);
+ else
+ return *this;
+}
+
+const String String::trimStart() const throw()
+{
+ if (isEmpty())
+ return empty;
+
+ const tchar* t = text->text;
+
+ while (CharacterFunctions::isWhitespace (*t))
+ ++t;
+
+ if (t == text->text)
+ return *this;
+ else
+ return String (t);
+}
+
+const String String::trimEnd() const throw()
+{
+ if (isEmpty())
+ return empty;
+
+ const tchar* endT = text->text + (CharacterFunctions::length (text->text) - 1);
+
+ while ((endT >= text->text) && CharacterFunctions::isWhitespace (*endT))
+ --endT;
+
+ return String (text->text, (int) (++endT - text->text));
+}
+
+const String String::retainCharacters (const tchar* const charactersToRetain) const throw()
+{
+ jassert (charactersToRetain != 0);
+
+ if (isEmpty())
+ return empty;
+
+ String result (text->allocatedNumChars, (int) 0);
+ tchar* dst = result.text->text;
+ const tchar* src = text->text;
+
+ while (*src != 0)
+ {
+ if (CharacterFunctions::indexOfCharFast (charactersToRetain, *src) >= 0)
+ *dst++ = *src;
+
+ ++src;
+ }
+
+ *dst = 0;
+ return result;
+}
+
+const String String::removeCharacters (const tchar* const charactersToRemove) const throw()
+{
+ jassert (charactersToRemove != 0);
+
+ if (isEmpty())
+ return empty;
+
+ String result (text->allocatedNumChars, (int) 0);
+ tchar* dst = result.text->text;
+ const tchar* src = text->text;
+
+ while (*src != 0)
+ {
+ if (CharacterFunctions::indexOfCharFast (charactersToRemove, *src) < 0)
+ *dst++ = *src;
+
+ ++src;
+ }
+
+ *dst = 0;
+ return result;
+}
+
+const String String::initialSectionContainingOnly (const tchar* const permittedCharacters) const throw()
+{
+ return substring (0, CharacterFunctions::getIntialSectionContainingOnly (text->text, permittedCharacters));
+}
+
+const String String::initialSectionNotContaining (const tchar* const charactersToStopAt) const throw()
+{
+ jassert (charactersToStopAt != 0);
+
+ const tchar* const t = text->text;
+ int i = 0;
+
+ while (t[i] != 0)
+ {
+ if (CharacterFunctions::indexOfCharFast (charactersToStopAt, t[i]) >= 0)
+ return String (text->text, i);
+
+ ++i;
+ }
+
+ return empty;
+}
+
+bool String::containsOnly (const tchar* const chars) const throw()
+{
+ jassert (chars != 0);
+
+ const tchar* t = text->text;
+
+ while (*t != 0)
+ if (CharacterFunctions::indexOfCharFast (chars, *t++) < 0)
+ return false;
+
+ return true;
+}
+
+bool String::containsAnyOf (const tchar* const chars) const throw()
+{
+ jassert (chars != 0);
+
+ const tchar* t = text->text;
+
+ while (*t != 0)
+ if (CharacterFunctions::indexOfCharFast (chars, *t++) >= 0)
+ return true;
+
+ return false;
+}
+
+int String::getIntValue() const throw()
+{
+ return CharacterFunctions::getIntValue (text->text);
+}
+
+int String::getTrailingIntValue() const throw()
+{
+ int n = 0;
+ int mult = 1;
+ const tchar* t = text->text + length();
+
+ while (--t >= text->text)
+ {
+ const tchar c = *t;
+
+ if (! CharacterFunctions::isDigit (c))
+ {
+ if (c == T('-'))
+ n = -n;
+
+ break;
+ }
+
+ n += mult * (c - T('0'));
+ mult *= 10;
+ }
+
+ return n;
+}
+
+int64 String::getLargeIntValue() const throw()
+{
+ return CharacterFunctions::getInt64Value (text->text);
+}
+
+float String::getFloatValue() const throw()
+{
+ return (float) CharacterFunctions::getDoubleValue (text->text);
+}
+
+double String::getDoubleValue() const throw()
+{
+ return CharacterFunctions::getDoubleValue (text->text);
+}
+
+static const tchar* const hexDigits = T("0123456789abcdef");
+
+const String String::toHexString (const int number) throw()
+{
+ tchar buffer[32];
+ tchar* const end = buffer + 32;
+ tchar* t = end;
+ *--t = 0;
+ unsigned int v = (unsigned int) number;
+
+ do
+ {
+ *--t = hexDigits [v & 15];
+ v >>= 4;
+
+ } while (v != 0);
+
+ return String (t, (int) (((char*) end) - (char*) t) - 1);
+}
+
+const String String::toHexString (const int64 number) throw()
+{
+ tchar buffer[32];
+ tchar* const end = buffer + 32;
+ tchar* t = end;
+ *--t = 0;
+ uint64 v = (uint64) number;
+
+ do
+ {
+ *--t = hexDigits [(int) (v & 15)];
+ v >>= 4;
+
+ } while (v != 0);
+
+ return String (t, (int) (((char*) end) - (char*) t));
+}
+
+const String String::toHexString (const short number) throw()
+{
+ return toHexString ((int) (unsigned short) number);
+}
+
+const String String::toHexString (const unsigned char* data,
+ const int size,
+ const int groupSize) throw()
+{
+ if (size <= 0)
+ return empty;
+
+ int numChars = (size * 2) + 2;
+ if (groupSize > 0)
+ numChars += size / groupSize;
+
+ String s (numChars, (int) 0);
+
+ tchar* d = s.text->text;
+
+ for (int i = 0; i < size; ++i)
+ {
+ *d++ = hexDigits [(*data) >> 4];
+ *d++ = hexDigits [(*data) & 0xf];
+ ++data;
+
+ if (groupSize > 0 && (i % groupSize) == 0)
+ *d++ = T(' ');
+ }
+
+ if (groupSize > 0)
+ --d;
+
+ *d = 0;
+
+ return s;
+}
+
+int String::getHexValue32() const throw()
+{
+ int result = 0;
+ const tchar* c = text->text;
+
+ for (;;)
+ {
+ const int hexValue = CharacterFunctions::getHexDigitValue (*c);
+
+ if (hexValue >= 0)
+ result = (result << 4) | hexValue;
+ else if (*c == 0)
+ break;
+
+ ++c;
+ }
+
+ return result;
+}
+
+int64 String::getHexValue64() const throw()
+{
+ int64 result = 0;
+ const tchar* c = text->text;
+
+ for (;;)
+ {
+ const int hexValue = CharacterFunctions::getHexDigitValue (*c);
+
+ if (hexValue >= 0)
+ result = (result << 4) | hexValue;
+ else if (*c == 0)
+ break;
+
+ ++c;
+ }
+
+ return result;
+}
+
+const String String::createStringFromData (const void* const data_,
+ const int size) throw()
+{
+ const char* const data = (const char*) data_;
+
+ if (size <= 0 || data == 0)
+ {
+ return empty;
+ }
+ else if (size < 2)
+ {
+ return charToString (data[0]);
+ }
+ else if ((data[0] == (char)-2 && data[1] == (char)-1)
+ || (data[0] == (char)-1 && data[1] == (char)-2))
+ {
+ // assume it's 16-bit unicode
+ const bool bigEndian = (data[0] == (char)-2);
+ const int numChars = size / 2 - 1;
+
+ String result;
+ result.preallocateStorage (numChars + 2);
+
+ const uint16* const src = (const uint16*) (data + 2);
+ tchar* const dst = const_cast ((const tchar*) result);
+
+ if (bigEndian)
+ {
+ for (int i = 0; i < numChars; ++i)
+ dst[i] = (tchar) swapIfLittleEndian (src[i]);
+ }
+ else
+ {
+ for (int i = 0; i < numChars; ++i)
+ dst[i] = (tchar) swapIfBigEndian (src[i]);
+ }
+
+ dst [numChars] = 0;
+ return result;
+ }
+ else
+ {
+#if JUCE_STRINGS_ARE_UNICODE && JUCE_LINUX
+ // (workaround for strange behaviour of mbstowcs)
+ int i;
+ for (i = 0; i < size; ++i)
+ if (data[i] == 0)
+ break;
+
+ String result;
+ result.preallocateStorage (i + 1);
+ tchar* const dst = const_cast ((const tchar*) result);
+
+ for (int j = 0; j < i; ++j)
+ dst[j] = (juce_wchar) (unsigned char) data[j];
+
+ dst[i] = 0;
+
+ return result;
+#else
+ return String (data, size);
+#endif
+ }
+}
+
+const char* String::toUTF8() const throw()
+{
+ if (isEmpty())
+ {
+ return (const char*) emptyCharString;
+ }
+ else
+ {
+ String* const mutableThis = const_cast (this);
+
+ mutableThis->dupeInternalIfMultiplyReferenced();
+
+ const int currentLen = CharacterFunctions::length (text->text) + 1;
+ const int utf8BytesNeeded = copyToUTF8 (0);
+
+ mutableThis->text = (InternalRefCountedStringHolder*)
+ juce_realloc (text, sizeof (InternalRefCountedStringHolder)
+ + (currentLen * sizeof (juce_wchar) + utf8BytesNeeded));
+
+ char* const otherCopy = (char*) (text->text + currentLen);
+ copyToUTF8 ((uint8*) otherCopy);
+
+ return otherCopy;
+ }
+}
+
+int String::copyToUTF8 (uint8* const buffer) const throw()
+{
+#if JUCE_STRINGS_ARE_UNICODE
+ int num = 0, index = 0;
+
+ for (;;)
+ {
+ const uint32 c = (uint32) text->text [index++];
+
+ if (c >= 0x80)
+ {
+ int numExtraBytes = 1;
+
+ if (c >= 0x800)
+ {
+ ++numExtraBytes;
+
+ if (c >= 0x10000)
+ {
+ ++numExtraBytes;
+
+ if (c >= 0x200000)
+ {
+ ++numExtraBytes;
+
+ if (c >= 0x4000000)
+ ++numExtraBytes;
+ }
+ }
+ }
+
+ if (buffer != 0)
+ {
+ buffer [num++] = (uint8) ((0xff << (7 - numExtraBytes)) | (c >> (numExtraBytes * 6)));
+
+ while (--numExtraBytes >= 0)
+ buffer [num++] = (uint8) (0x80 | (0x3f & (c >> (numExtraBytes * 6))));
+ }
+ else
+ {
+ num += numExtraBytes + 1;
+ }
+ }
+ else
+ {
+ if (buffer != 0)
+ buffer [num] = (uint8) c;
+
+ ++num;
+ }
+
+ if (c == 0)
+ break;
+ }
+
+ return num;
+
+#else
+ const int numBytes = length() + 1;
+
+ if (buffer != 0)
+ copyToBuffer ((char*) buffer, numBytes);
+
+ return numBytes;
+#endif
+}
+
+const String String::fromUTF8 (const uint8* const buffer, int bufferSizeBytes) throw()
+{
+ if (buffer == 0)
+ return empty;
+
+ if (bufferSizeBytes < 0)
+ bufferSizeBytes = INT_MAX;
+
+ int numBytes;
+ for (numBytes = 0; numBytes < bufferSizeBytes; ++numBytes)
+ if (buffer [numBytes] == 0)
+ break;
+
+ String result (numBytes + 1, 0);
+ tchar* dest = result.text->text;
+
+ int i = 0;
+ while (i < numBytes)
+ {
+ const uint8 c = buffer [i++];
+
+ if ((c & 0x80) != 0)
+ {
+ int mask = 0x7f;
+ int bit = 0x40;
+ int numExtraValues = 0;
+
+ while (bit != 0 && (c & bit) != 0)
+ {
+ bit >>= 1;
+ mask >>= 1;
+ ++numExtraValues;
+ }
+
+ int n = (c & mask);
+
+ while (--numExtraValues >= 0 && i < bufferSizeBytes)
+ {
+ const uint8 c = buffer[i];
+
+ if ((c & 0xc0) != 0x80)
+ break;
+
+ n <<= 6;
+ n |= (c & 0x3f);
+ ++i;
+ }
+
+ *dest++ = (tchar) n;
+ }
+ else
+ {
+ *dest++ = (tchar) c;
+ }
+ }
+
+ *dest = 0;
+ return result;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_String.cpp *********/
+
+/********* Start of inlined file: juce_StringArray.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+StringArray::StringArray() throw()
+{
+}
+
+StringArray::StringArray (const StringArray& other) throw()
+{
+ addArray (other);
+}
+
+StringArray::StringArray (const juce_wchar** const strings,
+ const int numberOfStrings) throw()
+{
+ for (int i = 0; i < numberOfStrings; ++i)
+ add (strings [i]);
+}
+
+StringArray::StringArray (const char** const strings,
+ const int numberOfStrings) throw()
+{
+ for (int i = 0; i < numberOfStrings; ++i)
+ add (strings [i]);
+}
+
+StringArray::StringArray (const juce_wchar** const strings) throw()
+{
+ int i = 0;
+
+ while (strings[i] != 0)
+ add (strings [i++]);
+}
+
+StringArray::StringArray (const char** const strings) throw()
+{
+ int i = 0;
+
+ while (strings[i] != 0)
+ add (strings [i++]);
+}
+
+const StringArray& StringArray::operator= (const StringArray& other) throw()
+{
+ if (this != &other)
+ {
+ clear();
+ addArray (other);
+ }
+
+ return *this;
+}
+
+StringArray::~StringArray() throw()
+{
+ clear();
+}
+
+bool StringArray::operator== (const StringArray& other) const throw()
+{
+ if (other.size() != size())
+ return false;
+
+ for (int i = size(); --i >= 0;)
+ {
+ if (*(String*) other.strings.getUnchecked(i)
+ != *(String*) strings.getUnchecked(i))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool StringArray::operator!= (const StringArray& other) const throw()
+{
+ return ! operator== (other);
+}
+
+void StringArray::clear() throw()
+{
+ for (int i = size(); --i >= 0;)
+ {
+ String* const s = (String*) strings.getUnchecked(i);
+ delete s;
+ }
+
+ strings.clear();
+}
+
+const String& StringArray::operator[] (const int index) const throw()
+{
+ if (((unsigned int) index) < (unsigned int) strings.size())
+ return *(const String*) (strings.getUnchecked (index));
+
+ return String::empty;
+}
+
+void StringArray::add (const String& newString) throw()
+{
+ strings.add (new String (newString));
+}
+
+void StringArray::insert (const int index,
+ const String& newString) throw()
+{
+ strings.insert (index, new String (newString));
+}
+
+void StringArray::addIfNotAlreadyThere (const String& newString,
+ const bool ignoreCase) throw()
+{
+ if (! contains (newString, ignoreCase))
+ add (newString);
+}
+
+void StringArray::addArray (const StringArray& otherArray,
+ int startIndex,
+ int numElementsToAdd) throw()
+{
+ if (startIndex < 0)
+ {
+ jassertfalse
+ startIndex = 0;
+ }
+
+ if (numElementsToAdd < 0 || startIndex + numElementsToAdd > otherArray.size())
+ numElementsToAdd = otherArray.size() - startIndex;
+
+ while (--numElementsToAdd >= 0)
+ strings.add (new String (*(const String*) otherArray.strings.getUnchecked (startIndex++)));
+}
+
+void StringArray::set (const int index,
+ const String& newString) throw()
+{
+ String* const s = (String*) strings [index];
+
+ if (s != 0)
+ {
+ *s = newString;
+ }
+ else if (index >= 0)
+ {
+ add (newString);
+ }
+}
+
+bool StringArray::contains (const String& stringToLookFor,
+ const bool ignoreCase) const throw()
+{
+ if (ignoreCase)
+ {
+ for (int i = size(); --i >= 0;)
+ if (stringToLookFor.equalsIgnoreCase (*(const String*)(strings.getUnchecked(i))))
+ return true;
+ }
+ else
+ {
+ for (int i = size(); --i >= 0;)
+ if (stringToLookFor == *(const String*)(strings.getUnchecked(i)))
+ return true;
+ }
+
+ return false;
+}
+
+int StringArray::indexOf (const String& stringToLookFor,
+ const bool ignoreCase,
+ int i) const throw()
+{
+ if (i < 0)
+ i = 0;
+
+ const int numElements = size();
+
+ if (ignoreCase)
+ {
+ while (i < numElements)
+ {
+ if (stringToLookFor.equalsIgnoreCase (*(const String*) strings.getUnchecked (i)))
+ return i;
+
+ ++i;
+ }
+ }
+ else
+ {
+ while (i < numElements)
+ {
+ if (stringToLookFor == *(const String*) strings.getUnchecked (i))
+ return i;
+
+ ++i;
+ }
+ }
+
+ return -1;
+}
+
+void StringArray::remove (const int index) throw()
+{
+ String* const s = (String*) strings [index];
+
+ if (s != 0)
+ {
+ strings.remove (index);
+ delete s;
+ }
+}
+
+void StringArray::removeString (const String& stringToRemove,
+ const bool ignoreCase) throw()
+{
+ if (ignoreCase)
+ {
+ for (int i = size(); --i >= 0;)
+ if (stringToRemove.equalsIgnoreCase (*(const String*) strings.getUnchecked (i)))
+ remove (i);
+ }
+ else
+ {
+ for (int i = size(); --i >= 0;)
+ if (stringToRemove == *(const String*) strings.getUnchecked (i))
+ remove (i);
+ }
+}
+
+void StringArray::removeEmptyStrings (const bool removeWhitespaceStrings) throw()
+{
+ if (removeWhitespaceStrings)
+ {
+ for (int i = size(); --i >= 0;)
+ if (((const String*) strings.getUnchecked(i))->trim().isEmpty())
+ remove (i);
+ }
+ else
+ {
+ for (int i = size(); --i >= 0;)
+ if (((const String*) strings.getUnchecked(i))->isEmpty())
+ remove (i);
+ }
+}
+
+void StringArray::trim() throw()
+{
+ for (int i = size(); --i >= 0;)
+ {
+ String& s = *(String*) strings.getUnchecked(i);
+ s = s.trim();
+ }
+}
+
+class InternalStringArrayComparator
+{
+public:
+ static int compareElements (void* const first, void* const second) throw()
+ {
+ return ((const String*) first)->compare (*(const String*) second);
+ }
+};
+
+class InsensitiveInternalStringArrayComparator
+{
+public:
+ static int compareElements (void* const first, void* const second) throw()
+ {
+ return ((const String*) first)->compareIgnoreCase (*(const String*) second);
+ }
+};
+
+void StringArray::sort (const bool ignoreCase) throw()
+{
+ if (ignoreCase)
+ {
+ InsensitiveInternalStringArrayComparator comp;
+ strings.sort (comp);
+ }
+ else
+ {
+ InternalStringArrayComparator comp;
+ strings.sort (comp);
+ }
+}
+
+void StringArray::move (const int currentIndex, int newIndex) throw()
+{
+ strings.move (currentIndex, newIndex);
+}
+
+const String StringArray::joinIntoString (const String& separator,
+ int start,
+ int numberToJoin) const throw()
+{
+ const int last = (numberToJoin < 0) ? size()
+ : jmin (size(), start + numberToJoin);
+
+ if (start < 0)
+ start = 0;
+
+ if (start >= last)
+ return String::empty;
+
+ if (start == last - 1)
+ return *(const String*) strings.getUnchecked (start);
+
+ const int separatorLen = separator.length();
+ int charsNeeded = separatorLen * (last - start - 1);
+
+ for (int i = start; i < last; ++i)
+ charsNeeded += ((const String*) strings.getUnchecked(i))->length();
+
+ String result;
+ result.preallocateStorage (charsNeeded);
+
+ tchar* dest = (tchar*) (const tchar*) result;
+
+ while (start < last)
+ {
+ const String& s = *(const String*) strings.getUnchecked (start);
+ const int len = s.length();
+
+ if (len > 0)
+ {
+ s.copyToBuffer (dest, len);
+ dest += len;
+ }
+
+ if (++start < last && separatorLen > 0)
+ {
+ separator.copyToBuffer (dest, separatorLen);
+ dest += separatorLen;
+ }
+ }
+
+ *dest = 0;
+
+ return result;
+}
+
+int StringArray::addTokens (const tchar* const text,
+ const bool preserveQuotedStrings) throw()
+{
+ return addTokens (text,
+ T(" \n\r\t"),
+ preserveQuotedStrings ? T("\"") : 0);
+}
+
+int StringArray::addTokens (const tchar* const text,
+ const tchar* breakCharacters,
+ const tchar* quoteCharacters) throw()
+{
+ int num = 0;
+
+ if (text != 0 && *text != 0)
+ {
+ if (breakCharacters == 0)
+ breakCharacters = T("");
+
+ if (quoteCharacters == 0)
+ quoteCharacters = T("");
+
+ bool insideQuotes = false;
+ tchar currentQuoteChar = 0;
+
+ int i = 0;
+ int tokenStart = 0;
+
+ for (;;)
+ {
+ const tchar c = text[i];
+
+ bool isBreak = (c == 0);
+
+ if (! (insideQuotes || isBreak))
+ {
+ const tchar* b = breakCharacters;
+ while (*b != 0)
+ {
+ if (*b++ == c)
+ {
+ isBreak = true;
+ break;
+ }
+ }
+ }
+
+ if (! isBreak)
+ {
+ bool isQuote = false;
+ const tchar* q = quoteCharacters;
+ while (*q != 0)
+ {
+ if (*q++ == c)
+ {
+ isQuote = true;
+ break;
+ }
+ }
+
+ if (isQuote)
+ {
+ if (insideQuotes)
+ {
+ // only break out of quotes-mode if we find a matching quote to the
+ // one that we opened with..
+ if (currentQuoteChar == c)
+ insideQuotes = false;
+ }
+ else
+ {
+ insideQuotes = true;
+ currentQuoteChar = c;
+ }
+ }
+ }
+ else
+ {
+ add (String (text + tokenStart, i - tokenStart));
+
+ ++num;
+ tokenStart = i + 1;
+ }
+
+ if (c == 0)
+ break;
+
+ ++i;
+ }
+ }
+
+ return num;
+}
+
+int StringArray::addLines (const tchar* text) throw()
+{
+ int numLines = 0;
+
+ if (text != 0)
+ {
+ while (*text != 0)
+ {
+ const tchar* const startOfLine = text;
+
+ while (*text != 0)
+ {
+ if (*text == T('\r'))
+ {
+ ++text;
+ if (*text == T('\n'))
+ ++text;
+
+ break;
+ }
+
+ if (*text == T('\n'))
+ {
+ ++text;
+ break;
+ }
+
+ ++text;
+ }
+
+ const tchar* endOfLine = text;
+ if (endOfLine > startOfLine && (*(endOfLine - 1) == T('\r') || *(endOfLine - 1) == T('\n')))
+ --endOfLine;
+
+ if (endOfLine > startOfLine && (*(endOfLine - 1) == T('\r') || *(endOfLine - 1) == T('\n')))
+ --endOfLine;
+
+ add (String (startOfLine, jmax (0, (int) (endOfLine - startOfLine))));
+
+ ++numLines;
+ }
+ }
+
+ return numLines;
+}
+
+void StringArray::removeDuplicates (const bool ignoreCase) throw()
+{
+ for (int i = 0; i < size() - 1; ++i)
+ {
+ const String& s = *(String*) strings.getUnchecked(i);
+
+ int nextIndex = i + 1;
+
+ for (;;)
+ {
+ nextIndex = indexOf (s, ignoreCase, nextIndex);
+
+ if (nextIndex < 0)
+ break;
+
+ remove (nextIndex);
+ }
+ }
+}
+
+void StringArray::appendNumbersToDuplicates (const bool ignoreCase,
+ const bool appendNumberToFirstInstance,
+ const tchar* const preNumberString,
+ const tchar* const postNumberString) throw()
+{
+ for (int i = 0; i < size() - 1; ++i)
+ {
+ String& s = *(String*) strings.getUnchecked(i);
+
+ int nextIndex = indexOf (s, ignoreCase, i + 1);
+
+ if (nextIndex >= 0)
+ {
+ const String original (s);
+
+ int number = 0;
+
+ if (appendNumberToFirstInstance)
+ s = original + preNumberString + String (++number) + postNumberString;
+ else
+ ++number;
+
+ while (nextIndex >= 0)
+ {
+ set (nextIndex, (*this)[nextIndex] + preNumberString + String (++number) + postNumberString);
+ nextIndex = indexOf (original, ignoreCase, nextIndex + 1);
+ }
+ }
+ }
+}
+
+void StringArray::minimiseStorageOverheads() throw()
+{
+ strings.minimiseStorageOverheads();
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_StringArray.cpp *********/
+
+/********* Start of inlined file: juce_StringPairArray.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+StringPairArray::StringPairArray (const bool ignoreCase_) throw()
+ : ignoreCase (ignoreCase_)
+{
+}
+
+StringPairArray::StringPairArray (const StringPairArray& other) throw()
+ : keys (other.keys),
+ values (other.values),
+ ignoreCase (other.ignoreCase)
+{
+}
+
+StringPairArray::~StringPairArray() throw()
+{
+}
+
+const StringPairArray& StringPairArray::operator= (const StringPairArray& other) throw()
+{
+ keys = other.keys;
+ values = other.values;
+
+ return *this;
+}
+
+bool StringPairArray::operator== (const StringPairArray& other) const throw()
+{
+ for (int i = keys.size(); --i >= 0;)
+ if (other [keys[i]] != values[i])
+ return false;
+
+ return true;
+}
+
+bool StringPairArray::operator!= (const StringPairArray& other) const throw()
+{
+ return ! operator== (other);
+}
+
+const String& StringPairArray::operator[] (const String& key) const throw()
+{
+ return values [keys.indexOf (key, ignoreCase)];
+}
+
+const String StringPairArray::getValue (const String& key, const String& defaultReturnValue) const
+{
+ const int i = keys.indexOf (key, ignoreCase);
+
+ if (i >= 0)
+ return values[i];
+
+ return defaultReturnValue;
+}
+
+void StringPairArray::set (const String& key,
+ const String& value) throw()
+{
+ const int i = keys.indexOf (key, ignoreCase);
+
+ if (i >= 0)
+ {
+ values.set (i, value);
+ }
+ else
+ {
+ keys.add (key);
+ values.add (value);
+ }
+}
+
+void StringPairArray::addArray (const StringPairArray& other)
+{
+ for (int i = 0; i < other.size(); ++i)
+ set (other.keys[i], other.values[i]);
+}
+
+void StringPairArray::clear() throw()
+{
+ keys.clear();
+ values.clear();
+}
+
+void StringPairArray::remove (const String& key) throw()
+{
+ remove (keys.indexOf (key, ignoreCase));
+}
+
+void StringPairArray::remove (const int index) throw()
+{
+ keys.remove (index);
+ values.remove (index);
+}
+
+void StringPairArray::minimiseStorageOverheads() throw()
+{
+ keys.minimiseStorageOverheads();
+ values.minimiseStorageOverheads();
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_StringPairArray.cpp *********/
+
+/********* Start of inlined file: juce_XmlDocument.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+static bool isXmlIdentifierChar_Slow (const tchar c) throw()
+{
+ return CharacterFunctions::isLetterOrDigit (c)
+ || c == T('_')
+ || c == T('-')
+ || c == T(':')
+ || c == T('.');
+}
+
+#define isXmlIdentifierChar(c) \
+ ((c > 0 && c <= 127) ? identifierLookupTable [(int) c] : isXmlIdentifierChar_Slow (c))
+
+XmlDocument::XmlDocument (const String& documentText) throw()
+ : originalText (documentText),
+ inputSource (0)
+{
+}
+
+XmlDocument::XmlDocument (const File& file)
+{
+ inputSource = new FileInputSource (file);
+}
+
+XmlDocument::~XmlDocument() throw()
+{
+ delete inputSource;
+}
+
+void XmlDocument::setInputSource (InputSource* const newSource) throw()
+{
+ if (inputSource != newSource)
+ {
+ delete inputSource;
+ inputSource = newSource;
+ }
+}
+
+XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement)
+{
+ String textToParse (originalText);
+
+ if (textToParse.isEmpty() && inputSource != 0)
+ {
+ InputStream* const in = inputSource->createInputStream();
+
+ if (in != 0)
+ {
+ MemoryBlock data;
+
+ in->readIntoMemoryBlock (data, onlyReadOuterDocumentElement ? 8192 : -1);
+ delete in;
+
+ if (data.getSize() >= 2
+ && ((data[0] == (char)-2 && data[1] == (char)-1)
+ || (data[0] == (char)-1 && data[1] == (char)-2)))
+ {
+ textToParse = String::createStringFromData ((const char*) data.getData(), data.getSize());
+ }
+ else
+ {
+ textToParse = String::fromUTF8 ((const uint8*) data.getData(), data.getSize());
+ }
+
+ if (! onlyReadOuterDocumentElement)
+ originalText = textToParse;
+ }
+ }
+
+ input = textToParse;
+ lastError = String::empty;
+ errorOccurred = false;
+ outOfData = false;
+ needToLoadDTD = true;
+
+ for (int i = 0; i < 128; ++i)
+ identifierLookupTable[i] = isXmlIdentifierChar_Slow ((tchar) i);
+
+ if (textToParse.isEmpty())
+ {
+ lastError = "not enough input";
+ }
+ else
+ {
+ skipHeader();
+
+ if (input != 0)
+ {
+ XmlElement* const result = readNextElement (! onlyReadOuterDocumentElement);
+
+ if (errorOccurred)
+ delete result;
+ else
+ return result;
+ }
+ else
+ {
+ lastError = "incorrect xml header";
+ }
+ }
+
+ return 0;
+}
+
+const String& XmlDocument::getLastParseError() const throw()
+{
+ return lastError;
+}
+
+void XmlDocument::setLastError (const String& desc, const bool carryOn) throw()
+{
+ lastError = desc;
+ errorOccurred = ! carryOn;
+}
+
+const String XmlDocument::getFileContents (const String& filename) const
+{
+ String result;
+
+ if (inputSource != 0)
+ {
+ InputStream* const in = inputSource->createInputStreamFor (filename.trim().unquoted());
+
+ if (in != 0)
+ {
+ result = in->readEntireStreamAsString();
+ delete in;
+ }
+ }
+
+ return result;
+}
+
+tchar XmlDocument::readNextChar() throw()
+{
+ if (*input != 0)
+ {
+ return *input++;
+ }
+ else
+ {
+ outOfData = true;
+ return 0;
+ }
+}
+
+int XmlDocument::findNextTokenLength() throw()
+{
+ int len = 0;
+ tchar c = *input;
+
+ while (isXmlIdentifierChar (c))
+ c = input [++len];
+
+ return len;
+}
+
+void XmlDocument::skipHeader() throw()
+{
+ const tchar* const found = CharacterFunctions::find (input, T(""));
+
+ if (input == 0)
+ return;
+
+ input += 2;
+ }
+
+ skipNextWhiteSpace();
+ const tchar* docType = CharacterFunctions::find (input, T(" 0)
+ {
+ const tchar c = readNextChar();
+
+ if (outOfData)
+ return;
+
+ if (c == T('<'))
+ ++n;
+ else if (c == T('>'))
+ --n;
+ }
+
+ docType += 9;
+ dtdText = String (docType, (int) (input - (docType + 1))).trim();
+}
+
+void XmlDocument::skipNextWhiteSpace() throw()
+{
+ for (;;)
+ {
+ tchar c = *input;
+
+ while (CharacterFunctions::isWhitespace (c))
+ c = *++input;
+
+ if (c == 0)
+ {
+ outOfData = true;
+ break;
+ }
+ else if (c == T('<'))
+ {
+ if (input[1] == T('!')
+ && input[2] == T('-')
+ && input[3] == T('-'))
+ {
+ const tchar* const closeComment = CharacterFunctions::find (input, T("-->"));
+
+ if (closeComment == 0)
+ {
+ outOfData = true;
+ break;
+ }
+
+ input = closeComment + 3;
+ continue;
+ }
+ else if (input[1] == T('?'))
+ {
+ const tchar* const closeBracket = CharacterFunctions::find (input, T("?>"));
+
+ if (closeBracket == 0)
+ {
+ outOfData = true;
+ break;
+ }
+
+ input = closeBracket + 2;
+ continue;
+ }
+ }
+
+ break;
+ }
+}
+
+void XmlDocument::readQuotedString (String& result) throw()
+{
+ const tchar quote = readNextChar();
+
+ while (! outOfData)
+ {
+ const tchar character = readNextChar();
+
+ if (character == quote)
+ break;
+
+ if (character == T('&'))
+ {
+ --input;
+ readEntity (result);
+ }
+ else
+ {
+ --input;
+ const tchar* const start = input;
+
+ for (;;)
+ {
+ const tchar character = *input;
+
+ if (character == quote)
+ {
+ result.append (start, (int) (input - start));
+ ++input;
+
+ return;
+ }
+ else if (character == T('&'))
+ {
+ result.append (start, (int) (input - start));
+ break;
+ }
+ else if (character == 0)
+ {
+ outOfData = true;
+ setLastError ("unmatched quotes", false);
+ break;
+ }
+
+ ++input;
+ }
+ }
+ }
+}
+
+XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) throw()
+{
+ XmlElement* node = 0;
+
+ skipNextWhiteSpace();
+ if (outOfData)
+ return 0;
+
+ input = CharacterFunctions::find (input, T("<"));
+
+ if (input != 0)
+ {
+ ++input;
+ int tagLen = findNextTokenLength();
+
+ if (tagLen == 0)
+ {
+ // no tag name - but allow for a gap after the '<' before giving an error
+ skipNextWhiteSpace();
+ tagLen = findNextTokenLength();
+
+ if (tagLen == 0)
+ {
+ setLastError ("tag name missing", false);
+ return node;
+ }
+ }
+
+ node = new XmlElement (input, tagLen);
+ input += tagLen;
+ XmlElement::XmlAttributeNode* lastAttribute = 0;
+
+ // look for attributes
+ for (;;)
+ {
+ skipNextWhiteSpace();
+
+ const tchar c = *input;
+
+ // empty tag..
+ if (c == T('/') && input[1] == T('>'))
+ {
+ input += 2;
+ break;
+ }
+
+ // parse the guts of the element..
+ if (c == T('>'))
+ {
+ ++input;
+ skipNextWhiteSpace();
+
+ if (alsoParseSubElements)
+ readChildElements (node);
+
+ break;
+ }
+
+ // get an attribute..
+ if (isXmlIdentifierChar (c))
+ {
+ const int attNameLen = findNextTokenLength();
+
+ if (attNameLen > 0)
+ {
+ const tchar* attNameStart = input;
+ input += attNameLen;
+
+ skipNextWhiteSpace();
+
+ if (readNextChar() == T('='))
+ {
+ skipNextWhiteSpace();
+
+ const tchar c = *input;
+
+ if (c == T('"') || c == T('\''))
+ {
+ XmlElement::XmlAttributeNode* const newAtt
+ = new XmlElement::XmlAttributeNode (String (attNameStart, attNameLen),
+ String::empty);
+
+ readQuotedString (newAtt->value);
+
+ if (lastAttribute == 0)
+ node->attributes = newAtt;
+ else
+ lastAttribute->next = newAtt;
+
+ lastAttribute = newAtt;
+
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (! outOfData)
+ setLastError ("illegal character found in " + node->getTagName() + ": '" + c + "'", false);
+ }
+
+ break;
+ }
+ }
+
+ return node;
+}
+
+void XmlDocument::readChildElements (XmlElement* parent) throw()
+{
+ XmlElement* lastChildNode = 0;
+
+ for (;;)
+ {
+ skipNextWhiteSpace();
+
+ if (outOfData)
+ {
+ setLastError ("unmatched tags", false);
+ break;
+ }
+
+ if (*input == T('<'))
+ {
+ if (input[1] == T('/'))
+ {
+ // our close tag..
+ input = CharacterFunctions::find (input, T(">"));
+ ++input;
+ break;
+ }
+ else if (input[1] == T('!')
+ && input[2] == T('[')
+ && input[3] == T('C')
+ && input[4] == T('D')
+ && input[5] == T('A')
+ && input[6] == T('T')
+ && input[7] == T('A')
+ && input[8] == T('['))
+ {
+ input += 9;
+ const tchar* const inputStart = input;
+
+ int len = 0;
+
+ for (;;)
+ {
+ if (*input == 0)
+ {
+ setLastError ("unterminated CDATA section", false);
+ outOfData = true;
+ break;
+ }
+ else if (input[0] == T(']')
+ && input[1] == T(']')
+ && input[2] == T('>'))
+ {
+ input += 3;
+ break;
+ }
+
+ ++input;
+ ++len;
+ }
+
+ XmlElement* const e = new XmlElement ((int) 0);
+ e->setText (String (inputStart, len));
+
+ if (lastChildNode != 0)
+ lastChildNode->nextElement = e;
+ else
+ parent->addChildElement (e);
+
+ lastChildNode = e;
+ }
+ else
+ {
+ // this is some other element, so parse and add it..
+ XmlElement* const n = readNextElement (true);
+
+ if (n != 0)
+ {
+ if (lastChildNode == 0)
+ parent->addChildElement (n);
+ else
+ lastChildNode->nextElement = n;
+
+ lastChildNode = n;
+ }
+ else
+ {
+ return;
+ }
+ }
+ }
+ else
+ {
+ // read character block..
+ XmlElement* const e = new XmlElement ((int)0);
+
+ if (lastChildNode != 0)
+ lastChildNode->nextElement = e;
+ else
+ parent->addChildElement (e);
+
+ lastChildNode = e;
+
+ String textElementContent;
+
+ for (;;)
+ {
+ const tchar c = *input;
+
+ if (c == T('<'))
+ break;
+
+ if (c == 0)
+ {
+ setLastError ("unmatched tags", false);
+ outOfData = true;
+ return;
+ }
+
+ if (c == T('&'))
+ {
+ String entity;
+ readEntity (entity);
+
+ if (entity.startsWithChar (T('<')) && entity [1] != 0)
+ {
+ const tchar* const oldInput = input;
+ const bool oldOutOfData = outOfData;
+
+ input = (const tchar*) entity;
+ outOfData = false;
+
+ for (;;)
+ {
+ XmlElement* const n = readNextElement (true);
+
+ if (n == 0)
+ break;
+
+ if (lastChildNode == 0)
+ parent->addChildElement (n);
+ else
+ lastChildNode->nextElement = n;
+
+ lastChildNode = n;
+ }
+
+ input = oldInput;
+ outOfData = oldOutOfData;
+ }
+ else
+ {
+ textElementContent += entity;
+ }
+ }
+ else
+ {
+ const tchar* start = input;
+ int len = 0;
+
+ for (;;)
+ {
+ const tchar c = *input;
+
+ if (c == T('<') || c == T('&'))
+ {
+ break;
+ }
+ else if (c == 0)
+ {
+ setLastError ("unmatched tags", false);
+ outOfData = true;
+ return;
+ }
+
+ ++input;
+ ++len;
+ }
+
+ textElementContent.append (start, len);
+ }
+ }
+
+ textElementContent = textElementContent.trim();
+
+ if (textElementContent.isNotEmpty())
+ e->setText (textElementContent);
+ }
+ }
+}
+
+void XmlDocument::readEntity (String& result) throw()
+{
+ // skip over the ampersand
+ ++input;
+
+ if (CharacterFunctions::compareIgnoreCase (input, T("amp;"), 4) == 0)
+ {
+ input += 4;
+ result += T("&");
+ }
+ else if (CharacterFunctions::compareIgnoreCase (input, T("quot;"), 5) == 0)
+ {
+ input += 5;
+ result += T("\"");
+ }
+ else if (CharacterFunctions::compareIgnoreCase (input, T("apos;"), 5) == 0)
+ {
+ input += 5;
+ result += T("\'");
+ }
+ else if (CharacterFunctions::compareIgnoreCase (input, T("lt;"), 3) == 0)
+ {
+ input += 3;
+ result += T("<");
+ }
+ else if (CharacterFunctions::compareIgnoreCase (input, T("gt;"), 3) == 0)
+ {
+ input += 3;
+ result += T(">");
+ }
+ else if (*input == T('#'))
+ {
+ int charCode = 0;
+ ++input;
+
+ if (*input == T('x') || *input == T('X'))
+ {
+ ++input;
+ int numChars = 0;
+
+ while (input[0] != T(';'))
+ {
+ const int hexValue = CharacterFunctions::getHexDigitValue (input[0]);
+
+ if (hexValue < 0 || ++numChars > 8)
+ {
+ setLastError ("illegal escape sequence", true);
+ break;
+ }
+
+ charCode = (charCode << 4) | hexValue;
+ ++input;
+ }
+
+ ++input;
+ }
+ else if (input[0] >= T('0') && input[0] <= T('9'))
+ {
+ int numChars = 0;
+
+ while (input[0] != T(';'))
+ {
+ if (++numChars > 12)
+ {
+ setLastError ("illegal escape sequence", true);
+ break;
+ }
+
+ charCode = charCode * 10 + (input[0] - T('0'));
+ ++input;
+ }
+
+ ++input;
+ }
+ else
+ {
+ setLastError ("illegal escape sequence", true);
+ result += T("&");
+ return;
+ }
+
+ result << (tchar) charCode;
+ }
+ else
+ {
+ const tchar* const entityNameStart = input;
+ const tchar* const closingSemiColon = CharacterFunctions::find (input, T(";"));
+
+ if (closingSemiColon == 0)
+ {
+ outOfData = true;
+ result += T("&");
+ }
+ else
+ {
+ input = closingSemiColon + 1;
+
+ result += expandExternalEntity (String (entityNameStart,
+ (int) (closingSemiColon - entityNameStart)));
+ }
+ }
+}
+
+const String XmlDocument::expandEntity (const String& ent)
+{
+ if (ent.equalsIgnoreCase (T("amp")))
+ {
+ return T("&");
+ }
+ else if (ent.equalsIgnoreCase (T("quot")))
+ {
+ return T("\"");
+ }
+ else if (ent.equalsIgnoreCase (T("apos")))
+ {
+ return T("\'");
+ }
+ else if (ent.equalsIgnoreCase (T("lt")))
+ {
+ return T("<");
+ }
+ else if (ent.equalsIgnoreCase (T("gt")))
+ {
+ return T(">");
+ }
+ else if (ent[0] == T('#'))
+ {
+ if (ent[1] == T('x') || ent[1] == T('X'))
+ {
+ return String::charToString ((tchar) ent.substring (2).getHexValue32());
+ }
+ else if (ent[1] >= T('0') && ent[1] <= T('9'))
+ {
+ return String::charToString ((tchar) ent.substring (1).getIntValue());
+ }
+
+ setLastError ("illegal escape sequence", false);
+ return T("&");
+ }
+ else
+ {
+ return expandExternalEntity (ent);
+ }
+}
+
+const String XmlDocument::expandExternalEntity (const String& entity)
+{
+ if (needToLoadDTD)
+ {
+ if (dtdText.isNotEmpty())
+ {
+ while (dtdText.endsWithChar (T('>')))
+ dtdText = dtdText.dropLastCharacters (1);
+
+ tokenisedDTD.addTokens (dtdText, true);
+
+ if (tokenisedDTD [tokenisedDTD.size() - 2].equalsIgnoreCase (T("system"))
+ && tokenisedDTD [tokenisedDTD.size() - 1].isQuotedString())
+ {
+ const String fn (tokenisedDTD [tokenisedDTD.size() - 1]);
+
+ tokenisedDTD.clear();
+ tokenisedDTD.addTokens (getFileContents (fn), true);
+ }
+ else
+ {
+ tokenisedDTD.clear();
+ const int openBracket = dtdText.indexOfChar (T('['));
+
+ if (openBracket > 0)
+ {
+ const int closeBracket = dtdText.lastIndexOfChar (T(']'));
+
+ if (closeBracket > openBracket)
+ tokenisedDTD.addTokens (dtdText.substring (openBracket + 1,
+ closeBracket), true);
+ }
+ }
+
+ for (int i = tokenisedDTD.size(); --i >= 0;)
+ {
+ if (tokenisedDTD[i].startsWithChar (T('%'))
+ && tokenisedDTD[i].endsWithChar (T(';')))
+ {
+ const String parsed (getParameterEntity (tokenisedDTD[i].substring (1, tokenisedDTD[i].length() - 1)));
+ StringArray newToks;
+ newToks.addTokens (parsed, true);
+
+ tokenisedDTD.remove (i);
+
+ for (int j = newToks.size(); --j >= 0;)
+ tokenisedDTD.insert (i, newToks[j]);
+ }
+ }
+ }
+
+ needToLoadDTD = false;
+ }
+
+ for (int i = 0; i < tokenisedDTD.size(); ++i)
+ {
+ if (tokenisedDTD[i] == entity)
+ {
+ if (tokenisedDTD[i - 1].equalsIgnoreCase (T("')))
+ ent = ent.dropLastCharacters (1);
+
+ ent = ent.trim().unquoted();
+
+ // check for sub-entities..
+ int ampersand = ent.indexOfChar (T('&'));
+
+ while (ampersand >= 0)
+ {
+ const int semiColon = ent.indexOf (i + 1, T(";"));
+
+ if (semiColon < 0)
+ {
+ setLastError ("entity without terminating semi-colon", false);
+ break;
+ }
+
+ const String resolved (expandEntity (ent.substring (i + 1, semiColon)));
+
+ ent = ent.substring (0, ampersand)
+ + resolved
+ + ent.substring (semiColon + 1);
+
+ ampersand = ent.indexOfChar (semiColon + 1, T('&'));
+ }
+
+ return ent;
+ }
+ }
+ }
+
+ setLastError ("unknown entity", true);
+
+ return entity;
+}
+
+const String XmlDocument::getParameterEntity (const String& entity)
+{
+ for (int i = 0; i < tokenisedDTD.size(); ++i)
+ {
+ if (tokenisedDTD[i] == entity)
+ {
+ if (tokenisedDTD [i - 1] == T("%")
+ && tokenisedDTD [i - 2].equalsIgnoreCase (T("')))
+ ent = ent.dropLastCharacters (1);
+
+ if (ent.equalsIgnoreCase (T("system")))
+ {
+ String filename (tokenisedDTD [i + 2]);
+
+ while (filename.endsWithChar (T('>')))
+ filename = filename.dropLastCharacters (1);
+
+ return getFileContents (filename);
+ }
+ else
+ {
+ return ent.trim().unquoted();
+ }
+ }
+ }
+ }
+
+ return entity;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_XmlDocument.cpp *********/
+
+/********* Start of inlined file: juce_XmlElement.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) throw()
+ : name (other.name),
+ value (other.value),
+ next (0)
+{
+}
+
+XmlElement::XmlAttributeNode::XmlAttributeNode (const String& name_,
+ const String& value_) throw()
+ : name (name_),
+ value (value_),
+ next (0)
+{
+}
+
+XmlElement::XmlElement (const String& tagName_) throw()
+ : tagName (tagName_),
+ firstChildElement (0),
+ nextElement (0),
+ attributes (0)
+{
+ // the tag name mustn't be empty, or it'll look like a text element!
+ jassert (tagName_.trim().isNotEmpty())
+}
+
+XmlElement::XmlElement (int /*dummy*/) throw()
+ : firstChildElement (0),
+ nextElement (0),
+ attributes (0)
+{
+}
+
+XmlElement::XmlElement (const tchar* const tagName_,
+ const int nameLen) throw()
+ : tagName (tagName_, nameLen),
+ firstChildElement (0),
+ nextElement (0),
+ attributes (0)
+{
+}
+
+XmlElement::XmlElement (const XmlElement& other) throw()
+ : tagName (other.tagName),
+ firstChildElement (0),
+ nextElement (0),
+ attributes (0)
+{
+ copyChildrenAndAttributesFrom (other);
+}
+
+const XmlElement& XmlElement::operator= (const XmlElement& other) throw()
+{
+ if (this != &other)
+ {
+ removeAllAttributes();
+ deleteAllChildElements();
+
+ tagName = other.tagName;
+
+ copyChildrenAndAttributesFrom (other);
+ }
+
+ return *this;
+}
+
+void XmlElement::copyChildrenAndAttributesFrom (const XmlElement& other) throw()
+{
+ XmlElement* child = other.firstChildElement;
+ XmlElement* lastChild = 0;
+
+ while (child != 0)
+ {
+ XmlElement* const copiedChild = new XmlElement (*child);
+
+ if (lastChild != 0)
+ lastChild->nextElement = copiedChild;
+ else
+ firstChildElement = copiedChild;
+
+ lastChild = copiedChild;
+ child = child->nextElement;
+ }
+
+ const XmlAttributeNode* att = other.attributes;
+ XmlAttributeNode* lastAtt = 0;
+
+ while (att != 0)
+ {
+ XmlAttributeNode* const newAtt = new XmlAttributeNode (*att);
+
+ if (lastAtt != 0)
+ lastAtt->next = newAtt;
+ else
+ attributes = newAtt;
+
+ lastAtt = newAtt;
+ att = att->next;
+ }
+}
+
+XmlElement::~XmlElement() throw()
+{
+ XmlElement* child = firstChildElement;
+
+ while (child != 0)
+ {
+ XmlElement* const nextChild = child->nextElement;
+ delete child;
+ child = nextChild;
+ }
+
+ XmlAttributeNode* att = attributes;
+
+ while (att != 0)
+ {
+ XmlAttributeNode* const nextAtt = att->next;
+ delete att;
+ att = nextAtt;
+ }
+}
+
+static bool isLegalXmlChar (const juce_wchar character)
+{
+ if ((character >= 'a' && character <= 'z')
+ || (character >= 'A' && character <= 'Z')
+ || (character >= '0' && character <= '9'))
+ return true;
+
+ const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}";
+
+ do
+ {
+ if (((juce_wchar) (uint8) *t) == character)
+ return true;
+ }
+ while (*++t != 0);
+
+ return false;
+}
+
+static void escapeIllegalXmlChars (OutputStream& outputStream,
+ const String& text,
+ const bool changeNewLines) throw()
+{
+ const juce_wchar* t = (const juce_wchar*) text;
+
+ for (;;)
+ {
+ const juce_wchar character = *t++;
+
+ if (character == 0)
+ {
+ break;
+ }
+ else if (isLegalXmlChar (character))
+ {
+ outputStream.writeByte ((char) character);
+ }
+ else
+ {
+ switch (character)
+ {
+ case '&':
+ outputStream.write ("&", 5);
+ break;
+
+ case '"':
+ outputStream.write (""", 6);
+ break;
+
+ case '>':
+ outputStream.write (">", 4);
+ break;
+
+ case '<':
+ outputStream.write ("<", 4);
+ break;
+
+ case '\n':
+ if (changeNewLines)
+ outputStream.write ("
", 5);
+ else
+ outputStream.writeByte ((char) character);
+
+ break;
+
+ case '\r':
+ if (changeNewLines)
+ outputStream.write ("
", 5);
+ else
+ outputStream.writeByte ((char) character);
+
+ break;
+
+ default:
+ {
+ String encoded (T(""));
+ encoded << String ((int) (unsigned int) character).trim()
+ << T(';');
+
+ outputStream.write ((const char*) encoded, encoded.length());
+ }
+ }
+ }
+ }
+}
+
+static void writeSpaces (OutputStream& out, int numSpaces) throw()
+{
+ if (numSpaces > 0)
+ {
+ const char* const blanks = " ";
+ const int blankSize = (int) sizeof (blanks) - 1;
+
+ while (numSpaces > blankSize)
+ {
+ out.write (blanks, blankSize);
+ numSpaces -= blankSize;
+ }
+
+ out.write (blanks, numSpaces);
+ }
+}
+
+void XmlElement::writeElementAsText (OutputStream& outputStream,
+ const int indentationLevel) const throw()
+{
+ writeSpaces (outputStream, indentationLevel);
+
+ if (! isTextElement())
+ {
+ outputStream.writeByte ('<');
+ const int nameLen = tagName.length();
+ outputStream.write ((const char*) tagName, nameLen);
+
+ const int attIndent = indentationLevel + nameLen + 1;
+ int lineLen = 0;
+
+ const XmlAttributeNode* att = attributes;
+ while (att != 0)
+ {
+ if (lineLen > 60 && indentationLevel >= 0)
+ {
+ outputStream.write ("\r\n", 2);
+ writeSpaces (outputStream, attIndent);
+ lineLen = 0;
+ }
+
+ const int attNameLen = att->name.length();
+ outputStream.writeByte (' ');
+ outputStream.write ((const char*) (att->name), attNameLen);
+ outputStream.write ("=\"", 2);
+ escapeIllegalXmlChars (outputStream, att->value, true);
+ outputStream.writeByte ('"');
+ lineLen += 4 + attNameLen + att->value.length();
+
+ att = att->next;
+ }
+
+ if (firstChildElement != 0)
+ {
+ XmlElement* child = firstChildElement;
+
+ if (child->nextElement == 0 && child->isTextElement())
+ {
+ outputStream.writeByte ('>');
+ escapeIllegalXmlChars (outputStream, child->getText(), false);
+ }
+ else
+ {
+ if (indentationLevel >= 0)
+ outputStream.write (">\r\n", 3);
+ else
+ outputStream.writeByte ('>');
+
+ bool lastWasTextNode = false;
+
+ while (child != 0)
+ {
+ if (child->isTextElement())
+ {
+ if ((! lastWasTextNode) && (indentationLevel >= 0))
+ writeSpaces (outputStream, indentationLevel + 2);
+
+ escapeIllegalXmlChars (outputStream, child->getText(), false);
+ lastWasTextNode = true;
+ }
+ else
+ {
+ if (indentationLevel >= 0)
+ {
+ if (lastWasTextNode)
+ outputStream.write ("\r\n", 2);
+
+ child->writeElementAsText (outputStream, indentationLevel + 2);
+ }
+ else
+ {
+ child->writeElementAsText (outputStream, indentationLevel);
+ }
+
+ lastWasTextNode = false;
+ }
+
+ child = child->nextElement;
+ }
+
+ if (indentationLevel >= 0)
+ {
+ if (lastWasTextNode)
+ outputStream.write ("\r\n", 2);
+
+ writeSpaces (outputStream, indentationLevel);
+ }
+ }
+
+ outputStream.write ("", 2);
+ outputStream.write ((const char*) tagName, nameLen);
+
+ if (indentationLevel >= 0)
+ outputStream.write (">\r\n", 3);
+ else
+ outputStream.writeByte ('>');
+ }
+ else
+ {
+ if (indentationLevel >= 0)
+ outputStream.write ("/>\r\n", 4);
+ else
+ outputStream.write ("/>", 2);
+ }
+ }
+ else
+ {
+ if (indentationLevel >= 0)
+ writeSpaces (outputStream, indentationLevel + 2);
+
+ escapeIllegalXmlChars (outputStream, getText(), false);
+ }
+}
+
+const String XmlElement::createDocument (const String& dtd,
+ const bool allOnOneLine,
+ const bool includeXmlHeader,
+ const tchar* const encoding) const throw()
+{
+ String doc;
+ doc.preallocateStorage (1024);
+
+ if (includeXmlHeader)
+ {
+ doc << " ";
+ else
+ doc += "\"?>\n\n";
+ }
+
+ if (dtd.isNotEmpty())
+ {
+ if (allOnOneLine)
+ doc << dtd << " ";
+ else
+ doc << dtd << "\r\n";
+ }
+
+ MemoryOutputStream mem (2048, 4096);
+ writeElementAsText (mem, allOnOneLine ? -1 : 0);
+
+ return doc + String (mem.getData(),
+ mem.getDataSize());
+}
+
+bool XmlElement::writeToFile (const File& f,
+ const String& dtd,
+ const tchar* const encoding) const throw()
+{
+ if (f.hasWriteAccess())
+ {
+ const File tempFile (f.getNonexistentSibling());
+
+ FileOutputStream* const out = tempFile.createOutputStream();
+
+ if (out != 0)
+ {
+ *out << "\r\n\r\n"
+ << dtd << "\r\n";
+
+ writeElementAsText (*out, 0);
+
+ delete out;
+
+ if (tempFile.moveFileTo (f))
+ return true;
+
+ tempFile.deleteFile();
+ }
+ }
+
+ return false;
+}
+
+bool XmlElement::hasTagName (const tchar* const tagNameWanted) const throw()
+{
+#ifdef JUCE_DEBUG
+ // if debugging, check that the case is actually the same, because
+ // valid xml is case-sensitive, and although this lets it pass, it's
+ // better not to..
+ if (tagName.equalsIgnoreCase (tagNameWanted))
+ {
+ jassert (tagName == tagNameWanted);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+#else
+ return tagName.equalsIgnoreCase (tagNameWanted);
+#endif
+}
+
+XmlElement* XmlElement::getNextElementWithTagName (const tchar* const requiredTagName) const
+{
+ XmlElement* e = nextElement;
+
+ while (e != 0 && ! e->hasTagName (requiredTagName))
+ e = e->nextElement;
+
+ return e;
+}
+
+int XmlElement::getNumAttributes() const throw()
+{
+ const XmlAttributeNode* att = attributes;
+ int count = 0;
+
+ while (att != 0)
+ {
+ att = att->next;
+ ++count;
+ }
+
+ return count;
+}
+
+const String& XmlElement::getAttributeName (const int index) const throw()
+{
+ const XmlAttributeNode* att = attributes;
+ int count = 0;
+
+ while (att != 0)
+ {
+ if (count == index)
+ return att->name;
+
+ att = att->next;
+ ++count;
+ }
+
+ return String::empty;
+}
+
+const String& XmlElement::getAttributeValue (const int index) const throw()
+{
+ const XmlAttributeNode* att = attributes;
+ int count = 0;
+
+ while (att != 0)
+ {
+ if (count == index)
+ return att->value;
+
+ att = att->next;
+ ++count;
+ }
+
+ return String::empty;
+}
+
+bool XmlElement::hasAttribute (const tchar* const attributeName) const throw()
+{
+ const XmlAttributeNode* att = attributes;
+
+ while (att != 0)
+ {
+ if (att->name.equalsIgnoreCase (attributeName))
+ return true;
+
+ att = att->next;
+ }
+
+ return false;
+}
+
+const String XmlElement::getStringAttribute (const tchar* const attributeName,
+ const tchar* const defaultReturnValue) const throw()
+{
+ const XmlAttributeNode* att = attributes;
+
+ while (att != 0)
+ {
+ if (att->name.equalsIgnoreCase (attributeName))
+ return att->value;
+
+ att = att->next;
+ }
+
+ return defaultReturnValue;
+}
+
+int XmlElement::getIntAttribute (const tchar* const attributeName,
+ const int defaultReturnValue) const throw()
+{
+ const XmlAttributeNode* att = attributes;
+
+ while (att != 0)
+ {
+ if (att->name.equalsIgnoreCase (attributeName))
+ return att->value.getIntValue();
+
+ att = att->next;
+ }
+
+ return defaultReturnValue;
+}
+
+double XmlElement::getDoubleAttribute (const tchar* const attributeName,
+ const double defaultReturnValue) const throw()
+{
+ const XmlAttributeNode* att = attributes;
+
+ while (att != 0)
+ {
+ if (att->name.equalsIgnoreCase (attributeName))
+ return att->value.getDoubleValue();
+
+ att = att->next;
+ }
+
+ return defaultReturnValue;
+}
+
+bool XmlElement::getBoolAttribute (const tchar* const attributeName,
+ const bool defaultReturnValue) const throw()
+{
+ const XmlAttributeNode* att = attributes;
+
+ while (att != 0)
+ {
+ if (att->name.equalsIgnoreCase (attributeName))
+ {
+ tchar firstChar = att->value[0];
+
+ if (CharacterFunctions::isWhitespace (firstChar))
+ firstChar = att->value.trimStart() [0];
+
+ return firstChar == T('1')
+ || firstChar == T('t')
+ || firstChar == T('y')
+ || firstChar == T('T')
+ || firstChar == T('Y');
+ }
+
+ att = att->next;
+ }
+
+ return defaultReturnValue;
+}
+
+bool XmlElement::compareAttribute (const tchar* const attributeName,
+ const tchar* const stringToCompareAgainst,
+ const bool ignoreCase) const throw()
+{
+ const XmlAttributeNode* att = attributes;
+
+ while (att != 0)
+ {
+ if (att->name.equalsIgnoreCase (attributeName))
+ {
+ if (ignoreCase)
+ return att->value.equalsIgnoreCase (stringToCompareAgainst);
+ else
+ return att->value == stringToCompareAgainst;
+ }
+
+ att = att->next;
+ }
+
+ return false;
+}
+
+void XmlElement::setAttribute (const tchar* const attributeName,
+ const String& value) throw()
+{
+#ifdef JUCE_DEBUG
+ // check the identifier being passed in is legal..
+ const tchar* t = attributeName;
+ while (*t != 0)
+ {
+ jassert (CharacterFunctions::isLetterOrDigit (*t)
+ || *t == T('_')
+ || *t == T('-')
+ || *t == T(':'));
+ ++t;
+ }
+#endif
+
+ if (attributes == 0)
+ {
+ attributes = new XmlAttributeNode (attributeName, value);
+ }
+ else
+ {
+ XmlAttributeNode* att = attributes;
+
+ for (;;)
+ {
+ if (att->name.equalsIgnoreCase (attributeName))
+ {
+ att->value = value;
+ break;
+ }
+ else if (att->next == 0)
+ {
+ att->next = new XmlAttributeNode (attributeName, value);
+ break;
+ }
+
+ att = att->next;
+ }
+ }
+}
+
+void XmlElement::setAttribute (const tchar* const attributeName,
+ const tchar* const text) throw()
+{
+ setAttribute (attributeName, String (text));
+}
+
+void XmlElement::setAttribute (const tchar* const attributeName,
+ const int number) throw()
+{
+ setAttribute (attributeName, String (number));
+}
+
+void XmlElement::setAttribute (const tchar* const attributeName,
+ const double number) throw()
+{
+ tchar buffer [40];
+ CharacterFunctions::printf (buffer, numElementsInArray (buffer), T("%.9g"), number);
+
+ setAttribute (attributeName, buffer);
+}
+
+void XmlElement::removeAttribute (const tchar* const attributeName) throw()
+{
+ XmlAttributeNode* att = attributes;
+ XmlAttributeNode* lastAtt = 0;
+
+ while (att != 0)
+ {
+ if (att->name.equalsIgnoreCase (attributeName))
+ {
+ if (lastAtt == 0)
+ attributes = att->next;
+ else
+ lastAtt->next = att->next;
+
+ delete att;
+ break;
+ }
+
+ lastAtt = att;
+ att = att->next;
+ }
+}
+
+void XmlElement::removeAllAttributes() throw()
+{
+ while (attributes != 0)
+ {
+ XmlAttributeNode* const nextAtt = attributes->next;
+ delete attributes;
+ attributes = nextAtt;
+ }
+}
+
+int XmlElement::getNumChildElements() const throw()
+{
+ int count = 0;
+ const XmlElement* child = firstChildElement;
+
+ while (child != 0)
+ {
+ ++count;
+ child = child->nextElement;
+ }
+
+ return count;
+}
+
+XmlElement* XmlElement::getChildElement (const int index) const throw()
+{
+ int count = 0;
+ XmlElement* child = firstChildElement;
+
+ while (child != 0 && count < index)
+ {
+ child = child->nextElement;
+ ++count;
+ }
+
+ return child;
+}
+
+XmlElement* XmlElement::getChildByName (const tchar* const childName) const throw()
+{
+ XmlElement* child = firstChildElement;
+
+ while (child != 0)
+ {
+ if (child->hasTagName (childName))
+ break;
+
+ child = child->nextElement;
+ }
+
+ return child;
+}
+
+void XmlElement::addChildElement (XmlElement* const newNode) throw()
+{
+ if (newNode != 0)
+ {
+ if (firstChildElement == 0)
+ {
+ firstChildElement = newNode;
+ }
+ else
+ {
+ XmlElement* child = firstChildElement;
+
+ while (child->nextElement != 0)
+ child = child->nextElement;
+
+ child->nextElement = newNode;
+
+ // if this is non-zero, then something's probably
+ // gone wrong..
+ jassert (newNode->nextElement == 0);
+ }
+ }
+}
+
+void XmlElement::insertChildElement (XmlElement* const newNode,
+ int indexToInsertAt) throw()
+{
+ if (newNode != 0)
+ {
+ removeChildElement (newNode, false);
+
+ if (indexToInsertAt == 0)
+ {
+ newNode->nextElement = firstChildElement;
+ firstChildElement = newNode;
+ }
+ else
+ {
+ if (firstChildElement == 0)
+ {
+ firstChildElement = newNode;
+ }
+ else
+ {
+ if (indexToInsertAt < 0)
+ indexToInsertAt = INT_MAX;
+
+ XmlElement* child = firstChildElement;
+
+ while (child->nextElement != 0 && --indexToInsertAt > 0)
+ child = child->nextElement;
+
+ newNode->nextElement = child->nextElement;
+ child->nextElement = newNode;
+ }
+ }
+ }
+}
+
+bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
+ XmlElement* const newNode) throw()
+{
+ if (newNode != 0)
+ {
+ XmlElement* child = firstChildElement;
+ XmlElement* previousNode = 0;
+
+ while (child != 0)
+ {
+ if (child == currentChildElement)
+ {
+ if (child != newNode)
+ {
+ if (previousNode == 0)
+ firstChildElement = newNode;
+ else
+ previousNode->nextElement = newNode;
+
+ newNode->nextElement = child->nextElement;
+
+ delete child;
+ }
+
+ return true;
+ }
+
+ previousNode = child;
+ child = child->nextElement;
+ }
+ }
+
+ return false;
+}
+
+void XmlElement::removeChildElement (XmlElement* const childToRemove,
+ const bool shouldDeleteTheChild) throw()
+{
+ if (childToRemove != 0)
+ {
+ if (firstChildElement == childToRemove)
+ {
+ firstChildElement = childToRemove->nextElement;
+ childToRemove->nextElement = 0;
+ }
+ else
+ {
+ XmlElement* child = firstChildElement;
+ XmlElement* last = 0;
+
+ while (child != 0)
+ {
+ if (child == childToRemove)
+ {
+ if (last == 0)
+ firstChildElement = child->nextElement;
+ else
+ last->nextElement = child->nextElement;
+
+ childToRemove->nextElement = 0;
+ break;
+ }
+
+ last = child;
+ child = child->nextElement;
+ }
+ }
+
+ if (shouldDeleteTheChild)
+ delete childToRemove;
+ }
+}
+
+bool XmlElement::isEquivalentTo (const XmlElement* const other,
+ const bool ignoreOrderOfAttributes) const throw()
+{
+ if (this != other)
+ {
+ if (other == 0 || tagName != other->tagName)
+ {
+ return false;
+ }
+
+ if (ignoreOrderOfAttributes)
+ {
+ int totalAtts = 0;
+ const XmlAttributeNode* att = attributes;
+
+ while (att != 0)
+ {
+ if (! other->compareAttribute (att->name, att->value))
+ return false;
+
+ att = att->next;
+ ++totalAtts;
+ }
+
+ if (totalAtts != other->getNumAttributes())
+ return false;
+ }
+ else
+ {
+ const XmlAttributeNode* thisAtt = attributes;
+ const XmlAttributeNode* otherAtt = other->attributes;
+
+ for (;;)
+ {
+ if (thisAtt == 0 || otherAtt == 0)
+ {
+ if (thisAtt == otherAtt) // both 0, so it's a match
+ break;
+
+ return false;
+ }
+
+ if (thisAtt->name != otherAtt->name
+ || thisAtt->value != otherAtt->value)
+ {
+ return false;
+ }
+
+ thisAtt = thisAtt->next;
+ otherAtt = otherAtt->next;
+ }
+ }
+
+ const XmlElement* thisChild = firstChildElement;
+ const XmlElement* otherChild = other->firstChildElement;
+
+ for (;;)
+ {
+ if (thisChild == 0 || otherChild == 0)
+ {
+ if (thisChild == otherChild) // both 0, so it's a match
+ break;
+
+ return false;
+ }
+
+ if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
+ return false;
+
+ thisChild = thisChild->nextElement;
+ otherChild = otherChild->nextElement;
+ }
+ }
+
+ return true;
+}
+
+void XmlElement::deleteAllChildElements() throw()
+{
+ while (firstChildElement != 0)
+ {
+ XmlElement* const nextChild = firstChildElement->nextElement;
+ delete firstChildElement;
+ firstChildElement = nextChild;
+ }
+}
+
+void XmlElement::deleteAllChildElementsWithTagName (const tchar* const name) throw()
+{
+ XmlElement* child = firstChildElement;
+
+ while (child != 0)
+ {
+ if (child->hasTagName (name))
+ {
+ XmlElement* const nextChild = child->nextElement;
+ removeChildElement (child, true);
+ child = nextChild;
+ }
+ else
+ {
+ child = child->nextElement;
+ }
+ }
+}
+
+bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const throw()
+{
+ const XmlElement* child = firstChildElement;
+
+ while (child != 0)
+ {
+ if (child == possibleChild)
+ return true;
+
+ child = child->nextElement;
+ }
+
+ return false;
+}
+
+XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) throw()
+{
+ if (this == elementToLookFor || elementToLookFor == 0)
+ return 0;
+
+ XmlElement* child = firstChildElement;
+
+ while (child != 0)
+ {
+ if (elementToLookFor == child)
+ return this;
+
+ XmlElement* const found = child->findParentElementOf (elementToLookFor);
+
+ if (found != 0)
+ return found;
+
+ child = child->nextElement;
+ }
+
+ return 0;
+}
+
+XmlElement** XmlElement::getChildElementsAsArray (const int num) const throw()
+{
+ XmlElement** const elems = new XmlElement* [num];
+
+ XmlElement* e = firstChildElement;
+ int i = 0;
+
+ while (e != 0)
+ {
+ elems [i++] = e;
+ e = e->nextElement;
+ }
+
+ return elems;
+}
+
+void XmlElement::reorderChildElements (XmlElement** const elems, const int num) throw()
+{
+ XmlElement* e = firstChildElement = elems[0];
+
+ for (int i = 1; i < num; ++i)
+ {
+ e->nextElement = elems[i];
+ e = e->nextElement;
+ }
+
+ e->nextElement = 0;
+}
+
+bool XmlElement::isTextElement() const throw()
+{
+ return tagName.isEmpty();
+}
+
+static const tchar* const juce_xmltextContentAttributeName = T("text");
+
+const String XmlElement::getText() const throw()
+{
+ jassert (isTextElement()); // you're trying to get the text from an element that
+ // isn't actually a text element.. If this contains text sub-nodes, you
+ // can use getAllSubText instead to
+
+ return getStringAttribute (juce_xmltextContentAttributeName);
+}
+
+void XmlElement::setText (const String& newText) throw()
+{
+ if (isTextElement())
+ {
+ setAttribute (juce_xmltextContentAttributeName, newText);
+ }
+ else
+ {
+ jassertfalse // you can only change the text in a text element, not a normal one.
+ }
+}
+
+const String XmlElement::getAllSubText() const throw()
+{
+ String result;
+ const XmlElement* child = firstChildElement;
+
+ while (child != 0)
+ {
+ if (child->isTextElement())
+ result += child->getText();
+
+ child = child->nextElement;
+ }
+
+ return result;
+}
+
+const String XmlElement::getChildElementAllSubText (const tchar* const childTagName,
+ const String& defaultReturnValue) const throw()
+{
+ const XmlElement* const child = getChildByName (childTagName);
+
+ if (child != 0)
+ return child->getAllSubText();
+
+ return defaultReturnValue;
+}
+
+XmlElement* XmlElement::createTextElement (const String& text) throw()
+{
+ XmlElement* const e = new XmlElement ((int) 0);
+ e->setAttribute (juce_xmltextContentAttributeName, text);
+ return e;
+}
+
+void XmlElement::addTextElement (const String& text) throw()
+{
+ addChildElement (createTextElement (text));
+}
+
+void XmlElement::deleteAllTextElements() throw()
+{
+ XmlElement* child = firstChildElement;
+
+ while (child != 0)
+ {
+ XmlElement* const next = child->nextElement;
+
+ if (child->isTextElement())
+ removeChildElement (child, true);
+
+ child = next;
+ }
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_XmlElement.cpp *********/
+
+/********* Start of inlined file: juce_InterProcessLock.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+// (implemented in the platform-specific code files)
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_InterProcessLock.cpp *********/
+
+/********* Start of inlined file: juce_ReadWriteLock.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+ReadWriteLock::ReadWriteLock() throw()
+ : numWaitingWriters (0),
+ numWriters (0),
+ writerThreadId (0)
+{
+}
+
+ReadWriteLock::~ReadWriteLock() throw()
+{
+ jassert (readerThreads.size() == 0);
+ jassert (numWriters == 0);
+}
+
+void ReadWriteLock::enterRead() const throw()
+{
+ const int threadId = Thread::getCurrentThreadId();
+ const ScopedLock sl (accessLock);
+
+ for (;;)
+ {
+ jassert (readerThreads.size() % 2 == 0);
+
+ int i;
+ for (i = 0; i < readerThreads.size(); i += 2)
+ if (readerThreads.getUnchecked(i) == threadId)
+ break;
+
+ if (i < readerThreads.size()
+ || numWriters + numWaitingWriters == 0
+ || (threadId == writerThreadId && numWriters > 0))
+ {
+ if (i < readerThreads.size())
+ {
+ readerThreads.set (i + 1, readerThreads.getUnchecked (i + 1) + 1);
+ }
+ else
+ {
+ readerThreads.add (threadId);
+ readerThreads.add (1);
+ }
+
+ return;
+ }
+
+ const ScopedUnlock ul (accessLock);
+ waitEvent.wait (100);
+ }
+}
+
+void ReadWriteLock::exitRead() const throw()
+{
+ const int threadId = Thread::getCurrentThreadId();
+ const ScopedLock sl (accessLock);
+
+ for (int i = 0; i < readerThreads.size(); i += 2)
+ {
+ if (readerThreads.getUnchecked(i) == threadId)
+ {
+ const int newCount = readerThreads.getUnchecked (i + 1) - 1;
+
+ if (newCount == 0)
+ {
+ readerThreads.removeRange (i, 2);
+ waitEvent.signal();
+ }
+ else
+ {
+ readerThreads.set (i + 1, newCount);
+ }
+
+ return;
+ }
+ }
+
+ jassertfalse // unlocking a lock that wasn't locked..
+}
+
+void ReadWriteLock::enterWrite() const throw()
+{
+ const int threadId = Thread::getCurrentThreadId();
+ const ScopedLock sl (accessLock);
+
+ for (;;)
+ {
+ if (readerThreads.size() + numWriters == 0
+ || threadId == writerThreadId
+ || (readerThreads.size() == 2
+ && readerThreads.getUnchecked(0) == threadId))
+ {
+ writerThreadId = threadId;
+ ++numWriters;
+ break;
+ }
+
+ ++numWaitingWriters;
+ accessLock.exit();
+ waitEvent.wait (100);
+ accessLock.enter();
+ --numWaitingWriters;
+ }
+}
+
+void ReadWriteLock::exitWrite() const throw()
+{
+ const ScopedLock sl (accessLock);
+
+ // check this thread actually had the lock..
+ jassert (numWriters > 0 && writerThreadId == Thread::getCurrentThreadId());
+
+ if (--numWriters == 0)
+ {
+ writerThreadId = 0;
+ waitEvent.signal();
+ }
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_ReadWriteLock.cpp *********/
+
+/********* Start of inlined file: juce_Thread.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+// these functions are implemented in the platform-specific code.
+void* juce_createThread (void* userData) throw();
+void juce_killThread (void* handle) throw();
+void juce_setThreadPriority (void* handle, int priority) throw();
+void juce_setCurrentThreadName (const String& name) throw();
+#if JUCE_WIN32
+void juce_CloseThreadHandle (void* handle) throw();
+#endif
+
+static VoidArray runningThreads (4);
+static CriticalSection runningThreadsLock;
+
+void Thread::threadEntryPoint (Thread* const thread) throw()
+{
+ runningThreadsLock.enter();
+ runningThreads.add (thread);
+ runningThreadsLock.exit();
+
+ JUCE_TRY
+ {
+ thread->threadId_ = Thread::getCurrentThreadId();
+
+ if (thread->threadName_.isNotEmpty())
+ juce_setCurrentThreadName (thread->threadName_);
+
+ if (thread->startSuspensionEvent_.wait (10000))
+ {
+ if (thread->affinityMask_ != 0)
+ setCurrentThreadAffinityMask (thread->affinityMask_);
+
+ thread->run();
+ }
+ }
+ JUCE_CATCH_ALL_ASSERT
+
+ runningThreadsLock.enter();
+ jassert (runningThreads.contains (thread));
+ runningThreads.removeValue (thread);
+ runningThreadsLock.exit();
+
+#if JUCE_WIN32
+ juce_CloseThreadHandle (thread->threadHandle_);
+#endif
+
+ thread->threadHandle_ = 0;
+ thread->threadId_ = 0;
+}
+
+// used to wrap the incoming call from the platform-specific code
+void JUCE_API juce_threadEntryPoint (void* userData)
+{
+ Thread::threadEntryPoint ((Thread*) userData);
+}
+
+Thread::Thread (const String& threadName)
+ : threadName_ (threadName),
+ threadHandle_ (0),
+ threadPriority_ (5),
+ threadId_ (0),
+ affinityMask_ (0),
+ threadShouldExit_ (false)
+{
+}
+
+Thread::~Thread()
+{
+ stopThread (100);
+}
+
+void Thread::startThread() throw()
+{
+ const ScopedLock sl (startStopLock);
+
+ threadShouldExit_ = false;
+
+ if (threadHandle_ == 0)
+ {
+ threadHandle_ = juce_createThread ((void*) this);
+ juce_setThreadPriority (threadHandle_, threadPriority_);
+ startSuspensionEvent_.signal();
+ }
+}
+
+void Thread::startThread (const int priority) throw()
+{
+ const ScopedLock sl (startStopLock);
+
+ if (threadHandle_ == 0)
+ {
+ threadPriority_ = priority;
+ startThread();
+ }
+ else
+ {
+ setPriority (priority);
+ }
+}
+
+bool Thread::isThreadRunning() const throw()
+{
+ return threadHandle_ != 0;
+}
+
+void Thread::signalThreadShouldExit() throw()
+{
+ threadShouldExit_ = true;
+}
+
+bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const throw()
+{
+ // Doh! So how exactly do you expect this thread to wait for itself to stop??
+ jassert (getThreadId() != getCurrentThreadId());
+
+ const int sleepMsPerIteration = 5;
+ int count = timeOutMilliseconds / sleepMsPerIteration;
+
+ while (isThreadRunning())
+ {
+ if (timeOutMilliseconds > 0 && --count < 0)
+ return false;
+
+ sleep (sleepMsPerIteration);
+ }
+
+ return true;
+}
+
+void Thread::stopThread (const int timeOutMilliseconds) throw()
+{
+ // agh! You can't stop the thread that's calling this method! How on earth
+ // would that work??
+ jassert (getCurrentThreadId() != getThreadId());
+
+ const ScopedLock sl (startStopLock);
+
+ if (isThreadRunning())
+ {
+ signalThreadShouldExit();
+ notify();
+
+ if (timeOutMilliseconds != 0)
+ waitForThreadToExit (timeOutMilliseconds);
+
+ if (isThreadRunning())
+ {
+ // very bad karma if this point is reached, as
+ // there are bound to be locks and events left in
+ // silly states when a thread is killed by force..
+ jassertfalse
+ Logger::writeToLog ("!! killing thread by force !!");
+
+ juce_killThread (threadHandle_);
+ threadHandle_ = 0;
+ threadId_ = 0;
+
+ const ScopedLock sl (runningThreadsLock);
+ runningThreads.removeValue (this);
+ }
+ }
+}
+
+void Thread::setPriority (const int priority) throw()
+{
+ const ScopedLock sl (startStopLock);
+
+ threadPriority_ = priority;
+ juce_setThreadPriority (threadHandle_, priority);
+}
+
+void Thread::setCurrentThreadPriority (const int priority) throw()
+{
+ juce_setThreadPriority (0, priority);
+}
+
+void Thread::setAffinityMask (const uint32 affinityMask) throw()
+{
+ affinityMask_ = affinityMask;
+}
+
+int Thread::getThreadId() const throw()
+{
+ return threadId_;
+}
+
+bool Thread::wait (const int timeOutMilliseconds) const throw()
+{
+ return defaultEvent_.wait (timeOutMilliseconds);
+}
+
+void Thread::notify() const throw()
+{
+ defaultEvent_.signal();
+}
+
+int Thread::getNumRunningThreads() throw()
+{
+ return runningThreads.size();
+}
+
+Thread* Thread::getCurrentThread() throw()
+{
+ const int thisId = getCurrentThreadId();
+ Thread* result = 0;
+
+ runningThreadsLock.enter();
+
+ for (int i = runningThreads.size(); --i >= 0;)
+ {
+ Thread* const t = (Thread*) (runningThreads.getUnchecked(i));
+
+ if (t->threadId_ == thisId)
+ {
+ result = t;
+ break;
+ }
+ }
+
+ runningThreadsLock.exit();
+
+ return result;
+}
+
+void Thread::stopAllThreads (const int timeOutMilliseconds) throw()
+{
+ runningThreadsLock.enter();
+
+ for (int i = runningThreads.size(); --i >= 0;)
+ ((Thread*) runningThreads.getUnchecked(i))->signalThreadShouldExit();
+
+ runningThreadsLock.exit();
+
+ for (;;)
+ {
+ runningThreadsLock.enter();
+ Thread* const t = (Thread*) runningThreads[0];
+ runningThreadsLock.exit();
+
+ if (t == 0)
+ break;
+
+ t->stopThread (timeOutMilliseconds);
+ }
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_Thread.cpp *********/
+
+/********* Start of inlined file: juce_ThreadPool.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+ThreadPoolJob::ThreadPoolJob (const String& name)
+ : jobName (name),
+ pool (0),
+ shouldStop (false),
+ isActive (false),
+ shouldBeDeleted (false)
+{
+}
+
+ThreadPoolJob::~ThreadPoolJob()
+{
+ // you mustn't delete a job while it's still in a pool! Use ThreadPool::removeJob()
+ // to remove it first!
+ jassert (pool == 0 || ! pool->contains (this));
+}
+
+const String ThreadPoolJob::getJobName() const
+{
+ return jobName;
+}
+
+void ThreadPoolJob::setJobName (const String& newName)
+{
+ jobName = newName;
+}
+
+void ThreadPoolJob::signalJobShouldExit()
+{
+ shouldStop = true;
+}
+
+class ThreadPoolThread : public Thread
+{
+ ThreadPool& pool;
+ bool volatile busy;
+
+ ThreadPoolThread (const ThreadPoolThread&);
+ const ThreadPoolThread& operator= (const ThreadPoolThread&);
+
+public:
+ ThreadPoolThread (ThreadPool& pool_)
+ : Thread (T("Pool")),
+ pool (pool_),
+ busy (false)
+ {
+ }
+
+ ~ThreadPoolThread()
+ {
+ }
+
+ void run()
+ {
+ while (! threadShouldExit())
+ {
+ if (! pool.runNextJob())
+ wait (500);
+ }
+ }
+};
+
+ThreadPool::ThreadPool (const int numThreads_,
+ const bool startThreadsOnlyWhenNeeded,
+ const int stopThreadsWhenNotUsedTimeoutMs)
+ : numThreads (jmax (1, numThreads_)),
+ threadStopTimeout (stopThreadsWhenNotUsedTimeoutMs),
+ priority (5)
+{
+ jassert (numThreads_ > 0); // not much point having one of these with no threads in it.
+
+ threads = (Thread**) juce_calloc (sizeof (Thread*) * numThreads);
+
+ for (int i = numThreads; --i >= 0;)
+ {
+ threads[i] = new ThreadPoolThread (*this);
+
+ if (! startThreadsOnlyWhenNeeded)
+ threads[i]->startThread();
+ }
+}
+
+ThreadPool::~ThreadPool()
+{
+ removeAllJobs (true, 4000);
+
+ int i;
+ for (i = numThreads; --i >= 0;)
+ threads[i]->signalThreadShouldExit();
+
+ for (i = numThreads; --i >= 0;)
+ {
+ threads[i]->stopThread (500);
+ delete threads[i];
+ }
+
+ juce_free (threads);
+}
+
+void ThreadPool::addJob (ThreadPoolJob* const job)
+{
+ jassert (job->pool == 0);
+
+ if (job->pool == 0)
+ {
+ job->pool = this;
+ job->shouldStop = false;
+ job->isActive = false;
+
+ lock.enter();
+ jobs.add (job);
+
+ int numRunning = 0;
+
+ int i;
+ for (i = numThreads; --i >= 0;)
+ if (threads[i]->isThreadRunning() && ! threads[i]->threadShouldExit())
+ ++numRunning;
+
+ if (numRunning < numThreads)
+ {
+ bool startedOne = false;
+ int n = 1000;
+
+ while (--n >= 0 && ! startedOne)
+ {
+ for (int i = numThreads; --i >= 0;)
+ {
+ if (! threads[i]->isThreadRunning())
+ {
+ threads[i]->startThread();
+ startedOne = true;
+ }
+ }
+
+ if (! startedOne)
+ Thread::sleep (5);
+ }
+ }
+
+ lock.exit();
+
+ for (i = numThreads; --i >= 0;)
+ threads[i]->notify();
+ }
+}
+
+int ThreadPool::getNumJobs() const throw()
+{
+ return jobs.size();
+}
+
+ThreadPoolJob* ThreadPool::getJob (const int index) const
+{
+ const ScopedLock sl (lock);
+ return (ThreadPoolJob*) jobs [index];
+}
+
+bool ThreadPool::contains (const ThreadPoolJob* const job) const throw()
+{
+ const ScopedLock sl (lock);
+
+ return jobs.contains ((void*) job);
+}
+
+bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const
+{
+ const ScopedLock sl (lock);
+
+ return jobs.contains ((void*) job) && job->isActive;
+}
+
+bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job,
+ const int timeOutMs) const
+{
+ if (job != 0)
+ {
+ const uint32 start = Time::getMillisecondCounter();
+
+ while (contains (job))
+ {
+ if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + timeOutMs)
+ return false;
+
+ Thread::sleep (2);
+ }
+ }
+
+ return true;
+}
+
+bool ThreadPool::removeJob (ThreadPoolJob* const job,
+ const bool interruptIfRunning,
+ const int timeOutMs)
+{
+ if (job != 0)
+ {
+ lock.enter();
+
+ if (jobs.contains (job))
+ {
+ if (job->isActive)
+ {
+ if (interruptIfRunning)
+ job->signalJobShouldExit();
+
+ lock.exit();
+
+ return waitForJobToFinish (job, timeOutMs);
+ }
+ else
+ {
+ jobs.removeValue (job);
+ }
+ }
+
+ lock.exit();
+ }
+
+ return true;
+}
+
+bool ThreadPool::removeAllJobs (const bool interruptRunningJobs,
+ const int timeOutMs)
+{
+ lock.enter();
+
+ for (int i = jobs.size(); --i >= 0;)
+ {
+ ThreadPoolJob* const job = (ThreadPoolJob*) jobs.getUnchecked(i);
+
+ if (job->isActive)
+ {
+ if (interruptRunningJobs)
+ job->signalJobShouldExit();
+ }
+ else
+ {
+ jobs.remove (i);
+ }
+ }
+
+ lock.exit();
+
+ const uint32 start = Time::getMillisecondCounter();
+
+ while (jobs.size() > 0)
+ {
+ if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + timeOutMs)
+ return false;
+
+ Thread::sleep (2);
+ }
+
+ return true;
+}
+
+const StringArray ThreadPool::getNamesOfAllJobs (const bool onlyReturnActiveJobs) const
+{
+ StringArray s;
+ const ScopedLock sl (lock);
+
+ for (int i = 0; i < jobs.size(); ++i)
+ {
+ const ThreadPoolJob* const job = (const ThreadPoolJob*) jobs.getUnchecked(i);
+ if (job->isActive || ! onlyReturnActiveJobs)
+ s.add (job->getJobName());
+ }
+
+ return s;
+}
+
+void ThreadPool::setThreadPriorities (const int newPriority)
+{
+ if (priority != newPriority)
+ {
+ priority = newPriority;
+
+ for (int i = numThreads; --i >= 0;)
+ threads[i]->setPriority (newPriority);
+ }
+}
+
+bool ThreadPool::runNextJob()
+{
+ lock.enter();
+ ThreadPoolJob* job = 0;
+
+ for (int i = 0; i < jobs.size(); ++i)
+ {
+ job = (ThreadPoolJob*) jobs [i];
+
+ if (job != 0 && ! (job->isActive || job->shouldStop))
+ break;
+
+ job = 0;
+ }
+
+ if (job != 0)
+ {
+ job->isActive = true;
+ lock.exit();
+
+ JUCE_TRY
+ {
+ ThreadPoolJob::JobStatus result = job->runJob();
+
+ lastJobEndTime = Time::getApproximateMillisecondCounter();
+
+ const ScopedLock sl (lock);
+
+ if (jobs.contains (job))
+ {
+ job->isActive = false;
+
+ if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop)
+ {
+ job->pool = 0;
+ job->shouldStop = true;
+ jobs.removeValue (job);
+
+ if (result == ThreadPoolJob::jobHasFinishedAndShouldBeDeleted)
+ delete job;
+ }
+ else
+ {
+ // move the job to the end of the queue if it wants another go
+ jobs.move (jobs.indexOf (job), -1);
+ }
+ }
+ }
+#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
+ catch (...)
+ {
+ lock.enter();
+ jobs.removeValue (job);
+ lock.exit();
+ }
+#endif
+ }
+ else
+ {
+ lock.exit();
+
+ if (threadStopTimeout > 0
+ && Time::getApproximateMillisecondCounter() > lastJobEndTime + threadStopTimeout)
+ {
+ lock.enter();
+
+ if (jobs.size() == 0)
+ {
+ for (int i = numThreads; --i >= 0;)
+ threads[i]->signalThreadShouldExit();
+ }
+
+ lock.exit();
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_ThreadPool.cpp *********/
+
+/********* Start of inlined file: juce_TimeSliceThread.cpp *********/
+
+BEGIN_JUCE_NAMESPACE
+
+TimeSliceThread::TimeSliceThread (const String& threadName)
+ : Thread (threadName),
+ index (0),
+ clientBeingCalled (0),
+ clientsChanged (false)
+{
+}
+
+TimeSliceThread::~TimeSliceThread()
+{
+ stopThread (2000);
+}
+
+void TimeSliceThread::addTimeSliceClient (TimeSliceClient* const client)
+{
+ const ScopedLock sl (listLock);
+ clients.addIfNotAlreadyThere (client);
+ clientsChanged = true;
+ notify();
+}
+
+void TimeSliceThread::removeTimeSliceClient (TimeSliceClient* const client)
+{
+ const ScopedLock sl1 (listLock);
+ clientsChanged = true;
+
+ // if there's a chance we're in the middle of calling this client, we need to
+ // also lock the outer lock..
+ if (clientBeingCalled == client)
+ {
+ const ScopedUnlock ul (listLock); // unlock first to get the order right..
+
+ const ScopedLock sl1 (callbackLock);
+ const ScopedLock sl2 (listLock);
+
+ clients.removeValue (client);
+ }
+ else
+ {
+ clients.removeValue (client);
+ }
+}
+
+int TimeSliceThread::getNumClients() const throw()
+{
+ return clients.size();
+}
+
+TimeSliceClient* TimeSliceThread::getClient (const int index) const throw()
+{
+ const ScopedLock sl (listLock);
+ return clients [index];
+}
+
+void TimeSliceThread::run()
+{
+ int numCallsSinceBusy = 0;
+
+ while (! threadShouldExit())
+ {
+ int timeToWait = 500;
+
+ {
+ const ScopedLock sl (callbackLock);
+
+ {
+ const ScopedLock sl (listLock);
+
+ if (clients.size() > 0)
+ {
+ index = (index + 1) % clients.size();
+
+ clientBeingCalled = clients [index];
+ }
+ else
+ {
+ index = 0;
+ clientBeingCalled = 0;
+ }
+
+ if (clientsChanged)
+ {
+ clientsChanged = false;
+ numCallsSinceBusy = 0;
+ }
+ }
+
+ if (clientBeingCalled != 0)
+ {
+ if (clientBeingCalled->useTimeSlice())
+ numCallsSinceBusy = 0;
+ else
+ ++numCallsSinceBusy;
+
+ if (numCallsSinceBusy >= clients.size())
+ timeToWait = 500;
+ else if (index == 0)
+ timeToWait = 1; // throw in an occasional pause, to stop everything locking up
+ else
+ timeToWait = 0;
+ }
+ }
+
+ if (timeToWait > 0)
+ wait (timeToWait);
+ }
+}
+
+END_JUCE_NAMESPACE
+/********* End of inlined file: juce_TimeSliceThread.cpp *********/
+
+#if ! JUCE_ONLY_BUILD_CORE_LIBRARY
+
+/********* Start of inlined file: juce_Application.cpp *********/
+
+#if JUCE_MSVC
+ #pragma warning (push)
+ #pragma warning (disable: 4245 4514 4100)
+ #include
+ #pragma warning (pop)
+#endif
+
+BEGIN_JUCE_NAMESPACE
+
+void juce_setCurrentExecutableFileName (const String& filename) throw();
+void juce_setCurrentThreadName (const String& name) throw();
+
+static JUCEApplication* appInstance = 0;
+
+JUCEApplication::JUCEApplication()
+ : appReturnValue (0),
+ stillInitialising (true)
+{
+}
+
+JUCEApplication::~JUCEApplication()
+{
+}
+
+JUCEApplication* JUCEApplication::getInstance() throw()
+{
+ return appInstance;
+}
+
+bool JUCEApplication::isInitialising() const throw()
+{
+ return stillInitialising;
+}
+
+const String JUCEApplication::getApplicationVersion()
+{
+ return String::empty;
+}
+
+bool JUCEApplication::moreThanOneInstanceAllowed()
+{
+ return true;
+}
+
+void JUCEApplication::anotherInstanceStarted (const String&)
+{
+}
+
+void JUCEApplication::systemRequestedQuit()
+{
+ quit();
+}
+
+void JUCEApplication::quit (const bool useMaximumForce)
+{
+ MessageManager::getInstance()->postQuitMessage (useMaximumForce);
+}
+
+void JUCEApplication::setApplicationReturnValue (const int newReturnValue) throw()
+{
+ appReturnValue = newReturnValue;
+}
+
+void JUCEApplication::unhandledException (const std::exception*,
+ const String&,
+ const int)
+{
+ jassertfalse
+}
+
+void JUCEApplication::sendUnhandledException (const std::exception* const e,
+ const char* const sourceFile,
+ const int lineNumber)
+{
+ if (appInstance != 0)
+ appInstance->unhandledException (e, sourceFile, lineNumber);
+}
+
+ApplicationCommandTarget* JUCEApplication::getNextCommandTarget()
+{
+ return 0;
+}
+
+void JUCEApplication::getAllCommands (Array & commands)
+{
+ commands.add (StandardApplicationCommandIDs::quit);
+}
+
+void JUCEApplication::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
+{
+ if (commandID == StandardApplicationCommandIDs::quit)
+ {
+ result.setInfo ("Quit",
+ "Quits the application",
+ "Application",
+ 0);
+
+ result.defaultKeypresses.add (KeyPress (T('q'), ModifierKeys::commandModifier, 0));
+ }
+}
+
+bool JUCEApplication::perform (const InvocationInfo& info)
+{
+ if (info.commandID == StandardApplicationCommandIDs::quit)
+ {
+ systemRequestedQuit();
+ return true;
+ }
+
+ return false;
+}
+
+int JUCEApplication::main (String& commandLine, JUCEApplication* const app)
+{
+ jassert (appInstance == 0);
+ appInstance = app;
+ bool useForce = true;
+
+ initialiseJuce_GUI();
+
+ InterProcessLock* appLock = 0;
+
+ if (! app->moreThanOneInstanceAllowed())
+ {
+ appLock = new InterProcessLock ("juceAppLock_" + app->getApplicationName());
+
+ if (! appLock->enter(0))
+ {
+ MessageManager::broadcastMessage (app->getApplicationName() + "/" + commandLine);
+
+ delete appInstance;
+ appInstance = 0;
+ commandLine = String::empty;
+
+ DBG ("Another instance is running - quitting...");
+ return 0;
+ }
+ }
+
+ JUCE_TRY
+ {
+ juce_setCurrentThreadName ("Juce Message Thread");
+
+ // let the app do its setting-up..
+ app->initialise (commandLine.trim());
+
+ commandLine = String::empty;
+
+ // register for broadcast new app messages
+ MessageManager::getInstance()->registerBroadcastListener (app);
+
+ app->stillInitialising = false;
+
+ // now loop until a quit message is received..
+ useForce = MessageManager::getInstance()->runDispatchLoop();
+
+ MessageManager::getInstance()->deregisterBroadcastListener (app);
+
+ if (appLock != 0)
+ {
+ appLock->exit();
+ delete appLock;
+ }
+ }
+#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
+ catch (const std::exception& e)
+ {
+ app->unhandledException (&e, __FILE__, __LINE__);
+ }
+ catch (...)
+ {
+ app->unhandledException (0, __FILE__, __LINE__);
+ }
+#endif
+
+ return shutdownAppAndClearUp (useForce);
+}
+
+int JUCEApplication::shutdownAppAndClearUp (const bool useMaximumForce)
+{
+ jassert (appInstance != 0);
+ JUCEApplication* const app = appInstance;
+ int returnValue = 0;
+
+ static bool reentrancyCheck = false;
+
+ if (! reentrancyCheck)
+ {
+ reentrancyCheck = true;
+
+ JUCE_TRY
+ {
+ // give the app a chance to clean up..
+ app->shutdown();
+ }
+#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
+ catch (const std::exception& e)
+ {
+ app->unhandledException (&e, __FILE__, __LINE__);
+ }
+ catch (...)
+ {
+ app->unhandledException (0, __FILE__, __LINE__);
+ }
+#endif
+
+ JUCE_TRY
+ {
+ shutdownJuce_GUI();
+
+ returnValue = app->getApplicationReturnValue();
+
+ appInstance = 0;
+ delete app;
+ }
+ JUCE_CATCH_ALL_ASSERT
+
+ if (useMaximumForce)
+ {
+ Process::terminate();
+ }
+
+ reentrancyCheck = false;
+ }
+
+ return returnValue;
+}
+
+int JUCEApplication::main (int argc, char* argv[],
+ JUCEApplication* const newApp)
+{
+ juce_setCurrentExecutableFileName (argv[0]);
+
+ String cmd;
+ for (int i = 1; i < argc; ++i)
+ cmd << argv[i] << T(' ');
+
+ return JUCEApplication::main (cmd, newApp);
+}
+
+void JUCEApplication::actionListenerCallback (const String& message)
+{
+ if (message.startsWith (getApplicationName() + "/"))
+ anotherInstanceStarted (message.substring (getApplicationName().length() + 1));
+}
+
+static bool juceInitialisedGUI = false;
+
+void JUCE_PUBLIC_FUNCTION initialiseJuce_GUI()
+{
+ if (! juceInitialisedGUI)
+ {
+ juceInitialisedGUI = true;
+
+ initialiseJuce_NonGUI();
+ MessageManager::getInstance();
+ Font::initialiseDefaultFontNames();
+ LookAndFeel::setDefaultLookAndFeel (0);
+
+#if JUCE_WIN32 && JUCE_DEBUG
+ // This section is just for catching people who mess up their project settings and
+ // turn RTTI off..
+ try
+ {
+ TextButton tb (String::empty);
+ Component* c = &tb;
+
+ // Got an exception here? Then TURN ON RTTI in your compiler settings!!
+ c = dynamic_cast