I'm still here!

Rewrote fame ranking lists- changed MAP_NAME_LENGTH to 12, now there's MAP_NAME_LENGTH_EXT at 16 for
	  uses where there is / may be the .gat extension, code adjusted accordingly
- moved map_normalize_name to mapindex_normalize_name so that everything
	  handling map names uses the same extension-removing function
- greatly enhanced the map cache generator, complete documentation on the
	  tool and the map cache format can be found in doc/
- the map cache format changed a bit as a consequence, but of course a new
	  valid one is included (contains latest Nameless Island maps)
- fixed a duplicate entry in map index


git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@10167 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
DracoRPG 2007-04-06 17:22:40 +00:00
parent a881931cb6
commit 7c8f12ccd5
25 changed files with 425 additions and 243 deletions

View File

@ -5,6 +5,17 @@ IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
2007/04/07 2007/04/07
* Final touches to the whole map crap [DracoRPG]
- changed MAP_NAME_LENGTH to 12, now there's MAP_NAME_LENGTH_EXT at 16 for
uses where there is / may be the .gat extension, code adjusted accordingly
- moved map_normalize_name to mapindex_normalize_name so that everything
handling map names uses the same extension-removing function
- greatly enhanced the map cache generator, complete documentation on the
tool and the map cache format can be found in doc/
- the map cache format changed a bit as a consequence, but of course a new
valid one is included (contains latest Nameless Island maps)
- and I'm sorry for the number of times I've moved around and renamed files,
now the final structure should have been reached
* Capped clif_heal's heal field (the argument received is int, but the * Capped clif_heal's heal field (the argument received is int, but the
packet field is short, meaning that if the heal is high enough, the client packet field is short, meaning that if the heal is high enough, the client
would receive a negative heal amount). [Skotlex] would receive a negative heal amount). [Skotlex]

View File

@ -744,7 +744,6 @@ map: ice_dun04
// --- Veins & Thor Dungeon --- // --- Veins & Thor Dungeon ---
// -- 2006-12-19sdata_k.gpf -- // -- 2006-12-19sdata_k.gpf --
map: que_thor map: que_thor
map: thor_camp map: thor_camp
map: thor_v01 map: thor_v01
@ -760,6 +759,17 @@ map: ve_fild05
map: ve_fild06 map: ve_fild06
map: ve_fild07 map: ve_fild07
// --- Unknown Island & Abbey Dungeon ---
// -- 2007-04-02sdata_k.gpf
map: nameless_i
map: nameless_n
map: nameless_in
map: abbey01
map: abbey02
map: abbey03
map: poring_w01
map: poring_w02
//------------------------- Clone Maps --------------------------- //------------------------- Clone Maps ---------------------------
//------------------------- Extra Maps --------------------------- //------------------------- Extra Maps ---------------------------

View File

@ -21,6 +21,8 @@
======================== ========================
04/07 04/07
* Updated map index and map cache with Nameless Island maps [DracoRPG]
- also removed the duplicate g_room2 entry from map index, read the note
* Corrected Aliza card's item_db line. [Skotlex] * Corrected Aliza card's item_db line. [Skotlex]
04/05 04/05
* Fixed some items that should heal percentual hp/sp [Playtester] * Fixed some items that should heal percentual hp/sp [Playtester]

View File

@ -1,15 +0,0 @@
//-----------------------------------------
// GRF List
//-----------------------------------------
grf: C:\Program Files\Gravity\RO\data.grf
grf: C:\Program Files\Gravity\RO\sdata.grf
// You may add more in this format
// grf: <data file path>
//------ Others ---------------------------
//Path to directory that contains the data dir
//NOTE: Path must include trailing backslash, only one data_dir entry is supported.
//data_dir: C:\Program Files\Gravity\RO\

Binary file not shown.

View File

@ -9,6 +9,11 @@
//mapname <- map will use index of previous map +1 //mapname <- map will use index of previous map +1
//Note that map index 0 is special and reserved for "error" status. //Note that map index 0 is special and reserved for "error" status.
// NOTE TO DEVELOPERS
// Due to a removed duplicate, index 591 is free, if you have to add a single map,
// please add it there instead of the end. Then remove the 592 on next line (rachel)
// and remove all those comments. ~DracoRPG
alb_ship 1 alb_ship 1
alb2trea alb2trea
alberta alberta
@ -599,8 +604,8 @@ job_hunter
job_knight job_knight
job_priest job_priest
job_wizard job_wizard
g_room2 // INDEX 591 IS FREE, PLEASE USE IT
rachel rachel 592
ra_in01 ra_in01
ra_fild01 ra_fild01
ra_fild02 ra_fild02
@ -645,6 +650,14 @@ ve_fild07
poring_c01 poring_c01
poring_c02 poring_c02
que_ng que_ng
nameless_i
nameless_n
nameless_in
abbey01
abbey02
abbey03
poring_w01
poring_w02
// Only add maps under this line if they are not standard maps! // Only add maps under this line if they are not standard maps!

View File

@ -1,18 +1,67 @@
"How to use the mapcache builder" //===== Athena Doc ========================================
DracoRPG //= Map Cache Builder and Format Documentation
//===== By ================================================
//= DracoRPG
//===== Version ===========================================
//= 1.0
//=========================================================
//= 0.1 - Short howto for the initial builder version
//= 1.0 - Complete manual covering the improved version
//===== Description =======================================
//= A complete manual for eAthena's map cache generator
//= as well as a reference on the map cache format used
//=========================================================
Preface:
-------------------------------------------------------------------------------
Since SVN revision ~10000, the map-server does not know how to read RO client files anymore. It reads maps from a
"map cache" file that contains all and only the useful data about the maps. A map cache containing every official
kRO Sakray map currently supported by eAthena is provided as a default.
If you have custom maps or want to minimize the size of your map cache because your server does not load all of them
(multi-map-server or light test server), you can use the map cache builder to generate a new one fitting your needs.
Map cache builder manual:
-------------------------------------------------------------------------------
The source code for the map cache builder is located in src/tool/. It can be built using "make tools" if you use the Makefile
or using the "mapcache" project under Visual Studio. Named "mapcache", the executable will be in your eAthena main folder.
The map cache builder needs 3 file paths : one is a list of GRFs and/or data directory containing the maps, the second
is the list of maps to add to the map cache, and the last one is the path of the map cache to generate. Default values for
those paths are "tools/mapcache/grf_files.txt", "db/map_index.txt" and "db/map_cache.dat".
The list of GRF and/or data directory must follow the format and indication of the default file: as many "grf:" entries as
you wish and optionally one only "data_dir:" entry with trailing backslash included. // comments are supported as usual.
In fact, any file with one map name per line can be used as a map list, that's why the map index list is used as a default:
we are sure it contains every map supported by the server. Anything after the map name is ignored, // comments are supported
and if the first word on the line is "map:" then the second word is used as the map name instead: that allows using
maps_athena.conf as your map list, which is handy if you want to generate a minimal map cache for each of your multiple
map-servers.
The map cache file path can point to an already existing file, as the builder adds a map only if it's not already cached.
This way, you can add custom maps to the base map cache without even needing kRO Sakray maps. If you wish to rebuild the
entire map cache, though, you can either provide a path to a non-existing file, or force the rebuild mode.
Here are the command-line arguments you can provide to the map cache builder to customize its behavior:
-grf path/to/grf/list
Allows to specify the file containing the list of GRFs and/or data directory
-list path/to/map/list
Allows to specify the file containing the list of maps to add to the map cache
-cache path/to/map/cache
Allows to specify the path to the generated map cache
- rebuild
Allows to force the rebuild mode (map cache will be overwritten even if it already exists)
This is only useful if you have custom maps, as eAthena is provided with an updated mapcache containing every map Map cache format reference:
from kRO Sakray's data. -------------------------------------------------------------------------------
1. First add the path to the directory / GRF containing your maps to db/grf_files.txt The file is written as little-endian, even on big-endian systems, for cross-compatibility reasons. Appropriate conversions
/!\ Please note you must also have the official maps as the whole mapcache will be rebuilt from scratch are done when generating it, so don't worry about it.
The first 6 bytes are a main header:
2. Then add those custom maps at the end of db/map_list.txt, carefully chosing their index <unsigned long> file size
<unsigned short> number of maps
3. Now just run the mapcache builder and it'll build a new one at db/map_cache.dat Then maps are stored one right after another:
<12-characters-long string> map name
<short> X size
NOTE: <short> Y size
You can override those default paths by providing your own ones as command-line arguments to the mapcache builder: <long> compressed cell data length
$> mapcache [grf_files_path [map_list_path [map_cache_path]]] <variable> compressed cell data

View File

@ -1605,10 +1605,6 @@ void create_online_files(void) {
if (online_display_option & 24) { // 8 or 16 if (online_display_option & 24) { // 8 or 16
// prepare map name // prepare map name
memcpy(temp, mapindex_id2name(char_dat[j].status.last_point.map), MAP_NAME_LENGTH); memcpy(temp, mapindex_id2name(char_dat[j].status.last_point.map), MAP_NAME_LENGTH);
temp[MAP_NAME_LENGTH] = '\0';
if (strstr(temp, ".gat") != NULL) {
temp[strstr(temp, ".gat") - temp] = 0; // suppress the '.gat'
}
// write map name // write map name
if (online_display_option & 16) { // map-name AND coordinates if (online_display_option & 16) { // map-name AND coordinates
fprintf(fp2, " <td>%s (%d, %d)</td>\n", temp, char_dat[j].status.last_point.x, char_dat[j].status.last_point.y); fprintf(fp2, " <td>%s (%d, %d)</td>\n", temp, char_dat[j].status.last_point.x, char_dat[j].status.last_point.y);
@ -3532,13 +3528,13 @@ int parse_char(int fd)
{ {
//Send player to map //Send player to map
uint32 subnet_map_ip; uint32 subnet_map_ip;
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
snprintf(map_name, MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(cd->last_point.map)); snprintf(map_name, MAP_NAME_LENGTH_EXT, "%s.gat", mapindex_id2name(cd->last_point.map));
WFIFOHEAD(fd,28); WFIFOHEAD(fd,28);
WFIFOW(fd,0) = 0x71; WFIFOW(fd,0) = 0x71;
WFIFOL(fd,2) = cd->char_id; WFIFOL(fd,2) = cd->char_id;
memcpy(WFIFOP(fd,6), map_name, MAP_NAME_LENGTH); memcpy(WFIFOP(fd,6), map_name, MAP_NAME_LENGTH_EXT);
// Advanced subnet check [LuzZza] // Advanced subnet check [LuzZza]
subnet_map_ip = lan_subnetcheck(ipl); subnet_map_ip = lan_subnetcheck(ipl);

View File

@ -3340,13 +3340,12 @@ int parse_char(int fd)
{ {
//Send player to map. //Send player to map.
uint32 subnet_map_ip; uint32 subnet_map_ip;
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
snprintf(map_name, MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(char_dat.last_point.map)); snprintf(map_name, MAP_NAME_LENGTH_EXT, "%s.gat", mapindex_id2name(char_dat.last_point.map));
WFIFOHEAD(fd,28); WFIFOHEAD(fd,28);
WFIFOW(fd,0) = 0x71; WFIFOW(fd,0) = 0x71;
WFIFOL(fd,2) = char_dat.char_id; WFIFOL(fd,2) = char_dat.char_id;
memcpy(WFIFOP(fd,6), map_name, MAP_NAME_LENGTH); memcpy(WFIFOP(fd,6), map_name, MAP_NAME_LENGTH_EXT);
// Advanced subnet check [LuzZza] // Advanced subnet check [LuzZza]
subnet_map_ip = lan_subnetcheck(ipl); subnet_map_ip = lan_subnetcheck(ipl);
@ -4024,17 +4023,15 @@ int char_config_read(const char *cfgName) {
} else if (strcmpi(w1, "save_log") == 0) { } else if (strcmpi(w1, "save_log") == 0) {
save_log = config_switch(w2); save_log = config_switch(w2);
} else if (strcmpi(w1, "start_point") == 0) { } else if (strcmpi(w1, "start_point") == 0) {
char map[MAP_NAME_LENGTH]; char map[MAP_NAME_LENGTH_EXT];
int x, y; int x, y;
if (sscanf(w2, "%16[^,],%d,%d", map, &x, &y) < 3) if (sscanf(w2, "%16[^,],%d,%d", map, &x, &y) < 3)
continue; continue;
if (strstr(map, ".gat") != NULL) { // Verify at least if '.gat' is in the map name start_point.map = mapindex_name2id(map);
start_point.map = mapindex_name2id(map); if (!start_point.map)
if (!start_point.map) ShowError("Specified start_point %s not found in map-index cache.\n", map);
ShowError("Specified start_point %s not found in map-index cache.\n", map); start_point.x = x;
start_point.x = x; start_point.y = y;
start_point.y = y;
}
} else if (strcmpi(w1, "start_zeny") == 0) { } else if (strcmpi(w1, "start_zeny") == 0) {
start_zeny = atoi(w2); start_zeny = atoi(w2);
if (start_zeny < 0) if (start_zeny < 0)

View File

@ -15,42 +15,53 @@
//Leave an extra char of space to hold the terminator, in case for the strncpy(mapindex_id2name()) calls. //Leave an extra char of space to hold the terminator, in case for the strncpy(mapindex_id2name()) calls.
struct indexes { struct indexes {
char name[MAP_NAME_LENGTH+1]; //Stores map name char name[MAP_NAME_LENGTH+1]; //Stores map name
int length; //Stores string length WITHOUT the extension for quick lookup. char exists; //Set to 1 if index exists
} indexes[MAX_MAPINDEX]; } indexes[MAX_MAPINDEX];
static unsigned short max_index = 0; static unsigned short max_index = 0;
char mapindex_cfgfile[80] = "db/map_list.txt"; char mapindex_cfgfile[80] = "db/map_index.txt";
// Removes the extension from a map name
char *mapindex_normalize_name(char *mapname)
{
char *ptr, *ptr2;
ptr = strchr(mapname, '.');
if (ptr) { //Check and remove extension.
while (ptr[1] && (ptr2 = strchr(ptr+1, '.')))
ptr = ptr2; //Skip to the last dot.
if(stricmp(ptr,".gat") == 0 ||
stricmp(ptr,".afm") == 0 ||
stricmp(ptr,".af2") == 0)
*ptr = '\0'; //Remove extension.
}
return mapname;
}
/// Adds a map to the specified index /// Adds a map to the specified index
/// Returns 1 if successful, 0 oherwise /// Returns 1 if successful, 0 oherwise
int mapindex_addmap(int index, const char *name) int mapindex_addmap(int index, const char *name)
{ {
char map_name[1024]; char map_name[1024];
char *ext;
int length;
if (index < 0 || index >= MAX_MAPINDEX) { if (index < 0 || index >= MAX_MAPINDEX) {
ShowError("(mapindex_add) Map index (%d) for \"%s\" out of range (max is %d)\n", index, name, MAX_MAPINDEX); ShowError("(mapindex_add) Map index (%d) for \"%s\" out of range (max is %d)\n", index, name, MAX_MAPINDEX);
return 0; return 0;
} }
snprintf(map_name, 1024, "%s", name); snprintf(map_name, 1024, "%s", name);
map_name[1023] = 0; mapindex_normalize_name(map_name);
length = strlen(map_name);
if (length > MAP_NAME_LENGTH) { if (strlen(map_name) > MAP_NAME_LENGTH-1) {
ShowError("(mapindex_add) Map name %s is too long. Maps are limited to %d characters.\n", map_name, MAP_NAME_LENGTH); ShowError("(mapindex_add) Map name %s is too long. Maps are limited to %d characters.\n", map_name, MAP_NAME_LENGTH);
return 0; return 0;
} }
if ((ext = strstr(map_name, ".")) != NULL) { // Remove extension
length = ext-map_name;
*ext = '\0';
}
if (indexes[index].length) if (indexes[index].exists)
ShowWarning("(mapindex_add) Overriding index %d: map \"%s\" -> \"%s\"\n", index, indexes[index].name, map_name); ShowWarning("(mapindex_add) Overriding index %d: map \"%s\" -> \"%s\"\n", index, indexes[index].name, map_name);
strncpy(indexes[index].name, map_name, MAP_NAME_LENGTH); strncpy(indexes[index].name, map_name, MAP_NAME_LENGTH);
indexes[index].length = length; indexes[index].exists = 1;
if (max_index <= index) if (max_index <= index)
max_index = index+1; max_index = index+1;
return 1; return 1;
@ -59,17 +70,18 @@ int mapindex_addmap(int index, const char *name)
unsigned short mapindex_name2id(const char* name) { unsigned short mapindex_name2id(const char* name) {
//TODO: Perhaps use a db to speed this up? [Skotlex] //TODO: Perhaps use a db to speed this up? [Skotlex]
int i; int i;
int length = strlen(name); char map_name[1024];
char *ext = strstr(name, ".");
if (ext) snprintf(map_name, 1024, "%s", name);
length = ext-name; //Base map-name length without the extension. mapindex_normalize_name(map_name);
for (i = 1; i < max_index; i++) for (i = 1; i < max_index; i++)
{ {
if (strncmp(indexes[i].name,name,length)==0) if (strcmp(indexes[i].name,map_name)==0)
return i; return i;
} }
#ifdef MAPINDEX_AUTOADD #ifdef MAPINDEX_AUTOADD
if( mapindex_addmap(i,name) ) if( mapindex_addmap(i,map_name) )
{ {
ShowDebug("mapindex_name2id: Auto-added map \"%s\" to position %d\n", indexes[i], i); ShowDebug("mapindex_name2id: Auto-added map \"%s\" to position %d\n", indexes[i], i);
return i; return i;
@ -83,7 +95,7 @@ unsigned short mapindex_name2id(const char* name) {
} }
const char* mapindex_id2name(unsigned short id) { const char* mapindex_id2name(unsigned short id) {
if (id > MAX_MAPINDEX || !indexes[id].length) { if (id > MAX_MAPINDEX || !indexes[id].exists) {
ShowDebug("mapindex_id2name: Requested name for non-existant map index [%d] in cache.\n", id); ShowDebug("mapindex_id2name: Requested name for non-existant map index [%d] in cache.\n", id);
return indexes[0].name; //Theorically this should never happen, hence we return this string to prevent null pointer crashes. return indexes[0].name; //Theorically this should never happen, hence we return this string to prevent null pointer crashes.
} }

View File

@ -37,6 +37,7 @@ extern char mapindex_cfgfile[80];
#define MAP_VEINS "veins" #define MAP_VEINS "veins"
#define MAP_JAIL "sec_pri" #define MAP_JAIL "sec_pri"
#define MAP_NOVICE "new_zone01" #define MAP_NOVICE "new_zone01"
char *mapindex_normalize_name(char *mapname);
int mapindex_addmap(int index, const char *name); int mapindex_addmap(int index, const char *name);
unsigned short mapindex_name2id(const char*); unsigned short mapindex_name2id(const char*);
const char* mapindex_id2name(unsigned short); const char* mapindex_id2name(unsigned short);

View File

@ -77,8 +77,9 @@
#define NAME_LENGTH 24 #define NAME_LENGTH 24
//For item names, which tend to have much longer names. //For item names, which tend to have much longer names.
#define ITEM_NAME_LENGTH 50 #define ITEM_NAME_LENGTH 50
//For Map Names, which the client considers to be 16 in length //For Map Names, which the client considers to be 16 in length including the .gat extension
#define MAP_NAME_LENGTH 16 #define MAP_NAME_LENGTH 12
#define MAP_NAME_LENGTH_EXT 16
#define MAX_FRIENDS 40 #define MAX_FRIENDS 40
#define MAX_MEMOPOINTS 10 #define MAX_MEMOPOINTS 10

View File

@ -1310,7 +1310,7 @@ int atcommand_send(const int fd, struct map_session_data* sd, const char* comman
*/ */
int atcommand_rura( const int fd, struct map_session_data* sd, const char* command, const char* message) int atcommand_rura( const int fd, struct map_session_data* sd, const char* command, const char* message)
{ {
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
unsigned short mapindex; unsigned short mapindex;
int x = 0, y = 0; int x = 0, y = 0;
int m = -1; int m = -1;
@ -1698,7 +1698,7 @@ int atcommand_whomap3(const int fd, struct map_session_data* sd, const char* com
int i, count, users; int i, count, users;
int pl_GM_level, GM_level; int pl_GM_level, GM_level;
int map_id; int map_id;
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
memset(atcmd_output, '\0', sizeof(atcmd_output)); memset(atcmd_output, '\0', sizeof(atcmd_output));
memset(map_name, '\0', sizeof(map_name)); memset(map_name, '\0', sizeof(map_name));
@ -1752,7 +1752,7 @@ int atcommand_whomap2(const int fd, struct map_session_data* sd, const char* com
int i, count, users; int i, count, users;
int pl_GM_level, GM_level; int pl_GM_level, GM_level;
int map_id = 0; int map_id = 0;
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
nullpo_retr(-1, sd); nullpo_retr(-1, sd);
@ -1810,7 +1810,7 @@ int atcommand_whomap(const int fd, struct map_session_data* sd, const char* comm
int i, count, users; int i, count, users;
int pl_GM_level, GM_level; int pl_GM_level, GM_level;
int map_id = 0; int map_id = 0;
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
struct guild *g; struct guild *g;
struct party_data *p; struct party_data *p;
@ -3190,10 +3190,10 @@ int atcommand_go(const int fd, struct map_session_data* sd, const char* command,
{ {
int i; int i;
int town; int town;
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
int m; int m;
const struct { char map[MAP_NAME_LENGTH]; int x, y; } data[] = { const struct { char map[MAP_NAME_LENGTH_EXT]; int x, y; } data[] = {
{ MAP_PRONTERA, 156, 191 }, // 0=Prontera { MAP_PRONTERA, 156, 191 }, // 0=Prontera
{ MAP_MORROC, 156, 93 }, // 1=Morroc { MAP_MORROC, 156, 93 }, // 1=Morroc
{ MAP_GEFFEN, 119, 59 }, // 2=Geffen { MAP_GEFFEN, 119, 59 }, // 2=Geffen
@ -3250,7 +3250,7 @@ int atcommand_go(const int fd, struct map_session_data* sd, const char* command,
return -1; return -1;
} else { } else {
// get possible name of the city // get possible name of the city
map_name[MAP_NAME_LENGTH-1] = '\0'; map_name[MAP_NAME_LENGTH_EXT-1] = '\0';
for (i = 0; map_name[i]; i++) for (i = 0; map_name[i]; i++)
map_name[i] = TOLOWER(map_name[i]); map_name[i] = TOLOWER(map_name[i]);
// try to see if it's a name, and not a number (try a lot of possibilities, write errors and abbreviations too) // try to see if it's a name, and not a number (try a lot of possibilities, write errors and abbreviations too)
@ -3636,7 +3636,7 @@ static int atkillmonster_sub(struct block_list *bl, va_list ap)
void atcommand_killmonster_sub(const int fd, struct map_session_data* sd, const char* message, const int drop) void atcommand_killmonster_sub(const int fd, struct map_session_data* sd, const char* message, const int drop)
{ {
int map_id; int map_id;
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
if (!sd) return; if (!sd) return;
@ -5432,8 +5432,8 @@ int atcommand_mapinfo(const int fd, struct map_session_data* sd, const char* com
} }
if (atcmd_player_name[0] == '\0') { if (atcmd_player_name[0] == '\0') {
memcpy(atcmd_player_name, mapindex_id2name(sd->mapindex), MAP_NAME_LENGTH); memcpy(atcmd_player_name, mapindex_id2name(sd->mapindex), MAP_NAME_LENGTH_EXT);
atcmd_player_name[MAP_NAME_LENGTH] = '\0'; atcmd_player_name[MAP_NAME_LENGTH_EXT] = '\0';
m_id = map_mapindex2mapid(sd->mapindex); m_id = map_mapindex2mapid(sd->mapindex);
} else { } else {
m_id = map_mapname2mapid(atcmd_player_name); m_id = map_mapname2mapid(atcmd_player_name);

View File

@ -617,7 +617,7 @@ int charcommand_save(
const int fd, struct map_session_data* sd, const int fd, struct map_session_data* sd,
const char* command, const char* message) const char* command, const char* message)
{ {
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
char character[NAME_LENGTH]; char character[NAME_LENGTH];
struct map_session_data* pl_sd; struct map_session_data* pl_sd;
int x = 0, y = 0; int x = 0, y = 0;
@ -1130,7 +1130,7 @@ int charcommand_warp(
const int fd, struct map_session_data* sd, const int fd, struct map_session_data* sd,
const char* command, const char* message) const char* command, const char* message)
{ {
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
char character[NAME_LENGTH]; char character[NAME_LENGTH];
int x = 0, y = 0; int x = 0, y = 0;
struct map_session_data *pl_sd; struct map_session_data *pl_sd;

View File

@ -328,7 +328,7 @@ int chrif_changemapserver(struct map_session_data *sd, short map, int x, int y,
int chrif_changemapserverack(int fd) int chrif_changemapserverack(int fd)
{ {
struct map_session_data *sd; struct map_session_data *sd;
char mapname[MAP_NAME_LENGTH+1]; char mapname[MAP_NAME_LENGTH_EXT];
RFIFOHEAD(fd); RFIFOHEAD(fd);
sd = map_id2sd(RFIFOL(fd,2)); sd = map_id2sd(RFIFOL(fd,2));

View File

@ -1208,12 +1208,16 @@ static void clif_spiritball_single(int fd, struct map_session_data *sd)
*------------------------------------------ *------------------------------------------
*/ */
static int clif_set0192(int fd, int m, int x, int y, int type) { static int clif_set0192(int fd, int m, int x, int y, int type) {
char map_name[MAP_NAME_LENGTH_EXT];
sprintf(map_name, "%s.gat", map[m].name);
WFIFOHEAD(fd, packet_len(0x192)); WFIFOHEAD(fd, packet_len(0x192));
WFIFOW(fd,0) = 0x192; WFIFOW(fd,0) = 0x192;
WFIFOW(fd,2) = x; WFIFOW(fd,2) = x;
WFIFOW(fd,4) = y; WFIFOW(fd,4) = y;
WFIFOW(fd,6) = type; WFIFOW(fd,6) = type;
memcpy(WFIFOP(fd,8),map[m].name,MAP_NAME_LENGTH); memcpy(WFIFOP(fd,8),map_name,MAP_NAME_LENGTH_EXT);
WFIFOSET(fd,packet_len(0x192)); WFIFOSET(fd,packet_len(0x192));
return 0; return 0;
@ -1600,17 +1604,17 @@ void clif_setwaitclose(int fd) {
*/ */
int clif_changemap(struct map_session_data *sd, short map, int x, int y) { int clif_changemap(struct map_session_data *sd, short map, int x, int y) {
int fd; int fd;
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
nullpo_retr(0, sd); nullpo_retr(0, sd);
fd = sd->fd; fd = sd->fd;
snprintf(map_name, MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(map)); sprintf(map_name, "%s.gat", mapindex_id2name(map));
WFIFOHEAD(fd, packet_len(0x91)); WFIFOHEAD(fd, packet_len(0x91));
WFIFOW(fd,0) = 0x91; WFIFOW(fd,0) = 0x91;
memcpy(WFIFOP(fd,2), map_name, MAP_NAME_LENGTH); memcpy(WFIFOP(fd,2), map_name, MAP_NAME_LENGTH_EXT);
WFIFOW(fd,18) = x; WFIFOW(fd,18) = x;
WFIFOW(fd,20) = y; WFIFOW(fd,20) = y;
WFIFOSET(fd, packet_len(0x91)); WFIFOSET(fd, packet_len(0x91));
@ -1631,7 +1635,7 @@ int clif_changemapserver(struct map_session_data* sd, const char* mapname, int x
WFIFOHEAD(fd, packet_len(0x92)); WFIFOHEAD(fd, packet_len(0x92));
WFIFOW(fd,0) = 0x92; WFIFOW(fd,0) = 0x92;
//Better not trust the null-terminator is there. [Skotlex] //Better not trust the null-terminator is there. [Skotlex]
memcpy(WFIFOP(fd,2), mapname, MAP_NAME_LENGTH); memcpy(WFIFOP(fd,2), mapname, MAP_NAME_LENGTH_EXT);
WFIFOB(fd,17) = 0; //Null terminator for mapname WFIFOB(fd,17) = 0; //Null terminator for mapname
WFIFOW(fd,18) = x; WFIFOW(fd,18) = x;
WFIFOW(fd,20) = y; WFIFOW(fd,20) = y;
@ -4661,10 +4665,10 @@ int clif_skill_warppoint(struct map_session_data *sd,int skill_num,int skill_lv,
WFIFOHEAD(fd,packet_len(0x11c)); WFIFOHEAD(fd,packet_len(0x11c));
WFIFOW(fd,0)=0x11c; WFIFOW(fd,0)=0x11c;
WFIFOW(fd,2)=skill_num; WFIFOW(fd,2)=skill_num;
strncpy((char*)WFIFOP(fd, 4),map1,MAP_NAME_LENGTH); strncpy((char*)WFIFOP(fd, 4),map1,MAP_NAME_LENGTH_EXT);
strncpy((char*)WFIFOP(fd,20),map2,MAP_NAME_LENGTH); strncpy((char*)WFIFOP(fd,20),map2,MAP_NAME_LENGTH_EXT);
strncpy((char*)WFIFOP(fd,36),map3,MAP_NAME_LENGTH); strncpy((char*)WFIFOP(fd,36),map3,MAP_NAME_LENGTH_EXT);
strncpy((char*)WFIFOP(fd,52),map4,MAP_NAME_LENGTH); strncpy((char*)WFIFOP(fd,52),map4,MAP_NAME_LENGTH_EXT);
WFIFOSET(fd,packet_len(0x11c)); WFIFOSET(fd,packet_len(0x11c));
sd->menuskill_id = skill_num; sd->menuskill_id = skill_num;
if (skill_num == AL_WARP) if (skill_num == AL_WARP)
@ -5660,7 +5664,7 @@ int clif_party_created(struct map_session_data *sd,int flag)
int clif_party_member_info(struct party_data *p, struct map_session_data *sd) int clif_party_member_info(struct party_data *p, struct map_session_data *sd)
{ {
unsigned char buf[96]; unsigned char buf[96];
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
if (!sd) { //Pick any party member (this call is used when changing item share rules) if (!sd) { //Pick any party member (this call is used when changing item share rules)
int i; int i;
@ -5669,7 +5673,7 @@ int clif_party_member_info(struct party_data *p, struct map_session_data *sd)
sd = p->data[i].sd; sd = p->data[i].sd;
} }
snprintf(map_name, MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(sd->mapindex)); snprintf(map_name, MAP_NAME_LENGTH_EXT, "%s.gat", mapindex_id2name(sd->mapindex));
WBUFW(buf,0)=0x1e9; WBUFW(buf,0)=0x1e9;
WBUFL(buf,2)= sd->status.account_id; WBUFL(buf,2)= sd->status.account_id;
@ -5679,7 +5683,7 @@ int clif_party_member_info(struct party_data *p, struct map_session_data *sd)
WBUFB(buf,14)=0; //Unconfirmed byte, could be online/offline. WBUFB(buf,14)=0; //Unconfirmed byte, could be online/offline.
memcpy(WBUFP(buf,15), p->party.name, NAME_LENGTH); memcpy(WBUFP(buf,15), p->party.name, NAME_LENGTH);
memcpy(WBUFP(buf,39), sd->status.name, NAME_LENGTH); memcpy(WBUFP(buf,39), sd->status.name, NAME_LENGTH);
memcpy(WBUFP(buf,63), map_name, MAP_NAME_LENGTH); memcpy(WBUFP(buf,63), map_name, MAP_NAME_LENGTH_EXT);
WBUFB(buf,79) = (p->party.item&1)?1:0; WBUFB(buf,79) = (p->party.item&1)?1:0;
WBUFB(buf,80) = (p->party.item&2)?1:0; WBUFB(buf,80) = (p->party.item&2)?1:0;
clif_send(buf,packet_len(0x1e9),&sd->bl,PARTY); clif_send(buf,packet_len(0x1e9),&sd->bl,PARTY);
@ -5693,7 +5697,7 @@ int clif_party_member_info(struct party_data *p, struct map_session_data *sd)
*------------------------------------------*/ *------------------------------------------*/
int clif_party_info(struct party_data* p, struct map_session_data *sd) int clif_party_info(struct party_data* p, struct map_session_data *sd)
{ {
unsigned char buf[2+2+NAME_LENGTH+(4+NAME_LENGTH+MAP_NAME_LENGTH+1+1)*MAX_PARTY]; unsigned char buf[2+2+NAME_LENGTH+(4+NAME_LENGTH+MAP_NAME_LENGTH_EXT+1+1)*MAX_PARTY];
struct map_session_data* party_sd = NULL; struct map_session_data* party_sd = NULL;
int i, c; int i, c;
@ -5704,17 +5708,17 @@ int clif_party_info(struct party_data* p, struct map_session_data *sd)
for(i = 0, c = 0; i < MAX_PARTY; i++) for(i = 0, c = 0; i < MAX_PARTY; i++)
{ {
struct party_member* m = &p->party.member[i]; struct party_member* m = &p->party.member[i];
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
if(!m->account_id) continue; if(!m->account_id) continue;
if(party_sd == NULL) party_sd = p->data[i].sd; if(party_sd == NULL) party_sd = p->data[i].sd;
snprintf(map_name, MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(m->map)); snprintf(map_name, MAP_NAME_LENGTH_EXT, "%s.gat", mapindex_id2name(m->map));
WBUFL(buf,28+c*46) = m->account_id; WBUFL(buf,28+c*46) = m->account_id;
memcpy(WBUFP(buf,28+c*46+4), m->name, NAME_LENGTH); memcpy(WBUFP(buf,28+c*46+4), m->name, NAME_LENGTH);
memcpy(WBUFP(buf,28+c*46+28), map_name, MAP_NAME_LENGTH); memcpy(WBUFP(buf,28+c*46+28), map_name, MAP_NAME_LENGTH_EXT);
WBUFB(buf,28+c*46+44) = (m->leader) ? 0 : 1; WBUFB(buf,28+c*46+44) = (m->leader) ? 0 : 1;
WBUFB(buf,28+c*46+45) = (m->online) ? 0 : 1; WBUFB(buf,28+c*46+45) = (m->online) ? 0 : 1;
c++; c++;
@ -5993,12 +5997,12 @@ int clif_hpmeter(struct map_session_data *sd)
int clif_party_move(struct party *p,struct map_session_data *sd,int online) int clif_party_move(struct party *p,struct map_session_data *sd,int online)
{ {
unsigned char buf[128]; unsigned char buf[128];
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
nullpo_retr(0, sd); nullpo_retr(0, sd);
nullpo_retr(0, p); nullpo_retr(0, p);
snprintf(map_name, MAP_NAME_LENGTH, "%s.gat", map[sd->bl.m].name); snprintf(map_name, MAP_NAME_LENGTH_EXT, "%s.gat", map[sd->bl.m].name);
WBUFW(buf, 0)=0x104; WBUFW(buf, 0)=0x104;
WBUFL(buf, 2)=sd->status.account_id; WBUFL(buf, 2)=sd->status.account_id;
@ -6008,7 +6012,7 @@ int clif_party_move(struct party *p,struct map_session_data *sd,int online)
WBUFB(buf,14)=!online; WBUFB(buf,14)=!online;
memcpy(WBUFP(buf,15),p->name, NAME_LENGTH); memcpy(WBUFP(buf,15),p->name, NAME_LENGTH);
memcpy(WBUFP(buf,39),sd->status.name, NAME_LENGTH); memcpy(WBUFP(buf,39),sd->status.name, NAME_LENGTH);
memcpy(WBUFP(buf,63),map_name, MAP_NAME_LENGTH); memcpy(WBUFP(buf,63),map_name, MAP_NAME_LENGTH_EXT);
clif_send(buf,packet_len(0x104),&sd->bl,PARTY); clif_send(buf,packet_len(0x104),&sd->bl,PARTY);
return 0; return 0;
} }
@ -6415,6 +6419,9 @@ int clif_changemapcell(int m,int x,int y,int cell_type,int type)
{ {
struct block_list bl; struct block_list bl;
unsigned char buf[32]; unsigned char buf[32];
char map_name[MAP_NAME_LENGTH_EXT];
snprintf(map_name, MAP_NAME_LENGTH_EXT, "%s.gat", map[m].name);
bl.type = BL_NUL; bl.type = BL_NUL;
bl.m = m; bl.m = m;
@ -6424,7 +6431,7 @@ int clif_changemapcell(int m,int x,int y,int cell_type,int type)
WBUFW(buf,2) = x; WBUFW(buf,2) = x;
WBUFW(buf,4) = y; WBUFW(buf,4) = y;
WBUFW(buf,6) = cell_type; WBUFW(buf,6) = cell_type;
memcpy(WBUFP(buf,8),map[m].name,MAP_NAME_LENGTH); memcpy(WBUFP(buf,8),map_name,MAP_NAME_LENGTH_EXT);
if(!type) if(!type)
clif_send(buf,packet_len(0x192),&bl,AREA); clif_send(buf,packet_len(0x192),&bl,AREA);
else else
@ -7850,13 +7857,13 @@ void clif_gospel_info(struct map_session_data *sd, int type)
void clif_feel_info(struct map_session_data *sd, unsigned char feel_level, unsigned char type) void clif_feel_info(struct map_session_data *sd, unsigned char feel_level, unsigned char type)
{ {
int fd=sd->fd; int fd=sd->fd;
char map_name[MAP_NAME_LENGTH]; char map_name[MAP_NAME_LENGTH_EXT];
snprintf(map_name, MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(sd->feel_map[feel_level].index)); snprintf(map_name, MAP_NAME_LENGTH_EXT, "%s.gat", mapindex_id2name(sd->feel_map[feel_level].index));
WFIFOHEAD(fd,packet_len(0x20e)); WFIFOHEAD(fd,packet_len(0x20e));
WFIFOW(fd,0)=0x20e; WFIFOW(fd,0)=0x20e;
memcpy(WFIFOP(fd,2),map_name, MAP_NAME_LENGTH); memcpy(WFIFOP(fd,2),map_name, MAP_NAME_LENGTH_EXT);
WFIFOL(fd,26)=sd->bl.id; WFIFOL(fd,26)=sd->bl.id;
WFIFOB(fd,30)=feel_level; WFIFOB(fd,30)=feel_level;
WFIFOB(fd,31)=type?1:0; WFIFOB(fd,31)=type?1:0;
@ -8636,8 +8643,8 @@ int clif_message(struct block_list *bl, const char* msg)
*/ */
void clif_parse_MapMove(int fd, struct map_session_data *sd) { void clif_parse_MapMove(int fd, struct map_session_data *sd) {
// /m /mapmove (as @rura GM command) // /m /mapmove (as @rura GM command)
char output[MAP_NAME_LENGTH+15]; // Max length of a short: ' -6XXXX' -> 7 digits char output[MAP_NAME_LENGTH_EXT+15]; // Max length of a short: ' -6XXXX' -> 7 digits
char message[MAP_NAME_LENGTH+15+5]; // "/mm "+output char message[MAP_NAME_LENGTH_EXT+15+5]; // "/mm "+output
char *map_name; char *map_name;
RFIFOHEAD(fd); RFIFOHEAD(fd);
@ -8647,7 +8654,7 @@ void clif_parse_MapMove(int fd, struct map_session_data *sd) {
return; return;
map_name = RFIFOP(fd,2); map_name = RFIFOP(fd,2);
map_name[MAP_NAME_LENGTH-1]='\0'; map_name[MAP_NAME_LENGTH_EXT-1]='\0';
sprintf(output, "%s %d %d", map_name, RFIFOW(fd,18), RFIFOW(fd,20)); sprintf(output, "%s %d %d", map_name, RFIFOW(fd,18), RFIFOW(fd,20));
atcommand_rura(fd, sd, "@rura", output); atcommand_rura(fd, sd, "@rura", output);
if(log_config.gm && get_atcommand_level(AtCommand_MapMove) >= log_config.gm) if(log_config.gm && get_atcommand_level(AtCommand_MapMove) >= log_config.gm)

View File

@ -9,6 +9,7 @@
#include "../common/timer.h" #include "../common/timer.h"
#include "../common/nullpo.h" #include "../common/nullpo.h"
#include "../common/malloc.h" #include "../common/malloc.h"
#include "../common/mapindex.h"
#include "../common/showmsg.h" #include "../common/showmsg.h"
#include "../common/ers.h" #include "../common/ers.h"
@ -183,7 +184,7 @@ static int guild_read_castledb(void)
gc=(struct guild_castle *)aCalloc(1,sizeof(struct guild_castle)); gc=(struct guild_castle *)aCalloc(1,sizeof(struct guild_castle));
gc->castle_id=atoi(str[0]); gc->castle_id=atoi(str[0]);
memcpy(gc->map_name,map_normalize_name(str[1]),MAP_NAME_LENGTH-1); memcpy(gc->map_name,mapindex_normalize_name(str[1]),MAP_NAME_LENGTH-1);
memcpy(gc->castle_name,str[2],NAME_LENGTH-1); memcpy(gc->castle_name,str[2],NAME_LENGTH-1);
memcpy(gc->castle_event,str[3],NAME_LENGTH-1); memcpy(gc->castle_event,str[3],NAME_LENGTH-1);
@ -254,7 +255,7 @@ struct guild_castle *guild_mapname2gc(char *mapname)
int i; int i;
struct guild_castle *gc=NULL; struct guild_castle *gc=NULL;
map_normalize_name(mapname); mapindex_normalize_name(mapname);
for(i=0;i<MAX_GUILDCASTLE;i++){ for(i=0;i<MAX_GUILDCASTLE;i++){
gc=guild_castle_search(i); gc=guild_castle_search(i);

View File

@ -145,6 +145,20 @@ struct charid2nick {
int req_id; int req_id;
}; };
// This is the main header found at the very beginning of the map cache
struct map_cache_main_header {
unsigned long file_size;
unsigned short map_count;
};
// This is the header appended before every compressed map cells info in the map cache
struct map_cache_map_info {
char name[MAP_NAME_LENGTH];
short xs;
short ys;
long len;
};
char map_cache_file[256]="db/map_cache.dat"; char map_cache_file[256]="db/map_cache.dat";
char db_path[256] = "db"; char db_path[256] = "db";
char motd_txt[256] = "conf/motd.txt"; char motd_txt[256] = "conf/motd.txt";
@ -2410,61 +2424,34 @@ int map_eraseipport(unsigned short mapindex, uint32 ip, uint16 port)
* Map cache reading * Map cache reading
*===========================================*/ *===========================================*/
// This is the header appended before every compressed map cells info int map_readfromcache(struct map_data *m, FILE *fp)
struct map_cache_info {
char name[MAP_NAME_LENGTH];
unsigned short index;
short xs;
short ys;
long len;
};
FILE *map_cache_fp;
// Removes the extension from a map name
char *map_normalize_name(char *mapname)
{
char *ptr, *ptr2;
ptr = strchr(mapname, '.');
if (ptr) { //Check and remove extension.
while (ptr[1] && (ptr2 = strchr(ptr+1, '.')))
ptr = ptr2; //Skip to the last dot.
if(stricmp(ptr,".gat") == 0 ||
stricmp(ptr,".afm") == 0 ||
stricmp(ptr,".af2") == 0)
*ptr = '\0'; //Remove extension.
}
return mapname;
}
int map_readmap(struct map_data *m)
{ {
int i; int i;
unsigned short map_count; struct map_cache_main_header header;
struct map_cache_info info; struct map_cache_map_info info;
unsigned long size; unsigned long size;
unsigned char *buf; unsigned char *buf;
if(!map_cache_fp) if(!fp)
return 0; return 0;
fseek(map_cache_fp, 0, SEEK_SET); fseek(fp, 0, SEEK_SET);
fread(&map_count, sizeof(map_count), 1, map_cache_fp); fread(&header, sizeof(struct map_cache_main_header), 1, fp);
for(i = 0; i < map_count; i++) { for(i = 0; i < header.map_count; i++) {
fread(&info, sizeof(info), 1, map_cache_fp); fread(&info, sizeof(struct map_cache_map_info), 1, fp);
if(strcmp(m->name, info.name) == 0) { // Map found if(strcmp(m->name, info.name) == 0) { // Map found
m->xs = info.xs; m->xs = info.xs;
m->ys = info.ys; m->ys = info.ys;
m->gat = (unsigned char *)aMalloc(m->xs*m->ys); // Allocate room for map cells data m->gat = (unsigned char *)aMalloc(m->xs*m->ys); // Allocate room for map cells data
buf = aMalloc(info.len); // Allocate a temp buffer to read the zipped map buf = aMalloc(info.len); // Allocate a temp buffer to read the zipped map
fread(buf, info.len, 1, map_cache_fp); fread(buf, info.len, 1, fp);
size = m->xs*m->ys; size = m->xs*m->ys;
decode_zip(m->gat, &size, buf, info.len); // Unzip the map from the buffer decode_zip(m->gat, &size, buf, info.len); // Unzip the map from the buffer
aFree(buf); aFree(buf);
return 1; return 1;
} else // Map not found, jump to the beginning of the next map info header } else // Map not found, jump to the beginning of the next map info header
fseek(map_cache_fp, info.len, SEEK_CUR); fseek(fp, info.len, SEEK_CUR);
} }
return 0; return 0;
@ -2482,7 +2469,7 @@ int map_addmap(char *mapname) {
return 1; return 1;
} }
memcpy(map[map_num].name, map_normalize_name(mapname), MAP_NAME_LENGTH-1); memcpy(map[map_num].name, mapindex_normalize_name(mapname), MAP_NAME_LENGTH-1);
map_num++; map_num++;
return 0; return 0;
} }
@ -2520,8 +2507,9 @@ int map_readallmaps (void)
{ {
int i; int i;
int maps_removed = 0; int maps_removed = 0;
FILE *fp;
if(!(map_cache_fp = fopen(map_cache_file, "rb"))) if(!(fp = fopen(map_cache_file, "rb")))
{ {
ShowFatalError("Unable to open map cache file "CL_WHITE"%s"CL_RESET"\n", map_cache_file); ShowFatalError("Unable to open map cache file "CL_WHITE"%s"CL_RESET"\n", map_cache_file);
exit(1); //No use launching server if maps can't be read. exit(1); //No use launching server if maps can't be read.
@ -2559,7 +2547,7 @@ int map_readallmaps (void)
fflush(stdout); fflush(stdout);
} }
if(!map_readmap(&map[i])) { if(!map_readfromcache(&map[i], fp)) {
map_delmapid(i); map_delmapid(i);
maps_removed++; maps_removed++;
i--; i--;
@ -2609,6 +2597,8 @@ int map_readallmaps (void)
map[i].block_mob_count = (int*)aCallocA(size, 1); map[i].block_mob_count = (int*)aCallocA(size, 1);
} }
fclose(fp);
// finished map loading // finished map loading
printf("\r"); printf("\r");
ShowInfo("Successfully loaded '"CL_WHITE"%d"CL_RESET"' maps.%30s\n",map_num,""); ShowInfo("Successfully loaded '"CL_WHITE"%d"CL_RESET"' maps.%30s\n",map_num,"");

View File

@ -1030,7 +1030,7 @@ enum { ATK_LUCKY=1,ATK_FLEE,ATK_DEF}; //
struct map_data { struct map_data {
char name[MAP_NAME_LENGTH]; char name[MAP_NAME_LENGTH];
unsigned short index; //Index is the map index used by the mapindex* functions. unsigned short index; //Index is the map index used by the mapindex* functions.
unsigned char *gat; // NULLなら下のmap_data_other_serverとして扱う unsigned char *gat; // If this is NULL the map is not on this map-server
unsigned char *cell; //Contains temporary cell data that is set/unset on tiles. unsigned char *cell; //Contains temporary cell data that is set/unset on tiles.
#ifdef CELL_NOSTACK #ifdef CELL_NOSTACK
unsigned char *cell_bl; //Holds amount of bls in any given cell. unsigned char *cell_bl; //Holds amount of bls in any given cell.
@ -1350,7 +1350,6 @@ void map_foreachpc(int (*func)(DBKey,void*,va_list),...);
int map_foreachiddb(int (*)(DBKey,void*,va_list),...); int map_foreachiddb(int (*)(DBKey,void*,va_list),...);
void map_addnickdb(struct map_session_data *); void map_addnickdb(struct map_session_data *);
struct map_session_data * map_nick2sd(const char*); struct map_session_data * map_nick2sd(const char*);
char *map_normalize_name(char *mapname);
// ‚»‚Ì‘¼ // ‚»‚Ì‘¼
int map_check_dir(int s_dir,int t_dir); int map_check_dir(int s_dir,int t_dir);

View File

@ -1659,7 +1659,7 @@ int npc_parse_warp (char *w1,char *w2,char *w3,char *w4)
{ {
int x, y, xs, ys, to_x, to_y, m; int x, y, xs, ys, to_x, to_y, m;
int i; int i;
char mapname[MAP_NAME_LENGTH], to_mapname[MAP_NAME_LENGTH]; char mapname[MAP_NAME_LENGTH_EXT], to_mapname[MAP_NAME_LENGTH_EXT];
struct npc_data *nd; struct npc_data *nd;
// 引数の個数チェック // 引数の個数チェック
@ -1723,7 +1723,7 @@ static int npc_parse_shop (char *w1, char *w2, char *w3, char *w4)
#define MAX_SHOPITEM 100 #define MAX_SHOPITEM 100
char *p; char *p;
int x, y, dir, m, pos = 0; int x, y, dir, m, pos = 0;
char mapname[MAP_NAME_LENGTH]; char mapname[MAP_NAME_LENGTH_EXT];
struct npc_data *nd; struct npc_data *nd;
if (strcmp(w1, "-") == 0) { if (strcmp(w1, "-") == 0) {
@ -1953,7 +1953,7 @@ static int npc_skip_script (char *w1,char *w2,char *w3,char *w4,char *first_line
static int npc_parse_script(char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines,const char* file) static int npc_parse_script(char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines,const char* file)
{ {
int x, y, dir = 0, m, xs = 0, ys = 0, class_ = 0; // [Valaris] thanks to fov int x, y, dir = 0, m, xs = 0, ys = 0, class_ = 0; // [Valaris] thanks to fov
char mapname[MAP_NAME_LENGTH]; char mapname[MAP_NAME_LENGTH_EXT];
unsigned char *srcbuf = NULL; unsigned char *srcbuf = NULL;
struct script_code *script; struct script_code *script;
int srcsize = 65536; int srcsize = 65536;
@ -2380,7 +2380,7 @@ int npc_parse_mob2 (struct spawn_data *mob, int index)
int npc_parse_mob (char *w1, char *w2, char *w3, char *w4) int npc_parse_mob (char *w1, char *w2, char *w3, char *w4)
{ {
int level, num, class_, mode, x,y,xs,ys; int level, num, class_, mode, x,y,xs,ys;
char mapname[MAP_NAME_LENGTH]; char mapname[MAP_NAME_LENGTH_EXT];
char mobname[NAME_LENGTH]; char mobname[NAME_LENGTH];
struct spawn_data mob, *data; struct spawn_data mob, *data;
@ -2515,7 +2515,7 @@ int npc_parse_mob (char *w1, char *w2, char *w3, char *w4)
static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4) static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4)
{ {
int m; int m;
char mapname[MAP_NAME_LENGTH]; char mapname[MAP_NAME_LENGTH_EXT];
int state = 1; int state = 1;
// 引数の個数チェック // 引数の個数チェック
@ -2530,7 +2530,7 @@ static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4)
//マップフラグ //マップフラグ
if (strcmpi(w3, "nosave") == 0) { if (strcmpi(w3, "nosave") == 0) {
char savemap[MAP_NAME_LENGTH]; char savemap[MAP_NAME_LENGTH_EXT];
int savex, savey; int savex, savey;
if (state == 0) if (state == 0)
; //Map flag disabled. ; //Map flag disabled.
@ -2764,7 +2764,7 @@ static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4)
static int npc_parse_mapcell (char *w1, char *w2, char *w3, char *w4) static int npc_parse_mapcell (char *w1, char *w2, char *w3, char *w4)
{ {
int m, cell, x, y, x0, y0, x1, y1; int m, cell, x, y, x0, y0, x1, y1;
char type[24], mapname[MAP_NAME_LENGTH]; char type[24], mapname[MAP_NAME_LENGTH_EXT];
if (sscanf(w1, "%15[^,]", mapname) != 1) if (sscanf(w1, "%15[^,]", mapname) != 1)
return 1; return 1;

View File

@ -9119,10 +9119,10 @@ BUILDIN_FUNC(flagemblem)
BUILDIN_FUNC(getcastlename) BUILDIN_FUNC(getcastlename)
{ {
char mapname[MAP_NAME_LENGTH]; char mapname[MAP_NAME_LENGTH_EXT];
struct guild_castle *gc; struct guild_castle *gc;
strncpy(mapname, script_getstr(st,2), MAP_NAME_LENGTH); strncpy(mapname, script_getstr(st,2), MAP_NAME_LENGTH_EXT);
gc = guild_mapname2gc(mapname); gc = guild_mapname2gc(mapname);
if(gc) if(gc)
@ -9134,13 +9134,13 @@ BUILDIN_FUNC(getcastlename)
BUILDIN_FUNC(getcastledata) BUILDIN_FUNC(getcastledata)
{ {
char mapname[MAP_NAME_LENGTH]; char mapname[MAP_NAME_LENGTH_EXT];
int index=script_getnum(st,3); int index=script_getnum(st,3);
const char *event=NULL; const char *event=NULL;
struct guild_castle *gc; struct guild_castle *gc;
int i; int i;
strncpy(mapname, script_getstr(st,2), MAP_NAME_LENGTH); strncpy(mapname, script_getstr(st,2), MAP_NAME_LENGTH_EXT);
gc = guild_mapname2gc(mapname); gc = guild_mapname2gc(mapname);
if(script_hasdata(st,4) && index==0 && gc) { if(script_hasdata(st,4) && index==0 && gc) {
@ -9202,12 +9202,12 @@ BUILDIN_FUNC(getcastledata)
BUILDIN_FUNC(setcastledata) BUILDIN_FUNC(setcastledata)
{ {
char mapname[MAP_NAME_LENGTH]; char mapname[MAP_NAME_LENGTH_EXT];
int index=script_getnum(st,3); int index=script_getnum(st,3);
int value=script_getnum(st,4); int value=script_getnum(st,4);
struct guild_castle *gc; struct guild_castle *gc;
strncpy(mapname, script_getstr(st,2), MAP_NAME_LENGTH); strncpy(mapname, script_getstr(st,2), MAP_NAME_LENGTH_EXT);
gc = guild_mapname2gc(mapname); gc = guild_mapname2gc(mapname);
if(gc) { if(gc) {
@ -11084,9 +11084,9 @@ BUILDIN_FUNC(getsavepoint)
y=sd->status.save_point.y; y=sd->status.save_point.y;
switch(type){ switch(type){
case 0: case 0:
mapname=(char *) aMallocA((MAP_NAME_LENGTH+1)*sizeof(char)); mapname=(char *) aMallocA((MAP_NAME_LENGTH)*sizeof(char));
memcpy(mapname, mapindex_id2name(sd->status.save_point.map), MAP_NAME_LENGTH); memcpy(mapname, mapindex_id2name(sd->status.save_point.map), MAP_NAME_LENGTH);
mapname[MAP_NAME_LENGTH]='\0'; mapname[MAP_NAME_LENGTH-1]='\0';
script_pushstr(st,mapname); script_pushstr(st,mapname);
break; break;
case 1: case 1:
@ -11133,7 +11133,7 @@ BUILDIN_FUNC(getmapxy)
char prefix; char prefix;
int x,y,type; int x,y,type;
char mapname[MAP_NAME_LENGTH+1]; char mapname[MAP_NAME_LENGTH];
memset(mapname, 0, sizeof(mapname)); memset(mapname, 0, sizeof(mapname));
if( !data_isreference(script_getdata(st,2)) ){ if( !data_isreference(script_getdata(st,2)) ){

View File

@ -4445,8 +4445,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
} else { } else {
if (sd->skillitem != AL_TELEPORT) if (sd->skillitem != AL_TELEPORT)
{ {
char save_map[MAP_NAME_LENGTH]; char save_map[MAP_NAME_LENGTH_EXT];
snprintf(save_map, MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(sd->status.save_point.map)); snprintf(save_map, MAP_NAME_LENGTH_EXT, "%s.gat", mapindex_id2name(sd->status.save_point.map));
clif_skill_warppoint(sd,skillid,skilllv,"Random",save_map,"",""); clif_skill_warppoint(sd,skillid,skilllv,"Random",save_map,"","");
} }
else //Autocasted Teleport level 2?? else //Autocasted Teleport level 2??
@ -6086,14 +6086,14 @@ int skill_castend_pos2 (struct block_list *src, int x, int y, int skillid, int s
case AL_WARP: case AL_WARP:
if(sd) { if(sd) {
char memo[4][MAP_NAME_LENGTH] = {"", "", "", ""}; char memo[4][MAP_NAME_LENGTH_EXT] = {"", "", "", ""};
snprintf(memo[0], MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(sd->status.save_point.map)); snprintf(memo[0], MAP_NAME_LENGTH_EXT, "%s.gat", mapindex_id2name(sd->status.save_point.map));
if (skilllv>1 && sd->status.memo_point[0].map) if (skilllv>1 && sd->status.memo_point[0].map)
snprintf(memo[1], MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(sd->status.memo_point[0].map)); snprintf(memo[1], MAP_NAME_LENGTH_EXT, "%s.gat", mapindex_id2name(sd->status.memo_point[0].map));
if (skilllv>2 && sd->status.memo_point[1].map) if (skilllv>2 && sd->status.memo_point[1].map)
snprintf(memo[2], MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(sd->status.memo_point[1].map)); snprintf(memo[2], MAP_NAME_LENGTH_EXT, "%s.gat", mapindex_id2name(sd->status.memo_point[1].map));
if (skilllv>3 && sd->status.memo_point[2].map) if (skilllv>3 && sd->status.memo_point[2].map)
snprintf(memo[3], MAP_NAME_LENGTH, "%s.gat", mapindex_id2name(sd->status.memo_point[2].map)); snprintf(memo[3], MAP_NAME_LENGTH_EXT, "%s.gat", mapindex_id2name(sd->status.memo_point[2].map));
clif_skill_warppoint(sd,skillid,skilllv, clif_skill_warppoint(sd,skillid,skilllv,
memo[0],memo[1],memo[2],memo[3]); memo[0],memo[1],memo[2],memo[3]);

View File

@ -578,7 +578,6 @@ void* grfio_reads(char *fname, int *size)
if (entry != NULL && entry->gentry < 0) { if (entry != NULL && entry->gentry < 0) {
entry->gentry = -entry->gentry; // local file checked entry->gentry = -entry->gentry; // local file checked
} else { } else {
printf("%s not found (grfio_reads - local file %s)\n", fname, lfname);
return NULL; return NULL;
} }
} }

View File

@ -4,6 +4,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <malloc.h>
#ifndef _WIN32 #ifndef _WIN32
#include <unistd.h> #include <unistd.h>
@ -11,13 +12,19 @@
#include "grfio.h" #include "grfio.h"
char grf_list_file[256] = "db/grf_files.txt"; #define MAP_NAME_LENGTH 12
char map_list_file[256] = "db/map_list.txt"; #define MAP_NAME_LENGTH_EXT 16
char map_cache_file[256] = "db/map_cache.dat";
#define MAP_NAME_LENGTH 16
#define NO_WATER 1000000 #define NO_WATER 1000000
char grf_list_file[256] = "tools/mapcache/grf_files.txt";
char map_list_file[256] = "db/map_index.txt";
char map_cache_file[256] = "db/map_cache.dat";
int rebuild = 0;
FILE *map_cache_fp;
unsigned long file_size;
// Used internally, this structure contains the physical map cells // Used internally, this structure contains the physical map cells
struct map_data { struct map_data {
short xs; short xs;
@ -26,31 +33,25 @@ struct map_data {
}; };
// This is the main header found at the very beginning of the file // This is the main header found at the very beginning of the file
unsigned short map_count; struct main_header {
unsigned long file_size;
unsigned short map_count;
} header;
// This is the header appended before every compressed map cells info // This is the header appended before every compressed map cells info
struct map_cache_info { struct map_info {
char name[MAP_NAME_LENGTH]; char name[MAP_NAME_LENGTH];
unsigned short index;
short xs; short xs;
short ys; short ys;
long len; long len;
}; };
FILE *map_cache_fp;
int filesize; /*************************************
* Big-endian compatibility functions *
*************************************/
/// Converts an unsigned short (16 bits) from current machine order to little-endian // Converts a short (16 bits) from current machine order to little-endian
unsigned short MakeUShortLE(unsigned short val)
{
unsigned char buf[2];
buf[0] = (unsigned char)( (val & 0x00FF) );
buf[1] = (unsigned char)( (val & 0xFF00) >> 0x08 );
return *((unsigned short*)buf);
}
/// Converts a short (16 bits) from current machine order to little-endian
short MakeShortLE(short val) short MakeShortLE(short val)
{ {
unsigned char buf[2]; unsigned char buf[2];
@ -59,7 +60,7 @@ short MakeShortLE(short val)
return *((short*)buf); return *((short*)buf);
} }
/// Converts a long (32 bits) from current machine order to little-endian // Converts a long (32 bits) from current machine order to little-endian
long MakeLongLE(long val) long MakeLongLE(long val)
{ {
unsigned char buf[4]; unsigned char buf[4];
@ -70,7 +71,23 @@ long MakeLongLE(long val)
return *((long*)buf); return *((long*)buf);
} }
/// Reads an unsigned long (32 bits) in little-endian from the buffer // Reads an unsigned short (16 bits) in little-endian from the buffer
unsigned short GetUShort(const unsigned char *buf)
{
return ( ((unsigned short)(buf[0])) )
|( ((unsigned short)(buf[1])) << 0x08 );
}
// Reads a long (32 bits) in little-endian from the buffer
long GetLong(const unsigned char *buf)
{
return ( ((long)(buf[0])) )
|( ((long)(buf[1])) << 0x08 )
|( ((long)(buf[2])) << 0x10 )
|( ((long)(buf[3])) << 0x18 );
}
// Reads an unsigned long (32 bits) in little-endian from the buffer
unsigned long GetULong(const unsigned char *buf) unsigned long GetULong(const unsigned char *buf)
{ {
return ( ((unsigned long)(buf[0])) ) return ( ((unsigned long)(buf[0])) )
@ -87,7 +104,7 @@ float GetFloat(const unsigned char *buf)
} }
// Read map from GRF's GAT and RSW files // Reads a map from GRF's GAT and RSW files
int read_map(char *name, struct map_data *m) int read_map(char *name, struct map_data *m)
{ {
char filename[256]; char filename[256];
@ -143,31 +160,31 @@ int read_map(char *name, struct map_data *m)
return 1; return 1;
} }
void cache_map(char *name, unsigned short index, struct map_data *m) // Adds a map to the cache
void cache_map(char *name, struct map_data *m)
{ {
struct map_cache_info info; struct map_info info;
unsigned long len; long len;
char *write_buf; unsigned char *write_buf;
// Create an output buffer twice as big as the uncompressed map... this way we're sure it fits // Create an output buffer twice as big as the uncompressed map... this way we're sure it fits
len = m->xs*m->ys*2; len = m->xs*m->ys*2;
write_buf = (char *)malloc(len); write_buf = (unsigned char *)malloc(len);
// Compress the cells and get the compressed length // Compress the cells and get the compressed length
encode_zip((unsigned char *)write_buf, &len, m->cells, m->xs*m->ys); encode_zip(write_buf, &len, m->cells, m->xs*m->ys);
// Fill the map header // Fill the map header
strncpy(info.name, name, MAP_NAME_LENGTH); strncpy(info.name, name, MAP_NAME_LENGTH);
info.index = MakeUShortLE(index);
info.xs = MakeShortLE(m->xs); info.xs = MakeShortLE(m->xs);
info.ys = MakeShortLE(m->ys); info.ys = MakeShortLE(m->ys);
info.len = MakeLongLE((long)len); info.len = MakeLongLE(len);
// Append map header then compressed cells at the end of the file // Append map header then compressed cells at the end of the file
fseek(map_cache_fp, filesize, SEEK_SET); fseek(map_cache_fp, header.file_size, SEEK_SET);
fwrite(&info, sizeof(struct map_cache_info), 1, map_cache_fp); fwrite(&info, sizeof(struct map_info), 1, map_cache_fp);
fwrite(write_buf, 1, len, map_cache_fp); fwrite(write_buf, 1, len, map_cache_fp);
map_count++; header.file_size += sizeof(struct map_info) + len;
filesize += sizeof(struct map_cache_info) + len; header.map_count++;
free(write_buf); free(write_buf);
free(m->cells); free(m->cells);
@ -175,41 +192,111 @@ void cache_map(char *name, unsigned short index, struct map_data *m)
return; return;
} }
// Checks whether a map is already is the cache
int find_map(char *name)
{
int i;
struct map_info info;
fseek(map_cache_fp, sizeof(struct main_header), SEEK_SET);
for(i = 0; i < header.map_count; i++) {
fread(&info, sizeof(info), 1, map_cache_fp);
if(strcmp(name, info.name) == 0) // Map found
return 1;
else // Map not found, jump to the beginning of the next map info header
fseek(map_cache_fp, GetLong((unsigned char *)&(info.len)), SEEK_CUR);
}
return 0;
}
// Cuts the extension from a map name
char *remove_extension(char *mapname)
{
char *ptr, *ptr2;
ptr = strchr(mapname, '.');
if (ptr) { //Check and remove extension.
while (ptr[1] && (ptr2 = strchr(ptr+1, '.')))
ptr = ptr2; //Skip to the last dot.
if(stricmp(ptr,".gat") == 0 ||
stricmp(ptr,".afm") == 0 ||
stricmp(ptr,".af2") == 0)
*ptr = '\0'; //Remove extension.
}
return mapname;
}
// Processes command-line arguments
void process_args(int argc, char *argv[])
{
int i;
for(i = 0; i < argc; i++) {
if(strcmp(argv[i], "-grf") == 0) {
if(++i < argc)
strcpy(grf_list_file, argv[i]);
} else if(strcmp(argv[i], "-list") == 0) {
if(++i < argc)
strcpy(map_list_file, argv[i]);
} else if(strcmp(argv[i], "-cache") == 0) {
if(++i < argc)
strcpy(map_cache_file, argv[i]);
} else if(strcmp(argv[i], "-rebuild") == 0)
rebuild = 1;
}
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
FILE *list; FILE *list;
char line[1024]; char line[1024];
struct map_data map; struct map_data map;
char name[MAP_NAME_LENGTH]; char name[MAP_NAME_LENGTH_EXT];
unsigned short index = 1;
if(argc > 1) // Process the command-line arguments
strcpy(grf_list_file, argv[1]); process_args(argc, argv);
if(argc > 2)
strcpy(map_list_file, argv[2]);
if(argc > 3)
strcpy(map_cache_file, argv[3]);
printf("Initializing grfio with %s\n", grf_list_file); printf("Initializing grfio with %s\n", grf_list_file);
grfio_init(grf_list_file); grfio_init(grf_list_file);
// Attempt to open the map cache file and force rebuild if not found
printf("Opening map cache: %s\n", map_cache_file); printf("Opening map cache: %s\n", map_cache_file);
map_cache_fp = fopen(map_cache_file, "wb"); if(!rebuild) {
if( map_cache_fp == NULL ) { map_cache_fp = fopen(map_cache_file, "rb");
if(map_cache_fp == NULL) {
printf("Existing map cache not found, forcing rebuild mode\n");
rebuild = 1;
} else
fclose(map_cache_fp);
}
if(rebuild)
map_cache_fp = fopen(map_cache_file, "w+b");
else
map_cache_fp = fopen(map_cache_file, "r+b");
if(map_cache_fp == NULL) {
printf("Failure when opening map cache file %s\n", map_cache_file); printf("Failure when opening map cache file %s\n", map_cache_file);
exit(1); exit(1);
} }
// Open the map list
printf("Opening map list: %s\n", map_list_file); printf("Opening map list: %s\n", map_list_file);
list = fopen(map_list_file, "r"); list = fopen(map_list_file, "r");
if( list == NULL ) { if(list == NULL) {
printf("Failure when opening maps list file %s\n", map_list_file); printf("Failure when opening maps list file %s\n", map_list_file);
exit(1); exit(2);
} }
// Initialize the main header // Initialize the main header
map_count = 0; if(rebuild) {
filesize = sizeof(map_count); header.file_size = sizeof(struct main_header);
header.map_count = 0;
} else {
fread(&header, sizeof(struct main_header), 1, map_cache_fp);
header.file_size = GetULong((unsigned char *)&(header.file_size));
header.map_count = GetUShort((unsigned char *)&(header.map_count));
}
// Read and process the map list // Read and process the map list
while(fgets(line, 1020, list)){ while(fgets(line, 1020, list)){
@ -217,30 +304,37 @@ int main(int argc, char *argv[])
if(line[0] == '/' && line[1] == '/') if(line[0] == '/' && line[1] == '/')
continue; continue;
if(sscanf(line, "%16s %hu", name, &index) > 0) { // No defines in strings, 16 is hardcoded here if(sscanf(line, "%15s", name) < 1)
printf("Index %d : %s\n", index, name); continue;
if(read_map(name, &map))
cache_map(name, index, &map); if(strcmp("map:", name) == 0 && sscanf(line, "%*s %15s", name) < 1)
else continue;
printf("Map file not found in GRF\n");
// If the 2nd argument is omitted at next line, we'll keep last used index + 1 remove_extension(name);
index++; printf("%s", name);
} if(find_map(name))
printf(" already in cache!\n");
else if(read_map(name, &map)) {
cache_map(name, &map);
printf(" successfully cached\n");
} else
printf(" not found in GRF!\n");
} }
printf("Closing map list: %s\n", map_list_file); printf("Closing map list: %s\n", map_list_file);
fclose(list); fclose(list);
printf("Closing map cache: %s\n", map_cache_file);
// Write the main header and close the map cache // Write the main header and close the map cache
printf("Closing map cache: %s\n", map_cache_file);
fseek(map_cache_fp, 0, SEEK_SET); fseek(map_cache_fp, 0, SEEK_SET);
fwrite(&map_count, sizeof(map_count), 1, map_cache_fp); fwrite(&header, sizeof(struct main_header), 1, map_cache_fp);
fclose(map_cache_fp); fclose(map_cache_fp);
printf("Finalizing grfio\n"); printf("Finalizing grfio\n");
grfio_final(); grfio_final();
printf("%d maps cached\n", map_count); printf("%d maps now in cache\n", header.map_count);
return 0; return 0;
} }

View File

@ -0,0 +1,15 @@
//-----------------------------------------
// GRF List
// Add as many entries as you wish
//-----------------------------------------
//grf: C:\Program Files\Gravity\RO\data.grf
grf: C:\Program Files\Gravity\RO\sdata.grf
//-----------------------------------------
// Data Directory
// Path must include trailing backslash
// Only one entry supported!
//-----------------------------------------
//data_dir: C:\Program Files\Gravity\RO\