cubiomes/util.c
2024-11-10 13:21:31 +01:00

585 lines
23 KiB
C

#include "util.h"
#include "finders.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
uint64_t *loadSavedSeeds(const char *fnam, uint64_t *scnt)
{
FILE *fp = fopen(fnam, "r");
uint64_t seed, i;
uint64_t *baseSeeds;
if (fp == NULL)
return NULL;
*scnt = 0;
while (!feof(fp))
{
if (fscanf(fp, "%" PRId64, (int64_t*)&seed) == 1) (*scnt)++;
else while (!feof(fp) && fgetc(fp) != '\n');
}
if (*scnt == 0)
return NULL;
baseSeeds = (uint64_t*) calloc(*scnt, sizeof(*baseSeeds));
rewind(fp);
for (i = 0; i < *scnt && !feof(fp);)
{
if (fscanf(fp, "%" PRId64, (int64_t*)&baseSeeds[i]) == 1) i++;
else while (!feof(fp) && fgetc(fp) != '\n');
}
fclose(fp);
return baseSeeds;
}
const char* mc2str(int mc)
{
switch (mc)
{
case MC_B1_7: return "Beta 1.7"; break;
case MC_B1_8: return "Beta 1.8"; break;
case MC_1_0: return "1.0"; break;
case MC_1_1: return "1.1"; break;
case MC_1_2: return "1.2"; break;
case MC_1_3: return "1.3"; break;
case MC_1_4: return "1.4"; break;
case MC_1_5: return "1.5"; break;
case MC_1_6: return "1.6"; break;
case MC_1_7: return "1.7"; break;
case MC_1_8: return "1.8"; break;
case MC_1_9: return "1.9"; break;
case MC_1_10: return "1.10"; break;
case MC_1_11: return "1.11"; break;
case MC_1_12: return "1.12"; break;
case MC_1_13: return "1.13"; break;
case MC_1_14: return "1.14"; break;
case MC_1_15: return "1.15"; break;
case MC_1_16_1: return "1.16.1"; break;
case MC_1_16: return "1.16"; break;
case MC_1_17: return "1.17"; break;
case MC_1_18: return "1.18"; break;
case MC_1_19_2: return "1.19.2"; break;
case MC_1_19: return "1.19"; break;
case MC_1_20: return "1.20"; break;
case MC_1_21_1: return "1.21.1"; break;
case MC_1_21_3: return "1.21.3"; break;
case MC_1_21_WD: return "1.21 WD"; break;
default: return "?";
}
}
int str2mc(const char *s)
{
if (!strcmp(s, "1.21")) return MC_1_21;
if (!strcmp(s, "1.21 WD")) return MC_1_21_WD;
if (!strcmp(s, "1.21.3")) return MC_1_21_3;
if (!strcmp(s, "1.21.2")) return MC_1_21_3; // backwards compatibility
if (!strcmp(s, "1.21.1")) return MC_1_21_1;
if (!strcmp(s, "1.20")) return MC_1_20;
if (!strcmp(s, "1.20.6")) return MC_1_20_6;
if (!strcmp(s, "1.19")) return MC_1_19;
if (!strcmp(s, "1.19.4")) return MC_1_19_4;
if (!strcmp(s, "1.19.2")) return MC_1_19_2;
if (!strcmp(s, "1.18")) return MC_1_18;
if (!strcmp(s, "1.18.2")) return MC_1_18_2;
if (!strcmp(s, "1.17")) return MC_1_17;
if (!strcmp(s, "1.17.1")) return MC_1_17_1;
if (!strcmp(s, "1.16")) return MC_1_16;
if (!strcmp(s, "1.16.5")) return MC_1_16_5;
if (!strcmp(s, "1.16.1")) return MC_1_16_1;
if (!strcmp(s, "1.15")) return MC_1_15;
if (!strcmp(s, "1.15.2")) return MC_1_15_2;
if (!strcmp(s, "1.14")) return MC_1_14;
if (!strcmp(s, "1.14.4")) return MC_1_14_4;
if (!strcmp(s, "1.13")) return MC_1_13;
if (!strcmp(s, "1.13.2")) return MC_1_13_2;
if (!strcmp(s, "1.12")) return MC_1_12;
if (!strcmp(s, "1.12.2")) return MC_1_12_2;
if (!strcmp(s, "1.11")) return MC_1_11;
if (!strcmp(s, "1.11.2")) return MC_1_11_2;
if (!strcmp(s, "1.10")) return MC_1_10;
if (!strcmp(s, "1.10.2")) return MC_1_10_2;
if (!strcmp(s, "1.9")) return MC_1_9;
if (!strcmp(s, "1.9.4")) return MC_1_9_4;
if (!strcmp(s, "1.8")) return MC_1_8;
if (!strcmp(s, "1.8.9")) return MC_1_8_9;
if (!strcmp(s, "1.7")) return MC_1_7;
if (!strcmp(s, "1.7.10")) return MC_1_7_10;
if (!strcmp(s, "1.6")) return MC_1_6;
if (!strcmp(s, "1.6.4")) return MC_1_6_4;
if (!strcmp(s, "1.5")) return MC_1_5;
if (!strcmp(s, "1.5.2")) return MC_1_5_2;
if (!strcmp(s, "1.4")) return MC_1_4;
if (!strcmp(s, "1.4.7")) return MC_1_4_7;
if (!strcmp(s, "1.3")) return MC_1_3;
if (!strcmp(s, "1.3.2")) return MC_1_3_2;
if (!strcmp(s, "1.2")) return MC_1_2;
if (!strcmp(s, "1.2.5")) return MC_1_2_5;
if (!strcmp(s, "1.1")) return MC_1_1;
if (!strcmp(s, "1.1.0")) return MC_1_1_0;
if (!strcmp(s, "1.0")) return MC_1_0;
if (!strcmp(s, "1.0.0")) return MC_1_0_0;
if (!strcmp(s, "Beta 1.8")) return MC_B1_8;
if (!strcmp(s, "Beta 1.7")) return MC_B1_7;
return MC_UNDEF;
}
const char *biome2str(int mc, int id)
{
if (mc >= MC_1_18)
{
// a bunch of 'new' biomes in 1.18 actually just got renamed
// (based on their features and biome id conversion when upgrading)
switch (id)
{
case old_growth_birch_forest: return "old_growth_birch_forest";
case old_growth_pine_taiga: return "old_growth_pine_taiga";
case old_growth_spruce_taiga: return "old_growth_spruce_taiga";
case snowy_plains: return "snowy_plains";
case sparse_jungle: return "sparse_jungle";
case stony_shore: return "stony_shore";
case windswept_hills: return "windswept_hills";
case windswept_forest: return "windswept_forest";
case windswept_gravelly_hills: return "windswept_gravelly_hills";
case windswept_savanna: return "windswept_savanna";
case wooded_badlands: return "wooded_badlands";
}
}
switch (id)
{
case ocean: return "ocean";
case plains: return "plains";
case desert: return "desert";
case mountains: return "mountains";
case forest: return "forest";
case taiga: return "taiga";
case swamp: return "swamp";
case river: return "river";
case nether_wastes: return "nether_wastes";
case the_end: return "the_end";
// 10
case frozen_ocean: return "frozen_ocean";
case frozen_river: return "frozen_river";
case snowy_tundra: return "snowy_tundra";
case snowy_mountains: return "snowy_mountains";
case mushroom_fields: return "mushroom_fields";
case mushroom_field_shore: return "mushroom_field_shore";
case beach: return "beach";
case desert_hills: return "desert_hills";
case wooded_hills: return "wooded_hills";
case taiga_hills: return "taiga_hills";
// 20
case mountain_edge: return "mountain_edge";
case jungle: return "jungle";
case jungle_hills: return "jungle_hills";
case jungle_edge: return "jungle_edge";
case deep_ocean: return "deep_ocean";
case stone_shore: return "stone_shore";
case snowy_beach: return "snowy_beach";
case birch_forest: return "birch_forest";
case birch_forest_hills: return "birch_forest_hills";
case dark_forest: return "dark_forest";
// 30
case snowy_taiga: return "snowy_taiga";
case snowy_taiga_hills: return "snowy_taiga_hills";
case giant_tree_taiga: return "giant_tree_taiga";
case giant_tree_taiga_hills: return "giant_tree_taiga_hills";
case wooded_mountains: return "wooded_mountains";
case savanna: return "savanna";
case savanna_plateau: return "savanna_plateau";
case badlands: return "badlands";
case wooded_badlands_plateau: return "wooded_badlands_plateau";
case badlands_plateau: return "badlands_plateau";
// 40 -- 1.13
case small_end_islands: return "small_end_islands";
case end_midlands: return "end_midlands";
case end_highlands: return "end_highlands";
case end_barrens: return "end_barrens";
case warm_ocean: return "warm_ocean";
case lukewarm_ocean: return "lukewarm_ocean";
case cold_ocean: return "cold_ocean";
case deep_warm_ocean: return "deep_warm_ocean";
case deep_lukewarm_ocean: return "deep_lukewarm_ocean";
case deep_cold_ocean: return "deep_cold_ocean";
// 50
case deep_frozen_ocean: return "deep_frozen_ocean";
// Alpha 1.2 - Beta 1.7
case seasonal_forest: return "seasonal_forest";
case shrubland: return "shrubland";
case rainforest: return "rainforest";
case the_void: return "the_void";
// mutated variants
case sunflower_plains: return "sunflower_plains";
case desert_lakes: return "desert_lakes";
case gravelly_mountains: return "gravelly_mountains";
case flower_forest: return "flower_forest";
case taiga_mountains: return "taiga_mountains";
case swamp_hills: return "swamp_hills";
case ice_spikes: return "ice_spikes";
case modified_jungle: return "modified_jungle";
case modified_jungle_edge: return "modified_jungle_edge";
case tall_birch_forest: return "tall_birch_forest";
case tall_birch_hills: return "tall_birch_hills";
case dark_forest_hills: return "dark_forest_hills";
case snowy_taiga_mountains: return "snowy_taiga_mountains";
case giant_spruce_taiga: return "giant_spruce_taiga";
case giant_spruce_taiga_hills: return "giant_spruce_taiga_hills";
case modified_gravelly_mountains: return "modified_gravelly_mountains";
case shattered_savanna: return "shattered_savanna";
case shattered_savanna_plateau: return "shattered_savanna_plateau";
case eroded_badlands: return "eroded_badlands";
case modified_wooded_badlands_plateau: return "modified_wooded_badlands_plateau";
case modified_badlands_plateau: return "modified_badlands_plateau";
// 1.14
case bamboo_jungle: return "bamboo_jungle";
case bamboo_jungle_hills: return "bamboo_jungle_hills";
// 1.16
case soul_sand_valley: return "soul_sand_valley";
case crimson_forest: return "crimson_forest";
case warped_forest: return "warped_forest";
case basalt_deltas: return "basalt_deltas";
// 1.17
case dripstone_caves: return "dripstone_caves";
case lush_caves: return "lush_caves";
// 1.18
case meadow: return "meadow";
case grove: return "grove";
case snowy_slopes: return "snowy_slopes";
case stony_peaks: return "stony_peaks";
case jagged_peaks: return "jagged_peaks";
case frozen_peaks: return "frozen_peaks";
// 1.19
case deep_dark: return "deep_dark";
case mangrove_swamp: return "mangrove_swamp";
// 1.20
case cherry_grove: return "cherry_grove";
// 1.21.4 (Winter Drop)
case pale_garden: return "pale_garden";
}
return NULL;
}
const char* struct2str(int stype)
{
switch (stype)
{
case Desert_Pyramid: return "desert_pyramid";
case Jungle_Temple: return "jungle_pyramid";
case Swamp_Hut: return "swamp_hut";
case Igloo: return "igloo";
case Village: return "village";
case Ocean_Ruin: return "ocean_ruin";
case Shipwreck: return "shipwreck";
case Monument: return "monument";
case Mansion: return "mansion";
case Outpost: return "pillager_outpost";
case Treasure: return "buried_treasure";
case Mineshaft: return "mineshaft";
case Desert_Well: return "desert_well";
case Ruined_Portal: return "ruined_portal";
case Ruined_Portal_N: return "ruined_portal_nether";
case Geode: return "amethyst_geode";
case Ancient_City: return "ancient_city";
case Trail_Ruins: return "trail_ruins";
case Trial_Chambers: return "trial_chambers";
case Fortress: return "fortress";
case Bastion: return "bastion_remnant";
case End_City: return "end_city";
case End_Gateway: return "end_gateway";
}
return NULL;
}
static void setColor(unsigned char colors[256][3], int id, uint32_t hex)
{
colors[id][0] = (hex >> 16) & 0xff;
colors[id][1] = (hex >> 8) & 0xff;
colors[id][2] = (hex >> 0) & 0xff;
}
void initBiomeColors(unsigned char colors[256][3])
{
// This coloring scheme is largely inspired by the AMIDST program:
// https://github.com/toolbox4minecraft/amidst/wiki/Biome-Color-Table
// but with additional biomes for 1.18+, and with some subtle changes to
// improve contrast for the new world generation.
memset(colors, 0, 256*3);
// biome hex color AMIDST
setColor(colors, ocean, 0x000070);
setColor(colors, plains, 0x8db360);
setColor(colors, desert, 0xfa9418);
setColor(colors, windswept_hills, 0x606060);
setColor(colors, forest, 0x056621);
setColor(colors, taiga, 0x0b6a5f); // 0b6659
setColor(colors, swamp, 0x07f9b2);
setColor(colors, river, 0x0000ff);
setColor(colors, nether_wastes, 0x572526); // bf3b3b
setColor(colors, the_end, 0x8080ff);
setColor(colors, frozen_ocean, 0x7070d6);
setColor(colors, frozen_river, 0xa0a0ff);
setColor(colors, snowy_plains, 0xffffff);
setColor(colors, snowy_mountains, 0xa0a0a0);
setColor(colors, mushroom_fields, 0xff00ff);
setColor(colors, mushroom_field_shore, 0xa000ff);
setColor(colors, beach, 0xfade55);
setColor(colors, desert_hills, 0xd25f12);
setColor(colors, wooded_hills, 0x22551c);
setColor(colors, taiga_hills, 0x163933);
setColor(colors, mountain_edge, 0x72789a);
setColor(colors, jungle, 0x507b0a); // 537b09
setColor(colors, jungle_hills, 0x2c4205);
setColor(colors, sparse_jungle, 0x60930f); // 628b17
setColor(colors, deep_ocean, 0x000030);
setColor(colors, stony_shore, 0xa2a284);
setColor(colors, snowy_beach, 0xfaf0c0);
setColor(colors, birch_forest, 0x307444);
setColor(colors, birch_forest_hills, 0x1f5f32);
setColor(colors, dark_forest, 0x40511a);
setColor(colors, snowy_taiga, 0x31554a);
setColor(colors, snowy_taiga_hills, 0x243f36);
setColor(colors, old_growth_pine_taiga, 0x596651);
setColor(colors, giant_tree_taiga_hills, 0x454f3e);
setColor(colors, windswept_forest, 0x5b7352); // 507050
setColor(colors, savanna, 0xbdb25f);
setColor(colors, savanna_plateau, 0xa79d64);
setColor(colors, badlands, 0xd94515);
setColor(colors, wooded_badlands, 0xb09765);
setColor(colors, badlands_plateau, 0xca8c65);
setColor(colors, small_end_islands, 0x4b4bab); // 8080ff
setColor(colors, end_midlands, 0xc9c959); // 8080ff
setColor(colors, end_highlands, 0xb5b536); // 8080ff
setColor(colors, end_barrens, 0x7070cc); // 8080ff
setColor(colors, warm_ocean, 0x0000ac);
setColor(colors, lukewarm_ocean, 0x000090);
setColor(colors, cold_ocean, 0x202070);
setColor(colors, deep_warm_ocean, 0x000050);
setColor(colors, deep_lukewarm_ocean, 0x000040);
setColor(colors, deep_cold_ocean, 0x202038);
setColor(colors, deep_frozen_ocean, 0x404090);
setColor(colors, seasonal_forest, 0x2f560f); // -
setColor(colors, rainforest, 0x47840e); // -
setColor(colors, shrubland, 0x789e31); // -
setColor(colors, the_void, 0x000000);
setColor(colors, sunflower_plains, 0xb5db88);
setColor(colors, desert_lakes, 0xffbc40);
setColor(colors, windswept_gravelly_hills, 0x888888);
setColor(colors, flower_forest, 0x2d8e49);
setColor(colors, taiga_mountains, 0x339287); // 338e81
setColor(colors, swamp_hills, 0x2fffda);
setColor(colors, ice_spikes, 0xb4dcdc);
setColor(colors, modified_jungle, 0x78a332); // 7ba331
setColor(colors, modified_jungle_edge, 0x88bb37); // 8ab33f
setColor(colors, old_growth_birch_forest, 0x589c6c);
setColor(colors, tall_birch_hills, 0x47875a);
setColor(colors, dark_forest_hills, 0x687942);
setColor(colors, snowy_taiga_mountains, 0x597d72);
setColor(colors, old_growth_spruce_taiga, 0x818e79);
setColor(colors, giant_spruce_taiga_hills, 0x6d7766);
setColor(colors, modified_gravelly_mountains, 0x839b7a); // 789878
setColor(colors, windswept_savanna, 0xe5da87);
setColor(colors, shattered_savanna_plateau, 0xcfc58c);
setColor(colors, eroded_badlands, 0xff6d3d);
setColor(colors, modified_wooded_badlands_plateau, 0xd8bf8d);
setColor(colors, modified_badlands_plateau, 0xf2b48d);
setColor(colors, bamboo_jungle, 0x849500); // 768e14
setColor(colors, bamboo_jungle_hills, 0x5c6c04); // 3b470a
setColor(colors, soul_sand_valley, 0x4d3a2e); // 5e3830
setColor(colors, crimson_forest, 0x981a11); // dd0808
setColor(colors, warped_forest, 0x49907b);
setColor(colors, basalt_deltas, 0x645f63); // 403636
setColor(colors, dripstone_caves, 0x4e3012); // -
setColor(colors, lush_caves, 0x283c00); // -
setColor(colors, meadow, 0x60a445); // -
setColor(colors, grove, 0x47726c); // -
setColor(colors, snowy_slopes, 0xc4c4c4); // -
setColor(colors, jagged_peaks, 0xdcdcc8); // -
setColor(colors, frozen_peaks, 0xb0b3ce); // -
setColor(colors, stony_peaks, 0x7b8f74); // -
setColor(colors, deep_dark, 0x031f29); // -
setColor(colors, mangrove_swamp, 0x2ccc8e); // -
setColor(colors, cherry_grove, 0xff91c8); // -
setColor(colors, pale_garden, 0x696d95); // -
}
void initBiomeTypeColors(unsigned char colors[256][3])
{
memset(colors, 0, 256*3);
setColor(colors, Oceanic, 0x0000a0);
setColor(colors, Warm, 0xffc000);
setColor(colors, Lush, 0x00a000);
setColor(colors, Cold, 0x606060);
setColor(colors, Freezing, 0xffffff);
}
// find the longest biome name contained in 's'
static int _str2id(const char *s)
{
if (*s == 0)
return -1;
const char *f = NULL;
int ret = -1, id;
for (id = 0; id < 256; id++)
{
const char *p = biome2str(MC_NEWEST, id);
if (p && (!f || strlen(f) < strlen(p)))
if (strstr(s, p)) {f = p; ret = id;}
const char *t = biome2str(MC_1_17, id);
if (t && t != p && (!f || strlen(f) < strlen(t)))
if (strstr(s, t)) {f = t; ret = id;}
}
return ret;
}
int parseBiomeColors(unsigned char biomeColors[256][3], const char *buf)
{
const char *p = buf;
char bstr[64];
int id, col[4], n, ib, ic;
n = 0;
while (*p)
{
for (ib = ic = 0; *p && *p != '\n' && *p != ';'; p++)
{
if ((size_t)ib+1 < sizeof(bstr))
{
if ((*p >= 'a' && *p <= 'z') || *p == '_')
bstr[ib++] = *p;
else if (*p >= 'A' && *p <= 'Z')
bstr[ib++] = (*p - 'A') + 'a';
}
if (ic < 4 && (*p == '#' || (p[0] == '0' && p[1] == 'x')))
col[ic++] = strtol(p+1+(*p=='0'), (char**)&p, 16);
else if (ic < 4 && *p >= '0' && *p <= '9')
col[ic++] = strtol(p, (char**)&p, 10);
if (*p == '\n' || *p == ';')
break;
}
while (*p && *p != '\n') p++;
while (*p == '\n') p++;
bstr[ib] = 0;
id = _str2id(bstr);
if (id >= 0 && id < 256)
{
if (ic == 3)
{
biomeColors[id][0] = col[0] & 0xff;
biomeColors[id][1] = col[1] & 0xff;
biomeColors[id][2] = col[2] & 0xff;
n++;
}
else if (ic == 1)
{
biomeColors[id][0] = (col[0] >> 16) & 0xff;
biomeColors[id][1] = (col[0] >> 8) & 0xff;
biomeColors[id][2] = (col[0] >> 0) & 0xff;
n++;
}
}
else if (ic == 4)
{
id = col[0] & 0xff;
biomeColors[id][0] = col[1] & 0xff;
biomeColors[id][1] = col[2] & 0xff;
biomeColors[id][2] = col[3] & 0xff;
n++;
}
else if (ic == 2)
{
id = col[0] & 0xff;
biomeColors[id][0] = (col[1] >> 16) & 0xff;
biomeColors[id][1] = (col[1] >> 8) & 0xff;
biomeColors[id][2] = (col[1] >> 0) & 0xff;
n++;
}
}
return n;
}
int biomesToImage(unsigned char *pixels,
unsigned char biomeColors[256][3], const int *biomes,
const unsigned int sx, const unsigned int sy,
const unsigned int pixscale, const int flip)
{
unsigned int i, j;
int containsInvalidBiomes = 0;
for (j = 0; j < sy; j++)
{
for (i = 0; i < sx; i++)
{
int id = biomes[j*sx+i];
unsigned int r, g, b;
if (id < 0 || id >= 256)
{
// This may happen for some intermediate layers
containsInvalidBiomes = 1;
r = biomeColors[id&0x7f][0]-40; r = (r>0xff) ? 0x00 : r&0xff;
g = biomeColors[id&0x7f][1]-40; g = (g>0xff) ? 0x00 : g&0xff;
b = biomeColors[id&0x7f][2]-40; b = (b>0xff) ? 0x00 : b&0xff;
}
else
{
r = biomeColors[id][0];
g = biomeColors[id][1];
b = biomeColors[id][2];
}
unsigned int m, n;
for (m = 0; m < pixscale; m++) {
for (n = 0; n < pixscale; n++) {
int idx = pixscale * i + n;
if (flip)
idx += (sx * pixscale) * ((pixscale * j) + m);
else
idx += (sx * pixscale) * ((pixscale * (sy-1-j)) + m);
unsigned char *pix = pixels + 3*idx;
pix[0] = (unsigned char)r;
pix[1] = (unsigned char)g;
pix[2] = (unsigned char)b;
}
}
}
}
return containsInvalidBiomes;
}
int savePPM(const char *path, const unsigned char *pixels, const unsigned int sx, const unsigned int sy)
{
FILE *fp = fopen(path, "wb");
if (!fp)
return -1;
fprintf(fp, "P6\n%d %d\n255\n", sx, sy);
size_t pixelsLen = 3 * sx * sy;
size_t written = fwrite(pixels, sizeof pixels[0], pixelsLen, fp);
fclose(fp);
return written != pixelsLen;
}