summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--projects/openttd_vs100.vcxproj1
-rw-r--r--projects/openttd_vs100.vcxproj.filters3
-rw-r--r--projects/openttd_vs80.vcproj4
-rw-r--r--projects/openttd_vs90.vcproj4
-rw-r--r--source.list1
-rw-r--r--src/ini.cpp292
-rw-r--r--src/ini_load.cpp311
-rw-r--r--src/ini_type.h16
8 files changed, 336 insertions, 296 deletions
diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj
index c1bb317d3..d4e38e713 100644
--- a/projects/openttd_vs100.vcxproj
+++ b/projects/openttd_vs100.vcxproj
@@ -325,6 +325,7 @@
<ClCompile Include="..\src\highscore.cpp" />
<ClCompile Include="..\src\hotkeys.cpp" />
<ClCompile Include="..\src\ini.cpp" />
+ <ClCompile Include="..\src\ini_load.cpp" />
<ClCompile Include="..\src\landscape.cpp" />
<ClCompile Include="..\src\map.cpp" />
<ClCompile Include="..\src\misc.cpp" />
diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters
index 16031fa6c..1f4ba1c4f 100644
--- a/projects/openttd_vs100.vcxproj.filters
+++ b/projects/openttd_vs100.vcxproj.filters
@@ -195,6 +195,9 @@
<ClCompile Include="..\src\ini.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\src\ini_load.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\src\landscape.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj
index 33701798d..640423250 100644
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -571,6 +571,10 @@
>
</File>
<File
+ RelativePath=".\..\src\ini_load.cpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\landscape.cpp"
>
</File>
diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj
index 4d6671c20..1f8b60b0a 100644
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -568,6 +568,10 @@
>
</File>
<File
+ RelativePath=".\..\src\ini_load.cpp"
+ >
+ </File>
+ <File
RelativePath=".\..\src\landscape.cpp"
>
</File>
diff --git a/source.list b/source.list
index 5dfb06adb..45a5cf9a9 100644
--- a/source.list
+++ b/source.list
@@ -33,6 +33,7 @@ heightmap.cpp
highscore.cpp
hotkeys.cpp
ini.cpp
+ini_load.cpp
landscape.cpp
map.cpp
misc.cpp
diff --git a/src/ini.cpp b/src/ini.cpp
index 35d8910c8..3dc1219bb 100644
--- a/src/ini.cpp
+++ b/src/ini.cpp
@@ -10,8 +10,6 @@
/** @file ini.cpp Definition of the IniItem class, related to reading/writing '*.ini' files. */
#include "stdafx.h"
-#include "core/alloc_func.hpp"
-#include "core/mem_func.hpp"
#include "debug.h"
#include "ini_type.h"
#include "string_func.h"
@@ -26,296 +24,8 @@
# include <shellapi.h>
#endif
-/**
- * Construct a new in-memory item of an Ini file.
- * @param parent the group we belong to
- * @param name the name of the item
- * @param len the length of the name of the item
- */
-IniItem::IniItem(IniGroup *parent, const char *name, size_t len) : next(NULL), value(NULL), comment(NULL)
-{
- if (len == 0) len = strlen(name);
-
- this->name = strndup(name, len);
- *parent->last_item = this;
- parent->last_item = &this->next;
-}
-
-/** Free everything we loaded. */
-IniItem::~IniItem()
-{
- free(this->name);
- free(this->value);
- free(this->comment);
-
- delete this->next;
-}
-
-/**
- * Replace the current value with another value.
- * @param value the value to replace with.
- */
-void IniItem::SetValue(const char *value)
-{
- free(this->value);
- this->value = strdup(value);
-}
-
-/**
- * Construct a new in-memory group of an Ini file.
- * @param parent the file we belong to
- * @param name the name of the group
- * @param len the length of the name of the group
- */
-IniGroup::IniGroup(IniFile *parent, const char *name, size_t len) : next(NULL), type(IGT_VARIABLES), item(NULL), comment(NULL)
-{
- if (len == 0) len = strlen(name);
-
- this->name = strndup(name, len);
- this->last_item = &this->item;
- *parent->last_group = this;
- parent->last_group = &this->next;
-
- if (parent->list_group_names == NULL) return;
-
- for (uint i = 0; parent->list_group_names[i] != NULL; i++) {
- if (strcmp(this->name, parent->list_group_names[i]) == 0) {
- this->type = IGT_LIST;
- return;
- }
- }
-}
-
-/** Free everything we loaded. */
-IniGroup::~IniGroup()
-{
- free(this->name);
- free(this->comment);
-
- delete this->item;
- delete this->next;
-}
-
-/**
- * Get the item with the given name, and if it doesn't exist
- * and create is true it creates a new item.
- * @param name name of the item to find.
- * @param create whether to create an item when not found or not.
- * @return the requested item or NULL if not found.
- */
-IniItem *IniGroup::GetItem(const char *name, bool create)
-{
- for (IniItem *item = this->item; item != NULL; item = item->next) {
- if (strcmp(item->name, name) == 0) return item;
- }
-
- if (!create) return NULL;
-
- /* otherwise make a new one */
- return new IniItem(this, name, strlen(name));
-}
-
-/**
- * Clear all items in the group
- */
-void IniGroup::Clear()
-{
- delete this->item;
- this->item = NULL;
- this->last_item = &this->item;
-}
-
-/**
- * Construct a new in-memory Ini file representation.
- * @param list_group_names A NULL terminated list with groups that should be
- * loaded as lists instead of variables.
- */
-IniFile::IniFile(const char * const *list_group_names) : group(NULL), comment(NULL), list_group_names(list_group_names)
-{
- this->last_group = &this->group;
-}
-
-/** Free everything we loaded. */
-IniFile::~IniFile()
-{
- free(this->comment);
- delete this->group;
-}
-
-/**
- * Get the group with the given name, and if it doesn't exist
- * create a new group.
- * @param name name of the group to find.
- * @param len the maximum length of said name.
- * @return the requested group.
- */
-IniGroup *IniFile::GetGroup(const char *name, size_t len)
+IniFile::IniFile(const char * const *list_group_names) : IniLoadFile(list_group_names)
{
- if (len == 0) len = strlen(name);
-
- /* does it exist already? */
- for (IniGroup *group = this->group; group != NULL; group = group->next) {
- if (!strncmp(group->name, name, len) && group->name[len] == 0) {
- return group;
- }
- }
-
- /* otherwise make a new one */
- IniGroup *group = new IniGroup(this, name, len);
- group->comment = strdup("\n");
- return group;
-}
-
-/**
- * Remove the group with the given name.
- * @param name name of the group to remove.
- */
-void IniFile::RemoveGroup(const char *name)
-{
- size_t len = strlen(name);
- IniGroup *prev = NULL;
- IniGroup *group;
-
- /* does it exist already? */
- for (group = this->group; group != NULL; prev = group, group = group->next) {
- if (strncmp(group->name, name, len) == 0) {
- break;
- }
- }
-
- if (group == NULL) return;
-
- if (prev != NULL) {
- prev->next = prev->next->next;
- if (this->last_group == &group->next) this->last_group = &prev->next;
- } else {
- this->group = this->group->next;
- if (this->last_group == &group->next) this->last_group = &this->group;
- }
-
- group->next = NULL;
- delete group;
-}
-
-/**
- * Load the Ini file's data from the disk.
- * @param filename the file to load.
- * @pre nothing has been loaded yet.
- */
-void IniFile::LoadFromDisk(const char *filename)
-{
- assert(this->last_group == &this->group);
-
- char buffer[1024];
- IniGroup *group = NULL;
-
- char *comment = NULL;
- uint comment_size = 0;
- uint comment_alloc = 0;
-
- size_t end;
- /*
- * Now we are going to open a file that contains no more than simple
- * plain text. That would raise the question: "why open the file as
- * if it is a binary file?". That's simple... Microsoft, in all
- * their greatness and wisdom decided it would be useful if ftell
- * is aware of '\r\n' and "sees" that as a single character. The
- * easiest way to test for that situation is by searching for '\n'
- * and decrease the value every time you encounter a '\n'. This will
- * thus also make ftell "see" the '\r' when it is not there, so the
- * result of ftell will be highly unreliable. So to work around this
- * marvel of wisdom we have to open in as a binary file.
- */
- FILE *in = FioFOpenFile(filename, "rb", DATA_DIR, &end);
- if (in == NULL) return;
-
- end += ftell(in);
-
- /* for each line in the file */
- while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) {
- char c, *s;
- /* trim whitespace from the left side */
- for (s = buffer; *s == ' ' || *s == '\t'; s++) {}
-
- /* trim whitespace from right side. */
- char *e = s + strlen(s);
- while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
- *e = '\0';
-
- /* skip comments and empty lines */
- if (*s == '#' || *s == ';' || *s == '\0') {
- uint ns = comment_size + (e - s + 1);
- uint a = comment_alloc;
- /* add to comment */
- if (ns > a) {
- a = max(a, 128U);
- do a *= 2; while (a < ns);
- comment = ReallocT(comment, comment_alloc = a);
- }
- uint pos = comment_size;
- comment_size += (e - s + 1);
- comment[pos + e - s] = '\n'; // comment newline
- memcpy(comment + pos, s, e - s); // copy comment contents
- continue;
- }
-
- /* it's a group? */
- if (s[0] == '[') {
- if (e[-1] != ']') {
- ShowInfoF("ini: invalid group name '%s'", buffer);
- } else {
- e--;
- }
- s++; // skip [
- group = new IniGroup(this, s, e - s);
- if (comment_size != 0) {
- group->comment = strndup(comment, comment_size);
- comment_size = 0;
- }
- } else if (group != NULL) {
- char *t;
- /* find end of keyname */
- if (*s == '\"') {
- s++;
- for (t = s; *t != '\0' && *t != '\"'; t++) {}
- if (*t == '\"') *t = ' ';
- } else {
- for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {}
- }
-
- /* it's an item in an existing group */
- IniItem *item = new IniItem(group, s, t - s);
- if (comment_size != 0) {
- item->comment = strndup(comment, comment_size);
- comment_size = 0;
- }
-
- /* find start of parameter */
- while (*t == '=' || *t == ' ' || *t == '\t') t++;
-
- bool quoted = (*t == '\"');
- /* remove starting quotation marks */
- if (*t == '\"') t++;
- /* remove ending quotation marks */
- e = t + strlen(t);
- if (e > t && e[-1] == '\"') e--;
- *e = '\0';
-
- /* If the value was not quoted and empty, it must be NULL */
- item->value = (!quoted && e == t) ? NULL : strndup(t, e - t);
- } else {
- /* it's an orphan item */
- ShowInfoF("ini: '%s' outside of group", buffer);
- }
- }
-
- if (comment_size > 0) {
- this->comment = strndup(comment, comment_size);
- comment_size = 0;
- }
-
- free(comment);
- fclose(in);
}
/**
diff --git a/src/ini_load.cpp b/src/ini_load.cpp
new file mode 100644
index 000000000..65c8b865c
--- /dev/null
+++ b/src/ini_load.cpp
@@ -0,0 +1,311 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file ini_load.cpp Definition of the #IniLoadFile class, related to reading and storing '*.ini' files. */
+
+#include "stdafx.h"
+#include "core/alloc_func.hpp"
+#include "core/mem_func.hpp"
+#include "debug.h"
+#include "ini_type.h"
+#include "string_func.h"
+#include "fileio_func.h"
+
+/**
+ * Construct a new in-memory item of an Ini file.
+ * @param parent the group we belong to
+ * @param name the name of the item
+ * @param len the length of the name of the item
+ */
+IniItem::IniItem(IniGroup *parent, const char *name, size_t len) : next(NULL), value(NULL), comment(NULL)
+{
+ if (len == 0) len = strlen(name);
+
+ this->name = strndup(name, len);
+ *parent->last_item = this;
+ parent->last_item = &this->next;
+}
+
+/** Free everything we loaded. */
+IniItem::~IniItem()
+{
+ free(this->name);
+ free(this->value);
+ free(this->comment);
+
+ delete this->next;
+}
+
+/**
+ * Replace the current value with another value.
+ * @param value the value to replace with.
+ */
+void IniItem::SetValue(const char *value)
+{
+ free(this->value);
+ this->value = strdup(value);
+}
+
+/**
+ * Construct a new in-memory group of an Ini file.
+ * @param parent the file we belong to
+ * @param name the name of the group
+ * @param len the length of the name of the group
+ */
+IniGroup::IniGroup(IniLoadFile *parent, const char *name, size_t len) : next(NULL), type(IGT_VARIABLES), item(NULL), comment(NULL)
+{
+ if (len == 0) len = strlen(name);
+
+ this->name = strndup(name, len);
+ this->last_item = &this->item;
+ *parent->last_group = this;
+ parent->last_group = &this->next;
+
+ if (parent->list_group_names == NULL) return;
+
+ for (uint i = 0; parent->list_group_names[i] != NULL; i++) {
+ if (strcmp(this->name, parent->list_group_names[i]) == 0) {
+ this->type = IGT_LIST;
+ return;
+ }
+ }
+}
+
+/** Free everything we loaded. */
+IniGroup::~IniGroup()
+{
+ free(this->name);
+ free(this->comment);
+
+ delete this->item;
+ delete this->next;
+}
+
+/**
+ * Get the item with the given name, and if it doesn't exist
+ * and create is true it creates a new item.
+ * @param name name of the item to find.
+ * @param create whether to create an item when not found or not.
+ * @return the requested item or NULL if not found.
+ */
+IniItem *IniGroup::GetItem(const char *name, bool create)
+{
+ for (IniItem *item = this->item; item != NULL; item = item->next) {
+ if (strcmp(item->name, name) == 0) return item;
+ }
+
+ if (!create) return NULL;
+
+ /* otherwise make a new one */
+ return new IniItem(this, name, strlen(name));
+}
+
+/**
+ * Clear all items in the group
+ */
+void IniGroup::Clear()
+{
+ delete this->item;
+ this->item = NULL;
+ this->last_item = &this->item;
+}
+
+/**
+ * Construct a new in-memory Ini file representation.
+ * @param list_group_names A NULL terminated list with groups that should be
+ * loaded as lists instead of variables.
+ */
+IniLoadFile::IniLoadFile(const char * const *list_group_names) : group(NULL), comment(NULL), list_group_names(list_group_names)
+{
+ this->last_group = &this->group;
+}
+
+/** Free everything we loaded. */
+IniLoadFile::~IniLoadFile()
+{
+ free(this->comment);
+ delete this->group;
+}
+
+/**
+ * Get the group with the given name, and if it doesn't exist
+ * create a new group.
+ * @param name name of the group to find.
+ * @param len the maximum length of said name.
+ * @return the requested group.
+ */
+IniGroup *IniLoadFile::GetGroup(const char *name, size_t len)
+{
+ if (len == 0) len = strlen(name);
+
+ /* does it exist already? */
+ for (IniGroup *group = this->group; group != NULL; group = group->next) {
+ if (!strncmp(group->name, name, len) && group->name[len] == 0) {
+ return group;
+ }
+ }
+
+ /* otherwise make a new one */
+ IniGroup *group = new IniGroup(this, name, len);
+ group->comment = strdup("\n");
+ return group;
+}
+
+/**
+ * Remove the group with the given name.
+ * @param name name of the group to remove.
+ */
+void IniLoadFile::RemoveGroup(const char *name)
+{
+ size_t len = strlen(name);
+ IniGroup *prev = NULL;
+ IniGroup *group;
+
+ /* does it exist already? */
+ for (group = this->group; group != NULL; prev = group, group = group->next) {
+ if (strncmp(group->name, name, len) == 0) {
+ break;
+ }
+ }
+
+ if (group == NULL) return;
+
+ if (prev != NULL) {
+ prev->next = prev->next->next;
+ if (this->last_group == &group->next) this->last_group = &prev->next;
+ } else {
+ this->group = this->group->next;
+ if (this->last_group == &group->next) this->last_group = &this->group;
+ }
+
+ group->next = NULL;
+ delete group;
+}
+
+/**
+ * Load the Ini file's data from the disk.
+ * @param filename the file to load.
+ * @pre nothing has been loaded yet.
+ */
+void IniLoadFile::LoadFromDisk(const char *filename)
+{
+ assert(this->last_group == &this->group);
+
+ char buffer[1024];
+ IniGroup *group = NULL;
+
+ char *comment = NULL;
+ uint comment_size = 0;
+ uint comment_alloc = 0;
+
+ size_t end;
+ /*
+ * Now we are going to open a file that contains no more than simple
+ * plain text. That would raise the question: "why open the file as
+ * if it is a binary file?". That's simple... Microsoft, in all
+ * their greatness and wisdom decided it would be useful if ftell
+ * is aware of '\r\n' and "sees" that as a single character. The
+ * easiest way to test for that situation is by searching for '\n'
+ * and decrease the value every time you encounter a '\n'. This will
+ * thus also make ftell "see" the '\r' when it is not there, so the
+ * result of ftell will be highly unreliable. So to work around this
+ * marvel of wisdom we have to open in as a binary file.
+ */
+ FILE *in = FioFOpenFile(filename, "rb", DATA_DIR, &end);
+ if (in == NULL) return;
+
+ end += ftell(in);
+
+ /* for each line in the file */
+ while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) {
+ char c, *s;
+ /* trim whitespace from the left side */
+ for (s = buffer; *s == ' ' || *s == '\t'; s++) {}
+
+ /* trim whitespace from right side. */
+ char *e = s + strlen(s);
+ while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
+ *e = '\0';
+
+ /* skip comments and empty lines */
+ if (*s == '#' || *s == ';' || *s == '\0') {
+ uint ns = comment_size + (e - s + 1);
+ uint a = comment_alloc;
+ /* add to comment */
+ if (ns > a) {
+ a = max(a, 128U);
+ do a *= 2; while (a < ns);
+ comment = ReallocT(comment, comment_alloc = a);
+ }
+ uint pos = comment_size;
+ comment_size += (e - s + 1);
+ comment[pos + e - s] = '\n'; // comment newline
+ memcpy(comment + pos, s, e - s); // copy comment contents
+ continue;
+ }
+
+ /* it's a group? */
+ if (s[0] == '[') {
+ if (e[-1] != ']') {
+ ShowInfoF("ini: invalid group name '%s'", buffer);
+ } else {
+ e--;
+ }
+ s++; // skip [
+ group = new IniGroup(this, s, e - s);
+ if (comment_size != 0) {
+ group->comment = strndup(comment, comment_size);
+ comment_size = 0;
+ }
+ } else if (group != NULL) {
+ char *t;
+ /* find end of keyname */
+ if (*s == '\"') {
+ s++;
+ for (t = s; *t != '\0' && *t != '\"'; t++) {}
+ if (*t == '\"') *t = ' ';
+ } else {
+ for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {}
+ }
+
+ /* it's an item in an existing group */
+ IniItem *item = new IniItem(group, s, t - s);
+ if (comment_size != 0) {
+ item->comment = strndup(comment, comment_size);
+ comment_size = 0;
+ }
+
+ /* find start of parameter */
+ while (*t == '=' || *t == ' ' || *t == '\t') t++;
+
+ bool quoted = (*t == '\"');
+ /* remove starting quotation marks */
+ if (*t == '\"') t++;
+ /* remove ending quotation marks */
+ e = t + strlen(t);
+ if (e > t && e[-1] == '\"') e--;
+ *e = '\0';
+
+ /* If the value was not quoted and empty, it must be NULL */
+ item->value = (!quoted && e == t) ? NULL : strndup(t, e - t);
+ } else {
+ /* it's an orphan item */
+ ShowInfoF("ini: '%s' outside of group", buffer);
+ }
+ }
+
+ if (comment_size > 0) {
+ this->comment = strndup(comment, comment_size);
+ comment_size = 0;
+ }
+
+ free(comment);
+ fclose(in);
+}
+
diff --git a/src/ini_type.h b/src/ini_type.h
index b89a77b56..2bb4fea27 100644
--- a/src/ini_type.h
+++ b/src/ini_type.h
@@ -40,27 +40,33 @@ struct IniGroup {
char *name; ///< name of group
char *comment; ///< comment for group
- IniGroup(struct IniFile *parent, const char *name, size_t len = 0);
+ IniGroup(struct IniLoadFile *parent, const char *name, size_t len = 0);
~IniGroup();
IniItem *GetItem(const char *name, bool create);
void Clear();
};
-/** The complete ini file. */
-struct IniFile {
+/** Ini file that only supports loading. */
+struct IniLoadFile {
IniGroup *group; ///< the first group in the ini
IniGroup **last_group; ///< the last group in the ini
char *comment; ///< last comment in file
const char * const *list_group_names; ///< NULL terminated list with group names that are lists
- IniFile(const char * const *list_group_names = NULL);
- ~IniFile();
+ IniLoadFile(const char * const *list_group_names = NULL);
+ virtual ~IniLoadFile();
IniGroup *GetGroup(const char *name, size_t len = 0);
void RemoveGroup(const char *name);
void LoadFromDisk(const char *filename);
+};
+
+/** Ini file that supports both loading and saving. */
+struct IniFile : IniLoadFile {
+ IniFile(const char * const *list_group_names = NULL);
+
bool SaveToDisk(const char *filename);
};