Commit 3768819a authored by Bkg2k's avatar Bkg2k Committed by OyyoDams

Resolve "[XMas Beta] Multiple bugs related to gamelist.xml and metadata in general"

parent a95c40de
......@@ -3,6 +3,7 @@ project("emulationstation")
set(ES_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/MameNameMap.h
${CMAKE_CURRENT_SOURCE_DIR}/src/EmulationStation.h
${CMAKE_CURRENT_SOURCE_DIR}/src/ItemType.h
${CMAKE_CURRENT_SOURCE_DIR}/src/FileData.h
${CMAKE_CURRENT_SOURCE_DIR}/src/FolderData.h
${CMAKE_CURRENT_SOURCE_DIR}/src/RootFolderData.h
......
......@@ -7,16 +7,17 @@
namespace fs = boost::filesystem;
FileData::FileData(FileData::FileType type, const fs::path& path, SystemData* system)
FileData::FileData(ItemType type, const fs::path& path, SystemData* system)
: mSystem(system),
mParent(NULL),
mType(type),
mPath(path),
mMetadata(getCleanName()) // TODO: Move clean name into metadata
mMetadata(getCleanName(), type) // TODO: Move clean name into metadata
{
}
FileData::FileData(const fs::path& path, SystemData* system) : FileData(FileData::FileType::Game, path, system)
FileData::FileData(const fs::path& path, SystemData* system) : FileData(ItemType::Game, path, system)
{
}
......
......@@ -6,6 +6,7 @@
#include <boost/filesystem.hpp>
#include <components/IList.h>
#include "MetadataDescriptor.h"
#include "ItemType.h"
// Bitflag operator for use on strongly-typed enums - Pass enum type and underlying cardinal type
#define DEFINE_BITFLAG_ENUM(enumtype, ut) \
......@@ -30,13 +31,6 @@ class FileData
typedef std::unordered_map<std::string, FileData*> StringMap;
typedef std::vector<FileData*> List;
//! Item types
enum class FileType
{
Game, //!< Launchable game
Folder, //!< Subfolder
};
//! Game filters
enum class Filter
{
......@@ -62,7 +56,7 @@ class FileData
//! Parent folder
FolderData* mParent;
//! Item type - Const ensure mType cannot be modified after being set by the constructor, so that it's alays safe to use c-style cast for FolderData sub-class.
const FileType mType;
const ItemType mType;
private:
//! Item path on the filesystem
......@@ -77,7 +71,7 @@ class FileData
* @param path Item path
* @param system Parent system
*/
FileData(FileType type, const boost::filesystem::path& path, SystemData* system);
FileData(ItemType type, const boost::filesystem::path& path, SystemData* system);
public:
/*!
......@@ -93,7 +87,7 @@ class FileData
inline const std::string& getName() const { return mMetadata.Name(); }
inline const std::string getHash() const { return mMetadata.RomCrc32AsString(); }
inline FileType getType() const { return mType; }
inline ItemType getType() const { return mType; }
inline const boost::filesystem::path& getPath() const { return mPath; }
inline FolderData* getParent() const { return mParent; }
inline SystemData* getSystem() const { return mSystem; }
......@@ -102,8 +96,8 @@ class FileData
* Booleans
*/
inline bool isGame() const { return mType == FileType::Game; }
inline bool isFolder() const { return mType == FileType::Folder; }
inline bool isGame() const { return mType == ItemType::Game; }
inline bool isFolder() const { return mType == ItemType::Folder; }
/*
* Setters
......
......@@ -37,10 +37,10 @@ namespace FileSorts
static int compareFoldersAndGames(FileData* const fd1, FileData* const fd2)
{
FileData::FileType f1 = fd1->getType();
FileData::FileType f2 = fd2->getType();
ItemType f1 = fd1->getType();
ItemType f2 = fd2->getType();
if (f1 == f2) return 0; // Both are games or folders
if (f1 == FileData::FileType::Folder) return -1; // f1 is a folder, f2 is a game
if (f1 == ItemType::Folder) return -1; // f1 is a folder, f2 is a game
return 1; // f2 is a folder
}
......
......@@ -68,14 +68,6 @@ void FolderData::populateRecursiveFolder(const std::string& filteredExtensions,
// No extension?
bool noExtensions = filteredExtensions.empty();
/*std::string filteredExtensions;
for(auto x : searchExtensions)
{
if (!filteredExtensions.empty())
filteredExtensions += '|';
filteredExtensions += x;
}*/
// Keep temporary object outside the loop to avoid construction/destruction and keep memory allocated AMAP
fs::path filePath;
std::string extension, key, stem;
......@@ -107,6 +99,7 @@ void FolderData::populateRecursiveFolder(const std::string& filteredExtensions,
if (doppelgangerWatcher.find(key) == doppelgangerWatcher.end())
{
FileData* newGame = new FileData(key, systemData);
newGame->Metadata().SetDirty();
addChild(newGame, true);
doppelgangerWatcher[key] = newGame;
}
......
......@@ -75,7 +75,7 @@ class FolderData : public FileData
* Constructor
*/
FolderData(const std::string& startpath, SystemData* system)
: FileData(FileData::FileType::Folder, startpath, system)
: FileData(ItemType::Folder, startpath, system)
{
}
......@@ -100,7 +100,7 @@ class FolderData : public FileData
* Return true if this FileData is a folder and has at lease one child
* @return Boolean result
*/
bool hasChildren() const { return mChildren.size() != 0; }
bool hasChildren() const { return !mChildren.empty(); }
/*!
* Run through filesystem's folders, seeking for games, and when found, add them into the current tree
......
......@@ -11,7 +11,7 @@
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
FileData* findOrCreateFile(SystemData* system, const boost::filesystem::path& path, FileData::FileType type, bool trustGamelist, FileData::StringMap& doppelgangerWatcher)
FileData* findOrCreateFile(SystemData* system, const boost::filesystem::path& path, ItemType type, bool trustGamelist, FileData::StringMap& doppelgangerWatcher)
{
// first, verify that path is within the system's root folder
FolderData* root = system->getRootFolder();
......@@ -36,7 +36,7 @@ FileData* findOrCreateFile(SystemData* system, const boost::filesystem::path& pa
// Last path component?
if (path_it == --relative.end())
{
if (type == FileData::FileType::Game) // Final file
if (type == ItemType::Game) // Final file
{
FileData* game = item;
if (game == nullptr)
......@@ -105,9 +105,9 @@ void parseGamelist(SystemData* system, FileData::StringMap& doppelgangerWatcher)
fs::path path;
for (const auto& fileNode : gameList.get_child("gameList"))
{
FileData::FileType type;
if (fileNode.first == "game") type = FileData::FileType::Game;
else if (fileNode.first == "folder") type = FileData::FileType::Folder;
ItemType type;
if (fileNode.first == "game") type = ItemType::Game;
else if (fileNode.first == "folder") type = ItemType::Folder;
else continue; // Unknown node
const MetadataDescriptor::Tree& children = fileNode.second;
......@@ -149,7 +149,7 @@ void updateGamelist(SystemData* system)
FileData::List fileData = rootFolder->getAllItemsRecursively(true);
// Nothing to process?
if (fileData.size() == 0) return;
if (fileData.empty()) return;
/*
* Create game/folder map for fast seeking using relative path as key
......@@ -170,7 +170,7 @@ void updateGamelist(SystemData* system)
{
try
{
pt::read_xml(xmlReadPath, document, 0, std::locale("en_US.UTF8"));
pt::read_xml(xmlReadPath, document, pt::xml_parser::trim_whitespace, std::locale("en_US.UTF8"));
}
catch (std::exception &e)
{
......@@ -206,11 +206,12 @@ void updateGamelist(SystemData* system)
* Write the list.
* At this point, we're sure at least one node has been updated (or added and updated).
*/
boost::filesystem::path xmlWritePath(system->getGamelistPath(true) + ".xml");
boost::filesystem::path xmlWritePath(system->getGamelistPath(true));
boost::filesystem::create_directories(xmlWritePath.parent_path());
try
{
pt::write_xml(xmlWritePath.generic_string(), document, std::locale("en_US.UTF8"));
pt::xml_writer_settings<std::string> settings(' ', 2);
pt::write_xml(xmlWritePath.generic_string(), document, std::locale("en_US.UTF8"), settings);
LOG(LogInfo) << "Saved gamelist.xml for system " << system->getFullName() << ". Updated items: " << fileLinks.size() << "/" << fileData.size();
}
catch (std::exception &e)
......
#pragma once
//! Item type
enum class ItemType : char
{
Game,
Folder,
};
......@@ -20,16 +20,15 @@ const std::string MetadataDescriptor::FolderNodeIdentifier = "folder";
#ifdef _METADATA_STATS_
int MetadataDescriptor::LivingClasses = 0;
int MetadataDescriptor::LivingNone = 0;
int MetadataDescriptor::LivingFolders = 0;
int MetadataDescriptor::LivingGames = 0;
#endif
const MetadataFieldDescriptor* MetadataDescriptor::GetMetadataFieldDescriptors(MetadataDescriptor::ObjectType type, int& count)
const MetadataFieldDescriptor* MetadataDescriptor::GetMetadataFieldDescriptors(ItemType type, int& count)
{
switch(type)
{
case ObjectType::Game:
case ItemType::Game:
{
// This static field descriptor array is defined here to allow
// offsetof accessing private fields
......@@ -59,7 +58,7 @@ const MetadataFieldDescriptor* MetadataDescriptor::GetMetadataFieldDescriptors(M
count = sizeof(_GameMetadataDescriptors) / sizeof(MetadataFieldDescriptor);
return &_GameMetadataDescriptors[0];
}
case ObjectType::Folder:
case ItemType::Folder:
{
// This static field descriptor array is defined here to allow
// offsetof accessing private fields
......@@ -75,7 +74,6 @@ const MetadataFieldDescriptor* MetadataDescriptor::GetMetadataFieldDescriptors(M
count = sizeof(_FolderMetadataDescriptors) / sizeof(MetadataFieldDescriptor);
return &_FolderMetadataDescriptors[0];
}
case ObjectType::None:
default: break;
}
count = 0;
......@@ -86,9 +84,9 @@ MetadataDescriptor MetadataDescriptor::_Default = MetadataDescriptor::BuildDefau
MetadataDescriptor MetadataDescriptor::BuildDefaultValueMetadataDescriptor()
{
MetadataDescriptor defaultData("default");
MetadataDescriptor defaultData("default", ItemType::Game);
int count = 0;
const MetadataFieldDescriptor* fields = GetMetadataFieldDescriptors(ObjectType::Game, count);
const MetadataFieldDescriptor* fields = GetMetadataFieldDescriptors(ItemType::Game, count);
for(; --count >= 0; )
{
......@@ -138,8 +136,7 @@ std::string MetadataDescriptor::IntToRange(int range)
else
{
// Full range
value += '-';
value += std::to_string(min);
value = std::to_string(min) + '-' + value;
}
return value;
}
......@@ -186,7 +183,7 @@ bool MetadataDescriptor::IntToHex(int from, std::string& to)
bool MetadataDescriptor::HexToInt(const std::string& from, int& to)
{
if (from.size() == 0) return false;
if (from.empty()) return false;
const char* src = from.c_str();
int result = 0;
......@@ -266,18 +263,17 @@ bool MetadataDescriptor::StringToFloat(const std::string& from, float& to)
bool MetadataDescriptor::Deserialize(const TreeNode& from, const std::string& relativeTo)
{
#ifdef _METADATA_STATS_
if (_Type == ObjectType::None) LivingNone--;
if (_Type == ObjectType::Game) LivingGames--;
if (_Type == ObjectType::Folder) LivingFolders--;
if (_Type == ItemType::Game) LivingGames--;
if (_Type == ItemType::Folder) LivingFolders--;
#endif
if (from.first == GameNodeIdentifier) _Type = ObjectType::Game;
else if (from.first == FolderNodeIdentifier) _Type = ObjectType::Folder;
if (from.first == GameNodeIdentifier) _Type = ItemType::Game;
else if (from.first == FolderNodeIdentifier) _Type = ItemType::Folder;
else return false; // Unidentified node
#ifdef _METADATA_STATS_
if (_Type == ObjectType::Game) LivingGames++;
if (_Type == ObjectType::Folder) LivingFolders++;
if (_Type == ItemType::Game) LivingGames++;
if (_Type == ItemType::Folder) LivingFolders++;
#endif
int count = 0;
......@@ -363,7 +359,7 @@ bool MetadataDescriptor::Deserialize(const TreeNode& from, const std::string& re
if (value.length() != 0)
{
DateTime dt(false); // Unitialized DateTime
if (dt.ParseFromString("%yyyy%MM%ddT%hh%mm%ss", value, dt))
if (DateTime::ParseFromString("%yyyy%MM%ddT%hh%mm%ss", value, dt))
epoch = (int) dt.ToEpochTime();
else
LOG(LogWarning) << "Invalid DateTime value " << value;
......@@ -399,6 +395,7 @@ bool MetadataDescriptor::Deserialize(const TreeNode& from, const std::string& re
_Name = std::move(defaultName);
_Dirty = true;
}
else _Dirty = false;
return true;
}
......@@ -409,10 +406,11 @@ void MetadataDescriptor::Serialize(Tree& parentTree, const std::string& filePath
const MetadataFieldDescriptor* fields = GetMetadataFieldDescriptors(_Type, count);
// Add empty node game/folder
Tree& tree = parentTree.add_child(_Type == ObjectType::Game ? GameNodeIdentifier : FolderNodeIdentifier, Tree());
Tree& tree = parentTree.add_child(_Type == ItemType::Game ? GameNodeIdentifier : FolderNodeIdentifier, Tree());
// Add path
tree.put("path", makeRelativePath(filePath, relativeTo, true));
std::string relative = makeRelativePath(filePath, relativeTo, true).generic_string();
tree.put("path", relative);
// Metadata
for(; --count >= 0; )
......@@ -431,7 +429,7 @@ void MetadataDescriptor::Serialize(Tree& parentTree, const std::string& filePath
// to do additional tasks regarding each different type
switch(field.Type())
{
case MetadataFieldDescriptor::DataType::String:break;
case MetadataFieldDescriptor::DataType::String:
case MetadataFieldDescriptor::DataType::Text:
{
tree.put(field.Key(), *((std::string*)source));
......@@ -463,7 +461,7 @@ void MetadataDescriptor::Serialize(Tree& parentTree, const std::string& filePath
tree.put(field.Key(), *((bool*)source) ? "true" : "false");
break;
}
case MetadataFieldDescriptor::DataType::Float:break;
case MetadataFieldDescriptor::DataType::Float:
case MetadataFieldDescriptor::DataType::Rating:
{
tree.put(field.Key(), *((float*)source)); // Autoboxing std::string(float)
......@@ -502,7 +500,7 @@ void MetadataDescriptor::Merge(const MetadataDescriptor& sourceMetadata)
const MetadataFieldDescriptor& field = fields[count];
// Default value?
if ((this->*field.IsDefaultValueMethod())()) continue;
if ((sourceMetadata.*field.IsDefaultValueMethod())()) continue;
// Get neutral source
void* source = ((unsigned char*)&sourceMetadata) + field.Offset();
......@@ -511,7 +509,7 @@ void MetadataDescriptor::Merge(const MetadataDescriptor& sourceMetadata)
// Convert & store
switch(field.Type())
{
case MetadataFieldDescriptor::DataType::String:break;
case MetadataFieldDescriptor::DataType::String:
case MetadataFieldDescriptor::DataType::Text:
case MetadataFieldDescriptor::DataType::Path:
{
......@@ -538,7 +536,7 @@ void MetadataDescriptor::Merge(const MetadataDescriptor& sourceMetadata)
*((bool*)destination) = *((bool*)source);
break;
}
case MetadataFieldDescriptor::DataType::Float:break;
case MetadataFieldDescriptor::DataType::Float:
case MetadataFieldDescriptor::DataType::Rating:
{
*((float*)destination) = *((float*)source);
......@@ -556,9 +554,8 @@ void MetadataDescriptor::FreeAll()
{
#ifdef _METADATA_STATS_
LivingClasses--;
if (_Type == ObjectType::None) LivingNone--;
if (_Type == ObjectType::Game) LivingGames--;
if (_Type == ObjectType::Folder) LivingFolders--;
if (_Type == ItemType::Game) LivingGames--;
if (_Type == ItemType::Folder) LivingFolders--;
#endif
int count = 0;
......
......@@ -2,6 +2,7 @@
#include <boost/property_tree/ptree.hpp>
#include "datetime/DateTime.h"
#include "ItemType.h"
#define _METADATA_STATS_
......@@ -10,15 +11,6 @@ class MetadataFieldDescriptor;
class MetadataDescriptor
{
public:
//! Metadata type
enum class ObjectType : char
{
None,
Game,
Folder,
};
private:
//! Default value storage for fast default detection
static MetadataDescriptor _Default;
......@@ -69,7 +61,7 @@ class MetadataDescriptor
bool _Hidden; //!< Hidden game
bool _Dirty; //!< Dirty flag (modified data flag)
ObjectType _Type; //!< Metadata type
ItemType _Type; //!< Metadata type
/*!
* Build an empty object filled with default values
......@@ -123,7 +115,7 @@ class MetadataDescriptor
* @param count Number of actual field descriptor available
* @return first static internal field descriptor reference
*/
static const MetadataFieldDescriptor* GetMetadataFieldDescriptors(ObjectType type, int& count);
static const MetadataFieldDescriptor* GetMetadataFieldDescriptors(ItemType type, int& count);
static std::string FloatToString(float source, int precision);
/*!
......@@ -195,7 +187,7 @@ class MetadataDescriptor
/*!
* Default constructor
*/
explicit MetadataDescriptor(const std::string& defaultName)
explicit MetadataDescriptor(const std::string& defaultName, ItemType type)
: _Name(defaultName),
_Description(),
_Image(),
......@@ -216,11 +208,12 @@ class MetadataDescriptor
_Favorite(false),
_Hidden(false),
_Dirty(false),
_Type(ObjectType::None)
_Type(type)
{
#ifdef _METADATA_STATS_
LivingClasses++;
LivingNone++;
if (_Type == ItemType::Game) LivingGames++;
if (_Type == ItemType::Folder) LivingFolders++;
#endif
}
......@@ -253,9 +246,8 @@ class MetadataDescriptor
{
#ifdef _METADATA_STATS_
LivingClasses++;
if (_Type == ObjectType::None) LivingNone++;
if (_Type == ObjectType::Game) LivingGames++;
if (_Type == ObjectType::Folder) LivingFolders++;
if (_Type == ItemType::Game) LivingGames++;
if (_Type == ItemType::Folder) LivingFolders++;
#endif
}
......@@ -263,7 +255,7 @@ class MetadataDescriptor
* Move constructor
* @param source Source to move data from
*/
MetadataDescriptor(MetadataDescriptor&& source)
MetadataDescriptor(MetadataDescriptor&& source) noexcept
: _Name (std::move(source._Name )),
_Description (std::move(source._Description)),
_Image (std::move(source._Image )),
......@@ -288,9 +280,8 @@ class MetadataDescriptor
{
#ifdef _METADATA_STATS_
LivingClasses++;
if (_Type == ObjectType::None) LivingNone++;
if (_Type == ObjectType::Game) LivingGames++;
if (_Type == ObjectType::Folder) LivingFolders++;
if (_Type == ItemType::Game) LivingGames++;
if (_Type == ItemType::Folder) LivingFolders++;
#endif
source._Emulator = nullptr;
source._Core = nullptr;
......@@ -308,9 +299,8 @@ class MetadataDescriptor
if (&source == this) return *this;
#ifdef _METADATA_STATS_
if (_Type == ObjectType::None) LivingNone--;
if (_Type == ObjectType::Game) LivingGames--;
if (_Type == ObjectType::Folder) LivingFolders--;
if (_Type == ItemType::Game) LivingGames--;
if (_Type == ItemType::Folder) LivingFolders--;
#endif
FreeAll();
......@@ -337,9 +327,8 @@ class MetadataDescriptor
_Type = source._Type ;
#ifdef _METADATA_STATS_
if (_Type == ObjectType::None) LivingNone++;
if (_Type == ObjectType::Game) LivingGames++;
if (_Type == ObjectType::Folder) LivingFolders++;
if (_Type == ItemType::Game) LivingGames++;
if (_Type == ItemType::Folder) LivingFolders++;
#endif
return *this;
......@@ -349,12 +338,11 @@ class MetadataDescriptor
* Move operator
* @param source Source to move data from
*/
MetadataDescriptor& operator = (MetadataDescriptor&& source)
MetadataDescriptor& operator = (MetadataDescriptor&& source) noexcept
{
#ifdef _METADATA_STATS_
if (_Type == ObjectType::None) LivingNone--;
if (_Type == ObjectType::Game) LivingGames--;
if (_Type == ObjectType::Folder) LivingFolders--;
if (_Type == ItemType::Game) LivingGames--;
if (_Type == ItemType::Folder) LivingFolders--;
#endif
FreeAll();
......@@ -382,9 +370,8 @@ class MetadataDescriptor
_Type = source._Type ;
#ifdef _METADATA_STATS_
if (_Type == ObjectType::None) LivingNone++;
if (_Type == ObjectType::Game) LivingGames++;
if (_Type == ObjectType::Folder) LivingFolders++;
if (_Type == ItemType::Game) LivingGames++;
if (_Type == ItemType::Folder) LivingFolders++;
#endif
return *this;
......@@ -416,7 +403,7 @@ class MetadataDescriptor
* Accessors
*/
ObjectType Type() const { return _Type; }
ItemType Type() const { return _Type; }
const std::string& Name() const { return _Name; }
const std::string& Emulator() const { return ReadPString(_Emulator, DefaultValueEmulator); }
......@@ -484,12 +471,19 @@ class MetadataDescriptor
void SetPublisher(const std::string& publisher) { _Publisher = publisher; _Dirty = true; }
void SetGenre(const std::string& genre) { _Genre = genre; _Dirty = true; }
void SetRating(float rating) { _Rating = rating; _Dirty = true; }
void SetPlayers(int min, int max) { _Players = (max << 16) + min; _Dirty = true; }
void SetPlayers(int min, int max)
{
_Players = (max << 16) + min;
_Dirty = true;
}
void SetRegion(const std::string& region) { AssignPString(_Region, region); _Dirty = true; }
void SetRomCrc32(int romcrc32) { _RomCrc32 = romcrc32; _Dirty = true; }
void SetFavorite(bool favorite) { _Favorite = favorite; _Dirty = true; }
void SetHidden(bool hidden) { _Hidden = hidden; _Dirty = true; }
// Special setter to force dirty
void SetDirty() { _Dirty = true; }
/*
* String setters
*/
......@@ -497,17 +491,17 @@ class MetadataDescriptor
void SetReleaseDateAsString(const std::string& releasedate)
{
DateTime st;
_ReleaseDate = st.FromCompactISO6801(releasedate, st) ? (int)st.ToEpochTime() : 0;
_ReleaseDate = DateTime::FromCompactISO6801(releasedate, st) ? (int)st.ToEpochTime() : 0;
_Dirty = true;
}
void SetLastPlayedAsString(const std::string& lastplayed)
{
DateTime st;
_LastPlayed = st.FromCompactISO6801(lastplayed, st) ? (int)st.ToEpochTime() : 0;
_LastPlayed = DateTime::FromCompactISO6801(lastplayed, st) ? (int)st.ToEpochTime() : 0;
_Dirty = true;
}
void SetRatingAsString(const std::string& rating) { float f = 0.0f; if (StringToFloat(rating, f)) SetRating(f); }
void SetPlayersAsString(const std::string& players) { int p = 0; if (RangeToInt(players, p)) SetPlayers(p, p); }
void SetPlayersAsString(const std::string& players) { if (!RangeToInt(players, _Players)) SetPlayers(1, 1); }
void SetFavoriteAsString(const std::string& favorite) { SetFavorite(favorite == "true"); }
void SetHiddenAsString(const std::string& hidden) { SetHidden(hidden == "true"); }
void SetRomCrc32AsString(const std::string& romcrc32) { int c; if (HexToInt(romcrc32, c)) SetRomCrc32(c); }
......
......@@ -20,7 +20,7 @@ struct ScraperSearchParams
struct ScraperSearchResult
{
ScraperSearchResult() : mdl("no-name") {};
ScraperSearchResult() : mdl("no-name", ItemType::Game) {};
MetadataDescriptor mdl;
std::string imageUrl;
......
......@@ -5,6 +5,14 @@
namespace fs = boost::filesystem;
void strFindAndReplace(std::string& source, const std::string& find, const std::string& replace)
{
for(size_t pos = 0; (pos = source.find(find, pos)) != std::string::npos; pos += replace.length())
{
source.replace(pos, find.length(), replace);
}
}
std::string strToUpper(const char* from)
{
std::string str(from);
......
......@@ -5,6 +5,8 @@
#include <boost/filesystem.hpp>
#include <boost/date_time.hpp>
void strFindAndReplace(std::string& source, const std::string& find, const std::string& replace);
std::string strToUpper(const char* from);
std::string& strToUpper(std::string& str);
std::string strToUpper(const std::string& str);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment