Remove 6-define limit in the cache filename by storing part in a SHA-1 hash.
Note that i might have messed up somewhere my implementation of the SHA-1 algorithm since it doesn't seem to produce exactly the same result as sha1sum :/ ... but well it seems good enough for the task.
This commit is contained in:
parent
4fbd88f19b
commit
32251b76a6
4 changed files with 320 additions and 136 deletions
|
@ -113,6 +113,7 @@ wesnoth_SOURCES = \
|
|||
replay_controller.cpp \
|
||||
reports.cpp \
|
||||
sdl_utils.cpp \
|
||||
sha1.cpp \
|
||||
show_dialog.cpp \
|
||||
sound.cpp \
|
||||
soundsource.cpp \
|
||||
|
@ -397,6 +398,7 @@ noinst_HEADERS = \
|
|||
replay_controller.hpp \
|
||||
reports.hpp \
|
||||
scoped_resource.hpp \
|
||||
sha1.hpp \
|
||||
sdl_utils.hpp \
|
||||
show_dialog.hpp \
|
||||
sound.hpp \
|
||||
|
|
272
src/game.cpp
272
src/game.cpp
|
@ -52,6 +52,7 @@
|
|||
#include "serialization/binary_wml.hpp"
|
||||
#include "serialization/parser.hpp"
|
||||
#include "serialization/preprocessor.hpp"
|
||||
#include "sha1.hpp"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
#include "ai_python.hpp"
|
||||
|
@ -1505,152 +1506,151 @@ void game_controller::read_game_cfg(const preproc_map& defines, config& cfg, boo
|
|||
{
|
||||
log_scope("read_game_cfg");
|
||||
|
||||
if(defines.size() < 6) {
|
||||
bool is_valid = true;
|
||||
std::stringstream str;
|
||||
str << "-v" << game_config::version;
|
||||
for(preproc_map::const_iterator i = defines.begin(); i != defines.end(); ++i) {
|
||||
if(i->second.value != "" || i->second.arguments.empty() == false) {
|
||||
is_valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
str << "-" << i->first;
|
||||
bool is_valid = true;
|
||||
std::stringstream str;
|
||||
for(preproc_map::const_iterator i = defines.begin(); i != defines.end(); ++i) {
|
||||
if(i->second.value != "" || i->second.arguments.empty() == false) {
|
||||
is_valid = false;
|
||||
break;
|
||||
}
|
||||
//std::string localename = get_locale().localename;
|
||||
//str << "-lang_" << (localename.empty() ? "default" : localename);
|
||||
|
||||
if(is_valid) {
|
||||
const std::string& cache = get_cache_dir();
|
||||
if(cache != "") {
|
||||
const std::string fname = cache + "/cache" + str.str();
|
||||
const std::string fname_checksum = fname + ".checksum";
|
||||
str << " " << i->first;
|
||||
}
|
||||
//std::string localename = get_locale().localename;
|
||||
//str << "-lang_" << (localename.empty() ? "default" : localename);
|
||||
|
||||
file_tree_checksum dir_checksum;
|
||||
if(is_valid) {
|
||||
const std::string& cache = get_cache_dir();
|
||||
if(cache != "") {
|
||||
sha1_hash sha;
|
||||
sha.hash(str.str()); // use a hash for a shorter display of the defines
|
||||
const std::string fname = cache + "/cache-v" + game_config::version + "-" + sha.display();
|
||||
const std::string fname_checksum = fname + ".checksum";
|
||||
|
||||
if(use_cache && !force_valid_cache_) {
|
||||
try {
|
||||
if(file_exists(fname_checksum)) {
|
||||
config checksum_cfg;
|
||||
scoped_istream stream = istream_file(fname_checksum);
|
||||
read(checksum_cfg, *stream);
|
||||
dir_checksum = file_tree_checksum(checksum_cfg);
|
||||
}
|
||||
} catch(config::error&) {
|
||||
std::cerr << "cache checksum is corrupt\n";
|
||||
} catch(io_exception&) {
|
||||
std::cerr << "error reading cache checksum\n";
|
||||
}
|
||||
}
|
||||
file_tree_checksum dir_checksum;
|
||||
|
||||
if(force_valid_cache_)
|
||||
std::cerr << "skipping cache validation (forced)\n";
|
||||
|
||||
if(use_cache && file_exists(fname) && (force_valid_cache_ || file_create_time(fname) > data_tree_checksum().modified && dir_checksum == data_tree_checksum())) {
|
||||
std::cerr << "found valid cache at '" << fname << "' using it\n";
|
||||
log_scope("read cache");
|
||||
try {
|
||||
scoped_istream stream = istream_file(fname);
|
||||
read_compressed(cfg, *stream);
|
||||
return;
|
||||
} catch(config::error&) {
|
||||
std::cerr << "cache is corrupt. Loading from files\n";
|
||||
} catch(io_exception&) {
|
||||
std::cerr << "error reading cache. Loading from files\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "no valid cache found. Writing cache to '" << fname << "'\n";
|
||||
|
||||
preproc_map defines_map(defines);
|
||||
|
||||
//read the file and then write to the cache
|
||||
scoped_istream stream = preprocess_file("data/", &defines_map);
|
||||
|
||||
//reset the parse counter before reading the game files
|
||||
if (loadscreen::global_loadscreen) {
|
||||
loadscreen::global_loadscreen->parser_counter = 0;
|
||||
}
|
||||
|
||||
std::string error_log, user_error_log;
|
||||
|
||||
read(cfg, *stream, &error_log);
|
||||
|
||||
//load user campaigns
|
||||
const std::string user_campaign_dir = get_user_data_dir() + "/data/campaigns/";
|
||||
std::vector<std::string> user_campaigns, error_campaigns;
|
||||
get_files_in_dir(user_campaign_dir,&user_campaigns,NULL,ENTIRE_FILE_PATH);
|
||||
for(std::vector<std::string>::const_iterator uc = user_campaigns.begin(); uc != user_campaigns.end(); ++uc) {
|
||||
static const std::string extension = ".cfg";
|
||||
if(uc->size() < extension.size() || std::equal(uc->end() - extension.size(),uc->end(),extension.begin()) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
preproc_map user_defines_map(defines_map);
|
||||
scoped_istream stream = preprocess_file(*uc,&user_defines_map);
|
||||
|
||||
std::string campaign_error_log;
|
||||
|
||||
config user_campaign_cfg;
|
||||
read(user_campaign_cfg,*stream,&campaign_error_log);
|
||||
|
||||
if(campaign_error_log.empty()) {
|
||||
cfg.append(user_campaign_cfg);
|
||||
} else {
|
||||
user_error_log += campaign_error_log;
|
||||
error_campaigns.push_back(*uc);
|
||||
}
|
||||
} catch(config::error& err) {
|
||||
std::cerr << "error reading user campaign '" << *uc << "'\n";
|
||||
error_campaigns.push_back(*uc);
|
||||
|
||||
user_error_log += err.message + "\n";
|
||||
} catch(io_exception&) {
|
||||
std::cerr << "error reading user campaign '" << *uc << "'\n";
|
||||
error_campaigns.push_back(*uc);
|
||||
}
|
||||
}
|
||||
|
||||
if(error_campaigns.empty() == false) {
|
||||
std::stringstream msg;
|
||||
msg << _("The following add-on campaign(s) had errors and could not be loaded:");
|
||||
for(std::vector<std::string>::const_iterator i = error_campaigns.begin(); i != error_campaigns.end(); ++i) {
|
||||
msg << "\n" << *i;
|
||||
}
|
||||
|
||||
msg << "\n" << _("ERROR DETAILS:") << "\n" << user_error_log;
|
||||
|
||||
gui::show_error_message(disp(),msg.str());
|
||||
}
|
||||
|
||||
cfg.merge_children("units");
|
||||
|
||||
config& hashes = cfg.add_child("multiplayer_hashes");
|
||||
for(config::child_list::const_iterator ch = cfg.get_children("multiplayer").begin(); ch != cfg.get_children("multiplayer").end(); ++ch) {
|
||||
hashes[(**ch)["id"]] = (*ch)->hash();
|
||||
}
|
||||
|
||||
if(!error_log.empty()) {
|
||||
gui::show_error_message(disp(),
|
||||
_("Warning: Errors occurred while loading game configuration files: '") +
|
||||
error_log);
|
||||
|
||||
} else {
|
||||
try {
|
||||
scoped_ostream cache = ostream_file(fname);
|
||||
write_compressed(*cache, cfg);
|
||||
if(use_cache && !force_valid_cache_) {
|
||||
try {
|
||||
if(file_exists(fname_checksum)) {
|
||||
config checksum_cfg;
|
||||
data_tree_checksum().write(checksum_cfg);
|
||||
scoped_ostream checksum = ostream_file(fname_checksum);
|
||||
write(*checksum, checksum_cfg);
|
||||
} catch(io_exception&) {
|
||||
std::cerr << "could not write to cache '" << fname << "'\n";
|
||||
scoped_istream stream = istream_file(fname_checksum);
|
||||
read(checksum_cfg, *stream);
|
||||
dir_checksum = file_tree_checksum(checksum_cfg);
|
||||
}
|
||||
} catch(config::error&) {
|
||||
std::cerr << "cache checksum is corrupt\n";
|
||||
} catch(io_exception&) {
|
||||
std::cerr << "error reading cache checksum\n";
|
||||
}
|
||||
}
|
||||
|
||||
if(force_valid_cache_)
|
||||
std::cerr << "skipping cache validation (forced)\n";
|
||||
|
||||
if(use_cache && file_exists(fname) && (force_valid_cache_ || file_create_time(fname) > data_tree_checksum().modified && dir_checksum == data_tree_checksum())) {
|
||||
std::cerr << "found valid cache at '" << fname << "' using it\n";
|
||||
log_scope("read cache");
|
||||
try {
|
||||
scoped_istream stream = istream_file(fname);
|
||||
read_compressed(cfg, *stream);
|
||||
return;
|
||||
} catch(config::error&) {
|
||||
std::cerr << "cache is corrupt. Loading from files\n";
|
||||
} catch(io_exception&) {
|
||||
std::cerr << "error reading cache. Loading from files\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "no valid cache found. Writing cache to '" << fname << "'\n";
|
||||
|
||||
preproc_map defines_map(defines);
|
||||
|
||||
//read the file and then write to the cache
|
||||
scoped_istream stream = preprocess_file("data/", &defines_map);
|
||||
|
||||
//reset the parse counter before reading the game files
|
||||
if (loadscreen::global_loadscreen) {
|
||||
loadscreen::global_loadscreen->parser_counter = 0;
|
||||
}
|
||||
|
||||
std::string error_log, user_error_log;
|
||||
|
||||
read(cfg, *stream, &error_log);
|
||||
|
||||
//load user campaigns
|
||||
const std::string user_campaign_dir = get_user_data_dir() + "/data/campaigns/";
|
||||
std::vector<std::string> user_campaigns, error_campaigns;
|
||||
get_files_in_dir(user_campaign_dir,&user_campaigns,NULL,ENTIRE_FILE_PATH);
|
||||
for(std::vector<std::string>::const_iterator uc = user_campaigns.begin(); uc != user_campaigns.end(); ++uc) {
|
||||
static const std::string extension = ".cfg";
|
||||
if(uc->size() < extension.size() || std::equal(uc->end() - extension.size(),uc->end(),extension.begin()) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return;
|
||||
try {
|
||||
preproc_map user_defines_map(defines_map);
|
||||
scoped_istream stream = preprocess_file(*uc,&user_defines_map);
|
||||
|
||||
std::string campaign_error_log;
|
||||
|
||||
config user_campaign_cfg;
|
||||
read(user_campaign_cfg,*stream,&campaign_error_log);
|
||||
|
||||
if(campaign_error_log.empty()) {
|
||||
cfg.append(user_campaign_cfg);
|
||||
} else {
|
||||
user_error_log += campaign_error_log;
|
||||
error_campaigns.push_back(*uc);
|
||||
}
|
||||
} catch(config::error& err) {
|
||||
std::cerr << "error reading user campaign '" << *uc << "'\n";
|
||||
error_campaigns.push_back(*uc);
|
||||
|
||||
user_error_log += err.message + "\n";
|
||||
} catch(io_exception&) {
|
||||
std::cerr << "error reading user campaign '" << *uc << "'\n";
|
||||
error_campaigns.push_back(*uc);
|
||||
}
|
||||
}
|
||||
|
||||
if(error_campaigns.empty() == false) {
|
||||
std::stringstream msg;
|
||||
msg << _("The following add-on campaign(s) had errors and could not be loaded:");
|
||||
for(std::vector<std::string>::const_iterator i = error_campaigns.begin(); i != error_campaigns.end(); ++i) {
|
||||
msg << "\n" << *i;
|
||||
}
|
||||
|
||||
msg << "\n" << _("ERROR DETAILS:") << "\n" << user_error_log;
|
||||
|
||||
gui::show_error_message(disp(),msg.str());
|
||||
}
|
||||
|
||||
cfg.merge_children("units");
|
||||
|
||||
config& hashes = cfg.add_child("multiplayer_hashes");
|
||||
for(config::child_list::const_iterator ch = cfg.get_children("multiplayer").begin(); ch != cfg.get_children("multiplayer").end(); ++ch) {
|
||||
hashes[(**ch)["id"]] = (*ch)->hash();
|
||||
}
|
||||
|
||||
if(!error_log.empty()) {
|
||||
gui::show_error_message(disp(),
|
||||
_("Warning: Errors occurred while loading game configuration files: '") +
|
||||
error_log);
|
||||
|
||||
} else {
|
||||
try {
|
||||
scoped_ostream cache = ostream_file(fname);
|
||||
write_compressed(*cache, cfg);
|
||||
config checksum_cfg;
|
||||
data_tree_checksum().write(checksum_cfg);
|
||||
scoped_ostream checksum = ostream_file(fname_checksum);
|
||||
write(*checksum, checksum_cfg);
|
||||
} catch(io_exception&) {
|
||||
std::cerr << "could not write to cache '" << fname << "'\n";
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
151
src/sha1.cpp
Normal file
151
src/sha1.cpp
Normal file
|
@ -0,0 +1,151 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2007 by Benoit Timbert <benoit.timbert@free.fr>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/* This is supposed to be an implementation of the
|
||||
Secure Hash Algorithm 1 (SHA-1)
|
||||
|
||||
Check RFC 3174 for details about the algorithm.
|
||||
|
||||
Currently this implementation might produce different result on endian and
|
||||
bigendian machines, but for our current usage, we don't care :)
|
||||
*/
|
||||
|
||||
#include "sha1.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#define sha_rotl(n,x) ( ((x) << (n)) | ((x) >> (32-(n))) )
|
||||
#define sha_ch(x,y,z) ( ((x) & (y)) | ((!(x)) & (z)) )
|
||||
#define sha_parity(x,y,z) ( (x) ^ (y) ^ (z) )
|
||||
#define sha_maj(x,y,z) ( ((x) & (y)) | ((x) & (z)) | ((y) & (z)) )
|
||||
|
||||
// display our hash
|
||||
std::string sha1_hash::display() {
|
||||
std::stringstream s;
|
||||
s << std::hex << std::setfill('0') << std::setw(8) << H0;
|
||||
s << std::hex << std::setfill('0') << std::setw(8) << H1;
|
||||
s << std::hex << std::setfill('0') << std::setw(8) << H2;
|
||||
s << std::hex << std::setfill('0') << std::setw(8) << H3;
|
||||
s << std::hex << std::setfill('0') << std::setw(8) << H4;
|
||||
return s.str();
|
||||
}
|
||||
|
||||
// make a hash from a string
|
||||
void sha1_hash::hash(const std::string& str) {
|
||||
char block[64];
|
||||
|
||||
int bytes_left = str.size();
|
||||
uint32_t ssz = bytes_left * 8; // string length in bits
|
||||
|
||||
// initialize the hash values
|
||||
H0 = 0x67452301;
|
||||
H1 = 0xefcdab89;
|
||||
H2 = 0x98badcfe;
|
||||
H3 = 0x10325476;
|
||||
H4 = 0xc3d2e1f0;
|
||||
|
||||
std::stringstream iss (str, std::stringstream::in);
|
||||
// cut our string in 64 bytes blocks then process it
|
||||
while (bytes_left > 0) {
|
||||
iss.read(block, 64);
|
||||
if (bytes_left <= 64) { // if it's the last block, pad it
|
||||
if (bytes_left < 64) {
|
||||
block[bytes_left]=0x80; // add a 1 bit right after the end of the string
|
||||
}
|
||||
int i;
|
||||
for (i = 63; i > bytes_left; i--) {
|
||||
block[i]=0; // pad our block with zeros
|
||||
}
|
||||
if (bytes_left < 60) { // enough space to store the length
|
||||
// put the length at the end of the block
|
||||
block[60] = ssz >> 24;
|
||||
block[61] = ssz >> 16;
|
||||
block[62] = ssz >> 8;
|
||||
block[63] = ssz;
|
||||
} else { // not enough space for the zeros => we need a new block
|
||||
next(block);
|
||||
// new block
|
||||
for (i = 0; i < 60 ; i++) {
|
||||
block[i]=0; // pad our block with zeros
|
||||
}
|
||||
if (bytes_left == 64) {
|
||||
block[0]=0x80; // add a 1 bit right after the end of the string = beginning of our new block
|
||||
}
|
||||
// put the length at the end of the block
|
||||
block[60] = ssz >> 24;
|
||||
block[61] = ssz >> 16;
|
||||
block[62] = ssz >> 8;
|
||||
block[63] = ssz;
|
||||
}
|
||||
}
|
||||
next(block);
|
||||
bytes_left -= 64;
|
||||
}
|
||||
}
|
||||
|
||||
// process the next 512 bits block
|
||||
void sha1_hash::next(char block[64]) {
|
||||
uint32_t W[80];
|
||||
uint32_t A, B, C, D, E, T;
|
||||
int i;
|
||||
|
||||
A = H0;
|
||||
B = H1;
|
||||
C = H2;
|
||||
D = H3;
|
||||
E = H4;
|
||||
for (i = 0; i < 16; i++) {
|
||||
W[i]= (block[4 * i] << 24) | (block[4 * i + 1] << 16) | (block[4 * i + 2] << 8) | block[4 * i + 3];
|
||||
}
|
||||
for (; i < 80; i++) {
|
||||
W[i]=sha_rotl(1, W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]);
|
||||
}
|
||||
for (i = 0; i < 20; i++) {
|
||||
T = sha_rotl(5,A) + sha_ch(B,C,D) + E + W[i] + 0x5a827999;
|
||||
E = D;
|
||||
D = C;
|
||||
C = sha_rotl(30,B);
|
||||
B = A;
|
||||
A = T;
|
||||
}
|
||||
for (; i < 40; i++) {
|
||||
T = sha_rotl(5,A) + sha_parity(B,C,D) + E + W[i] + 0x6ed9eba1;
|
||||
E = D;
|
||||
D = C;
|
||||
C = sha_rotl(30,B);
|
||||
B = A;
|
||||
A = T;
|
||||
}
|
||||
for (; i < 60; i++) {
|
||||
T = sha_rotl(5,A) + sha_maj(B,C,D) + E + W[i] + 0x8f1bbcdc;
|
||||
E = D;
|
||||
D = C;
|
||||
C = sha_rotl(30,B);
|
||||
B = A;
|
||||
A = T;
|
||||
}
|
||||
for (; i < 80; i++) {
|
||||
T = sha_rotl(5,A) + sha_parity(B,C,D) + E + W[i] + 0xca62c1d6;
|
||||
E = D;
|
||||
D = C;
|
||||
C = sha_rotl(30,B);
|
||||
B = A;
|
||||
A = T;
|
||||
}
|
||||
H0 += A;
|
||||
H1 += B;
|
||||
H2 += C;
|
||||
H3 += D;
|
||||
H4 += E;
|
||||
}
|
31
src/sha1.hpp
Normal file
31
src/sha1.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
Copyright (C) 2007 by Benoit Timbert <benoit.timbert@free.fr>
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef SHA1_H_INCLUDED
|
||||
#define SHA1_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
class sha1_hash
|
||||
{
|
||||
public:
|
||||
void hash(const std::string& str);
|
||||
std::string display();
|
||||
private:
|
||||
void next(char block[64]);
|
||||
uint32_t H0, H1, H2, H3, H4;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue