Skip to content

Commit

Permalink
feat: V2 save format
Browse files Browse the repository at this point in the history
  Implement new SQLite-based save format. New worlds can be configured
  to use V2. Existing worlds can be converted from the main menu.

  Existing V1 worlds should load exactly the same as before until
  converted.

Signed-off-by: David Li <[email protected]>
  • Loading branch information
randombk committed Dec 10, 2024
1 parent 062461f commit b67cb16
Show file tree
Hide file tree
Showing 10 changed files with 596 additions and 30 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ include(CheckCXXCompilerFlag)
#SET(CMAKE_SHARED_LIBRARY_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_CXX_FLAGS} -m32")

find_package(PkgConfig)
find_package(SQLite3)
find_package(ZLIB)
if (NOT DYNAMIC_LINKING)
if(NOT MSVC)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.dll.a")
Expand Down
7 changes: 7 additions & 0 deletions data/raw/keybindings/keybindings.json
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,13 @@
"name": "Pick random world name",
"bindings": [ { "input_method": "keyboard", "key": "*" } ]
},
{
"type": "keybinding",
"id": "TOGGLE_V2_SAVE_FORMAT",
"category": "WORLDGEN_CONFIRM_DIALOG",
"name": "Toggle new world save format",
"bindings": [ { "input_method": "keyboard", "key": "=" } ]
},
{
"type": "keybinding",
"id": "QUIT",
Expand Down
5 changes: 5 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ if (TILES)

add_dependencies(cataclysm-bn-tiles-common get_version)

target_link_libraries(cataclysm-bn-tiles-common PUBLIC ZLIB::ZLIB)
target_link_libraries(cataclysm-bn-tiles-common PUBLIC SQLite::SQLite3)
target_link_libraries(cataclysm-bn-tiles PRIVATE cataclysm-bn-tiles-common)
target_compile_definitions(cataclysm-bn-tiles-common PUBLIC TILES )

Expand Down Expand Up @@ -176,9 +178,12 @@ if (CURSES)
endif ()

add_dependencies(cataclysm-bn-common get_version)

target_link_libraries(cataclysm-bn PRIVATE cataclysm-bn-common)

target_include_directories(cataclysm-bn-common PUBLIC ${CURSES_INCLUDE_DIR})
target_link_libraries(cataclysm-bn-common PUBLIC ZLIB::ZLIB)
target_link_libraries(cataclysm-bn-common PUBLIC SQLite::SQLite3)
target_link_libraries(cataclysm-bn-common PUBLIC ${CURSES_LIBRARIES})

if (CMAKE_USE_PTHREADS_INIT)
Expand Down
51 changes: 51 additions & 0 deletions src/compress.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "compress.h"

#include <zlib.h>
#include <vector>
#include <string>
#include <stdexcept>
#include <cstddef>

void zlib_compress(const std::string &input, std::vector<std::byte> &output) {
uLongf compressedSize = compressBound(input.size());
output.resize(compressedSize);

int result = compress2(
reinterpret_cast<Bytef *>(output.data()),
&compressedSize,
reinterpret_cast<const Bytef *>(input.data()),
input.size(),
Z_BEST_SPEED
);

if (result != Z_OK) {
throw std::runtime_error("Zlib compression error");
}

output.resize(compressedSize);
}

void zlib_decompress(const void *compressed_data, int compressed_size, std::string &output) {
// We need to guess at the decompressed size - we expect things to compress fairly well.
uLongf decompressedSize = compressed_size * 8;
output.resize(decompressedSize);

int result;
do {
result = uncompress(
reinterpret_cast<Bytef *>(&output[0]),
&decompressedSize,
reinterpret_cast<const Bytef *>(compressed_data),
compressed_size
);

if (result == Z_BUF_ERROR) {
decompressedSize *= 2; // Double the buffer size and retry
output.resize(decompressedSize);
} else if (result != Z_OK) {
throw std::runtime_error("Zlib decompression failed");
}
} while (result == Z_BUF_ERROR);

output.resize(decompressedSize);
}
12 changes: 12 additions & 0 deletions src/compress.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once
#ifndef CATA_SRC_COMPRESS_H
#define CATA_SRC_COMPRESS_H

#include <string>

#include "fstream_utils.h"

void zlib_compress(const std::string &input, std::vector<std::byte> &output);
void zlib_decompress(const void *compressed_data, int compressed_size, std::string &output);

#endif // CATA_SRC_COMPRESS_H
14 changes: 14 additions & 0 deletions src/main_menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ void main_menu::init_strings()
vWorldSubItems.emplace_back( pgettext( "Main Menu|World", "Character to Template" ) );
vWorldSubItems.emplace_back( pgettext( "Main Menu|World", "Reset World" ) );
vWorldSubItems.emplace_back( pgettext( "Main Menu|World", "Delete World" ) );
vWorldSubItems.emplace_back( pgettext( "Main Menu|World", "Convert to V2 Save Format" ) );
vWorldSubItems.emplace_back( pgettext( "Main Menu|World", "<= Return" ) );

vWorldHotkeys = { 'm', 'e', 's', 't', 'r', 'd', 'q' };
Expand Down Expand Up @@ -1061,7 +1062,20 @@ void main_menu::world_tab( const std::string &worldname )
}
};

auto convert_v2 = [this, &worldname]() {
world_generator->set_active_world( nullptr );
savegames.clear();
MAPBUFFER.clear();
overmap_buffer.clear();
world_generator->convert_to_v2( worldname );
};

switch( opt_val ) {
case 6: // Convert to V2 Save Format
if( query_yn( _( "Convert to V2 Save Format? A backup will be created. Conversion may take several minutes." ) ) ) {
convert_v2();
}
break;
case 5: // Delete World
if( query_yn( _( "Delete the world and all saves within?" ) ) ) {
clear_world( true );
Expand Down
Loading

0 comments on commit b67cb16

Please sign in to comment.