Initial source commit

Initial source commit
This commit is contained in:
mfeemster
2014-07-08 00:11:14 -07:00
parent 1493c22925
commit ef56c16b2b
214 changed files with 108754 additions and 0 deletions

View File

@ -0,0 +1,261 @@
#pragma once
#include "EmberCommonPch.h"
/// <summary>
/// Global utility classes and functions that are common to all programs that use
/// Ember and its derivatives.
/// </summary>
/// <summary>
/// Derivation of the RenderCallback class to do custom printing action
/// whenever the progress function is internally called inside of Ember
/// and its derivatives.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class RenderProgress : public RenderCallback
{
public:
/// <summary>
/// Constructor that initializes the state to zero.
/// </summary>
RenderProgress()
{
Clear();
}
/// <summary>
/// The progress function which will be called from inside the renderer.
/// </summary>
/// <param name="ember">The ember currently being rendered</param>
/// <param name="foo">An extra dummy parameter</param>
/// <param name="fraction">The progress fraction from 0-100</param>
/// <param name="stage">The stage of iteration. 1 is iterating, 2 is density filtering, 2 is final accumulation.</param>
/// <param name="etaMs">The estimated milliseconds to completion of the current stage</param>
/// <returns>1 since this is intended to run in an environment where the render runs to completion, unlike interactive rendering.</returns>
virtual int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs)
{
if (stage == 0 || stage == 1)
{
if (m_LastStage != stage)
cout << endl;
cout << "\r" << string(m_S.length(), ' ');//Clear what was previously here.
m_SS.str("");//Begin new output.
m_SS << "\rStage = " << (stage ? "filtering" : "chaos");
m_SS << ", progress = " << int(fraction) << "%";
m_SS << ", eta = " << t.Format(etaMs);
m_S = m_SS.str();
cout << m_S;
}
m_LastStage = stage;
return 1;
}
/// <summary>
/// Reset the state.
/// </summary>
void Clear()
{
m_LastStage = 0;
m_LastLength = 0;
m_SS.clear();
m_S.clear();
}
private:
int m_LastStage;
int m_LastLength;
stringstream m_SS;
string m_S;
Timing t;
};
/// <summary>
/// Wrapper for parsing an ember Xml file, storing the embers in a vector and printing
/// any errors that occurred.
/// Template argument expected to be float or double.
/// </summary>
/// <param name="parser">The parser to use</param>
/// <param name="filename">The full path and name of the file</param>
/// <param name="embers">Storage for the embers read from the file</param>
/// <returns>True if success, else false.</returns>
template <typename T>
static bool ParseEmberFile(XmlToEmber<T>& parser, string filename, vector<Ember<T>>& embers)
{
if (!parser.Parse(filename.c_str(), embers))
{
cout << "Error parsing flame file " << filename << ", returning without executing." << endl;
return false;
}
if (embers.empty())
{
cout << "Error: No data present in file " << filename << ". Aborting." << endl;
return false;
}
return true;
}
/// <summary>
/// Wrapper for parsing palette Xml file and initializing it's private static members,
/// and printing any errors that occurred.
/// Template argument expected to be float or double.
/// </summary>
/// <param name="filename">The full path and name of the file</param>
/// <returns>True if success, else false.</returns>
template <typename T>
static bool InitPaletteList(string filename)
{
PaletteList<T> paletteList;//Even though this is local, the members are static so they will remain.
if (!paletteList.Init(filename))
{
cout << "Error parsing palette file " << filename << ". Reason: " << endl;
cout << paletteList.ErrorReportString() << endl << "Returning without executing." << endl;
return false;
}
return true;
}
/// <summary>
/// Calculate the number of strips required if the needed amount of memory
/// is greater than the system memory, or greater than what the user want to allow.
/// </summary>
/// <param name="mem">Amount of memory required</param>
/// <param name="useMem">The maximum amount of memory to use. Use max if 0.</param>
/// <returns>The number of strips to use</returns>
static unsigned int CalcStrips(double mem, double memAvailable, double useMem)
{
unsigned int strips;
double memRequired;
if (useMem > 0)
memAvailable = useMem;
else
memAvailable *= 0.8;
memRequired = mem;
if (memAvailable >= memRequired)
return 1;
strips = (unsigned int)ceil(memRequired / memAvailable);
return strips;
}
/// <summary>
/// Given a numerator and a denominator, find the next highest denominator that divides
/// evenly into the numerator.
/// </summary>
/// <param name="numerator">The numerator</param>
/// <param name="denominator">The denominator</param>
/// <returns>The next highest divisor if found, else 1.</returns>
static unsigned int NextHighestEvenDiv(unsigned int numerator, unsigned int denominator)
{
unsigned int result = 1;
unsigned int numDiv2 = numerator / 2;
do
{
denominator++;
if (numerator % denominator == 0)
{
result = denominator;
break;
}
}
while (denominator <= numDiv2);
return result;
}
/// <summary>
/// Given a numerator and a denominator, find the next lowest denominator that divides
/// evenly into the numerator.
/// </summary>
/// <param name="numerator">The numerator</param>
/// <param name="denominator">The denominator</param>
/// <returns>The next lowest divisor if found, else 1.</returns>
static unsigned int NextLowestEvenDiv(unsigned int numerator, unsigned int denominator)
{
unsigned int result = 1;
unsigned int numDiv2 = numerator / 2;
denominator--;
if (denominator > numDiv2)
denominator = numDiv2;
while (denominator >= 1)
{
if (numerator % denominator == 0)
{
result = denominator;
break;
}
denominator--;
}
return result;
}
/// <summary>
/// Wrapper for creating a renderer of the specified type.
/// First template argument expected to be float or double for CPU renderer,
/// Second argument expected to be float or double for CPU renderer, and only float for OpenCL renderer.
/// </summary>
/// <param name="renderType">Type of renderer to create</param>
/// <param name="platform">The index platform of the platform to use</param>
/// <param name="device">The index device of the device to use</param>
/// <param name="shared">True if shared with OpenGL, else false.</param>
/// <param name="texId">The texture ID of the shared OpenGL texture if shared</param>
/// <param name="errorReport">The error report for holding errors if anything goes wrong</param>
/// <returns>A pointer to the created renderer if successful, else false.</returns>
template <typename T, typename bucketT>
static Renderer<T, bucketT>* CreateRenderer(eRendererType renderType, unsigned int platform, unsigned int device, bool shared, GLuint texId, EmberReport& errorReport)
{
string s;
auto_ptr<Renderer<T, bucketT>> renderer;
try
{
if (renderType == CPU_RENDERER)
{
s = "CPU";
renderer = auto_ptr<Renderer<T, bucketT>>(new Renderer<T, bucketT>());
}
else if (renderType == OPENCL_RENDERER)
{
s = "OpenCL";
renderer = auto_ptr<Renderer<T, bucketT>>(new RendererCL<T>(platform, device, shared, texId));
if (!renderer.get() || !renderer->Ok())
{
if (renderer.get())
errorReport.AddToReport(renderer->ErrorReport());
errorReport.AddToReport("Error initializing OpenCL renderer, using CPU renderer instead.");
renderer = auto_ptr<Renderer<T, bucketT>>(new Renderer<T, bucketT>());
}
}
}
catch (...)
{
errorReport.AddToReport("Error creating " + s + " renderer.\n");
}
return renderer.release();
}
/// <summary>
/// Simple macro to print a string if the --verbose options has been specified.
/// </summary>
#define VerbosePrint(s) if (opt.Verbose()) cout << s << endl

View File

@ -0,0 +1 @@
#include "EmberCommonPch.h"

View File

@ -0,0 +1,54 @@
#pragma once
/// <summary>
/// Precompiled header file. Place all system includes here with appropriate #defines for different operating systems and compilers.
/// </summary>
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN//Exclude rarely-used stuff from Windows headers.
#define _USE_MATH_DEFINES
#ifdef _WIN32
#include <SDKDDKVer.h>
#include <windows.h>
#include <winsock.h>//For htons().
#define snprintf _snprintf
#else
#include <arpa/inet.h>
#endif
#include <BaseTsd.h>
#include <crtdbg.h>
#include <iostream>
#include <iomanip>
#include <ostream>
#include <sstream>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include "jconfig.h"
#include "jpeglib.h"
#include "png.h"
//#include "pnginfo.h"
//Ember.
#include "Ember.h"
#include "Variation.h"
#include "EmberToXml.h"
#include "XmlToEmber.h"
#include "PaletteList.h"
#include "Iterator.h"
#include "Renderer.h"
#include "RendererCL.h"
#include "SheepTools.h"
//Options.
#include "SimpleGlob.h"
#include "SimpleOpt.h"
using namespace EmberNs;
using namespace EmberCLns;

View File

@ -0,0 +1,705 @@
#pragma once
#include "EmberCommon.h"
/// <summary>
/// EmberOptionEntry and EmberOptions classes.
/// </summary>
static char* DescriptionString = "Ember - Fractal flames C++ port and enhancement with OpenCL GPU support";
/// <summary>
/// Enum for specifying which command line programs an option is meant to be used with.
/// If an option is used with multiple programs, their values are ORed together.
/// </summary>
enum eOptionUse
{
OPT_USE_RENDER = 1,
OPT_USE_ANIMATE = 1 << 1,
OPT_USE_GENOME = 1 << 2,
OPT_RENDER_ANIM = OPT_USE_RENDER | OPT_USE_ANIMATE,
OPT_ANIM_GENOME = OPT_USE_ANIMATE | OPT_USE_GENOME,
OPT_USE_ALL = OPT_USE_RENDER | OPT_USE_ANIMATE | OPT_USE_GENOME
};
/// <summary>
/// Unique identifiers for every available option across all programs.
/// </summary>
enum eOptionIDs
{
//Diagnostic args.
OPT_HELP,
OPT_VERSION,
OPT_VERBOSE,
OPT_DEBUG,
OPT_DUMP_ARGS,
OPT_PROGRESS,
OPT_DUMP_OPENCL_INFO,
//Boolean args.
OPT_OPENCL,
OPT_EARLYCLIP,
OPT_TRANSPARENCY,
OPT_NAME_ENABLE,
OPT_INT_PALETTE,
OPT_HEX_PALETTE,
OPT_INSERT_PALETTE,
OPT_JPEG_COMMENTS,
OPT_PNG_COMMENTS,
OPT_WRITE_GENOME,
OPT_ENCLOSED,
OPT_NO_EDITS,
OPT_UNSMOOTH_EDGE,
OPT_LOCK_ACCUM,
OPT_DUMP_KERNEL,
//Value args.
OPT_OPENCL_PLATFORM,//Int value args.
OPT_OPENCL_DEVICE,
OPT_SEED,
OPT_NTHREADS,
OPT_STRIPS,
OPT_BITS,
OPT_BPC,
OPT_SBS,
OPT_PRINT_EDIT_DEPTH,
OPT_JPEG,
OPT_BEGIN,
OPT_END,
OPT_FRAME,
OPT_TIME,
OPT_DTIME,
OPT_NFRAMES,
OPT_SYMMETRY,
OPT_SHEEP_GEN,
OPT_SHEEP_ID,
OPT_LOOPS,
OPT_REPEAT,
OPT_TRIES,
OPT_MAX_XFORMS,
OPT_SS,//Float value args.
OPT_QS,
OPT_PIXEL_ASPECT,
OPT_STAGGER,
OPT_AVG_THRESH,
OPT_BLACK_THRESH,
OPT_WHITE_LIMIT,
OPT_SPEED,
OPT_OFFSETX,
OPT_OFFSETY,
OPT_USEMEM,
OPT_ISAAC_SEED,//String value args.
OPT_IN,
OPT_OUT,
OPT_PREFIX,
OPT_SUFFIX,
OPT_FORMAT,
OPT_PALETTE_FILE,
OPT_PALETTE_IMAGE,
OPT_ID,
OPT_URL,
OPT_NICK,
OPT_COMMENT,
OPT_TEMPLATE,
OPT_CLONE,
OPT_CLONE_ALL,
OPT_CLONE_ACTION,
OPT_ANIMATE,
OPT_MUTATE,
OPT_CROSS0,
OPT_CROSS1,
OPT_METHOD,
OPT_INTER,
OPT_ROTATE,
OPT_STRIP,
OPT_SEQUENCE,
OPT_USE_VARS,
OPT_DONT_USE_VARS,
OPT_EXTRAS
};
class EmberOptions;
/// <summary>
/// A single option.
/// Template argument expected to be bool, int, unsigned int, double or string.
/// </summary>
template <typename T>
class EmberOptionEntry
{
friend class EmberOptions;
private:
/// <summary>
/// Default constructor. This should never be used, instead use the one that takes arguments.
/// </summary>
EmberOptionEntry()
{
m_OptionUse = OPT_USE_ALL;
m_Option.nArgType = SO_NONE;
m_Option.nId = 0;
m_Option.pszArg = _T("--fillmein");
m_DocString = "Dummy doc";
}
public:
/// <summary>
/// Constructor that takes arguments.
/// </summary>
/// <param name="optUsage">The specified program usage</param>
/// <param name="optId">The option identifier enum</param>
/// <param name="arg">The command line argument (--arg)</param>
/// <param name="defaultVal">The default value to use the option was not given on the command line</param>
/// <param name="argType">The format the argument should be given in</param>
/// <param name="docString">The documentation string describing what the argument means</param>
EmberOptionEntry(eOptionUse optUsage, eOptionIDs optId, const CharT* arg, T defaultVal, ESOArgType argType, string docString)
{
m_OptionUse = optUsage;
m_Option.nId = (int)optId;
m_Option.pszArg = arg;
m_Option.nArgType = argType;
m_DocString = docString;
m_NameWithoutDashes = Trim(string(arg), '-');
m_Val = Arg<T>((char*)m_NameWithoutDashes.c_str(), defaultVal);
}
/// <summary>
/// Copy constructor.
/// </summary>
/// <param name="entry">The EmberOptionEntry object to copy</param>
EmberOptionEntry(const EmberOptionEntry& entry)
{
*this = entry;
}
/// <summary>
/// Functor accessors.
/// </summary>
inline T operator() (void) { return m_Val; }
inline void operator() (T t) { m_Val = t; }
private:
eOptionUse m_OptionUse;
CSimpleOpt::SOption m_Option;
string m_DocString;
string m_NameWithoutDashes;
T m_Val;
};
/// <summary>
/// Macros for setting up and parsing various option types.
/// </summary>
//Bool.
#define Eob EmberOptionEntry<bool>
#define INITBOOLOPTION(member, option) \
member = option; \
m_BoolArgs.push_back(&member)
#define PARSEBOOLOPTION(opt, member) \
case (opt): \
member(true); \
break
//Int.
#define Eoi EmberOptionEntry<int>
#define INITINTOPTION(member, option) \
member = option; \
m_IntArgs.push_back(&member)
#define PARSEINTOPTION(opt, member) \
case (opt): \
sscanf_s(args.OptionArg(), "%d", &member.m_Val); \
break
//Uint.
#define Eou EmberOptionEntry<unsigned int>
#define INITUINTOPTION(member, option) \
member = option; \
m_UintArgs.push_back(&member)
#define PARSEUINTOPTION(opt, member) \
case (opt): \
sscanf_s(args.OptionArg(), "%u", &member.m_Val); \
break
//Double.
#define Eod EmberOptionEntry<double>
#define INITDOUBLEOPTION(member, option) \
member = option; \
m_DoubleArgs.push_back(&member)
#define PARSEDOUBLEOPTION(opt, member) \
case (opt): \
sscanf_s(args.OptionArg(), "%lf", &member.m_Val); \
break
//String.
#define Eos EmberOptionEntry<string>
#define INITSTRINGOPTION(member, option) \
member = option; \
m_StringArgs.push_back(&member)
#define PARSESTRINGOPTION(opt, member) \
case (opt): \
member.m_Val = args.OptionArg(); \
break
/// <summary>
/// Class for holding all available options across all command line programs.
/// Some are used only for a single program, while others are used for more than one.
/// This prevents having to keep separate documentation strings around for different programs.
/// </summary>
class EmberOptions
{
public:
/// <summary>
/// Constructor that populates all available options.
/// </summary>
EmberOptions()
{
m_BoolArgs.reserve(25);
m_IntArgs.reserve(25);
m_UintArgs.reserve(25);
m_DoubleArgs.reserve(25);
m_StringArgs.reserve(35);
//Diagnostic bools.
INITBOOLOPTION(Help, Eob(OPT_USE_ALL, OPT_HELP, _T("--help"), false, SO_NONE, "\t--help Show this screen.\n"));
INITBOOLOPTION(Version, Eob(OPT_USE_ALL, OPT_VERSION, _T("--version"), false, SO_NONE, "\t--version Show version.\n"));
INITBOOLOPTION(Verbose, Eob(OPT_USE_ALL, OPT_VERBOSE, _T("--verbose"), false, SO_NONE, "\t--verbose Verbose output.\n"));
INITBOOLOPTION(Debug, Eob(OPT_USE_ALL, OPT_DEBUG, _T("--debug"), false, SO_NONE, "\t--debug Debug output.\n"));
INITBOOLOPTION(DumpArgs, Eob(OPT_USE_ALL, OPT_DUMP_ARGS, _T("--dumpargs"), false, SO_NONE, "\t--dumpargs Print all arguments entered from either the command line or environment variables.\n"));
INITBOOLOPTION(DoProgress, Eob(OPT_USE_ALL, OPT_PROGRESS, _T("--progress"), false, SO_NONE, "\t--progress Display progress. This will slow down processing by about 10%%.\n"));
INITBOOLOPTION(OpenCLInfo, Eob(OPT_USE_ALL, OPT_DUMP_OPENCL_INFO, _T("--openclinfo"), false, SO_NONE, "\t--openclinfo Display platforms and devices for OpenCL.\n"));
//Execution bools.
INITBOOLOPTION(EmberCL, Eob(OPT_USE_ALL, OPT_OPENCL, _T("--opencl"), false, SO_NONE, "\t--opencl Use OpenCL renderer (EmberCL) for rendering [default: false].\n"));
INITBOOLOPTION(EarlyClip, Eob(OPT_USE_ALL, OPT_EARLYCLIP, _T("--earlyclip"), false, SO_NONE, "\t--earlyclip Perform clipping of RGB values before spatial filtering for better antialiasing and resizing [default: false].\n"));
INITBOOLOPTION(Transparency, Eob(OPT_RENDER_ANIM, OPT_TRANSPARENCY, _T("--transparency"), false, SO_NONE, "\t--transparency Include alpha channel in final output [default: false except for PNG].\n"));
INITBOOLOPTION(NameEnable, Eob(OPT_USE_RENDER, OPT_NAME_ENABLE, _T("--name_enable"), false, SO_NONE, "\t--name_enable Use the name attribute contained in the xml as the output filename [default: false].\n"));
INITBOOLOPTION(IntPalette, Eob(OPT_USE_ALL, OPT_INT_PALETTE, _T("--intpalette"), false, SO_NONE, "\t--intpalette Force palette RGB values to be integers [default: false (float)].\n"));
INITBOOLOPTION(HexPalette, Eob(OPT_USE_ALL, OPT_HEX_PALETTE, _T("--hex_palette"), true, SO_NONE, "\t--hex_palette Force palette RGB values to be hex [default: true].\n"));
INITBOOLOPTION(InsertPalette, Eob(OPT_RENDER_ANIM, OPT_INSERT_PALETTE, _T("--insert_palette"), false, SO_NONE, "\t--insert_palette Insert the palette into the image for debugging purposes [default: false].\n"));
INITBOOLOPTION(JpegComments, Eob(OPT_RENDER_ANIM, OPT_JPEG_COMMENTS, _T("--enable_jpeg_comments"), true, SO_NONE, "\t--enable_jpeg_comments Enables comments in the jpeg header [default: true].\n"));
INITBOOLOPTION(PngComments, Eob(OPT_RENDER_ANIM, OPT_PNG_COMMENTS, _T("--enable_png_comments"), true, SO_NONE, "\t--enable_png_comments Enables comments in the png header [default: true].\n"));
INITBOOLOPTION(WriteGenome, Eob(OPT_USE_ANIMATE, OPT_WRITE_GENOME, _T("--write_genome"), false, SO_NONE, "\t--write_genome Write out flame associated with center of motion blur window [default: false].\n"));
INITBOOLOPTION(Enclosed, Eob(OPT_USE_GENOME, OPT_ENCLOSED, _T("--enclosed"), true, SO_NONE, "\t--enclosed Use enclosing XML tags [default: false].\n"));
INITBOOLOPTION(NoEdits, Eob(OPT_USE_GENOME, OPT_NO_EDITS, _T("--noedits"), false, SO_NONE, "\t--noedits Exclude edit tags when writing Xml [default: false].\n"));
INITBOOLOPTION(UnsmoothEdge, Eob(OPT_USE_GENOME, OPT_UNSMOOTH_EDGE, _T("--unsmoother"), false, SO_NONE, "\t--unsmoother Do not use smooth blending for sheep edges [default: false].\n"));
INITBOOLOPTION(LockAccum, Eob(OPT_USE_ALL, OPT_LOCK_ACCUM, _T("--lock_accum"), false, SO_NONE, "\t--lock_accum Lock threads when accumulating to the histogram using the CPU (ignored for OpenCL). This will drop performance to that of single threading [default: false].\n"));
INITBOOLOPTION(DumpKernel, Eob(OPT_USE_RENDER, OPT_DUMP_KERNEL, _T("--dump_kernel"), false, SO_NONE, "\t--dump_kernel Print the iteration kernel string when using OpenCL (ignored for CPU) [default: false].\n"));
//Int.
INITINTOPTION(Symmetry, Eoi(OPT_USE_GENOME, OPT_SYMMETRY, _T("--symmetry"), 0, SO_REQ_SEP, "\t--symmetry=<val> Set symmetry of result [default: 0].\n"));
INITINTOPTION(SheepGen, Eoi(OPT_USE_GENOME, OPT_SHEEP_GEN, _T("--sheep_gen"), -1, SO_REQ_SEP, "\t--sheep_gen=<val> Sheep generation of this flame [default: -1].\n"));
INITINTOPTION(SheepId, Eoi(OPT_USE_GENOME, OPT_SHEEP_ID, _T("--sheep_id"), -1, SO_REQ_SEP, "\t--sheep_id=<val> Sheep ID of this flame [default: -1].\n"));
INITUINTOPTION(Platform, Eou(OPT_RENDER_ANIM, OPT_OPENCL_PLATFORM, _T("--platform"), 0, SO_REQ_SEP, "\t--platform The OpenCL platform index to use [default: 0].\n"));
INITUINTOPTION(Device, Eou(OPT_RENDER_ANIM, OPT_OPENCL_DEVICE, _T("--device"), 0, SO_REQ_SEP, "\t--device The OpenCL device index within the specified platform to use [default: 0].\n"));
INITUINTOPTION(Seed, Eou(OPT_USE_ALL, OPT_SEED, _T("--seed"), 0, SO_REQ_SEP, "\t--seed=<val> Integer seed to use for the random number generator [default: random].\n"));
INITUINTOPTION(ThreadCount, Eou(OPT_USE_ALL, OPT_NTHREADS, _T("--nthreads"), 0, SO_REQ_SEP, "\t--nthreads=<val> The number of threads to use [default: use all available cores].\n"));
INITUINTOPTION(Strips, Eou(OPT_USE_RENDER, OPT_STRIPS, _T("--nstrips"), 1, SO_REQ_SEP, "\t--nstrips=<val> The number of fractions to split a single render frame into. Useful for print size renders or low memory systems [default: 1].\n"));
INITUINTOPTION(BitsPerChannel, Eou(OPT_RENDER_ANIM, OPT_BPC, _T("--bpc"), 8, SO_REQ_SEP, "\t--bpc=<val> Bits per channel. 8 or 16 for PNG, 8 for all others [default: 8].\n"));
INITUINTOPTION(SubBatchSize, Eou(OPT_USE_ALL, OPT_SBS, _T("--sub_batch_size"), 10000, SO_REQ_SEP, "\t--sub_batch_size=<val> The chunk size that iterating will be broken into [default: 10000].\n"));
INITUINTOPTION(Bits, Eou(OPT_USE_ALL, OPT_BITS, _T("--bits"), 33, SO_REQ_SEP, "\t--bits=<val> Determines the types used for the histogram and accumulator [default: 33].\n"
"\t\t\t\t\t32: Histogram: float, Accumulator: float.\n"
"\t\t\t\t\t33: Histogram: float, Accumulator: float.\n"//This differs from the original which used an int hist for bits 33.
"\t\t\t\t\t64: Histogram: double, Accumulator: double.\n"));
INITUINTOPTION(PrintEditDepth, Eou(OPT_USE_ALL, OPT_PRINT_EDIT_DEPTH, _T("--print_edit_depth"), 0, SO_REQ_SEP, "\t--print_edit_depth=<val> Depth to truncate <edit> tag structure when converting a flame to xml. 0 prints all <edit> tags [default: 0].\n"));
INITUINTOPTION(JpegQuality, Eou(OPT_USE_ALL, OPT_JPEG, _T("--jpeg"), 95, SO_REQ_SEP, "\t--jpeg=<val> Jpeg quality 0-100 for compression [default: 95].\n"));
INITUINTOPTION(FirstFrame, Eou(OPT_USE_ANIMATE, OPT_BEGIN, _T("--begin"), UINT_MAX, SO_REQ_SEP, "\t--begin=<val> Time of first frame to render [default: first time specified in file].\n"));
INITUINTOPTION(LastFrame, Eou(OPT_USE_ANIMATE, OPT_END, _T("--end"), UINT_MAX, SO_REQ_SEP, "\t--end=<val> Time of last frame to render [default: last time specified in the input file].\n"));
INITUINTOPTION(Time, Eou(OPT_ANIM_GENOME, OPT_TIME, _T("--time"), 0, SO_REQ_SEP, "\t--time=<val> Time of first and last frame (ie do one frame).\n"));
INITUINTOPTION(Frame, Eou(OPT_ANIM_GENOME, OPT_FRAME, _T("--frame"), 0, SO_REQ_SEP, "\t--frame=<val> Synonym for \"time\".\n"));
INITUINTOPTION(Dtime, Eou(OPT_USE_ANIMATE, OPT_DTIME, _T("--dtime"), 1, SO_REQ_SEP, "\t--dtime=<val> Time between frames [default: 1].\n"));
INITUINTOPTION(Frames, Eou(OPT_USE_GENOME, OPT_NFRAMES, _T("--nframes"), 20, SO_REQ_SEP, "\t--nframes=<val> Number of frames for each stage of the animation [default: 20].\n"));
INITUINTOPTION(Loops, Eou(OPT_USE_GENOME, OPT_LOOPS, _T("--loops"), 1, SO_REQ_SEP, "\t--loops=<val> Number of times to rotate each control point in sequence [default: 1].\n"));
INITUINTOPTION(Repeat, Eou(OPT_USE_GENOME, OPT_REPEAT, _T("--repeat"), 1, SO_REQ_SEP, "\t--repeat=<val> Number of new flames to create. Ignored if sequence, inter or rotate were specified [default: 1].\n"));
INITUINTOPTION(Tries, Eou(OPT_USE_GENOME, OPT_TRIES, _T("--tries"), 10, SO_REQ_SEP, "\t--tries=<val> Number times to try creating a flame that meets the specified constraints. Ignored if sequence, inter or rotate were specified [default: 10].\n"));
INITUINTOPTION(MaxXforms, Eou(OPT_USE_GENOME, OPT_MAX_XFORMS, _T("--maxxforms"), UINT_MAX, SO_REQ_SEP, "\t--maxxforms=<val> The maximum number of xforms allowed in the final output.\n"));
//Double.
INITDOUBLEOPTION(SizeScale, Eod(OPT_USE_ALL, OPT_SS, _T("--ss"), 1, SO_REQ_SEP, "\t--ss=<val> Size scale. All dimensions are scaled by this amount [default: 1.0].\n"));
INITDOUBLEOPTION(QualityScale, Eod(OPT_USE_ALL, OPT_QS, _T("--qs"), 1, SO_REQ_SEP, "\t--qs=<val> Quality scale. All quality values are scaled by this amount [default: 1.0].\n"));
INITDOUBLEOPTION(AspectRatio, Eod(OPT_USE_ALL, OPT_PIXEL_ASPECT, _T("--pixel_aspect"), 1, SO_REQ_SEP, "\t--pixel_aspect=<val> Aspect ratio of pixels (width over height), eg. 0.90909 for NTSC [default: 1.0].\n"));
INITDOUBLEOPTION(Stagger, Eod(OPT_USE_ALL, OPT_STAGGER, _T("--stagger"), 0, SO_REQ_SEP, "\t--stagger=<val> Affects simultaneity of xform interpolation during flame interpolation.\n"
"\t Represents how 'separate' the xforms are interpolated. Set to 1 for each\n"
"\t xform to be interpolated individually, fractions control interpolation overlap [default: 0].\n"));
INITDOUBLEOPTION(AvgThresh, Eod(OPT_USE_GENOME, OPT_AVG_THRESH, _T("--avg"), 20.0, SO_REQ_SEP, "\t--avg=<val> Minimum average pixel channel sum (r + g + b) threshold from 0 - 765. Ignored if sequence, inter or rotate were specified [default: 20].\n"));
INITDOUBLEOPTION(BlackThresh, Eod(OPT_USE_GENOME, OPT_BLACK_THRESH, _T("--black"), 0.01, SO_REQ_SEP, "\t--black=<val> Minimum number of allowed black pixels as a percentage from 0 - 1. Ignored if sequence, inter or rotate were specified [default: 0.01].\n"));
INITDOUBLEOPTION(WhiteLimit, Eod(OPT_USE_GENOME, OPT_WHITE_LIMIT, _T("--white"), 0.05, SO_REQ_SEP, "\t--white=<val> Maximum number of allowed white pixels as a percentage from 0 - 1. Ignored if sequence, inter or rotate were specified [default: 0.05].\n"));
INITDOUBLEOPTION(Speed, Eod(OPT_USE_GENOME, OPT_SPEED, _T("--speed"), 0.1, SO_REQ_SEP, "\t--speed=<val> Speed as a percentage from 0 - 1 that the affine transform of an existing flame mutates with the new flame. Ignored if sequence, inter or rotate were specified [default: 0.1].\n"));
INITDOUBLEOPTION(OffsetX, Eod(OPT_USE_GENOME, OPT_OFFSETX, _T("--offsetx"), 0.0, SO_REQ_SEP, "\t--offsetx=<val> Amount to jitter each flame horizontally when applying genome tools [default: 0].\n"));
INITDOUBLEOPTION(OffsetY, Eod(OPT_USE_GENOME, OPT_OFFSETY, _T("--offsety"), 0.0, SO_REQ_SEP, "\t--offsety=<val> Amount to jitter each flame vertically when applying genome tools [default: 0].\n"));
INITDOUBLEOPTION(UseMem, Eod(OPT_USE_RENDER, OPT_USEMEM, _T("--use_mem"), 0.0, SO_REQ_SEP, "\t--use_mem=<val> Number of bytes of memory to use [default: max system memory].\n"));
//String.
INITSTRINGOPTION(IsaacSeed, Eos(OPT_USE_ALL, OPT_ISAAC_SEED, _T("--isaac_seed"), "", SO_REQ_SEP, "\t--isaac_seed=<val> Character-based seed for the random number generator [default: random].\n"));
INITSTRINGOPTION(Input, Eos(OPT_USE_ALL, OPT_IN, _T("--in"), "", SO_REQ_SEP, "\t--in=<val> Name of the input file.\n"));
INITSTRINGOPTION(Out, Eos(OPT_USE_ALL, OPT_OUT, _T("--out"), "", SO_REQ_SEP, "\t--out=<val> Name of a single output file. Not recommended when rendering more than one image.\n"));
INITSTRINGOPTION(Prefix, Eos(OPT_USE_ALL, OPT_PREFIX, _T("--prefix"), "", SO_REQ_SEP, "\t--prefix=<val> Prefix to prepend to all output files.\n"));
INITSTRINGOPTION(Suffix, Eos(OPT_USE_ALL, OPT_SUFFIX, _T("--suffix"), "", SO_REQ_SEP, "\t--suffix=<val> Suffix to append to all output files.\n"));
INITSTRINGOPTION(Format, Eos(OPT_RENDER_ANIM, OPT_FORMAT, _T("--format"), "png", SO_REQ_SEP, "\t--format=<val> Format of the output file. Valid values are: bmp, jpg, png, ppm [default: jpg].\n"));
INITSTRINGOPTION(PalettePath, Eos(OPT_USE_ALL, OPT_PALETTE_FILE, _T("--flam3_palettes"), "flam3-palettes.xml", SO_REQ_SEP, "\t--flam3_palettes=<val> Path and name of the palette file [default: flam3-palettes.xml].\n"));
INITSTRINGOPTION(PaletteImage, Eos(OPT_USE_ALL, OPT_PALETTE_IMAGE, _T("--image"), "", SO_REQ_SEP, "\t--image=<val> Replace palette with png, jpg, or ppm image.\n"));
INITSTRINGOPTION(Id, Eos(OPT_USE_ALL, OPT_ID, _T("--id"), "", SO_REQ_SEP, "\t--id=<val> ID to use in <edit> tags.\n"));
INITSTRINGOPTION(Url, Eos(OPT_USE_ALL, OPT_URL, _T("--url"), "", SO_REQ_SEP, "\t--url=<val> URL to use in <edit> tags / img comments.\n"));
INITSTRINGOPTION(Nick, Eos(OPT_USE_ALL, OPT_NICK, _T("--nick"), "", SO_REQ_SEP, "\t--nick=<val> Nickname to use in <edit> tags / img comments.\n"));
INITSTRINGOPTION(Comment, Eos(OPT_USE_GENOME, OPT_COMMENT, _T("--comment"), "", SO_REQ_SEP, "\t--comment=<val> Comment to use in <edit> tags.\n"));
INITSTRINGOPTION(TemplateFile, Eos(OPT_USE_GENOME, OPT_TEMPLATE, _T("--template"), "", SO_REQ_SEP, "\t--template=<val> Apply defaults based on this flame.\n"));
INITSTRINGOPTION(Clone, Eos(OPT_USE_GENOME, OPT_CLONE, _T("--clone"), "", SO_REQ_SEP, "\t--clone=<val> Clone random flame in input.\n"));
INITSTRINGOPTION(CloneAll, Eos(OPT_USE_GENOME, OPT_CLONE_ALL, _T("--clone_all"), "", SO_REQ_SEP, "\t--clone_all=<val> Clones all flames in the input file. Useful for applying template to all flames.\n"));
INITSTRINGOPTION(CloneAction, Eos(OPT_USE_GENOME, OPT_CLONE_ACTION, _T("--clone_action"), "", SO_REQ_SEP, "\t--clone_action=<val> A description of the clone action taking place.\n"));
INITSTRINGOPTION(Animate, Eos(OPT_USE_GENOME, OPT_ANIMATE, _T("--animate"), "", SO_REQ_SEP, "\t--animate=<val> Interpolates between all flames in the input file, using times specified in file.\n"));
INITSTRINGOPTION(Mutate, Eos(OPT_USE_GENOME, OPT_MUTATE, _T("--mutate"), "", SO_REQ_SEP, "\t--mutate=<val> Randomly mutate a random flame from the input file.\n"));
INITSTRINGOPTION(Cross0, Eos(OPT_USE_GENOME, OPT_CROSS0, _T("--cross0"), "", SO_REQ_SEP, "\t--cross0=<val> Randomly select one flame from the input file to genetically cross...\n"));
INITSTRINGOPTION(Cross1, Eos(OPT_USE_GENOME, OPT_CROSS1, _T("--cross1"), "", SO_REQ_SEP, "\t--cross1=<val> ...with one flame from this file.\n"));
INITSTRINGOPTION(Method, Eos(OPT_USE_GENOME, OPT_METHOD, _T("--method"), "", SO_REQ_SEP, "\t--method=<val> Method used for genetic cross: alternate, interpolate, or union. For mutate: all_vars, one_xform, add_symmetry, post_xforms, color_palette, delete_xform, all_coefs [default: random].\n"));//Original ommitted this important documentation for mutate!
INITSTRINGOPTION(Inter, Eos(OPT_USE_GENOME, OPT_INTER, _T("--inter"), "", SO_REQ_SEP, "\t--inter=<val> Interpolate the input file.\n"));
INITSTRINGOPTION(Rotate, Eos(OPT_USE_GENOME, OPT_ROTATE, _T("--rotate"), "", SO_REQ_SEP, "\t--rotate=<val> Rotate the input file.\n"));
INITSTRINGOPTION(Strip, Eos(OPT_USE_GENOME, OPT_STRIP, _T("--strip"), "", SO_REQ_SEP, "\t--strip=<val> Break strip out of each flame in the input file.\n"));
INITSTRINGOPTION(Sequence, Eos(OPT_USE_GENOME, OPT_SEQUENCE, _T("--sequence"), "", SO_REQ_SEP, "\t--sequence=<val> 360 degree rotation 'loops' times of each control point in the input file plus rotating transitions.\n"));
INITSTRINGOPTION(UseVars, Eos(OPT_USE_GENOME, OPT_USE_VARS, _T("--use_vars"), "", SO_REQ_SEP, "\t--use_vars=<val> Comma separated list of variation #'s to use when generating a random flame.\n"));
INITSTRINGOPTION(DontUseVars, Eos(OPT_USE_GENOME, OPT_DONT_USE_VARS, _T("--dont_use_vars"), "", SO_REQ_SEP, "\t--dont_use_vars=<val> Comma separated list of variation #'s to NOT use when generating a random flame.\n"));
INITSTRINGOPTION(Extras, Eos(OPT_USE_GENOME, OPT_EXTRAS, _T("--extras"), "", SO_REQ_SEP, "\t--extras=<val> Extra attributes to place in the flame section of the Xml.\n"));
}
/// <summary>
/// Parse and populate the supplied command line options for the specified program usage.
/// If --help or --version were specified, information will be printed
/// and parsing will cease.
/// </summary>
/// <param name="argc">The number of command line arguments passed</param>
/// <param name="argv">The command line arguments passed</param>
/// <param name="optUsage">The program for which these options are to be parsed and used.</param>
/// <returns>True if --help or --version specified, else false</returns>
bool Populate(int argc, _TCHAR* argv[], eOptionUse optUsage)
{
EmberOptions options;
vector<CSimpleOpt::SOption> sOptions = options.GetSimpleOptions();
CSimpleOpt args(argc, argv, sOptions.data());
//Process args.
while (args.Next())
{
ESOError errorCode = args.LastError();
if (errorCode == SO_SUCCESS)
{
switch (args.OptionId())
{
case OPT_HELP://Bool args.
{
ShowUsage(optUsage);
return true;
}
case OPT_VERSION:
{
cout << EmberVersion() << endl;
return true;
}
PARSEBOOLOPTION(OPT_VERBOSE, Verbose);
PARSEBOOLOPTION(OPT_DEBUG, Debug);
PARSEBOOLOPTION(OPT_DUMP_ARGS, DumpArgs);
PARSEBOOLOPTION(OPT_PROGRESS, DoProgress);
PARSEBOOLOPTION(OPT_DUMP_OPENCL_INFO, OpenCLInfo);
PARSEBOOLOPTION(OPT_OPENCL, EmberCL);
PARSEBOOLOPTION(OPT_EARLYCLIP, EarlyClip);
PARSEBOOLOPTION(OPT_TRANSPARENCY, Transparency);
PARSEBOOLOPTION(OPT_NAME_ENABLE, NameEnable);
PARSEBOOLOPTION(OPT_INT_PALETTE, IntPalette);
PARSEBOOLOPTION(OPT_HEX_PALETTE, HexPalette);
PARSEBOOLOPTION(OPT_INSERT_PALETTE, InsertPalette);
PARSEBOOLOPTION(OPT_JPEG_COMMENTS, JpegComments);
PARSEBOOLOPTION(OPT_PNG_COMMENTS, PngComments);
PARSEBOOLOPTION(OPT_WRITE_GENOME, WriteGenome);
PARSEBOOLOPTION(OPT_ENCLOSED, Enclosed);
PARSEBOOLOPTION(OPT_NO_EDITS, NoEdits);
PARSEBOOLOPTION(OPT_UNSMOOTH_EDGE, UnsmoothEdge);
PARSEBOOLOPTION(OPT_LOCK_ACCUM, LockAccum);
PARSEBOOLOPTION(OPT_DUMP_KERNEL, DumpKernel);
PARSEINTOPTION(OPT_SYMMETRY, Symmetry);//Int args
PARSEINTOPTION(OPT_SHEEP_GEN, SheepGen);
PARSEINTOPTION(OPT_SHEEP_ID, SheepId);
PARSEUINTOPTION(OPT_OPENCL_PLATFORM, Platform);//Unsigned int args.
PARSEUINTOPTION(OPT_OPENCL_DEVICE, Device);
PARSEUINTOPTION(OPT_SEED, Seed);
PARSEUINTOPTION(OPT_NTHREADS, ThreadCount);
PARSEUINTOPTION(OPT_STRIPS, Strips);
PARSEUINTOPTION(OPT_BITS, Bits);
PARSEUINTOPTION(OPT_BPC, BitsPerChannel);
PARSEUINTOPTION(OPT_SBS, SubBatchSize);
PARSEUINTOPTION(OPT_PRINT_EDIT_DEPTH, PrintEditDepth);
PARSEUINTOPTION(OPT_JPEG, JpegQuality);
PARSEUINTOPTION(OPT_BEGIN, FirstFrame);
PARSEUINTOPTION(OPT_END, LastFrame);
PARSEUINTOPTION(OPT_FRAME, Frame);
PARSEUINTOPTION(OPT_TIME, Time);
PARSEUINTOPTION(OPT_DTIME, Dtime);
PARSEUINTOPTION(OPT_NFRAMES, Frames);
PARSEUINTOPTION(OPT_LOOPS, Loops);
PARSEUINTOPTION(OPT_REPEAT, Repeat);
PARSEUINTOPTION(OPT_TRIES, Tries);
PARSEUINTOPTION(OPT_MAX_XFORMS, MaxXforms);
PARSEDOUBLEOPTION(OPT_SS, SizeScale);//Float args.
PARSEDOUBLEOPTION(OPT_QS, QualityScale);
PARSEDOUBLEOPTION(OPT_PIXEL_ASPECT, AspectRatio);
PARSEDOUBLEOPTION(OPT_STAGGER, Stagger);
PARSEDOUBLEOPTION(OPT_AVG_THRESH, AvgThresh);
PARSEDOUBLEOPTION(OPT_BLACK_THRESH, BlackThresh);
PARSEDOUBLEOPTION(OPT_WHITE_LIMIT, WhiteLimit);
PARSEDOUBLEOPTION(OPT_SPEED, Speed);
PARSEDOUBLEOPTION(OPT_OFFSETX, OffsetX);
PARSEDOUBLEOPTION(OPT_OFFSETY, OffsetY);
PARSEDOUBLEOPTION(OPT_USEMEM, UseMem);
PARSESTRINGOPTION(OPT_ISAAC_SEED, IsaacSeed);//String args.
PARSESTRINGOPTION(OPT_IN, Input);
PARSESTRINGOPTION(OPT_OUT, Out);
PARSESTRINGOPTION(OPT_PREFIX, Prefix);
PARSESTRINGOPTION(OPT_SUFFIX, Suffix);
PARSESTRINGOPTION(OPT_FORMAT, Format);
PARSESTRINGOPTION(OPT_PALETTE_FILE, PalettePath);
PARSESTRINGOPTION(OPT_PALETTE_IMAGE, PaletteImage);
PARSESTRINGOPTION(OPT_ID, Id);
PARSESTRINGOPTION(OPT_URL, Url);
PARSESTRINGOPTION(OPT_NICK, Nick);
PARSESTRINGOPTION(OPT_COMMENT, Comment);
PARSESTRINGOPTION(OPT_TEMPLATE, TemplateFile);
PARSESTRINGOPTION(OPT_CLONE, Clone);
PARSESTRINGOPTION(OPT_CLONE_ALL, CloneAll);
PARSESTRINGOPTION(OPT_CLONE_ACTION, CloneAction);
PARSESTRINGOPTION(OPT_ANIMATE, Animate);
PARSESTRINGOPTION(OPT_MUTATE, Mutate);
PARSESTRINGOPTION(OPT_CROSS0, Cross0);
PARSESTRINGOPTION(OPT_CROSS1, Cross1);
PARSESTRINGOPTION(OPT_METHOD, Method);
PARSESTRINGOPTION(OPT_INTER, Inter);
PARSESTRINGOPTION(OPT_ROTATE, Rotate);
PARSESTRINGOPTION(OPT_STRIP, Strip);
PARSESTRINGOPTION(OPT_SEQUENCE, Sequence);
PARSESTRINGOPTION(OPT_USE_VARS, UseVars);
PARSESTRINGOPTION(OPT_DONT_USE_VARS, DontUseVars);
PARSESTRINGOPTION(OPT_EXTRAS, Extras);
}
}
else
{
cout << "Invalid argument: " << args.OptionText() << endl;
cout << "\tReason: " << GetLastErrorText(errorCode) << endl;
}
}
return false;
}
/// <summary>
/// Return a vector of all available options for the specified program.
/// </summary>
/// <param name="optUsage">The specified program usage</param>
/// <returns>A vector of all available options for the specified program</returns>
vector<CSimpleOpt::SOption> GetSimpleOptions(eOptionUse optUsage = OPT_USE_ALL)
{
vector<CSimpleOpt::SOption> entries;
CSimpleOpt::SOption endOption = SO_END_OF_OPTIONS;
entries.reserve(75);
std::for_each(m_BoolArgs.begin(), m_BoolArgs.end(), [&](Eob* entry) { if (entry->m_OptionUse & optUsage) entries.push_back(entry->m_Option); });
std::for_each(m_IntArgs.begin(), m_IntArgs.end(), [&](Eoi* entry) { if (entry->m_OptionUse & optUsage) entries.push_back(entry->m_Option); });
std::for_each(m_UintArgs.begin(), m_UintArgs.end(), [&](Eou* entry) { if (entry->m_OptionUse & optUsage) entries.push_back(entry->m_Option); });
std::for_each(m_DoubleArgs.begin(), m_DoubleArgs.end(), [&](Eod* entry) { if (entry->m_OptionUse & optUsage) entries.push_back(entry->m_Option); });
std::for_each(m_StringArgs.begin(), m_StringArgs.end(), [&](Eos* entry) { if (entry->m_OptionUse & optUsage) entries.push_back(entry->m_Option); });
entries.push_back(endOption);
return entries;
}
/// <summary>
/// Return a string with the descriptions of all available options for the specified program.
/// </summary>
/// <param name="optUsage">The specified program usage</param>
/// <returns>A string with the descriptions of all available options for the specified program</returns>
string GetUsage(eOptionUse optUsage = OPT_USE_ALL)
{
ostringstream os;
std::for_each(m_BoolArgs.begin(), m_BoolArgs.end(), [&](Eob* entry) { if (entry->m_OptionUse & optUsage) os << entry->m_DocString << endl; });
std::for_each(m_IntArgs.begin(), m_IntArgs.end(), [&](Eoi* entry) { if (entry->m_OptionUse & optUsage) os << entry->m_DocString << endl; });
std::for_each(m_UintArgs.begin(), m_UintArgs.end(), [&](Eou* entry) { if (entry->m_OptionUse & optUsage) os << entry->m_DocString << endl; });
std::for_each(m_DoubleArgs.begin(), m_DoubleArgs.end(), [&](Eod* entry) { if (entry->m_OptionUse & optUsage) os << entry->m_DocString << endl; });
std::for_each(m_StringArgs.begin(), m_StringArgs.end(), [&](Eos* entry) { if (entry->m_OptionUse & optUsage) os << entry->m_DocString << endl; });
return os.str();
}
/// <summary>
/// Return a string with all of the names and values for all available options for the specified program.
/// </summary>
/// <param name="optUsage">The specified program usage</param>
/// <returns>A string with all of the names and values for all available options for the specified program</returns>
string GetValues(eOptionUse optUsage = OPT_USE_ALL)
{
ostringstream os;
os << std::boolalpha;
std::for_each(m_BoolArgs.begin(), m_BoolArgs.end(), [&](Eob* entry) { if (entry->m_OptionUse & optUsage) os << entry->m_NameWithoutDashes << ": " << (*entry)() << endl; });
std::for_each(m_IntArgs.begin(), m_IntArgs.end(), [&](Eoi* entry) { if (entry->m_OptionUse & optUsage) os << entry->m_NameWithoutDashes << ": " << (*entry)() << endl; });
std::for_each(m_UintArgs.begin(), m_UintArgs.end(), [&](Eou* entry) { if (entry->m_OptionUse & optUsage) os << entry->m_NameWithoutDashes << ": " << (*entry)() << endl; });
std::for_each(m_DoubleArgs.begin(), m_DoubleArgs.end(), [&](Eod* entry) { if (entry->m_OptionUse & optUsage) os << entry->m_NameWithoutDashes << ": " << (*entry)() << endl; });
std::for_each(m_StringArgs.begin(), m_StringArgs.end(), [&](Eos* entry) { if (entry->m_OptionUse & optUsage) os << entry->m_NameWithoutDashes << ": " << (*entry)() << endl; });
return os.str();
}
/// <summary>
/// Print description string, version and description of all available options for the specified program.
/// </summary>
/// <param name="optUsage">The specified program usage</param>
void ShowUsage(eOptionUse optUsage)
{
cout << DescriptionString << " version " << EmberVersion() << endl << endl;
if (optUsage == OPT_USE_RENDER)
{
cout << "Usage:\n"
"\tEmberRender.exe --in=test.flam3 [--out=outfile --format=png --verbose --progress --opencl]\n" << endl;
}
else if (optUsage == OPT_USE_ANIMATE)
{
cout << "Usage:\n"
"\tEmberAnimate.exe --in=sequence.flam3 [--format=png --verbose --progress --opencl]\n" << endl;
}
else if (optUsage == OPT_USE_GENOME)
{
cout << "Usage:\n"
"\tEmberGenome.exe --sequence=test.flam3 > sequenceout.flam3\n" << endl;
}
cout << GetUsage(optUsage) << endl;
}
/// <summary>
/// Return the last option parsing error text as a string.
/// </summary>
/// <param name="errorCode">The code of the last parsing error</param>
/// <returns>The last option parsing error text as a string</returns>
string GetLastErrorText(int errorCode)
{
switch (errorCode)
{
case SO_SUCCESS: return "Success";
case SO_OPT_INVALID: return "Unrecognized option";
case SO_OPT_MULTIPLE: return "Option matched multiple strings";
case SO_ARG_INVALID: return "Option does not accept argument";
case SO_ARG_INVALID_TYPE: return "Invalid argument format";
case SO_ARG_MISSING: return "Required argument is missing";
case SO_ARG_INVALID_DATA: return "Invalid argument data";
default: return "Unknown error";
}
}
//Break from the usual m_* notation for members here because
//each of these is a functor, so it looks nicer and is less typing
//to just say opt.Member().
EmberOptionEntry<bool> Help;//Diagnostic bool.
EmberOptionEntry<bool> Version;
EmberOptionEntry<bool> Verbose;
EmberOptionEntry<bool> Debug;
EmberOptionEntry<bool> DumpArgs;
EmberOptionEntry<bool> DoProgress;
EmberOptionEntry<bool> OpenCLInfo;
EmberOptionEntry<bool> EmberCL;//Value bool.
EmberOptionEntry<bool> EarlyClip;
EmberOptionEntry<bool> Transparency;
EmberOptionEntry<bool> NameEnable;
EmberOptionEntry<bool> IntPalette;
EmberOptionEntry<bool> HexPalette;
EmberOptionEntry<bool> InsertPalette;
EmberOptionEntry<bool> JpegComments;
EmberOptionEntry<bool> PngComments;
EmberOptionEntry<bool> WriteGenome;
EmberOptionEntry<bool> Enclosed;
EmberOptionEntry<bool> NoEdits;
EmberOptionEntry<bool> UnsmoothEdge;
EmberOptionEntry<bool> LockAccum;
EmberOptionEntry<bool> DumpKernel;
EmberOptionEntry<int> Symmetry;//Value int.
EmberOptionEntry<int> SheepGen;//Value int.
EmberOptionEntry<int> SheepId;//Value int.
EmberOptionEntry<unsigned int> Platform;//Value unsigned int.
EmberOptionEntry<unsigned int> Device;
EmberOptionEntry<unsigned int> Seed;
EmberOptionEntry<unsigned int> ThreadCount;
EmberOptionEntry<unsigned int> Strips;
EmberOptionEntry<unsigned int> BitsPerChannel;
EmberOptionEntry<unsigned int> SubBatchSize;
EmberOptionEntry<unsigned int> Bits;
EmberOptionEntry<unsigned int> PrintEditDepth;
EmberOptionEntry<unsigned int> JpegQuality;
EmberOptionEntry<unsigned int> FirstFrame;
EmberOptionEntry<unsigned int> LastFrame;
EmberOptionEntry<unsigned int> Frame;
EmberOptionEntry<unsigned int> Time;
EmberOptionEntry<unsigned int> Dtime;
EmberOptionEntry<unsigned int> Frames;
EmberOptionEntry<unsigned int> Loops;
EmberOptionEntry<unsigned int> Repeat;
EmberOptionEntry<unsigned int> Tries;
EmberOptionEntry<unsigned int> MaxXforms;
EmberOptionEntry<double> SizeScale;//Value double.
EmberOptionEntry<double> QualityScale;
EmberOptionEntry<double> AspectRatio;
EmberOptionEntry<double> Stagger;
EmberOptionEntry<double> AvgThresh;
EmberOptionEntry<double> BlackThresh;
EmberOptionEntry<double> WhiteLimit;
EmberOptionEntry<double> Speed;
EmberOptionEntry<double> OffsetX;
EmberOptionEntry<double> OffsetY;
EmberOptionEntry<double> UseMem;
EmberOptionEntry<string> IsaacSeed;//Value string.
EmberOptionEntry<string> Input;
EmberOptionEntry<string> Out;
EmberOptionEntry<string> Prefix;
EmberOptionEntry<string> Suffix;
EmberOptionEntry<string> Format;
EmberOptionEntry<string> PalettePath;
EmberOptionEntry<string> PaletteImage;
EmberOptionEntry<string> Id;
EmberOptionEntry<string> Url;
EmberOptionEntry<string> Nick;
EmberOptionEntry<string> Comment;
EmberOptionEntry<string> TemplateFile;
EmberOptionEntry<string> Clone;
EmberOptionEntry<string> CloneAll;
EmberOptionEntry<string> CloneAction;
EmberOptionEntry<string> Animate;
EmberOptionEntry<string> Mutate;
EmberOptionEntry<string> Cross0;
EmberOptionEntry<string> Cross1;
EmberOptionEntry<string> Method;
EmberOptionEntry<string> Inter;
EmberOptionEntry<string> Rotate;
EmberOptionEntry<string> Strip;
EmberOptionEntry<string> Sequence;
EmberOptionEntry<string> UseVars;
EmberOptionEntry<string> DontUseVars;
EmberOptionEntry<string> Extras;
private:
vector<EmberOptionEntry<bool>*> m_BoolArgs;
vector<EmberOptionEntry<int>*> m_IntArgs;
vector<EmberOptionEntry<unsigned int>*> m_UintArgs;
vector<EmberOptionEntry<double>*> m_DoubleArgs;
vector<EmberOptionEntry<string>*> m_StringArgs;
};

