You need a C or C++ compiler for that. (Whatever the compiler name, you can use mingw or VC++ or another compiler, but the provided files were compiled with msvc)
Basicaly, you will always use another file and change it's value.
For example, I will recreate StringDatabase.cpp from MixCategoryDatabase.cpp (which correspond to RDB files db_string.rdb and db_mixcategory.rdb)
Here the code for MixCategoryDatabase.cpp:
Code:
#include "../Base/DataType.h"
#include "../Base/ExportDLL.h"
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#pragma comment(linker, "/EXPORT:registerDBStructure=_registerDBStructure@8")
void EDATABASEDLL DLLCALLCONV registerDBStructure(FieldDescriptor **dfmPtr, int *sizePtr) {
FieldDescriptor df[] =
{{1, TYPE_INT16, "id"},
{1, TYPE_INT8, "mix_id"},
{1, TYPE_INT32, "local_flag"},
{1, TYPE_INT16, "high_category_id"},
{1, TYPE_INT16, "middle_category_id"},
{1, TYPE_INT16, "low_category_id"},
{1, TYPE_INT32, "category_text_id"},
{1, TYPE_INT32, "formal_id"},
{1, TYPE_INT32, "result_id"}};
const int size = sizeof(df) / sizeof(FieldDescriptor);
FieldDescriptor *dfm = (FieldDescriptor*) malloc(sizeof(FieldDescriptor)*size);
memcpy(dfm, df, sizeof(FieldDescriptor)*size);
*dfmPtr = dfm;
*sizePtr = size;
}
#ifdef __cplusplus
}
#endif
Then change the returned structure from the registerDBStructure function.
Actually, the code that describe the db_mixcategory.rdb is that:
Code:
FieldDescriptor df[] =
{{1, TYPE_INT16, "id"},
{1, TYPE_INT8, "mix_id"},
{1, TYPE_INT32, "local_flag"},
{1, TYPE_INT16, "high_category_id"},
{1, TYPE_INT16, "middle_category_id"},
{1, TYPE_INT16, "low_category_id"},
{1, TYPE_INT32, "category_text_id"},
{1, TYPE_INT32, "formal_id"},
{1, TYPE_INT32, "result_id"}};
You need only to change that more than 60% of the time. For the other 40% you need also to change that, but you will need to provide some information about columns ordering in the SQL table or default values, or ...
To begin, you know that db_string.rdb contain these field:
Code:
int name_string_size; //This value tell the reader how much bytes to read from the "name" field
int value_string_size; //Same for "value" field
char name[name_string_size];
char value[value_string_size];
int code;
int group_id;
//Here are unknown field which seems to be always 0. Be we need them too to be able to read and write a full record.
int unknownValue0;
int unknownValue1;
int unknownValue2;
int unknownValue3;
Having that, you can make the FieldDescriptor array.
The first column is the field size.
The second is the flag field. It contains information about the RDB field, if it should be in SQL table, it's type, ...
The third is the name of the field. Use the same name as in the SQL table if you want to be able to read SQL table as well.
So for the fields of db_string:
- The first field tell the size of a string. So it's type is TYPE_VARCHAR_SIZE. Also, we don't want it in the SQL table, so we will need to add TYPE_SQLIGNORE. These to token are flags. You can find them all in DataType.h. This field is just to know the size of a string, so we don't need to set a field name.
Then you have: MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""
MAKEINDEXEDVAR indicate that the variable has an index, which will be needed when we will describe the associated string which use this field to know it's size. For this first field description, we use 0 as the index.
Use MAKEINDEXEDVAR like that: MAKEINDEXEDVAR(index, size) where size is in number of TYPE_VARCHAR_SIZE. Except for arrays, it's always 1 (it's not the size in byte, but rather the number of element)
Flag are concatenated using the pipe operator, that is |
The name "" is just an empty name as we don't need one.
So for now we have:
Code:
FieldDescriptor df[] =
{{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""}};
Another possible and maybe easier way to lay out these data is:
Code:
FieldDescriptor df[] = {
{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""}
};
As we only have a double bracket at the begin of the array and at the end. So when adding a new field description, we must not copy them as well, we will see that for the second field.
The second field is almost the same as the first one, except that we need to provide a different index to be able to differentiate them after. So we use 1 as the index.
Then we have:
Code:
FieldDescriptor df[] =
{{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""}};
Notice the bracket, we didn't duplicated them all.
Another way is to write that:
Code:
FieldDescriptor df[] = {
{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""}
};
There, we just have to copy the first line for the first field (but I haven't thought about it)
Then we have a different field, this one is the "name" field which is a string. This string is not a fixed length string, so we don't use an array of character, but rather a "varchar" (as in SQL). So it's type will be TYPE_VARCHAR_STR. This field is also in the SQL table, so we don't provide TYPE_SQLIGNORE. We use "name" as it's name.
There the MAKEINDEXEDVAR indicate different things than before. The index is which field to use to know the string size. The "name" field use the first field (name_string_size) which we have provided an index of 0. So we use here 0 as the index for the "name" field. When the RDB will be read, name_string_size will be used to know the size of "name".
The provided size is 64, as the number provided to varchar when creating a SQL table. The name field was of type "varchar(64)" in my SQL table, so I use the same number (but it could be more or less, provided that all string are not larger than this number)
So we have:
Code:
FieldDescriptor df[] =
{{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(0, 64), TYPE_VARCHAR_STR, "name"}};
Or:
Code:
FieldDescriptor df[] = {
{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(0, 64), TYPE_VARCHAR_STR, "name"}
};
The next field, "value" is the same, but the varchar max size is not 64 but 4096. And the index of the field telling the size of the "value" field is the second one which have an index of 1 instead of 0. We also use "value" as it's name.
So we have:
Code:
FieldDescriptor df[] =
{{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(0, 64), TYPE_VARCHAR_STR, "name"},
{MAKEINDEXEDVAR(1, 4096), TYPE_VARCHAR_STR, "value"}};
Or:
Code:
FieldDescriptor df[] = {
{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(0, 64), TYPE_VARCHAR_STR, "name"},
{MAKEINDEXEDVAR(1, 4096), TYPE_VARCHAR_STR, "value"}
};
The next field, "code", is a 32 bits integer. We only have one value (ie: it's not an array) so the size will be 1. We will not use MAKEINDEXEDVAR as we don't need an index for this field (in fact, we use this macro only for TYPE_VARCHAR_SIZE and TYPE_VARCHAR_STR types)
The type will be TYPE_INT32. We also add another flag, TYPE_FLAG_KEY, so when we will create the table in SQL, we will use this field as the primary key, as the code is what Rappelz use to search a value in this table. (that's optionnal)
And we use "code" as the field name
So we have:
Code:
FieldDescriptor df[] =
{{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(0, 64), TYPE_VARCHAR_STR, "name"},
{MAKEINDEXEDVAR(1, 4096), TYPE_VARCHAR_STR, "value"},
{1, TYPE_INT32 | TYPE_FLAG_KEY, "code"}};
Or:
Code:
FieldDescriptor df[] = {
{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(0, 64), TYPE_VARCHAR_STR, "name"},
{MAKEINDEXEDVAR(1, 4096), TYPE_VARCHAR_STR, "value"},
{1, TYPE_INT32 | TYPE_FLAG_KEY, "code"}
};
Then we have other fields which are all 32 bits integer. The only difference is that we don't declare them as primary keys, so we don't provide TYPE_FLAG_KEY. (There should be always at most one primary key, if there are)
So we have at the end:
Code:
FieldDescriptor df[] =
{{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(0, 64), TYPE_VARCHAR_STR, "name"},
{MAKEINDEXEDVAR(1, 4096), TYPE_VARCHAR_STR, "value"},
{1, TYPE_INT32 | TYPE_FLAG_KEY, "code"},
{1, TYPE_INT32, "group_id"},
{1, TYPE_INT32 | TYPE_SQLIGNORE | TYPE_CSVIGNORE, "unknownValue0"},
{1, TYPE_INT32 | TYPE_SQLIGNORE | TYPE_CSVIGNORE, "unknownValue1"},
{1, TYPE_INT32 | TYPE_SQLIGNORE | TYPE_CSVIGNORE, "unknownValue2"},
{1, TYPE_INT32 | TYPE_SQLIGNORE | TYPE_CSVIGNORE, "unknownValue3"}};
Or:
[code]FieldDescriptor df[] = {
{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(0, 64), TYPE_VARCHAR_STR, "name"},
{MAKEINDEXEDVAR(1, 4096), TYPE_VARCHAR_STR, "value"},
{1, TYPE_INT32 | TYPE_FLAG_KEY, "code"},
{1, TYPE_INT32, "group_id"},
{1, TYPE_INT32 | TYPE_SQLIGNORE | TYPE_CSVIGNORE, "unknownValue0"},
{1, TYPE_INT32 | TYPE_SQLIGNORE | TYPE_CSVIGNORE, "unknownValue1"},
{1, TYPE_INT32 | TYPE_SQLIGNORE | TYPE_CSVIGNORE, "unknownValue2"},
{1, TYPE_INT32 | TYPE_SQLIGNORE | TYPE_CSVIGNORE, "unknownValue3"}
};[code]
We could also compact the last unknown fields, but if we discover what they mean, we will have to reseparated them so I didn't compacted them in an array.
This would be that:
Code:
FieldDescriptor df[] =
{{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(0, 64), TYPE_VARCHAR_STR, "name"},
{MAKEINDEXEDVAR(1, 4096), TYPE_VARCHAR_STR, "value"},
{1, TYPE_INT32 | TYPE_FLAG_KEY, "code"},
{1, TYPE_INT32, "group_id"},
{4, TYPE_INT32 | TYPE_SQLIGNORE | TYPE_CSVIGNORE, "unknownValues"}};
Or:
[code]FieldDescriptor df[] = {
{MAKEINDEXEDVAR(0, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(1, 1), TYPE_VARCHAR_SIZE | TYPE_SQLIGNORE, ""},
{MAKEINDEXEDVAR(0, 64), TYPE_VARCHAR_STR, "name"},
{MAKEINDEXEDVAR(1, 4096), TYPE_VARCHAR_STR, "value"},
{1, TYPE_INT32 | TYPE_FLAG_KEY, "code"},
{1, TYPE_INT32, "group_id"},
{4, TYPE_INT32 | TYPE_SQLIGNORE | TYPE_CSVIGNORE, "unknownValues"}
};[code]
Where we tell that we have an array of 4 unknown values which are not referenced in the SQL table and we don't want them either when using a CSV file.
(actually the CSV file is more a TSV file, but this extension is not well recognized, so I use CSV. These files are readable with any text editor and you can open them in a spreadsheet program with the tabulation as the separator (nothing else else it will mess up columns. Also be sure to disable any quotes. If you have some rows with more than the expected number of columns, it's probably that you forgot to tell you spreadsheet program to ignore quotes)
Finally, to compile the new file (StringDatabase.cpp here), create a DLL.
With the command line version of msvc2010 (it's probably the same for all msvc versions) it's:
cl.exe -D_CRT_SECURE_NO_WARNINGS -DBUILDING_DATABASE /DWIN32 /D_WINDOWS /W3 /Zm1000 /EHsc /GR /MD /O2 /Ob2 /D NDEBUG <files-to-compile> <files-to-link> /link /DLL /OUT:<desired-dll-name>.dll
Where here <files-to-compile> is only StringDatabase.cpp (for this example, else use the file name you want)
<files-to-link> is RpzRdbBase.lib and <desired-dll-name>.dll is StringDatabase.dll
I will edit this post later to provide more explanation about compiling as a DLL. But if you have an IDE (like codeblocks or MS Visual Studio C++), juste create a new DLL project (dynamic library) and use StringDatabase.cpp as the only file to compile. (There is no main function as it's just a DLL). If your IDE generate a file like main.cpp file with content inside it (for DLL initializatino for example), you can just remove all that code and replace with the code that would be in StringDatabase.cpp.
It's best to name your DLLs as <rdb_type>Database.dll as the GUI show by default all file which end with Database.dll and not the others.