Cleanup and minor refactoring of progress.cc/hh

Also document some stuff in the progress.hh header
This commit is contained in:
ROllerozxa 2026-06-02 21:23:20 +02:00
commit cfcc169661
4 changed files with 300 additions and 287 deletions

View file

@ -1896,9 +1896,13 @@ initial_loader(int step)
P.s_loading_screen->set_text("Loading progress...");
break;
case 15:
progress::init();
case 15: {
char tmp[1024];
const char *storage = tms_storage_path();
snprintf(tmp, 1024, "%s/data.bin", storage);
progress::init(tmp);
break;
}
case 16:
{

View file

@ -916,11 +916,6 @@ _submit_score(void *p)
CURLcode r;
char data_path[1024];
const char *storage = tms_storage_path();
snprintf(data_path, 1023, "%s/data.bin", storage);
int highscore_level_offset = highscore_offset(W->level.community_id);
lvledit lvl;
@ -975,7 +970,7 @@ _submit_score(void *p)
part = curl_mime_addpart(mime);
curl_mime_name(part, "data.bin");
curl_mime_filedata(part, data_path);
curl_mime_filedata(part, progress::path);
char tmp[32];
sprintf(tmp, "%u", W->level.community_id);

View file

@ -22,11 +22,9 @@
std::map<uint32_t, lvl_progress*> progress::levels[3];
bool progress::initialized = false;
char progress::path[1024] = {0};
lvl_progress*
progress::get_level_progress(int level_type,
uint32_t level_id)
{
lvl_progress *progress::get_level_progress(int level_type, uint32_t level_id) {
if (level_type < 0 || level_type >= 3)
return 0;
@ -43,309 +41,310 @@ progress::get_level_progress(int level_type,
return ret;
}
void
progress::init(char *custom_path/*=0*/)
{
void progress::init(char *data_path) {
char tmp[1024];
if (custom_path) {
strcpy(tmp, custom_path);
} else {
#ifdef _NO_TMS
return;
#else
const char *storage = tms_storage_path();
snprintf(tmp, 1023, "%s/data.bin", storage);
#endif
}
memcpy(path, data_path, 1024);
long crc_pos[4]={0,0,0,0};
uint32_t crc_set=0;
long crc_pos[4] = {0,0,0,0};
uint32_t crc_set = 0;
uint32_t crc_v=0;
uint32_t crc_v = 0;
uint32_t crc_base = 0;
FILE *fp = fopen(tmp, "rb");
FILE *fp = fopen(path, "rb");
if (fp) {
fread(&tmp, 1, strlen(WARNING_STR), fp);
for (int x=0; x<3; x++) {
uint32_t nlevels;
fread(&nlevels, 1, sizeof(uint32_t), fp);
nlevels ^= A_XOR;
for (int l=0; l<nlevels; l++) {
lvl_progress p;
uint32_t r_nn=0, r_nnn=0;
uint32_t id;
switch (l%5) {
case 0: fread(tmp, 1, sizeof(uint8_t), fp); break;
case 1: fread(tmp, 3, sizeof(uint8_t), fp); break;
case 2: fread(tmp, 4, sizeof(uint8_t), fp); break;
case 3: fread(tmp, 2, sizeof(uint8_t), fp); break;
case 4: fread(tmp, 5, sizeof(uint8_t), fp); break;
}
fread(&id, 1, sizeof(uint32_t), fp);
id ^= ID_XOR;
id -= l;
if (crc_set < 4u) {
crc_pos[crc_set] = ftell(fp);
uint8_t t;
fread(&t, 1, sizeof(uint8_t), fp);
crc_v |= t << crc_set*8u;
crc_set ++;
}
fread(&p.num_plays, 1, sizeof(uint32_t), fp);
r_nn = p.num_plays;
p.completed = p.num_plays >> 31;
p.num_plays &= ~(1<<31);
fread(&p.top_score, 1, sizeof(uint32_t), fp);
r_nn += p.top_score;
p.top_score ^= O_XOR;
//tms_infof("reading: %u, %u", id, p.top_score);
fread(&p.last_score, 1, sizeof(uint32_t), fp);
r_nn += p.last_score;
p.last_score ^= O_XOR;
fread(&p.time, 1, sizeof(uint32_t), fp);
r_nn += p.time;
p.time ^= O_XOR;
uint32_t n;
fread(&n, 1, sizeof(uint32_t), fp);
n ^= X_XOR;
/* verify lower 16 bits of top score */
uint32_t t = p.top_score;
if ((t & 0xffff) != (n & 0xffff)) {
tms_infof("invalid score 1");
goto err;
}
if ( (((t < 1000000000) << 31) != (n & (1<<31)))
|| ((t < 100000) << 30) != (n & (1<<30))
|| ((t < 10000000) << 29) != (n & (1<<29))
|| ((t < 100) << 28) != (n & (1<<28))
|| ((t < 100000000) << 27) != (n & (1<<27))
|| ((t < 10000) << 26) != (n & (1<<26))
|| ((t < 1000000) << 25) != (n & (1<<25))
|| ((t < 1000) << 24) != (n & (1<<24))
|| ((t < 10) << 23) != (n & (1<<23))) {
tms_infof("invalid score 2");
goto err;
}
uint32_t nn,nnn;
fread(&nn, 1, sizeof(uint32_t), fp);
switch (l%9) {
case 0: fread(tmp, 9, sizeof(uint8_t), fp); break;
case 1: fread(tmp, 1, sizeof(uint8_t), fp); break;
case 2: fread(tmp, 2, sizeof(uint8_t), fp); break;
case 3: fread(tmp, 5, sizeof(uint8_t), fp); break;
case 4: fread(tmp, 7, sizeof(uint8_t), fp); break;
case 5: fread(tmp, 8, sizeof(uint8_t), fp); break;
case 6: fread(tmp, 3, sizeof(uint8_t), fp); break;
case 7: fread(tmp, 4, sizeof(uint8_t), fp); break;
case 8: fread(tmp, 6, sizeof(uint8_t), fp); break;
}
fread(&nnn, 1, sizeof(uint32_t), fp);
if (nn != r_nn) {tms_errorf("invalid r_nn"); goto err;};
if (nnn != (p.num_plays + p.top_score + p.last_score + p.time)) {tms_errorf("invalid nnn");goto err;};
crc_base += p.top_score;
lvl_progress *pp = new lvl_progress();
*pp = p;
levels[x].insert(std::pair<uint32_t, lvl_progress*>(id, pp));
}
}
while (crc_set < 4u) {
crc_pos[crc_set] = ftell(fp);
uint8_t t;
fread(&t, 1, sizeof(uint8_t), fp);
crc_v |= t << crc_set*8u;
crc_set ++;
}
/* verify crc32 */
long size = ftell(fp);
if (size < 0) {goto err; };
char *tcrc = (char*)malloc(size);
fseek(fp, 0, SEEK_SET);
fread(tcrc, 1, size, fp);
fclose(fp);
fp = 0;
tms_infof("crc base read: %x", crc_base);
tms_infof("crc sum read: %x", crc_v);
for (uint32_t x=0; x<4; x++) {
tcrc[crc_pos[x]] = (uint8_t)((crc_base >> (x*8u)) & 0xffu);
}
uint32_t crc = crc32(0L, (const Bytef*)tcrc, size);
free(tcrc);
if (crc != crc_v) {
tms_infof("crc mismatch");
//goto err;
}
initialized = true;
return;
} else {
if (!fp) {
tms_infof("data.bin doesn't exist, creating one");
initialized = true;
return;
}
// well...
goto load;
err:
levels[0].clear();
levels[1].clear();
levels[2].clear();
tms_errorf("ERROR: invalid data in data.bin");
if (fp) fclose(fp);
if (fp)
fclose(fp);
initialized = true;
return;
load:
fread(tmp, 1, strlen(WARNING_STR), fp);
for (int x = 0; x < 3; x++) {
uint32_t nlevels;
fread(&nlevels, 1, sizeof(uint32_t), fp);
nlevels ^= A_XOR;
for (uint32_t l = 0; l < nlevels; l++) {
lvl_progress p;
uint32_t r_nn = 0;
uint32_t id;
size_t l_size;
switch (l % 5) {
case 0: l_size = 1; break;
case 1: l_size = 3; break;
case 2: l_size = 4; break;
case 3: l_size = 2; break;
case 4: l_size = 5; break;
}
fread(tmp, l_size, sizeof(uint8_t), fp);
fread(&id, 1, sizeof(uint32_t), fp);
id ^= ID_XOR;
id -= l;
if (crc_set < 4u) {
crc_pos[crc_set] = ftell(fp);
uint8_t t;
fread(&t, 1, sizeof(uint8_t), fp);
crc_v |= t << crc_set*8u;
crc_set ++;
}
fread(&p.num_plays, 1, sizeof(uint32_t), fp);
r_nn = p.num_plays;
p.completed = p.num_plays >> 31;
p.num_plays &= ~(1<<31);
fread(&p.top_score, 1, sizeof(uint32_t), fp);
r_nn += p.top_score;
p.top_score ^= O_XOR;
//tms_infof("reading: %u, %u", id, p.top_score);
fread(&p.last_score, 1, sizeof(uint32_t), fp);
r_nn += p.last_score;
p.last_score ^= O_XOR;
fread(&p.time, 1, sizeof(uint32_t), fp);
r_nn += p.time;
p.time ^= O_XOR;
uint32_t n;
fread(&n, 1, sizeof(uint32_t), fp);
n ^= X_XOR;
/* verify lower 16 bits of top score */
uint32_t t = p.top_score;
if ((t & 0xffff) != (n & 0xffff)) {
tms_infof("invalid score 1");
goto err;
}
if ( (((t < 1000000000) << 31) != (n & (1<<31)))
|| ((t < 100000) << 30) != (n & (1<<30))
|| ((t < 10000000) << 29) != (n & (1<<29))
|| ((t < 100) << 28) != (n & (1<<28))
|| ((t < 100000000) << 27) != (n & (1<<27))
|| ((t < 10000) << 26) != (n & (1<<26))
|| ((t < 1000000) << 25) != (n & (1<<25))
|| ((t < 1000) << 24) != (n & (1<<24))
|| ((t < 10) << 23) != (n & (1<<23))) {
tms_infof("invalid score 2");
goto err;
}
uint32_t nn,nnn;
fread(&nn, 1, sizeof(uint32_t), fp);
switch (l % 9) {
case 0: l_size = 9; break;
case 1: l_size = 1; break;
case 2: l_size = 2; break;
case 3: l_size = 5; break;
case 4: l_size = 7; break;
case 5: l_size = 8; break;
case 6: l_size = 3; break;
case 7: l_size = 4; break;
case 8: l_size = 6; break;
}
fread(tmp, l_size, sizeof(uint8_t), fp);
fread(&nnn, 1, sizeof(uint32_t), fp);
if (nn != r_nn) {
tms_errorf("invalid r_nn");
goto err;
}
if (nnn != (p.num_plays + p.top_score + p.last_score + p.time)) {
tms_errorf("invalid nnn");
goto err;
}
crc_base += p.top_score;
lvl_progress *pp = new lvl_progress();
*pp = p;
levels[x].insert(std::pair<uint32_t, lvl_progress*>(id, pp));
}
}
while (crc_set < 4u) {
crc_pos[crc_set] = ftell(fp);
uint8_t t;
fread(&t, 1, sizeof(uint8_t), fp);
crc_v |= t << crc_set*8u;
crc_set ++;
}
// verify crc32
long size = ftell(fp);
if (size < 0) {
goto err;
}
char *tcrc = (char*)malloc(size);
fseek(fp, 0, SEEK_SET);
fread(tcrc, 1, size, fp);
fclose(fp);
fp = 0;
tms_infof("crc base read: %x", crc_base);
tms_infof("crc sum read: %x", crc_v);
for (uint32_t x = 0; x < 4; x++) {
tcrc[crc_pos[x]] = (uint8_t)((crc_base >> (x*8u)) & 0xffu);
}
uint32_t crc = crc32(0L, (const Bytef*)tcrc, size);
free(tcrc);
if (crc != crc_v) {
tms_infof("crc mismatch");
//goto err;
}
initialized = true;
}
void
progress::commit()
{
#ifndef _NO_TMS
if (!initialized) {
void progress::commit() {
if (!initialized)
return;
}
char filename[1024];
const char *storage = tms_storage_path();
snprintf(filename, 1023, "%s/data.bin", storage);
long crc_pos[4]={0,0,0,0};
uint32_t crc_set=0;
FILE *fp = fopen(filename, "w+b");
FILE *fp = fopen(path, "w+b");
if (fp) {
uint32_t crc_val = 0;
fwrite(WARNING_STR, 1, strlen(WARNING_STR), fp);
for (int x=0; x<3; x++) {
uint32_t t = levels[x].size() ^ A_XOR;
fwrite(&t, 1, sizeof(uint32_t), fp);
uint32_t c = 0;
for (std::map<uint32_t, lvl_progress*>::iterator i = levels[x].begin();
i != levels[x].end(); i++) {
lvl_progress p = *i->second;
uint32_t id = (i->first+c) ^ ID_XOR;
switch (c%5) {
case 0: fwrite(i->second, 1, sizeof(uint8_t), fp); break;
case 1: fwrite(i->second, 3, sizeof(uint8_t), fp); break;
case 2: fwrite(i->second, 4, sizeof(uint8_t), fp); break;
case 3: fwrite(i->second, 2, sizeof(uint8_t), fp); break;
case 4: fwrite(i->second, 5, sizeof(uint8_t), fp); break;
}
fwrite(&id, 1, sizeof(uint32_t), fp);
if (crc_set < 4) {
crc_pos[crc_set] = ftell(fp);
uint8_t xx = (uint8_t)levels[1].size();
fwrite(&xx, 1, sizeof(uint8_t), fp);
crc_set ++;
}
uint32_t n = p.num_plays | (p.completed << 31), nn=0,nnn=0;
nn += n;
nnn += p.num_plays;
fwrite(&n, 1, sizeof(uint32_t), fp);
n = p.top_score ^ O_XOR;
nn += n;
nnn += p.top_score;
fwrite(&n, 1, sizeof(uint32_t), fp);
n = p.last_score ^ O_XOR;
nn += n;
nnn += p.last_score;
fwrite(&n, 1, sizeof(uint32_t), fp);
n = p.time ^ O_XOR;
nn += n;
nnn += p.time;
fwrite(&n, 1, sizeof(uint32_t), fp);
crc_val += p.top_score;
n =(
((uint32_t)(p.top_score < 1000000000) << 31)
| ((uint32_t)(p.top_score < 100000) << 30)
| ((uint32_t)(p.top_score < 10000000) << 29)
| ((uint32_t)(p.top_score < 100) << 28)
| ((uint32_t)(p.top_score < 100000000) << 27)
| ((uint32_t)(p.top_score < 10000) << 26)
| ((uint32_t)(p.top_score < 1000000) << 25)
| ((uint32_t)(p.top_score < 1000) << 24)
| ((uint32_t)(p.top_score < 10) << 23)
| (p.top_score & 0xffff)
) ^ X_XOR;
fwrite(&n, 1, sizeof(uint32_t), fp);
fwrite(&nn, 1, sizeof(uint32_t), fp);
switch (c%9) {
case 0: fwrite(i->second, 9, sizeof(uint8_t), fp); break;
case 1: fwrite(i->second, 1, sizeof(uint8_t), fp); break;
case 2: fwrite(i->second, 2, sizeof(uint8_t), fp); break;
case 3: fwrite(i->second, 5, sizeof(uint8_t), fp); break;
case 4: fwrite(i->second, 7, sizeof(uint8_t), fp); break;
case 5: fwrite(i->second, 8, sizeof(uint8_t), fp); break;
case 6: fwrite(i->second, 3, sizeof(uint8_t), fp); break;
case 7: fwrite(i->second, 4, sizeof(uint8_t), fp); break;
case 8: fwrite(i->second, 6, sizeof(uint8_t), fp); break;
}
fwrite(&nnn, 1, sizeof(uint32_t), fp);
c++;
}
}
for (; crc_set<4; crc_set++) {
crc_pos[crc_set] = ftell(fp);
uint8_t xx = (uint8_t)levels[1].size();
fwrite(&xx, 1, sizeof(uint8_t), fp);
}
long size = ftell(fp);
for (uint32_t x=0; x<4; x++) {
fseek(fp, crc_pos[x], SEEK_SET);
uint8_t v = (uint8_t)((crc_val >> (x*8u)) & 0xffu);
fwrite(&v, 1, sizeof(uint8_t), fp);
}
fseek(fp, 0, SEEK_SET);
size_t bl;
unsigned char b[1024];
uint32_t crc = crc32(0L, Z_NULL, 0);
while ((bl = fread(b, 1, 1024, fp)) > 0) {
crc = crc32(crc, b, bl);
}
for (uint32_t x=0; x<4; x++) {
fseek(fp, crc_pos[x], SEEK_SET);
uint8_t v = (uint8_t)((crc >> (x*8u)) & 0xffu);
fwrite(&v, 1, sizeof(uint8_t), fp);
}
fclose(fp);
} else {
tms_errorf("Error: could not save progress %s", filename);
if (!fp) {
tms_errorf("Error: could not save progress %s", path);
return;
}
#endif
uint32_t crc_val = 0;
fwrite(WARNING_STR, 1, strlen(WARNING_STR), fp);
for (int x=0; x<3; x++) {
uint32_t t = levels[x].size() ^ A_XOR;
fwrite(&t, 1, sizeof(uint32_t), fp);
uint32_t c = 0;
for (std::map<uint32_t, lvl_progress*>::iterator i = levels[x].begin();
i != levels[x].end(); i++) {
lvl_progress p = *i->second;
uint32_t id = (i->first+c) ^ ID_XOR;
size_t l_size;
switch (c%5) {
case 0: l_size = 1; break;
case 1: l_size = 3; break;
case 2: l_size = 4; break;
case 3: l_size = 2; break;
case 4: l_size = 5; break;
}
fwrite(i->second, l_size, sizeof(uint8_t), fp);
fwrite(&id, 1, sizeof(uint32_t), fp);
if (crc_set < 4) {
crc_pos[crc_set] = ftell(fp);
uint8_t xx = (uint8_t)levels[1].size();
fwrite(&xx, 1, sizeof(uint8_t), fp);
crc_set ++;
}
uint32_t n = p.num_plays | (p.completed << 31), nn=0,nnn=0;
nn += n;
nnn += p.num_plays;
fwrite(&n, 1, sizeof(uint32_t), fp);
n = p.top_score ^ O_XOR;
nn += n;
nnn += p.top_score;
fwrite(&n, 1, sizeof(uint32_t), fp);
n = p.last_score ^ O_XOR;
nn += n;
nnn += p.last_score;
fwrite(&n, 1, sizeof(uint32_t), fp);
n = p.time ^ O_XOR;
nn += n;
nnn += p.time;
fwrite(&n, 1, sizeof(uint32_t), fp);
crc_val += p.top_score;
n =(
((uint32_t)(p.top_score < 1000000000) << 31)
| ((uint32_t)(p.top_score < 100000) << 30)
| ((uint32_t)(p.top_score < 10000000) << 29)
| ((uint32_t)(p.top_score < 100) << 28)
| ((uint32_t)(p.top_score < 100000000) << 27)
| ((uint32_t)(p.top_score < 10000) << 26)
| ((uint32_t)(p.top_score < 1000000) << 25)
| ((uint32_t)(p.top_score < 1000) << 24)
| ((uint32_t)(p.top_score < 10) << 23)
| (p.top_score & 0xffff)
) ^ X_XOR;
fwrite(&n, 1, sizeof(uint32_t), fp);
fwrite(&nn, 1, sizeof(uint32_t), fp);
switch (c % 9) {
case 0: l_size = 9; break;
case 1: l_size = 1; break;
case 2: l_size = 2; break;
case 3: l_size = 5; break;
case 4: l_size = 7; break;
case 5: l_size = 8; break;
case 6: l_size = 3; break;
case 7: l_size = 4; break;
case 8: l_size = 6; break;
}
fwrite(i->second, l_size, sizeof(uint8_t), fp);
fwrite(&nnn, 1, sizeof(uint32_t), fp);
c++;
}
}
for (; crc_set < 4; crc_set++) {
crc_pos[crc_set] = ftell(fp);
uint8_t xx = (uint8_t)levels[1].size();
fwrite(&xx, 1, sizeof(uint8_t), fp);
}
long size = ftell(fp);
for (uint32_t x = 0; x < 4; x++) {
fseek(fp, crc_pos[x], SEEK_SET);
uint8_t v = (uint8_t)((crc_val >> (x*8u)) & 0xffu);
fwrite(&v, 1, sizeof(uint8_t), fp);
}
fseek(fp, 0, SEEK_SET);
size_t bl;
unsigned char b[1024];
uint32_t crc = crc32(0L, Z_NULL, 0);
while ((bl = fread(b, 1, 1024, fp)) > 0) {
crc = crc32(crc, b, bl);
}
for (uint32_t x=0; x<4; x++) {
fseek(fp, crc_pos[x], SEEK_SET);
uint8_t v = (uint8_t)((crc >> (x*8u)) & 0xffu);
fwrite(&v, 1, sizeof(uint8_t), fp);
}
fclose(fp);
}

View file

@ -3,17 +3,21 @@
#include <stdint.h>
#include <map>
class lvl_progress
{
/// Class representing progress information of a specific level
class lvl_progress {
public:
// Whether the level has been completed at least once
uint8_t completed;
/// Top score achieved by the player in this level (or lowest, if LVL_LOWER_SCORE_IS_BETTER)
uint32_t top_score;
/// Last score achieved by the player in this level
uint32_t last_score;
/// Amount of time in seconds it took to complete the level? (Currently unused, always 0)
uint32_t time;
/// Amount of times a level has been played
uint32_t num_plays;
lvl_progress()
{
lvl_progress() {
completed = 0;
top_score = 0;
last_score = 0;
@ -22,13 +26,24 @@ class lvl_progress
}
};
class progress
{
/// Class used for storing and loading player persistent progress in a way that
/// is not easily tampered with.
class progress {
public:
static bool initialized;
static char path[1024];
static std::map<uint32_t, lvl_progress*> levels[3];
/**
* Return a pointer to a level progress instance. If there is no progress for the
* given level, a new instance is created, added to the internal map and returned.
*/
static lvl_progress *get_level_progress(int level_type, uint32_t level_id);
static void init(char *custom_path=0);
/// Initialise progress and load from disk if it exists
static void init(char *data_path);
/// Save progress to disk
static void commit();
};