diff options
-rw-r--r-- | VC2008/MCServer.vcproj | 8 | ||||
-rw-r--r-- | source/Bindings.cpp | 38 | ||||
-rw-r--r-- | source/Bindings.h | 2 | ||||
-rw-r--r-- | source/Generating/ComposableGenerator.cpp | 20 | ||||
-rw-r--r-- | source/Generating/FinishGen.cpp | 175 | ||||
-rw-r--r-- | source/Generating/FinishGen.h | 37 | ||||
-rw-r--r-- | source/OSSupport/ListenThread.cpp | 6 | ||||
-rw-r--r-- | source/ProbabDistrib.cpp | 141 | ||||
-rw-r--r-- | source/ProbabDistrib.h | 74 | ||||
-rw-r--r-- | source/StringUtils.cpp | 24 | ||||
-rw-r--r-- | source/StringUtils.h | 6 |
11 files changed, 520 insertions, 11 deletions
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index 6f6e8cb6e..88a9798d4 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -577,6 +577,14 @@ >
</File>
<File
+ RelativePath="..\source\ProbabDistrib.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\ProbabDistrib.h"
+ >
+ </File>
+ <File
RelativePath="..\source\ReferenceManager.cpp"
>
</File>
diff --git a/source/Bindings.cpp b/source/Bindings.cpp index c22032619..5c7ee3b1d 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 03/09/13 15:33:58. +** Generated automatically by tolua++-1.0.92 on 03/14/13 10:02:26. */ #ifndef __cplusplus @@ -9999,6 +9999,38 @@ static int tolua_AllToLua_cWorld_IsDeepSnowEnabled00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: GetDimension of class cWorld */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetDimension00 +static int tolua_AllToLua_cWorld_GetDimension00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDimension'", NULL); +#endif + { + cWorld::eDimension tolua_ret = (cWorld::eDimension) self->GetDimension(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetDimension'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: GetHeight of class cWorld */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetHeight00 static int tolua_AllToLua_cWorld_GetHeight00(lua_State* tolua_S) @@ -22059,6 +22091,9 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cWorld","cWorld","",NULL); tolua_beginmodule(tolua_S,"cWorld"); + tolua_constant(tolua_S,"dimNether",cWorld::dimNether); + tolua_constant(tolua_S,"dimOverworld",cWorld::dimOverworld); + tolua_constant(tolua_S,"dimEnd",cWorld::dimEnd); tolua_function(tolua_S,"GetClassStatic",tolua_AllToLua_cWorld_GetClassStatic00); tolua_function(tolua_S,"GetTime",tolua_AllToLua_cWorld_GetTime00); tolua_function(tolua_S,"GetWorldAge",tolua_AllToLua_cWorld_GetWorldAge00); @@ -22068,6 +22103,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"GetGameMode",tolua_AllToLua_cWorld_GetGameMode00); tolua_function(tolua_S,"IsPVPEnabled",tolua_AllToLua_cWorld_IsPVPEnabled00); tolua_function(tolua_S,"IsDeepSnowEnabled",tolua_AllToLua_cWorld_IsDeepSnowEnabled00); + tolua_function(tolua_S,"GetDimension",tolua_AllToLua_cWorld_GetDimension00); tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cWorld_GetHeight00); tolua_function(tolua_S,"UnloadUnusedChunks",tolua_AllToLua_cWorld_UnloadUnusedChunks00); tolua_function(tolua_S,"GetMaxPlayers",tolua_AllToLua_cWorld_GetMaxPlayers00); diff --git a/source/Bindings.h b/source/Bindings.h index a65a70022..525565986 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 03/09/13 15:33:59. +** Generated automatically by tolua++-1.0.92 on 03/14/13 10:02:26. */ /* Exported function */ diff --git a/source/Generating/ComposableGenerator.cpp b/source/Generating/ComposableGenerator.cpp index 0cd6bfb4f..e9196757b 100644 --- a/source/Generating/ComposableGenerator.cpp +++ b/source/Generating/ComposableGenerator.cpp @@ -24,6 +24,11 @@ +
+
+
+
+
cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
m_BiomeGen(NULL),
@@ -308,7 +313,7 @@ void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile) AString Structures = a_IniFile.GetValueSet("Generator", "Structures", "Ravines,WormNestCaves,OreNests,Trees");
int Seed = m_ChunkGenerator.GetSeed();
- AStringVector Str = StringSplit(Structures, ",");
+ AStringVector Str = StringSplitAndTrim(Structures, ",");
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
if (NoCaseCompare(*itr, "trees") == 0)
@@ -355,13 +360,14 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) int Seed = m_ChunkGenerator.GetSeed();
AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator");
- AStringVector Str = StringSplit(Structures, ",");
+ AStringVector Str = StringSplitAndTrim(Structures, ",");
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
// Finishers, alpha-sorted:
if (NoCaseCompare(*itr, "BottomLava") == 0)
{
- int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", 10);
+ int DefaultBottomLavaLevel = (m_World->GetDimension() == cWorld::dimNether) ? 30 : 10;
+ int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel));
}
else if (NoCaseCompare(*itr, "DeadBushes") == 0)
@@ -372,6 +378,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) {
m_FinishGens.push_back(new cFinishGenIce);
}
+ else if (NoCaseCompare(*itr, "LavaSprings") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World));
+ }
else if (NoCaseCompare(*itr, "Lilypads") == 0)
{
m_FinishGens.push_back(new cFinishGenLilypads(Seed));
@@ -388,6 +398,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) {
m_FinishGens.push_back(new cFinishGenSprinkleFoliage(Seed));
}
+ else if (NoCaseCompare(*itr, "WaterSprings") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World));
+ }
} // for itr - Str[]
}
diff --git a/source/Generating/FinishGen.cpp b/source/Generating/FinishGen.cpp index 83788fcab..8c13b6158 100644 --- a/source/Generating/FinishGen.cpp +++ b/source/Generating/FinishGen.cpp @@ -13,6 +13,18 @@ #include "../Noise.h" #include "../BlockID.h" #include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway() +#include "../World.h" + + + + + +#define DEF_NETHER_WATER_SPRINGS "0, 0; 255, 0" +#define DEF_NETHER_LAVA_SPRINGS "0, 0; 30, 0; 31, 50; 120, 50; 127, 0" +#define DEF_OVERWORLD_WATER_SPRINGS "0, 0; 10, 10; 11, 75; 16, 83; 20, 83; 24, 78; 32, 62; 40, 40; 44, 15; 48, 7; 56, 2; 64, 1; 255, 0" +#define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0" +#define DEF_END_WATER_SPRINGS "0, 0; 255, 0" +#define DEF_END_LAVA_SPRINGS "0, 0; 255, 0" @@ -629,3 +641,166 @@ void cFinishGenDeadBushes::GenFinish( } // for i } + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFinishGenFluidSprings: + +cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World) : + m_Noise(a_Seed + a_Fluid * 100), // Need to take fluid into account, otherwise water and lava springs generate next to each other + m_HeightDistribution(255), + m_Fluid(a_Fluid) +{ + bool IsWater = (a_Fluid == E_BLOCK_WATER); + AString SectionName = IsWater ? "WaterSprings" : "LavaSprings"; + AString DefaultHeightDistribution; + int DefaultChance; + switch (a_World.GetDimension()) + { + case cWorld::dimNether: + { + DefaultHeightDistribution = IsWater ? DEF_NETHER_WATER_SPRINGS : DEF_NETHER_LAVA_SPRINGS; + DefaultChance = IsWater ? 0 : 15; + break; + } + case cWorld::dimOverworld: + { + DefaultHeightDistribution = IsWater ? DEF_OVERWORLD_WATER_SPRINGS : DEF_OVERWORLD_LAVA_SPRINGS; + DefaultChance = IsWater ? 24 : 9; + break; + } + case cWorld::dimEnd: + { + DefaultHeightDistribution = IsWater ? DEF_END_WATER_SPRINGS : DEF_END_LAVA_SPRINGS; + DefaultChance = 0; + break; + } + default: + { + ASSERT(!"Unhandled world dimension"); + break; + } + } // switch (dimension) + AString HeightDistribution = a_IniFile.GetValueSet(SectionName, "HeightDistribution", DefaultHeightDistribution); + m_HeightDistribution.SetDefString(HeightDistribution); + m_Chance = a_IniFile.GetValueSetI(SectionName, "Chance", DefaultChance); +} + + + + + +void cFinishGenFluidSprings::GenFinish( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change + cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change + cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data + const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to + cEntityList & a_Entities, // Entities may be added or deleted + cBlockEntityList & a_BlockEntities // Block entities may be added or deleted +) +{ + int ChanceRnd = (m_Noise.IntNoise3DInt(128 * a_ChunkX, 512, 256 * a_ChunkZ) / 13) % 100; + if (ChanceRnd > m_Chance) + { + // Not in this chunk + return; + } + + // Get the height at which to try: + int Height = m_Noise.IntNoise3DInt(128 * a_ChunkX, 512, 256 * a_ChunkZ) / 11; + Height %= m_HeightDistribution.GetSum(); + Height = m_HeightDistribution.MapValue(Height); + + // Try adding the spring at the height, if unsuccessful, move lower: + for (int y = Height; y > 1; y--) + { + // TODO: randomize the order in which the coords are being checked + for (int z = 1; z < cChunkDef::Width - 1; z++) + { + for (int x = 1; x < cChunkDef::Width - 1; x++) + { + switch (cChunkDef::GetBlock(a_BlockTypes, x, y, z)) + { + case E_BLOCK_NETHERRACK: + { + if (m_Fluid != E_BLOCK_LAVA) + { + // Only lava springs in the netherrack + continue; + } + // fallthrough: + } + case E_BLOCK_STONE: + { + if (TryPlaceSpring(a_BlockTypes, a_BlockMeta, x, y, z)) + { + // Succeeded, bail out + return; + } + } + } // switch (BlockType) + } // for x + } // for y + } // for y +} + + + + + +bool cFinishGenFluidSprings::TryPlaceSpring( + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::BlockNibbles & a_BlockMetas, + int x, int y, int z +) +{ + // In order to place a spring, it needs exactly one of the XZ neighbors or a below neighbor to be air + // Also, its neighbor on top of it must be non-air + if (cChunkDef::GetBlock(a_BlockTypes, x, y + 1, z) == E_BLOCK_AIR) + { + return false; + } + + static const struct + { + int x, y, z; + } Coords[] = + { + {-1, 0, 0}, + { 1, 0, 0}, + { 0, -1, 0}, + { 0, 0, -1}, + { 0, 0, 1}, + } ; + int NumAirNeighbors = 0; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + switch (cChunkDef::GetBlock(a_BlockTypes, x + Coords[i].x, y + Coords[i].y, z + Coords[i].z)) + { + case E_BLOCK_AIR: + { + NumAirNeighbors += 1; + if (NumAirNeighbors > 1) + { + return false; + } + } + } + } + if (NumAirNeighbors == 0) + { + return false; + } + + // Has exactly one air neighbor, place a spring: + cChunkDef::SetBlock(a_BlockTypes, x, y, z, m_Fluid); + cChunkDef::SetNibble(a_BlockMetas, x, y, z, 0); + return true; +} + + + + diff --git a/source/Generating/FinishGen.h b/source/Generating/FinishGen.h index 573060843..dde073f67 100644 --- a/source/Generating/FinishGen.h +++ b/source/Generating/FinishGen.h @@ -17,6 +17,7 @@ #include "ComposableGenerator.h" #include "../Noise.h" +#include "../ProbabDistrib.h" @@ -223,3 +224,39 @@ protected: + +class cFinishGenFluidSprings : + public cFinishGen +{ +public: + cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World); + +protected: + + cNoise m_Noise; + cProbabDistrib m_HeightDistribution; + BLOCKTYPE m_Fluid; + int m_Chance; ///< Chance, [0..100], that a spring will be generated in a chunk + + // cFinishGen override: + virtual void GenFinish( + int a_ChunkX, int a_ChunkZ, + cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change + cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change + cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data + const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to + cEntityList & a_Entities, // Entities may be added or deleted + cBlockEntityList & a_BlockEntities // Block entities may be added or deleted + ) override; + + /// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful + bool TryPlaceSpring( + cChunkDef::BlockTypes & a_BlockTypes, + cChunkDef::BlockNibbles & a_BlockMetas, + int x, int y, int z + ); +} ; + + + + diff --git a/source/OSSupport/ListenThread.cpp b/source/OSSupport/ListenThread.cpp index 3013f8cbf..27c77a897 100644 --- a/source/OSSupport/ListenThread.cpp +++ b/source/OSSupport/ListenThread.cpp @@ -98,7 +98,7 @@ void cListenThread::SetReuseAddr(bool a_Reuse) bool cListenThread::CreateSockets(const AString & a_PortsString)
{
- AStringVector Ports = StringSplit(a_PortsString, ",");
+ AStringVector Ports = StringSplitAndTrim(a_PortsString, ",");
if (Ports.empty())
{
@@ -119,10 +119,10 @@ bool cListenThread::CreateSockets(const AString & a_PortsString) for (AStringVector::const_iterator itr = Ports.begin(), end = Ports.end(); itr != end; ++itr)
{
- int Port = atoi(Trim(*itr).c_str());
+ int Port = atoi(itr->c_str());
if ((Port <= 0) || (Port > 65535))
{
- LOGWARNING("Invalid port specified: \"%s\".", Trim(*itr).c_str());
+ LOGWARNING("Invalid port specified: \"%s\".", itr->c_str());
continue;
}
m_Sockets.push_back(cSocket::CreateSocket(m_Family));
diff --git a/source/ProbabDistrib.cpp b/source/ProbabDistrib.cpp new file mode 100644 index 000000000..fbc9f9428 --- /dev/null +++ b/source/ProbabDistrib.cpp @@ -0,0 +1,141 @@ +
+// ProbabDistrib.cpp
+
+// Implements the cProbabDistrib class representing a discrete probability distribution curve and random generator
+
+#include "Globals.h"
+#include "ProbabDistrib.h"
+#include "MersenneTwister.h"
+
+
+
+
+
+
+cProbabDistrib::cProbabDistrib(int a_MaxValue) :
+ m_MaxValue(a_MaxValue),
+ m_Sum(-1)
+{
+}
+
+
+
+
+
+
+void cProbabDistrib::SetPoints(const cProbabDistrib::cPoints & a_Points)
+{
+ ASSERT(!a_Points.empty());
+ m_Sum = 0;
+ m_Cumulative.clear();
+ m_Cumulative.reserve(a_Points.size() + 1);
+ int ProbSum = 0;
+ int LastProb = 0;
+ int LastValue = 0;
+ if (a_Points[0].m_Value != 0)
+ {
+ m_Cumulative.push_back(cPoint(0, 0)); // Always push in the [0, 0] point for easier search algorithm bounds
+ }
+ for (cPoints::const_iterator itr = a_Points.begin(), end = a_Points.end(); itr != end; ++itr)
+ {
+ if (itr->m_Value == LastValue)
+ {
+ continue;
+ }
+
+ // Add the current trapezoid to the sum:
+ ProbSum += (LastProb + itr->m_Probability) * (itr->m_Value - LastValue) / 2;
+ LastProb = itr->m_Probability;
+ LastValue = itr->m_Value;
+ m_Cumulative.push_back(cPoint(itr->m_Value, ProbSum));
+ } // for itr - a_Points[]
+ if (LastValue != m_MaxValue)
+ {
+ m_Cumulative.push_back(cPoint(m_MaxValue, 0)); // Always push in the last point for easier search algorithm bounds
+ }
+ m_Sum = ProbSum;
+}
+
+
+
+
+
+bool cProbabDistrib::SetDefString(const AString & a_DefString)
+{
+ AStringVector Points = StringSplitAndTrim(a_DefString, ";");
+ if (Points.empty())
+ {
+ return false;
+ }
+ cPoints Pts;
+ for (AStringVector::const_iterator itr = Points.begin(), end = Points.end(); itr != end; ++itr)
+ {
+ AStringVector Split = StringSplitAndTrim(*itr, ",");
+ if (Split.size() != 2)
+ {
+ // Bad format
+ return false;
+ }
+ int Value = atoi(Split[0].c_str());
+ int Prob = atoi(Split[1].c_str());
+ if (
+ ((Value == 0) && (Split[0] != "0")) ||
+ ((Prob == 0) && (Split[1] != "0"))
+ )
+ {
+ // Number parse error
+ return false;
+ }
+ Pts.push_back(cPoint(Value, Prob));
+ } // for itr - Points[]
+
+ SetPoints(Pts);
+ return true;
+}
+
+
+
+
+
+int cProbabDistrib::Random(MTRand & a_Rand) const
+{
+ int v = a_Rand.randInt(m_Sum);
+ return MapValue(v);
+}
+
+
+
+
+
+int cProbabDistrib::MapValue(int a_OrigValue) const
+{
+ ASSERT(a_OrigValue >= 0);
+ ASSERT(a_OrigValue < m_Sum);
+
+ // Binary search through m_Cumulative for placement:
+ size_t Lo = 0;
+ size_t Hi = m_Cumulative.size() - 1;
+ while (Hi - Lo > 1)
+ {
+ int Mid = (Lo + Hi) / 2;
+ int MidProbab = m_Cumulative[Mid].m_Probability;
+ if (MidProbab < a_OrigValue)
+ {
+ Lo = Mid;
+ }
+ else
+ {
+ Hi = Mid;
+ }
+ }
+ ASSERT(Hi - Lo == 1);
+
+ // Linearly interpolate between Lo and Hi:
+ int ProbDif = m_Cumulative[Hi].m_Probability - m_Cumulative[Lo].m_Probability;
+ int ValueDif = m_Cumulative[Hi].m_Value - m_Cumulative[Lo].m_Value;
+ return m_Cumulative[Lo].m_Value + (a_OrigValue - m_Cumulative[Lo].m_Probability) * ValueDif / ProbDif;
+}
+
+
+
+
diff --git a/source/ProbabDistrib.h b/source/ProbabDistrib.h new file mode 100644 index 000000000..ac5f24894 --- /dev/null +++ b/source/ProbabDistrib.h @@ -0,0 +1,74 @@ +
+// ProbabDistrib.h
+
+// Declares the cProbabDistrib class representing a discrete probability distribution curve and random generator
+
+/*
+Usage:
+1, Create a cProbabDistrib instance
+2, Initialize the distribution either programmatically, using the SetPoints() function, or using a definition string
+3, Ask for random numbers in that probability distribution using the Random() function
+*/
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class MTRand;
+
+
+
+
+
+class cProbabDistrib
+{
+public:
+ class cPoint
+ {
+ public:
+ int m_Value;
+ int m_Probability;
+
+ cPoint(int a_Value, int a_Probability) :
+ m_Value(a_Value),
+ m_Probability(a_Probability)
+ {
+ }
+ } ;
+
+ typedef std::vector<cPoint> cPoints;
+
+
+ cProbabDistrib(int a_MaxValue);
+
+ /// Sets the distribution curve using an array of [value, probability] points, linearly interpolated. a_Points must not be empty.
+ void SetPoints(const cPoints & a_Points);
+
+ /// Sets the distribution curve using a definition string; returns true on successful parse
+ bool SetDefString(const AString & a_DefString);
+
+ /// Gets a random value from a_Rand, shapes it into the distribution curve and returns the value.
+ int Random(MTRand & a_Rand) const;
+
+ /// Maps value in range [0, m_Sum] into the range [0, m_MaxValue] using the stored probability
+ int MapValue(int a_OrigValue) const;
+
+ int GetSum(void) const { return m_Sum; }
+
+protected:
+
+ int m_MaxValue;
+ cPoints m_Cumulative; ///< Cumulative probability of the values, sorted, for fast bsearch lookup
+ int m_Sum; ///< Sum of all the probabilities across all values in the domain; -1 if not set
+} ;
+
+
+
+
diff --git a/source/StringUtils.cpp b/source/StringUtils.cpp index dc128e61d..e98830f00 100644 --- a/source/StringUtils.cpp +++ b/source/StringUtils.cpp @@ -118,6 +118,26 @@ AStringVector StringSplit(const AString & str, const AString & delim) +AStringVector StringSplitAndTrim(const AString & str, const AString & delim) +{ + AStringVector results; + size_t cutAt = 0; + size_t Prev = 0; + while ((cutAt = str.find_first_of(delim, Prev)) != str.npos) + { + results.push_back(TrimString(str.substr(Prev, cutAt - Prev))); + Prev = cutAt + delim.length(); + } + if (Prev < str.length()) + { + results.push_back(TrimString(str.substr(Prev))); + } + return results; +} + + + + AString TrimString(const AString & str) { size_t len = str.length(); @@ -506,7 +526,11 @@ AString & CreateHexDump(AString & a_Out, const void * a_Data, int a_Size, int a_ k = a_LineLength; } memset(line, ' ', sizeof(line)); + #ifdef _MSC_VER // MSVC provides a "secure" version of sprintf() + line[sprintf_s(line, sizeof(line), "%08x:", i)] = 32; // Remove the terminating NULL that sprintf puts there + #else line[sprintf(line, "%08x:", i)] = 32; // Remove the terminating NULL that sprintf puts there + #endif p = line + 10; q = p + 2 + a_LineLength * 3 + 1; for (int j = 0; j < k; j++) diff --git a/source/StringUtils.h b/source/StringUtils.h index f8a7d7106..06bf77035 100644 --- a/source/StringUtils.h +++ b/source/StringUtils.h @@ -36,6 +36,9 @@ extern AString & AppendPrintf (AString & str, const char * format, ...); /// Split the string at delimiters, return as a stringvector extern AStringVector StringSplit(const AString & str, const AString & delim); +/// Split the string at delimiters and trim each value, return as a stringvector +extern AStringVector StringSplitAndTrim(const AString & str, const AString & delim); + /// Trime whitespace at both ends of the string extern AString TrimString(const AString & str); @@ -63,9 +66,6 @@ extern AString & UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length, AStr /// Creates a nicely formatted HEX dump of the given memory block. Max a_BytesPerLine is 120 extern AString & CreateHexDump(AString & a_Out, const void * a_Data, int a_Size, int a_BytesPerLine); -/// Removes whitespace at the beginning and left of the string -extern AString Trim(const AString & a_Text); - // If you have any other string helper functions, declare them here |