aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dungeongen.cpp135
-rw-r--r--src/dungeongen.h84
-rw-r--r--src/mapgen_v6.cpp40
-rw-r--r--src/mapgen_v6.h7
-rw-r--r--src/mapgen_v7.cpp2
5 files changed, 158 insertions, 110 deletions
diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp
index 7b529a83f..fee6f66c0 100644
--- a/src/dungeongen.cpp
+++ b/src/dungeongen.cpp
@@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h" // For g_settings
#include "main.h" // For g_profiler
+//#define DGEN_USE_TORCHES
+
NoiseParams nparams_dungeon_rarity =
{0.0, 1.0, v3f(500.0, 500.0, 500.0), 0, 2, 0.8};
NoiseParams nparams_dungeon_wetness =
@@ -40,42 +42,49 @@ NoiseParams nparams_dungeon_density =
///////////////////////////////////////////////////////////////////////////////
-DungeonGen::DungeonGen(INodeDefManager *ndef, u64 seed, s16 waterlevel) {
+DungeonGen::DungeonGen(INodeDefManager *ndef, u64 seed, s16 waterlevel,
+ DungeonParams *dparams) {
this->ndef = ndef;
this->mapseed = seed;
this->water_level = waterlevel;
+
+#ifdef DGEN_USE_TORCHES
+ c_torch = ndef->getId("default:torch");
+#endif
- np_rarity = &nparams_dungeon_rarity;
- np_wetness = &nparams_dungeon_wetness;
- np_density = &nparams_dungeon_density;
- /*
- cid_water_source = ndef->getId("mapgen_water_source");
- cid_cobble = ndef->getId("mapgen_cobble");
- cid_mossycobble = ndef->getId("mapgen_mossycobble");
- cid_torch = ndef->getId("default:torch");
- */
+ if (dparams) {
+ memcpy(&dp, dparams, sizeof(dp));
+ } else {
+ dp.c_water = ndef->getId("mapgen_water_source");
+ dp.c_cobble = ndef->getId("mapgen_cobble");
+ dp.c_moss = ndef->getId("mapgen_mossycobble");
+ dp.c_stair = ndef->getId("mapgen_stair_cobble");
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 3.0;
+ dp.holesize = v3s16(1, 2, 1);
+ dp.roomsize = v3s16(0,0,0);
+
+ dp.np_rarity = nparams_dungeon_rarity;
+ dp.np_wetness = nparams_dungeon_wetness;
+ dp.np_density = nparams_dungeon_density;
+ }
}
void DungeonGen::generate(ManualMapVoxelManipulator *vm, u32 bseed,
- v3s16 nmin, v3s16 nmax) {
+ v3s16 nmin, v3s16 nmax) {
//TimeTaker t("gen dungeons");
int approx_groundlevel = 10 + water_level;
if ((nmin.Y + nmax.Y) / 2 >= approx_groundlevel ||
- NoisePerlin3D(np_rarity, nmin.X, nmin.Y, nmin.Z, mapseed) < 0.2)
+ NoisePerlin3D(&dp.np_rarity, nmin.X, nmin.Y, nmin.Z, mapseed) < 0.2)
return;
this->vmanip = vm;
this->blockseed = bseed;
random.seed(bseed + 2);
- cid_water_source = ndef->getId("mapgen_water_source");
- cid_cobble = ndef->getId("mapgen_cobble");
- cid_mossycobble = ndef->getId("mapgen_mossycobble");
- //cid_torch = ndef->getId("default:torch");
- cid_cobblestair = ndef->getId("mapgen_stair_cobble");
-
// Dungeon generator doesn't modify places which have this set
vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);
@@ -86,7 +95,7 @@ void DungeonGen::generate(ManualMapVoxelManipulator *vm, u32 bseed,
u32 i = vmanip->m_area.index(nmin.X, y, z);
for (s16 x = nmin.X; x <= nmax.X; x++) {
content_t c = vmanip->m_data[i].getContent();
- if (c == CONTENT_AIR || c == cid_water_source)
+ if (c == CONTENT_AIR || c == dp.c_water)
vmanip->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
i++;
}
@@ -95,17 +104,18 @@ void DungeonGen::generate(ManualMapVoxelManipulator *vm, u32 bseed,
// Add it
makeDungeon(v3s16(1,1,1) * MAP_BLOCKSIZE);
-
+
// Convert some cobble to mossy cobble
- for (s16 z = nmin.Z; z <= nmax.Z; z++) {
+ if (dp.mossratio != 0.0) {
+ for (s16 z = nmin.Z; z <= nmax.Z; z++)
for (s16 y = nmin.Y; y <= nmax.Y; y++) {
u32 i = vmanip->m_area.index(nmin.X, y, z);
for (s16 x = nmin.X; x <= nmax.X; x++) {
- if (vmanip->m_data[i].getContent() == cid_cobble) {
- float wetness = NoisePerlin3D(np_wetness, x, y, z, mapseed);
- float density = NoisePerlin3D(np_density, x, y, z, blockseed);
- if (density < wetness / 3.0)
- vmanip->m_data[i].setContent(cid_mossycobble);
+ if (vmanip->m_data[i].getContent() == dp.c_cobble) {
+ float wetness = NoisePerlin3D(&dp.np_wetness, x, y, z, mapseed);
+ float density = NoisePerlin3D(&dp.np_density, x, y, z, blockseed);
+ if (density < wetness / dp.mossratio)
+ vmanip->m_data[i].setContent(dp.c_moss);
}
i++;
}
@@ -132,7 +142,8 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
roomsize = is_large_room ?
v3s16(random.range(8, 16),random.range(8, 16),random.range(8, 16)) :
v3s16(random.range(4, 8),random.range(4, 6),random.range(4, 8));
-
+ roomsize += dp.roomsize;
+
// start_padding is used to disallow starting the generation of
// a dungeon in a neighboring generation chunk
roomplace = vmanip->m_area.MinEdge + start_padding + v3s16(
@@ -184,8 +195,10 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
+#ifdef DGEN_USE_TORCHES
// Place torch at room center (for testing)
- //vmanip->m_data[vmanip->m_area.index(room_center)] = MapNode(cid_torch);
+ vmanip->m_data[vmanip->m_area.index(room_center)] = MapNode(c_torch);
+#endif
// Quit if last room
if (i == room_count - 1)
@@ -231,6 +244,8 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
// Find a place for a random sized room
roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
+ roomsize += dp.roomsize;
+
m_pos = corridor_end;
m_dir = corridor_end_dir;
r = findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace);
@@ -250,7 +265,7 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
{
- MapNode n_cobble(cid_cobble);
+ MapNode n_cobble(dp.c_cobble);
MapNode n_air(CONTENT_AIR);
// Make +-X walls
@@ -361,16 +376,19 @@ void DungeonGen::makeFill(v3s16 place, v3s16 size,
void DungeonGen::makeHole(v3s16 place)
{
- makeFill(place, v3s16(1, 2, 1), 0, MapNode(CONTENT_AIR),
- VMANIP_FLAG_DUNGEON_INSIDE);
+ makeFill(place, dp.holesize, 0,
+ MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE);
}
void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir)
{
makeHole(doorplace);
+
+#ifdef DGEN_USE_TORCHES
// Place torch (for testing)
- //vmanip->m_data[vmanip->m_area.index(doorplace)] = MapNode(cid_torch);
+ vmanip->m_data[vmanip->m_area.index(doorplace)] = MapNode(c_torch);
+#endif
}
@@ -401,30 +419,32 @@ void DungeonGen::makeCorridor(v3s16 doorplace,
if (vmanip->m_area.contains(p) == true &&
vmanip->m_area.contains(p + v3s16(0, 1, 0)) == true) {
if (make_stairs) {
- makeFill(p + v3s16(-1, -1, -1), v3s16(3, 5, 3),
- VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(cid_cobble), 0);
+ makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 3, 2),
+ VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0);
makeHole(p);
makeHole(p - dir);
// TODO: fix stairs code so it works 100% (quite difficult)
// exclude stairs from the bottom step
- if (((make_stairs == 1) && i != 0) ||
- ((make_stairs == -1) && i != length - 1)) {
+ // exclude stairs from diagonal steps
+ if (((dir.X ^ dir.Z) & 1) &&
+ (((make_stairs == 1) && i != 0) ||
+ ((make_stairs == -1) && i != length - 1))) {
// rotate face 180 deg if making stairs backwards
int facedir = dir_to_facedir(dir * make_stairs);
u32 vi = vmanip->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z);
- if (vmanip->m_data[vi].getContent() == cid_cobble)
- vmanip->m_data[vi] = MapNode(cid_cobblestair, 0, facedir);
+ if (vmanip->m_data[vi].getContent() == dp.c_cobble)
+ vmanip->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
vi = vmanip->m_area.index(p.X, p.Y, p.Z);
- if (vmanip->m_data[vi].getContent() == cid_cobble)
- vmanip->m_data[vi] = MapNode(cid_cobblestair, 0, facedir);
+ if (vmanip->m_data[vi].getContent() == dp.c_cobble)
+ vmanip->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
}
} else {
- makeFill(p + v3s16(-1, -1, -1), v3s16(3, 4, 3),
- VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(cid_cobble), 0);
+ makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 2, 2),
+ VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0);
makeHole(p);
}
@@ -469,8 +489,8 @@ bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
randomizeDir();
continue;
}
- if (vmanip->getNodeNoExNoEmerge(p).getContent() == cid_cobble
- && vmanip->getNodeNoExNoEmerge(p1).getContent() == cid_cobble)
+ if (vmanip->getNodeNoExNoEmerge(p).getContent() == dp.c_cobble
+ && vmanip->getNodeNoExNoEmerge(p1).getContent() == dp.c_cobble)
{
// Found wall, this is a good place!
result_place = p;
@@ -483,12 +503,12 @@ bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
Determine where to move next
*/
// Jump one up if the actual space is there
- if (vmanip->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == cid_cobble
+ if (vmanip->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == dp.c_cobble
&& vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == CONTENT_AIR
&& vmanip->getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent() == CONTENT_AIR)
p += v3s16(0,1,0);
// Jump one down if the actual space is there
- if (vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == cid_cobble
+ if (vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == dp.c_cobble
&& vmanip->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == CONTENT_AIR
&& vmanip->getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent() == CONTENT_AIR)
p += v3s16(0,-1,0);
@@ -577,12 +597,25 @@ bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
}
-v3s16 rand_ortho_dir(PseudoRandom &random)
+v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs)
{
- if (random.next() % 2 == 0)
- return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0);
- else
- return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1);
+ // Make diagonal directions somewhat rare
+ if (diagonal_dirs && (random.next() % 4 == 0)) {
+ v3s16 dir;
+ int trycount = 0;
+
+ do {
+ trycount++;
+ dir = v3s16(random.next() % 3 - 1, 0, random.next() % 3 - 1);
+ } while ((dir.X == 0 && dir.Z == 0) && trycount < 10);
+
+ return dir;
+ } else {
+ if (random.next() % 2 == 0)
+ return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0);
+ else
+ return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1);
+ }
}
diff --git a/src/dungeongen.h b/src/dungeongen.h
index 4be3df4aa..b2b28db3a 100644
--- a/src/dungeongen.h
+++ b/src/dungeongen.h
@@ -31,11 +31,29 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class ManualMapVoxelManipulator;
class INodeDefManager;
-v3s16 rand_ortho_dir(PseudoRandom &random);
+
+v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs);
v3s16 turn_xz(v3s16 olddir, int t);
v3s16 random_turn(PseudoRandom &random, v3s16 olddir);
int dir_to_facedir(v3s16 d);
+
+struct DungeonParams {
+ content_t c_water;
+ content_t c_cobble;
+ content_t c_moss;
+ content_t c_stair;
+
+ bool diagonal_dirs;
+ float mossratio;
+ v3s16 holesize;
+ v3s16 roomsize;
+
+ NoiseParams np_rarity;
+ NoiseParams np_wetness;
+ NoiseParams np_density;
+};
+
class DungeonGen {
public:
u32 blockseed;
@@ -45,25 +63,17 @@ public:
PseudoRandom random;
v3s16 csize;
s16 water_level;
-
- NoiseParams *np_rarity;
- NoiseParams *np_wetness;
- NoiseParams *np_density;
-
- content_t cid_water_source;
- content_t cid_cobble;
- content_t cid_mossycobble;
- content_t cid_torch;
- content_t cid_cobblestair;
+
+ content_t c_torch;
+ DungeonParams dp;
//RoomWalker
v3s16 m_pos;
v3s16 m_dir;
- DungeonGen(INodeDefManager *ndef, u64 seed, s16 waterlevel);
+ DungeonGen(INodeDefManager *ndef, u64 seed, s16 waterlevel, DungeonParams *dparams);
void generate(ManualMapVoxelManipulator *vm, u32 bseed,
- v3s16 full_node_min, v3s16 full_node_max);
- //void generate(v3s16 full_node_min, v3s16 full_node_max, u32 bseed);
+ v3s16 full_node_min, v3s16 full_node_max);
void makeDungeon(v3s16 start_padding);
void makeRoom(v3s16 roomsize, v3s16 roomplace);
@@ -79,50 +89,12 @@ public:
void randomizeDir()
{
- m_dir = rand_ortho_dir(random);
- }
-};
-
-class RoomWalker
-{
-public:
-
- RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random,
- INodeDefManager *ndef):
- vmanip(vmanip_),
- m_pos(pos),
- m_random(random),
- m_ndef(ndef)
- {
- randomizeDir();
+ m_dir = rand_ortho_dir(random, dp.diagonal_dirs);
}
-
- void randomizeDir()
- {
- m_dir = rand_ortho_dir(m_random);
- }
-
- void setPos(v3s16 pos)
- {
- m_pos = pos;
- }
-
- void setDir(v3s16 dir)
- {
- m_dir = dir;
- }
-
- //bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir);
- //bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
- // v3s16 &result_doordir, v3s16 &result_roomplace);
-
-private:
- VoxelManipulator &vmanip;
- v3s16 m_pos;
- v3s16 m_dir;
- PseudoRandom &m_random;
- INodeDefManager *m_ndef;
};
+extern NoiseParams nparams_dungeon_rarity;
+extern NoiseParams nparams_dungeon_wetness;
+extern NoiseParams nparams_dungeon_density;
#endif
diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp
index 64f3b9ed9..11491b6b6 100644
--- a/src/mapgen_v6.cpp
+++ b/src/mapgen_v6.cpp
@@ -393,10 +393,22 @@ void MapgenV6::makeChunk(BlockMakeData *data) {
c_cobble = ndef->getId("mapgen_cobble");
c_desert_sand = ndef->getId("mapgen_desert_sand");
c_desert_stone = ndef->getId("mapgen_desert_stone");
+ c_mossycobble = ndef->getId("mapgen_mossycobble");
+ c_sandbrick = ndef->getId("mapgen_sandstonebrick");
+ c_stair_cobble = ndef->getId("mapgen_stair_cobble");
+ c_stair_sandstone = ndef->getId("mapgen_stair_sandstone");
if (c_desert_sand == CONTENT_IGNORE)
c_desert_sand = c_sand;
if (c_desert_stone == CONTENT_IGNORE)
c_desert_stone = c_stone;
+ if (c_mossycobble == CONTENT_IGNORE)
+ c_mossycobble = c_cobble;
+ if (c_sandbrick == CONTENT_IGNORE)
+ c_sandbrick = c_desert_stone;
+ if (c_stair_cobble == CONTENT_IGNORE)
+ c_stair_cobble = c_cobble;
+ if (c_stair_sandstone == CONTENT_IGNORE)
+ c_stair_sandstone = c_sandbrick;
// Maximum height of the stone surface and obstacles.
// This is used to guide the cave generation
@@ -432,7 +444,33 @@ void MapgenV6::makeChunk(BlockMakeData *data) {
// Add dungeons
if (flags & MG_DUNGEONS) {
- DungeonGen dgen(ndef, data->seed, water_level);
+ DungeonParams dp;
+
+ dp.np_rarity = nparams_dungeon_rarity;
+ dp.np_density = nparams_dungeon_density;
+ dp.np_wetness = nparams_dungeon_wetness;
+ dp.c_water = c_water_source;
+ if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_NORMAL) {
+ dp.c_cobble = c_cobble;
+ dp.c_moss = c_mossycobble;
+ dp.c_stair = c_stair_cobble;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 3.0;
+ dp.holesize = v3s16(1, 2, 1);
+ dp.roomsize = v3s16(0, 0, 0);
+ } else {
+ dp.c_cobble = c_sandbrick;
+ dp.c_moss = c_sandbrick; // should make this 'cracked sandstone' later
+ dp.c_stair = c_stair_sandstone;
+
+ dp.diagonal_dirs = true;
+ dp.mossratio = 0.0;
+ dp.holesize = v3s16(2, 3, 2);
+ dp.roomsize = v3s16(2, 5, 2);
+ }
+
+ DungeonGen dgen(ndef, data->seed, water_level, &dp);
dgen.generate(vm, blockseed, full_node_min, full_node_max);
}
diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h
index 14736e3d0..eec5e4677 100644
--- a/src/mapgen_v6.h
+++ b/src/mapgen_v6.h
@@ -107,7 +107,7 @@ public:
NoiseParams *np_apple_trees;
float freq_desert;
float freq_beach;
-
+
content_t c_stone;
content_t c_dirt;
content_t c_dirt_with_grass;
@@ -119,6 +119,11 @@ public:
content_t c_desert_sand;
content_t c_desert_stone;
+ content_t c_mossycobble;
+ content_t c_sandbrick;
+ content_t c_stair_cobble;
+ content_t c_stair_sandstone;
+
MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge);
~MapgenV6();
diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp
index 1579c313c..88da4fcd2 100644
--- a/src/mapgen_v7.cpp
+++ b/src/mapgen_v7.cpp
@@ -207,7 +207,7 @@ void MapgenV7::makeChunk(BlockMakeData *data) {
generateCaves(stone_surface_max_y);
if (flags & MG_DUNGEONS) {
- DungeonGen dgen(ndef, data->seed, water_level);
+ DungeonGen dgen(ndef, data->seed, water_level, NULL);
dgen.generate(vm, blockseed, full_node_min, full_node_max);
}
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
/*
 * Secure Remote Password 6a implementation
 * https://github.com/est31/csrp-gmp
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2010, 2013 Tom Cocagne, 2015 est31 <MTest31@outlook.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

// clang-format off
#ifdef WIN32
	#include <windows.h>
	#include <wincrypt.h>
#else
	#include <ctime>

#endif
// clang-format on

#include <cstdlib>
#include <cstring>
#include <cstdio>

#include <config.h>

#if USE_SYSTEM_GMP || defined (__ANDROID__) || defined (ANDROID)
	#include <gmp.h>
#else
	#include <mini-gmp.h>
#endif

#include <util/sha2.h>

#include "srp.h"
//#define CSRP_USE_SHA1
#define CSRP_USE_SHA256

#define srp_dbg_data(data, datalen, prevtext) ;
/*void srp_dbg_data(unsigned char * data, size_t datalen, char * prevtext)
{
	printf(prevtext);
	size_t i;
	for (i = 0; i < datalen; i++)
	{
		printf("%02X", data[i]);
	}
	printf("\n");
}*/

static int g_initialized = 0;

#define RAND_BUFF_MAX 128
static unsigned int g_rand_idx;
static unsigned char g_rand_buff[RAND_BUFF_MAX];

void *(*srp_alloc)(size_t) = &malloc;
void *(*srp_realloc)(void *, size_t) = &realloc;
void (*srp_free)(void *) = &free;

// clang-format off
void srp_set_memory_functions(
		void *(*new_srp_alloc)(size_t),
		void *(*new_srp_realloc)(void *, size_t),
		void (*new_srp_free)(void *))
{
	srp_alloc = new_srp_alloc;
	srp_realloc = new_srp_realloc;
	srp_free = new_srp_free;
}
// clang-format on

typedef struct {
	mpz_t N;
	mpz_t g;
} NGConstant;

struct NGHex {
	const char *n_hex;
	const char *g_hex;
};

/* All constants here were pulled from Appendix A of RFC 5054 */
static struct NGHex global_Ng_constants[] = {
	{/* 1024 */
		"EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C"
		"9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4"
		"8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29"
		"7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A"
		"FD5138FE8376435B9FC61D2FC0EB06E3",
		"2"},
	{/* 2048 */
		"AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC319294"
		"3DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310D"
		"CD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FB"
		"D5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF74"
		"7359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A"
		"436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D"
		"5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E73"
		"03CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB6"
		"94B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F"
		"9E4AFF73",
		"2"},
	{/* 4096 */
		"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
		"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
		"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
		"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
		"49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
		"FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
		"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
		"180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
		"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
		"04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
		"B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
		"1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
		"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
		"E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
		"99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
		"04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
		"233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
		"D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
		"FFFFFFFFFFFFFFFF",
		"5"},
	{/* 8192 */
		"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
		"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
		"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
		"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
		"49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
		"FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
		"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
		"180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
		"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
		"04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
		"B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
		"1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
		"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
		"E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
		"99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
		"04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
		"233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
		"D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
		"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
		"AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
		"DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
		"2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
		"F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
		"BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
		"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
		"B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
		"387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
		"6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA"
		"3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C"
		"5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
		"22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886"
		"2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6"
		"6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5"
		"0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268"
		"359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6"
		"FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71"
		"60C980DD98EDD3DFFFFFFFFFFFFFFFFF",
		"13"},
	{0, 0} /* null sentinel */
};

static void delete_ng(NGConstant *ng)
{
	if (ng) {
		mpz_clear(ng->N);
		mpz_clear(ng->g);
		srp_free(ng);
	}
}

static NGConstant *new_ng(SRP_NGType ng_type, const char *n_hex, const char *g_hex)
{
	NGConstant *ng = (NGConstant *)srp_alloc(sizeof(NGConstant));

	if (!ng) return 0;

	mpz_init(ng->N);
	mpz_init(ng->g);

	if (ng_type != SRP_NG_CUSTOM) {
		n_hex = global_Ng_constants[ng_type].n_hex;
		g_hex = global_Ng_constants[ng_type].g_hex;
	}

	int rv = 0;
	rv = mpz_set_str(ng->N, n_hex, 16);
	rv = rv | mpz_set_str(ng->g, g_hex, 16);

	if (rv) {
		delete_ng(ng);
		return 0;
	}

	return ng;
}

typedef union {
	SHA_CTX sha;
	SHA256_CTX sha256;
	// SHA512_CTX sha512;
} HashCTX;

struct SRPVerifier {
	SRP_HashAlgorithm hash_alg;
	NGConstant *ng;

	char *username;
	unsigned char *bytes_B;
	int authenticated;

	unsigned char M[SHA512_DIGEST_LENGTH];
	unsigned char H_AMK[SHA512_DIGEST_LENGTH];
	unsigned char session_key[SHA512_DIGEST_LENGTH];
};

struct SRPUser {
	SRP_HashAlgorithm hash_alg;
	NGConstant *ng;

	mpz_t a;
	mpz_t A;
	mpz_t S;

	unsigned char *bytes_A;
	int authenticated;

	char *username;
	char *username_verifier;
	unsigned char *password;
	size_t password_len;

	unsigned char M[SHA512_DIGEST_LENGTH];
	unsigned char H_AMK[SHA512_DIGEST_LENGTH];
	unsigned char session_key[SHA512_DIGEST_LENGTH];
};

// clang-format off
static int hash_init(SRP_HashAlgorithm alg, HashCTX *c)
{
	switch (alg) {
#ifdef CSRP_USE_SHA1
		case SRP_SHA1: return SHA1_Init(&c->sha);
#endif
		/*
		case SRP_SHA224: return SHA224_Init(&c->sha256);
		*/
#ifdef CSRP_USE_SHA256
		case SRP_SHA256: return SHA256_Init(&c->sha256);
#endif
		/*
		case SRP_SHA384: return SHA384_Init(&c->sha512);
		case SRP_SHA512: return SHA512_Init(&c->sha512);
		*/
		default: return -1;
	};
}
static int hash_update( SRP_HashAlgorithm alg, HashCTX *c, const void *data, size_t len )
{
	switch (alg) {
#ifdef CSRP_USE_SHA1
		case SRP_SHA1: return SHA1_Update(&c->sha, data, len);
#endif
		/*
		case SRP_SHA224: return SHA224_Update(&c->sha256, data, len);
		*/
#ifdef CSRP_USE_SHA256
		case SRP_SHA256: return SHA256_Update(&c->sha256, data, len);
#endif
		/*
		case SRP_SHA384: return SHA384_Update(&c->sha512, data, len);
		case SRP_SHA512: return SHA512_Update(&c->sha512, data, len);
		*/
		default: return -1;
	};
}
static int hash_final( SRP_HashAlgorithm alg, HashCTX *c, unsigned char *md )
{
	switch (alg) {
#ifdef CSRP_USE_SHA1
		case SRP_SHA1: return SHA1_Final(md, &c->sha);
#endif
		/*
		case SRP_SHA224: return SHA224_Final(md, &c->sha256);
		*/
#ifdef CSRP_USE_SHA256
		case SRP_SHA256: return SHA256_Final(md, &c->sha256);
#endif
		/*
		case SRP_SHA384: return SHA384_Final(md, &c->sha512);
		case SRP_SHA512: return SHA512_Final(md, &c->sha512);
		*/
		default: return -1;
	};
}
static unsigned char *hash(SRP_HashAlgorithm alg, const unsigned char *d, size_t n, unsigned char *md)
{
	switch (alg) {
#ifdef CSRP_USE_SHA1
		case SRP_SHA1: return SHA1(d, n, md);
#endif
		/*
		case SRP_SHA224: return SHA224( d, n, md );
		*/
#ifdef CSRP_USE_SHA256
		case SRP_SHA256: return SHA256(d, n, md);
#endif
		/*
		case SRP_SHA384: return SHA384( d, n, md );
		case SRP_SHA512: return SHA512( d, n, md );
		*/
		default: return 0;
	};
}
static size_t hash_length(SRP_HashAlgorithm alg)
{
	switch (alg) {
#ifdef CSRP_USE_SHA1
		case SRP_SHA1: return SHA_DIGEST_LENGTH;
#endif
		/*
		case SRP_SHA224: return SHA224_DIGEST_LENGTH;
		*/
#ifdef CSRP_USE_SHA256
		case SRP_SHA256: return SHA256_DIGEST_LENGTH;
#endif
		/*
		case SRP_SHA384: return SHA384_DIGEST_LENGTH;
		case SRP_SHA512: return SHA512_DIGEST_LENGTH;
		*/
		default: return -1;
	};
}
// clang-format on

inline static int mpz_num_bytes(const mpz_t op)
{
	return (mpz_sizeinbase(op, 2) + 7) / 8;
}

inline static void mpz_to_bin(const mpz_t op, unsigned char *to)
{
	mpz_export(to, NULL, 1, 1, 1, 0, op);
}

inline static void mpz_from_bin(const unsigned char *s, size_t len, mpz_t ret)
{
	mpz_import(ret, len, 1, 1, 1, 0, s);
}

// set op to (op1 * op2) mod d, using tmp for the calculation
inline static void mpz_mulm(
	mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp)
{
	mpz_mul(tmp, op1, op2);
	mpz_mod(op, tmp, d);
}

// set op to (op1 + op2) mod d, using tmp for the calculation
inline static void mpz_addm(
	mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp)
{
	mpz_add(tmp, op1, op2);
	mpz_mod(op, tmp, d);
}

// set op to (op1 - op2) mod d, using tmp for the calculation
inline static void mpz_subm(
	mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp)
{
	mpz_sub(tmp, op1, op2);
	mpz_mod(op, tmp, d);
}

static SRP_Result H_nn(
	mpz_t result, SRP_HashAlgorithm alg, const mpz_t N, const mpz_t n1, const mpz_t n2)
{
	unsigned char buff[SHA512_DIGEST_LENGTH];
	size_t len_N = mpz_num_bytes(N);
	size_t len_n1 = mpz_num_bytes(n1);
	size_t len_n2 = mpz_num_bytes(n2);
	size_t nbytes = len_N + len_N;
	unsigned char *bin = (unsigned char *)srp_alloc(nbytes);
	if (!bin) return SRP_ERR;
	if (len_n1 > len_N || len_n2 > len_N) {
		srp_free(bin);
		return SRP_ERR;
	}
	memset(bin, 0, nbytes);
	mpz_to_bin(n1, bin + (len_N - len_n1));
	mpz_to_bin(n2, bin + (len_N + len_N - len_n2));
	hash(alg, bin, nbytes, buff);
	srp_free(bin);
	mpz_from_bin(buff, hash_length(alg), result);
	return SRP_OK;
}

static SRP_Result H_ns(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *n,
	size_t len_n, const unsigned char *bytes, size_t len_bytes)
{
	unsigned char buff[SHA512_DIGEST_LENGTH];
	size_t nbytes = len_n + len_bytes;
	unsigned char *bin = (unsigned char *)srp_alloc(nbytes);
	if (!bin) return SRP_ERR;
	memcpy(bin, n, len_n);
	memcpy(bin + len_n, bytes, len_bytes);
	hash(alg, bin, nbytes, buff);
	srp_free(bin);
	mpz_from_bin(buff, hash_length(alg), result);
	return SRP_OK;
}

static int calculate_x(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *salt,
	size_t salt_len, const char *username, const unsigned char *password,
	size_t password_len)
{
	unsigned char ucp_hash[SHA512_DIGEST_LENGTH];
	HashCTX ctx;
	hash_init(alg, &ctx);