mirror of
				https://github.com/KartKrewDev/RingRacers.git
				synced 2025-10-30 08:01:28 +00:00 
			
		
		
		
	Merge branch 'new-data-structures' into 'master'
New data structures See merge request kart-krew-dev/ring-racers-internal!2498
This commit is contained in:
		
						commit
						4e13f61de5
					
				
					 87 changed files with 6082 additions and 538 deletions
				
			
		| 
						 | 
				
			
			@ -76,7 +76,7 @@ Ogg::Ogg() noexcept : memory_data_(), instance_(nullptr)
 | 
			
		|||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ogg::Ogg(std::vector<std::byte> data) : memory_data_(std::move(data)), instance_(nullptr)
 | 
			
		||||
Ogg::Ogg(Vector<std::byte> data) : memory_data_(std::move(data)), instance_(nullptr)
 | 
			
		||||
{
 | 
			
		||||
	_init_with_data();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -153,8 +153,8 @@ OggComment Ogg::comment() const
 | 
			
		|||
	stb_vorbis_comment c_comment = stb_vorbis_get_comment(instance_);
 | 
			
		||||
 | 
			
		||||
	return OggComment {
 | 
			
		||||
		std::string(c_comment.vendor),
 | 
			
		||||
		std::vector<std::string>(c_comment.comment_list, c_comment.comment_list + c_comment.comment_list_length)};
 | 
			
		||||
		String(c_comment.vendor),
 | 
			
		||||
		Vector<String>(c_comment.comment_list, c_comment.comment_list + c_comment.comment_list_length)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::size_t Ogg::sample_rate() const
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,19 +35,19 @@ public:
 | 
			
		|||
 | 
			
		||||
struct OggComment
 | 
			
		||||
{
 | 
			
		||||
	std::string vendor;
 | 
			
		||||
	std::vector<std::string> comments;
 | 
			
		||||
	String vendor;
 | 
			
		||||
	Vector<String> comments;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Ogg final
 | 
			
		||||
{
 | 
			
		||||
	std::vector<std::byte> memory_data_;
 | 
			
		||||
	Vector<std::byte> memory_data_;
 | 
			
		||||
	stb_vorbis* instance_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	Ogg() noexcept;
 | 
			
		||||
 | 
			
		||||
	explicit Ogg(std::vector<std::byte> data);
 | 
			
		||||
	explicit Ogg(Vector<std::byte> data);
 | 
			
		||||
	explicit Ogg(tcb::span<std::byte> data);
 | 
			
		||||
 | 
			
		||||
	Ogg(const Ogg&) = delete;
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +77,7 @@ private:
 | 
			
		|||
template <typename I, typename std::enable_if_t<srb2::io::IsInputStreamV<I>, int> = 0>
 | 
			
		||||
inline Ogg load_ogg(I& stream)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<std::byte> data = srb2::io::read_to_vec(stream);
 | 
			
		||||
	srb2::Vector<std::byte> data = srb2::io::read_to_vec(stream);
 | 
			
		||||
	return Ogg {std::move(data)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -126,9 +126,9 @@ void visit_tag(Visitor& visitor, io::SpanStream& stream, const TagHeader& header
 | 
			
		|||
	stream.seek(io::SeekFrom::kStart, dest);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<uint8_t> read_uint8_samples_from_stream(io::SpanStream& stream, std::size_t count)
 | 
			
		||||
Vector<uint8_t> read_uint8_samples_from_stream(io::SpanStream& stream, std::size_t count)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<uint8_t> samples;
 | 
			
		||||
	Vector<uint8_t> samples;
 | 
			
		||||
	samples.reserve(count);
 | 
			
		||||
	for (std::size_t i = 0; i < count; i++)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -137,9 +137,9 @@ std::vector<uint8_t> read_uint8_samples_from_stream(io::SpanStream& stream, std:
 | 
			
		|||
	return samples;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<int16_t> read_int16_samples_from_stream(io::SpanStream& stream, std::size_t count)
 | 
			
		||||
Vector<int16_t> read_int16_samples_from_stream(io::SpanStream& stream, std::size_t count)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<int16_t> samples;
 | 
			
		||||
	Vector<int16_t> samples;
 | 
			
		||||
	samples.reserve(count);
 | 
			
		||||
	for (std::size_t i = 0; i < count; i++)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -177,7 +177,7 @@ Wav::Wav(tcb::span<std::byte> data)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	std::optional<FmtTag> read_fmt;
 | 
			
		||||
	std::variant<std::vector<uint8_t>, std::vector<int16_t>> interleaved_samples;
 | 
			
		||||
	std::variant<Vector<uint8_t>, Vector<int16_t>> interleaved_samples;
 | 
			
		||||
 | 
			
		||||
	while (stream.seek(io::SeekFrom::kCurrent, 0) < riff_end)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -247,7 +247,7 @@ template <typename T>
 | 
			
		|||
std::size_t read_samples(
 | 
			
		||||
	std::size_t channels,
 | 
			
		||||
	std::size_t offset,
 | 
			
		||||
	const std::vector<T>& samples,
 | 
			
		||||
	const Vector<T>& samples,
 | 
			
		||||
	tcb::span<audio::Sample<1>> buffer
 | 
			
		||||
) noexcept
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -281,8 +281,8 @@ std::size_t read_samples(
 | 
			
		|||
std::size_t Wav::get_samples(std::size_t offset, tcb::span<audio::Sample<1>> buffer) const noexcept
 | 
			
		||||
{
 | 
			
		||||
	auto samples_visitor = srb2::Overload {
 | 
			
		||||
		[&](const std::vector<uint8_t>& samples) { return read_samples<uint8_t>(channels(), offset, samples, buffer); },
 | 
			
		||||
		[&](const std::vector<int16_t>& samples)
 | 
			
		||||
		[&](const Vector<uint8_t>& samples) { return read_samples<uint8_t>(channels(), offset, samples, buffer); },
 | 
			
		||||
		[&](const Vector<int16_t>& samples)
 | 
			
		||||
		{ return read_samples<int16_t>(channels(), offset, samples, buffer); }};
 | 
			
		||||
 | 
			
		||||
	return std::visit(samples_visitor, interleaved_samples_);
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +291,7 @@ std::size_t Wav::get_samples(std::size_t offset, tcb::span<audio::Sample<1>> buf
 | 
			
		|||
std::size_t Wav::interleaved_length() const noexcept
 | 
			
		||||
{
 | 
			
		||||
	auto samples_visitor = srb2::Overload {
 | 
			
		||||
		[](const std::vector<uint8_t>& samples) { return samples.size(); },
 | 
			
		||||
		[](const std::vector<int16_t>& samples) { return samples.size(); }};
 | 
			
		||||
		[](const Vector<uint8_t>& samples) { return samples.size(); },
 | 
			
		||||
		[](const Vector<int16_t>& samples) { return samples.size(); }};
 | 
			
		||||
	return std::visit(samples_visitor, interleaved_samples_);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,10 +15,10 @@
 | 
			
		|||
#include <cstdint>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <variant>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <tcb/span.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../io/streams.hpp"
 | 
			
		||||
#include "sample.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +27,7 @@ namespace srb2::audio
 | 
			
		|||
 | 
			
		||||
class Wav final
 | 
			
		||||
{
 | 
			
		||||
	std::variant<std::vector<uint8_t>, std::vector<int16_t>> interleaved_samples_;
 | 
			
		||||
	std::variant<Vector<uint8_t>, Vector<int16_t>> interleaved_samples_;
 | 
			
		||||
	std::size_t channels_ = 1;
 | 
			
		||||
	std::size_t sample_rate_ = 44100;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ public:
 | 
			
		|||
template <typename I, typename std::enable_if_t<srb2::io::IsInputStreamV<I>, int> = 0>
 | 
			
		||||
inline Wav load_wav(I& stream)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<std::byte> data = srb2::io::read_to_vec(stream);
 | 
			
		||||
	Vector<std::byte> data = srb2::io::read_to_vec(stream);
 | 
			
		||||
	return Wav {data};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ Xmp<C>::Xmp() : data_(), instance_(nullptr), module_loaded_(false), looping_(fal
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
template <size_t C>
 | 
			
		||||
Xmp<C>::Xmp(std::vector<std::byte> data)
 | 
			
		||||
Xmp<C>::Xmp(Vector<std::byte> data)
 | 
			
		||||
	: data_(std::move(data)), instance_(nullptr), module_loaded_(false), looping_(false)
 | 
			
		||||
{
 | 
			
		||||
	_init();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,11 +15,11 @@
 | 
			
		|||
#include <cstddef>
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <tcb/span.hpp>
 | 
			
		||||
#include <xmp.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../io/streams.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2::audio
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ public:
 | 
			
		|||
template <size_t C>
 | 
			
		||||
class Xmp final
 | 
			
		||||
{
 | 
			
		||||
	std::vector<std::byte> data_;
 | 
			
		||||
	Vector<std::byte> data_;
 | 
			
		||||
	xmp_context instance_;
 | 
			
		||||
	bool module_loaded_;
 | 
			
		||||
	bool looping_;
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ class Xmp final
 | 
			
		|||
public:
 | 
			
		||||
	Xmp();
 | 
			
		||||
 | 
			
		||||
	explicit Xmp(std::vector<std::byte> data);
 | 
			
		||||
	explicit Xmp(Vector<std::byte> data);
 | 
			
		||||
	explicit Xmp(tcb::span<std::byte> data);
 | 
			
		||||
 | 
			
		||||
	Xmp(const Xmp<C>&) = delete;
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +74,7 @@ extern template class Xmp<2>;
 | 
			
		|||
template <size_t C, typename I, typename std::enable_if_t<srb2::io::IsInputStreamV<I>, int> = 0>
 | 
			
		||||
inline Xmp<C> load_xmp(I& stream)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<std::byte> data = srb2::io::read_to_vec(stream);
 | 
			
		||||
	Vector<std::byte> data = srb2::io::read_to_vec(stream);
 | 
			
		||||
	return Xmp<C> {std::move(data)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,8 @@
 | 
			
		|||
 | 
			
		||||
#include <cmath>
 | 
			
		||||
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
 | 
			
		||||
using namespace srb2;
 | 
			
		||||
using namespace srb2::audio;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,8 @@
 | 
			
		|||
#include "source.hpp"
 | 
			
		||||
#include "xmp.hpp"
 | 
			
		||||
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2::audio
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +23,7 @@ template <size_t C>
 | 
			
		|||
class XmpPlayer final : public Source<C>
 | 
			
		||||
{
 | 
			
		||||
	Xmp<C> xmp_;
 | 
			
		||||
	std::vector<std::array<int16_t, C>> buf_;
 | 
			
		||||
	srb2::Vector<std::array<int16_t, C>> buf_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	XmpPlayer(Xmp<C>&& xmp);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,17 @@
 | 
			
		|||
target_sources(SRB2SDL2 PRIVATE
 | 
			
		||||
	hash_map.hpp
 | 
			
		||||
	hash_set.cpp
 | 
			
		||||
	hash_set.hpp
 | 
			
		||||
	json.cpp
 | 
			
		||||
	json.hpp
 | 
			
		||||
	memory.cpp
 | 
			
		||||
	memory.h
 | 
			
		||||
	spmc_queue.hpp
 | 
			
		||||
	static_vec.hpp
 | 
			
		||||
	string.cpp
 | 
			
		||||
	string.h
 | 
			
		||||
	thread_pool.cpp
 | 
			
		||||
	thread_pool.h
 | 
			
		||||
	vector.cpp
 | 
			
		||||
	vector.hpp
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										678
									
								
								src/core/hash_map.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										678
									
								
								src/core/hash_map.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,678 @@
 | 
			
		|||
// DR. ROBOTNIK'S RING RACERS
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// Copyright (C) 2025 by Ronald "Eidolon" Kinard
 | 
			
		||||
// Copyright (C) 2025 by Kart Krew
 | 
			
		||||
//
 | 
			
		||||
// This program is free software distributed under the
 | 
			
		||||
// terms of the GNU General Public License, version 2.
 | 
			
		||||
// See the 'LICENSE' file for more details.
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#ifndef SRB2_CORE_HASH_MAP_HPP
 | 
			
		||||
#define SRB2_CORE_HASH_MAP_HPP
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <initializer_list>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "../cxxutil.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
template <typename V>
 | 
			
		||||
class HashSet;
 | 
			
		||||
 | 
			
		||||
template <typename K, typename V>
 | 
			
		||||
class HashMap
 | 
			
		||||
{
 | 
			
		||||
	struct Elem;
 | 
			
		||||
 | 
			
		||||
	using Hasher = std::hash<K>;
 | 
			
		||||
	using KeyEqual = std::equal_to<K>;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	using Entry = std::pair<K, V>;
 | 
			
		||||
 | 
			
		||||
	class ConstIter;
 | 
			
		||||
 | 
			
		||||
	class Iter
 | 
			
		||||
	{
 | 
			
		||||
		const HashMap* self_;
 | 
			
		||||
		uint32_t bucket_;
 | 
			
		||||
		Elem* cur_;
 | 
			
		||||
 | 
			
		||||
		Iter(const HashMap* self, uint32_t bucket, Elem* cur)
 | 
			
		||||
			: self_(self)
 | 
			
		||||
			, bucket_(bucket)
 | 
			
		||||
			, cur_(cur)
 | 
			
		||||
		{}
 | 
			
		||||
 | 
			
		||||
		Iter(const ConstIter& r)
 | 
			
		||||
			: self_(r.iter_.self_)
 | 
			
		||||
			, bucket_(r.iter_.bucket_)
 | 
			
		||||
			, cur_(r.iter_.cur_)
 | 
			
		||||
		{}
 | 
			
		||||
 | 
			
		||||
		friend class HashMap;
 | 
			
		||||
		friend class HashSet<K>;
 | 
			
		||||
		friend class ConstIter;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		Iter() : Iter(nullptr, 0, nullptr) {}
 | 
			
		||||
		Iter(const Iter&) = default;
 | 
			
		||||
		Iter(Iter&&) noexcept = default;
 | 
			
		||||
		~Iter() = default;
 | 
			
		||||
		Iter& operator=(const Iter&) = default;
 | 
			
		||||
		Iter& operator=(Iter&&) noexcept = default;
 | 
			
		||||
 | 
			
		||||
		Entry& operator*() const noexcept { return cur_->entry; }
 | 
			
		||||
		Entry* operator->() const noexcept { return &cur_->entry; }
 | 
			
		||||
		bool operator==(const Iter& r) const noexcept
 | 
			
		||||
		{
 | 
			
		||||
			return self_ == r.self_ && bucket_ == r.bucket_ && cur_ == r.cur_;
 | 
			
		||||
		}
 | 
			
		||||
		bool operator!=(const Iter& r) const noexcept { return !(*this == r); }
 | 
			
		||||
 | 
			
		||||
		Iter& operator++()
 | 
			
		||||
		{
 | 
			
		||||
			if (cur_ == nullptr)
 | 
			
		||||
			{
 | 
			
		||||
				return *this;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (cur_->next == nullptr)
 | 
			
		||||
			{
 | 
			
		||||
				do
 | 
			
		||||
				{
 | 
			
		||||
					if (bucket_ < self_->buckets_ - 1)
 | 
			
		||||
					{
 | 
			
		||||
						bucket_++;
 | 
			
		||||
						cur_ = self_->heads_[bucket_];
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
					{
 | 
			
		||||
						self_ = nullptr;
 | 
			
		||||
						bucket_ = 0;
 | 
			
		||||
						cur_ = nullptr;
 | 
			
		||||
						return *this;
 | 
			
		||||
					}
 | 
			
		||||
				} while (cur_ == nullptr);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				cur_ = cur_->next;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return *this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Iter operator++(int)
 | 
			
		||||
		{
 | 
			
		||||
			Iter itr = *this;
 | 
			
		||||
			++(*this);
 | 
			
		||||
			return itr;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class ConstIter
 | 
			
		||||
	{
 | 
			
		||||
		mutable Iter iter_;
 | 
			
		||||
 | 
			
		||||
		friend class HashMap;
 | 
			
		||||
	public:
 | 
			
		||||
		ConstIter() : iter_() {}
 | 
			
		||||
		ConstIter(const ConstIter&) = default;
 | 
			
		||||
		ConstIter(ConstIter&&) noexcept = default;
 | 
			
		||||
		~ConstIter() = default;
 | 
			
		||||
		ConstIter& operator=(const ConstIter&) = default;
 | 
			
		||||
		ConstIter& operator=(ConstIter&&) noexcept = default;
 | 
			
		||||
 | 
			
		||||
		ConstIter(const Iter& iter) : iter_(iter) {}
 | 
			
		||||
 | 
			
		||||
		const Entry& operator*() const noexcept { return iter_.cur_->entry; }
 | 
			
		||||
		const Entry* operator->() const noexcept { return &iter_.cur_->entry; }
 | 
			
		||||
		bool operator==(const ConstIter& r) const noexcept { return iter_ == r.iter_; }
 | 
			
		||||
		bool operator!=(const ConstIter& r) const noexcept { return !(*this == r); }
 | 
			
		||||
		ConstIter& operator++() noexcept
 | 
			
		||||
		{
 | 
			
		||||
			++iter_;
 | 
			
		||||
			return *this;
 | 
			
		||||
		}
 | 
			
		||||
		ConstIter operator++(int) noexcept
 | 
			
		||||
		{
 | 
			
		||||
			ConstIter itr = *this;
 | 
			
		||||
			++*this;
 | 
			
		||||
			return itr;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// iter traits
 | 
			
		||||
	using key_type = K;
 | 
			
		||||
	using mapped_type = V;
 | 
			
		||||
	using value_type = Entry;
 | 
			
		||||
	using size_type = uint32_t;
 | 
			
		||||
	using difference_type = int64_t;
 | 
			
		||||
	using hasher = Hasher;
 | 
			
		||||
	using key_equal = KeyEqual;
 | 
			
		||||
	using reference = Entry&;
 | 
			
		||||
	using const_reference = const Entry&;
 | 
			
		||||
	using pointer = Entry*;
 | 
			
		||||
	using const_pointer = const Entry*;
 | 
			
		||||
	using iterator = Iter;
 | 
			
		||||
	using const_iterator = ConstIter;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	struct Elem
 | 
			
		||||
	{
 | 
			
		||||
		Elem* prev;
 | 
			
		||||
		Elem* next;
 | 
			
		||||
		Entry entry;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Elem** heads_ = nullptr;
 | 
			
		||||
	size_t size_ = 0;
 | 
			
		||||
	uint32_t buckets_;
 | 
			
		||||
	Hasher hasher_;
 | 
			
		||||
	KeyEqual key_equal_;
 | 
			
		||||
 | 
			
		||||
	constexpr static uint32_t kDefaultBuckets = 16;
 | 
			
		||||
 | 
			
		||||
	void init_buckets()
 | 
			
		||||
	{
 | 
			
		||||
		if (buckets_ == 0)
 | 
			
		||||
		{
 | 
			
		||||
			buckets_ = kDefaultBuckets;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::allocator<Elem*> allocator;
 | 
			
		||||
		heads_ = allocator.allocate(buckets_);
 | 
			
		||||
		for (uint32_t i = 0; i < buckets_; i++)
 | 
			
		||||
		{
 | 
			
		||||
			heads_[i] = nullptr;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void init_if_needed()
 | 
			
		||||
	{
 | 
			
		||||
		if (heads_ == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			init_buckets();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void rehash_if_needed(size_t target_size)
 | 
			
		||||
	{
 | 
			
		||||
		if (target_size >= buckets_)
 | 
			
		||||
		{
 | 
			
		||||
			rehash(std::max<size_t>(target_size, buckets_ * 2));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	friend class Iter;
 | 
			
		||||
	friend class ConstIter;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	HashMap() : heads_(nullptr), size_(0), buckets_(0), hasher_(), key_equal_()
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap(uint32_t buckets)
 | 
			
		||||
		: heads_(nullptr)
 | 
			
		||||
		, size_(0)
 | 
			
		||||
		, buckets_(buckets)
 | 
			
		||||
		, hasher_()
 | 
			
		||||
		, key_equal_()
 | 
			
		||||
	{
 | 
			
		||||
		init_buckets();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap(const HashMap& r)
 | 
			
		||||
	{
 | 
			
		||||
		*this = r;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap(HashMap&& r) noexcept
 | 
			
		||||
	{
 | 
			
		||||
		*this = std::move(r);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	~HashMap()
 | 
			
		||||
	{
 | 
			
		||||
		clear();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap(std::initializer_list<Entry> list) : HashMap(list.size())
 | 
			
		||||
	{
 | 
			
		||||
		for (auto v : list)
 | 
			
		||||
		{
 | 
			
		||||
			insert(v);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap& operator=(const HashMap& r)
 | 
			
		||||
	{
 | 
			
		||||
		clear();
 | 
			
		||||
		buckets_ = r.buckets_;
 | 
			
		||||
		if (buckets_ == 0)
 | 
			
		||||
		{
 | 
			
		||||
			return *this;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		init_buckets();
 | 
			
		||||
		for (auto itr = r.begin(); itr != r.end(); itr++)
 | 
			
		||||
		{
 | 
			
		||||
			insert({itr->first, itr->second});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return *this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	HashMap& operator=(HashMap&& r) noexcept
 | 
			
		||||
	{
 | 
			
		||||
		std::swap(buckets_, r.buckets_);
 | 
			
		||||
		std::swap(size_, r.size_);
 | 
			
		||||
		std::swap(heads_, r.heads_);
 | 
			
		||||
		std::swap(hasher_, r.hasher_);
 | 
			
		||||
		std::swap(key_equal_, r.key_equal_);
 | 
			
		||||
		return *this;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	constexpr bool empty() const noexcept { return size_ == 0; }
 | 
			
		||||
	constexpr size_t size() const noexcept { return size_; }
 | 
			
		||||
	constexpr uint32_t buckets() const noexcept { return buckets_; }
 | 
			
		||||
 | 
			
		||||
	Iter begin() noexcept
 | 
			
		||||
	{
 | 
			
		||||
		if (size_ == 0)
 | 
			
		||||
		{
 | 
			
		||||
			return end();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Iter ret = end();
 | 
			
		||||
		for (uint32_t i = 0; i < buckets_; i++)
 | 
			
		||||
		{
 | 
			
		||||
			Elem* ptr = heads_[i];
 | 
			
		||||
			if (ptr != nullptr)
 | 
			
		||||
			{
 | 
			
		||||
				ret = Iter { this, i, ptr };
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ConstIter cbegin() const noexcept
 | 
			
		||||
	{
 | 
			
		||||
		ConstIter ret = end();
 | 
			
		||||
		for (uint32_t i = 0; i < buckets_; i++)
 | 
			
		||||
		{
 | 
			
		||||
			Elem* ptr = heads_[i];
 | 
			
		||||
			if (ptr != nullptr)
 | 
			
		||||
			{
 | 
			
		||||
				ret = ConstIter { Iter { this, i, ptr } };
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ConstIter begin() const noexcept { return cbegin(); }
 | 
			
		||||
 | 
			
		||||
	constexpr Iter end() noexcept { return Iter(); }
 | 
			
		||||
 | 
			
		||||
	constexpr ConstIter cend() const noexcept { return ConstIter(); }
 | 
			
		||||
 | 
			
		||||
	constexpr ConstIter end() const noexcept { return cend(); }
 | 
			
		||||
 | 
			
		||||
	void clear()
 | 
			
		||||
	{
 | 
			
		||||
		if (heads_)
 | 
			
		||||
		{
 | 
			
		||||
			std::allocator<Elem> elem_allocator;
 | 
			
		||||
			for (uint32_t i = 0; i < buckets_; i++)
 | 
			
		||||
			{
 | 
			
		||||
				auto itr = heads_[i];
 | 
			
		||||
				while (itr != nullptr)
 | 
			
		||||
				{
 | 
			
		||||
					auto nextitr = itr->next;
 | 
			
		||||
					itr->~Elem();
 | 
			
		||||
					elem_allocator.deallocate(itr, 1);
 | 
			
		||||
					itr = nextitr;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			std::allocator<Elem*> buckets_allocator;
 | 
			
		||||
			buckets_allocator.deallocate(heads_, buckets_);
 | 
			
		||||
		}
 | 
			
		||||
		heads_ = nullptr;
 | 
			
		||||
		size_ = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Iter find(const K& key)
 | 
			
		||||
	{
 | 
			
		||||
		if (buckets_ == 0 || heads_ == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			return end();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		uint32_t bucket = (hasher_)(key) % buckets_;
 | 
			
		||||
		for (Elem* p = heads_[bucket]; p != nullptr; p = p->next)
 | 
			
		||||
		{
 | 
			
		||||
			if ((key_equal_)(p->entry.first, key))
 | 
			
		||||
			{
 | 
			
		||||
				return Iter { this, bucket, p };
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
		return end();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ConstIter find(const K& key) const
 | 
			
		||||
	{
 | 
			
		||||
		Iter iter = const_cast<HashMap*>(this)->find(key);
 | 
			
		||||
		return ConstIter { iter };
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::pair<Iter, bool> insert(const Entry& value)
 | 
			
		||||
	{
 | 
			
		||||
		Entry copy = value;
 | 
			
		||||
		return insert(std::move(copy));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void rehash(uint32_t count)
 | 
			
		||||
	{
 | 
			
		||||
		count = size_ > count ? size_ : count;
 | 
			
		||||
		HashMap rehashed { count };
 | 
			
		||||
		for (Iter itr = begin(); itr != end();)
 | 
			
		||||
		{
 | 
			
		||||
			Iter itrcopy = itr;
 | 
			
		||||
			++itr;
 | 
			
		||||
 | 
			
		||||
			uint32_t oldbucket = itrcopy.bucket_;
 | 
			
		||||
			if (heads_[oldbucket] == itrcopy.cur_)
 | 
			
		||||
			{
 | 
			
		||||
				heads_[oldbucket] = nullptr;
 | 
			
		||||
			}
 | 
			
		||||
			itrcopy.self_ = &rehashed;
 | 
			
		||||
			itrcopy.bucket_ = (rehashed.hasher_)(itrcopy.cur_->entry.first) % count;
 | 
			
		||||
			Elem* p = rehashed.heads_[itrcopy.bucket_];
 | 
			
		||||
			if (p == nullptr)
 | 
			
		||||
			{
 | 
			
		||||
				p = rehashed.heads_[itrcopy.bucket_] = itrcopy.cur_;
 | 
			
		||||
				size_ -= 1;
 | 
			
		||||
				rehashed.size_ += 1;
 | 
			
		||||
 | 
			
		||||
				if (p->next)
 | 
			
		||||
				{
 | 
			
		||||
					p->next->prev = nullptr;
 | 
			
		||||
				}
 | 
			
		||||
				if (p->prev)
 | 
			
		||||
				{
 | 
			
		||||
					p->prev->next = nullptr;
 | 
			
		||||
				}
 | 
			
		||||
				p->next = nullptr;
 | 
			
		||||
				p->prev = nullptr;
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				for (; p != nullptr; p = p->next)
 | 
			
		||||
				{
 | 
			
		||||
					if (p->next != nullptr)
 | 
			
		||||
					{
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
					p->next = itrcopy.cur_;
 | 
			
		||||
					size_ -= 1;
 | 
			
		||||
					rehashed.size_ += 1;
 | 
			
		||||
					p->next->prev = p;
 | 
			
		||||
					p->next->next = nullptr;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (heads_)
 | 
			
		||||
		{
 | 
			
		||||
			std::allocator<Elem*> buckets_allocator;
 | 
			
		||||
			buckets_allocator.deallocate(heads_, buckets_);
 | 
			
		||||
			heads_ = nullptr;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		*this = std::move(rehashed);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::pair<Iter, bool> insert(Entry&& value)
 | 
			
		||||
	{
 | 
			
		||||
		std::pair<Iter, bool> ret { end(), false };
 | 
			
		||||
		std::allocator<Elem> allocator;
 | 
			
		||||
 | 
			
		||||
		init_if_needed();
 | 
			
		||||
 | 
			
		||||
		rehash_if_needed(size_ + 1);
 | 
			
		||||
 | 
			
		||||
		uint32_t bucket = (hasher_)(value.first) % buckets_;
 | 
			
		||||
		Elem* p = heads_[bucket];
 | 
			
		||||
		if (p == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			// Make a new slot
 | 
			
		||||
			ret.second = true;
 | 
			
		||||
			Elem* newslot = allocator.allocate(1);
 | 
			
		||||
			newslot->prev = nullptr;
 | 
			
		||||
			newslot->next = nullptr;
 | 
			
		||||
			heads_[bucket] = newslot;
 | 
			
		||||
			new (&newslot->entry) Entry { std::move(value) };
 | 
			
		||||
			size_++;
 | 
			
		||||
			ret.first = Iter { this, bucket, newslot };
 | 
			
		||||
			return ret;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for(; p != nullptr;)
 | 
			
		||||
		{
 | 
			
		||||
			if ((key_equal_)(p->entry.first, value.first))
 | 
			
		||||
			{
 | 
			
		||||
				ret.second = false;
 | 
			
		||||
				ret.first = Iter { this, bucket, p };
 | 
			
		||||
				return ret;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (p->next == nullptr)
 | 
			
		||||
			{
 | 
			
		||||
				// Make a new slot
 | 
			
		||||
				ret.second = true;
 | 
			
		||||
				Elem* newslot = allocator.allocate(1);
 | 
			
		||||
				newslot->prev = p;
 | 
			
		||||
				newslot->next = nullptr;
 | 
			
		||||
				p->next = newslot;
 | 
			
		||||
				new (&newslot->entry) Entry { std::move(value) };
 | 
			
		||||
				size_++;
 | 
			
		||||
				ret.first = Iter { this, bucket, newslot };
 | 
			
		||||
				return ret;
 | 
			
		||||
			}
 | 
			
		||||
			p = p->next;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename M>
 | 
			
		||||
	std::pair<Iter, bool> insert_or_assign(const K& key, M&& value)
 | 
			
		||||
	{
 | 
			
		||||
		K kcopy = key;
 | 
			
		||||
		return insert_or_assign(std::move(kcopy), std::forward<M>(value));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename M>
 | 
			
		||||
	std::pair<Iter, bool> insert_or_assign(K&& key, M&& value)
 | 
			
		||||
	{
 | 
			
		||||
		std::pair<Iter, bool> ret { find(key), false };
 | 
			
		||||
		if (ret.first != end())
 | 
			
		||||
		{
 | 
			
		||||
			ret.first->second = std::forward<M>(value);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			ret = insert({ std::move(key), std::forward<M>(value) });
 | 
			
		||||
		}
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename ...Args>
 | 
			
		||||
	std::pair<Iter, bool> emplace(Args&&... args)
 | 
			
		||||
	{
 | 
			
		||||
		Entry entry { std::forward<Args>(args)... };
 | 
			
		||||
		return insert(std::move(entry));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename ...Args>
 | 
			
		||||
	std::pair<Iter, bool> try_emplace(const K& key, Args&&... args)
 | 
			
		||||
	{
 | 
			
		||||
		Iter itr = find(key);
 | 
			
		||||
		if (itr != end())
 | 
			
		||||
		{
 | 
			
		||||
			return { itr, false };
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return insert({ key, V(std::forward<Args>(args)...) });
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename ...Args>
 | 
			
		||||
	std::pair<Iter, bool> try_emplace(K&& key, Args... args)
 | 
			
		||||
	{
 | 
			
		||||
		std::pair<Iter, bool> ret { end(), false };
 | 
			
		||||
		return insert({ std::move(key), V(std::forward<Args>(args)...)});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	V& operator[](const K& key)
 | 
			
		||||
	{
 | 
			
		||||
		K copy { key };
 | 
			
		||||
		return this->operator[](std::move(copy));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	V& operator[](K&& key)
 | 
			
		||||
	{
 | 
			
		||||
		std::allocator<Elem> allocator;
 | 
			
		||||
		init_if_needed();
 | 
			
		||||
 | 
			
		||||
		rehash_if_needed(size_ + 1);
 | 
			
		||||
 | 
			
		||||
		uint32_t bucket = (hasher_)(key) % buckets_;
 | 
			
		||||
		Elem* p = heads_[bucket];
 | 
			
		||||
		if (p == nullptr)
 | 
			
		||||
		{
 | 
			
		||||
			// Make a new slot
 | 
			
		||||
			Elem* newslot = allocator.allocate(1);
 | 
			
		||||
			newslot->prev = nullptr;
 | 
			
		||||
			newslot->next = nullptr;
 | 
			
		||||
			heads_[bucket] = newslot;
 | 
			
		||||
			new (&newslot->entry) Entry { std::move(key), {} };
 | 
			
		||||
			size_++;
 | 
			
		||||
			return newslot->entry.second;
 | 
			
		||||
		}
 | 
			
		||||
		for (; p != nullptr;)
 | 
			
		||||
		{
 | 
			
		||||
			if ((key_equal_)(p->entry.first, key))
 | 
			
		||||
			{
 | 
			
		||||
				return p->entry.second;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (p->next == nullptr)
 | 
			
		||||
			{
 | 
			
		||||
				// Make a new slot
 | 
			
		||||
				Elem* newslot = allocator.allocate(1);
 | 
			
		||||
				newslot->prev = p;
 | 
			
		||||
				newslot->next = nullptr;
 | 
			
		||||
				p->next = newslot;
 | 
			
		||||
				new (&newslot->entry) Entry { std::move(key), {} };
 | 
			
		||||
				size_++;
 | 
			
		||||
				return newslot->entry.second;
 | 
			
		||||
			}
 | 
			
		||||
			p = p->next;
 | 
			
		||||
		}
 | 
			
		||||
		return end()->second;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	V& at(const K& key)
 | 
			
		||||
	{
 | 
			
		||||
		auto itr = find(key);
 | 
			
		||||
		if (itr == end())
 | 
			
		||||
		{
 | 
			
		||||
			throw std::out_of_range("key not found");
 | 
			
		||||
		}
 | 
			
		||||
		return itr->second;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const V& at(const K& key) const
 | 
			
		||||
	{
 | 
			
		||||
		auto itr = find(key);
 | 
			
		||||
		if (itr == cend())
 | 
			
		||||
		{
 | 
			
		||||
			throw std::out_of_range("key not found");
 | 
			
		||||
		}
 | 
			
		||||
		return itr->second;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Iter erase(Iter pos)
 | 
			
		||||
	{
 | 
			
		||||
		Iter ret = pos;
 | 
			
		||||
		if (pos == end())
 | 
			
		||||
		{
 | 
			
		||||
			return end();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::allocator<Elem> allocator;
 | 
			
		||||
		uint32_t bucket = (hasher_)(pos.cur_->entry.first) % buckets_;
 | 
			
		||||
		Elem* p = heads_[bucket];
 | 
			
		||||
		for (; p != nullptr;)
 | 
			
		||||
		{
 | 
			
		||||
			if ((key_equal_)(p->entry.first, pos.cur_->entry.first))
 | 
			
		||||
			{
 | 
			
		||||
				// found it; remove, return next iter
 | 
			
		||||
				++ret;
 | 
			
		||||
				if (p->next != nullptr)
 | 
			
		||||
				{
 | 
			
		||||
					p->next->prev = p->prev;
 | 
			
		||||
				}
 | 
			
		||||
				if (p->prev != nullptr)
 | 
			
		||||
				{
 | 
			
		||||
					p->prev->next = p->next;
 | 
			
		||||
				}
 | 
			
		||||
				if (p == heads_[bucket])
 | 
			
		||||
				{
 | 
			
		||||
					heads_[bucket] = p->next;
 | 
			
		||||
				}
 | 
			
		||||
				p->~Elem();
 | 
			
		||||
				allocator.deallocate(p, 1);
 | 
			
		||||
				size_ -= 1;
 | 
			
		||||
				return ret;
 | 
			
		||||
			}
 | 
			
		||||
			p = p->next;
 | 
			
		||||
		}
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Iter erase(ConstIter pos)
 | 
			
		||||
	{
 | 
			
		||||
		return erase(pos.iter_);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uint32_t erase(const K& key)
 | 
			
		||||
	{
 | 
			
		||||
		Iter pos = find(key);
 | 
			
		||||
		if (pos != end())
 | 
			
		||||
		{
 | 
			
		||||
			erase(pos);
 | 
			
		||||
			return 1;
 | 
			
		||||
		}
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace srb2
 | 
			
		||||
 | 
			
		||||
#endif // SRB2_CORE_HASH_MAP_HPP
 | 
			
		||||
							
								
								
									
										32
									
								
								src/core/hash_set.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/core/hash_set.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
// DR. ROBOTNIK'S RING RACERS
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// Copyright (C) 2025 by Ronald "Eidolon" Kinard
 | 
			
		||||
// Copyright (C) 2025 by Kart Krew
 | 
			
		||||
//
 | 
			
		||||
// This program is free software distributed under the
 | 
			
		||||
// terms of the GNU General Public License, version 2.
 | 
			
		||||
// See the 'LICENSE' file for more details.
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#include "hash_set.hpp"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "string.h"
 | 
			
		||||
 | 
			
		||||
namespace srb2
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
template class HashSet<bool>;
 | 
			
		||||
template class HashSet<uint8_t>;
 | 
			
		||||
template class HashSet<uint16_t>;
 | 
			
		||||
template class HashSet<uint32_t>;
 | 
			
		||||
template class HashSet<uint64_t>;
 | 
			
		||||
template class HashSet<int8_t>;
 | 
			
		||||
template class HashSet<int16_t>;
 | 
			
		||||
template class HashSet<int32_t>;
 | 
			
		||||
template class HashSet<int64_t>;
 | 
			
		||||
template class HashSet<String>;
 | 
			
		||||
template class HashSet<std::string>;
 | 
			
		||||
 | 
			
		||||
} // namespace srb2
 | 
			
		||||
							
								
								
									
										214
									
								
								src/core/hash_set.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/core/hash_set.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,214 @@
 | 
			
		|||
// DR. ROBOTNIK'S RING RACERS
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// Copyright (C) 2025 by Ronald "Eidolon" Kinard
 | 
			
		||||
// Copyright (C) 2025 by Kart Krew
 | 
			
		||||
//
 | 
			
		||||
// This program is free software distributed under the
 | 
			
		||||
// terms of the GNU General Public License, version 2.
 | 
			
		||||
// See the 'LICENSE' file for more details.
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#ifndef SRB2_CORE_HASH_SET_HPP
 | 
			
		||||
#define SRB2_CORE_HASH_SET_HPP
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "hash_map.hpp"
 | 
			
		||||
#include "string.h"
 | 
			
		||||
 | 
			
		||||
namespace srb2
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
template <typename V>
 | 
			
		||||
class HashSet
 | 
			
		||||
{
 | 
			
		||||
	using Inner = HashMap<V, uint8_t>;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	class Iter
 | 
			
		||||
	{
 | 
			
		||||
		typename Inner::Iter iter_;
 | 
			
		||||
 | 
			
		||||
		Iter(typename Inner::Iter iter) : iter_(iter) {}
 | 
			
		||||
 | 
			
		||||
		friend class HashSet;
 | 
			
		||||
		friend class ConstIter;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		Iter() = default;
 | 
			
		||||
		Iter(const Iter&) = default;
 | 
			
		||||
		Iter(Iter&&) noexcept = default;
 | 
			
		||||
		~Iter() = default;
 | 
			
		||||
 | 
			
		||||
		Iter& operator=(const Iter&) = default;
 | 
			
		||||
		Iter& operator=(Iter&&) noexcept = default;
 | 
			
		||||
 | 
			
		||||
		bool operator==(const Iter& r) const noexcept { return iter_ == r.iter_; }
 | 
			
		||||
		bool operator!=(const Iter& r) const noexcept { return !(*this == r); }
 | 
			
		||||
 | 
			
		||||
		V& operator*() const noexcept { return iter_->first; }
 | 
			
		||||
		V* operator->() const noexcept { return &iter_->first; }
 | 
			
		||||
		Iter& operator++() noexcept
 | 
			
		||||
		{
 | 
			
		||||
			++iter_;
 | 
			
		||||
			return *this;
 | 
			
		||||
		}
 | 
			
		||||
		Iter operator++(int) noexcept
 | 
			
		||||
		{
 | 
			
		||||
			Iter self = *this;
 | 
			
		||||
			++*this;
 | 
			
		||||
			return self;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class ConstIter
 | 
			
		||||
	{
 | 
			
		||||
		typename Inner::ConstIter iter_;
 | 
			
		||||
 | 
			
		||||
		friend class HashSet;
 | 
			
		||||
		friend class Iter;
 | 
			
		||||
 | 
			
		||||
	public:
 | 
			
		||||
		ConstIter() = default;
 | 
			
		||||
		ConstIter(const ConstIter&) = default;
 | 
			
		||||
		ConstIter(ConstIter&&) noexcept = default;
 | 
			
		||||
		~ConstIter() = default;
 | 
			
		||||
 | 
			
		||||
		ConstIter(const Iter& iter) : iter_(iter.iter_) {}
 | 
			
		||||
		ConstIter(const typename Inner::ConstIter& iter) : iter_(iter) {}
 | 
			
		||||
 | 
			
		||||
		ConstIter& operator=(const ConstIter&) = default;
 | 
			
		||||
		ConstIter& operator=(ConstIter&&) noexcept = default;
 | 
			
		||||
 | 
			
		||||
		bool operator==(const ConstIter& r) const noexcept { return iter_ == r.iter_; }
 | 
			
		||||
		bool operator!=(const ConstIter& r) const noexcept { return !(*this == r); }
 | 
			
		||||
 | 
			
		||||
		const V& operator*() const noexcept { return iter_->first; }
 | 
			
		||||
		const V* operator->() const noexcept { return &iter_->first; }
 | 
			
		||||
		ConstIter& operator++() noexcept
 | 
			
		||||
		{
 | 
			
		||||
			++iter_;
 | 
			
		||||
			return *this;
 | 
			
		||||
		}
 | 
			
		||||
		ConstIter operator++(int) noexcept
 | 
			
		||||
		{
 | 
			
		||||
			ConstIter self = *this;
 | 
			
		||||
			++*this;
 | 
			
		||||
			return self;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Inner map_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	using key_type = V;
 | 
			
		||||
	using value_type = V;
 | 
			
		||||
	using size_type = typename Inner::size_type;
 | 
			
		||||
	using difference_type = typename Inner::difference_type;
 | 
			
		||||
	using hasher = typename Inner::hasher;
 | 
			
		||||
	using key_equal = typename Inner::key_equal;
 | 
			
		||||
	using reference = V&;
 | 
			
		||||
	using const_reference = const V&;
 | 
			
		||||
	using pointer = V*;
 | 
			
		||||
	using const_pointer = const V*;
 | 
			
		||||
	using iterator = Iter;
 | 
			
		||||
	using const_iterator = ConstIter;
 | 
			
		||||
 | 
			
		||||
	HashSet() = default;
 | 
			
		||||
	HashSet(const HashSet&) = default;
 | 
			
		||||
	HashSet(HashSet&&) noexcept = default;
 | 
			
		||||
	~HashSet() = default;
 | 
			
		||||
 | 
			
		||||
	HashSet& operator=(const HashSet&) = default;
 | 
			
		||||
	HashSet& operator=(HashSet&&) noexcept = default;
 | 
			
		||||
 | 
			
		||||
	Iter begin() { return map_.begin(); }
 | 
			
		||||
	Iter end() { return map_.end(); }
 | 
			
		||||
	ConstIter cbegin() const { return map_.cbegin(); }
 | 
			
		||||
	ConstIter cend() const { return map_.cend(); }
 | 
			
		||||
	ConstIter begin() const { return cbegin(); }
 | 
			
		||||
	ConstIter end() const { return cend(); }
 | 
			
		||||
 | 
			
		||||
	bool empty() const noexcept { return map_.empty(); }
 | 
			
		||||
	size_t size() const noexcept { return map_.size(); }
 | 
			
		||||
 | 
			
		||||
	void clear() { map_.clear(); }
 | 
			
		||||
 | 
			
		||||
	std::pair<Iter, bool> insert(const V& value)
 | 
			
		||||
	{
 | 
			
		||||
		V copy { value };
 | 
			
		||||
		return insert(std::move(copy));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::pair<Iter, bool> insert(V&& value)
 | 
			
		||||
	{
 | 
			
		||||
		return emplace(std::move(value));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename ...Args>
 | 
			
		||||
	std::pair<Iter, bool> emplace(Args&&... args)
 | 
			
		||||
	{
 | 
			
		||||
		std::pair<Iter, bool> res;
 | 
			
		||||
		auto [map_iter, map_inserted] = map_.emplace(std::forward<Args&&>(args)..., 0);
 | 
			
		||||
		res.first = map_iter;
 | 
			
		||||
		res.second = map_inserted;
 | 
			
		||||
		return res;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Iter erase(Iter pos)
 | 
			
		||||
	{
 | 
			
		||||
		auto map_ret = map_.erase(pos.iter_);
 | 
			
		||||
		return map_ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Iter erase(ConstIter pos)
 | 
			
		||||
	{
 | 
			
		||||
		auto map_ret = map_.erase(pos.iter_);
 | 
			
		||||
		return map_ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Iter erase(ConstIter first, ConstIter last)
 | 
			
		||||
	{
 | 
			
		||||
		ConstIter iter;
 | 
			
		||||
		for (iter = first; iter != last;)
 | 
			
		||||
		{
 | 
			
		||||
			iter = erase(iter);
 | 
			
		||||
		}
 | 
			
		||||
		return typename Inner::Iter(iter.iter_);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Iter find(const V& value)
 | 
			
		||||
	{
 | 
			
		||||
		auto map_ret = map_.find(value);
 | 
			
		||||
		return map_ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ConstIter find(const V& value) const
 | 
			
		||||
	{
 | 
			
		||||
		auto map_ret = map_.find(value);
 | 
			
		||||
		return map_ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void rehash(size_t count)
 | 
			
		||||
	{
 | 
			
		||||
		map_.rehash(count);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern template class HashSet<bool>;
 | 
			
		||||
extern template class HashSet<uint8_t>;
 | 
			
		||||
extern template class HashSet<uint16_t>;
 | 
			
		||||
extern template class HashSet<uint32_t>;
 | 
			
		||||
extern template class HashSet<uint64_t>;
 | 
			
		||||
extern template class HashSet<int8_t>;
 | 
			
		||||
extern template class HashSet<int16_t>;
 | 
			
		||||
extern template class HashSet<int32_t>;
 | 
			
		||||
extern template class HashSet<int64_t>;
 | 
			
		||||
extern template class HashSet<String>;
 | 
			
		||||
extern template class HashSet<std::string>;
 | 
			
		||||
 | 
			
		||||
} // namespace srb2
 | 
			
		||||
 | 
			
		||||
#endif // SRB2_CORE_HASH_SET_HPP
 | 
			
		||||
							
								
								
									
										1809
									
								
								src/core/json.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1809
									
								
								src/core/json.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										533
									
								
								src/core/json.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										533
									
								
								src/core/json.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,533 @@
 | 
			
		|||
// DR. ROBOTNIK'S RING RACERS
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// Copyright (C) 2025 by Ronald "Eidolon" Kinard
 | 
			
		||||
// Copyright (C) 2025 by Kart Krew
 | 
			
		||||
//
 | 
			
		||||
// This program is free software distributed under the
 | 
			
		||||
// terms of the GNU General Public License, version 2.
 | 
			
		||||
// See the 'LICENSE' file for more details.
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#ifndef SRB2_CORE_JSON_HPP
 | 
			
		||||
#define SRB2_CORE_JSON_HPP
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <tcb/span.hpp>
 | 
			
		||||
 | 
			
		||||
#include "hash_map.hpp"
 | 
			
		||||
#include "string.h"
 | 
			
		||||
#include "vector.hpp"
 | 
			
		||||
 | 
			
		||||
#include "json_macro.inl"
 | 
			
		||||
 | 
			
		||||
namespace srb2
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class JsonValue;
 | 
			
		||||
 | 
			
		||||
using JsonArray = Vector<JsonValue>;
 | 
			
		||||
using JsonObject = HashMap<String, JsonValue>;
 | 
			
		||||
 | 
			
		||||
class JsonError : public std::runtime_error
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	JsonError(const std::string& what);
 | 
			
		||||
	JsonError(const char* what);
 | 
			
		||||
	JsonError(const JsonError&) noexcept;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class JsonParseError : public JsonError
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	JsonParseError(const std::string& what);
 | 
			
		||||
	JsonParseError(const char* what);
 | 
			
		||||
	JsonParseError(const JsonParseError&) noexcept;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class JsonValue
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	enum class Type : int
 | 
			
		||||
	{
 | 
			
		||||
		kNull,
 | 
			
		||||
		kBoolean,
 | 
			
		||||
		kNumber,
 | 
			
		||||
		kString,
 | 
			
		||||
		kArray,
 | 
			
		||||
		kObject
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	Type type_;
 | 
			
		||||
	union {
 | 
			
		||||
		struct {} dummy;
 | 
			
		||||
		bool boolean_;
 | 
			
		||||
		double number_;
 | 
			
		||||
		String string_;
 | 
			
		||||
		JsonArray array_;
 | 
			
		||||
		JsonObject object_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	static void value_to_ubjson(srb2::Vector<std::byte>& out);
 | 
			
		||||
	static void value_to_ubjson(srb2::Vector<std::byte>& out, bool value);
 | 
			
		||||
	static void value_to_ubjson(srb2::Vector<std::byte>& out, double value);
 | 
			
		||||
	static void value_to_ubjson(srb2::Vector<std::byte>& out, uint64_t value);
 | 
			
		||||
	static void value_to_ubjson(srb2::Vector<std::byte>& out, const String& value, bool include_type);
 | 
			
		||||
	static void value_to_ubjson(srb2::Vector<std::byte>& out, const JsonArray& value);
 | 
			
		||||
	static void value_to_ubjson(srb2::Vector<std::byte>& out, const JsonObject& value);
 | 
			
		||||
 | 
			
		||||
	void do_to_ubjson(srb2::Vector<std::byte>& out) const;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	JsonValue();
 | 
			
		||||
	JsonValue(const JsonValue&);
 | 
			
		||||
	JsonValue(JsonValue&&) noexcept;
 | 
			
		||||
	~JsonValue();
 | 
			
		||||
	JsonValue& operator=(const JsonValue&);
 | 
			
		||||
	JsonValue& operator=(JsonValue&&) noexcept;
 | 
			
		||||
 | 
			
		||||
	JsonValue(const String& value);
 | 
			
		||||
	JsonValue(String&& value);
 | 
			
		||||
	JsonValue(const JsonArray& value);
 | 
			
		||||
	JsonValue(JsonArray&& value);
 | 
			
		||||
	JsonValue(const JsonObject& value);
 | 
			
		||||
	JsonValue(JsonObject&& value);
 | 
			
		||||
	JsonValue(float value);
 | 
			
		||||
	JsonValue(double value);
 | 
			
		||||
	JsonValue(int8_t value);
 | 
			
		||||
	JsonValue(int16_t value);
 | 
			
		||||
	JsonValue(int32_t value);
 | 
			
		||||
	JsonValue(int64_t value);
 | 
			
		||||
	JsonValue(uint8_t value);
 | 
			
		||||
	JsonValue(uint16_t value);
 | 
			
		||||
	JsonValue(uint32_t value);
 | 
			
		||||
	JsonValue(uint64_t value);
 | 
			
		||||
	JsonValue(bool value);
 | 
			
		||||
 | 
			
		||||
	static JsonValue array() { return JsonValue(JsonArray()); }
 | 
			
		||||
	static JsonValue object() { return JsonValue(JsonObject()); }
 | 
			
		||||
 | 
			
		||||
	bool operator==(const JsonValue& rhs) const;
 | 
			
		||||
	bool operator!=(const JsonValue& rhs) const { return !(*this == rhs); }
 | 
			
		||||
 | 
			
		||||
	void to_json(JsonValue& to) const;
 | 
			
		||||
	void from_json(const JsonValue& from);
 | 
			
		||||
	String to_json_string() const;
 | 
			
		||||
	static JsonValue from_json_string(const String& str);
 | 
			
		||||
	srb2::Vector<std::byte> to_ubjson() const;
 | 
			
		||||
	static JsonValue from_ubjson(tcb::span<const std::byte> ubjson);
 | 
			
		||||
	constexpr Type type() const noexcept { return type_; }
 | 
			
		||||
 | 
			
		||||
	template <typename V> V get() const;
 | 
			
		||||
 | 
			
		||||
	constexpr bool is_null() const noexcept { return type_ == Type::kNull; }
 | 
			
		||||
	constexpr bool is_boolean() const noexcept { return type_ == Type::kBoolean; }
 | 
			
		||||
	constexpr bool is_number() const noexcept { return type_ == Type::kNumber; }
 | 
			
		||||
	constexpr bool is_string() const noexcept { return type_ == Type::kString; }
 | 
			
		||||
	constexpr bool is_array() const noexcept { return type_ == Type::kArray; }
 | 
			
		||||
	constexpr bool is_object() const noexcept { return type_ == Type::kObject; }
 | 
			
		||||
	JsonArray& as_array();
 | 
			
		||||
	JsonObject& as_object();
 | 
			
		||||
	const JsonArray& as_array() const;
 | 
			
		||||
	const JsonObject& as_object() const;
 | 
			
		||||
 | 
			
		||||
	JsonValue& at(uint32_t i);
 | 
			
		||||
	const JsonValue& at(uint32_t i) const;
 | 
			
		||||
	JsonValue& at(const char* key);
 | 
			
		||||
	JsonValue& at(const String& key);
 | 
			
		||||
	const JsonValue& at(const char* key) const;
 | 
			
		||||
	const JsonValue& at(const String& key) const;
 | 
			
		||||
 | 
			
		||||
	template <typename V>
 | 
			
		||||
	V value(const char* key, const V& def) const;
 | 
			
		||||
	template <typename V>
 | 
			
		||||
	V value(const String& key, const V& def) const;
 | 
			
		||||
	template <typename V>
 | 
			
		||||
	V value(const char* key, V&& def) const;
 | 
			
		||||
	template <typename V>
 | 
			
		||||
	V value(const String& key, V&& def) const;
 | 
			
		||||
 | 
			
		||||
	bool contains(const char* key) const;
 | 
			
		||||
	bool contains(const String& key) const;
 | 
			
		||||
 | 
			
		||||
	JsonValue& operator[](uint32_t i);
 | 
			
		||||
	JsonValue& operator[](const char* key);
 | 
			
		||||
	JsonValue& operator[](const String& key);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void to_json(JsonValue& to, bool value);
 | 
			
		||||
void to_json(JsonValue& to, int8_t value);
 | 
			
		||||
void to_json(JsonValue& to, int16_t value);
 | 
			
		||||
void to_json(JsonValue& to, int32_t value);
 | 
			
		||||
void to_json(JsonValue& to, int64_t value);
 | 
			
		||||
void to_json(JsonValue& to, uint8_t value);
 | 
			
		||||
void to_json(JsonValue& to, uint16_t value);
 | 
			
		||||
void to_json(JsonValue& to, uint32_t value);
 | 
			
		||||
void to_json(JsonValue& to, uint64_t value);
 | 
			
		||||
void to_json(JsonValue& to, float value);
 | 
			
		||||
void to_json(JsonValue& to, double value);
 | 
			
		||||
void to_json(JsonValue& to, const String& value);
 | 
			
		||||
void to_json(JsonValue& to, const JsonArray& value);
 | 
			
		||||
void to_json(JsonValue& to, const JsonObject& value);
 | 
			
		||||
 | 
			
		||||
void from_json(const JsonValue& to, bool& value);
 | 
			
		||||
void from_json(const JsonValue& to, int8_t& value);
 | 
			
		||||
void from_json(const JsonValue& to, int16_t& value);
 | 
			
		||||
void from_json(const JsonValue& to, int32_t& value);
 | 
			
		||||
void from_json(const JsonValue& to, int64_t& value);
 | 
			
		||||
void from_json(const JsonValue& to, uint8_t& value);
 | 
			
		||||
void from_json(const JsonValue& to, uint16_t& value);
 | 
			
		||||
void from_json(const JsonValue& to, uint32_t& value);
 | 
			
		||||
void from_json(const JsonValue& to, uint64_t& value);
 | 
			
		||||
void from_json(const JsonValue& to, float& value);
 | 
			
		||||
void from_json(const JsonValue& to, double& value);
 | 
			
		||||
void from_json(const JsonValue& to, String& value);
 | 
			
		||||
void from_json(const JsonValue& to, JsonArray& value);
 | 
			
		||||
void from_json(const JsonValue& to, JsonObject& value);
 | 
			
		||||
 | 
			
		||||
template <typename T, size_t S>
 | 
			
		||||
inline void to_json(JsonValue& to, const std::array<T, S>& v)
 | 
			
		||||
{
 | 
			
		||||
	JsonArray arr;
 | 
			
		||||
	for (auto itr = v.begin(); itr != v.end(); itr++)
 | 
			
		||||
	{
 | 
			
		||||
		JsonValue conv;
 | 
			
		||||
		to_json(conv, *itr);
 | 
			
		||||
		arr.push_back(conv);
 | 
			
		||||
	}
 | 
			
		||||
	to = JsonValue(std::move(arr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, size_t S>
 | 
			
		||||
inline void from_json(const JsonValue& to, std::array<T, S>& v)
 | 
			
		||||
{
 | 
			
		||||
	if (!to.is_array())
 | 
			
		||||
	{
 | 
			
		||||
		throw JsonError("json value must be an array");
 | 
			
		||||
	}
 | 
			
		||||
	const JsonArray& arr = to.as_array();
 | 
			
		||||
	size_t si = 0;
 | 
			
		||||
	for (auto& i : arr)
 | 
			
		||||
	{
 | 
			
		||||
		if (si >= v.size())
 | 
			
		||||
		{
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		T conv;
 | 
			
		||||
		from_json(i, conv);
 | 
			
		||||
		v[si] = std::move(conv);
 | 
			
		||||
		si++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename A>
 | 
			
		||||
inline void to_json(JsonValue& to, const std::vector<T, A>& v)
 | 
			
		||||
{
 | 
			
		||||
	JsonArray arr;
 | 
			
		||||
	for (auto itr = v.begin(); itr != v.end(); itr++)
 | 
			
		||||
	{
 | 
			
		||||
		JsonValue conv;
 | 
			
		||||
		to_json(conv, *itr);
 | 
			
		||||
		arr.push_back(conv);
 | 
			
		||||
	}
 | 
			
		||||
	to = JsonValue(std::move(arr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T, typename A>
 | 
			
		||||
inline void from_json(const JsonValue& to, std::vector<T, A>& v)
 | 
			
		||||
{
 | 
			
		||||
	if (!to.is_array())
 | 
			
		||||
	{
 | 
			
		||||
		throw JsonError("json value must be an array");
 | 
			
		||||
	}
 | 
			
		||||
	v.clear();
 | 
			
		||||
	const JsonArray& arr = to.as_array();
 | 
			
		||||
	for (auto& i : arr)
 | 
			
		||||
	{
 | 
			
		||||
		T conv;
 | 
			
		||||
		from_json(i, conv);
 | 
			
		||||
		v.push_back(std::move(conv));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void to_json(JsonValue& to, const srb2::Vector<T>& v)
 | 
			
		||||
{
 | 
			
		||||
	JsonArray arr;
 | 
			
		||||
	for (auto itr = v.begin(); itr != v.end(); itr++)
 | 
			
		||||
	{
 | 
			
		||||
		JsonValue conv;
 | 
			
		||||
		to_json(conv, *itr);
 | 
			
		||||
		arr.push_back(conv);
 | 
			
		||||
	}
 | 
			
		||||
	to = JsonValue(std::move(arr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
inline void from_json(const JsonValue& to, srb2::Vector<T>& v)
 | 
			
		||||
{
 | 
			
		||||
	if (!to.is_array())
 | 
			
		||||
	{
 | 
			
		||||
		throw JsonError("json value must be an array");
 | 
			
		||||
	}
 | 
			
		||||
	v.clear();
 | 
			
		||||
	const JsonArray& arr = to.as_array();
 | 
			
		||||
	for (auto& i : arr)
 | 
			
		||||
	{
 | 
			
		||||
		T conv;
 | 
			
		||||
		from_json(i, conv);
 | 
			
		||||
		v.push_back(std::move(conv));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename K, typename V, typename H, typename E, typename A>
 | 
			
		||||
inline void to_json(JsonValue& to, const std::unordered_map<K, V, H, E, A>& v)
 | 
			
		||||
{
 | 
			
		||||
	JsonObject obj;
 | 
			
		||||
	for (auto itr = v.begin(); itr != v.end(); itr++)
 | 
			
		||||
	{
 | 
			
		||||
		to_json(obj[itr->first], itr->second);
 | 
			
		||||
	}
 | 
			
		||||
	to = JsonValue(std::move(obj));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename K, typename V, typename H, typename E, typename A>
 | 
			
		||||
inline void from_json(const JsonValue& to, std::unordered_map<K, V, H, E, A>& v)
 | 
			
		||||
{
 | 
			
		||||
	if (!to.is_object())
 | 
			
		||||
	{
 | 
			
		||||
		throw JsonError("json value must be an object");
 | 
			
		||||
	}
 | 
			
		||||
	v.clear();
 | 
			
		||||
	const JsonObject& obj = to.as_object();
 | 
			
		||||
	for (auto itr = obj.begin(); itr != obj.end(); itr++)
 | 
			
		||||
	{
 | 
			
		||||
		V conv;
 | 
			
		||||
		from_json(itr->second, conv);
 | 
			
		||||
		v[itr->first] = std::move(conv);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename K, typename V>
 | 
			
		||||
inline void to_json(JsonValue& to, const srb2::HashMap<K, V>& v)
 | 
			
		||||
{
 | 
			
		||||
	JsonObject obj;
 | 
			
		||||
	for (auto itr = v.begin(); itr != v.end(); itr++)
 | 
			
		||||
	{
 | 
			
		||||
		to_json(obj[itr->first], itr->second);
 | 
			
		||||
	}
 | 
			
		||||
	to = JsonValue(std::move(obj));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename K, typename V>
 | 
			
		||||
inline void from_json(const JsonValue& to, srb2::HashMap<K, V>& v)
 | 
			
		||||
{
 | 
			
		||||
	if (!to.is_object())
 | 
			
		||||
	{
 | 
			
		||||
		throw JsonError("json value must be an object");
 | 
			
		||||
	}
 | 
			
		||||
	v.clear();
 | 
			
		||||
	const JsonObject& obj = to.as_object();
 | 
			
		||||
	for (auto itr = obj.begin(); itr != obj.end(); itr++)
 | 
			
		||||
	{
 | 
			
		||||
		V conv;
 | 
			
		||||
		from_json(itr->second, conv);
 | 
			
		||||
		v[itr->first] = std::move(conv);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void from_json(const JsonValue& to, std::string& v);
 | 
			
		||||
void to_json(JsonValue& to, const std::string& v);
 | 
			
		||||
 | 
			
		||||
// template <typename K, typename V>
 | 
			
		||||
// inline void to_json(JsonValue& to, const srb2::OAHashMap<K, V>& v)
 | 
			
		||||
// {
 | 
			
		||||
// 	JsonObject obj;
 | 
			
		||||
// 	for (auto itr = v.begin(); itr != v.end(); itr++)
 | 
			
		||||
// 	{
 | 
			
		||||
// 		to_json(obj[itr->first], itr->second);
 | 
			
		||||
// 	}
 | 
			
		||||
// 	to = JsonValue(std::move(obj));
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// template <typename K, typename V>
 | 
			
		||||
// inline void from_json(const JsonValue& to, srb2::OAHashMap<K, V>& v)
 | 
			
		||||
// {
 | 
			
		||||
// 	if (!to.is_object())
 | 
			
		||||
// 	{
 | 
			
		||||
// 		throw JsonError("json value must be an object");
 | 
			
		||||
// 	}
 | 
			
		||||
// 	v.clear();
 | 
			
		||||
// 	const JsonObject& obj = to.as_object();
 | 
			
		||||
// 	for (auto itr = obj.begin(); itr != obj.end(); itr++)
 | 
			
		||||
// 	{
 | 
			
		||||
// 		V conv;
 | 
			
		||||
// 		from_json(itr->second, conv);
 | 
			
		||||
// 		v[itr->first] = std::move(conv);
 | 
			
		||||
// 	}
 | 
			
		||||
// }
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
template <typename V>
 | 
			
		||||
inline V JsonValue::get() const
 | 
			
		||||
{
 | 
			
		||||
	V v;
 | 
			
		||||
	from_json(*this, v);
 | 
			
		||||
	return v;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename V>
 | 
			
		||||
inline V JsonValue::value(const char* key, const V& def) const
 | 
			
		||||
{
 | 
			
		||||
	V copy { def };
 | 
			
		||||
	return value(key, std::move(copy));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename V>
 | 
			
		||||
inline V JsonValue::value(const String& key, const V& def) const
 | 
			
		||||
{
 | 
			
		||||
	return value(key.c_str(), def);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename V>
 | 
			
		||||
inline V JsonValue::value(const char* key, V&& def) const
 | 
			
		||||
{
 | 
			
		||||
	const JsonObject& obj = as_object();
 | 
			
		||||
	auto itr = obj.find(key);
 | 
			
		||||
	if (itr != obj.end())
 | 
			
		||||
	{
 | 
			
		||||
		return itr->second.get<V>();
 | 
			
		||||
	}
 | 
			
		||||
	return def;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <> bool JsonValue::get() const;
 | 
			
		||||
template <> int8_t JsonValue::get() const;
 | 
			
		||||
template <> int16_t JsonValue::get() const;
 | 
			
		||||
template <> int32_t JsonValue::get() const;
 | 
			
		||||
template <> int64_t JsonValue::get() const;
 | 
			
		||||
template <> uint8_t JsonValue::get() const;
 | 
			
		||||
template <> uint16_t JsonValue::get() const;
 | 
			
		||||
template <> uint32_t JsonValue::get() const;
 | 
			
		||||
template <> uint64_t JsonValue::get() const;
 | 
			
		||||
template <> float JsonValue::get() const;
 | 
			
		||||
template <> double JsonValue::get() const;
 | 
			
		||||
template <> String JsonValue::get() const;
 | 
			
		||||
template <> std::string JsonValue::get() const;
 | 
			
		||||
template <> std::string_view JsonValue::get() const;
 | 
			
		||||
template <> JsonArray JsonValue::get() const;
 | 
			
		||||
template <> JsonObject JsonValue::get() const;
 | 
			
		||||
template <> JsonValue JsonValue::get() const;
 | 
			
		||||
 | 
			
		||||
inline bool operator==(const JsonValue& lhs, bool rhs)
 | 
			
		||||
{
 | 
			
		||||
	if (!lhs.is_boolean())
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return lhs.get<bool>() == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator==(const JsonValue& lhs, int64_t rhs)
 | 
			
		||||
{
 | 
			
		||||
	if (!lhs.is_number())
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return lhs.get<int64_t>() == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator==(const JsonValue& lhs, uint64_t rhs)
 | 
			
		||||
{
 | 
			
		||||
	if (!lhs.is_number())
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return lhs.get<uint64_t>() == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator==(const JsonValue& lhs, double rhs)
 | 
			
		||||
{
 | 
			
		||||
	if (!lhs.is_number())
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return lhs.get<double>() == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator==(const JsonValue& lhs, std::string_view rhs)
 | 
			
		||||
{
 | 
			
		||||
	if (!lhs.is_string())
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return lhs.get<std::string_view>() == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator==(const JsonValue& lhs, const JsonArray& rhs)
 | 
			
		||||
{
 | 
			
		||||
	if (!lhs.is_array())
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return lhs.as_array() == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator==(const JsonValue& lhs, const JsonObject& rhs)
 | 
			
		||||
{
 | 
			
		||||
	if (!lhs.is_object())
 | 
			
		||||
	{
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	return lhs.as_object() == rhs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator!=(const JsonValue& lhs, bool rhs)
 | 
			
		||||
{
 | 
			
		||||
	return !(lhs == rhs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator!=(const JsonValue& lhs, int64_t rhs)
 | 
			
		||||
{
 | 
			
		||||
	return !(lhs == rhs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator!=(const JsonValue& lhs, uint64_t rhs)
 | 
			
		||||
{
 | 
			
		||||
	return !(lhs == rhs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator!=(const JsonValue& lhs, double rhs)
 | 
			
		||||
{
 | 
			
		||||
	return !(lhs == rhs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator!=(const JsonValue& lhs, std::string_view rhs)
 | 
			
		||||
{
 | 
			
		||||
	return !(lhs == rhs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator!=(const JsonValue& lhs, const JsonArray& rhs)
 | 
			
		||||
{
 | 
			
		||||
	return !(lhs == rhs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool operator!=(const JsonValue& lhs, const JsonObject& rhs)
 | 
			
		||||
{
 | 
			
		||||
	return !(lhs == rhs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern template class Vector<srb2::JsonValue>;
 | 
			
		||||
extern template class HashMap<String, srb2::JsonValue>;
 | 
			
		||||
 | 
			
		||||
} // namespace srb2
 | 
			
		||||
 | 
			
		||||
#endif // SRB2_CORE_JSON_HPP
 | 
			
		||||
							
								
								
									
										185
									
								
								src/core/json_macro.inl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								src/core/json_macro.inl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,185 @@
 | 
			
		|||
//     __ _____ _____ _____
 | 
			
		||||
//  __|  |   __|     |   | |  JSON for Modern C++
 | 
			
		||||
// |  |  |__   |  |  | | | |  version 3.11.3
 | 
			
		||||
// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 | 
			
		||||
//
 | 
			
		||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
 | 
			
		||||
// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson <evan@nemerson.com>
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
// file: macro_scope.hpp
 | 
			
		||||
 | 
			
		||||
// This file is derived from nlohmman json's codegen macros and thus is provided under its license.
 | 
			
		||||
 | 
			
		||||
#define SRB2_JSON_EXPAND( x ) x
 | 
			
		||||
#define SRB2_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
 | 
			
		||||
#define SRB2_JSON_PASTE(...) SRB2_JSON_EXPAND(SRB2_JSON_GET_MACRO(__VA_ARGS__, \
 | 
			
		||||
        SRB2_JSON_PASTE64, \
 | 
			
		||||
        SRB2_JSON_PASTE63, \
 | 
			
		||||
        SRB2_JSON_PASTE62, \
 | 
			
		||||
        SRB2_JSON_PASTE61, \
 | 
			
		||||
        SRB2_JSON_PASTE60, \
 | 
			
		||||
        SRB2_JSON_PASTE59, \
 | 
			
		||||
        SRB2_JSON_PASTE58, \
 | 
			
		||||
        SRB2_JSON_PASTE57, \
 | 
			
		||||
        SRB2_JSON_PASTE56, \
 | 
			
		||||
        SRB2_JSON_PASTE55, \
 | 
			
		||||
        SRB2_JSON_PASTE54, \
 | 
			
		||||
        SRB2_JSON_PASTE53, \
 | 
			
		||||
        SRB2_JSON_PASTE52, \
 | 
			
		||||
        SRB2_JSON_PASTE51, \
 | 
			
		||||
        SRB2_JSON_PASTE50, \
 | 
			
		||||
        SRB2_JSON_PASTE49, \
 | 
			
		||||
        SRB2_JSON_PASTE48, \
 | 
			
		||||
        SRB2_JSON_PASTE47, \
 | 
			
		||||
        SRB2_JSON_PASTE46, \
 | 
			
		||||
        SRB2_JSON_PASTE45, \
 | 
			
		||||
        SRB2_JSON_PASTE44, \
 | 
			
		||||
        SRB2_JSON_PASTE43, \
 | 
			
		||||
        SRB2_JSON_PASTE42, \
 | 
			
		||||
        SRB2_JSON_PASTE41, \
 | 
			
		||||
        SRB2_JSON_PASTE40, \
 | 
			
		||||
        SRB2_JSON_PASTE39, \
 | 
			
		||||
        SRB2_JSON_PASTE38, \
 | 
			
		||||
        SRB2_JSON_PASTE37, \
 | 
			
		||||
        SRB2_JSON_PASTE36, \
 | 
			
		||||
        SRB2_JSON_PASTE35, \
 | 
			
		||||
        SRB2_JSON_PASTE34, \
 | 
			
		||||
        SRB2_JSON_PASTE33, \
 | 
			
		||||
        SRB2_JSON_PASTE32, \
 | 
			
		||||
        SRB2_JSON_PASTE31, \
 | 
			
		||||
        SRB2_JSON_PASTE30, \
 | 
			
		||||
        SRB2_JSON_PASTE29, \
 | 
			
		||||
        SRB2_JSON_PASTE28, \
 | 
			
		||||
        SRB2_JSON_PASTE27, \
 | 
			
		||||
        SRB2_JSON_PASTE26, \
 | 
			
		||||
        SRB2_JSON_PASTE25, \
 | 
			
		||||
        SRB2_JSON_PASTE24, \
 | 
			
		||||
        SRB2_JSON_PASTE23, \
 | 
			
		||||
        SRB2_JSON_PASTE22, \
 | 
			
		||||
        SRB2_JSON_PASTE21, \
 | 
			
		||||
        SRB2_JSON_PASTE20, \
 | 
			
		||||
        SRB2_JSON_PASTE19, \
 | 
			
		||||
        SRB2_JSON_PASTE18, \
 | 
			
		||||
        SRB2_JSON_PASTE17, \
 | 
			
		||||
        SRB2_JSON_PASTE16, \
 | 
			
		||||
        SRB2_JSON_PASTE15, \
 | 
			
		||||
        SRB2_JSON_PASTE14, \
 | 
			
		||||
        SRB2_JSON_PASTE13, \
 | 
			
		||||
        SRB2_JSON_PASTE12, \
 | 
			
		||||
        SRB2_JSON_PASTE11, \
 | 
			
		||||
        SRB2_JSON_PASTE10, \
 | 
			
		||||
        SRB2_JSON_PASTE9, \
 | 
			
		||||
        SRB2_JSON_PASTE8, \
 | 
			
		||||
        SRB2_JSON_PASTE7, \
 | 
			
		||||
        SRB2_JSON_PASTE6, \
 | 
			
		||||
        SRB2_JSON_PASTE5, \
 | 
			
		||||
        SRB2_JSON_PASTE4, \
 | 
			
		||||
        SRB2_JSON_PASTE3, \
 | 
			
		||||
        SRB2_JSON_PASTE2, \
 | 
			
		||||
        SRB2_JSON_PASTE1)(__VA_ARGS__))
 | 
			
		||||
#define SRB2_JSON_PASTE2(func, v1) func(v1)
 | 
			
		||||
#define SRB2_JSON_PASTE3(func, v1, v2) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE2(func, v2)
 | 
			
		||||
#define SRB2_JSON_PASTE4(func, v1, v2, v3) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE3(func, v2, v3)
 | 
			
		||||
#define SRB2_JSON_PASTE5(func, v1, v2, v3, v4) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE4(func, v2, v3, v4)
 | 
			
		||||
#define SRB2_JSON_PASTE6(func, v1, v2, v3, v4, v5) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE5(func, v2, v3, v4, v5)
 | 
			
		||||
#define SRB2_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE6(func, v2, v3, v4, v5, v6)
 | 
			
		||||
#define SRB2_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
 | 
			
		||||
#define SRB2_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
 | 
			
		||||
#define SRB2_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)
 | 
			
		||||
#define SRB2_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
 | 
			
		||||
#define SRB2_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
 | 
			
		||||
#define SRB2_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)
 | 
			
		||||
#define SRB2_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)
 | 
			
		||||
#define SRB2_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)
 | 
			
		||||
#define SRB2_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)
 | 
			
		||||
#define SRB2_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)
 | 
			
		||||
#define SRB2_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)
 | 
			
		||||
#define SRB2_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)
 | 
			
		||||
#define SRB2_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)
 | 
			
		||||
#define SRB2_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
 | 
			
		||||
#define SRB2_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)
 | 
			
		||||
#define SRB2_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)
 | 
			
		||||
#define SRB2_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)
 | 
			
		||||
#define SRB2_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)
 | 
			
		||||
#define SRB2_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)
 | 
			
		||||
#define SRB2_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)
 | 
			
		||||
#define SRB2_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)
 | 
			
		||||
#define SRB2_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)
 | 
			
		||||
#define SRB2_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)
 | 
			
		||||
#define SRB2_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)
 | 
			
		||||
#define SRB2_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)
 | 
			
		||||
#define SRB2_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)
 | 
			
		||||
#define SRB2_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)
 | 
			
		||||
#define SRB2_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)
 | 
			
		||||
#define SRB2_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)
 | 
			
		||||
#define SRB2_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)
 | 
			
		||||
#define SRB2_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)
 | 
			
		||||
#define SRB2_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)
 | 
			
		||||
#define SRB2_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)
 | 
			
		||||
#define SRB2_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)
 | 
			
		||||
#define SRB2_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)
 | 
			
		||||
#define SRB2_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)
 | 
			
		||||
#define SRB2_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)
 | 
			
		||||
#define SRB2_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)
 | 
			
		||||
#define SRB2_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)
 | 
			
		||||
#define SRB2_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)
 | 
			
		||||
#define SRB2_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)
 | 
			
		||||
#define SRB2_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)
 | 
			
		||||
#define SRB2_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)
 | 
			
		||||
#define SRB2_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)
 | 
			
		||||
#define SRB2_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)
 | 
			
		||||
#define SRB2_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)
 | 
			
		||||
#define SRB2_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)
 | 
			
		||||
#define SRB2_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)
 | 
			
		||||
#define SRB2_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)
 | 
			
		||||
#define SRB2_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)
 | 
			
		||||
#define SRB2_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)
 | 
			
		||||
#define SRB2_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)
 | 
			
		||||
#define SRB2_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)
 | 
			
		||||
#define SRB2_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)
 | 
			
		||||
#define SRB2_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)
 | 
			
		||||
#define SRB2_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)
 | 
			
		||||
#define SRB2_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)
 | 
			
		||||
 | 
			
		||||
#define SRB2_JSON_TO(v1) to_json(srb2_json_j.as_object()[#v1], srb2_json_t.v1);
 | 
			
		||||
#define SRB2_JSON_FROM(v1) srb2_json_t.v1 = srb2_json_j.as_object().at(#v1);
 | 
			
		||||
#define SRB2_JSON_FROM_WITH_DEFAULT(v1) if (srb2_json_j.as_object().find(#v1) != srb2_json_j.as_object().end()) \
 | 
			
		||||
    { \
 | 
			
		||||
        from_json(srb2_json_j.as_object().find(#v1)->second, srb2_json_t.v1); \
 | 
			
		||||
    } \
 | 
			
		||||
    else \
 | 
			
		||||
    { \
 | 
			
		||||
        srb2_json_t.v1 = srb2_json_default_obj.v1; \
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
@brief macro
 | 
			
		||||
@def SRB2_JSON_DEFINE_TYPE_INTRUSIVE
 | 
			
		||||
@since version 3.9.0
 | 
			
		||||
*/
 | 
			
		||||
#define SRB2_JSON_DEFINE_TYPE_INTRUSIVE(Type, ...)  \
 | 
			
		||||
    friend void to_json(srb2::JsonValue& srb2_json_j, const Type& srb2_json_t) { srb2_json_j = srb2::JsonObject(); SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_TO, __VA_ARGS__)) } \
 | 
			
		||||
    friend void from_json(const srb2::JsonValue& srb2_json_j, Type& srb2_json_t) { SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_FROM, __VA_ARGS__)) }
 | 
			
		||||
 | 
			
		||||
#define SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...)  \
 | 
			
		||||
    friend void to_json(srb2::JsonValue& srb2_json_j, const Type& srb2_json_t) { srb2_json_j = srb2::JsonObject(); SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_TO, __VA_ARGS__)) } \
 | 
			
		||||
    friend void from_json(const srb2::JsonValue& srb2_json_j, Type& srb2_json_t) { const Type srb2_json_default_obj{}; SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
@brief macro
 | 
			
		||||
@def SRB2_JSON_DEFINE_TYPE_NON_INTRUSIVE
 | 
			
		||||
@since version 3.9.0
 | 
			
		||||
*/
 | 
			
		||||
#define SRB2_JSON_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \
 | 
			
		||||
    inline void to_json(srb2::JsonValue& srb2_json_j, const Type& srb2_json_t) { SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_TO, __VA_ARGS__)) } \
 | 
			
		||||
    inline void from_json(const srb2::JsonValue& srb2_json_j, Type& srb2_json_t) { SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_FROM, __VA_ARGS__)) }
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
@brief macro
 | 
			
		||||
@def SRB2_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
 | 
			
		||||
@since version 3.11.0
 | 
			
		||||
*/
 | 
			
		||||
#define SRB2_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...)  \
 | 
			
		||||
    inline void to_json(srb2::JsonValue& srb2_json_j, const Type& srb2_json_t) { SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_TO, __VA_ARGS__)) } \
 | 
			
		||||
    inline void from_json(const srb2::JsonValue& srb2_json_j, Type& srb2_json_t) { const Type srb2_json_default_obj{}; SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
 | 
			
		||||
							
								
								
									
										1125
									
								
								src/core/string.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1125
									
								
								src/core/string.cpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										453
									
								
								src/core/string.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										453
									
								
								src/core/string.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,453 @@
 | 
			
		|||
// DR. ROBOTNIK'S RING RACERS
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// Copyright (C) 2025 by Ronald "Eidolon" Kinard
 | 
			
		||||
// Copyright (C) 2025 by Kart Krew
 | 
			
		||||
//
 | 
			
		||||
// This program is free software distributed under the
 | 
			
		||||
// terms of the GNU General Public License, version 2.
 | 
			
		||||
// See the 'LICENSE' file for more details.
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#ifndef SRB2_CORE_STRING_HPP
 | 
			
		||||
#define SRB2_CORE_STRING_HPP
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <initializer_list>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
 | 
			
		||||
#include "fmt/core.h"
 | 
			
		||||
 | 
			
		||||
#include "static_vec.hpp"
 | 
			
		||||
#include "vector.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class Utf8Iter
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	using difference_type = size_t;
 | 
			
		||||
	using value_type = uint32_t;
 | 
			
		||||
	using pointer = void;
 | 
			
		||||
	using reference = void;
 | 
			
		||||
	using iterator_category = std::input_iterator_tag;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	size_t i_;
 | 
			
		||||
	std::string_view s_;
 | 
			
		||||
 | 
			
		||||
	Utf8Iter(std::string_view s, size_t i) : i_(i), s_(s) {}
 | 
			
		||||
 | 
			
		||||
	friend class String;
 | 
			
		||||
	uint32_t do_codepoint() const;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	Utf8Iter() = default;
 | 
			
		||||
	Utf8Iter(const Utf8Iter&) = default;
 | 
			
		||||
	Utf8Iter(Utf8Iter&&) noexcept = default;
 | 
			
		||||
	~Utf8Iter() = default;
 | 
			
		||||
	Utf8Iter& operator=(const Utf8Iter&) = default;
 | 
			
		||||
	Utf8Iter& operator=(Utf8Iter&&) noexcept = default;
 | 
			
		||||
 | 
			
		||||
	static Utf8Iter begin(std::string_view s) { return Utf8Iter(s, 0); }
 | 
			
		||||
	static Utf8Iter end(std::string_view s) { return Utf8Iter(s, s.size()); }
 | 
			
		||||
 | 
			
		||||
	bool operator==(const Utf8Iter& r) const noexcept
 | 
			
		||||
	{
 | 
			
		||||
		return s_ == r.s_ && i_ == r.i_;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool operator!=(const Utf8Iter& r) const noexcept { return !(*this == r); }
 | 
			
		||||
 | 
			
		||||
	uint32_t operator*() const { return codepoint(); }
 | 
			
		||||
	Utf8Iter& operator++()
 | 
			
		||||
	{
 | 
			
		||||
		i_ += size();
 | 
			
		||||
		return *this;
 | 
			
		||||
	}
 | 
			
		||||
	Utf8Iter operator++(int)
 | 
			
		||||
	{
 | 
			
		||||
		Utf8Iter copy = *this;
 | 
			
		||||
		++*this;
 | 
			
		||||
		return copy;
 | 
			
		||||
	}
 | 
			
		||||
	uint32_t codepoint() const;
 | 
			
		||||
	bool valid() const;
 | 
			
		||||
	uint8_t size() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Utf16Iter
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	using difference_type = size_t;
 | 
			
		||||
	using value_type = uint32_t;
 | 
			
		||||
	using pointer = void;
 | 
			
		||||
	using reference = void;
 | 
			
		||||
	using iterator_category = std::input_iterator_tag;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	size_t i_;
 | 
			
		||||
	std::u16string_view s_;
 | 
			
		||||
 | 
			
		||||
	Utf16Iter(std::u16string_view s, size_t i) : i_(i), s_(s) {}
 | 
			
		||||
 | 
			
		||||
	uint32_t do_codepoint() const;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	Utf16Iter() = default;
 | 
			
		||||
	Utf16Iter(const Utf16Iter&) = default;
 | 
			
		||||
	Utf16Iter(Utf16Iter&&) noexcept = default;
 | 
			
		||||
	~Utf16Iter() = default;
 | 
			
		||||
	Utf16Iter& operator=(const Utf16Iter&) = default;
 | 
			
		||||
	Utf16Iter& operator=(Utf16Iter&&) = default;
 | 
			
		||||
 | 
			
		||||
	static Utf16Iter begin(std::u16string_view s) { return Utf16Iter(s, 0); }
 | 
			
		||||
	static Utf16Iter end(std::u16string_view s) { return Utf16Iter(s, s.size()); }
 | 
			
		||||
 | 
			
		||||
	bool operator==(const Utf16Iter& r) const noexcept
 | 
			
		||||
	{
 | 
			
		||||
		return s_ == r.s_ && i_ == r.i_;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool operator!=(const Utf16Iter& r) const noexcept { return !(*this == r); }
 | 
			
		||||
 | 
			
		||||
	uint32_t operator*() const { return codepoint(); }
 | 
			
		||||
	Utf16Iter& operator++()
 | 
			
		||||
	{
 | 
			
		||||
		i_ += size();
 | 
			
		||||
		return *this;
 | 
			
		||||
	}
 | 
			
		||||
	Utf16Iter operator++(int)
 | 
			
		||||
	{
 | 
			
		||||
		Utf16Iter copy = *this;
 | 
			
		||||
		++*this;
 | 
			
		||||
		return copy;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uint32_t codepoint() const;
 | 
			
		||||
	bool valid() const { return true; } // we allow unpaired surrogates in general
 | 
			
		||||
	uint8_t size() const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class String
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	using size_type = uint32_t;
 | 
			
		||||
	using difference_type = int64_t;
 | 
			
		||||
	using value_type = uint8_t;
 | 
			
		||||
	using reference = uint8_t&;
 | 
			
		||||
	using const_reference = const uint8_t&;
 | 
			
		||||
	using pointer = uint8_t*;
 | 
			
		||||
	using const_pointer = const uint8_t*;
 | 
			
		||||
	using iterator = uint8_t*;
 | 
			
		||||
	using const_iterator = const uint8_t*;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	srb2::Vector<uint8_t> data_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
	friend struct std::hash<String>;
 | 
			
		||||
 | 
			
		||||
	static constexpr const size_type npos = -1;
 | 
			
		||||
 | 
			
		||||
	String() = default;
 | 
			
		||||
	String(const String&);
 | 
			
		||||
	String(String&&) noexcept;
 | 
			
		||||
	~String();
 | 
			
		||||
	String& operator=(const String&);
 | 
			
		||||
	String& operator=(String&&) noexcept;
 | 
			
		||||
 | 
			
		||||
	String(const char* s);
 | 
			
		||||
	String(const char* s, size_t len);
 | 
			
		||||
	String(const std::string&);
 | 
			
		||||
 | 
			
		||||
	explicit String(std::string_view view);
 | 
			
		||||
 | 
			
		||||
	operator std::string() const;
 | 
			
		||||
	operator std::string_view() const;
 | 
			
		||||
 | 
			
		||||
	size_type size() const noexcept;
 | 
			
		||||
	bool empty() const noexcept { return data_.empty(); }
 | 
			
		||||
	const char* c_str() const;
 | 
			
		||||
	uint8_t* data() noexcept { return data_.data(); }
 | 
			
		||||
	const uint8_t* data() const noexcept { return data_.data(); }
 | 
			
		||||
	void reserve(size_type capacity);
 | 
			
		||||
 | 
			
		||||
	uint8_t* begin() noexcept;
 | 
			
		||||
	uint8_t* end() noexcept;
 | 
			
		||||
	const uint8_t* cbegin() const noexcept;
 | 
			
		||||
	const uint8_t* cend() const noexcept;
 | 
			
		||||
	const uint8_t* begin() const noexcept { return cbegin(); }
 | 
			
		||||
	const uint8_t* end() const noexcept { return cend(); }
 | 
			
		||||
 | 
			
		||||
	uint8_t& at(size_type i);
 | 
			
		||||
	const uint8_t& at(size_type i) const;
 | 
			
		||||
	uint8_t& operator[](size_type i) { return data_[i]; }
 | 
			
		||||
	const uint8_t& operator[](size_type i) const { return data_[i]; }
 | 
			
		||||
	uint8_t& front() { return data_[0]; }
 | 
			
		||||
	const uint8_t& front() const { return data_[0]; }
 | 
			
		||||
	uint8_t& back() { return data_[size() - 1]; }
 | 
			
		||||
	const uint8_t& back() const { return data_[size() - 1]; }
 | 
			
		||||
 | 
			
		||||
	void clear() { data_.clear(); }
 | 
			
		||||
	String& insert(size_type index, size_type count, uint8_t ch);
 | 
			
		||||
	String& insert(size_type index, const char* s);
 | 
			
		||||
	String& insert(size_type index, const char* s, size_type count);
 | 
			
		||||
	String& insert(size_type index, std::string_view str);
 | 
			
		||||
	String& insert(size_type index, std::string_view str, size_t s_index, size_t count = npos);
 | 
			
		||||
	iterator insert(const_iterator pos, uint8_t ch);
 | 
			
		||||
	iterator insert(const_iterator pos, size_type count, uint8_t ch);
 | 
			
		||||
 | 
			
		||||
	template <
 | 
			
		||||
		typename InputIt,
 | 
			
		||||
		typename std::enable_if_t<
 | 
			
		||||
			std::is_constructible<
 | 
			
		||||
				uint8_t,
 | 
			
		||||
				typename std::iterator_traits<InputIt>::reference
 | 
			
		||||
			>::value,
 | 
			
		||||
			int
 | 
			
		||||
		> = 0
 | 
			
		||||
	>
 | 
			
		||||
	iterator insert(const_iterator pos, InputIt first, InputIt last)
 | 
			
		||||
	{
 | 
			
		||||
		size_type offset = pos - data();
 | 
			
		||||
		if (!empty())
 | 
			
		||||
		{
 | 
			
		||||
			// remove null byte
 | 
			
		||||
			data_.pop_back();
 | 
			
		||||
		}
 | 
			
		||||
		auto ret = data_.insert(pos, first, last);
 | 
			
		||||
		if (data_.size() > 0)
 | 
			
		||||
		{
 | 
			
		||||
			data_.push_back(0);
 | 
			
		||||
		}
 | 
			
		||||
		return data() + offset;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	iterator insert(const_iterator pos, std::initializer_list<uint8_t> list);
 | 
			
		||||
	String& erase(size_type index = 0, size_type count = npos);
 | 
			
		||||
	iterator erase(const_iterator position);
 | 
			
		||||
	iterator erase(const_iterator first, const_iterator last);
 | 
			
		||||
	void push_back(uint8_t v);
 | 
			
		||||
	void pop_back();
 | 
			
		||||
	String& append(size_type count, uint8_t ch);
 | 
			
		||||
	String& append(const char* s, size_type count);
 | 
			
		||||
	String& append(const char* s);
 | 
			
		||||
	String& append(std::string_view str);
 | 
			
		||||
	String& append(std::string_view str, size_type pos, size_type count = npos);
 | 
			
		||||
 | 
			
		||||
	template <
 | 
			
		||||
		typename InputIt,
 | 
			
		||||
		typename std::enable_if_t<
 | 
			
		||||
			std::is_constructible<
 | 
			
		||||
				uint8_t,
 | 
			
		||||
				typename std::iterator_traits<InputIt>::reference
 | 
			
		||||
			>::value,
 | 
			
		||||
			int
 | 
			
		||||
		> = 0
 | 
			
		||||
	>
 | 
			
		||||
	String& append(InputIt first, InputIt last)
 | 
			
		||||
	{
 | 
			
		||||
		if (!empty())
 | 
			
		||||
		{
 | 
			
		||||
			// remove null byte
 | 
			
		||||
			data_.pop_back();
 | 
			
		||||
		}
 | 
			
		||||
		for (; first != last; first++)
 | 
			
		||||
		{
 | 
			
		||||
			data_.push_back(*first);
 | 
			
		||||
		}
 | 
			
		||||
		if (data_.size() > 0)
 | 
			
		||||
		{
 | 
			
		||||
			data_.push_back(0);
 | 
			
		||||
		}
 | 
			
		||||
		return *this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	String& append(std::initializer_list<uint8_t> ilist);
 | 
			
		||||
	String& operator+=(std::string_view r);
 | 
			
		||||
	String& operator+=(const char* r);
 | 
			
		||||
	String& operator+=(uint8_t r);
 | 
			
		||||
	String& operator+=(std::initializer_list<uint8_t> r);
 | 
			
		||||
	String& replace(size_type pos, size_type count, std::string_view str);
 | 
			
		||||
	String& replace(const_iterator first, const_iterator last, std::string_view str);
 | 
			
		||||
	String& replace(size_type pos, size_type count, std::string_view str, size_t pos2, size_t count2 = -1);
 | 
			
		||||
	String& replace(size_type pos, size_type count, const char* cstr, size_type count2);
 | 
			
		||||
	String& replace(const_iterator first, const_iterator last, const char* cstr, size_type count2);
 | 
			
		||||
	String& replace(size_type pos, size_type count, const char* cstr);
 | 
			
		||||
	String& replace(const_iterator first, const_iterator last, const char* cstr);
 | 
			
		||||
	// String& replace(size_type pos, size_type count, size_type count2, uint8_t ch);
 | 
			
		||||
	String& replace(const_iterator first, const_iterator last, uint8_t ch);
 | 
			
		||||
 | 
			
		||||
	template <
 | 
			
		||||
		typename InputIt,
 | 
			
		||||
		typename std::enable_if_t<
 | 
			
		||||
			std::is_constructible<
 | 
			
		||||
				uint8_t,
 | 
			
		||||
				typename std::iterator_traits<InputIt>::reference
 | 
			
		||||
			>::value,
 | 
			
		||||
			int
 | 
			
		||||
		> = 0
 | 
			
		||||
	>
 | 
			
		||||
	String& replace(const_iterator first, const_iterator last, InputIt first2, InputIt last2)
 | 
			
		||||
	{
 | 
			
		||||
		for (; first != last && first2 != last2; first++, first2++)
 | 
			
		||||
		{
 | 
			
		||||
			*const_cast<iterator>(first) = *first2;
 | 
			
		||||
		}
 | 
			
		||||
		return *this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	String& replace(const_iterator first, const_iterator last, std::initializer_list<uint8_t> ilist);
 | 
			
		||||
	size_type copy(uint8_t* dest, size_type count, size_type pos = 0) const;
 | 
			
		||||
	size_type copy(char* dest, size_type count, size_type pos = 0) const;
 | 
			
		||||
	void resize(size_type count);
 | 
			
		||||
	void resize(size_type count, uint8_t ch);
 | 
			
		||||
	void swap(String& other) noexcept;
 | 
			
		||||
 | 
			
		||||
	size_type find(const String& str, size_type pos = 0) const;
 | 
			
		||||
	size_type find(std::string_view str, size_type pos = 0) const;
 | 
			
		||||
	size_type find(const char* s, size_type pos, size_t count) const;
 | 
			
		||||
	size_type find(const char* s, size_type pos = 0) const;
 | 
			
		||||
	size_type find(uint8_t ch, size_type pos = 0) const;
 | 
			
		||||
	size_type rfind(const String& str, size_type pos = npos) const;
 | 
			
		||||
	size_type rfind(std::string_view str, size_type pos = npos) const;
 | 
			
		||||
	size_type rfind(const char* s, size_type pos, size_type count) const;
 | 
			
		||||
	size_type rfind(const char* s, size_type pos = npos) const;
 | 
			
		||||
	size_type rfind(uint8_t ch, size_type pos = npos) const;
 | 
			
		||||
	// size_type find_first_of(std::string_view str, size_type pos = 0) const;
 | 
			
		||||
	// size_type find_first_of(const char* s, size_type pos, size_type count) const;
 | 
			
		||||
	// size_type find_first_of(const char* s, size_type pos = 0) const;
 | 
			
		||||
	// size_type find_first_of(uint8_t ch, size_type pos = 0) const;
 | 
			
		||||
	// size_type find_first_not_of(std::string_view str, size_type pos = 0) const;
 | 
			
		||||
	// size_type find_first_not_of(const char* s, size_type pos, size_type count) const;
 | 
			
		||||
	// size_type find_first_not_of(const char* s, size_type pos = 0) const;
 | 
			
		||||
	// size_type find_first_not_of(uint8_t ch, size_type pos = 0) const;
 | 
			
		||||
	// size_type find_last_of(std::string_view str, size_type pos = npos) const;
 | 
			
		||||
	// size_type find_last_of(const char* s, size_type pos, size_type count) const;
 | 
			
		||||
	// size_type find_last_of(const char* s, size_type pos = npos) const;
 | 
			
		||||
	// size_type find_last_of(uint8_t ch, size_type pos = npos) const;
 | 
			
		||||
	// size_type find_last_not_of(std::string_view str, size_type pos = npos) const;
 | 
			
		||||
	// size_type find_last_not_of(const char* s, size_type pos, size_type count) const;
 | 
			
		||||
	// size_type find_last_not_of(const char* s, size_type pos = npos) const;
 | 
			
		||||
	// size_type find_last_not_of(uint8_t ch, size_type pos = npos) const;
 | 
			
		||||
 | 
			
		||||
	int compare(std::string_view str) const noexcept;
 | 
			
		||||
	// int compare(size_type pos1, size_type count1, std::string_view str) const;
 | 
			
		||||
	// int compare(size_type pos1, size_type count1, std::string_view str, size_type pos2, size_type count2 = npos) const;
 | 
			
		||||
	int compare(const char* s) const;
 | 
			
		||||
	// int compare(size_type pos1, size_type count1, const char* s) const;
 | 
			
		||||
	// int compare(size_type pos1, size_type count1, const char* s, size_type count2) const;
 | 
			
		||||
	String substr(size_type pos = 0, size_type count = npos) const;
 | 
			
		||||
 | 
			
		||||
	// Non-STL String functions
 | 
			
		||||
	bool valid_utf8() const noexcept;
 | 
			
		||||
	srb2::Vector<uint16_t> to_utf16() const;
 | 
			
		||||
	srb2::Vector<uint32_t> to_utf32() const;
 | 
			
		||||
	size_t length_decoded() const;
 | 
			
		||||
 | 
			
		||||
	Utf8Iter decode_begin() const { return Utf8Iter(*this, 0); }
 | 
			
		||||
	Utf8Iter decode_end() const { return Utf8Iter(*this, size()); };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
String operator+(const String& lhs, const String& rhs);
 | 
			
		||||
String operator+(const String& lhs, const char* rhs);
 | 
			
		||||
String operator+(const String& lhs, uint8_t rhs);
 | 
			
		||||
String operator+(const String& lhs, std::string_view view);
 | 
			
		||||
 | 
			
		||||
bool operator==(const String& lhs, const String& rhs);
 | 
			
		||||
bool operator==(const String& lhs, const char* rhs);
 | 
			
		||||
// bool operator==(const String& lhs, std::string_view rhs);
 | 
			
		||||
bool operator!=(const String& lhs, const String& rhs);
 | 
			
		||||
bool operator!=(const String& lhs, const char* rhs);
 | 
			
		||||
// bool operator!=(const String& lhs, std::string_view rhs);
 | 
			
		||||
bool operator<(const String& lhs, const String& rhs);
 | 
			
		||||
bool operator<(const String& lhs, const char* rhs);
 | 
			
		||||
// bool operator<(const String& lhs, std::string_view rhs);
 | 
			
		||||
bool operator<=(const String& lhs, const String& rhs);
 | 
			
		||||
bool operator<=(const String& lhs, const char* rhs);
 | 
			
		||||
// bool operator<=(const String& lhs, std::string_view rhs);
 | 
			
		||||
bool operator>(const String& lhs, const String& rhs);
 | 
			
		||||
bool operator>(const String& lhs, const char* rhs);
 | 
			
		||||
// bool operator>(const String& lhs, std::string_view rhs);
 | 
			
		||||
bool operator>=(const String& lhs, const String& rhs);
 | 
			
		||||
bool operator>=(const String& lhs, const char* rhs);
 | 
			
		||||
// bool operator>=(const String& lhs, std::string_view rhs);
 | 
			
		||||
 | 
			
		||||
Vector<uint16_t> to_utf16(std::string_view utf8);
 | 
			
		||||
Vector<uint32_t> to_utf32(std::string_view utf8);
 | 
			
		||||
 | 
			
		||||
srb2::StaticVec<uint8_t, 4> to_utf8(uint32_t codepoint);
 | 
			
		||||
 | 
			
		||||
template <
 | 
			
		||||
	typename ItUTF32,
 | 
			
		||||
	typename std::enable_if_t<
 | 
			
		||||
		std::is_constructible<
 | 
			
		||||
			uint32_t,
 | 
			
		||||
			typename std::iterator_traits<ItUTF32>::reference
 | 
			
		||||
		>::value,
 | 
			
		||||
		int
 | 
			
		||||
	> = 0
 | 
			
		||||
>
 | 
			
		||||
srb2::String to_utf8(ItUTF32 begin, ItUTF32 end)
 | 
			
		||||
{
 | 
			
		||||
	srb2::String ret;
 | 
			
		||||
	for (auto itr = begin; itr != end; ++itr)
 | 
			
		||||
	{
 | 
			
		||||
		srb2::StaticVec<uint8_t, 4> utf8 = to_utf8(*itr);
 | 
			
		||||
		ret.append(utf8.begin(), utf8.end());
 | 
			
		||||
	}
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
srb2::String to_utf8(std::u32string_view utf32view);
 | 
			
		||||
 | 
			
		||||
// fmtlib
 | 
			
		||||
inline auto format_as(const String& str) { return fmt::string_view(static_cast<std::string_view>(str)); }
 | 
			
		||||
 | 
			
		||||
srb2::String vformat(fmt::string_view fmt, fmt::format_args args);
 | 
			
		||||
 | 
			
		||||
template <typename... T>
 | 
			
		||||
inline auto format(fmt::format_string<T...> fmt, T&&... args)
 | 
			
		||||
{
 | 
			
		||||
	return ::srb2::vformat(fmt, fmt::vargs<T...>{{args...}});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace srb2
 | 
			
		||||
 | 
			
		||||
namespace std
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
template <> struct hash<srb2::String>
 | 
			
		||||
{
 | 
			
		||||
	size_t operator()(const srb2::String& v);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace std
 | 
			
		||||
 | 
			
		||||
#endif // __cplusplus
 | 
			
		||||
 | 
			
		||||
#ifndef __cplusplus
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C"
 | 
			
		||||
{
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int Str_IsValidUTF8(const char* str);
 | 
			
		||||
// int Str_ToUTF16(uint16_t* dst, size_t dstlen, const char* src);
 | 
			
		||||
// int Str_ToUTF32(uint32_t* dst, size_t dstlen, const char* src);
 | 
			
		||||
uint32_t Str_NextCodepointFromUTF8(const char** itr);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
} // extern "C"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif // SRB2_CORE_STRING_HPP
 | 
			
		||||
| 
						 | 
				
			
			@ -14,12 +14,13 @@
 | 
			
		|||
#include <condition_variable>
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <system_error>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <tracy/tracy/Tracy.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../cxxutil.hpp"
 | 
			
		||||
#include "../m_argv.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -50,11 +51,11 @@ static void pool_executor(
 | 
			
		|||
	std::shared_ptr<std::mutex> worker_ready_mutex,
 | 
			
		||||
	std::shared_ptr<std::condition_variable> worker_ready_condvar,
 | 
			
		||||
	std::shared_ptr<ThreadPool::Queue> my_wq,
 | 
			
		||||
	std::vector<std::shared_ptr<ThreadPool::Queue>> other_wqs
 | 
			
		||||
	srb2::Vector<std::shared_ptr<ThreadPool::Queue>> other_wqs
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
	{
 | 
			
		||||
		std::string thread_name = fmt::format("Thread Pool Thread {}", thread_index);
 | 
			
		||||
		srb2::String thread_name = srb2::format("Thread Pool Thread {}", thread_index);
 | 
			
		||||
		tracy::SetThreadName(thread_name.c_str());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +134,7 @@ ThreadPool::ThreadPool(size_t threads)
 | 
			
		|||
	for (size_t i = 0; i < threads; i++)
 | 
			
		||||
	{
 | 
			
		||||
		std::shared_ptr<Queue> my_queue = work_queues_[i];
 | 
			
		||||
		std::vector<std::shared_ptr<Queue>> other_queues;
 | 
			
		||||
		srb2::Vector<std::shared_ptr<Queue>> other_queues;
 | 
			
		||||
		for (size_t j = 0; j < threads; j++)
 | 
			
		||||
		{
 | 
			
		||||
			// Order the other queues starting from the next adjacent worker
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										61
									
								
								src/core/vector.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/core/vector.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
// DR. ROBOTNIK'S RING RACERS
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// Copyright (C) 2025 by Ronald "Eidolon" Kinard
 | 
			
		||||
// Copyright (C) 2025 by Kart Krew
 | 
			
		||||
//
 | 
			
		||||
// This program is free software distributed under the
 | 
			
		||||
// terms of the GNU General Public License, version 2.
 | 
			
		||||
// See the 'LICENSE' file for more details.
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#include "vector.hpp"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include "string.h"
 | 
			
		||||
 | 
			
		||||
namespace srb2
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
AbstractVector::GrowResult AbstractVector::_realloc_mem(void* data, size_t size, size_t old_cap, size_t elem_size, Move move, size_t cap) noexcept
 | 
			
		||||
{
 | 
			
		||||
	GrowResult ret;
 | 
			
		||||
	std::allocator<uint8_t> allocator;
 | 
			
		||||
 | 
			
		||||
	if (old_cap == 0)
 | 
			
		||||
	{
 | 
			
		||||
		cap = std::max<size_t>(cap, 1);
 | 
			
		||||
		ret.data = allocator.allocate(cap * elem_size + sizeof(std::max_align_t));
 | 
			
		||||
		ret.cap = cap;
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cap = std::max<size_t>(cap, old_cap * 2);
 | 
			
		||||
	ret.data = allocator.allocate(cap * elem_size + sizeof(std::max_align_t));
 | 
			
		||||
	(move)(ret.data, data, size);
 | 
			
		||||
	allocator.deallocate(reinterpret_cast<uint8_t*>(data), old_cap * elem_size + sizeof(std::max_align_t));
 | 
			
		||||
	ret.cap = cap;
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AbstractVector::_free_mem(void* data, size_t cap, size_t elem_size) noexcept
 | 
			
		||||
{
 | 
			
		||||
	std::allocator<uint8_t> allocator;
 | 
			
		||||
	allocator.deallocate(reinterpret_cast<uint8_t*>(data), cap * elem_size + sizeof(std::max_align_t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template class Vector<bool>;
 | 
			
		||||
template class Vector<std::byte>;
 | 
			
		||||
template class Vector<uint8_t>;
 | 
			
		||||
template class Vector<uint16_t>;
 | 
			
		||||
template class Vector<uint32_t>;
 | 
			
		||||
template class Vector<uint64_t>;
 | 
			
		||||
template class Vector<int8_t>;
 | 
			
		||||
template class Vector<int16_t>;
 | 
			
		||||
template class Vector<int32_t>;
 | 
			
		||||
template class Vector<int64_t>;
 | 
			
		||||
template class Vector<String>;
 | 
			
		||||
 | 
			
		||||
} // namespace srb2
 | 
			
		||||
							
								
								
									
										366
									
								
								src/core/vector.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								src/core/vector.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,366 @@
 | 
			
		|||
// DR. ROBOTNIK'S RING RACERS
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
// Copyright (C) 2025 by Ronald "Eidolon" Kinard
 | 
			
		||||
// Copyright (C) 2025 by Kart Krew
 | 
			
		||||
//
 | 
			
		||||
// This program is free software distributed under the
 | 
			
		||||
// terms of the GNU General Public License, version 2.
 | 
			
		||||
// See the 'LICENSE' file for more details.
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#ifndef SRB2_CORE_VEC_HPP
 | 
			
		||||
#define SRB2_CORE_VEC_HPP
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <initializer_list>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
namespace srb2
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class AbstractVector
 | 
			
		||||
{
 | 
			
		||||
protected:
 | 
			
		||||
	size_t size_;
 | 
			
		||||
	size_t capacity_;
 | 
			
		||||
	size_t elem_size_;
 | 
			
		||||
	void* data_;
 | 
			
		||||
 | 
			
		||||
	using Move = void(*)(void* dst, void* src, size_t size);
 | 
			
		||||
 | 
			
		||||
	template <typename T>
 | 
			
		||||
	static void _move(void* dst, void* src, size_t size) noexcept
 | 
			
		||||
	{
 | 
			
		||||
		T* d = (T*)dst;
 | 
			
		||||
		T* s = (T*)src;
 | 
			
		||||
		for (size_t i = 0; i < size; i++)
 | 
			
		||||
		{
 | 
			
		||||
			new (&d[i]) T(std::move(s[i]));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct GrowResult
 | 
			
		||||
	{
 | 
			
		||||
		void* data;
 | 
			
		||||
		size_t cap;
 | 
			
		||||
	};
 | 
			
		||||
	static GrowResult _realloc_mem(void* data, size_t size, size_t old_cap, size_t elem_size, Move move, size_t cap) noexcept;
 | 
			
		||||
	static void _free_mem(void* data, size_t cap, size_t elem_size) noexcept;
 | 
			
		||||
 | 
			
		||||
	template <typename T>
 | 
			
		||||
	void _reserve_mem(size_t c) noexcept
 | 
			
		||||
	{
 | 
			
		||||
		if (c > capacity_)
 | 
			
		||||
		{
 | 
			
		||||
			GrowResult r = _realloc_mem(data_, size_, capacity_, elem_size_, _move<T>, c);
 | 
			
		||||
			data_ = r.data;
 | 
			
		||||
			capacity_ = r.cap;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr AbstractVector() : size_(0), capacity_(0), elem_size_(0), data_(nullptr) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
class Vector : AbstractVector
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	// iter traits
 | 
			
		||||
	using value_type = T;
 | 
			
		||||
	using size_type = size_t;
 | 
			
		||||
	using difference_type = ptrdiff_t;
 | 
			
		||||
	using reference = T&;
 | 
			
		||||
	using const_reference = const T&;
 | 
			
		||||
	using pointer = T*;
 | 
			
		||||
	using const_pointer = const T*;
 | 
			
		||||
	using iterator = T*;
 | 
			
		||||
	using const_iterator = const T*;
 | 
			
		||||
	using reverse_iterator = std::reverse_iterator<iterator>;
 | 
			
		||||
	using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 | 
			
		||||
 | 
			
		||||
	Vector() : AbstractVector()
 | 
			
		||||
	{
 | 
			
		||||
		elem_size_ = sizeof(T);
 | 
			
		||||
	}
 | 
			
		||||
	Vector(const Vector& rhs) : Vector()
 | 
			
		||||
	{
 | 
			
		||||
		*this = rhs;
 | 
			
		||||
	}
 | 
			
		||||
	Vector(Vector&& rhs) noexcept : AbstractVector()
 | 
			
		||||
	{
 | 
			
		||||
		elem_size_ = sizeof(T);
 | 
			
		||||
		*this = std::move(rhs);
 | 
			
		||||
	}
 | 
			
		||||
	~Vector()
 | 
			
		||||
	{
 | 
			
		||||
		if (data_)
 | 
			
		||||
		{
 | 
			
		||||
			for (size_type i = 0; i < size_; i++)
 | 
			
		||||
			{
 | 
			
		||||
				((T*)(data_))[(size_ - i - 1)].~T();
 | 
			
		||||
			}
 | 
			
		||||
			_free_mem(data_, capacity_, elem_size_);
 | 
			
		||||
		}
 | 
			
		||||
		data_ = nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	explicit Vector(size_type capacity) : AbstractVector()
 | 
			
		||||
	{
 | 
			
		||||
		elem_size_ = sizeof(T);
 | 
			
		||||
		_reserve_mem<T>(capacity);
 | 
			
		||||
	}
 | 
			
		||||
	Vector(std::initializer_list<T> l) : AbstractVector()
 | 
			
		||||
	{
 | 
			
		||||
		elem_size_ = sizeof(T);
 | 
			
		||||
		if (l.size() == 0)
 | 
			
		||||
		{
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		_reserve_mem<T>(l.size());
 | 
			
		||||
		for (auto itr = l.begin(); itr != l.end(); itr++)
 | 
			
		||||
		{
 | 
			
		||||
			emplace_back(*itr);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <
 | 
			
		||||
		typename It,
 | 
			
		||||
		typename std::enable_if_t<
 | 
			
		||||
			std::is_constructible_v<
 | 
			
		||||
				T,
 | 
			
		||||
				typename std::iterator_traits<It>::reference
 | 
			
		||||
			>,
 | 
			
		||||
			int
 | 
			
		||||
		> = 0
 | 
			
		||||
	>
 | 
			
		||||
	Vector(It begin, It end) : AbstractVector()
 | 
			
		||||
	{
 | 
			
		||||
		elem_size_ = sizeof(T);
 | 
			
		||||
		for (auto itr = begin; itr != end; ++itr)
 | 
			
		||||
		{
 | 
			
		||||
			push_back(*itr);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Vector& operator=(const Vector& rhs)
 | 
			
		||||
	{
 | 
			
		||||
		for (auto itr = rhs.begin(); itr != rhs.end(); itr++)
 | 
			
		||||
		{
 | 
			
		||||
			push_back(*itr);
 | 
			
		||||
		}
 | 
			
		||||
		return *this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Vector& operator=(Vector&& rhs) noexcept
 | 
			
		||||
	{
 | 
			
		||||
		std::swap(size_, rhs.size_);
 | 
			
		||||
		std::swap(capacity_, rhs.capacity_);
 | 
			
		||||
		std::swap(data_, rhs.data_);
 | 
			
		||||
		return *this;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr size_type size() const noexcept { return size_; }
 | 
			
		||||
	constexpr size_type capacity() const noexcept { return capacity_; }
 | 
			
		||||
	constexpr bool empty() const noexcept { return size_ == 0; }
 | 
			
		||||
 | 
			
		||||
	constexpr T* data() noexcept { return (T*) data_; }
 | 
			
		||||
	constexpr const T* data() const noexcept { return (const T*) data_; }
 | 
			
		||||
	constexpr T* begin() noexcept { return data(); }
 | 
			
		||||
	constexpr const T* begin() const noexcept { return data(); }
 | 
			
		||||
	constexpr T* end() noexcept { return data() + size(); }
 | 
			
		||||
	constexpr const T* end() const noexcept { return data() + size(); }
 | 
			
		||||
	constexpr const T* cbegin() const noexcept { return data(); }
 | 
			
		||||
	constexpr const T* cend() const noexcept { return end(); }
 | 
			
		||||
	constexpr auto rbegin() noexcept { return std::reverse_iterator(end()); }
 | 
			
		||||
	constexpr auto rbegin() const noexcept { return std::reverse_iterator(end()); }
 | 
			
		||||
	constexpr auto rend() noexcept { return std::reverse_iterator(begin()); }
 | 
			
		||||
	constexpr auto rend() const noexcept { return std::reverse_iterator(begin()); }
 | 
			
		||||
	constexpr auto crbegin() const noexcept { return rbegin(); }
 | 
			
		||||
	constexpr auto crend() const noexcept { return rend(); }
 | 
			
		||||
	T& front() noexcept { return data()[0]; }
 | 
			
		||||
	const T& front() const noexcept { return data()[0]; }
 | 
			
		||||
	T& back() noexcept { return data()[size() - 1]; }
 | 
			
		||||
	const T& back() const noexcept { return data()[size() - 1]; }
 | 
			
		||||
 | 
			
		||||
	void push_back(const T& t)
 | 
			
		||||
	{
 | 
			
		||||
		reserve(size() + 1);
 | 
			
		||||
		new (&data()[size()]) T(t);
 | 
			
		||||
		size_++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void push_back(T&& t)
 | 
			
		||||
	{
 | 
			
		||||
		reserve(size() + 1);
 | 
			
		||||
		new (&data()[size()]) T(std::move(t));
 | 
			
		||||
		size_++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void pop_back()
 | 
			
		||||
	{
 | 
			
		||||
		T* end = &data()[size() - 1];
 | 
			
		||||
		end->~T();
 | 
			
		||||
		size_--;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void clear() { for (auto& x : *this) x.~T(); size_ = 0; }
 | 
			
		||||
 | 
			
		||||
	void reserve(size_type c)
 | 
			
		||||
	{
 | 
			
		||||
		_reserve_mem<T>(c);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void resize(size_type s)
 | 
			
		||||
	{
 | 
			
		||||
		resize(s, T());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void resize(size_type s, const T& value)
 | 
			
		||||
	{
 | 
			
		||||
		if (s <= size())
 | 
			
		||||
		{
 | 
			
		||||
			auto itr_begin = rbegin();
 | 
			
		||||
			auto itr_end = std::prev(rend(), s);
 | 
			
		||||
			size_t count_destroyed = 0;
 | 
			
		||||
			for (auto itr = itr_begin; itr != itr_end; itr++)
 | 
			
		||||
			{
 | 
			
		||||
				itr->~T();
 | 
			
		||||
				count_destroyed++;
 | 
			
		||||
			}
 | 
			
		||||
			size_ = s;
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			reserve(s);
 | 
			
		||||
			size_type oldsize = size();
 | 
			
		||||
			size_ = s;
 | 
			
		||||
			for (auto itr = std::next(begin(), oldsize); itr != end(); itr++)
 | 
			
		||||
			{
 | 
			
		||||
				try
 | 
			
		||||
				{
 | 
			
		||||
					new (itr) T(value);
 | 
			
		||||
				}
 | 
			
		||||
				catch (...)
 | 
			
		||||
				{
 | 
			
		||||
					size_ = oldsize;
 | 
			
		||||
					std::rethrow_exception(std::current_exception());
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const T& at(size_type i) const { if (i >= size()) throw std::out_of_range("index out of range"); return data()[i]; }
 | 
			
		||||
	T& at(size_type i) { if (i >= size()) throw std::out_of_range("index out of range"); return data()[i]; }
 | 
			
		||||
 | 
			
		||||
	T& operator[](size_type i) { return data()[i]; }
 | 
			
		||||
	const T& operator[](size_type i) const { return data()[i]; }
 | 
			
		||||
 | 
			
		||||
	iterator erase(const_iterator first) { return erase(first, first + 1); }
 | 
			
		||||
	iterator erase(const_iterator first, const_iterator last)
 | 
			
		||||
	{
 | 
			
		||||
		iterator firstm = const_cast<iterator>(first);
 | 
			
		||||
		iterator lastm = const_cast<iterator>(last);
 | 
			
		||||
		if (first == last) return firstm;
 | 
			
		||||
 | 
			
		||||
		auto diff = last - first;
 | 
			
		||||
		if (last != end()) std::move(lastm, end(), firstm);
 | 
			
		||||
		resize(size_ - diff);
 | 
			
		||||
 | 
			
		||||
		return firstm;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	iterator insert(const_iterator pos, const T& value)
 | 
			
		||||
	{
 | 
			
		||||
		return insert(pos, (size_type)1, std::forward<const T&>(value));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	iterator insert(const_iterator pos, T&& value)
 | 
			
		||||
	{
 | 
			
		||||
		size_type oldsize = size();
 | 
			
		||||
		difference_type offs = pos - data();
 | 
			
		||||
		push_back(std::move(value));
 | 
			
		||||
		std::rotate(data() + offs, data() + oldsize, data() + size());
 | 
			
		||||
		return data() + offs;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	iterator insert(const_iterator pos, size_type count, const T& value)
 | 
			
		||||
	{
 | 
			
		||||
		size_type oldsize = size();
 | 
			
		||||
		difference_type offs = pos - data();
 | 
			
		||||
		reserve(oldsize + count);
 | 
			
		||||
		T* d = data();
 | 
			
		||||
		for (uint32_t i = 0; i < count; i++)
 | 
			
		||||
		{
 | 
			
		||||
			// must be copy-initialized;
 | 
			
		||||
			// value is currently uninitialized
 | 
			
		||||
			new ((T*)(&d[oldsize + i])) T(value);
 | 
			
		||||
		}
 | 
			
		||||
		size_ = oldsize + count;
 | 
			
		||||
		std::rotate(d + offs, d + oldsize, d + (oldsize + count));
 | 
			
		||||
		return data() + offs;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <
 | 
			
		||||
		class InputIt,
 | 
			
		||||
		typename std::enable_if_t<
 | 
			
		||||
			std::is_constructible<
 | 
			
		||||
				T,
 | 
			
		||||
				typename std::iterator_traits<InputIt>::reference
 | 
			
		||||
			>::value,
 | 
			
		||||
			int
 | 
			
		||||
		> = 0
 | 
			
		||||
	>
 | 
			
		||||
	iterator insert(const_iterator pos, InputIt first, InputIt last)
 | 
			
		||||
	{
 | 
			
		||||
		size_type oldsize = size();
 | 
			
		||||
		difference_type offs = pos - data();
 | 
			
		||||
 | 
			
		||||
		size_type count = 0;
 | 
			
		||||
		while (first != last)
 | 
			
		||||
		{
 | 
			
		||||
			push_back(*first);
 | 
			
		||||
			++first;
 | 
			
		||||
			++count;
 | 
			
		||||
		}
 | 
			
		||||
		std::rotate(data() + offs, data() + oldsize, data() + (oldsize + count));
 | 
			
		||||
		return data() + offs;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename... A>
 | 
			
		||||
	iterator emplace(const_iterator position, A&&... args)
 | 
			
		||||
	{
 | 
			
		||||
		return insert(position, T(std::forward<A>(args)...));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename... A>
 | 
			
		||||
	T& emplace_back(A&&... args)
 | 
			
		||||
	{
 | 
			
		||||
		reserve(size() + 1);
 | 
			
		||||
		new (&data()[size()]) T(std::forward<A>(args)...);
 | 
			
		||||
		size_++;
 | 
			
		||||
		return (*this)[size_ - 1];
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class String;
 | 
			
		||||
 | 
			
		||||
extern template class Vector<bool>;
 | 
			
		||||
extern template class Vector<std::byte>;
 | 
			
		||||
extern template class Vector<uint8_t>;
 | 
			
		||||
extern template class Vector<uint16_t>;
 | 
			
		||||
extern template class Vector<uint32_t>;
 | 
			
		||||
extern template class Vector<uint64_t>;
 | 
			
		||||
extern template class Vector<int8_t>;
 | 
			
		||||
extern template class Vector<int16_t>;
 | 
			
		||||
extern template class Vector<int32_t>;
 | 
			
		||||
extern template class Vector<int64_t>;
 | 
			
		||||
extern template class Vector<String>;
 | 
			
		||||
 | 
			
		||||
} // namespace srb2
 | 
			
		||||
 | 
			
		||||
#endif // SRB2_CORE_VEC_HPP
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +16,6 @@
 | 
			
		|||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
#include <tcb/span.hpp>
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
 | 
			
		||||
#include "doomdef.h"
 | 
			
		||||
#include "doomtype.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +49,7 @@
 | 
			
		|||
#include "md5.h" // demo checksums
 | 
			
		||||
#include "p_saveg.h" // savebuffer_t
 | 
			
		||||
#include "g_party.h"
 | 
			
		||||
#include "core/json.hpp"
 | 
			
		||||
 | 
			
		||||
// SRB2Kart
 | 
			
		||||
#include "d_netfil.h" // nameonly
 | 
			
		||||
| 
						 | 
				
			
			@ -2437,17 +2437,18 @@ void G_BeginRecording(void)
 | 
			
		|||
void srb2::write_current_demo_standings(const srb2::StandingsJson& standings)
 | 
			
		||||
{
 | 
			
		||||
	using namespace srb2;
 | 
			
		||||
	using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
	// TODO populate standings data
 | 
			
		||||
 | 
			
		||||
	std::vector<uint8_t> ubjson = json::to_ubjson(standings);
 | 
			
		||||
	JsonValue value { JsonObject() };
 | 
			
		||||
	to_json(value, standings);
 | 
			
		||||
	Vector<std::byte> ubjson = value.to_ubjson();
 | 
			
		||||
	uint32_t bytes = ubjson.size();
 | 
			
		||||
 | 
			
		||||
	WRITEUINT8(demobuf.p, DW_STANDING2);
 | 
			
		||||
 | 
			
		||||
	WRITEUINT32(demobuf.p, bytes);
 | 
			
		||||
	WRITEMEM(demobuf.p, ubjson.data(), bytes);
 | 
			
		||||
	WRITEMEM(demobuf.p, (UINT8*)ubjson.data(), bytes);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void srb2::write_current_demo_end_marker()
 | 
			
		||||
| 
						 | 
				
			
			@ -2615,12 +2616,12 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
 | 
			
		|||
static bool load_ubjson_standing(menudemo_t* pdemo, tcb::span<std::byte> slice, tcb::span<democharlist_t> demoskins)
 | 
			
		||||
{
 | 
			
		||||
	using namespace srb2;
 | 
			
		||||
	using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
	StandingsJson js;
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		js = json::from_ubjson(slice).template get<StandingsJson>();
 | 
			
		||||
		JsonValue value { JsonValue::from_ubjson(tcb::as_bytes(slice)) };
 | 
			
		||||
		from_json(value, js);
 | 
			
		||||
	}
 | 
			
		||||
	catch (...)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										17
									
								
								src/g_demo.h
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								src/g_demo.h
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -21,10 +21,9 @@
 | 
			
		|||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
#include "core/json.hpp"
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "core/vector.hpp"
 | 
			
		||||
 | 
			
		||||
// Modern json formats
 | 
			
		||||
namespace srb2
 | 
			
		||||
| 
						 | 
				
			
			@ -32,12 +31,12 @@ namespace srb2
 | 
			
		|||
struct StandingJson
 | 
			
		||||
{
 | 
			
		||||
	uint8_t ranking;
 | 
			
		||||
	std::string name;
 | 
			
		||||
	String name;
 | 
			
		||||
	uint8_t demoskin;
 | 
			
		||||
	std::string skincolor;
 | 
			
		||||
	String skincolor;
 | 
			
		||||
	uint32_t timeorscore;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
		StandingJson,
 | 
			
		||||
		ranking,
 | 
			
		||||
		name,
 | 
			
		||||
| 
						 | 
				
			
			@ -48,9 +47,9 @@ struct StandingJson
 | 
			
		|||
};
 | 
			
		||||
struct StandingsJson
 | 
			
		||||
{
 | 
			
		||||
	std::vector<StandingJson> standings;
 | 
			
		||||
	Vector<StandingJson> standings;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(StandingsJson, standings)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(StandingsJson, standings)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void write_current_demo_standings(const StandingsJson& standings);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,6 @@
 | 
			
		|||
#include "z_zone.h"
 | 
			
		||||
 | 
			
		||||
namespace fs = std::filesystem;
 | 
			
		||||
using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
#define GD_VERSION_MAJOR (0xBA5ED321)
 | 
			
		||||
#define GD_VERSION_MINOR (1)
 | 
			
		||||
| 
						 | 
				
			
			@ -137,13 +136,13 @@ void srb2::save_ng_gamedata()
 | 
			
		|||
		skin_t& memskin = skins[i];
 | 
			
		||||
 | 
			
		||||
		auto skin = skintojson(&memskin.records);
 | 
			
		||||
		std::string name = std::string(memskin.name);
 | 
			
		||||
		srb2::String name { memskin.name };
 | 
			
		||||
		ng.skins[name] = std::move(skin);
 | 
			
		||||
	}
 | 
			
		||||
	for (auto unloadedskin = unloadedskins; unloadedskin; unloadedskin = unloadedskin->next)
 | 
			
		||||
	{
 | 
			
		||||
		auto skin = skintojson(&unloadedskin->records);
 | 
			
		||||
		std::string name = std::string(unloadedskin->name);
 | 
			
		||||
		srb2::String name { unloadedskin->name };
 | 
			
		||||
		ng.skins[name] = std::move(skin);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -175,13 +174,13 @@ void srb2::save_ng_gamedata()
 | 
			
		|||
	for (int i = 0; i < nummapheaders; i++)
 | 
			
		||||
	{
 | 
			
		||||
		auto map = maptojson(&mapheaderinfo[i]->records);
 | 
			
		||||
		std::string lumpname = std::string(mapheaderinfo[i]->lumpname);
 | 
			
		||||
		srb2::String lumpname { mapheaderinfo[i]->lumpname };
 | 
			
		||||
		ng.maps[lumpname] = std::move(map);
 | 
			
		||||
	}
 | 
			
		||||
	for (auto unloadedmap = unloadedmapheaders; unloadedmap; unloadedmap = unloadedmap->next)
 | 
			
		||||
	{
 | 
			
		||||
		auto map = maptojson(&unloadedmap->records);
 | 
			
		||||
		std::string lumpname = std::string(unloadedmap->lumpname);
 | 
			
		||||
		srb2::String lumpname { unloadedmap->lumpname };
 | 
			
		||||
		ng.maps[lumpname] = std::move(map);
 | 
			
		||||
	}
 | 
			
		||||
	for (int i = 0; i < gamedata->numspraycans; i++)
 | 
			
		||||
| 
						 | 
				
			
			@ -194,7 +193,7 @@ void srb2::save_ng_gamedata()
 | 
			
		|||
		{
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		spraycan.color = std::string(skincolors[can->col].name);
 | 
			
		||||
		spraycan.color = String(skincolors[can->col].name);
 | 
			
		||||
 | 
			
		||||
		if (can->map == NEXTMAP_INVALID)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -213,7 +212,7 @@ void srb2::save_ng_gamedata()
 | 
			
		|||
		{
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		spraycan.map = std::string(mapheader->lumpname);
 | 
			
		||||
		spraycan.map = String(mapheader->lumpname);
 | 
			
		||||
		ng.spraycans.emplace_back(std::move(spraycan));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -230,11 +229,11 @@ void srb2::save_ng_gamedata()
 | 
			
		|||
			skinreference_t& skinref = windata[i].best_skin;
 | 
			
		||||
			if (skinref.unloaded)
 | 
			
		||||
			{
 | 
			
		||||
				newrecords.bestskin = std::string(skinref.unloaded->name);
 | 
			
		||||
				newrecords.bestskin = String(skinref.unloaded->name);
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				newrecords.bestskin = std::string(skins[skinref.id].name);
 | 
			
		||||
				newrecords.bestskin = String(skins[skinref.id].name);
 | 
			
		||||
			}
 | 
			
		||||
			newrecords.gotemerald = windata[i].got_emerald;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -252,7 +251,7 @@ void srb2::save_ng_gamedata()
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		auto cupdata = cuptojson(cup->windata);
 | 
			
		||||
		cupdata.name = std::string(cup->name);
 | 
			
		||||
		cupdata.name = String(cup->name);
 | 
			
		||||
		ng.cups[cupdata.name] = std::move(cupdata);
 | 
			
		||||
	}
 | 
			
		||||
	for (auto unloadedcup = unloadedcupheaders; unloadedcup; unloadedcup = unloadedcup->next)
 | 
			
		||||
| 
						 | 
				
			
			@ -263,7 +262,7 @@ void srb2::save_ng_gamedata()
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		auto cupdata = cuptojson(unloadedcup->windata);
 | 
			
		||||
		cupdata.name = std::string(unloadedcup->name);
 | 
			
		||||
		cupdata.name = String(unloadedcup->name);
 | 
			
		||||
		ng.cups[cupdata.name] = std::move(cupdata);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -273,17 +272,19 @@ void srb2::save_ng_gamedata()
 | 
			
		|||
 | 
			
		||||
		cupheader_t* cup = gamedata->sealedswaps[i];
 | 
			
		||||
 | 
			
		||||
		sealedswap.name = std::string(cup->name);
 | 
			
		||||
		sealedswap.name = String(cup->name);
 | 
			
		||||
 | 
			
		||||
		ng.sealedswaps.emplace_back(std::move(sealedswap));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::string gamedataname_s {gamedatafilename};
 | 
			
		||||
	fs::path savepath {fmt::format("{}/{}", srb2home, gamedataname_s)};
 | 
			
		||||
	fs::path baksavepath {fmt::format("{}/{}.bak", srb2home, gamedataname_s)};
 | 
			
		||||
 | 
			
		||||
	json ngdata_json = ng;
 | 
			
		||||
	String gamedataname_s {gamedatafilename};
 | 
			
		||||
	String savepath_string = srb2::format("{}/{}", srb2home, gamedataname_s);
 | 
			
		||||
	String baksavepath_string = srb2::format("{}/{}.bak", srb2home, gamedataname_s);
 | 
			
		||||
	fs::path savepath { static_cast<std::string_view>(savepath_string) };
 | 
			
		||||
	fs::path baksavepath { static_cast<std::string_view>(srb2::format("{}/{}.bak", srb2home, gamedataname_s)) };
 | 
			
		||||
 | 
			
		||||
	JsonValue ngdata_json { JsonObject() };
 | 
			
		||||
	to_json(ngdata_json, ng);
 | 
			
		||||
 | 
			
		||||
	if (fs::exists(savepath))
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -300,7 +301,7 @@ void srb2::save_ng_gamedata()
 | 
			
		|||
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		std::string savepathstring = savepath.string();
 | 
			
		||||
		String savepathstring = savepath.string();
 | 
			
		||||
		srb2::io::FileStream file {savepathstring, srb2::io::FileStreamMode::kWrite};
 | 
			
		||||
 | 
			
		||||
		// The header is necessary to validate during loading.
 | 
			
		||||
| 
						 | 
				
			
			@ -308,7 +309,7 @@ void srb2::save_ng_gamedata()
 | 
			
		|||
		srb2::io::write(static_cast<uint8_t>(GD_VERSION_MINOR), file); // minor/flags
 | 
			
		||||
		srb2::io::write(static_cast<uint8_t>(gamedata->evercrashed), file); // dirty (crash recovery)
 | 
			
		||||
 | 
			
		||||
		std::vector<uint8_t> ubjson = json::to_ubjson(ng);
 | 
			
		||||
		srb2::Vector<std::byte> ubjson = ngdata_json.to_ubjson();
 | 
			
		||||
		srb2::io::write_exact(file, tcb::as_bytes(tcb::make_span(ubjson)));
 | 
			
		||||
		file.close();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -382,7 +383,7 @@ void srb2::load_ng_gamedata()
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::string datapath {fmt::format("{}/{}", srb2home, gamedatafilename)};
 | 
			
		||||
	String datapath {srb2::format("{}/{}", srb2home, gamedatafilename)};
 | 
			
		||||
 | 
			
		||||
	srb2::io::BufferedInputStream<srb2::io::FileStream> bis;
 | 
			
		||||
	try
 | 
			
		||||
| 
						 | 
				
			
			@ -419,15 +420,13 @@ void srb2::load_ng_gamedata()
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::vector<std::byte> remainder = srb2::io::read_to_vec(bis);
 | 
			
		||||
	srb2::Vector<std::byte> remainder = srb2::io::read_to_vec(bis);
 | 
			
		||||
 | 
			
		||||
	GamedataJson js;
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		// safety: std::byte repr is always uint8_t 1-byte aligned
 | 
			
		||||
		tcb::span<uint8_t> remainder_as_u8 = tcb::span((uint8_t*)remainder.data(), remainder.size());
 | 
			
		||||
		json parsed = json::from_ubjson(remainder_as_u8);
 | 
			
		||||
		js = parsed.template get<GamedataJson>();
 | 
			
		||||
		JsonValue parsed = JsonValue::from_ubjson(remainder);
 | 
			
		||||
		from_json(parsed, js);
 | 
			
		||||
	}
 | 
			
		||||
	catch (const std::exception& ex)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -542,7 +541,7 @@ void srb2::load_ng_gamedata()
 | 
			
		|||
			gamedata->challengegrid = static_cast<uint16_t*>(Z_Malloc(
 | 
			
		||||
				(gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT * sizeof(UINT16)),
 | 
			
		||||
				PU_STATIC, NULL));
 | 
			
		||||
			for (size_t i = 0; i < std::min((size_t)(gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT), js.challengegrid.grid.size()); i++)
 | 
			
		||||
			for (size_t i = 0; i < std::min<size_t>((size_t)(gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT), js.challengegrid.grid.size()); i++)
 | 
			
		||||
			{
 | 
			
		||||
				uint16_t gridvalue = js.challengegrid.grid[i];
 | 
			
		||||
				gamedata->challengegrid[i] = gridvalue;
 | 
			
		||||
| 
						 | 
				
			
			@ -745,7 +744,7 @@ void srb2::load_ng_gamedata()
 | 
			
		|||
		// Find the loaded cup
 | 
			
		||||
		for (cup = kartcupheaders; cup; cup = cup->next)
 | 
			
		||||
		{
 | 
			
		||||
			std::string cupname = std::string(cup->name);
 | 
			
		||||
			String cupname { cup->name };
 | 
			
		||||
			if (cupname == cuppair.first)
 | 
			
		||||
			{
 | 
			
		||||
				break;
 | 
			
		||||
| 
						 | 
				
			
			@ -770,7 +769,7 @@ void srb2::load_ng_gamedata()
 | 
			
		|||
			}
 | 
			
		||||
			for (auto unloadedskin = unloadedskins; unloadedskin; unloadedskin = unloadedskin->next)
 | 
			
		||||
			{
 | 
			
		||||
				std::string skinname = std::string(unloadedskin->name);
 | 
			
		||||
				String skinname { unloadedskin->name };
 | 
			
		||||
				if (skinname == cuppair.second.records[j].bestskin)
 | 
			
		||||
				{
 | 
			
		||||
					skinreference_t ref {};
 | 
			
		||||
| 
						 | 
				
			
			@ -826,7 +825,7 @@ void srb2::load_ng_gamedata()
 | 
			
		|||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			std::string cupname = std::string(cup->name);
 | 
			
		||||
			String cupname { cup->name };
 | 
			
		||||
			if (cupname == js.sealedswaps[i].name)
 | 
			
		||||
			{
 | 
			
		||||
				break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,13 +13,12 @@
 | 
			
		|||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
#include "core/json.hpp"
 | 
			
		||||
#include "core/hash_map.hpp"
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "core/vector.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +38,7 @@ struct GamedataPlaytimeJson final
 | 
			
		|||
	uint32_t statistics;
 | 
			
		||||
	uint32_t tumble;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
		GamedataPlaytimeJson,
 | 
			
		||||
		total,
 | 
			
		||||
		netgame,
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +59,7 @@ struct GamedataRingsJson final
 | 
			
		|||
{
 | 
			
		||||
	uint32_t total;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataRingsJson, total)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataRingsJson, total)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataRoundsJson final
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +70,7 @@ struct GamedataRoundsJson final
 | 
			
		|||
	uint32_t special;
 | 
			
		||||
	uint32_t custom;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataRoundsJson, race, battle, prisons, special, custom)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataRoundsJson, race, battle, prisons, special, custom)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataChallengeKeysJson final
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +80,7 @@ struct GamedataChallengeKeysJson final
 | 
			
		|||
	uint16_t keyspending;
 | 
			
		||||
	uint16_t chaokeys;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataChallengeKeysJson, pendingkeyrounds, pendingkeyroundoffset, keyspending, chaokeys)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataChallengeKeysJson, pendingkeyrounds, pendingkeyroundoffset, keyspending, chaokeys)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataMilestonesJson final
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +97,7 @@ struct GamedataMilestonesJson final
 | 
			
		|||
	bool sealedswapalerted;
 | 
			
		||||
	bool tutorialdone;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
		GamedataMilestonesJson,
 | 
			
		||||
		gonerlevel,
 | 
			
		||||
		everloadedaddon,
 | 
			
		||||
| 
						 | 
				
			
			@ -119,15 +118,15 @@ struct GamedataPrisonEggPickupsJson final
 | 
			
		|||
	uint16_t thisprisoneggpickup;
 | 
			
		||||
	uint16_t prisoneggstothispickup;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataPrisonEggPickupsJson, thisprisoneggpickup, prisoneggstothispickup)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataPrisonEggPickupsJson, thisprisoneggpickup, prisoneggstothispickup)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataChallengeGridJson final
 | 
			
		||||
{
 | 
			
		||||
	uint32_t width;
 | 
			
		||||
	std::vector<uint16_t> grid;
 | 
			
		||||
	Vector<uint16_t> grid;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataChallengeGridJson, width, grid)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataChallengeGridJson, width, grid)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataSkinRecordsPlaytimeJson final
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +139,7 @@ struct GamedataSkinRecordsPlaytimeJson final
 | 
			
		|||
	uint32_t custom;
 | 
			
		||||
	uint32_t tumble;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
		GamedataSkinRecordsPlaytimeJson,
 | 
			
		||||
		total,
 | 
			
		||||
		race,
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +157,7 @@ struct GamedataSkinRecordsJson final
 | 
			
		|||
	uint32_t rounds;
 | 
			
		||||
	GamedataSkinRecordsPlaytimeJson time;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
		GamedataSkinRecordsJson,
 | 
			
		||||
		wins,
 | 
			
		||||
		rounds,
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +169,7 @@ struct GamedataSkinJson final
 | 
			
		|||
{
 | 
			
		||||
	GamedataSkinRecordsJson records;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSkinJson, records)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSkinJson, records)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataMapVisitedJson final
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +180,7 @@ struct GamedataMapVisitedJson final
 | 
			
		|||
	bool spbattack;
 | 
			
		||||
	bool mysticmelody;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapVisitedJson, visited, beaten, encore, spbattack, mysticmelody)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapVisitedJson, visited, beaten, encore, spbattack, mysticmelody)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataMapStatsTimeAttackJson final
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +188,7 @@ struct GamedataMapStatsTimeAttackJson final
 | 
			
		|||
	uint32_t besttime;
 | 
			
		||||
	uint32_t bestlap;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapStatsTimeAttackJson, besttime, bestlap)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapStatsTimeAttackJson, besttime, bestlap)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataMapStatsSpbAttackJson final
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +196,7 @@ struct GamedataMapStatsSpbAttackJson final
 | 
			
		|||
	uint32_t besttime;
 | 
			
		||||
	uint32_t bestlap;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapStatsSpbAttackJson, besttime, bestlap)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapStatsSpbAttackJson, besttime, bestlap)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataMapStatsPlaytimeJson final
 | 
			
		||||
| 
						 | 
				
			
			@ -212,7 +211,7 @@ struct GamedataMapStatsPlaytimeJson final
 | 
			
		|||
	uint32_t timeattack;
 | 
			
		||||
	uint32_t spbattack;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
		GamedataMapStatsPlaytimeJson,
 | 
			
		||||
		total,
 | 
			
		||||
		netgame,
 | 
			
		||||
| 
						 | 
				
			
			@ -232,7 +231,7 @@ struct GamedataMapStatsJson final
 | 
			
		|||
	GamedataMapStatsSpbAttackJson spbattack;
 | 
			
		||||
	GamedataMapStatsPlaytimeJson time;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
		GamedataMapStatsJson,
 | 
			
		||||
		timeattack,
 | 
			
		||||
		spbattack,
 | 
			
		||||
| 
						 | 
				
			
			@ -245,15 +244,15 @@ struct GamedataMapJson final
 | 
			
		|||
	GamedataMapVisitedJson visited;
 | 
			
		||||
	GamedataMapStatsJson stats;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapJson, visited, stats)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataMapJson, visited, stats)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataSprayCanJson final
 | 
			
		||||
{
 | 
			
		||||
	std::string map;
 | 
			
		||||
	std::string color;
 | 
			
		||||
	String map;
 | 
			
		||||
	String color;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSprayCanJson, map, color)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSprayCanJson, map, color)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataCupRecordsJson final
 | 
			
		||||
| 
						 | 
				
			
			@ -261,24 +260,24 @@ struct GamedataCupRecordsJson final
 | 
			
		|||
	uint8_t bestplacement;
 | 
			
		||||
	uint8_t bestgrade;
 | 
			
		||||
	bool gotemerald;
 | 
			
		||||
	std::string bestskin;
 | 
			
		||||
	String bestskin;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataCupRecordsJson, bestplacement, bestgrade, gotemerald, bestskin)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataCupRecordsJson, bestplacement, bestgrade, gotemerald, bestskin)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataCupJson final
 | 
			
		||||
{
 | 
			
		||||
	std::string name;
 | 
			
		||||
	std::vector<GamedataCupRecordsJson> records;
 | 
			
		||||
	String name;
 | 
			
		||||
	Vector<GamedataCupRecordsJson> records;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataCupJson, name, records)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataCupJson, name, records)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataSealedSwapJson final
 | 
			
		||||
{
 | 
			
		||||
	std::string name;
 | 
			
		||||
	String name;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSealedSwapJson, name)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSealedSwapJson, name)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct GamedataJson final
 | 
			
		||||
| 
						 | 
				
			
			@ -290,19 +289,19 @@ struct GamedataJson final
 | 
			
		|||
	GamedataMilestonesJson milestones;
 | 
			
		||||
	GamedataPrisonEggPickupsJson prisons;
 | 
			
		||||
	uint32_t tafolderhash;
 | 
			
		||||
	std::vector<bool> emblems;
 | 
			
		||||
	std::vector<bool> unlockables;
 | 
			
		||||
	std::vector<bool> unlockpending;
 | 
			
		||||
	std::vector<bool> conditionsets;
 | 
			
		||||
	Vector<bool> emblems;
 | 
			
		||||
	Vector<bool> unlockables;
 | 
			
		||||
	Vector<bool> unlockpending;
 | 
			
		||||
	Vector<bool> conditionsets;
 | 
			
		||||
	GamedataChallengeGridJson challengegrid;
 | 
			
		||||
	uint32_t timesBeaten;
 | 
			
		||||
	std::unordered_map<std::string, GamedataSkinJson> skins;
 | 
			
		||||
	std::unordered_map<std::string, GamedataMapJson> maps;
 | 
			
		||||
	std::vector<GamedataSprayCanJson> spraycans;
 | 
			
		||||
	std::unordered_map<std::string, GamedataCupJson> cups;
 | 
			
		||||
	std::vector<GamedataSealedSwapJson> sealedswaps;
 | 
			
		||||
	HashMap<String, GamedataSkinJson> skins;
 | 
			
		||||
	HashMap<String, GamedataMapJson> maps;
 | 
			
		||||
	Vector<GamedataSprayCanJson> spraycans;
 | 
			
		||||
	HashMap<String, GamedataCupJson> cups;
 | 
			
		||||
	Vector<GamedataSealedSwapJson> sealedswaps;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
		GamedataJson,
 | 
			
		||||
		playtime,
 | 
			
		||||
		rings,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -85,8 +85,8 @@ void K_DrawInputDisplay(float x, float y, INT32 flags, char mode, UINT8 pid, boo
 | 
			
		|||
 | 
			
		||||
	const ticcmd_t& cmd = players[displayplayers[pid]].cmd;
 | 
			
		||||
	const boolean analog = (mode == '4' || mode == '5') ? players[displayplayers[pid]].analoginput : false;
 | 
			
		||||
	const std::string prefix = fmt::format("PR{}", mode);
 | 
			
		||||
	auto gfx = [&](auto format, auto&&... args) { return prefix + fmt::format(format, args...); };
 | 
			
		||||
	srb2::String prefix = srb2::format("PR{}", mode);
 | 
			
		||||
	auto gfx = [&](auto format, auto&&... args) { return prefix + srb2::format(format, args...); };
 | 
			
		||||
	auto but = [&](char key, INT32 gc, UINT32 bt)
 | 
			
		||||
	{
 | 
			
		||||
		bool press = local ? G_PlayerInputAnalog(pid, gc, guessinput) : ((cmd.buttons & bt) == bt);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -165,7 +165,7 @@ void K_drawSpectatorHUD(boolean director)
 | 
			
		|||
 | 
			
		||||
	if (player)
 | 
			
		||||
	{
 | 
			
		||||
		std::string label = [player]
 | 
			
		||||
		srb2::String label = [player]
 | 
			
		||||
		{
 | 
			
		||||
			if (player->flashing)
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +183,7 @@ void K_drawSpectatorHUD(boolean director)
 | 
			
		|||
 | 
			
		||||
		if (cv_maxplayers.value)
 | 
			
		||||
		{
 | 
			
		||||
			label += fmt::format(" [{}/{}]", numingame, cv_maxplayers.value);
 | 
			
		||||
			label += srb2::format(" [{}/{}]", numingame, cv_maxplayers.value);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		list.insert({{label.c_str(), "<l_animated>"}});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,9 +13,9 @@
 | 
			
		|||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "../core/hash_map.hpp"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../rhi/rhi.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2::hwr2
 | 
			
		||||
| 
						 | 
				
			
			@ -77,10 +77,10 @@ class MainPaletteManager final
 | 
			
		|||
	rhi::Handle<rhi::Texture> encore_lighttable_;
 | 
			
		||||
	rhi::Handle<rhi::Texture> default_colormap_;
 | 
			
		||||
 | 
			
		||||
	std::unordered_map<const uint8_t*, rhi::Handle<rhi::Texture>> colormaps_;
 | 
			
		||||
	std::unordered_map<const uint8_t*, rhi::Handle<rhi::Texture>> lighttables_;
 | 
			
		||||
	std::vector<const uint8_t*> colormaps_to_upload_;
 | 
			
		||||
	std::vector<const uint8_t*> lighttables_to_upload_;
 | 
			
		||||
	srb2::HashMap<const uint8_t*, rhi::Handle<rhi::Texture>> colormaps_;
 | 
			
		||||
	srb2::HashMap<const uint8_t*, rhi::Handle<rhi::Texture>> lighttables_;
 | 
			
		||||
	srb2::Vector<const uint8_t*> colormaps_to_upload_;
 | 
			
		||||
	srb2::Vector<const uint8_t*> lighttables_to_upload_;
 | 
			
		||||
 | 
			
		||||
	void upload_palette(rhi::Rhi& rhi);
 | 
			
		||||
	void upload_lighttables(rhi::Rhi& rhi);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -72,7 +72,7 @@ rhi::Rect srb2::hwr2::trimmed_patch_dimensions(const patch_t* patch)
 | 
			
		|||
	return {minx, miny, static_cast<uint32_t>(maxx - minx), static_cast<uint32_t>(maxy - miny)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void srb2::hwr2::convert_patch_to_trimmed_rg8_pixels(const patch_t* patch, std::vector<uint8_t>& out)
 | 
			
		||||
void srb2::hwr2::convert_patch_to_trimmed_rg8_pixels(const patch_t* patch, srb2::Vector<uint8_t>& out)
 | 
			
		||||
{
 | 
			
		||||
	Rect trimmed_rect = srb2::hwr2::trimmed_patch_dimensions(patch);
 | 
			
		||||
	if (trimmed_rect.w % 2 > 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -299,7 +299,7 @@ void PatchAtlasCache::pack(Rhi& rhi)
 | 
			
		|||
	SRB2_ASSERT(ready_for_lookup());
 | 
			
		||||
 | 
			
		||||
	// Upload atlased patches
 | 
			
		||||
	std::vector<uint8_t> patch_data;
 | 
			
		||||
	srb2::Vector<uint8_t> patch_data;
 | 
			
		||||
	for (const patch_t* patch_to_upload : patches_to_upload_)
 | 
			
		||||
	{
 | 
			
		||||
		srb2::NotNull<PatchAtlas*> atlas = find_patch(patch_to_upload);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,12 +14,12 @@
 | 
			
		|||
#include <cstdint>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <tcb/span.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../core/hash_map.hpp"
 | 
			
		||||
#include "../core/hash_set.hpp"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../r_defs.h"
 | 
			
		||||
#include "../rhi/rhi.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ private:
 | 
			
		|||
	rhi::Handle<rhi::Texture> tex_;
 | 
			
		||||
	uint32_t size_;
 | 
			
		||||
 | 
			
		||||
	std::unordered_map<const patch_t*, Entry> entries_;
 | 
			
		||||
	srb2::HashMap<const patch_t*, Entry> entries_;
 | 
			
		||||
 | 
			
		||||
	std::unique_ptr<stbrp_context> rp_ctx {nullptr};
 | 
			
		||||
	std::unique_ptr<stbrp_node[]> rp_nodes {nullptr};
 | 
			
		||||
| 
						 | 
				
			
			@ -84,11 +84,11 @@ public:
 | 
			
		|||
/// drawing things like sprites and 2D elements.
 | 
			
		||||
class PatchAtlasCache
 | 
			
		||||
{
 | 
			
		||||
	std::vector<PatchAtlas> atlases_;
 | 
			
		||||
	std::unordered_map<const patch_t*, size_t> patch_lookup_;
 | 
			
		||||
	srb2::Vector<PatchAtlas> atlases_;
 | 
			
		||||
	srb2::HashMap<const patch_t*, size_t> patch_lookup_;
 | 
			
		||||
 | 
			
		||||
	std::unordered_set<const patch_t*> patches_to_pack_;
 | 
			
		||||
	std::unordered_set<const patch_t*> patches_to_upload_;
 | 
			
		||||
	srb2::HashSet<const patch_t*> patches_to_pack_;
 | 
			
		||||
	srb2::HashSet<const patch_t*> patches_to_upload_;
 | 
			
		||||
 | 
			
		||||
	uint32_t tex_size_ = 2048;
 | 
			
		||||
	size_t max_textures_ = 2;
 | 
			
		||||
| 
						 | 
				
			
			@ -135,7 +135,7 @@ rhi::Rect trimmed_patch_dimensions(const patch_t* patch);
 | 
			
		|||
/// during upload, but required for the RHI device's Unpack Alignment of 4 bytes.
 | 
			
		||||
/// @param patch the patch to convert
 | 
			
		||||
/// @param out the output vector, cleared before writing.
 | 
			
		||||
void convert_patch_to_trimmed_rg8_pixels(const patch_t* patch, std::vector<uint8_t>& out);
 | 
			
		||||
void convert_patch_to_trimmed_rg8_pixels(const patch_t* patch, srb2::Vector<uint8_t>& out);
 | 
			
		||||
 | 
			
		||||
} // namespace srb2::hwr2
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,12 +10,11 @@
 | 
			
		|||
 | 
			
		||||
#include "postprocess_wipe.hpp"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <glm/gtc/matrix_transform.hpp>
 | 
			
		||||
#include <tcb/span.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../f_finale.h"
 | 
			
		||||
#include "../w_wad.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +105,7 @@ void PostprocessWipePass::prepass(Rhi& rhi)
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::string lumpname = fmt::format(FMT_STRING("FADE{:02d}{:02d}"), wipe_type, wipe_frame);
 | 
			
		||||
	String lumpname = format(FMT_STRING("FADE{:02d}{:02d}"), wipe_type, wipe_frame);
 | 
			
		||||
	lumpnum_t mask_lump = W_CheckNumForName(lumpname.c_str());
 | 
			
		||||
	if (mask_lump == LUMPERROR)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -181,7 +181,7 @@ Handle<Texture> FlatTextureManager::find_or_create_indexed(Rhi& rhi, lumpnum_t l
 | 
			
		|||
	});
 | 
			
		||||
	flats_.insert({lump, new_tex});
 | 
			
		||||
 | 
			
		||||
	std::vector<std::array<uint8_t, 2>> flat_data;
 | 
			
		||||
	srb2::Vector<std::array<uint8_t, 2>> flat_data;
 | 
			
		||||
	std::size_t lump_length = W_LumpLength(lump);
 | 
			
		||||
	flat_data.reserve(flat_size * flat_size);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,10 +11,10 @@
 | 
			
		|||
#ifndef __SRB2_HWR2_RESOURCE_MANAGEMENT_HPP__
 | 
			
		||||
#define __SRB2_HWR2_RESOURCE_MANAGEMENT_HPP__
 | 
			
		||||
 | 
			
		||||
#include "../core/hash_map.hpp"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../rhi/rhi.hpp"
 | 
			
		||||
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
namespace srb2::hwr2
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,8 +27,8 @@ class PaletteManager
 | 
			
		|||
#endif
 | 
			
		||||
	rhi::Handle<rhi::Texture> default_colormap_;
 | 
			
		||||
 | 
			
		||||
	std::unordered_map<const uint8_t*, rhi::Handle<rhi::Texture>> colormaps_;
 | 
			
		||||
	std::unordered_map<const uint8_t*, rhi::Handle<rhi::Texture>> lighttables_;
 | 
			
		||||
	srb2::HashMap<const uint8_t*, rhi::Handle<rhi::Texture>> colormaps_;
 | 
			
		||||
	srb2::HashMap<const uint8_t*, rhi::Handle<rhi::Texture>> lighttables_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	PaletteManager();
 | 
			
		||||
| 
						 | 
				
			
			@ -67,9 +67,9 @@ from patch_t.
 | 
			
		|||
/// @brief Manages textures corresponding to specific flats indexed by lump number.
 | 
			
		||||
class FlatTextureManager
 | 
			
		||||
{
 | 
			
		||||
	std::unordered_map<lumpnum_t, rhi::Handle<rhi::Texture>> flats_;
 | 
			
		||||
	std::vector<lumpnum_t> to_upload_;
 | 
			
		||||
	std::vector<rhi::Handle<rhi::Texture>> disposed_textures_;
 | 
			
		||||
	srb2::HashMap<lumpnum_t, rhi::Handle<rhi::Texture>> flats_;
 | 
			
		||||
	srb2::Vector<lumpnum_t> to_upload_;
 | 
			
		||||
	srb2::Vector<rhi::Handle<rhi::Texture>> disposed_textures_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	FlatTextureManager();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,11 +11,11 @@
 | 
			
		|||
#include "twodee_renderer.hpp"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
 | 
			
		||||
#include <stb_rect_pack.h>
 | 
			
		||||
#include <glm/gtc/matrix_transform.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../core/hash_set.hpp"
 | 
			
		||||
#include "blendmode.hpp"
 | 
			
		||||
#include "../r_patch.h"
 | 
			
		||||
#include "../v_video.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +248,7 @@ void TwodeeRenderer::flush(Rhi& rhi, Twodee& twodee)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// Stage 1 - command list patch detection
 | 
			
		||||
	std::unordered_set<const patch_t*> found_patches;
 | 
			
		||||
	srb2::HashSet<const patch_t*> found_patches;
 | 
			
		||||
	for (const auto& list : twodee)
 | 
			
		||||
	{
 | 
			
		||||
		for (const auto& cmd : list.cmds)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,16 +12,16 @@
 | 
			
		|||
#define __SRB2_IO_STREAMS_HPP__
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <tcb/span.hpp>
 | 
			
		||||
#include <zlib.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2::io
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -484,13 +484,13 @@ inline void read_exact(SpanStream& stream, tcb::span<std::byte> buffer)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
class VecStream {
 | 
			
		||||
	std::vector<std::byte> vec_;
 | 
			
		||||
	srb2::Vector<std::byte> vec_;
 | 
			
		||||
	std::size_t head_ {0};
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	VecStream() = default;
 | 
			
		||||
	VecStream(const std::vector<std::byte>& vec) : vec_(vec) {}
 | 
			
		||||
	VecStream(std::vector<std::byte>&& vec) : vec_(std::move(vec)) {}
 | 
			
		||||
	VecStream(const srb2::Vector<std::byte>& vec) : vec_(vec) {}
 | 
			
		||||
	VecStream(srb2::Vector<std::byte>&& vec) : vec_(std::move(vec)) {}
 | 
			
		||||
	VecStream(const VecStream& rhs) = default;
 | 
			
		||||
	VecStream(VecStream&& rhs) = default;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -549,7 +549,7 @@ public:
 | 
			
		|||
		return head_;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::vector<std::byte>& vector() { return vec_; }
 | 
			
		||||
	srb2::Vector<std::byte>& vector() { return vec_; }
 | 
			
		||||
 | 
			
		||||
	friend void read_exact(VecStream& stream, tcb::span<std::byte> buffer);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -674,7 +674,7 @@ template <typename I,
 | 
			
		|||
class ZlibInputStream {
 | 
			
		||||
	I inner_;
 | 
			
		||||
	z_stream stream_;
 | 
			
		||||
	std::vector<std::byte> buf_;
 | 
			
		||||
	srb2::Vector<std::byte> buf_;
 | 
			
		||||
	std::size_t buf_head_;
 | 
			
		||||
	bool zstream_initialized_;
 | 
			
		||||
	bool zstream_ended_;
 | 
			
		||||
| 
						 | 
				
			
			@ -820,7 +820,7 @@ template <typename O,
 | 
			
		|||
class BufferedOutputStream final
 | 
			
		||||
{
 | 
			
		||||
	O inner_;
 | 
			
		||||
	std::vector<std::byte> buf_;
 | 
			
		||||
	srb2::Vector<std::byte> buf_;
 | 
			
		||||
	tcb::span<const std::byte>::size_type cap_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
| 
						 | 
				
			
			@ -872,7 +872,7 @@ template <typename I,
 | 
			
		|||
class BufferedInputStream final
 | 
			
		||||
{
 | 
			
		||||
	I inner_;
 | 
			
		||||
	std::vector<std::byte> buf_;
 | 
			
		||||
	srb2::Vector<std::byte> buf_;
 | 
			
		||||
	tcb::span<std::byte>::size_type cap_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
| 
						 | 
				
			
			@ -933,7 +933,7 @@ extern template class BufferedInputStream<FileStream>;
 | 
			
		|||
 | 
			
		||||
template <typename I, typename O>
 | 
			
		||||
StreamSize pipe_all(I& input, O& output) {
 | 
			
		||||
	std::vector<std::byte> buf;
 | 
			
		||||
	srb2::Vector<std::byte> buf;
 | 
			
		||||
 | 
			
		||||
	StreamSize total_written = 0;
 | 
			
		||||
	StreamSize read_this_time = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -951,7 +951,7 @@ StreamSize pipe_all(I& input, O& output) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
template <typename I>
 | 
			
		||||
std::vector<std::byte> read_to_vec(I& input) {
 | 
			
		||||
srb2::Vector<std::byte> read_to_vec(I& input) {
 | 
			
		||||
	VecStream out;
 | 
			
		||||
	pipe_all(input, out);
 | 
			
		||||
	return std::move(out.vector());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,13 +12,14 @@
 | 
			
		|||
/// \file  k_bans.c
 | 
			
		||||
/// \brief replacement for DooM Legacy ban system
 | 
			
		||||
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
#include <tcb/span.hpp>
 | 
			
		||||
 | 
			
		||||
#include "core/json.hpp"
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "io/streams.hpp"
 | 
			
		||||
#include "i_tcp_detail.h" // clientaddress
 | 
			
		||||
#include "k_bans.h"
 | 
			
		||||
#include "byteptr.h" // READ/WRITE macros
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +35,9 @@
 | 
			
		|||
#include "z_zone.h"
 | 
			
		||||
#include "i_addrinfo.h" // I_getaddrinfo
 | 
			
		||||
 | 
			
		||||
using nlohmann::json;
 | 
			
		||||
using srb2::JsonArray;
 | 
			
		||||
using srb2::JsonObject;
 | 
			
		||||
using srb2::JsonValue;
 | 
			
		||||
 | 
			
		||||
static mysockaddr_t *DuplicateSockAddr(const mysockaddr_t *source)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -43,22 +46,22 @@ static mysockaddr_t *DuplicateSockAddr(const mysockaddr_t *source)
 | 
			
		|||
	return dup;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static std::vector<banrecord_t> bans;
 | 
			
		||||
static srb2::Vector<banrecord_t> bans;
 | 
			
		||||
 | 
			
		||||
static uint8_t allZero[PUBKEYLENGTH];
 | 
			
		||||
 | 
			
		||||
static void load_bans_array_v1(json& array)
 | 
			
		||||
static void load_bans_array_v1(const JsonArray& array)
 | 
			
		||||
{
 | 
			
		||||
	for (json& object : array)
 | 
			
		||||
	for (const JsonValue& object : array)
 | 
			
		||||
	{
 | 
			
		||||
		uint8_t public_key_bin[PUBKEYLENGTH];
 | 
			
		||||
 | 
			
		||||
		std::string public_key = object.at("public_key");
 | 
			
		||||
		std::string ip_address = object.at("ip_address");
 | 
			
		||||
		time_t expires = object.at("expires");
 | 
			
		||||
		UINT8 subnet_mask = object.at("subnet_mask");
 | 
			
		||||
		std::string username = object.at("username");
 | 
			
		||||
		std::string reason = object.at("reason");
 | 
			
		||||
		srb2::String public_key = object.at("public_key").get<srb2::String>();
 | 
			
		||||
		srb2::String ip_address = object.at("ip_address").get<srb2::String>();
 | 
			
		||||
		time_t expires = object.at("expires").get<int64_t>();
 | 
			
		||||
		UINT8 subnet_mask = object.at("subnet_mask").get<UINT8>();
 | 
			
		||||
		srb2::String username = object.at("username").get<srb2::String>();
 | 
			
		||||
		srb2::String reason = object.at("reason").get<srb2::String>();
 | 
			
		||||
 | 
			
		||||
		if (!FromPrettyRRID(public_key_bin, public_key.c_str()))
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -86,16 +89,31 @@ void SV_LoadBans(void)
 | 
			
		|||
	if (!server)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	json object;
 | 
			
		||||
	JsonValue object;
 | 
			
		||||
 | 
			
		||||
	srb2::String banspath { srb2::format("{}/{}", srb2home, BANFILE) };
 | 
			
		||||
	srb2::io::BufferedInputStream<srb2::io::FileStream> bis;
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		srb2::io::FileStream fs { banspath, srb2::io::FileStreamMode::kRead };
 | 
			
		||||
		bis = srb2::io::BufferedInputStream(std::move(fs));
 | 
			
		||||
	}
 | 
			
		||||
	catch (const srb2::io::FileStreamException& ex)
 | 
			
		||||
	{
 | 
			
		||||
		// file didn't open, likely doesn't exist
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		std::ifstream f(va(pandf, srb2home, BANFILE));
 | 
			
		||||
 | 
			
		||||
		if (f.is_open())
 | 
			
		||||
		srb2::Vector<tcb::byte> data = srb2::io::read_to_vec(bis);
 | 
			
		||||
		srb2::String data_s;
 | 
			
		||||
		data_s.reserve(data.size());
 | 
			
		||||
		for (auto b : data)
 | 
			
		||||
		{
 | 
			
		||||
			f >> object;
 | 
			
		||||
			data_s.push_back(std::to_integer<char>(b));
 | 
			
		||||
		}
 | 
			
		||||
		object = JsonValue::from_json_string(data_s);
 | 
			
		||||
	}
 | 
			
		||||
	catch (const std::exception& ex)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -115,11 +133,11 @@ void SV_LoadBans(void)
 | 
			
		|||
	{
 | 
			
		||||
		if (object.value("version", 1) == 1)
 | 
			
		||||
		{
 | 
			
		||||
			json& array = object.at("bans");
 | 
			
		||||
			JsonValue& array = object.at("bans");
 | 
			
		||||
 | 
			
		||||
			if (array.is_array())
 | 
			
		||||
			{
 | 
			
		||||
				load_bans_array_v1(array);
 | 
			
		||||
				load_bans_array_v1(array.as_array());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -131,31 +149,36 @@ void SV_LoadBans(void)
 | 
			
		|||
 | 
			
		||||
void SV_SaveBans(void)
 | 
			
		||||
{
 | 
			
		||||
	json object = json::object();
 | 
			
		||||
	JsonValue object = JsonValue(JsonObject());
 | 
			
		||||
 | 
			
		||||
	object["version"] = 1;
 | 
			
		||||
	json& array = object["bans"];
 | 
			
		||||
	JsonValue& array_value = object["bans"];
 | 
			
		||||
 | 
			
		||||
	array = json::array();
 | 
			
		||||
	array_value = JsonValue(JsonArray());
 | 
			
		||||
	JsonArray& array = array_value.as_array();
 | 
			
		||||
 | 
			
		||||
	for (banrecord_t& ban : bans)
 | 
			
		||||
	{
 | 
			
		||||
		if (ban.deleted)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		array.push_back({
 | 
			
		||||
		array.push_back(JsonObject {
 | 
			
		||||
			{"public_key", GetPrettyRRID(ban.public_key, false)},
 | 
			
		||||
			{"ip_address", SOCK_AddrToStr(ban.address)},
 | 
			
		||||
			{"subnet_mask", ban.mask},
 | 
			
		||||
			{"expires", ban.expires},
 | 
			
		||||
			{"expires", static_cast<int64_t>(ban.expires)},
 | 
			
		||||
			{"username", ban.username},
 | 
			
		||||
			{"reason", ban.reason},
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	srb2::String json_string = object.to_json_string();
 | 
			
		||||
	srb2::String banfile_path = srb2::format("{}/{}", srb2home, BANFILE);
 | 
			
		||||
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		std::ofstream(va(pandf, srb2home, BANFILE)) << object;
 | 
			
		||||
		srb2::io::FileStream fs { banfile_path, srb2::io::FileStreamMode::kWrite };
 | 
			
		||||
		srb2::io::write_exact(fs, tcb::as_bytes(tcb::span(json_string)));
 | 
			
		||||
	}
 | 
			
		||||
	catch (const std::exception& ex)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -345,10 +368,10 @@ static void SV_BanSearch(boolean remove)
 | 
			
		|||
		const char* stringaddress = SOCK_AddrToStr(ban.address);
 | 
			
		||||
		const char* stringkey = GetPrettyRRID(ban.public_key, true);
 | 
			
		||||
 | 
			
		||||
		std::string recordprint = fmt::format(
 | 
			
		||||
		srb2::String recordprint = srb2::format(
 | 
			
		||||
			"{}{} - {} [{}] - {}",
 | 
			
		||||
			stringaddress,
 | 
			
		||||
			ban.mask && ban.mask != 32 ? fmt::format("/{}", ban.mask) : "",
 | 
			
		||||
			ban.mask && ban.mask != 32 ? srb2::format("/{}", ban.mask) : "",
 | 
			
		||||
			ban.username,
 | 
			
		||||
			stringkey,
 | 
			
		||||
			ban.reason
 | 
			
		||||
| 
						 | 
				
			
			@ -359,7 +382,7 @@ static void SV_BanSearch(boolean remove)
 | 
			
		|||
			if (ban.expires < now)
 | 
			
		||||
				recordprint += " - EXPIRED";
 | 
			
		||||
			else
 | 
			
		||||
				recordprint += fmt::format(" - expires {}m", (ban.expires - now)/60);
 | 
			
		||||
				recordprint += srb2::format(" - expires {}m", (ban.expires - now)/60);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		CONS_Printf("%s\n", recordprint.c_str());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,11 +12,8 @@
 | 
			
		|||
 | 
			
		||||
#include "k_credits.h"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
 | 
			
		||||
#include "doomdef.h"
 | 
			
		||||
#include "doomstat.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +56,9 @@
 | 
			
		|||
#include "r_main.h"
 | 
			
		||||
#include "m_easing.h"
 | 
			
		||||
 | 
			
		||||
using nlohmann::json;
 | 
			
		||||
using srb2::JsonArray;
 | 
			
		||||
using srb2::JsonObject;
 | 
			
		||||
using srb2::JsonValue;
 | 
			
		||||
 | 
			
		||||
enum credits_slide_types_e
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -75,14 +74,14 @@ enum credits_slide_types_e
 | 
			
		|||
struct credits_slide_s
 | 
			
		||||
{
 | 
			
		||||
	credits_slide_types_e type;
 | 
			
		||||
	std::string label;
 | 
			
		||||
	std::vector<std::string> strings;
 | 
			
		||||
	srb2::String label;
 | 
			
		||||
	srb2::Vector<srb2::String> strings;
 | 
			
		||||
	size_t strings_height;
 | 
			
		||||
	boolean play_demo_afterwards;
 | 
			
		||||
	int fade_out_music;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static std::vector<struct credits_slide_s> g_credits_slides;
 | 
			
		||||
static srb2::Vector<struct credits_slide_s> g_credits_slides;
 | 
			
		||||
 | 
			
		||||
struct credits_star_s
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -94,10 +93,10 @@ struct credits_star_s
 | 
			
		|||
static struct credits_s
 | 
			
		||||
{
 | 
			
		||||
	size_t current_slide;
 | 
			
		||||
	std::vector<UINT16> demo_maps;
 | 
			
		||||
	srb2::Vector<UINT16> demo_maps;
 | 
			
		||||
	boolean skip;
 | 
			
		||||
 | 
			
		||||
	std::vector<struct credits_star_s> stars;
 | 
			
		||||
	srb2::Vector<struct credits_star_s> stars;
 | 
			
		||||
 | 
			
		||||
	fixed_t transition;
 | 
			
		||||
	fixed_t transition_prev;
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +104,7 @@ static struct credits_s
 | 
			
		|||
 | 
			
		||||
	tic_t animation_timer;
 | 
			
		||||
 | 
			
		||||
	std::vector<std::vector<std::string>> split_slide_strings;
 | 
			
		||||
	srb2::Vector<srb2::Vector<srb2::String>> split_slide_strings;
 | 
			
		||||
	size_t split_slide_id;
 | 
			
		||||
	tic_t split_slide_delay;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -142,12 +141,14 @@ void F_LoadCreditsDefinitions(void)
 | 
			
		|||
	size_t credits_lump_len = W_LumpLength(credits_lump_id);
 | 
			
		||||
	const char *credits_lump = static_cast<const char *>( W_CacheLumpNum(credits_lump_id, PU_CACHE) );
 | 
			
		||||
 | 
			
		||||
	json credits_array = json::parse(credits_lump, credits_lump + credits_lump_len);
 | 
			
		||||
	if (credits_array.is_array() == false)
 | 
			
		||||
	srb2::String json_string { credits_lump, credits_lump_len };
 | 
			
		||||
	JsonValue credits_parsed = JsonValue::from_json_string(json_string);
 | 
			
		||||
	if (credits_parsed.is_array() == false)
 | 
			
		||||
	{
 | 
			
		||||
		I_Error("credits_def parse error: Not a JSON array");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	JsonArray credits_array = credits_parsed.as_array();
 | 
			
		||||
 | 
			
		||||
	if (credits_array.size() == 0)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -156,11 +157,11 @@ void F_LoadCreditsDefinitions(void)
 | 
			
		|||
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		for (json& slide_obj : credits_array)
 | 
			
		||||
		for (JsonValue& slide_obj : credits_array)
 | 
			
		||||
		{
 | 
			
		||||
			struct credits_slide_s slide;
 | 
			
		||||
 | 
			
		||||
			std::string type_str = slide_obj.value("type", "scroll");
 | 
			
		||||
			srb2::String type_str = slide_obj.value("type", srb2::String("scroll"));
 | 
			
		||||
 | 
			
		||||
			if (type_str == "scroll")
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -196,18 +197,19 @@ void F_LoadCreditsDefinitions(void)
 | 
			
		|||
				throw std::runtime_error("unexpected type name '" + type_str + "'");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			slide.label = slide_obj.value("label", "");
 | 
			
		||||
			slide.label = slide_obj.value("label", srb2::String(""));
 | 
			
		||||
 | 
			
		||||
			slide.strings_height = 0;
 | 
			
		||||
 | 
			
		||||
			if (slide_obj.contains("strings"))
 | 
			
		||||
			{
 | 
			
		||||
				json strings_array = slide_obj.at("strings");
 | 
			
		||||
				if (strings_array.is_array() == true)
 | 
			
		||||
				JsonValue strings_value = slide_obj.at("strings");
 | 
			
		||||
				if (strings_value.is_array() == true)
 | 
			
		||||
				{
 | 
			
		||||
					JsonArray& strings_array = strings_value.as_array();
 | 
			
		||||
					for (size_t i = 0; i < strings_array.size(); i++)
 | 
			
		||||
					{
 | 
			
		||||
						slide.strings.push_back( strings_array.at(i) );
 | 
			
		||||
						slide.strings.push_back( strings_array.at(i).get<srb2::String>() );
 | 
			
		||||
 | 
			
		||||
						if (slide.type == CRED_TYPE_SCROLL)
 | 
			
		||||
						{
 | 
			
		||||
| 
						 | 
				
			
			@ -289,7 +291,7 @@ static void F_InitCreditsSlide(void)
 | 
			
		|||
			size_t max_strings_per_screen = (num_strings - 1) / num_sub_screens + 1;
 | 
			
		||||
 | 
			
		||||
			size_t str_id = 0;
 | 
			
		||||
			std::vector<std::string> screen_strings;
 | 
			
		||||
			srb2::Vector<srb2::String> screen_strings;
 | 
			
		||||
 | 
			
		||||
			if (max_strings_per_screen == kMaxSlideStrings
 | 
			
		||||
				&& num_strings % kMaxSlideStrings == 1)
 | 
			
		||||
| 
						 | 
				
			
			@ -342,24 +344,24 @@ static void F_InitCreditsSlide(void)
 | 
			
		|||
#endif
 | 
			
		||||
			)
 | 
			
		||||
			{
 | 
			
		||||
				slide->strings.push_back("#" + std::string(def->title));
 | 
			
		||||
				slide->strings.push_back("#" + srb2::String(def->title));
 | 
			
		||||
				slide->strings_height += 12;
 | 
			
		||||
 | 
			
		||||
				if (def->author)
 | 
			
		||||
				{
 | 
			
		||||
					slide->strings.push_back("by " + std::string(def->author));
 | 
			
		||||
					slide->strings.push_back("by " + srb2::String(def->author));
 | 
			
		||||
					slide->strings_height += 12;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (def->source)
 | 
			
		||||
				{
 | 
			
		||||
					slide->strings.push_back("from " + std::string(def->source));
 | 
			
		||||
					slide->strings.push_back("from " + srb2::String(def->source));
 | 
			
		||||
					slide->strings_height += 12;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (def->composers)
 | 
			
		||||
				{
 | 
			
		||||
					slide->strings.push_back("originally by " + std::string(def->composers));
 | 
			
		||||
					slide->strings.push_back("originally by " + srb2::String(def->composers));
 | 
			
		||||
					slide->strings_height += 12;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -478,7 +480,7 @@ void F_ContinueCredits(void)
 | 
			
		|||
 | 
			
		||||
static UINT16 F_PickRandomCreditsDemoMap(void)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<UINT16> allowedMaps;
 | 
			
		||||
	srb2::Vector<UINT16> allowedMaps;
 | 
			
		||||
 | 
			
		||||
	for (INT32 i = 0; i < basenummapheaders; i++) // Only take from the base game.
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -911,7 +913,7 @@ static void F_DrawCreditsScroll(void)
 | 
			
		|||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			std::string new_str = str;
 | 
			
		||||
			srb2::String new_str = str;
 | 
			
		||||
 | 
			
		||||
			if (new_str.at(0) == '*')
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -1017,7 +1019,7 @@ static void F_DrawCreditsSlide(void)
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const std::vector<std::string> *slide_strings = &g_credits.split_slide_strings[ g_credits.split_slide_id ];
 | 
			
		||||
	const srb2::Vector<srb2::String> *slide_strings = &g_credits.split_slide_strings[ g_credits.split_slide_id ];
 | 
			
		||||
	const fixed_t strings_height = slide_strings->size() * 30 * FRACUNIT;
 | 
			
		||||
 | 
			
		||||
	fixed_t y = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1113,7 +1115,7 @@ static void F_DrawCreditsTyler52(void)
 | 
			
		|||
		V_DrawFadeScreen(0xFF00, 31 - (g_credits.tyler_fade * 4));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::string memory_str = "In memory of";
 | 
			
		||||
	srb2::String memory_str = "In memory of";
 | 
			
		||||
	const fixed_t memory_width = V_StringScaledWidth(
 | 
			
		||||
		FRACUNIT, FRACUNIT, FRACUNIT,
 | 
			
		||||
		0, LSLOW_FONT,
 | 
			
		||||
| 
						 | 
				
			
			@ -1126,7 +1128,7 @@ static void F_DrawCreditsTyler52(void)
 | 
			
		|||
		memory_str.c_str()
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	std::string tyler_str = "Tyler52";
 | 
			
		||||
	srb2::String tyler_str = "Tyler52";
 | 
			
		||||
	const fixed_t tyler_width = V_StringScaledWidth(
 | 
			
		||||
		FRACUNIT, FRACUNIT, FRACUNIT,
 | 
			
		||||
		0, LSHI_FONT,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,9 +14,10 @@
 | 
			
		|||
#include "k_dialogue.hpp"
 | 
			
		||||
#include "k_dialogue.h"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "info.h"
 | 
			
		||||
#include "sounds.h"
 | 
			
		||||
#include "g_game.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +44,7 @@ void Dialogue::Typewriter::ClearText(void)
 | 
			
		|||
	textDest.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Dialogue::Typewriter::NewText(std::string newText)
 | 
			
		||||
void Dialogue::Typewriter::NewText(const srb2::String& newText)
 | 
			
		||||
{
 | 
			
		||||
	text.clear();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -174,7 +175,7 @@ void Dialogue::SetSpeaker(void)
 | 
			
		|||
	typewriter.voiceSfx = sfx_ktalk;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Dialogue::SetSpeaker(std::string skinName, int portraitID)
 | 
			
		||||
void Dialogue::SetSpeaker(srb2::String skinName, int portraitID)
 | 
			
		||||
{
 | 
			
		||||
	Init();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -215,7 +216,7 @@ void Dialogue::SetSpeaker(std::string skinName, int portraitID)
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Dialogue::SetSpeaker(std::string name, patch_t *patch, UINT8 *colormap, sfxenum_t voice)
 | 
			
		||||
void Dialogue::SetSpeaker(srb2::String name, patch_t *patch, UINT8 *colormap, sfxenum_t voice)
 | 
			
		||||
{
 | 
			
		||||
	Init();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -472,7 +473,7 @@ void Dialogue::Draw(void)
 | 
			
		|||
		.flags(V_VFLIP|V_FLIP)
 | 
			
		||||
		.patch(patchCache["TUTDIAGE"]);
 | 
			
		||||
 | 
			
		||||
	std::string intertext = "<large>";
 | 
			
		||||
	srb2::String intertext = "<large>";
 | 
			
		||||
 | 
			
		||||
	drawer
 | 
			
		||||
		.xy(10 - BASEVIDWIDTH, -3-32)
 | 
			
		||||
| 
						 | 
				
			
			@ -486,7 +487,7 @@ void Dialogue::Draw(void)
 | 
			
		|||
			.patch(patchCache["TUTDIAG2"]);
 | 
			
		||||
 | 
			
		||||
		if (Held())
 | 
			
		||||
			intertext += "<z_pressed>";	
 | 
			
		||||
			intertext += "<z_pressed>";
 | 
			
		||||
		else
 | 
			
		||||
			intertext += "<z_animated>";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,10 +14,10 @@
 | 
			
		|||
#ifndef __K_DIALOGUE_HPP__
 | 
			
		||||
#define __K_DIALOGUE_HPP__
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
#include "core/hash_map.hpp"
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "doomdef.h"
 | 
			
		||||
#include "doomtype.h"
 | 
			
		||||
#include "typedef.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -33,8 +33,8 @@ public:
 | 
			
		|||
	static constexpr fixed_t kSlideSpeed = FRACUNIT / (TICRATE / 5);
 | 
			
		||||
 | 
			
		||||
	void SetSpeaker(void);
 | 
			
		||||
	void SetSpeaker(std::string skinName, int portraitID);
 | 
			
		||||
	void SetSpeaker(std::string name, patch_t *patch, UINT8 *colormap, sfxenum_t voice);
 | 
			
		||||
	void SetSpeaker(srb2::String skinName, int portraitID);
 | 
			
		||||
	void SetSpeaker(srb2::String name, patch_t *patch, UINT8 *colormap, sfxenum_t voice);
 | 
			
		||||
 | 
			
		||||
	void NewText(std::string_view newText);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -60,8 +60,8 @@ public:
 | 
			
		|||
		static constexpr fixed_t kTextSpeedDefault = FRACUNIT;
 | 
			
		||||
		static constexpr fixed_t kTextPunctPause = (FRACUNIT * TICRATE * 2) / 5;
 | 
			
		||||
 | 
			
		||||
		std::string text;
 | 
			
		||||
		std::string textDest;
 | 
			
		||||
		srb2::String text;
 | 
			
		||||
		srb2::String textDest;
 | 
			
		||||
 | 
			
		||||
		fixed_t textTimer;
 | 
			
		||||
		fixed_t textSpeed;
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +71,7 @@ public:
 | 
			
		|||
		sfxenum_t voiceSfx;
 | 
			
		||||
		bool syllable;
 | 
			
		||||
 | 
			
		||||
		void NewText(std::string newText);
 | 
			
		||||
		void NewText(const srb2::String& newText);
 | 
			
		||||
		void ClearText(void);
 | 
			
		||||
 | 
			
		||||
		void WriteText(void);
 | 
			
		||||
| 
						 | 
				
			
			@ -86,11 +86,11 @@ private:
 | 
			
		|||
	patch_t *bgPatch;
 | 
			
		||||
	patch_t *confirmPatch;
 | 
			
		||||
 | 
			
		||||
	std::string speaker;
 | 
			
		||||
	srb2::String speaker;
 | 
			
		||||
	patch_t *portrait;
 | 
			
		||||
	UINT8 *portraitColormap;
 | 
			
		||||
 | 
			
		||||
	std::unordered_map<std::string_view, patch_t*> patchCache;
 | 
			
		||||
	srb2::HashMap<std::string_view, patch_t*> patchCache;
 | 
			
		||||
 | 
			
		||||
	bool active;
 | 
			
		||||
	fixed_t slide;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,11 +11,12 @@
 | 
			
		|||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <deque>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "core/vector.hpp"
 | 
			
		||||
#include "k_hud.h"
 | 
			
		||||
#include "k_kart.h"
 | 
			
		||||
#include "k_battle.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -6332,21 +6333,21 @@ typedef enum
 | 
			
		|||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
	std::string text;
 | 
			
		||||
	srb2::String text;
 | 
			
		||||
	sfxenum_t sound;
 | 
			
		||||
} message_t;
 | 
			
		||||
 | 
			
		||||
struct messagestate_t
 | 
			
		||||
{
 | 
			
		||||
	std::deque<std::string> messages;
 | 
			
		||||
	std::string objective = "";
 | 
			
		||||
	std::deque<srb2::String> messages;
 | 
			
		||||
	srb2::String objective = "";
 | 
			
		||||
	tic_t timer = 0;
 | 
			
		||||
	boolean persist = false;
 | 
			
		||||
	messagemode_t mode = MM_IN;
 | 
			
		||||
	const tic_t speedyswitch = 2*TICRATE;
 | 
			
		||||
	const tic_t lazyswitch = 4*TICRATE;
 | 
			
		||||
 | 
			
		||||
	void add(std::string msg)
 | 
			
		||||
	void add(srb2::String msg)
 | 
			
		||||
	{
 | 
			
		||||
		messages.push_back(msg);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -6384,7 +6385,7 @@ struct messagestate_t
 | 
			
		|||
		switch (mode)
 | 
			
		||||
		{
 | 
			
		||||
			case MM_IN:
 | 
			
		||||
				if (timer > messages[0].length())
 | 
			
		||||
				if (timer > messages[0].size())
 | 
			
		||||
					switch_mode(MM_HOLD);
 | 
			
		||||
				break;
 | 
			
		||||
			case MM_HOLD:
 | 
			
		||||
| 
						 | 
				
			
			@ -6394,7 +6395,7 @@ struct messagestate_t
 | 
			
		|||
					switch_mode(MM_OUT);
 | 
			
		||||
				break;
 | 
			
		||||
			case MM_OUT:
 | 
			
		||||
				if (timer > messages[0].length())
 | 
			
		||||
				if (timer > messages[0].size())
 | 
			
		||||
					next();
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@
 | 
			
		|||
 | 
			
		||||
#include "k_podium.h"
 | 
			
		||||
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "doomdef.h"
 | 
			
		||||
#include "d_main.h"
 | 
			
		||||
#include "d_netcmd.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -51,8 +52,6 @@
 | 
			
		|||
 | 
			
		||||
#include "k_hud.h"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
typedef enum
 | 
			
		||||
{
 | 
			
		||||
	PODIUM_ST_CONGRATS_SLIDEIN,
 | 
			
		||||
| 
						 | 
				
			
			@ -652,7 +651,7 @@ void podiumData_s::Draw(void)
 | 
			
		|||
								}
 | 
			
		||||
 | 
			
		||||
								{
 | 
			
		||||
									std::string emeraldName;
 | 
			
		||||
									srb2::String emeraldName;
 | 
			
		||||
									if (emeraldNum > 7)
 | 
			
		||||
									{
 | 
			
		||||
										emeraldName = (useWhiteFrame ? "K_SUPER2" : "K_SUPER1");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,10 +12,13 @@
 | 
			
		|||
/// \brief implements methods for profiles etc.
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <exception>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "core/vector.hpp"
 | 
			
		||||
#include "io/streams.hpp"
 | 
			
		||||
#include "doomtype.h"
 | 
			
		||||
#include "d_main.h" // pandf
 | 
			
		||||
| 
						 | 
				
			
			@ -253,7 +256,6 @@ void PR_InitNewProfile(void)
 | 
			
		|||
void PR_SaveProfiles(void)
 | 
			
		||||
{
 | 
			
		||||
	namespace fs = std::filesystem;
 | 
			
		||||
	using json = nlohmann::json;
 | 
			
		||||
	using namespace srb2;
 | 
			
		||||
	namespace io = srb2::io;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -271,13 +273,13 @@ void PR_SaveProfiles(void)
 | 
			
		|||
		profile_t* cprof = profilesList[i];
 | 
			
		||||
 | 
			
		||||
		jsonprof.version = PROFILEVER;
 | 
			
		||||
		jsonprof.profilename = std::string(cprof->profilename);
 | 
			
		||||
		jsonprof.profilename = String(cprof->profilename);
 | 
			
		||||
		std::copy(std::begin(cprof->public_key), std::end(cprof->public_key), std::begin(jsonprof.publickey));
 | 
			
		||||
		std::copy(std::begin(cprof->secret_key), std::end(cprof->secret_key), std::begin(jsonprof.secretkey));
 | 
			
		||||
		jsonprof.playername = std::string(cprof->playername);
 | 
			
		||||
		jsonprof.skinname = std::string(cprof->skinname);
 | 
			
		||||
		jsonprof.colorname = std::string(skincolors[cprof->color].name);
 | 
			
		||||
		jsonprof.followername = std::string(cprof->follower);
 | 
			
		||||
		jsonprof.playername = String(cprof->playername);
 | 
			
		||||
		jsonprof.skinname = String(cprof->skinname);
 | 
			
		||||
		jsonprof.colorname = String(skincolors[cprof->color].name);
 | 
			
		||||
		jsonprof.followername = String(cprof->follower);
 | 
			
		||||
		if (cprof->followercolor == FOLLOWERCOLOR_MATCH)
 | 
			
		||||
		{
 | 
			
		||||
			jsonprof.followercolorname = "Match";
 | 
			
		||||
| 
						 | 
				
			
			@ -292,11 +294,11 @@ void PR_SaveProfiles(void)
 | 
			
		|||
		}
 | 
			
		||||
		else if (cprof->followercolor >= numskincolors)
 | 
			
		||||
		{
 | 
			
		||||
			jsonprof.followercolorname = std::string();
 | 
			
		||||
			jsonprof.followercolorname = String();
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
		{
 | 
			
		||||
			jsonprof.followercolorname = std::string(skincolors[cprof->followercolor].name);
 | 
			
		||||
			jsonprof.followercolorname = String(skincolors[cprof->followercolor].name);
 | 
			
		||||
		}
 | 
			
		||||
		jsonprof.records.wins = cprof->wins;
 | 
			
		||||
		jsonprof.records.rounds = cprof->rounds;
 | 
			
		||||
| 
						 | 
				
			
			@ -310,7 +312,7 @@ void PR_SaveProfiles(void)
 | 
			
		|||
 | 
			
		||||
		for (size_t j = 0; j < num_gamecontrols; j++)
 | 
			
		||||
		{
 | 
			
		||||
			std::vector<int32_t> mappings;
 | 
			
		||||
			srb2::Vector<int32_t> mappings;
 | 
			
		||||
			for (size_t k = 0; k < MAXINPUTMAPPING; k++)
 | 
			
		||||
			{
 | 
			
		||||
				mappings.push_back(cprof->controls[j][k]);
 | 
			
		||||
| 
						 | 
				
			
			@ -321,16 +323,18 @@ void PR_SaveProfiles(void)
 | 
			
		|||
		ng.profiles.emplace_back(std::move(jsonprof));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::vector<uint8_t> ubjson = json::to_ubjson(ng);
 | 
			
		||||
	JsonValue ngv;
 | 
			
		||||
	to_json(ngv, ng);
 | 
			
		||||
	Vector<std::byte> ubjson = ngv.to_ubjson();
 | 
			
		||||
 | 
			
		||||
	std::string realpath = fmt::format("{}/{}", srb2home, PROFILESFILE);
 | 
			
		||||
	std::string bakpath = fmt::format("{}.bak", realpath);
 | 
			
		||||
	String realpath = srb2::format("{}/{}", srb2home, PROFILESFILE);
 | 
			
		||||
	String bakpath = srb2::format("{}.bak", realpath);
 | 
			
		||||
 | 
			
		||||
	if (fs::exists(realpath))
 | 
			
		||||
	if (fs::exists(fs::path(static_cast<std::string_view>(realpath))))
 | 
			
		||||
	{
 | 
			
		||||
		try
 | 
			
		||||
		{
 | 
			
		||||
			fs::rename(realpath, bakpath);
 | 
			
		||||
			fs::rename(fs::path(static_cast<std::string_view>(realpath)), fs::path(static_cast<std::string_view>(bakpath)));
 | 
			
		||||
		}
 | 
			
		||||
		catch (const fs::filesystem_error& ex)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -349,7 +353,7 @@ void PR_SaveProfiles(void)
 | 
			
		|||
		io::write(static_cast<uint8_t>(0), file); // reserved2
 | 
			
		||||
		io::write(static_cast<uint8_t>(0), file); // reserved3
 | 
			
		||||
		io::write(static_cast<uint8_t>(0), file); // reserved4
 | 
			
		||||
		io::write_exact(file, tcb::as_bytes(tcb::make_span(ubjson)));
 | 
			
		||||
		io::write_exact(file, ubjson);
 | 
			
		||||
		file.close();
 | 
			
		||||
	}
 | 
			
		||||
	catch (const std::exception& ex)
 | 
			
		||||
| 
						 | 
				
			
			@ -367,7 +371,6 @@ void PR_LoadProfiles(void)
 | 
			
		|||
	namespace fs = std::filesystem;
 | 
			
		||||
	using namespace srb2;
 | 
			
		||||
	namespace io = srb2::io;
 | 
			
		||||
	using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
	profile_t *dprofile = PR_MakeProfile(
 | 
			
		||||
		PROFILEDEFAULTNAME,
 | 
			
		||||
| 
						 | 
				
			
			@ -378,7 +381,7 @@ void PR_LoadProfiles(void)
 | 
			
		|||
		true
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	std::string datapath {fmt::format("{}/{}", srb2home, PROFILESFILE)};
 | 
			
		||||
	String datapath { srb2::format("{}/{}", srb2home, PROFILESFILE) };
 | 
			
		||||
 | 
			
		||||
	io::BufferedInputStream<io::FileStream> bis;
 | 
			
		||||
	try
 | 
			
		||||
| 
						 | 
				
			
			@ -413,11 +416,10 @@ void PR_LoadProfiles(void)
 | 
			
		|||
			throw std::domain_error("Header is incompatible");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::vector<std::byte> remainder = io::read_to_vec(bis);
 | 
			
		||||
		Vector<std::byte> remainder = io::read_to_vec(bis);
 | 
			
		||||
		// safety: std::byte repr is always uint8_t 1-byte aligned
 | 
			
		||||
		tcb::span<uint8_t> remainder_as_u8 = tcb::span((uint8_t*)remainder.data(), remainder.size());
 | 
			
		||||
		json parsed = json::from_ubjson(remainder_as_u8);
 | 
			
		||||
		js = parsed.template get<ProfilesJson>();
 | 
			
		||||
		JsonValue parsed = JsonValue::from_ubjson(remainder);
 | 
			
		||||
		from_json(parsed, js);
 | 
			
		||||
	}
 | 
			
		||||
	catch (const std::exception& ex)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,10 +25,10 @@
 | 
			
		|||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
#include "core/json.hpp"
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "core/vector.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ struct ProfileRecordsJson
 | 
			
		|||
	uint32_t wins;
 | 
			
		||||
	uint32_t rounds;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ProfileRecordsJson, wins, rounds)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ProfileRecordsJson, wins, rounds)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ProfilePreferencesJson
 | 
			
		||||
| 
						 | 
				
			
			@ -50,9 +50,8 @@ struct ProfilePreferencesJson
 | 
			
		|||
	bool autoring;
 | 
			
		||||
	bool rumble;
 | 
			
		||||
	uint8_t fov;
 | 
			
		||||
	tm test;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
		ProfilePreferencesJson,
 | 
			
		||||
		kickstartaccel,
 | 
			
		||||
		autoroulette,
 | 
			
		||||
| 
						 | 
				
			
			@ -67,19 +66,19 @@ struct ProfilePreferencesJson
 | 
			
		|||
struct ProfileJson
 | 
			
		||||
{
 | 
			
		||||
	uint32_t version;
 | 
			
		||||
	std::string profilename;
 | 
			
		||||
	std::string playername;
 | 
			
		||||
	String profilename;
 | 
			
		||||
	String playername;
 | 
			
		||||
	std::array<uint8_t, 32> publickey = {{}};
 | 
			
		||||
	std::array<uint8_t, 64> secretkey = {{}};
 | 
			
		||||
	std::string skinname;
 | 
			
		||||
	std::string colorname;
 | 
			
		||||
	std::string followername;
 | 
			
		||||
	std::string followercolorname;
 | 
			
		||||
	String skinname;
 | 
			
		||||
	String colorname;
 | 
			
		||||
	String followername;
 | 
			
		||||
	String followercolorname;
 | 
			
		||||
	ProfileRecordsJson records;
 | 
			
		||||
	ProfilePreferencesJson preferences;
 | 
			
		||||
	std::vector<std::vector<int32_t>> controls = {};
 | 
			
		||||
	Vector<Vector<int32_t>> controls = {};
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
 | 
			
		||||
		ProfileJson,
 | 
			
		||||
		version,
 | 
			
		||||
		profilename,
 | 
			
		||||
| 
						 | 
				
			
			@ -98,9 +97,9 @@ struct ProfileJson
 | 
			
		|||
 | 
			
		||||
struct ProfilesJson
 | 
			
		||||
{
 | 
			
		||||
	std::vector<ProfileJson> profiles;
 | 
			
		||||
	Vector<ProfileJson> profiles;
 | 
			
		||||
 | 
			
		||||
	NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ProfilesJson, profiles)
 | 
			
		||||
	SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(ProfilesJson, profiles)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace srb2
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,10 +24,12 @@
 | 
			
		|||
#include "cxxutil.hpp"
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "core/vector.hpp"
 | 
			
		||||
 | 
			
		||||
// The number of sparkles per waypoint connection in the waypoint visualisation
 | 
			
		||||
static const UINT32 SPARKLES_PER_CONNECTION = 16U;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2377,8 +2379,8 @@ static BlockItReturn_t K_TrackWaypointNearOffroad(line_t *line)
 | 
			
		|||
struct complexity_sneaker_s
 | 
			
		||||
{
 | 
			
		||||
	fixed_t bbox[4];
 | 
			
		||||
	//std::vector<sector_t *> sectors;
 | 
			
		||||
	//std::vector<mapthing_t *> things;
 | 
			
		||||
	//srb2::Vector<sector_t *> sectors;
 | 
			
		||||
	//srb2::Vector<mapthing_t *> things;
 | 
			
		||||
 | 
			
		||||
	complexity_sneaker_s(sector_t *sec)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -2631,7 +2633,7 @@ static INT32 K_CalculateTrackComplexity(void)
 | 
			
		|||
 | 
			
		||||
			delta = FixedMul(delta, FixedMul(FixedMul(dist_factor, radius_factor), wall_factor));
 | 
			
		||||
 | 
			
		||||
			std::string msg = fmt::format(
 | 
			
		||||
			srb2::String msg = srb2::format(
 | 
			
		||||
				"TURN [{}]: r: {:.2f}, d: {:.2f}, w: {:.2f}, r*d*w: {:.2f}, DELTA: {}\n",
 | 
			
		||||
				turn_id,
 | 
			
		||||
				FixedToFloat(radius_factor),
 | 
			
		||||
| 
						 | 
				
			
			@ -2644,7 +2646,7 @@ static INT32 K_CalculateTrackComplexity(void)
 | 
			
		|||
			trackcomplexity += (delta / FRACUNIT);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::vector<complexity_sneaker_s> sneaker_panels;
 | 
			
		||||
		srb2::Vector<complexity_sneaker_s> sneaker_panels;
 | 
			
		||||
 | 
			
		||||
		for (size_t i = 0; i < numsectors; i++)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,10 +10,8 @@
 | 
			
		|||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +19,9 @@ extern "C" {
 | 
			
		|||
#include "blua/lua.h"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include "core/hash_map.hpp"
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "core/vector.hpp"
 | 
			
		||||
#include "v_draw.hpp"
 | 
			
		||||
 | 
			
		||||
#include "command.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +59,7 @@ struct lua_timer_t
 | 
			
		|||
namespace
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
std::unordered_map<std::string, lua_timer_t> g_tic_timers;
 | 
			
		||||
srb2::HashMap<srb2::String, lua_timer_t> g_tic_timers;
 | 
			
		||||
 | 
			
		||||
}; // namespace
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +157,7 @@ void LUA_RenderTimers(void)
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::vector<decltype(g_tic_timers)::iterator> view;
 | 
			
		||||
	srb2::Vector<decltype(g_tic_timers)::Iter> view;
 | 
			
		||||
	view.reserve(g_tic_timers.size());
 | 
			
		||||
 | 
			
		||||
	auto color_flag = [](double t)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								src/m_pw.cpp
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								src/m_pw.cpp
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -14,13 +14,13 @@
 | 
			
		|||
#include <cctype>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <variant>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "modp_b64/modp_b64.h"
 | 
			
		||||
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "core/vector.hpp"
 | 
			
		||||
#include "cxxutil.hpp"
 | 
			
		||||
 | 
			
		||||
#include "command.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -43,12 +43,13 @@ namespace
 | 
			
		|||
 | 
			
		||||
constexpr const UINT8 kRRSalt[17] = "0L4rlK}{9ay6'VJS";
 | 
			
		||||
 | 
			
		||||
std::array<UINT8, M_PW_BUF_SIZE> decode_hash(std::string encoded)
 | 
			
		||||
std::array<UINT8, M_PW_BUF_SIZE> decode_hash(srb2::String encoded)
 | 
			
		||||
{
 | 
			
		||||
	std::array<UINT8, M_PW_BUF_SIZE> decoded;
 | 
			
		||||
	if (modp::b64_decode(encoded).size() != decoded.size())
 | 
			
		||||
	std::string encoded_stl { static_cast<std::string_view>(encoded) };
 | 
			
		||||
	if (modp::b64_decode(encoded_stl).size() != decoded.size())
 | 
			
		||||
		throw std::invalid_argument("hash is incorrectly sized");
 | 
			
		||||
	std::copy(encoded.begin(), encoded.end(), decoded.begin());
 | 
			
		||||
	std::copy(encoded_stl.begin(), encoded_stl.end(), decoded.begin());
 | 
			
		||||
	return decoded;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +61,7 @@ struct Pw
 | 
			
		|||
	const std::array<UINT8, M_PW_BUF_SIZE> hash_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::vector<Pw> passwords;
 | 
			
		||||
srb2::Vector<Pw> passwords;
 | 
			
		||||
 | 
			
		||||
// m_cond.c
 | 
			
		||||
template <typename F>
 | 
			
		||||
| 
						 | 
				
			
			@ -629,8 +630,8 @@ try_password_e M_TryPassword(const char *password, boolean conditions)
 | 
			
		|||
	using var = std::variant<std::monostate, condition_t*, Pw*>;
 | 
			
		||||
 | 
			
		||||
	// Normalize input casing
 | 
			
		||||
	std::string key = password;
 | 
			
		||||
	strlwr(key.data());
 | 
			
		||||
	srb2::String key = password;
 | 
			
		||||
	strlwr((char*)key.data());
 | 
			
		||||
 | 
			
		||||
	UINT8 key_hash[M_PW_HASH_SIZE];
 | 
			
		||||
	M_HashPassword(key_hash, key.c_str(), kRRSalt);
 | 
			
		||||
| 
						 | 
				
			
			@ -684,8 +685,8 @@ try_password_e M_TryPassword(const char *password, boolean conditions)
 | 
			
		|||
boolean M_TryExactPassword(const char *password, const char *encodedhash)
 | 
			
		||||
{
 | 
			
		||||
	// Normalize input casing
 | 
			
		||||
	std::string key = password;
 | 
			
		||||
	strlwr(key.data());
 | 
			
		||||
	srb2::String key = password;
 | 
			
		||||
	strlwr((char*)key.data());
 | 
			
		||||
 | 
			
		||||
	UINT8 key_hash[M_PW_HASH_SIZE];
 | 
			
		||||
	M_HashPassword(key_hash, key.c_str(), kRRSalt);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,11 +17,11 @@
 | 
			
		|||
#include <cstdint>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <tcb/span.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../audio/sample.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2::media
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ public:
 | 
			
		|||
			int frame_rate;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		std::string file_name;
 | 
			
		||||
		srb2::String file_name;
 | 
			
		||||
 | 
			
		||||
		std::optional<std::size_t> max_size; // file size limit
 | 
			
		||||
		std::optional<std::chrono::duration<float>> max_duration;
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ public:
 | 
			
		|||
	{
 | 
			
		||||
		using instance_t = std::unique_ptr<StagingVideoFrame>;
 | 
			
		||||
 | 
			
		||||
		std::vector<uint8_t> screen;
 | 
			
		||||
		srb2::Vector<uint8_t> screen;
 | 
			
		||||
		uint32_t width, height;
 | 
			
		||||
		int pts;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,10 +10,10 @@
 | 
			
		|||
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../cxxutil.hpp"
 | 
			
		||||
#include "avrecorder_impl.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ void Impl::container_dtor_handler(const MediaContainer& container) const
 | 
			
		|||
 | 
			
		||||
	if (max_size_ && container.size() > *max_size_)
 | 
			
		||||
	{
 | 
			
		||||
		const std::string line = fmt::format(
 | 
			
		||||
		const srb2::String line = srb2::format(
 | 
			
		||||
			"Video size has exceeded limit {} > {} ({}%)."
 | 
			
		||||
			" This should not happen, please report this bug.\n",
 | 
			
		||||
			container.size(),
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +100,7 @@ void AVRecorder::draw_statistics() const
 | 
			
		|||
{
 | 
			
		||||
	SRB2_ASSERT(impl_->video_encoder_ != nullptr);
 | 
			
		||||
 | 
			
		||||
	auto draw = [](int x, std::string text, int32_t flags = 0)
 | 
			
		||||
	auto draw = [](int x, const srb2::String text, int32_t flags = 0)
 | 
			
		||||
	{
 | 
			
		||||
		V_DrawThinString(
 | 
			
		||||
			x,
 | 
			
		||||
| 
						 | 
				
			
			@ -144,7 +144,7 @@ void AVRecorder::draw_statistics() const
 | 
			
		|||
		return 0;
 | 
			
		||||
	}();
 | 
			
		||||
 | 
			
		||||
	draw(200, fmt::format("{:.0f}", fps), fps_color);
 | 
			
		||||
	draw(230, fmt::format("{:.1f}s", impl_->container_->duration().count()));
 | 
			
		||||
	draw(260, fmt::format("{:.1f} MB", size / kMb), mb_color);
 | 
			
		||||
	draw(200, srb2::format("{:.0f}", fps), fps_color);
 | 
			
		||||
	draw(230, srb2::format("{:.1f}s", impl_->container_->duration().count()));
 | 
			
		||||
	draw(260, srb2::format("{:.1f} MB", size / kMb), mb_color);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,8 +19,8 @@
 | 
			
		|||
#include <mutex>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../i_time.h"
 | 
			
		||||
#include "avrecorder.hpp"
 | 
			
		||||
#include "container.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +57,7 @@ public:
 | 
			
		|||
			using frame_type = StagingVideoFrame::instance_t;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		std::vector<typename Traits<T>::frame_type> vec_;
 | 
			
		||||
		srb2::Vector<typename Traits<T>::frame_type> vec_;
 | 
			
		||||
 | 
			
		||||
		// This number only decrements once a frame has
 | 
			
		||||
		// actually been written to container.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@
 | 
			
		|||
 | 
			
		||||
using namespace srb2::media;
 | 
			
		||||
 | 
			
		||||
CFile::CFile(const std::string file_name) : name_(file_name)
 | 
			
		||||
CFile::CFile(const srb2::String& file_name) : name_(file_name)
 | 
			
		||||
{
 | 
			
		||||
	file_ = std::fopen(name(), "wb");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,8 @@
 | 
			
		|||
#define __SRB2_MEDIA_CFILE_HPP__
 | 
			
		||||
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
 | 
			
		||||
namespace srb2::media
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +21,7 @@ namespace srb2::media
 | 
			
		|||
class CFile
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	CFile(const std::string file_name);
 | 
			
		||||
	CFile(const srb2::String& file_name);
 | 
			
		||||
	~CFile();
 | 
			
		||||
 | 
			
		||||
	operator std::FILE*() const { return file_; }
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +29,7 @@ public:
 | 
			
		|||
	const char* name() const { return name_.c_str(); }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	std::string name_;
 | 
			
		||||
	srb2::String name_;
 | 
			
		||||
	std::FILE* file_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,8 +14,8 @@
 | 
			
		|||
#include <chrono>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "audio_encoder.hpp"
 | 
			
		||||
#include "video_encoder.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +30,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	struct Config
 | 
			
		||||
	{
 | 
			
		||||
		std::string file_name;
 | 
			
		||||
		srb2::String file_name;
 | 
			
		||||
		dtor_cb_t destructor_callback;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,11 +13,11 @@
 | 
			
		|||
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "../core/hash_map.hpp"
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../command.h"
 | 
			
		||||
 | 
			
		||||
namespace srb2::media
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +26,7 @@ namespace srb2::media
 | 
			
		|||
class Options
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	using map_t = std::unordered_map<std::string, consvar_t>;
 | 
			
		||||
	using map_t = srb2::HashMap<srb2::String, consvar_t>;
 | 
			
		||||
 | 
			
		||||
	template <typename T>
 | 
			
		||||
	struct Range
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ public:
 | 
			
		|||
	static consvar_t values(const char* default_value, const Range<T> range, std::map<std::string_view, T> list = {});
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	static std::vector<consvar_t*> cvars_;
 | 
			
		||||
	static srb2::Vector<consvar_t*> cvars_;
 | 
			
		||||
 | 
			
		||||
	const char* prefix_;
 | 
			
		||||
	map_t map_;
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +54,11 @@ private:
 | 
			
		|||
	const consvar_t& cvar(const char* option) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
extern template consvar_t Options::values(const char* default_value, const Range<int> range, std::map<std::string_view, int> list);
 | 
			
		||||
extern template consvar_t Options::values(const char* default_value, const Range<float> range, std::map<std::string_view, float> list);
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
}; // namespace srb2::media
 | 
			
		||||
 | 
			
		||||
#endif // __SRB2_MEDIA_OPTIONS_HPP__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@
 | 
			
		|||
 | 
			
		||||
#include <vpx/vpx_encoder.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "options.hpp"
 | 
			
		||||
#include "vorbis.hpp"
 | 
			
		||||
#include "vp8.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +24,7 @@ using namespace srb2::media;
 | 
			
		|||
// to be defined in the same translation unit as
 | 
			
		||||
// Options::cvars_ to guarantee initialization order.
 | 
			
		||||
 | 
			
		||||
std::vector<consvar_t*> Options::cvars_;
 | 
			
		||||
srb2::Vector<consvar_t*> Options::cvars_;
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
const Options VorbisEncoder::options_("vorbis", {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,11 +11,11 @@
 | 
			
		|||
#ifndef __SRB2_MEDIA_VORBIS_ERROR_HPP__
 | 
			
		||||
#define __SRB2_MEDIA_VORBIS_ERROR_HPP__
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <vorbis/codec.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
 | 
			
		||||
class VorbisError
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ public:
 | 
			
		|||
 | 
			
		||||
	operator int() const { return error_; }
 | 
			
		||||
 | 
			
		||||
	std::string name() const
 | 
			
		||||
	srb2::String name() const
 | 
			
		||||
	{
 | 
			
		||||
		switch (error_)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -43,12 +43,12 @@ private:
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct fmt::formatter<VorbisError> : formatter<std::string>
 | 
			
		||||
struct fmt::formatter<VorbisError> : formatter<srb2::String>
 | 
			
		||||
{
 | 
			
		||||
	template <typename FormatContext>
 | 
			
		||||
	auto format(const VorbisError& error, FormatContext& ctx) const
 | 
			
		||||
	{
 | 
			
		||||
		return formatter<std::string>::format(error.name(), ctx);
 | 
			
		||||
		return formatter<srb2::String>::format(error.name(), ctx);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,22 +11,22 @@
 | 
			
		|||
#ifndef __SRB2_MEDIA_VPX_ERROR_HPP__
 | 
			
		||||
#define __SRB2_MEDIA_VPX_ERROR_HPP__
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <vpx/vpx_codec.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
 | 
			
		||||
class VpxError
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	VpxError(vpx_codec_ctx_t& ctx) : ctx_(&ctx) {}
 | 
			
		||||
 | 
			
		||||
	std::string description() const
 | 
			
		||||
	srb2::String description() const
 | 
			
		||||
	{
 | 
			
		||||
		const char* error = vpx_codec_error(ctx_);
 | 
			
		||||
		const char* detail = vpx_codec_error_detail(ctx_);
 | 
			
		||||
 | 
			
		||||
		return detail ? fmt::format("{}: {}", error, detail) : error;
 | 
			
		||||
		return detail ? srb2::format("{}: {}", error, detail) : error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
| 
						 | 
				
			
			@ -34,12 +34,12 @@ private:
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct fmt::formatter<VpxError> : formatter<std::string>
 | 
			
		||||
struct fmt::formatter<VpxError> : formatter<srb2::String>
 | 
			
		||||
{
 | 
			
		||||
	template <typename FormatContext>
 | 
			
		||||
	auto format(const VpxError& error, FormatContext& ctx) const
 | 
			
		||||
	{
 | 
			
		||||
		return formatter<std::string>::format(error.description(), ctx);
 | 
			
		||||
		return formatter<srb2::String>::format(error.description(), ctx);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,11 +13,11 @@
 | 
			
		|||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <mkvmuxer/mkvmuxer.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/hash_map.hpp"
 | 
			
		||||
#include "container.hpp"
 | 
			
		||||
#include "webm.hpp"
 | 
			
		||||
#include "webm_writer.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +88,7 @@ private:
 | 
			
		|||
 | 
			
		||||
	mutable std::recursive_mutex queue_mutex_;
 | 
			
		||||
 | 
			
		||||
	std::unordered_map<webm::track, FrameQueue> queue_;
 | 
			
		||||
	srb2::HashMap<webm::track, FrameQueue> queue_;
 | 
			
		||||
 | 
			
		||||
	webm::timestamp latest_timestamp_ = 0;
 | 
			
		||||
	std::size_t queue_size_ = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,10 +12,10 @@
 | 
			
		|||
#define __SRB2_MEDIA_WEBM_WRITER_HPP__
 | 
			
		||||
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <mkvmuxer/mkvwriter.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "cfile.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2::media
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ namespace srb2::media
 | 
			
		|||
class WebmWriter : public CFile, public mkvmuxer::MkvWriter
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	WebmWriter(const std::string file_name) : CFile(file_name), MkvWriter(static_cast<std::FILE*>(*this)) {}
 | 
			
		||||
	WebmWriter(const srb2::String& file_name) : CFile(file_name), MkvWriter(static_cast<std::FILE*>(*this)) {}
 | 
			
		||||
	~WebmWriter() { MkvWriter::Close(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,8 +12,8 @@
 | 
			
		|||
#define __SRB2_MEDIA_YUV420P_HPP__
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "video_frame.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2::media
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ public:
 | 
			
		|||
		int width_ = 0;
 | 
			
		||||
		int height_ = 0;
 | 
			
		||||
 | 
			
		||||
		std::vector<uint8_t> vec_;
 | 
			
		||||
		srb2::Vector<uint8_t> vec_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	YUV420pFrame(int pts, Buffer y, Buffer u, Buffer v, const BufferRGBA& rgba);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,11 +11,11 @@
 | 
			
		|||
#include <algorithm>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <fmt/chrono.h>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "../../core/string.h"
 | 
			
		||||
#include "../../cxxutil.hpp"
 | 
			
		||||
#include "../../v_draw.hpp"
 | 
			
		||||
#include "EggTV.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -85,11 +85,11 @@ void draw_face(const Draw& draw, const EggTVData::Replay::Standing& player, face
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string player_time_string(const EggTVData::Replay::Standing& player)
 | 
			
		||||
srb2::String player_time_string(const EggTVData::Replay::Standing& player)
 | 
			
		||||
{
 | 
			
		||||
	if (player.time)
 | 
			
		||||
	{
 | 
			
		||||
		return fmt::format(
 | 
			
		||||
		return srb2::format(
 | 
			
		||||
			R"({}'{}"{})",
 | 
			
		||||
			G_TicsToMinutes(*player.time, true),
 | 
			
		||||
			G_TicsToSeconds(*player.time),
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +102,7 @@ std::string player_time_string(const EggTVData::Replay::Standing& player)
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string player_points_string(const EggTVData::Replay::Standing& player)
 | 
			
		||||
srb2::String player_points_string(const EggTVData::Replay::Standing& player)
 | 
			
		||||
{
 | 
			
		||||
	return player.score ? fmt::format("{} PTS", *player.score) : "NO CONTEST";
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,7 @@
 | 
			
		|||
#include "EggTVData.hpp"
 | 
			
		||||
#include "EggTVGraphics.hpp"
 | 
			
		||||
 | 
			
		||||
#include "../../core/vector.hpp"
 | 
			
		||||
#include "../../doomdef.h" // TICRATE
 | 
			
		||||
#include "../../i_time.h"
 | 
			
		||||
#include "../../k_menu.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +198,7 @@ private:
 | 
			
		|||
	{
 | 
			
		||||
	public:
 | 
			
		||||
		using limiter_t = std::function<int()>;
 | 
			
		||||
		using anims_t = std::vector<Animation*>;
 | 
			
		||||
		using anims_t = srb2::Vector<Animation*>;
 | 
			
		||||
 | 
			
		||||
		explicit Cursor(anims_t anims, limiter_t limiter) : limiter_(limiter), anims_(anims) {}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,13 +14,14 @@
 | 
			
		|||
#include <fstream>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <fmt/std.h> // std::filesystem::path formatter
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../../core/string.h"
 | 
			
		||||
#include "../../core/json.hpp"
 | 
			
		||||
#include "../../io/streams.hpp"
 | 
			
		||||
#include "../../cxxutil.hpp"
 | 
			
		||||
#include "EggTVData.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -34,15 +35,15 @@ using namespace srb2::menus::egg_tv;
 | 
			
		|||
 | 
			
		||||
namespace fs = std::filesystem;
 | 
			
		||||
 | 
			
		||||
using nlohmann::json;
 | 
			
		||||
using srb2::JsonValue;
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct fmt::formatter<fs::filesystem_error> : formatter<std::string>
 | 
			
		||||
struct fmt::formatter<fs::filesystem_error> : formatter<srb2::String>
 | 
			
		||||
{
 | 
			
		||||
	template <typename FormatContext>
 | 
			
		||||
	auto format(const fs::filesystem_error& ex, FormatContext& ctx) const
 | 
			
		||||
	{
 | 
			
		||||
		return formatter<std::string>::format(
 | 
			
		||||
		return formatter<srb2::String>::format(
 | 
			
		||||
			fmt::format("{}, path1={}, path2={}", ex.what(), ex.path1(), ex.path2()),
 | 
			
		||||
			ctx
 | 
			
		||||
		);
 | 
			
		||||
| 
						 | 
				
			
			@ -65,13 +66,13 @@ To time_point_conv(From time)
 | 
			
		|||
	return std::chrono::time_point_cast<typename To::duration>(To::clock::now() + (time - From::clock::now()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
json& ensure_array(json& object, const char* key)
 | 
			
		||||
JsonValue& ensure_array(JsonValue& object, const char* key)
 | 
			
		||||
{
 | 
			
		||||
	json& array = object[key];
 | 
			
		||||
	JsonValue& array = object[key];
 | 
			
		||||
 | 
			
		||||
	if (!array.is_array())
 | 
			
		||||
	{
 | 
			
		||||
		array = json::array();
 | 
			
		||||
		array = JsonValue::array();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return array;
 | 
			
		||||
| 
						 | 
				
			
			@ -91,18 +92,16 @@ EggTVData::EggTVData() : favorites_(ensure_array(favoritesFile_, "favorites"))
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
json EggTVData::cache_favorites() const
 | 
			
		||||
JsonValue EggTVData::cache_favorites() const
 | 
			
		||||
{
 | 
			
		||||
	json object;
 | 
			
		||||
	JsonValue object;
 | 
			
		||||
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		std::ifstream f(favoritesPath_);
 | 
			
		||||
 | 
			
		||||
		if (f.is_open())
 | 
			
		||||
		{
 | 
			
		||||
			f >> object;
 | 
			
		||||
		}
 | 
			
		||||
		srb2::io::FileStream stream { favoritesPath_.generic_string() };
 | 
			
		||||
		srb2::Vector<std::byte> f = srb2::io::read_to_vec(stream);
 | 
			
		||||
		srb2::String json_string { (const char*)f.data(), f.size() };
 | 
			
		||||
		object = JsonValue::from_json_string(json_string);
 | 
			
		||||
	}
 | 
			
		||||
	catch (const std::exception& ex)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -199,9 +198,9 @@ EggTVData::Folder::Folder(EggTVData& tv, const fs::directory_entry& entry) :
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EggTVData::Replay::Title::operator const std::string() const
 | 
			
		||||
EggTVData::Replay::Title::operator const srb2::String() const
 | 
			
		||||
{
 | 
			
		||||
	return second().empty() ? first() : fmt::format("{} - {}", first(), second());
 | 
			
		||||
	return second().empty() ? first() : srb2::format("{} - {}", first(), second());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EggTVData::Replay::Replay(Folder::Cache::ReplayRef& ref) : ref_(&ref)
 | 
			
		||||
| 
						 | 
				
			
			@ -230,7 +229,7 @@ EggTVData::Replay::Replay(Folder::Cache::ReplayRef& ref) : ref_(&ref)
 | 
			
		|||
		const std::string_view str = info.title;
 | 
			
		||||
		const std::size_t mid = str.find(kDelimiter);
 | 
			
		||||
 | 
			
		||||
		title_ = Title(str.substr(0, mid), mid == std::string::npos ? "" : str.substr(mid + kDelimiter.size()));
 | 
			
		||||
		title_ = Title(str.substr(0, mid), mid == srb2::String::npos ? "" : str.substr(mid + kDelimiter.size()));
 | 
			
		||||
		//title_ = Title("WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", "WWWWWWWWWWWWWWWWWWWWWWWWWWW");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -316,13 +315,13 @@ void EggTVData::Replay::toggle_favorite() const
 | 
			
		|||
{
 | 
			
		||||
	const auto& it = ref_->iterator_to_favorite();
 | 
			
		||||
 | 
			
		||||
	if (it != ref_->favorites().end())
 | 
			
		||||
	if (it != ref_->favorites().as_array().end())
 | 
			
		||||
	{
 | 
			
		||||
		ref_->favorites().erase(it);
 | 
			
		||||
		ref_->favorites().as_array().erase(it);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		ref_->favorites().emplace_back(ref_->favorites_path());
 | 
			
		||||
		ref_->favorites().as_array().emplace_back(ref_->favorites_path());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ref_->cache().folder().tv().save_favorites();
 | 
			
		||||
| 
						 | 
				
			
			@ -382,7 +381,9 @@ void EggTVData::save_favorites() const
 | 
			
		|||
{
 | 
			
		||||
	try
 | 
			
		||||
	{
 | 
			
		||||
		std::ofstream(favoritesPath_) << favoritesFile_;
 | 
			
		||||
		srb2::String json_string = favoritesFile_.to_json_string();
 | 
			
		||||
		srb2::io::FileStream fs { favoritesPath_.generic_string(), srb2::io::FileStreamMode::kWrite };
 | 
			
		||||
		srb2::io::write_exact(fs, tcb::as_bytes(tcb::span(json_string.data(), json_string.size())));
 | 
			
		||||
	}
 | 
			
		||||
	catch (const std::exception& ex)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,14 +17,13 @@
 | 
			
		|||
#include <filesystem>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <variant>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../../core/string.h"
 | 
			
		||||
#include "../../core/json.hpp"
 | 
			
		||||
#include "../../cxxutil.hpp"
 | 
			
		||||
 | 
			
		||||
#include "../../d_main.h" // srb2home
 | 
			
		||||
| 
						 | 
				
			
			@ -40,10 +39,10 @@ private:
 | 
			
		|||
	const std::filesystem::path root_ = std::filesystem::path{srb2home} / "media/replay/online";
 | 
			
		||||
	const std::filesystem::path favoritesPath_ = root_ / "favorites.json";
 | 
			
		||||
 | 
			
		||||
	nlohmann::json favoritesFile_ = cache_favorites();
 | 
			
		||||
	nlohmann::json& favorites_;
 | 
			
		||||
	JsonValue favoritesFile_ = cache_favorites();
 | 
			
		||||
	JsonValue& favorites_;
 | 
			
		||||
 | 
			
		||||
	nlohmann::json cache_favorites() const;
 | 
			
		||||
	JsonValue cache_favorites() const;
 | 
			
		||||
 | 
			
		||||
	void cache_folders();
 | 
			
		||||
	void save_favorites() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -91,20 +90,21 @@ public:
 | 
			
		|||
					released_ = true;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				bool favorited() const { return iterator_to_favorite() != favorites().end(); }
 | 
			
		||||
				nlohmann::json& favorites() const { return cache().folder().tv().favorites_; }
 | 
			
		||||
				bool favorited() const { return iterator_to_favorite() != favorites().as_array().end(); }
 | 
			
		||||
				JsonValue& favorites() const { return cache().folder().tv().favorites_; }
 | 
			
		||||
 | 
			
		||||
				std::string favorites_path() const
 | 
			
		||||
				srb2::String favorites_path() const
 | 
			
		||||
				{
 | 
			
		||||
					// path::generic_string converts to forward
 | 
			
		||||
					// slashes on Windows. This should suffice to make
 | 
			
		||||
					// the JSON file portable across installations.
 | 
			
		||||
					return (std::filesystem::path{cache().folder().name()} / filename()).generic_string();
 | 
			
		||||
					return (std::filesystem::path{std::string_view(cache().folder().name())} / filename()).generic_string();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				nlohmann::json::const_iterator iterator_to_favorite() const
 | 
			
		||||
				JsonArray::const_iterator iterator_to_favorite() const
 | 
			
		||||
				{
 | 
			
		||||
					return std::find(favorites().begin(), favorites().end(), favorites_path());
 | 
			
		||||
					srb2::String path = favorites_path();
 | 
			
		||||
					return std::find(favorites().as_array().begin(), favorites().as_array().end(), static_cast<std::string_view>(path));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			private:
 | 
			
		||||
| 
						 | 
				
			
			@ -134,13 +134,13 @@ public:
 | 
			
		|||
		int y = 0;
 | 
			
		||||
 | 
			
		||||
		bool empty() { return size() == 0; }
 | 
			
		||||
		std::filesystem::path path() const { return tv_->root_ / name_; }
 | 
			
		||||
		std::filesystem::path path() const { return tv_->root_ / std::string_view(name_); }
 | 
			
		||||
 | 
			
		||||
		EggTVData& tv() const { return *tv_; }
 | 
			
		||||
 | 
			
		||||
		std::size_t size() const { return size_; }
 | 
			
		||||
		const time_point_t& time() const { return time_; }
 | 
			
		||||
		const std::string& name() const { return name_; }
 | 
			
		||||
		const srb2::String& name() const { return name_; }
 | 
			
		||||
 | 
			
		||||
		std::unique_ptr<Cache> load() { return std::make_unique<Cache>(*this); };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -150,7 +150,7 @@ public:
 | 
			
		|||
		std::size_t size_;
 | 
			
		||||
		time_point_t time_;
 | 
			
		||||
		EggTVData* tv_;
 | 
			
		||||
		std::string name_;
 | 
			
		||||
		srb2::String name_;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	class Replay
 | 
			
		||||
| 
						 | 
				
			
			@ -165,18 +165,18 @@ public:
 | 
			
		|||
			{
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const std::string& first() const { return first_; }
 | 
			
		||||
			const std::string& second() const { return second_; }
 | 
			
		||||
			const srb2::String& first() const { return first_; }
 | 
			
		||||
			const srb2::String& second() const { return second_; }
 | 
			
		||||
 | 
			
		||||
			operator const std::string() const;
 | 
			
		||||
			operator const srb2::String() const;
 | 
			
		||||
 | 
			
		||||
		private:
 | 
			
		||||
			std::string first_, second_;
 | 
			
		||||
			srb2::String first_, second_;
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		struct Standing
 | 
			
		||||
		{
 | 
			
		||||
			std::string name;
 | 
			
		||||
			srb2::String name;
 | 
			
		||||
			std::optional<std::size_t> skin;
 | 
			
		||||
			std::size_t color;
 | 
			
		||||
			std::optional<tic_t> time;
 | 
			
		||||
| 
						 | 
				
			
			@ -247,7 +247,7 @@ public:
 | 
			
		|||
		void toggle_favorite() const;
 | 
			
		||||
 | 
			
		||||
		bool invalid() const { return invalid_; }
 | 
			
		||||
		bool favorited() const { return ref_->iterator_to_favorite() != ref_->favorites().end(); }
 | 
			
		||||
		bool favorited() const { return ref_->iterator_to_favorite() != ref_->favorites().as_array().end(); }
 | 
			
		||||
 | 
			
		||||
		std::filesystem::path path() const { return ref_->cache().folder().path() / ref_->filename(); }
 | 
			
		||||
		const time_point_t& date() const { return ref_->time(); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,8 +13,8 @@
 | 
			
		|||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
#include "../../core/hash_map.hpp"
 | 
			
		||||
#include "../../doomdef.h" // skincolornum_t
 | 
			
		||||
#include "../../v_draw.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +90,7 @@ public:
 | 
			
		|||
 | 
			
		||||
		patch select		= "RHTVSQSL";
 | 
			
		||||
 | 
			
		||||
		std::unordered_map<std::string_view, patch> gametype = {
 | 
			
		||||
		srb2::HashMap<std::string_view, patch> gametype = {
 | 
			
		||||
			{"Race",			"RHGT1"},
 | 
			
		||||
			{"Battle",			"RHGT2"},
 | 
			
		||||
			{"Prison Break",	"RHGT3"},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,8 @@
 | 
			
		|||
/// \file  menus/extras-challenges.c
 | 
			
		||||
/// \brief Statistics menu
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
#include "../k_menu.h"
 | 
			
		||||
#include "../z_zone.h"
 | 
			
		||||
#include "../m_cond.h" // Condition Sets
 | 
			
		||||
| 
						 | 
				
			
			@ -260,7 +262,7 @@ static void M_StatisticsPageInit(void)
 | 
			
		|||
			M_StatisticsChars();
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
		case statisticspage_gp:
 | 
			
		||||
		{
 | 
			
		||||
			M_StatisticsGP();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,8 @@
 | 
			
		|||
 | 
			
		||||
#include <forward_list>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
 | 
			
		||||
static void M_GonerDrawer(void);
 | 
			
		||||
static void M_GonerConclude(INT32 choice);
 | 
			
		||||
static boolean M_GonerInputs(INT32 ch);
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +48,7 @@ menuitem_t MAIN_Goner[] =
 | 
			
		|||
		{.routine = M_VideoOptions}, 0, 0},
 | 
			
		||||
 | 
			
		||||
	{IT_STRING | IT_CALL, "SOUND OPTIONS",
 | 
			
		||||
		"CALIBRATE AURAL DATASTREAM.", NULL, 
 | 
			
		||||
		"CALIBRATE AURAL DATASTREAM.", NULL,
 | 
			
		||||
		{.routine = M_SoundOptions}, 0, 0},
 | 
			
		||||
 | 
			
		||||
	{IT_STRING | IT_CALL, "PROFILE SETUP",
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +97,7 @@ class GonerSpeaker
 | 
			
		|||
public:
 | 
			
		||||
	float offset = 0;
 | 
			
		||||
 | 
			
		||||
	GonerSpeaker(std::string skinName, float offset)
 | 
			
		||||
	GonerSpeaker(const srb2::String& skinName, float offset)
 | 
			
		||||
	{
 | 
			
		||||
		if (!skinName.empty())
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -144,11 +146,11 @@ class GonerChatLine
 | 
			
		|||
{
 | 
			
		||||
public:
 | 
			
		||||
	gonerspeakers_t speaker;
 | 
			
		||||
	std::string dialogue;
 | 
			
		||||
	srb2::String dialogue;
 | 
			
		||||
	int value; // Mutlipurpose.
 | 
			
		||||
	void (*routine)(void);
 | 
			
		||||
 | 
			
		||||
	GonerChatLine(gonerspeakers_t speaker, int delay, std::string dialogue)
 | 
			
		||||
	GonerChatLine(gonerspeakers_t speaker, int delay, const srb2::String& dialogue)
 | 
			
		||||
	{
 | 
			
		||||
		char *newText = V_ScaledWordWrap(
 | 
			
		||||
			(BASEVIDWIDTH/2 + 6) << FRACBITS,
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +160,7 @@ public:
 | 
			
		|||
		);
 | 
			
		||||
 | 
			
		||||
		this->speaker = speaker;
 | 
			
		||||
		this->dialogue = std::string(newText);
 | 
			
		||||
		this->dialogue = srb2::String(newText);
 | 
			
		||||
		this->value = delay;
 | 
			
		||||
 | 
			
		||||
		this->routine = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -1142,7 +1144,7 @@ static void M_GonerDrawer(void)
 | 
			
		|||
 | 
			
		||||
	for (auto & element : LinesOutput)
 | 
			
		||||
	{
 | 
			
		||||
		std::string text;
 | 
			
		||||
		srb2::String text;
 | 
			
		||||
		INT32 flags;
 | 
			
		||||
 | 
			
		||||
		if (newy < 0) break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -174,7 +174,7 @@ void Music_Init(void)
 | 
			
		|||
	{
 | 
			
		||||
		Tune& tune = g_tunes.insert("credits");
 | 
			
		||||
 | 
			
		||||
		tune.priority = 100;
 | 
			
		||||
		tune.priority = 101;
 | 
			
		||||
		tune.song = "_creds";
 | 
			
		||||
		tune.credit = true;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -182,7 +182,7 @@ void Music_Init(void)
 | 
			
		|||
	{
 | 
			
		||||
		Tune& tune = g_tunes.insert("shore");
 | 
			
		||||
 | 
			
		||||
		tune.priority = 100;
 | 
			
		||||
		tune.priority = 101;
 | 
			
		||||
		tune.loop = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,13 +14,13 @@
 | 
			
		|||
#include <cstdint>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "music_manager.hpp"
 | 
			
		||||
#include "music_tune.hpp"
 | 
			
		||||
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "d_clisrv.h"
 | 
			
		||||
#include "doomtype.h"
 | 
			
		||||
#include "i_sound.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -49,8 +49,8 @@ void TuneManager::tick()
 | 
			
		|||
 | 
			
		||||
	Tune* tune = current_tune();
 | 
			
		||||
 | 
			
		||||
	std::string old_song = current_song_;
 | 
			
		||||
	current_song_ = tune && tune->playing() && !tune->suspend ? tune->song : std::string{};
 | 
			
		||||
	srb2::String old_song = current_song_;
 | 
			
		||||
	current_song_ = tune && tune->playing() && !tune->suspend ? tune->song : srb2::String{};
 | 
			
		||||
 | 
			
		||||
	bool changed = current_song_ != old_song;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +167,8 @@ void TuneManager::pause_unpause() const
 | 
			
		|||
 | 
			
		||||
bool TuneManager::load() const
 | 
			
		||||
{
 | 
			
		||||
	lumpnum_t lumpnum = W_CheckNumForLongName(fmt::format("O_{}", current_song_).c_str());
 | 
			
		||||
	srb2::String lumpstring = srb2::format("O_{}", current_song_);
 | 
			
		||||
	lumpnum_t lumpnum = W_CheckNumForLongName(lumpstring.c_str());
 | 
			
		||||
 | 
			
		||||
	if (lumpnum == LUMPERROR)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,11 +11,10 @@
 | 
			
		|||
#ifndef MUSIC_MANAGER_HPP
 | 
			
		||||
#define MUSIC_MANAGER_HPP
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
#include "core/hash_map.hpp"
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "cxxutil.hpp"
 | 
			
		||||
#include "music_tune.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +24,7 @@ namespace srb2::music
 | 
			
		|||
class TuneManager
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
	const std::string& current_song() const { return current_song_; }
 | 
			
		||||
	const srb2::String& current_song() const { return current_song_; }
 | 
			
		||||
 | 
			
		||||
	Tune* current_tune() const
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -99,8 +98,8 @@ public:
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	std::unordered_map<std::string, Tune> map_;
 | 
			
		||||
	std::string current_song_;
 | 
			
		||||
	srb2::HashMap<srb2::String, Tune> map_;
 | 
			
		||||
	srb2::String current_song_;
 | 
			
		||||
 | 
			
		||||
	tic_t time_sync_;
 | 
			
		||||
	tic_t time_local_;
 | 
			
		||||
| 
						 | 
				
			
			@ -113,11 +112,20 @@ private:
 | 
			
		|||
 | 
			
		||||
	decltype(map_)::const_iterator current_iterator() const
 | 
			
		||||
	{
 | 
			
		||||
		return std::max_element(
 | 
			
		||||
			map_.begin(),
 | 
			
		||||
			map_.end(),
 | 
			
		||||
			[](const auto& a, const auto& b) { return a.second < b.second; }
 | 
			
		||||
		);
 | 
			
		||||
		// Not using std::max_element due to buggy clang libc++ LegacyInputIterator assertion
 | 
			
		||||
		auto first = map_.begin();
 | 
			
		||||
		auto last = map_.end();
 | 
			
		||||
		if (first == last) return first;
 | 
			
		||||
 | 
			
		||||
		auto max = first;
 | 
			
		||||
		while (++first != last)
 | 
			
		||||
		{
 | 
			
		||||
			if (max->second < first->second)
 | 
			
		||||
			{
 | 
			
		||||
				max = first;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return max;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool load() const;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,8 +14,8 @@
 | 
			
		|||
#include <algorithm>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "doomdef.h"
 | 
			
		||||
#include "doomtype.h"
 | 
			
		||||
#include "k_boss.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ class Tune
 | 
			
		|||
public:
 | 
			
		||||
	explicit Tune() {}
 | 
			
		||||
 | 
			
		||||
	std::string song; // looks up the lump
 | 
			
		||||
	srb2::String song; // looks up the lump
 | 
			
		||||
 | 
			
		||||
	// Higher priority tunes play first.
 | 
			
		||||
	int priority = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,8 @@
 | 
			
		|||
/// \file  ballhog.cpp
 | 
			
		||||
/// \brief Ballhog item code.
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
#include "../doomdef.h"
 | 
			
		||||
#include "../doomstat.h"
 | 
			
		||||
#include "../info.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,13 +9,13 @@
 | 
			
		|||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "../mobj_list.hpp"
 | 
			
		||||
 | 
			
		||||
#include "../core/hash_map.hpp"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../doomdef.h"
 | 
			
		||||
#include "../doomtype.h"
 | 
			
		||||
#include "../info.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -224,7 +224,7 @@ struct Checkpoint : mobj_t
 | 
			
		|||
				speed(speed() - FixedDiv(speed() / 50, max<fixed_t>(speed_multiplier(), 1)));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		if (!top_half_has_passed())
 | 
			
		||||
		{
 | 
			
		||||
			sparkle_between(0);
 | 
			
		||||
| 
						 | 
				
			
			@ -506,7 +506,7 @@ struct CheckpointManager
 | 
			
		|||
 | 
			
		||||
	auto count() { return list_.count(); }
 | 
			
		||||
 | 
			
		||||
	const std::vector<line_t*>* lines_for(const Checkpoint* chk) const
 | 
			
		||||
	const srb2::Vector<line_t*>* lines_for(const Checkpoint* chk) const
 | 
			
		||||
	{
 | 
			
		||||
		auto it = lines_.find(chk->linetag());
 | 
			
		||||
		return it != lines_.end() ? &it->second : nullptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -514,11 +514,11 @@ struct CheckpointManager
 | 
			
		|||
 | 
			
		||||
private:
 | 
			
		||||
	srb2::MobjList<Checkpoint, svg_checkpoints> list_;
 | 
			
		||||
	std::unordered_map<INT32, std::vector<line_t*>> lines_;
 | 
			
		||||
	srb2::HashMap<INT32, srb2::Vector<line_t*>> lines_;
 | 
			
		||||
 | 
			
		||||
	static std::vector<line_t*> tagged_lines(INT32 tag)
 | 
			
		||||
	static srb2::Vector<line_t*> tagged_lines(INT32 tag)
 | 
			
		||||
	{
 | 
			
		||||
		std::vector<line_t*> checklines;
 | 
			
		||||
		srb2::Vector<line_t*> checklines;
 | 
			
		||||
		INT32 li;
 | 
			
		||||
		TAG_ITER_LINES(tag, li)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -573,18 +573,18 @@ void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixe
 | 
			
		|||
			}
 | 
			
		||||
 | 
			
		||||
			LineOnDemand* gate;
 | 
			
		||||
			const std::vector<line_t*>* lines = g_checkpoints.lines_for(chk);
 | 
			
		||||
			const srb2::Vector<line_t*>* lines = g_checkpoints.lines_for(chk);
 | 
			
		||||
 | 
			
		||||
			if (!lines || lines->empty())
 | 
			
		||||
			{
 | 
			
		||||
				LineOnDemand dyngate = chk->crossing_line();
 | 
			
		||||
				if (!ray.overlaps(dyngate))
 | 
			
		||||
					return false;
 | 
			
		||||
				gate = &dyngate;				
 | 
			
		||||
				gate = &dyngate;
 | 
			
		||||
			}
 | 
			
		||||
			else 
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				auto it = find_if(
 | 
			
		||||
				auto it = std::find_if(
 | 
			
		||||
					lines->begin(),
 | 
			
		||||
					lines->end(),
 | 
			
		||||
					[&](const line_t* line)
 | 
			
		||||
| 
						 | 
				
			
			@ -592,7 +592,7 @@ void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixe
 | 
			
		|||
						return ray.overlaps(*line);
 | 
			
		||||
					}
 | 
			
		||||
				);
 | 
			
		||||
				
 | 
			
		||||
 | 
			
		||||
				if (it == lines->end())
 | 
			
		||||
				{
 | 
			
		||||
					return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -615,7 +615,7 @@ void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixe
 | 
			
		|||
			{
 | 
			
		||||
				// Did not cross.
 | 
			
		||||
				return false;
 | 
			
		||||
				
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,8 +13,6 @@
 | 
			
		|||
/// \brief Do all the WAD I/O, get map description, set up initial state and misc. LUTs
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -96,6 +94,8 @@
 | 
			
		|||
#include "taglist.h"
 | 
			
		||||
 | 
			
		||||
// SRB2Kart
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "core/vector.hpp"
 | 
			
		||||
#include "k_kart.h"
 | 
			
		||||
#include "k_race.h"
 | 
			
		||||
#include "k_battle.h" // K_BattleInit
 | 
			
		||||
| 
						 | 
				
			
			@ -7854,7 +7854,7 @@ static void P_LoadRecordGhosts(void)
 | 
			
		|||
			map(cv_ghost_last, value, kLast);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	auto add_ghosts = [gpath](const std::string& base, UINT8 bits)
 | 
			
		||||
	auto add_ghosts = [gpath](const srb2::String& base, UINT8 bits)
 | 
			
		||||
	{
 | 
			
		||||
		auto load = [base](const char* suffix) { P_TryAddExternalGhost(fmt::format("{}-{}.lmp", base, suffix).c_str()); };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -7991,7 +7991,7 @@ static void P_ShuffleTeams(void)
 | 
			
		|||
 | 
			
		||||
	CONS_Debug(DBG_TEAMS, "Shuffling player teams...\n");
 | 
			
		||||
 | 
			
		||||
	std::vector<UINT8> player_shuffle;
 | 
			
		||||
	srb2::Vector<UINT8> player_shuffle;
 | 
			
		||||
	for (i = 0; i < MAXPLAYERS; i++)
 | 
			
		||||
	{
 | 
			
		||||
		if (playeringame[i] == false || players[i].spectator == true)
 | 
			
		||||
| 
						 | 
				
			
			@ -9135,7 +9135,7 @@ static tic_t round_to_next_second(tic_t time)
 | 
			
		|||
static void P_DeriveAutoMedalTimes(mapheader_t& map)
 | 
			
		||||
{
 | 
			
		||||
	// Gather staff ghost times
 | 
			
		||||
	std::vector<tic_t> stafftimes;
 | 
			
		||||
	srb2::Vector<tic_t> stafftimes;
 | 
			
		||||
	for (int i = 0; i < map.ghostCount; i++)
 | 
			
		||||
	{
 | 
			
		||||
		tic_t time = map.ghostBrief[i]->time;
 | 
			
		||||
| 
						 | 
				
			
			@ -9325,7 +9325,7 @@ UINT8 P_InitMapData(void)
 | 
			
		|||
				{
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
				std::string ghostdirname = fmt::format("staffghosts/{}/", mapheaderinfo[i]->lumpname);
 | 
			
		||||
				srb2::String ghostdirname = srb2::format("staffghosts/{}/", mapheaderinfo[i]->lumpname);
 | 
			
		||||
 | 
			
		||||
				UINT16 lumpstart = W_CheckNumForFolderStartPK3(ghostdirname.c_str(), wadindex, 0);
 | 
			
		||||
				if (lumpstart == INT16_MAX)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,12 +9,12 @@
 | 
			
		|||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <unordered_set>
 | 
			
		||||
 | 
			
		||||
#include "r_debug.hpp"
 | 
			
		||||
#include "v_draw.hpp"
 | 
			
		||||
 | 
			
		||||
#include "core/hash_map.hpp"
 | 
			
		||||
#include "core/hash_set.hpp"
 | 
			
		||||
#include "doomdef.h"
 | 
			
		||||
#include "doomtype.h"
 | 
			
		||||
#include "r_textures.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ using srb2::Draw;
 | 
			
		|||
namespace
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
std::unordered_set<INT32> frame_list;
 | 
			
		||||
srb2::HashSet<INT32> frame_list;
 | 
			
		||||
 | 
			
		||||
}; // namespace
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,8 @@
 | 
			
		|||
///        while maintaining a per column clipping list only.
 | 
			
		||||
///        Moreover, the sky areas have to be determined.
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
 | 
			
		||||
#include <tracy/tracy/Tracy.hpp>
 | 
			
		||||
 | 
			
		||||
#include "command.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,14 +13,14 @@
 | 
			
		|||
#include <cstdint>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "core/hash_map.hpp"
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "core/vector.hpp"
 | 
			
		||||
#include "cxxutil.hpp"
 | 
			
		||||
#include "doomstat.h"
 | 
			
		||||
#include "r_textures.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -29,14 +29,14 @@
 | 
			
		|||
namespace
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
std::unordered_map<std::string, std::vector<const texture_t*>> g_dups;
 | 
			
		||||
std::map<std::string, std::vector<std::string>> g_warnings;
 | 
			
		||||
srb2::HashMap<srb2::String, srb2::Vector<const texture_t*>> g_dups;
 | 
			
		||||
std::map<srb2::String, srb2::Vector<srb2::String>> g_warnings;
 | 
			
		||||
std::thread g_dups_thread;
 | 
			
		||||
 | 
			
		||||
std::string key8char(const char cstr[8])
 | 
			
		||||
srb2::String key8char(const char cstr[8])
 | 
			
		||||
{
 | 
			
		||||
	std::string_view view(cstr, 8);
 | 
			
		||||
	std::string key;
 | 
			
		||||
	srb2::String key;
 | 
			
		||||
 | 
			
		||||
	view = view.substr(0, view.find('\0')); // terminate by '\0'
 | 
			
		||||
	key.reserve(view.size());
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ std::string key8char(const char cstr[8])
 | 
			
		|||
	return key;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string texture_location(const texture_t& tex)
 | 
			
		||||
srb2::String texture_location(const texture_t& tex)
 | 
			
		||||
{
 | 
			
		||||
	if (tex.type == TEXTURETYPE_SINGLEPATCH)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +59,7 @@ std::string texture_location(const texture_t& tex)
 | 
			
		|||
		const texpatch_t& texpat = tex.patches[0];
 | 
			
		||||
		const wadfile_t& wad = *wadfiles[texpat.wad];
 | 
			
		||||
 | 
			
		||||
		return fmt::format(
 | 
			
		||||
		return srb2::format(
 | 
			
		||||
			"'{}/{}'",
 | 
			
		||||
			std::filesystem::path(wad.filename).filename().string(),
 | 
			
		||||
			wad.lumpinfo[texpat.lump].fullname
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +108,7 @@ void R_CheckTextureDuplicates(INT32 start, INT32 end)
 | 
			
		|||
	auto collate_dups = [end, find_dup](int32_t start)
 | 
			
		||||
	{
 | 
			
		||||
		const texture_t* t1 = textures[start];
 | 
			
		||||
		const std::string key = key8char(t1->name);
 | 
			
		||||
		const srb2::String key = key8char(t1->name);
 | 
			
		||||
 | 
			
		||||
		if (g_dups.find(key) != g_dups.end())
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -119,7 +119,7 @@ void R_CheckTextureDuplicates(INT32 start, INT32 end)
 | 
			
		|||
 | 
			
		||||
		if (idx < end)
 | 
			
		||||
		{
 | 
			
		||||
			std::vector<const texture_t*>& v = g_dups[key];
 | 
			
		||||
			srb2::Vector<const texture_t*>& v = g_dups[key];
 | 
			
		||||
 | 
			
		||||
			v.push_back(textures[start]);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +175,7 @@ void R_PrintTextureWarnings(void)
 | 
			
		|||
	{
 | 
			
		||||
		CONS_Alert(CONS_WARNING, "\n%s", header.c_str());
 | 
			
		||||
 | 
			
		||||
		for (const std::string& warning : v)
 | 
			
		||||
		for (const srb2::String& warning : v)
 | 
			
		||||
		{
 | 
			
		||||
			CONS_Printf("%s\n", warning.c_str());
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,13 +14,13 @@
 | 
			
		|||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <glad/gl.h>
 | 
			
		||||
#include <glm/gtc/type_ptr.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../../core/vector.hpp"
 | 
			
		||||
#include "../shader_load_context.hpp"
 | 
			
		||||
 | 
			
		||||
using namespace srb2;
 | 
			
		||||
| 
						 | 
				
			
			@ -840,8 +840,8 @@ rhi::Handle<rhi::Program> Gl2Rhi::create_program(const ProgramDesc& desc)
 | 
			
		|||
	// breaks the AMD driver's program linker in a bizarre way.
 | 
			
		||||
 | 
			
		||||
	// Process shader sources
 | 
			
		||||
	std::vector<const char*> vert_sources;
 | 
			
		||||
	std::vector<const char*> frag_sources;
 | 
			
		||||
	srb2::Vector<const char*> vert_sources;
 | 
			
		||||
	srb2::Vector<const char*> frag_sources;
 | 
			
		||||
	ShaderLoadContext vert_ctx;
 | 
			
		||||
	ShaderLoadContext frag_ctx;
 | 
			
		||||
	vert_ctx.set_version("120");
 | 
			
		||||
| 
						 | 
				
			
			@ -873,11 +873,11 @@ rhi::Handle<rhi::Program> Gl2Rhi::create_program(const ProgramDesc& desc)
 | 
			
		|||
	{
 | 
			
		||||
		GLint max_length = 0;
 | 
			
		||||
		gl_->GetShaderiv(program.vertex_shader, GL_INFO_LOG_LENGTH, &max_length);
 | 
			
		||||
		std::vector<GLchar> compile_error(max_length);
 | 
			
		||||
		srb2::Vector<GLchar> compile_error(max_length);
 | 
			
		||||
		gl_->GetShaderInfoLog(program.vertex_shader, max_length, &max_length, compile_error.data());
 | 
			
		||||
 | 
			
		||||
		gl_->DeleteShader(program.vertex_shader);
 | 
			
		||||
		throw std::runtime_error(fmt::format("Vertex shader compilation failed: {}", std::string(compile_error.data())));
 | 
			
		||||
		throw std::runtime_error(fmt::format("Vertex shader compilation failed: {}", String(compile_error.data())));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	program.fragment_shader = gl_->CreateShader(GL_FRAGMENT_SHADER);
 | 
			
		||||
| 
						 | 
				
			
			@ -889,11 +889,11 @@ rhi::Handle<rhi::Program> Gl2Rhi::create_program(const ProgramDesc& desc)
 | 
			
		|||
	{
 | 
			
		||||
		GLint max_length = 0;
 | 
			
		||||
		gl_->GetShaderiv(program.fragment_shader, GL_INFO_LOG_LENGTH, &max_length);
 | 
			
		||||
		std::vector<GLchar> compile_error(max_length);
 | 
			
		||||
		srb2::Vector<GLchar> compile_error(max_length);
 | 
			
		||||
		gl_->GetShaderInfoLog(program.fragment_shader, max_length, &max_length, compile_error.data());
 | 
			
		||||
 | 
			
		||||
		gl_->DeleteShader(program.fragment_shader);
 | 
			
		||||
		throw std::runtime_error(fmt::format("Fragment shader compilation failed: {}", std::string(compile_error.data())));
 | 
			
		||||
		throw std::runtime_error(fmt::format("Fragment shader compilation failed: {}", String(compile_error.data())));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	program.program = gl_->CreateProgram();
 | 
			
		||||
| 
						 | 
				
			
			@ -907,13 +907,13 @@ rhi::Handle<rhi::Program> Gl2Rhi::create_program(const ProgramDesc& desc)
 | 
			
		|||
	{
 | 
			
		||||
		GLint max_length = 0;
 | 
			
		||||
		gl_->GetProgramiv(program.program, GL_INFO_LOG_LENGTH, &max_length);
 | 
			
		||||
		std::vector<GLchar> link_error(max_length);
 | 
			
		||||
		srb2::Vector<GLchar> link_error(max_length);
 | 
			
		||||
		gl_->GetProgramInfoLog(program.program, max_length, &max_length, link_error.data());
 | 
			
		||||
 | 
			
		||||
		gl_->DeleteProgram(program.program);
 | 
			
		||||
		gl_->DeleteShader(program.fragment_shader);
 | 
			
		||||
		gl_->DeleteShader(program.vertex_shader);
 | 
			
		||||
		throw std::runtime_error(fmt::format("Pipeline program link failed: {}", std::string(link_error.data())));
 | 
			
		||||
		throw std::runtime_error(fmt::format("Pipeline program link failed: {}", String(link_error.data())));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// get attribute information
 | 
			
		||||
| 
						 | 
				
			
			@ -936,7 +936,7 @@ rhi::Handle<rhi::Program> Gl2Rhi::create_program(const ProgramDesc& desc)
 | 
			
		|||
		GL_ASSERT;
 | 
			
		||||
		GLint location = gl_->GetAttribLocation(program.program, name);
 | 
			
		||||
		GL_ASSERT;
 | 
			
		||||
		program.attrib_locations[std::string(name)] = location;
 | 
			
		||||
		program.attrib_locations[String(name)] = location;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// get uniform information
 | 
			
		||||
| 
						 | 
				
			
			@ -959,7 +959,7 @@ rhi::Handle<rhi::Program> Gl2Rhi::create_program(const ProgramDesc& desc)
 | 
			
		|||
		GL_ASSERT;
 | 
			
		||||
		GLint location = gl_->GetUniformLocation(program.program, name);
 | 
			
		||||
		GL_ASSERT;
 | 
			
		||||
		program.uniform_locations[std::string(name)] = location;
 | 
			
		||||
		program.uniform_locations[String(name)] = location;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Handle<Program> program_handle = program_slab_.insert(std::move(program));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,11 +14,11 @@
 | 
			
		|||
#include <functional>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "../../core/hash_map.hpp"
 | 
			
		||||
#include "../../core/string.h"
 | 
			
		||||
#include "../../core/vector.hpp"
 | 
			
		||||
#include "../rhi.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2::rhi
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +71,7 @@ struct Gl2Platform
 | 
			
		|||
	virtual ~Gl2Platform();
 | 
			
		||||
 | 
			
		||||
	virtual void present() = 0;
 | 
			
		||||
	virtual std::tuple<std::vector<std::string>, std::vector<std::string>> find_shader_sources(const char* name) = 0;
 | 
			
		||||
	virtual std::tuple<srb2::Vector<srb2::String>, srb2::Vector<srb2::String>> find_shader_sources(const char* name) = 0;
 | 
			
		||||
	virtual Rect get_default_framebuffer_dimensions() = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -98,8 +98,8 @@ struct Gl2Program : public rhi::Program
 | 
			
		|||
	uint32_t vertex_shader = 0;
 | 
			
		||||
	uint32_t fragment_shader = 0;
 | 
			
		||||
	uint32_t program = 0;
 | 
			
		||||
	std::unordered_map<std::string, uint32_t> attrib_locations;
 | 
			
		||||
	std::unordered_map<std::string, uint32_t> uniform_locations;
 | 
			
		||||
	srb2::HashMap<srb2::String, uint32_t> attrib_locations;
 | 
			
		||||
	srb2::HashMap<srb2::String, uint32_t> uniform_locations;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Gl2ActiveUniform
 | 
			
		||||
| 
						 | 
				
			
			@ -121,14 +121,14 @@ class Gl2Rhi final : public Rhi
 | 
			
		|||
 | 
			
		||||
	Handle<Buffer> current_index_buffer_;
 | 
			
		||||
 | 
			
		||||
	std::unordered_map<Gl2FramebufferKey, uint32_t> framebuffers_ {16};
 | 
			
		||||
	srb2::HashMap<Gl2FramebufferKey, uint32_t> framebuffers_ {16};
 | 
			
		||||
 | 
			
		||||
	struct DefaultRenderPassState
 | 
			
		||||
	{
 | 
			
		||||
		bool clear = false;
 | 
			
		||||
	};
 | 
			
		||||
	using RenderPassState = std::variant<DefaultRenderPassState, RenderPassBeginInfo>;
 | 
			
		||||
	std::vector<RenderPassState> render_pass_stack_;
 | 
			
		||||
	srb2::Vector<RenderPassState> render_pass_stack_;
 | 
			
		||||
	std::optional<Handle<Program>> current_program_;
 | 
			
		||||
	PrimitiveType current_primitive_type_ = PrimitiveType::kPoints;
 | 
			
		||||
	uint32_t index_buffer_offset_ = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,9 @@
 | 
			
		|||
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
 | 
			
		||||
using namespace srb2;
 | 
			
		||||
using namespace rhi;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,27 +22,27 @@ ShaderLoadContext::ShaderLoadContext() = default;
 | 
			
		|||
 | 
			
		||||
void ShaderLoadContext::set_version(std::string_view version)
 | 
			
		||||
{
 | 
			
		||||
	version_ = fmt::format("#version {}\n", version);
 | 
			
		||||
	version_ = srb2::format("#version {}\n", version);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ShaderLoadContext::add_source(const std::string& source)
 | 
			
		||||
void ShaderLoadContext::add_source(const srb2::String& source)
 | 
			
		||||
{
 | 
			
		||||
	sources_.push_back(source);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ShaderLoadContext::add_source(std::string&& source)
 | 
			
		||||
void ShaderLoadContext::add_source(srb2::String&& source)
 | 
			
		||||
{
 | 
			
		||||
	sources_.push_back(std::move(source));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ShaderLoadContext::define(std::string_view name)
 | 
			
		||||
{
 | 
			
		||||
	defines_.append(fmt::format("#define {}\n", name));
 | 
			
		||||
	defines_.append(srb2::format("#define {}\n", name));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<const char*> ShaderLoadContext::get_sources_array()
 | 
			
		||||
srb2::Vector<const char*> ShaderLoadContext::get_sources_array()
 | 
			
		||||
{
 | 
			
		||||
	std::vector<const char*> ret;
 | 
			
		||||
	srb2::Vector<const char*> ret;
 | 
			
		||||
 | 
			
		||||
	ret.push_back(version_.c_str());
 | 
			
		||||
	ret.push_back(defines_.c_str());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,29 +11,30 @@
 | 
			
		|||
#ifndef __SRB2_RHI_SHADER_LOAD_CONTEXT_HPP__
 | 
			
		||||
#define __SRB2_RHI_SHADER_LOAD_CONTEXT_HPP__
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
 | 
			
		||||
namespace srb2::rhi
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
class ShaderLoadContext
 | 
			
		||||
{
 | 
			
		||||
	std::string version_;
 | 
			
		||||
	std::string defines_;
 | 
			
		||||
	std::vector<std::string> sources_;
 | 
			
		||||
	srb2::String version_;
 | 
			
		||||
	srb2::String defines_;
 | 
			
		||||
	srb2::Vector<srb2::String> sources_;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	ShaderLoadContext();
 | 
			
		||||
 | 
			
		||||
	void set_version(std::string_view version);
 | 
			
		||||
	void add_source(const std::string& source);
 | 
			
		||||
	void add_source(std::string&& source);
 | 
			
		||||
	void add_source(const srb2::String& source);
 | 
			
		||||
	void add_source(srb2::String&& source);
 | 
			
		||||
 | 
			
		||||
	void define(std::string_view name);
 | 
			
		||||
 | 
			
		||||
	std::vector<const char*> get_sources_array();
 | 
			
		||||
	srb2::Vector<const char*> get_sources_array();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}; // namespace srb2::rhi
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,9 +11,9 @@
 | 
			
		|||
#include <algorithm>
 | 
			
		||||
#include <cctype>
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "doomtype.h"
 | 
			
		||||
#include "sanitize.h"
 | 
			
		||||
#include "v_draw.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ bool color_filter(char c)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
template <typename F>
 | 
			
		||||
std::string& filter_out(std::string& out, const std::string_view& range, F filter)
 | 
			
		||||
srb2::String& filter_out(srb2::String& out, const std::string_view& range, F filter)
 | 
			
		||||
{
 | 
			
		||||
	std::remove_copy_if(
 | 
			
		||||
		range.begin(),
 | 
			
		||||
| 
						 | 
				
			
			@ -62,9 +62,9 @@ int hexconv(int c)
 | 
			
		|||
namespace srb2::sanitize
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
std::string sanitize(std::string_view in, SanitizeMode mode)
 | 
			
		||||
srb2::String sanitize(std::string_view in, SanitizeMode mode)
 | 
			
		||||
{
 | 
			
		||||
	std::string out;
 | 
			
		||||
	srb2::String out;
 | 
			
		||||
	return filter_out(out, in, [mode]
 | 
			
		||||
		{
 | 
			
		||||
			switch (mode)
 | 
			
		||||
| 
						 | 
				
			
			@ -78,9 +78,9 @@ std::string sanitize(std::string_view in, SanitizeMode mode)
 | 
			
		|||
		}());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string parse_carets(std::string_view in, ParseMode mode)
 | 
			
		||||
srb2::String parse_carets(std::string_view in, ParseMode mode)
 | 
			
		||||
{
 | 
			
		||||
	std::string out;
 | 
			
		||||
	srb2::String out;
 | 
			
		||||
 | 
			
		||||
	using std::size_t;
 | 
			
		||||
	for (;;)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,9 +14,10 @@
 | 
			
		|||
#include "doomtype.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
 | 
			
		||||
namespace srb2::sanitize
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -33,10 +34,10 @@ enum class ParseMode
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
// sanitizes string of all 0x80 codes
 | 
			
		||||
std::string sanitize(std::string_view in, SanitizeMode mode);
 | 
			
		||||
srb2::String sanitize(std::string_view in, SanitizeMode mode);
 | 
			
		||||
 | 
			
		||||
// sanitizes string of all 0x80 codes then parses caret codes
 | 
			
		||||
std::string parse_carets(std::string_view in, ParseMode mode);
 | 
			
		||||
srb2::String parse_carets(std::string_view in, ParseMode mode);
 | 
			
		||||
 | 
			
		||||
}; // namespace srb2
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,10 +17,10 @@
 | 
			
		|||
#include "../d_main.h"
 | 
			
		||||
#include "../m_misc.h"/* path shit */
 | 
			
		||||
#include "../i_system.h"
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <tracy/tracy/Tracy.hpp>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -227,14 +227,14 @@ ChDirToExe (void)
 | 
			
		|||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void walk_exception_stack(std::string& accum, bool nested) {
 | 
			
		||||
static void walk_exception_stack(srb2::String& accum, bool nested) {
 | 
			
		||||
	if (nested)
 | 
			
		||||
		accum.append("\n  Caused by: Unknown exception");
 | 
			
		||||
	else
 | 
			
		||||
		accum.append("Uncaught exception: Unknown exception");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void walk_exception_stack(std::string& accum, const std::exception& ex, bool nested) {
 | 
			
		||||
static void walk_exception_stack(srb2::String& accum, const std::exception& ex, bool nested) {
 | 
			
		||||
	if (nested)
 | 
			
		||||
		accum.append("\n  Caused by: ");
 | 
			
		||||
	else
 | 
			
		||||
| 
						 | 
				
			
			@ -331,11 +331,11 @@ int main(int argc, char **argv)
 | 
			
		|||
	D_SRB2Loop();
 | 
			
		||||
 | 
			
		||||
	} catch (const std::exception& ex) {
 | 
			
		||||
		std::string exception;
 | 
			
		||||
		srb2::String exception;
 | 
			
		||||
		walk_exception_stack(exception, ex, false);
 | 
			
		||||
		I_Error("%s", exception.c_str());
 | 
			
		||||
	} catch (...) {
 | 
			
		||||
		std::string exception;
 | 
			
		||||
		srb2::String exception;
 | 
			
		||||
		walk_exception_stack(exception, false);
 | 
			
		||||
		I_Error("%s", exception.c_str());
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,11 +11,13 @@
 | 
			
		|||
#include "rhi_gl2_platform.hpp"
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../cxxutil.hpp"
 | 
			
		||||
#include "../doomstat.h" // mainwads
 | 
			
		||||
#include "../w_wad.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -34,22 +36,22 @@ void SdlGl2Platform::present()
 | 
			
		|||
	SDL_GL_SwapWindow(window);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static std::array<std::string, 2> glsllist_lump_names(const char* name)
 | 
			
		||||
static std::array<srb2::String, 2> glsllist_lump_names(const char* name)
 | 
			
		||||
{
 | 
			
		||||
	std::string vertex_list_name = fmt::format("rhi_glsllist_{}_vertex.txt", name);
 | 
			
		||||
	std::string fragment_list_name = fmt::format("rhi_glsllist_{}_fragment.txt", name);
 | 
			
		||||
	srb2::String vertex_list_name = fmt::format("rhi_glsllist_{}_vertex.txt", name);
 | 
			
		||||
	srb2::String fragment_list_name = fmt::format("rhi_glsllist_{}_fragment.txt", name);
 | 
			
		||||
 | 
			
		||||
	return {std::move(vertex_list_name), std::move(fragment_list_name)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static std::vector<std::string> get_sources_from_glsllist_lump(const char* lumpname)
 | 
			
		||||
static srb2::Vector<srb2::String> get_sources_from_glsllist_lump(const char* lumpname)
 | 
			
		||||
{
 | 
			
		||||
	size_t buffer_size;
 | 
			
		||||
	if (!W_ReadShader(lumpname, &buffer_size, nullptr))
 | 
			
		||||
	{
 | 
			
		||||
		throw std::runtime_error(fmt::format("Unable to find glsllist lump {}", lumpname));
 | 
			
		||||
	}
 | 
			
		||||
	std::string glsllist_lump_data;
 | 
			
		||||
	srb2::String glsllist_lump_data;
 | 
			
		||||
	glsllist_lump_data.resize(buffer_size);
 | 
			
		||||
	if (!W_ReadShader(lumpname, &buffer_size, glsllist_lump_data.data()))
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +59,7 @@ static std::vector<std::string> get_sources_from_glsllist_lump(const char* lumpn
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	std::istringstream glsllist(glsllist_lump_data);
 | 
			
		||||
	std::vector<std::string> sources;
 | 
			
		||||
	srb2::Vector<srb2::String> sources;
 | 
			
		||||
	for (std::string line; std::getline(glsllist, line); )
 | 
			
		||||
	{
 | 
			
		||||
		if (line.empty())
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +89,7 @@ static std::vector<std::string> get_sources_from_glsllist_lump(const char* lumpn
 | 
			
		|||
		{
 | 
			
		||||
			throw std::runtime_error(fmt::format("Unable to find glsl source lump lump {}", line));
 | 
			
		||||
		}
 | 
			
		||||
		std::string source_lump;
 | 
			
		||||
		srb2::String source_lump;
 | 
			
		||||
		source_lump.resize(source_lump_size);
 | 
			
		||||
		if (!W_ReadShader(line.c_str(), &source_lump_size, source_lump.data()))
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -100,13 +102,13 @@ static std::vector<std::string> get_sources_from_glsllist_lump(const char* lumpn
 | 
			
		|||
	return sources;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::tuple<std::vector<std::string>, std::vector<std::string>>
 | 
			
		||||
std::tuple<srb2::Vector<srb2::String>, srb2::Vector<srb2::String>>
 | 
			
		||||
SdlGl2Platform::find_shader_sources(const char* name)
 | 
			
		||||
{
 | 
			
		||||
	std::array<std::string, 2> glsllist_names = glsllist_lump_names(name);
 | 
			
		||||
	std::array<srb2::String, 2> glsllist_names = glsllist_lump_names(name);
 | 
			
		||||
 | 
			
		||||
	std::vector<std::string> vertex_sources = get_sources_from_glsllist_lump(glsllist_names[0].c_str());
 | 
			
		||||
	std::vector<std::string> fragment_sources = get_sources_from_glsllist_lump(glsllist_names[1].c_str());
 | 
			
		||||
	srb2::Vector<srb2::String> vertex_sources = get_sources_from_glsllist_lump(glsllist_names[0].c_str());
 | 
			
		||||
	srb2::Vector<srb2::String> fragment_sources = get_sources_from_glsllist_lump(glsllist_names[1].c_str());
 | 
			
		||||
 | 
			
		||||
	return std::make_tuple(std::move(vertex_sources), std::move(fragment_sources));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,10 @@
 | 
			
		|||
#ifndef __SRB2_SDL_RHI_GL2_PLATFORM_HPP__
 | 
			
		||||
#define __SRB2_SDL_RHI_GL2_PLATFORM_HPP__
 | 
			
		||||
 | 
			
		||||
#include <tuple>
 | 
			
		||||
 | 
			
		||||
#include "../core/string.h"
 | 
			
		||||
#include "../core/vector.hpp"
 | 
			
		||||
#include "../rhi/gl2/gl2_rhi.hpp"
 | 
			
		||||
#include "../rhi/rhi.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +30,7 @@ struct SdlGl2Platform final : public Gl2Platform
 | 
			
		|||
	virtual ~SdlGl2Platform();
 | 
			
		||||
 | 
			
		||||
	virtual void present() override;
 | 
			
		||||
	virtual std::tuple<std::vector<std::string>, std::vector<std::string>>
 | 
			
		||||
	virtual std::tuple<Vector<srb2::String>, Vector<srb2::String>>
 | 
			
		||||
	find_shader_sources(const char* name) override;
 | 
			
		||||
	virtual Rect get_default_framebuffer_dimensions() override;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,8 +8,7 @@
 | 
			
		|||
// See the 'LICENSE' file for more details.
 | 
			
		||||
//-----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
#include "core/hash_map.hpp"
 | 
			
		||||
#include "doomdef.h" // skincolornum_t
 | 
			
		||||
#include "doomtype.h"
 | 
			
		||||
#include "hu_stuff.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +33,7 @@ int Draw::TextElement::width() const
 | 
			
		|||
 | 
			
		||||
Draw::TextElement& Draw::TextElement::parse(std::string_view raw)
 | 
			
		||||
{
 | 
			
		||||
	static const std::unordered_map<std::string_view, char> translation = {
 | 
			
		||||
	static const srb2::HashMap<std::string_view, char> translation = {
 | 
			
		||||
#define BUTTON(str, lower_bits) \
 | 
			
		||||
		{str,             0xB0 | lower_bits},\
 | 
			
		||||
		{str "_animated", 0xA0 | lower_bits},\
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +87,7 @@ Draw::TextElement& Draw::TextElement::parse(std::string_view raw)
 | 
			
		|||
	};
 | 
			
		||||
 | 
			
		||||
	// When we encounter a Saturn button, what gamecontrol does it represent?
 | 
			
		||||
	static const std::unordered_map<char, gamecontrols_e> inputdefinition = {
 | 
			
		||||
	static const srb2::HashMap<char, gamecontrols_e> inputdefinition = {
 | 
			
		||||
		{sb_up, gc_up},
 | 
			
		||||
		{sb_down, gc_down},
 | 
			
		||||
		{sb_right, gc_right},
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +113,7 @@ Draw::TextElement& Draw::TextElement::parse(std::string_view raw)
 | 
			
		|||
	// What physical binds should appear as Saturn icons anyway?
 | 
			
		||||
	// (We don't have generic binds for stick/dpad directions, so
 | 
			
		||||
	// using the existing arrow graphics is the best thing here.)
 | 
			
		||||
	static const std::unordered_map<INT32, char> prettyinputs = {
 | 
			
		||||
	static const srb2::HashMap<INT32, char> prettyinputs = {
 | 
			
		||||
		{KEY_UPARROW, sb_up},
 | 
			
		||||
		{KEY_DOWNARROW, sb_down},
 | 
			
		||||
		{KEY_LEFTARROW, sb_left},
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +163,7 @@ Draw::TextElement& Draw::TextElement::parse(std::string_view raw)
 | 
			
		|||
			// SPECIAL: Generic button that we invoke explicitly, not via gamecontrol reference.
 | 
			
		||||
			// If we ever add anything else to this category, I promise I will create a real abstraction,
 | 
			
		||||
			// but for now, just hardcode the character replacements and pray for forgiveness.
 | 
			
		||||
			
 | 
			
		||||
 | 
			
		||||
			string_.push_back(0xEF); // Control code: "switch to descriptive input mode"
 | 
			
		||||
			string_.push_back(0xEB); // Control code: "large button"
 | 
			
		||||
			if (code == "dpad")
 | 
			
		||||
| 
						 | 
				
			
			@ -198,7 +197,7 @@ Draw::TextElement& Draw::TextElement::parse(std::string_view raw)
 | 
			
		|||
 | 
			
		||||
					// EXTRA: descriptiveinput values above 1 translate binds back to Saturn buttons,
 | 
			
		||||
					// with various modes for various fucked up 6bt pads
 | 
			
		||||
					std::unordered_map<INT32, char> padconfig = {};
 | 
			
		||||
					srb2::HashMap<INT32, char> padconfig = {};
 | 
			
		||||
					switch (cv_descriptiveinput[localplayer].value)
 | 
			
		||||
					{
 | 
			
		||||
						case 1:
 | 
			
		||||
| 
						 | 
				
			
			@ -277,7 +276,7 @@ Draw::TextElement& Draw::TextElement::parse(std::string_view raw)
 | 
			
		|||
						pad->second = pad->second & (0x0F);
 | 
			
		||||
 | 
			
		||||
						// original invocation has the animation bits, but the glyph bits come from the table
 | 
			
		||||
						string_.push_back((it->second & 0xF0) | pad->second); 
 | 
			
		||||
						string_.push_back((it->second & 0xF0) | pad->second);
 | 
			
		||||
					}
 | 
			
		||||
					else
 | 
			
		||||
					{
 | 
			
		||||
| 
						 | 
				
			
			@ -349,6 +348,11 @@ void Chain::fill(UINT8 color) const
 | 
			
		|||
 | 
			
		||||
void Chain::string(const char* str, INT32 flags, Font font) const
 | 
			
		||||
{
 | 
			
		||||
	if (!str)
 | 
			
		||||
	{
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const auto _ = Clipper(*this);
 | 
			
		||||
 | 
			
		||||
	flags |= default_font_flags(font);
 | 
			
		||||
| 
						 | 
				
			
			@ -576,5 +580,10 @@ INT32 Draw::default_font_flags(Font font)
 | 
			
		|||
 | 
			
		||||
fixed_t Draw::font_width(Font font, INT32 flags, const char* string)
 | 
			
		||||
{
 | 
			
		||||
	if (!string)
 | 
			
		||||
	{
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return V_StringScaledWidth(FRACUNIT, FRACUNIT, FRACUNIT, flags, font_to_fontno(font), string);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,14 +11,14 @@
 | 
			
		|||
#ifndef __V_DRAW_HPP__
 | 
			
		||||
#define __V_DRAW_HPP__
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
#include <fmt/core.h>
 | 
			
		||||
 | 
			
		||||
#include "core/hash_map.hpp"
 | 
			
		||||
#include "core/string.h"
 | 
			
		||||
#include "doomdef.h" // skincolornum_t
 | 
			
		||||
#include "doomtype.h"
 | 
			
		||||
#include "screen.h" // BASEVIDWIDTH
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +66,7 @@ typedef enum
 | 
			
		|||
} saturn_buttons_e;
 | 
			
		||||
 | 
			
		||||
// Garden-variety standard gamepad
 | 
			
		||||
static const std::unordered_map<INT32, char> standardpad = {
 | 
			
		||||
static const srb2::HashMap<INT32, char> standardpad = {
 | 
			
		||||
	{nc_a, gb_a},
 | 
			
		||||
	{nc_b, gb_b},
 | 
			
		||||
	{nc_x, gb_x},
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ static const std::unordered_map<INT32, char> standardpad = {
 | 
			
		|||
 | 
			
		||||
// Standard gamepad, but evil Nintendo layout flip was applied by your
 | 
			
		||||
// controller firmware or Steam Input—swap B/A and X/Y
 | 
			
		||||
static const std::unordered_map<INT32, char> flippedpad = {
 | 
			
		||||
static const srb2::HashMap<INT32, char> flippedpad = {
 | 
			
		||||
	{nc_a, gb_b},
 | 
			
		||||
	{nc_b, gb_a},
 | 
			
		||||
	{nc_x, gb_y},
 | 
			
		||||
| 
						 | 
				
			
			@ -99,7 +99,7 @@ static const std::unordered_map<INT32, char> flippedpad = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
// Saturn Type A - Retrobit Wired Dinput, RB RT LB LT (CZLR)
 | 
			
		||||
static const std::unordered_map<INT32, char> saturntypeA = {
 | 
			
		||||
static const srb2::HashMap<INT32, char> saturntypeA = {
 | 
			
		||||
	{nc_a, sb_a},
 | 
			
		||||
	{nc_b, sb_b},
 | 
			
		||||
	{nc_x, sb_x},
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +115,7 @@ static const std::unordered_map<INT32, char> saturntypeA = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
// Saturn Type B - Retrobit Wireless Dinput, LB RB LT RT (CZLR)
 | 
			
		||||
static const std::unordered_map<INT32, char> saturntypeB = {
 | 
			
		||||
static const srb2::HashMap<INT32, char> saturntypeB = {
 | 
			
		||||
	{nc_a, sb_a},
 | 
			
		||||
	{nc_b, sb_b},
 | 
			
		||||
	{nc_x, sb_x},
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +131,7 @@ static const std::unordered_map<INT32, char> saturntypeB = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
// Saturn Type C - Retrobit Xinput, RT LT LB RB (CZLR)
 | 
			
		||||
static const std::unordered_map<INT32, char> saturntypeC = {
 | 
			
		||||
static const srb2::HashMap<INT32, char> saturntypeC = {
 | 
			
		||||
	{nc_a, sb_a},
 | 
			
		||||
	{nc_b, sb_b},
 | 
			
		||||
	{nc_x, sb_x},
 | 
			
		||||
| 
						 | 
				
			
			@ -150,7 +150,7 @@ static const std::unordered_map<INT32, char> saturntypeC = {
 | 
			
		|||
// This cannot be disambiguated (shares L/R with type 1)
 | 
			
		||||
// but is more spatially correct w/r/t SDL expectations
 | 
			
		||||
// and standard arcade mapping (Z on top, C on bottom)
 | 
			
		||||
static const std::unordered_map<INT32, char> saturntypeD = {
 | 
			
		||||
static const srb2::HashMap<INT32, char> saturntypeD = {
 | 
			
		||||
	{nc_a, sb_a},
 | 
			
		||||
	{nc_b, sb_b},
 | 
			
		||||
	{nc_x, sb_x},
 | 
			
		||||
| 
						 | 
				
			
			@ -169,7 +169,7 @@ static const std::unordered_map<INT32, char> saturntypeD = {
 | 
			
		|||
// The Hori layout is, to my knowledge, the only 6bt one that has fully
 | 
			
		||||
// unique buttons in every slot while having both bumpers and triggers,
 | 
			
		||||
// so there's no way to accurately portray it without using generics.
 | 
			
		||||
static const std::unordered_map<INT32, char> saturntypeE = {
 | 
			
		||||
static const srb2::HashMap<INT32, char> saturntypeE = {
 | 
			
		||||
	{nc_a, sb_a},
 | 
			
		||||
	{nc_b, sb_b},
 | 
			
		||||
	{nc_x, sb_x},
 | 
			
		||||
| 
						 | 
				
			
			@ -263,7 +263,7 @@ public:
 | 
			
		|||
	public:
 | 
			
		||||
		explicit TextElement() {}
 | 
			
		||||
 | 
			
		||||
		explicit TextElement(std::string string) : string_(string) {}
 | 
			
		||||
		explicit TextElement(const srb2::String& string) : string_(string) {}
 | 
			
		||||
 | 
			
		||||
		template <class... Args>
 | 
			
		||||
		explicit TextElement(fmt::format_string<Args...> format, Args&&... args) :
 | 
			
		||||
| 
						 | 
				
			
			@ -271,14 +271,14 @@ public:
 | 
			
		|||
		{
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const std::string& string() const { return string_; }
 | 
			
		||||
		const srb2::String& string() const { return string_; }
 | 
			
		||||
		std::optional<Font> font() const { return font_; }
 | 
			
		||||
		std::optional<INT32> flags() const { return flags_; }
 | 
			
		||||
		std::optional<UINT8> as() const { return as_; }
 | 
			
		||||
 | 
			
		||||
		int width() const;
 | 
			
		||||
 | 
			
		||||
		TextElement& string(std::string string)
 | 
			
		||||
		TextElement& string(srb2::String string)
 | 
			
		||||
		{
 | 
			
		||||
			string_ = string;
 | 
			
		||||
			return *this;
 | 
			
		||||
| 
						 | 
				
			
			@ -310,7 +310,7 @@ public:
 | 
			
		|||
			return *this;
 | 
			
		||||
		}
 | 
			
		||||
	private:
 | 
			
		||||
		std::string string_;
 | 
			
		||||
		srb2::String string_;
 | 
			
		||||
		std::optional<Font> font_;
 | 
			
		||||
		std::optional<INT32> flags_;
 | 
			
		||||
		std::optional<UINT8> as_;
 | 
			
		||||
| 
						 | 
				
			
			@ -357,7 +357,7 @@ public:
 | 
			
		|||
		Chain& colorize(UINT16 color);
 | 
			
		||||
 | 
			
		||||
		void text(const char* str) const { string(str, flags_, font_); }
 | 
			
		||||
		void text(const std::string& str) const { text(str.c_str()); }
 | 
			
		||||
		void text(const srb2::String& str) const { text(str.c_str()); }
 | 
			
		||||
		void text(const TextElement& elm) const
 | 
			
		||||
		{
 | 
			
		||||
			string(elm.string().c_str(), elm.flags().value_or(flags_), elm.font().value_or(font_));
 | 
			
		||||
| 
						 | 
				
			
			@ -370,7 +370,7 @@ public:
 | 
			
		|||
 | 
			
		||||
		void patch(patch_t* patch) const;
 | 
			
		||||
		void patch(const char* name) const { patch(Draw::cache_patch(name)); }
 | 
			
		||||
		void patch(const std::string& name) const { patch(name.c_str()); }
 | 
			
		||||
		void patch(const srb2::String& name) const { patch(name.c_str()); }
 | 
			
		||||
 | 
			
		||||
		void thumbnail(UINT16 mapnum) const;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -322,9 +322,9 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
 | 
			
		|||
			{
 | 
			
		||||
				srb2::StandingJson standing {};
 | 
			
		||||
				standing.ranking = data.pos[data.numplayers];
 | 
			
		||||
				standing.name = std::string(player_names[i]);
 | 
			
		||||
				standing.name = srb2::String(player_names[i]);
 | 
			
		||||
				standing.demoskin = players[i].skin;
 | 
			
		||||
				standing.skincolor = std::string(skincolors[players[i].skincolor].name);
 | 
			
		||||
				standing.skincolor = srb2::String(skincolors[players[i].skincolor].name);
 | 
			
		||||
				standing.timeorscore = data.val[data.numplayers];
 | 
			
		||||
				standings.standings.emplace_back(std::move(standing));
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue