Encode cursor hotspots into GIF comments
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 870 B After Width: | Height: | Size: 886 B |
Before Width: | Height: | Size: 875 B After Width: | Height: | Size: 892 B |
Before Width: | Height: | Size: 873 B After Width: | Height: | Size: 889 B |
10
rsrc/graphics.exd/mac/cursors/READ_BEFORE_EDITING.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
When editing the GIF files in this folder, you need to be aware how the hotspot is stored.
|
||||||
|
Each of these GIF files encodes the cursor's hotspot into a GIF comment with the following
|
||||||
|
syntax (case-sensitive):
|
||||||
|
|
||||||
|
Hotspot(x,y)
|
||||||
|
|
||||||
|
where x and y are substituted with the actual integer coordinates. There should be no extra
|
||||||
|
spaces anywhere in that. (However, anything after the closing parenthesis will be ignored.)
|
||||||
|
|
||||||
|
If you edit any of these images, be sure to update the hotspot if necessary!
|
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 878 B After Width: | Height: | Size: 896 B |
Before Width: | Height: | Size: 878 B After Width: | Height: | Size: 895 B |
Before Width: | Height: | Size: 870 B After Width: | Height: | Size: 886 B |
Before Width: | Height: | Size: 873 B After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 863 B After Width: | Height: | Size: 879 B |
Before Width: | Height: | Size: 868 B After Width: | Height: | Size: 885 B |
Before Width: | Height: | Size: 880 B After Width: | Height: | Size: 897 B |
Before Width: | Height: | Size: 859 B After Width: | Height: | Size: 875 B |
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 879 B |
Before Width: | Height: | Size: 880 B After Width: | Height: | Size: 897 B |
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 888 B |
Before Width: | Height: | Size: 874 B After Width: | Height: | Size: 890 B |
Before Width: | Height: | Size: 860 B After Width: | Height: | Size: 89 B |
Before Width: | Height: | Size: 875 B After Width: | Height: | Size: 891 B |
Before Width: | Height: | Size: 872 B After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 878 B After Width: | Height: | Size: 894 B |
Before Width: | Height: | Size: 863 B After Width: | Height: | Size: 879 B |
Before Width: | Height: | Size: 887 B After Width: | Height: | Size: 903 B |
Before Width: | Height: | Size: 873 B After Width: | Height: | Size: 889 B |
Before Width: | Height: | Size: 883 B After Width: | Height: | Size: 899 B |
@@ -42,43 +42,54 @@ namespace ResMgr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Load a cursor from a GIF file.
|
/// Load a cursor from a GIF file.
|
||||||
/// The cursor's hotspot location is stored in a .hot file, as plaintext:
|
/// The cursor's hotspot location is stored in a GIF comment, with the following syntax (case-sensitive):
|
||||||
/// simply the X value followed by the Y value, separated by whitespace.
|
/// "Hotspot(x,y)"
|
||||||
template<> inline CursorRsrc* resLoader<CursorRsrc>::operator() (std::string fname) {
|
template<> inline CursorRsrc* resLoader<CursorRsrc>::operator() (std::string fname) {
|
||||||
// TODO: Store the hotspots on disk instead of hardcoded here
|
|
||||||
static const std::map<std::string,location> cursor_hs = {
|
|
||||||
{"wand", {4, 1}}, {"eyedropper", {1, 14}}, {"brush", {5, 13}}, {"spraycan", {8, 8}},
|
|
||||||
{"eraser", {8, 8}}, {"topleft", {8, 8}}, {"bottomright", {8, 8}}, {"hand", {14, 0}},
|
|
||||||
{"NW", {3, 3}}, {"N", {9, 3}}, {"NE", {12, 3}},
|
|
||||||
{"W", {2, 7}}, {"wait", {8, 8}}, {"E", {14, 7}},
|
|
||||||
{"SW", {3, 12}}, {"S", {9, 12}}, {"SE", {12, 12}},
|
|
||||||
{"sword", {1, 1}}, {"boot", {2, 6}}, {"drop", {14, 0}}, {"target", {8, 8}},
|
|
||||||
{"talk", {2, 13}}, {"key", {3, 2}}, {"look", {7, 6}}, {"watch", {8,8}},
|
|
||||||
};
|
|
||||||
fs::path fpath = resPool<CursorRsrc>::rel2abs(fname + ".gif");
|
fs::path fpath = resPool<CursorRsrc>::rel2abs(fname + ".gif");
|
||||||
fs::path hotpath = resPool<CursorRsrc>::rel2abs(fname + ".hot");
|
if(!fs::exists(fpath))
|
||||||
int x = 0, y = 0;
|
throw xResMgrErr("Failed to load GIF cursor: " + fname);
|
||||||
if(fs::exists(hotpath)) {
|
int x = 0, y = 0, f_sz;
|
||||||
std::ifstream fin(hotpath.c_str());
|
std::ifstream fin(fpath.c_str(), std::ios::binary);
|
||||||
fin >> x >> y;
|
fin.seekg(0, std::ios::end);
|
||||||
fin.close();
|
f_sz = fin.tellg();
|
||||||
} else {
|
fin.clear();
|
||||||
auto entry = cursor_hs.find(fname);
|
fin.seekg(0, std::ios::beg);
|
||||||
if(entry == cursor_hs.end())
|
bool found_hotspot = false;
|
||||||
std::cerr << "Cursor hotspot missing: " << fname << std::endl;
|
while(fin && !found_hotspot) {
|
||||||
else {
|
unsigned char c = fin.get();
|
||||||
std::cerr << "Cursor hotspot missing (using fallback value): " << fname << std::endl;
|
if(c != 0x21) continue;
|
||||||
location hs = entry->second;
|
c = fin.get();
|
||||||
x = hs.x; y = hs.y;
|
if(c != 0xfe) continue;
|
||||||
|
// If we get here, we've probably found a GIF comment
|
||||||
|
std::string str;
|
||||||
|
int count;
|
||||||
|
found_hotspot = true;
|
||||||
|
do {
|
||||||
|
count = fin.get();
|
||||||
|
if(count + fin.tellg() >= f_sz) {
|
||||||
|
found_hotspot = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::copy_n(std::istream_iterator<std::string::value_type>(fin), count, std::back_inserter(str));
|
||||||
|
} while(count > 0);
|
||||||
|
if(found_hotspot) {
|
||||||
|
if(str.substr(0,7) == "Hotspot") {
|
||||||
|
size_t open_paren = str.find_first_of('('), comma = str.find_first_of(','), close_paren = str.find_first_of(')');
|
||||||
|
std::string x_str = str.substr(open_paren + 1, comma - open_paren - 1);
|
||||||
|
std::string y_str = str.substr(comma + 1, close_paren - comma - 1);
|
||||||
|
x = std::stoi(x_str);
|
||||||
|
y = std::stoi(y_str);
|
||||||
|
} else found_hotspot = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(!found_hotspot)
|
||||||
|
std::cerr << "Cursor hotspot missing: " << fname << std::endl;
|
||||||
// TODO: Handle errors?
|
// TODO: Handle errors?
|
||||||
CursorRsrc* cur = new Cursor(fpath.string(),x,y);
|
CursorRsrc* cur = new Cursor(fpath.string(),x,y);
|
||||||
return cur;
|
return cur;
|
||||||
throw xResMgrErr("Failed to load GIF cursor: " + fname);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load a font form a TTF file.
|
/// Load a font from a TTF file.
|
||||||
template<> inline FontRsrc* resLoader<FontRsrc>::operator() (std::string fname) {
|
template<> inline FontRsrc* resLoader<FontRsrc>::operator() (std::string fname) {
|
||||||
fs::path fpath = resPool<FontRsrc>::rel2abs(fname + ".ttf");
|
fs::path fpath = resPool<FontRsrc>::rel2abs(fname + ".ttf");
|
||||||
FontRsrc* theFont = new FontRsrc;
|
FontRsrc* theFont = new FontRsrc;
|
||||||
|