View File

@ -0,0 +1,362 @@
#pragma once
#include "EmberCommonPch.h"
#define PNG_COMMENT_MAX 8
/// <summary>
/// Write a PPM file.
/// </summary>
/// <param name="filename">The full path and name of the file</param>
/// <param name="image">Pointer to the image data to write</param>
/// <param name="width">Width of the image in pixels</param>
/// <param name="height">Height of the image in pixels</param>
/// <returns>True if success, else false</returns>
static bool WritePpm(const char* filename, unsigned char* image, int width, int height)
{
bool b = false;
unsigned int size = width * height * 3;
FILE* file;
if (fopen_s(&file, filename, "wb") == 0)
{
fprintf_s(file, "P6\n");
fprintf_s(file, "%d %d\n255\n", width, height);
b = (size == fwrite(image, 1, size, file));
fclose(file);
}
return b;
}
/// <summary>
/// Write a JPEG file.
/// </summary>
/// <param name="filename">The full path and name of the file</param>
/// <param name="image">Pointer to the image data to write</param>
/// <param name="width">Width of the image in pixels</param>
/// <param name="height">Height of the image in pixels</param>
/// <param name="quality">The quality to use</param>
/// <param name="enableComments">True to embed comments, else false</param>
/// <param name="comments">The comment string to embed</param>
/// <param name="id">Id of the author</param>
/// <param name="url">Url of the author</param>
/// <param name="nick">Nickname of the author</param>
/// <returns>True if success, else false</returns>
static bool WriteJpeg(const char* filename, unsigned char* image, unsigned int width, unsigned int height, int quality, bool enableComments, EmberImageComments& comments, string id, string url, string nick)
{
bool b = false;
FILE* file;
if (fopen_s(&file, filename, "wb") == 0)
{
size_t i;
jpeg_error_mgr jerr;
jpeg_compress_struct info;
char nickString[64], urlString[128], idString[128];
char bvString[64], niString[64], rtString[64];
char genomeString[65536], verString[64];
//Create the mandatory comment strings.
snprintf_s(genomeString, 65536, "flam3_genome: %s", comments.m_Genome.c_str());
snprintf_s(bvString, 64, "flam3_error_rate: %s", comments.m_Badvals.c_str());
snprintf_s(niString, 64, "flam3_samples: %s", comments.m_NumIters);
snprintf_s(rtString, 64, "flam3_time: %s", comments.m_Runtime.c_str());
snprintf_s(verString, 64, "flam3_version: %s", EmberVersion());
info.err = jpeg_std_error(&jerr);
jpeg_create_compress(&info);
jpeg_stdio_dest(&info, file);
info.in_color_space = JCS_RGB;
info.input_components = 3;
info.image_width = width;
info.image_height = height;
jpeg_set_defaults(&info);
jpeg_set_quality(&info, quality, TRUE);
jpeg_start_compress(&info, TRUE);
//Write comments to jpeg.
if (enableComments)
{
jpeg_write_marker(&info, JPEG_COM, (unsigned char*)verString, (int)strlen(verString));
if (nick != "")
{
snprintf_s(nickString, 64, "flam3_nickname: %s", nick.c_str());
jpeg_write_marker(&info, JPEG_COM, (unsigned char*)nickString, (int)strlen(nickString));
}
if (url != "")
{
snprintf_s(urlString, 128, "flam3_url: %s", url.c_str());
jpeg_write_marker(&info, JPEG_COM, (unsigned char*)urlString, (int)strlen(urlString));
}
if (id != "")
{
snprintf_s(idString, 128, "flam3_id: %s", id.c_str());
jpeg_write_marker(&info, JPEG_COM, (unsigned char*)idString, (int)strlen(idString));
}
jpeg_write_marker(&info, JPEG_COM, (unsigned char*)bvString, (int)strlen(bvString));
jpeg_write_marker(&info, JPEG_COM, (unsigned char*)niString, (int)strlen(niString));
jpeg_write_marker(&info, JPEG_COM, (unsigned char*)rtString, (int)strlen(rtString));
jpeg_write_marker(&info, JPEG_COM, (unsigned char*)genomeString, (int)strlen(genomeString));
}
for (i = 0; i < height; i++)
{
JSAMPROW row_pointer[1];
row_pointer[0] = (unsigned char*)image + (3 * width * i);
jpeg_write_scanlines(&info, row_pointer, 1);
}
jpeg_finish_compress(&info);
jpeg_destroy_compress(&info);
fclose(file);
b = true;
}
return b;
}
/// <summary>
/// Write a PNG file.
/// </summary>
/// <param name="filename">The full path and name of the file</param>
/// <param name="image">Pointer to the image data to write</param>
/// <param name="width">Width of the image in pixels</param>
/// <param name="height">Height of the image in pixels</param>
/// <param name="bytesPerChannel">Bytes per channel, 1 or 2.</param>
/// <param name="enableComments">True to embed comments, else false</param>
/// <param name="comments">The comment string to embed</param>
/// <param name="id">Id of the author</param>
/// <param name="url">Url of the author</param>
/// <param name="nick">Nickname of the author</param>
/// <returns>True if success, else false</returns>
static bool WritePng(const char* filename, unsigned char* image, unsigned int width, unsigned int height, int bytesPerChannel, bool enableComments, EmberImageComments& comments, string id, string url, string nick)
{
bool b = false;
FILE* file;
if (fopen_s(&file, filename, "wb") == 0)
{
png_structp png_ptr;
png_infop info_ptr;
png_text text[PNG_COMMENT_MAX];
size_t i;
unsigned short testbe = 1;
vector<unsigned char*> rows(height);
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
text[0].key = "flam3_version";
text[0].text = EmberVersion();
text[1].compression = PNG_TEXT_COMPRESSION_NONE;
text[1].key = "flam3_nickname";
text[1].text = (png_charp)nick.c_str();
text[2].compression = PNG_TEXT_COMPRESSION_NONE;
text[2].key = "flam3_url";
text[2].text = (png_charp)url.c_str();
text[3].compression = PNG_TEXT_COMPRESSION_NONE;
text[3].key = "flam3_id";
text[3].text = (png_charp)id.c_str();
text[4].compression = PNG_TEXT_COMPRESSION_NONE;
text[4].key = "flam3_error_rate";
text[4].text = (png_charp)comments.m_Badvals.c_str();
text[5].compression = PNG_TEXT_COMPRESSION_NONE;
text[5].key = "flam3_samples";
text[5].text = (png_charp)comments.m_NumIters.c_str();
text[6].compression = PNG_TEXT_COMPRESSION_NONE;
text[6].key = "flam3_time";
text[6].text = (png_charp)comments.m_Runtime.c_str();
text[7].compression = PNG_TEXT_COMPRESSION_zTXt;
text[7].key = "flam3_genome";
text[7].text = (png_charp)comments.m_Genome.c_str();
for (i = 0; i < height; i++)
rows[i] = (unsigned char*)image + i * width * 4 * bytesPerChannel;
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info_ptr = png_create_info_struct(png_ptr);
if (setjmp(png_jmpbuf(png_ptr)))
{
fclose(file);
png_destroy_write_struct(&png_ptr, &info_ptr);
perror("writing file");
return false;
}
png_init_io(png_ptr, file);
png_set_IHDR(png_ptr, info_ptr, width, height, 8 * bytesPerChannel,
PNG_COLOR_TYPE_RGBA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
if (enableComments == 1)
png_set_text(png_ptr, info_ptr, text, PNG_COMMENT_MAX);
png_write_info(png_ptr, info_ptr);
//Must set this after png_write_info().
if (bytesPerChannel == 2 && testbe != htons(testbe))
{
png_set_swap(png_ptr);
}
png_write_image(png_ptr, (png_bytepp) rows.data());
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(file);
b = true;
}
return b;
}
/// <summary>
/// Convert an RGB buffer to BGR for usage with BMP.
/// </summary>
/// <param name="buffer">The buffer to convert</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="newSize">The size of the new buffer created</param>
/// <returns>The converted buffer if successful, else NULL.</returns>
static BYTE* ConvertRGBToBMPBuffer(BYTE* buffer, int width, int height, long& newSize)
{
if (NULL == buffer || width == 0 || height == 0)
return NULL;
int padding = 0;
int scanlinebytes = width * 3;
while ((scanlinebytes + padding ) % 4 != 0)
padding++;
int psw = scanlinebytes + padding;
newSize = height * psw;
BYTE* newBuf = new BYTE[newSize];
if (newBuf)
{
memset (newBuf, 0, newSize);
long bufpos = 0;
long newpos = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < 3 * width; x += 3)
{
bufpos = y * 3 * width + x; // position in original buffer
newpos = (height - y - 1) * psw + x; // position in padded buffer
newBuf[newpos] = buffer[bufpos+2]; // swap r and b
newBuf[newpos + 1] = buffer[bufpos + 1]; // g stays
newBuf[newpos + 2] = buffer[bufpos]; // swap b and r
//No swap.
//newBuf[newpos] = buffer[bufpos];
//newBuf[newpos + 1] = buffer[bufpos + 1];
//newBuf[newpos + 2] = buffer[bufpos + 2];
}
}
return newBuf;
}
return NULL;
}
/// <summary>
/// Save a Bmp file.
/// </summary>
/// <param name="filename">The full path and name of the file</param>
/// <param name="image">Pointer to the image data to write</param>
/// <param name="width">Width of the image in pixels</param>
/// <param name="height">Height of the image in pixels</param>
/// <param name="paddedSize">Padded size, greater than or equal to total image size.</param>
/// <returns>True if success, else false</returns>
static bool SaveBmp(const char* filename, BYTE* image, int width, int height, long paddedSize)
{
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER info;
unsigned long bwritten;
HANDLE file;
memset (&bmfh, 0, sizeof (BITMAPFILEHEADER));
memset (&info, 0, sizeof (BITMAPINFOHEADER));
bmfh.bfType = 0x4d42; // 0x4d42 = 'BM'
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + paddedSize;
bmfh.bfOffBits = 0x36;
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = width;
info.biHeight = height;
info.biPlanes = 1;
info.biBitCount = 24;
info.biCompression = BI_RGB;
info.biSizeImage = 0;
info.biXPelsPerMeter = 0x0ec4;
info.biYPelsPerMeter = 0x0ec4;
info.biClrUsed = 0;
info.biClrImportant = 0;
if ((file = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == NULL)
{
CloseHandle(file);
return false;
}
if (WriteFile(file, &bmfh, sizeof (BITMAPFILEHEADER), &bwritten, NULL) == false)
{
CloseHandle(file);
return false;
}
if (WriteFile(file, &info, sizeof(BITMAPINFOHEADER), &bwritten, NULL) == false)
{
CloseHandle(file);
return false;
}
if (WriteFile(file, image, paddedSize, &bwritten, NULL) == false)
{
CloseHandle(file);
return false;
}
CloseHandle(file);
return true;
}
/// <summary>
/// Convert a buffer from RGB to BGR and write a Bmp file.
/// </summary>
/// <param name="filename">The full path and name of the file</param>
/// <param name="image">Pointer to the image data to write</param>
/// <param name="width">Width of the image in pixels</param>
/// <param name="height">Height of the image in pixels</param>
/// <returns>True if success, else false</returns>
static bool WriteBmp(const char* filename, unsigned char* image, int width, int height)
{
bool b = false;
long newSize;
auto_ptr<BYTE> bgrBuf(ConvertRGBToBMPBuffer(image, width, height, newSize));
if (bgrBuf.get())
b = SaveBmp(filename, bgrBuf.get(), width, height, newSize);
return b;
}

View File

@ -0,0 +1,959 @@
/*! @file SimpleGlob.h
@version 3.6
@brief A cross-platform file globbing library providing the ability to
expand wildcards in command-line arguments to a list of all matching
files. It is designed explicitly to be portable to any platform and has
been tested on Windows and Linux. See CSimpleGlobTempl for the class
definition.
@section features FEATURES
- MIT Licence allows free use in all software (including GPL and
commercial)
- multi-platform (Windows 95/98/ME/NT/2K/XP, Linux, Unix)
- supports most of the standard linux glob() options
- recognition of a forward paths as equivalent to a backward slash
on Windows. e.g. "c:/path/foo*" is equivalent to "c:\path\foo*".
- implemented with only a single C++ header file
- char, wchar_t and Windows TCHAR in the same program
- complete working examples included
- compiles cleanly at warning level 4 (Windows/VC.NET 2003),
warning level 3 (Windows/VC6) and -Wall (Linux/gcc)
@section usage USAGE
The SimpleGlob class is used by following these steps:
<ol>
<li> Include the SimpleGlob.h header file
<pre>
\#include "SimpleGlob.h"
</pre>
<li> Instantiate a CSimpleGlob object supplying the appropriate flags.
<pre>
@link CSimpleGlobTempl CSimpleGlob @endlink glob(FLAGS);
</pre>
<li> Add all file specifications to the glob class.
<pre>
glob.Add("file*");
glob.Add(argc, argv);
</pre>
<li> Process all files with File(), Files() and FileCount()
<pre>
for (int n = 0; n < glob.FileCount(); ++n) {
ProcessFile(glob.File(n));
}
</pre>
</ol>
@section licence MIT LICENCE
<pre>
The licence text below is the boilerplate "MIT Licence" used from:
http://www.opensource.org/licenses/mit-license.php
Copyright (c) 2006-2013, Brodie Thiesfield
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</pre>
*/
#ifndef INCLUDED_SimpleGlob
#define INCLUDED_SimpleGlob
/*! @brief The operation of SimpleGlob is fine-tuned via the use of a
combination of the following flags.
The flags may be passed at initialization of the class and used for every
filespec added, or alternatively they may optionally be specified in the
call to Add() and be different for each filespec.
@param SG_GLOB_ERR
Return upon read error (e.g. directory does not have read permission)
@param SG_GLOB_MARK
Append a slash (backslash in Windows) to every path which corresponds
to a directory
@param SG_GLOB_NOSORT
By default, files are returned in sorted into string order. With this
flag, no sorting is done. This is not compatible with
SG_GLOB_FULLSORT.
@param SG_GLOB_FULLSORT
By default, files are sorted in groups belonging to each filespec that
was added. For example if the filespec "b*" was added before the
filespec "a*" then the argv array will contain all b* files sorted in
order, followed by all a* files sorted in order. If this flag is
specified, the entire array will be sorted ignoring the filespec
groups.
@param SG_GLOB_NOCHECK
If the pattern doesn't match anything, return the original pattern.
@param SG_GLOB_TILDE
Tilde expansion is carried out (on Unix platforms)
@param SG_GLOB_ONLYDIR
Return only directories which match (not compatible with
SG_GLOB_ONLYFILE)
@param SG_GLOB_ONLYFILE
Return only files which match (not compatible with SG_GLOB_ONLYDIR)
@param SG_GLOB_NODOT
Do not return the "." or ".." special directories.
*/
enum SG_Flags {
SG_GLOB_ERR = 1 << 0,
SG_GLOB_MARK = 1 << 1,
SG_GLOB_NOSORT = 1 << 2,
SG_GLOB_NOCHECK = 1 << 3,
SG_GLOB_TILDE = 1 << 4,
SG_GLOB_ONLYDIR = 1 << 5,
SG_GLOB_ONLYFILE = 1 << 6,
SG_GLOB_NODOT = 1 << 7,
SG_GLOB_FULLSORT = 1 << 8
};
/*! @brief Error return codes */
enum SG_Error {
SG_SUCCESS = 0,
SG_ERR_NOMATCH = 1,
SG_ERR_MEMORY = -1,
SG_ERR_FAILURE = -2
};
// ---------------------------------------------------------------------------
// Platform dependent implementations
// if we aren't on Windows and we have ICU available, then enable ICU
// by default. Define this to 0 to intentially disable it.
#ifndef SG_HAVE_ICU
# if !defined(_WIN32) && defined(USTRING_H)
# define SG_HAVE_ICU 1
# else
# define SG_HAVE_ICU 0
# endif
#endif
// don't include this in documentation as it isn't relevant
#ifndef DOXYGEN
// on Windows we want to use MBCS aware string functions and mimic the
// Unix glob functionality. On Unix we just use glob.
#ifdef _WIN32
# include <mbstring.h>
# define sg_strchr ::_mbschr
# define sg_strrchr ::_mbsrchr
# define sg_strlen ::_mbslen
# if __STDC_WANT_SECURE_LIB__
# define sg_strcpy_s(a,n,b) ::_mbscpy_s(a,n,b)
# else
# define sg_strcpy_s(a,n,b) ::_mbscpy(a,b)
# endif
# define sg_strcmp ::_mbscmp
# define sg_strcasecmp ::_mbsicmp
# define SOCHAR_T unsigned char
#else
# include <sys/types.h>
# include <sys/stat.h>
# include <glob.h>
# include <limits.h>
# define MAX_PATH PATH_MAX
# define sg_strchr ::strchr
# define sg_strrchr ::strrchr
# define sg_strlen ::strlen
# define sg_strcpy_s(a,n,b) ::strcpy(a,b)
# define sg_strcmp ::strcmp
# define sg_strcasecmp ::strcasecmp
# define SOCHAR_T char
#endif
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
// use assertions to test the input data
#ifdef _DEBUG
# ifdef _MSC_VER
# include <crtdbg.h>
# define SG_ASSERT(b) _ASSERTE(b)
# else
# include <assert.h>
# define SG_ASSERT(b) assert(b)
# endif
#else
# define SG_ASSERT(b)
#endif
/*! @brief String manipulation functions. */
class SimpleGlobUtil
{
public:
static const char * strchr(const char *s, char c) {
return (char *) sg_strchr((const SOCHAR_T *)s, c);
}
static const wchar_t * strchr(const wchar_t *s, wchar_t c) {
return ::wcschr(s, c);
}
#if SG_HAVE_ICU
static const UChar * strchr(const UChar *s, UChar c) {
return ::u_strchr(s, c);
}
#endif
static const char * strrchr(const char *s, char c) {
return (char *) sg_strrchr((const SOCHAR_T *)s, c);
}
static const wchar_t * strrchr(const wchar_t *s, wchar_t c) {
return ::wcsrchr(s, c);
}
#if SG_HAVE_ICU
static const UChar * strrchr(const UChar *s, UChar c) {
return ::u_strrchr(s, c);
}
#endif
// Note: char strlen returns number of bytes, not characters
static size_t strlen(const char *s) { return ::strlen(s); }
static size_t strlen(const wchar_t *s) { return ::wcslen(s); }
#if SG_HAVE_ICU
static size_t strlen(const UChar *s) { return ::u_strlen(s); }
#endif
static void strcpy_s(char *dst, size_t n, const char *src) {
(void) n;
sg_strcpy_s((SOCHAR_T *)dst, n, (const SOCHAR_T *)src);
}
static void strcpy_s(wchar_t *dst, size_t n, const wchar_t *src) {
# if __STDC_WANT_SECURE_LIB__
::wcscpy_s(dst, n, src);
#else
(void) n;
::wcscpy(dst, src);
#endif
}
#if SG_HAVE_ICU
static void strcpy_s(UChar *dst, size_t n, const UChar *src) {
::u_strncpy(dst, src, n);
}
#endif
static int strcmp(const char *s1, const char *s2) {
return sg_strcmp((const SOCHAR_T *)s1, (const SOCHAR_T *)s2);
}
static int strcmp(const wchar_t *s1, const wchar_t *s2) {
return ::wcscmp(s1, s2);
}
#if SG_HAVE_ICU
static int strcmp(const UChar *s1, const UChar *s2) {
return ::u_strcmp(s1, s2);
}
#endif
static int strcasecmp(const char *s1, const char *s2) {
return sg_strcasecmp((const SOCHAR_T *)s1, (const SOCHAR_T *)s2);
}
#if _WIN32
static int strcasecmp(const wchar_t *s1, const wchar_t *s2) {
return ::_wcsicmp(s1, s2);
}
#endif // _WIN32
#if SG_HAVE_ICU
static int strcasecmp(const UChar *s1, const UChar *s2) {
return u_strcasecmp(s1, s2, 0);
}
#endif
};
enum SG_FileType {
SG_FILETYPE_INVALID,
SG_FILETYPE_FILE,
SG_FILETYPE_DIR
};
#ifdef _WIN32
#ifndef INVALID_FILE_ATTRIBUTES
# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
#endif
#define SG_PATH_CHAR '\\'
/*! @brief Windows glob implementation. */
template<class SOCHAR>
struct SimpleGlobBase
{
SimpleGlobBase() : m_hFind(INVALID_HANDLE_VALUE) { }
int FindFirstFileS(const char * a_pszFileSpec, unsigned int) {
m_hFind = FindFirstFileA(a_pszFileSpec, &m_oFindDataA);
if (m_hFind != INVALID_HANDLE_VALUE) {
return SG_SUCCESS;
}
DWORD dwErr = GetLastError();
if (dwErr == ERROR_FILE_NOT_FOUND) {
return SG_ERR_NOMATCH;
}
return SG_ERR_FAILURE;
}
int FindFirstFileS(const wchar_t * a_pszFileSpec, unsigned int) {
m_hFind = FindFirstFileW(a_pszFileSpec, &m_oFindDataW);
if (m_hFind != INVALID_HANDLE_VALUE) {
return SG_SUCCESS;
}
DWORD dwErr = GetLastError();
if (dwErr == ERROR_FILE_NOT_FOUND) {
return SG_ERR_NOMATCH;
}
return SG_ERR_FAILURE;
}
bool FindNextFileS(char) {
return FindNextFileA(m_hFind, &m_oFindDataA) != FALSE;
}
bool FindNextFileS(wchar_t) {
return FindNextFileW(m_hFind, &m_oFindDataW) != FALSE;
}
void FindDone() {
FindClose(m_hFind);
}
const char * GetFileNameS(char) const {
return m_oFindDataA.cFileName;
}
const wchar_t * GetFileNameS(wchar_t) const {
return m_oFindDataW.cFileName;
}
bool IsDirS(char) const {
return this->GetFileTypeS(m_oFindDataA.dwFileAttributes) == SG_FILETYPE_DIR;
}
bool IsDirS(wchar_t) const {
return this->GetFileTypeS(m_oFindDataW.dwFileAttributes) == SG_FILETYPE_DIR;
}
SG_FileType GetFileTypeS(const char * a_pszPath) {
return this->GetFileTypeS(GetFileAttributesA(a_pszPath));
}
SG_FileType GetFileTypeS(const wchar_t * a_pszPath) {
return this->GetFileTypeS(GetFileAttributesW(a_pszPath));
}
SG_FileType GetFileTypeS(DWORD a_dwAttribs) const {
if (a_dwAttribs == INVALID_FILE_ATTRIBUTES) {
return SG_FILETYPE_INVALID;
}
if (a_dwAttribs & FILE_ATTRIBUTE_DIRECTORY) {
return SG_FILETYPE_DIR;
}
return SG_FILETYPE_FILE;
}
private:
HANDLE m_hFind;
WIN32_FIND_DATAA m_oFindDataA;
WIN32_FIND_DATAW m_oFindDataW;
};
#else // !_WIN32
#define SG_PATH_CHAR '/'
/*! @brief Unix glob implementation. */
template<class SOCHAR>
struct SimpleGlobBase
{
SimpleGlobBase() {
memset(&m_glob, 0, sizeof(m_glob));
m_uiCurr = (size_t)-1;
}
~SimpleGlobBase() {
globfree(&m_glob);
}
void FilePrep() {
m_bIsDir = false;
size_t len = strlen(m_glob.gl_pathv[m_uiCurr]);
if (m_glob.gl_pathv[m_uiCurr][len-1] == '/') {
m_bIsDir = true;
m_glob.gl_pathv[m_uiCurr][len-1] = 0;
}
}
int FindFirstFileS(const char * a_pszFileSpec, unsigned int a_uiFlags) {
int nFlags = GLOB_MARK | GLOB_NOSORT;
if (a_uiFlags & SG_GLOB_ERR) nFlags |= GLOB_ERR;
if (a_uiFlags & SG_GLOB_TILDE) nFlags |= GLOB_TILDE;
int rc = glob(a_pszFileSpec, nFlags, NULL, &m_glob);
if (rc == GLOB_NOSPACE) return SG_ERR_MEMORY;
if (rc == GLOB_ABORTED) return SG_ERR_FAILURE;
if (rc == GLOB_NOMATCH) return SG_ERR_NOMATCH;
m_uiCurr = 0;
FilePrep();
return SG_SUCCESS;
}
#if SG_HAVE_ICU
int FindFirstFileS(const UChar * a_pszFileSpec, unsigned int a_uiFlags) {
char buf[PATH_MAX] = { 0 };
UErrorCode status = U_ZERO_ERROR;
u_strToUTF8(buf, sizeof(buf), NULL, a_pszFileSpec, -1, &status);
if (U_FAILURE(status)) return SG_ERR_FAILURE;
return this->FindFirstFileS(buf, a_uiFlags);
}
#endif
bool FindNextFileS(char) {
SG_ASSERT(m_uiCurr != (size_t)-1);
if (++m_uiCurr >= m_glob.gl_pathc) {
return false;
}
FilePrep();
return true;
}
#if SG_HAVE_ICU
bool FindNextFileS(UChar) {
return this->FindNextFileS((char)0);
}
#endif
void FindDone() {
globfree(&m_glob);
memset(&m_glob, 0, sizeof(m_glob));
m_uiCurr = (size_t)-1;
}
const char * GetFileNameS(char) const {
SG_ASSERT(m_uiCurr != (size_t)-1);
return m_glob.gl_pathv[m_uiCurr];
}
#if SG_HAVE_ICU
const UChar * GetFileNameS(UChar) const {
const char * pszFile = this->GetFileNameS((char)0);
if (!pszFile) return NULL;
UErrorCode status = U_ZERO_ERROR;
memset(m_szBuf, 0, sizeof(m_szBuf));
u_strFromUTF8(m_szBuf, PATH_MAX, NULL, pszFile, -1, &status);
if (U_FAILURE(status)) return NULL;
return m_szBuf;
}
#endif
bool IsDirS(char) const {
SG_ASSERT(m_uiCurr != (size_t)-1);
return m_bIsDir;
}
#if SG_HAVE_ICU
bool IsDirS(UChar) const {
return this->IsDirS((char)0);
}
#endif
SG_FileType GetFileTypeS(const char * a_pszPath) const {
struct stat sb;
if (0 != stat(a_pszPath, &sb)) {
return SG_FILETYPE_INVALID;
}
if (S_ISDIR(sb.st_mode)) {
return SG_FILETYPE_DIR;
}
if (S_ISREG(sb.st_mode)) {
return SG_FILETYPE_FILE;
}
return SG_FILETYPE_INVALID;
}
#if SG_HAVE_ICU
SG_FileType GetFileTypeS(const UChar * a_pszPath) const {
char buf[PATH_MAX] = { 0 };
UErrorCode status = U_ZERO_ERROR;
u_strToUTF8(buf, sizeof(buf), NULL, a_pszPath, -1, &status);
if (U_FAILURE(status)) return SG_FILETYPE_INVALID;
return this->GetFileTypeS(buf);
}
#endif
private:
glob_t m_glob;
size_t m_uiCurr;
bool m_bIsDir;
#if SG_HAVE_ICU
mutable UChar m_szBuf[PATH_MAX];
#endif
};
#endif // _WIN32
#endif // DOXYGEN
// ---------------------------------------------------------------------------
// MAIN TEMPLATE CLASS
// ---------------------------------------------------------------------------
/*! @brief Implementation of the SimpleGlob class */
template<class SOCHAR>
class CSimpleGlobTempl : private SimpleGlobBase<SOCHAR>
{
public:
/*! @brief Initialize the class.
@param a_uiFlags Combination of SG_GLOB flags.
@param a_nReservedSlots Number of slots in the argv array that
should be reserved. In the returned array these slots
argv[0] ... argv[a_nReservedSlots-1] will be left empty for
the caller to fill in.
*/
CSimpleGlobTempl(unsigned int a_uiFlags = 0, int a_nReservedSlots = 0);
/*! @brief Deallocate all memory buffers. */
~CSimpleGlobTempl();
/*! @brief Initialize (or re-initialize) the class in preparation for
adding new filespecs.
All existing files are cleared. Note that allocated memory is only
deallocated at object destruction.
@param a_uiFlags Combination of SG_GLOB flags.
@param a_nReservedSlots Number of slots in the argv array that
should be reserved. In the returned array these slots
argv[0] ... argv[a_nReservedSlots-1] will be left empty for
the caller to fill in.
*/
int Init(unsigned int a_uiFlags = 0, int a_nReservedSlots = 0);
/*! @brief Add a new filespec to the glob.
The filesystem will be immediately scanned for all matching files and
directories and they will be added to the glob.
@param a_pszFileSpec Filespec to add to the glob.
@return SG_SUCCESS Matching files were added to the glob.
@return SG_ERR_NOMATCH Nothing matched the pattern. To ignore this
error compare return value to >= SG_SUCCESS.
@return SG_ERR_MEMORY Out of memory failure.
@return SG_ERR_FAILURE General failure.
*/
int Add(const SOCHAR *a_pszFileSpec);
/*! @brief Add an array of filespec to the glob.
The filesystem will be immediately scanned for all matching files and
directories in each filespec and they will be added to the glob.
@param a_nCount Number of filespec in the array.
@param a_rgpszFileSpec Array of filespec to add to the glob.
@return SG_SUCCESS Matching files were added to the glob.
@return SG_ERR_NOMATCH Nothing matched the pattern. To ignore this
error compare return value to >= SG_SUCCESS.
@return SG_ERR_MEMORY Out of memory failure.
@return SG_ERR_FAILURE General failure.
*/
int Add(int a_nCount, const SOCHAR * const * a_rgpszFileSpec);
/*! @brief Return the number of files in the argv array.
*/
inline int FileCount() const { return m_nArgsLen; }
/*! @brief Return the full argv array. */
inline SOCHAR ** Files() {
SetArgvArrayType(POINTERS);
return m_rgpArgs;
}
/*! @brief Return the a single file. */
inline SOCHAR * File(int n) {
SG_ASSERT(n >= 0 && n < m_nArgsLen);
return Files()[n];
}
private:
CSimpleGlobTempl(const CSimpleGlobTempl &); // disabled
CSimpleGlobTempl & operator=(const CSimpleGlobTempl &); // disabled
/*! @brief The argv array has it's members stored as either an offset into
the string buffer, or as pointers to their string in the buffer. The
offsets are used because if the string buffer is dynamically resized,
all pointers into that buffer would become invalid.
*/
enum ARG_ARRAY_TYPE { OFFSETS, POINTERS };
/*! @brief Change the type of data stored in the argv array. */
void SetArgvArrayType(ARG_ARRAY_TYPE a_nNewType);
/*! @brief Add a filename to the array if it passes all requirements. */
int AppendName(const SOCHAR *a_pszFileName, bool a_bIsDir);
/*! @brief Grow the argv array to the required size. */
bool GrowArgvArray(int a_nNewLen);
/*! @brief Grow the string buffer to the required size. */
bool GrowStringBuffer(size_t a_uiMinSize);
/*! @brief Compare two (possible NULL) strings */
static int fileSortCompare(const void *a1, const void *a2);
private:
unsigned int m_uiFlags;
ARG_ARRAY_TYPE m_nArgArrayType; //!< argv is indexes or pointers
SOCHAR ** m_rgpArgs; //!< argv
int m_nReservedSlots; //!< # client slots in argv array
int m_nArgsSize; //!< allocated size of array
int m_nArgsLen; //!< used length
SOCHAR * m_pBuffer; //!< argv string buffer
size_t m_uiBufferSize; //!< allocated size of buffer
size_t m_uiBufferLen; //!< used length of buffer
SOCHAR m_szPathPrefix[MAX_PATH]; //!< wildcard path prefix
};
// ---------------------------------------------------------------------------
// IMPLEMENTATION
// ---------------------------------------------------------------------------
template<class SOCHAR>
CSimpleGlobTempl<SOCHAR>::CSimpleGlobTempl(
unsigned int a_uiFlags,
int a_nReservedSlots
)
{
m_rgpArgs = NULL;
m_nArgsSize = 0;
m_pBuffer = NULL;
m_uiBufferSize = 0;
Init(a_uiFlags, a_nReservedSlots);
}
template<class SOCHAR>
CSimpleGlobTempl<SOCHAR>::~CSimpleGlobTempl()
{
if (m_rgpArgs) free(m_rgpArgs);
if (m_pBuffer) free(m_pBuffer);
}
template<class SOCHAR>
int
CSimpleGlobTempl<SOCHAR>::Init(
unsigned int a_uiFlags,
int a_nReservedSlots
)
{
m_nArgArrayType = POINTERS;
m_uiFlags = a_uiFlags;
m_nArgsLen = a_nReservedSlots;
m_nReservedSlots = a_nReservedSlots;
m_uiBufferLen = 0;
if (m_nReservedSlots > 0) {
if (!GrowArgvArray(m_nReservedSlots)) {
return SG_ERR_MEMORY;
}
for (int n = 0; n < m_nReservedSlots; ++n) {
m_rgpArgs[n] = NULL;
}
}
return SG_SUCCESS;
}
template<class SOCHAR>
int
CSimpleGlobTempl<SOCHAR>::Add(
const SOCHAR *a_pszFileSpec
)
{
#ifdef _WIN32
// Windows FindFirst/FindNext recognizes forward slash as the same as
// backward slash and follows the directories. We need to do the same
// when calculating the prefix and when we have no wildcards.
SOCHAR szFileSpec[MAX_PATH];
SimpleGlobUtil::strcpy_s(szFileSpec, MAX_PATH, a_pszFileSpec);
const SOCHAR * pszPath = SimpleGlobUtil::strchr(szFileSpec, '/');
while (pszPath) {
szFileSpec[pszPath - szFileSpec] = SG_PATH_CHAR;
pszPath = SimpleGlobUtil::strchr(pszPath + 1, '/');
}
a_pszFileSpec = szFileSpec;
#endif
// if this doesn't contain wildcards then we can just add it directly
m_szPathPrefix[0] = 0;
if (!SimpleGlobUtil::strchr(a_pszFileSpec, '*') &&
!SimpleGlobUtil::strchr(a_pszFileSpec, '?'))
{
SG_FileType nType = this->GetFileTypeS(a_pszFileSpec);
if (nType == SG_FILETYPE_INVALID) {
if (m_uiFlags & SG_GLOB_NOCHECK) {
return AppendName(a_pszFileSpec, false);
}
return SG_ERR_NOMATCH;
}
return AppendName(a_pszFileSpec, nType == SG_FILETYPE_DIR);
}
#ifdef _WIN32
// Windows doesn't return the directory with the filename, so we need to
// extract the path from the search string ourselves and prefix it to the
// filename we get back.
const SOCHAR * pszFilename =
SimpleGlobUtil::strrchr(a_pszFileSpec, SG_PATH_CHAR);
if (pszFilename) {
SimpleGlobUtil::strcpy_s(m_szPathPrefix, MAX_PATH, a_pszFileSpec);
m_szPathPrefix[pszFilename - a_pszFileSpec + 1] = 0;
}
#endif
// search for the first match on the file
int rc = this->FindFirstFileS(a_pszFileSpec, m_uiFlags);
if (rc != SG_SUCCESS) {
if (rc == SG_ERR_NOMATCH && (m_uiFlags & SG_GLOB_NOCHECK)) {
int ok = AppendName(a_pszFileSpec, false);
if (ok != SG_SUCCESS) rc = ok;
}
return rc;
}
// add it and find all subsequent matches
int nError, nStartLen = m_nArgsLen;
bool bSuccess;
do {
nError = AppendName(this->GetFileNameS((SOCHAR)0), this->IsDirS((SOCHAR)0));
bSuccess = this->FindNextFileS((SOCHAR)0);
}
while (nError == SG_SUCCESS && bSuccess);
SimpleGlobBase<SOCHAR>::FindDone();
// sort these files if required
if (m_nArgsLen > nStartLen && !(m_uiFlags & SG_GLOB_NOSORT)) {
if (m_uiFlags & SG_GLOB_FULLSORT) {
nStartLen = m_nReservedSlots;
}
SetArgvArrayType(POINTERS);
qsort(
m_rgpArgs + nStartLen,
m_nArgsLen - nStartLen,
sizeof(m_rgpArgs[0]), fileSortCompare);
}
return nError;
}
template<class SOCHAR>
int
CSimpleGlobTempl<SOCHAR>::Add(
int a_nCount,
const SOCHAR * const * a_rgpszFileSpec
)
{
int nResult;
for (int n = 0; n < a_nCount; ++n) {
nResult = Add(a_rgpszFileSpec[n]);
if (nResult != SG_SUCCESS) {
return nResult;
}
}
return SG_SUCCESS;
}
template<class SOCHAR>
int
CSimpleGlobTempl<SOCHAR>::AppendName(
const SOCHAR * a_pszFileName,
bool a_bIsDir
)
{
// we need the argv array as offsets in case we resize it
SetArgvArrayType(OFFSETS);
// check for special cases which cause us to ignore this entry
if ((m_uiFlags & SG_GLOB_ONLYDIR) && !a_bIsDir) {
return SG_SUCCESS;
}
if ((m_uiFlags & SG_GLOB_ONLYFILE) && a_bIsDir) {
return SG_SUCCESS;
}
if ((m_uiFlags & SG_GLOB_NODOT) && a_bIsDir) {
if (a_pszFileName[0] == '.') {
if (a_pszFileName[1] == '\0') {
return SG_SUCCESS;
}
if (a_pszFileName[1] == '.' && a_pszFileName[2] == '\0') {
return SG_SUCCESS;
}
}
}
// ensure that we have enough room in the argv array
if (!GrowArgvArray(m_nArgsLen + 1)) {
return SG_ERR_MEMORY;
}
// ensure that we have enough room in the string buffer (+1 for null)
size_t uiPrefixLen = SimpleGlobUtil::strlen(m_szPathPrefix);
size_t uiLen = uiPrefixLen + SimpleGlobUtil::strlen(a_pszFileName) + 1;
if (a_bIsDir && (m_uiFlags & SG_GLOB_MARK) == SG_GLOB_MARK) {
++uiLen; // need space for the backslash
}
if (!GrowStringBuffer(m_uiBufferLen + uiLen)) {
return SG_ERR_MEMORY;
}
// add this entry. m_uiBufferLen is offset from beginning of buffer.
m_rgpArgs[m_nArgsLen++] = (SOCHAR*)m_uiBufferLen;
SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen,
m_uiBufferSize - m_uiBufferLen, m_szPathPrefix);
SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen + uiPrefixLen,
m_uiBufferSize - m_uiBufferLen - uiPrefixLen, a_pszFileName);
m_uiBufferLen += uiLen;
// add the directory slash if desired
if (a_bIsDir && (m_uiFlags & SG_GLOB_MARK) == SG_GLOB_MARK) {
const static SOCHAR szDirSlash[] = { SG_PATH_CHAR, 0 };
SimpleGlobUtil::strcpy_s(m_pBuffer + m_uiBufferLen - 2,
m_uiBufferSize - (m_uiBufferLen - 2), szDirSlash);
}
return SG_SUCCESS;
}
template<class SOCHAR>
void
CSimpleGlobTempl<SOCHAR>::SetArgvArrayType(
ARG_ARRAY_TYPE a_nNewType
)
{
if (m_nArgArrayType == a_nNewType) return;
if (a_nNewType == POINTERS) {
SG_ASSERT(m_nArgArrayType == OFFSETS);
for (int n = 0; n < m_nArgsLen; ++n) {
m_rgpArgs[n] = (m_rgpArgs[n] == (SOCHAR*)-1) ?
NULL : m_pBuffer + (size_t) m_rgpArgs[n];
}
}
else {
SG_ASSERT(a_nNewType == OFFSETS);
SG_ASSERT(m_nArgArrayType == POINTERS);
for (int n = 0; n < m_nArgsLen; ++n) {
m_rgpArgs[n] = (m_rgpArgs[n] == NULL) ?
(SOCHAR*) -1 : (SOCHAR*) (m_rgpArgs[n] - m_pBuffer);
}
}
m_nArgArrayType = a_nNewType;
}
template<class SOCHAR>
bool
CSimpleGlobTempl<SOCHAR>::GrowArgvArray(
int a_nNewLen
)
{
if (a_nNewLen >= m_nArgsSize) {
static const int SG_ARGV_INITIAL_SIZE = 32;
int nNewSize = (m_nArgsSize > 0) ?
m_nArgsSize * 2 : SG_ARGV_INITIAL_SIZE;
while (a_nNewLen >= nNewSize) {
nNewSize *= 2;
}
void * pNewBuffer = realloc(m_rgpArgs, nNewSize * sizeof(SOCHAR*));
if (!pNewBuffer) return false;
m_nArgsSize = nNewSize;
m_rgpArgs = (SOCHAR**) pNewBuffer;
}
return true;
}
template<class SOCHAR>
bool
CSimpleGlobTempl<SOCHAR>::GrowStringBuffer(
size_t a_uiMinSize
)
{
if (a_uiMinSize >= m_uiBufferSize) {
static const int SG_BUFFER_INITIAL_SIZE = 1024;
size_t uiNewSize = (m_uiBufferSize > 0) ?
m_uiBufferSize * 2 : SG_BUFFER_INITIAL_SIZE;
while (a_uiMinSize >= uiNewSize) {
uiNewSize *= 2;
}
void * pNewBuffer = realloc(m_pBuffer, uiNewSize * sizeof(SOCHAR));
if (!pNewBuffer) return false;
m_uiBufferSize = uiNewSize;
m_pBuffer = (SOCHAR*) pNewBuffer;
}
return true;
}
template<class SOCHAR>
int
CSimpleGlobTempl<SOCHAR>::fileSortCompare(
const void *a1,
const void *a2
)
{
const SOCHAR * s1 = *(const SOCHAR **)a1;
const SOCHAR * s2 = *(const SOCHAR **)a2;
if (s1 && s2) {
return SimpleGlobUtil::strcasecmp(s1, s2);
}
// NULL sorts first
return s1 == s2 ? 0 : (s1 ? 1 : -1);
}
// ---------------------------------------------------------------------------
// TYPE DEFINITIONS
// ---------------------------------------------------------------------------
/*! @brief ASCII/MBCS version of CSimpleGlob */
typedef CSimpleGlobTempl<char> CSimpleGlobA;
/*! @brief wchar_t version of CSimpleGlob */
typedef CSimpleGlobTempl<wchar_t> CSimpleGlobW;
#if SG_HAVE_ICU
/*! @brief UChar version of CSimpleGlob */
typedef CSimpleGlobTempl<UChar> CSimpleGlobU;
#endif
#ifdef _UNICODE
/*! @brief TCHAR version dependent on if _UNICODE is defined */
# if SG_HAVE_ICU
# define CSimpleGlob CSimpleGlobU
# else
# define CSimpleGlob CSimpleGlobW
# endif
#else
/*! @brief TCHAR version dependent on if _UNICODE is defined */
# define CSimpleGlob CSimpleGlobA
#endif
#endif // INCLUDED_SimpleGlob

File diff suppressed because it is too large Load Diff