rename and document DC serial number functions
This commit is contained in:
+1
-1
@@ -48,6 +48,7 @@ add_executable(newserv
|
||||
src/Client.cc
|
||||
src/CommonItemSet.cc
|
||||
src/Compression.cc
|
||||
src/DCSerialNumbers.cc
|
||||
src/DNSServer.cc
|
||||
src/EnemyType.cc
|
||||
src/Episode3/AssistServer.cc
|
||||
@@ -83,7 +84,6 @@ add_executable(newserv
|
||||
src/PatchFileIndex.cc
|
||||
src/Player.cc
|
||||
src/PlayerSubordinates.cc
|
||||
src/Product.cc
|
||||
src/ProxyCommands.cc
|
||||
src/ProxyServer.cc
|
||||
src/PSOEncryption.cc
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
- Make reloading happen on separate threads so compression doesn't block active clients
|
||||
- Try emu.cfg change instead of patch_flycast_memory (https://github.com/fuzziqersoftware/newserv/issues/132)
|
||||
- Try DCv2/PC crossplay
|
||||
- Implement decrypt/encrypt actions for VMS files and PC save files
|
||||
|
||||
## Episode 3
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Product.hh"
|
||||
#include "DCSerialNumbers.hh"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -1163,23 +1163,23 @@ static char replace_char_reverse(char ch) {
|
||||
|
||||
static constexpr uint64_t INVALID_PRODUCT = 0xFFFFFFFFFFFFFFFF;
|
||||
|
||||
static uint64_t decode_product_str(const string& s) {
|
||||
static uint64_t decode_dc_serial_number_str(const string& s) {
|
||||
if (s.size() != 8) {
|
||||
return INVALID_PRODUCT;
|
||||
}
|
||||
|
||||
uint64_t product = 0;
|
||||
uint64_t serial_number = 0;
|
||||
for (char ch : s) {
|
||||
char new_ch = replace_char_forward(ch);
|
||||
if (new_ch == '\0') {
|
||||
return INVALID_PRODUCT;
|
||||
}
|
||||
product = (product << 4) | value_for_hex_char(new_ch);
|
||||
serial_number = (serial_number << 4) | value_for_hex_char(new_ch);
|
||||
}
|
||||
return product;
|
||||
return serial_number;
|
||||
}
|
||||
|
||||
static uint32_t decode_product_int(uint32_t v) {
|
||||
static uint32_t decode_dc_serial_number_int(uint32_t v) {
|
||||
return (replace_nybble_forward(v >> 28) << 28) |
|
||||
(replace_nybble_forward(v >> 24) << 24) |
|
||||
(replace_nybble_forward(v >> 20) << 20) |
|
||||
@@ -1190,7 +1190,7 @@ static uint32_t decode_product_int(uint32_t v) {
|
||||
(replace_nybble_forward(v));
|
||||
}
|
||||
|
||||
static uint32_t encode_product_int(uint32_t v) {
|
||||
static uint32_t encode_dc_serial_number_int(uint32_t v) {
|
||||
return (replace_nybble_reverse(v >> 28) << 28) |
|
||||
(replace_nybble_reverse(v >> 24) << 24) |
|
||||
(replace_nybble_reverse(v >> 20) << 20) |
|
||||
@@ -1215,9 +1215,9 @@ static pair<size_t, size_t> compute_offset1_and_limit1(
|
||||
}
|
||||
}
|
||||
|
||||
bool product_is_valid_slow(const string& s, uint8_t domain, uint8_t subdomain) {
|
||||
uint64_t product = decode_product_str(s);
|
||||
if (product == INVALID_PRODUCT) {
|
||||
bool dc_serial_number_is_valid_slow(const string& s, uint8_t domain, uint8_t subdomain) {
|
||||
uint64_t serial_number = decode_dc_serial_number_str(s);
|
||||
if (serial_number == INVALID_PRODUCT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1229,7 +1229,7 @@ bool product_is_valid_slow(const string& s, uint8_t domain, uint8_t subdomain) {
|
||||
for (; offset1 < limit1; offset1++) {
|
||||
for (size_t offset2 = 0; offset2 < sizeof(primes2) / sizeof(primes2[0]); offset2++) {
|
||||
for (size_t offset3 = 0; offset3 < sizeof(primes3) / sizeof(primes3[0]); offset3++) {
|
||||
if (primes1[offset1] * primes2[offset2] * primes3[offset3] == product) {
|
||||
if (primes1[offset1] * primes2[offset2] * primes3[offset3] == serial_number) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1238,14 +1238,14 @@ bool product_is_valid_slow(const string& s, uint8_t domain, uint8_t subdomain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool decoded_product_is_valid_fast(uint32_t product, uint8_t domain, uint8_t subdomain) {
|
||||
bool decoded_dc_serial_number_is_valid_fast(uint32_t serial_number, uint8_t domain, uint8_t subdomain) {
|
||||
auto [offset1_start, limit1] = compute_offset1_and_limit1(domain, subdomain);
|
||||
if (limit1 == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint64_t prefix = 0; prefix < 0x000000E800000000; prefix += 0x0000000100000000) {
|
||||
uint64_t sub0 = product | prefix;
|
||||
uint64_t sub0 = serial_number | prefix;
|
||||
for (size_t offset1 = offset1_start; offset1 < limit1; offset1++) {
|
||||
if (sub0 % primes1[offset1]) {
|
||||
continue;
|
||||
@@ -1264,19 +1264,19 @@ bool decoded_product_is_valid_fast(uint32_t product, uint8_t domain, uint8_t sub
|
||||
return false;
|
||||
}
|
||||
|
||||
bool product_is_valid_fast(const string& s, uint8_t domain, uint8_t subdomain) {
|
||||
uint64_t product = decode_product_str(s);
|
||||
if (product == INVALID_PRODUCT) {
|
||||
bool dc_serial_number_is_valid_fast(const string& s, uint8_t domain, uint8_t subdomain) {
|
||||
uint64_t serial_number = decode_dc_serial_number_str(s);
|
||||
if (serial_number == INVALID_PRODUCT) {
|
||||
return false;
|
||||
}
|
||||
return decoded_product_is_valid_fast(product, domain, subdomain);
|
||||
return decoded_dc_serial_number_is_valid_fast(serial_number, domain, subdomain);
|
||||
}
|
||||
|
||||
bool product_is_valid_fast(uint32_t product, uint8_t domain, uint8_t subdomain) {
|
||||
return decoded_product_is_valid_fast(decode_product_int(product), domain, subdomain);
|
||||
bool dc_serial_number_is_valid_fast(uint32_t serial_number, uint8_t domain, uint8_t subdomain) {
|
||||
return decoded_dc_serial_number_is_valid_fast(decode_dc_serial_number_int(serial_number), domain, subdomain);
|
||||
}
|
||||
|
||||
string generate_product(uint8_t domain, uint8_t subdomain) {
|
||||
string generate_dc_serial_number(uint8_t domain, uint8_t subdomain) {
|
||||
size_t offset1, limit1;
|
||||
if (domain == 0) {
|
||||
offset1 = 0x00;
|
||||
@@ -1305,7 +1305,7 @@ string generate_product(uint8_t domain, uint8_t subdomain) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
unordered_map<uint32_t, string> generate_all_products(uint8_t domain, uint8_t subdomain) {
|
||||
unordered_map<uint32_t, string> generate_all_dc_serial_numbers(uint8_t domain, uint8_t subdomain) {
|
||||
vector<uint8_t> domains;
|
||||
if (domain == 0xFF) {
|
||||
domains.emplace_back(0x00);
|
||||
@@ -1345,16 +1345,16 @@ unordered_map<uint32_t, string> generate_all_products(uint8_t domain, uint8_t su
|
||||
for (size_t index2 = 0; index2 < sizeof(primes2) / sizeof(primes2[0]); index2++) {
|
||||
for (size_t index3 = 0; index3 < sizeof(primes3) / sizeof(primes3[0]); index3++) {
|
||||
uint32_t value = primes1[index1] * primes2[index2] * primes3[index3];
|
||||
ret[encode_product_int(value)].push_back(((domain << 2) & 3) | (subdomain & 3));
|
||||
ret[encode_dc_serial_number_int(value)].push_back(((domain << 2) & 3) | (subdomain & 3));
|
||||
}
|
||||
fprintf(stderr, "... domain=%hhu subdomain=%hhu index2=%zu products=%zu (0x%zX)\n", domain, subdomain, index2, ret.size(), ret.size());
|
||||
fprintf(stderr, "... domain=%hhu subdomain=%hhu index2=%zu results=%zu (0x%zX)\n", domain, subdomain, index2, ret.size(), ret.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void product_speed_test(uint64_t seed) {
|
||||
void dc_serial_number_speed_test(uint64_t seed) {
|
||||
uint32_t effective_seed = (seed & 0xFFFFFFFF00000000) ? random_object<uint32_t>() : seed;
|
||||
fprintf(stderr, "Product speed test with seed=%08" PRIX32 "\n", effective_seed);
|
||||
PSOV2Encryption crypt(effective_seed);
|
||||
@@ -1366,11 +1366,11 @@ void product_speed_test(uint64_t seed) {
|
||||
string s = string_printf("%08X", crypt.next());
|
||||
|
||||
uint64_t start = now();
|
||||
bool is_valid_fast = product_is_valid_fast(s, 1, 0xFF);
|
||||
bool is_valid_fast = dc_serial_number_is_valid_fast(s, 1, 0xFF);
|
||||
time_fast += now() - start;
|
||||
|
||||
start = now();
|
||||
bool is_valid_slow = product_is_valid_slow(s, 1, 0xFF);
|
||||
bool is_valid_slow = dc_serial_number_is_valid_slow(s, 1, 0xFF);
|
||||
time_slow += now() - start;
|
||||
|
||||
if (((z & 0xF) == 0) || is_valid_slow || is_valid_fast) {
|
||||
@@ -1381,8 +1381,8 @@ void product_speed_test(uint64_t seed) {
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Total time (slow): %" PRId64 " usecs (%" PRIu64 " per product)\n", time_slow, time_slow / count);
|
||||
fprintf(stderr, "Total time (fast): %" PRId64 " usecs (%" PRIu64 " per product)\n", time_fast, time_fast / count);
|
||||
fprintf(stderr, "Total time (slow): %" PRId64 " usecs (%" PRIu64 " per serial number)\n", time_slow, time_slow / count);
|
||||
fprintf(stderr, "Total time (fast): %" PRId64 " usecs (%" PRIu64 " per serial number)\n", time_fast, time_fast / count);
|
||||
fprintf(stderr, "Fast vs. slow speedup: %zux\n", static_cast<size_t>(time_slow / time_fast));
|
||||
fprintf(stderr, "Disagreements: %zu\n", num_disagreements);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
// dc_serial_number_is_valid_slow is Sega's implementation;
|
||||
// dc_serial_number_is_valid_fast produces identical results but is between 3000
|
||||
// and 7500 times faster, depending on the compiler's optimization level.
|
||||
bool dc_serial_number_is_valid_slow(
|
||||
const std::string& s, uint8_t domain, uint8_t subdomain = 0xFF);
|
||||
bool dc_serial_number_is_valid_fast(
|
||||
const std::string& s, uint8_t domain, uint8_t subdomain = 0xFF);
|
||||
bool dc_serial_number_is_valid_fast(
|
||||
uint32_t serial_number, uint8_t domain, uint8_t subdomain = 0xFF);
|
||||
bool decoded_dc_serial_number_is_valid_fast(
|
||||
uint32_t serial_number, uint8_t domain, uint8_t subdomain = 0xFF);
|
||||
|
||||
std::string generate_dc_serial_number(uint8_t domain, uint8_t subdomain = 0xFF);
|
||||
std::unordered_map<uint32_t, std::string> generate_all_dc_serial_numbers(uint8_t domain = 0xFF, uint8_t subdomain = 0xFF);
|
||||
|
||||
void dc_serial_number_speed_test(uint64_t seed = 0xFFFFFFFFFFFFFFFF);
|
||||
+45
-34
@@ -17,6 +17,7 @@
|
||||
#include "BMLArchive.hh"
|
||||
#include "CatSession.hh"
|
||||
#include "Compression.hh"
|
||||
#include "DCSerialNumbers.hh"
|
||||
#include "DNSServer.hh"
|
||||
#include "GSLArchive.hh"
|
||||
#include "GVMEncoder.hh"
|
||||
@@ -24,7 +25,6 @@
|
||||
#include "Loggers.hh"
|
||||
#include "NetworkAddresses.hh"
|
||||
#include "PSOGCObjectGraph.hh"
|
||||
#include "Product.hh"
|
||||
#include "ProxyServer.hh"
|
||||
#include "Quest.hh"
|
||||
#include "QuestScript.hh"
|
||||
@@ -244,6 +244,17 @@ The actions are:\n\
|
||||
convert-itemrt-rel-to-json [INPUT-FILENAME [OUTPUT-FILENAME]]\n\
|
||||
Convert a REL rare table to a JSON rare item set. The resulting JSON has\n\
|
||||
the same structure as system/blueburst/rare-table.json.\n\
|
||||
generate-dc-serial-number [--domain=DOMAIN] [--subdomain=SUBDOMAIN]\n\
|
||||
Generate a PSO DC serial number. DOMAIN should be 0 for DCv1 or 1 for DCv2;\n\
|
||||
SUBDOMAIN should be 0 for Japanese, 1 for USA, or 2 for Europe.\n\
|
||||
generate-all-dc-serial-numbers\n\
|
||||
Generate all possible PSO DC serial numbers.\n\
|
||||
inspect-dc-serial-number SERIAL-NUMBER\n\
|
||||
Show which domain and subdomain the serial number belongs to. (As with\n\
|
||||
generate-dc-serial-number, described above, this will tell you which PSO\n\
|
||||
version it is valid for.)\n\
|
||||
dc-serial-number-speed-test\n\
|
||||
Run a speed test of the two DC serial number validation functions.\n\
|
||||
\n\
|
||||
A few options apply to multiple modes described above:\n\
|
||||
--parse-data\n\
|
||||
@@ -300,10 +311,10 @@ enum class Behavior {
|
||||
PARSE_OBJECT_GRAPH,
|
||||
REPLAY_LOG,
|
||||
CAT_CLIENT,
|
||||
GENERATE_PRODUCT,
|
||||
GENERATE_ALL_PRODUCTS,
|
||||
INSPECT_PRODUCT,
|
||||
PRODUCT_SPEED_TEST,
|
||||
GENERATE_DC_SERIAL_NUMBER,
|
||||
GENERATE_ALL_DC_SERIAL_NUMBERS,
|
||||
INSPECT_DC_SERIAL_NUMBER,
|
||||
DC_SERIAL_NUMBER_SPEED_TEST,
|
||||
};
|
||||
|
||||
static bool behavior_takes_input_filename(Behavior b) {
|
||||
@@ -346,7 +357,7 @@ static bool behavior_takes_input_filename(Behavior b) {
|
||||
(b == Behavior::PARSE_OBJECT_GRAPH) ||
|
||||
(b == Behavior::REPLAY_LOG) ||
|
||||
(b == Behavior::CAT_CLIENT) ||
|
||||
(b == Behavior::INSPECT_PRODUCT);
|
||||
(b == Behavior::INSPECT_DC_SERIAL_NUMBER);
|
||||
}
|
||||
|
||||
static bool behavior_takes_output_filename(Behavior b) {
|
||||
@@ -604,14 +615,14 @@ int main(int argc, char** argv) {
|
||||
behavior = Behavior::EXTRACT_GSL;
|
||||
} else if (!strcmp(argv[x], "extract-bml")) {
|
||||
behavior = Behavior::EXTRACT_BML;
|
||||
} else if (!strcmp(argv[x], "generate-product")) {
|
||||
behavior = Behavior::GENERATE_PRODUCT;
|
||||
} else if (!strcmp(argv[x], "generate-all-products")) {
|
||||
behavior = Behavior::GENERATE_ALL_PRODUCTS;
|
||||
} else if (!strcmp(argv[x], "inspect-product")) {
|
||||
behavior = Behavior::INSPECT_PRODUCT;
|
||||
} else if (!strcmp(argv[x], "product-speed-test")) {
|
||||
behavior = Behavior::PRODUCT_SPEED_TEST;
|
||||
} else if (!strcmp(argv[x], "generate-dc-serial-number")) {
|
||||
behavior = Behavior::GENERATE_DC_SERIAL_NUMBER;
|
||||
} else if (!strcmp(argv[x], "generate-all-dc-serial-numbers")) {
|
||||
behavior = Behavior::GENERATE_ALL_DC_SERIAL_NUMBERS;
|
||||
} else if (!strcmp(argv[x], "inspect-dc-serial-number")) {
|
||||
behavior = Behavior::INSPECT_DC_SERIAL_NUMBER;
|
||||
} else if (!strcmp(argv[x], "dc-serial-number-speed-test")) {
|
||||
behavior = Behavior::DC_SERIAL_NUMBER_SPEED_TEST;
|
||||
} else {
|
||||
throw invalid_argument(string_printf("unknown command: %s (try --help)", argv[x]));
|
||||
}
|
||||
@@ -1791,17 +1802,17 @@ int main(int argc, char** argv) {
|
||||
break;
|
||||
}
|
||||
|
||||
case Behavior::GENERATE_PRODUCT: {
|
||||
auto product = generate_product(domain, subdomain);
|
||||
fprintf(stdout, "%s\n", product.c_str());
|
||||
case Behavior::GENERATE_DC_SERIAL_NUMBER: {
|
||||
string serial_number = generate_dc_serial_number(domain, subdomain);
|
||||
fprintf(stdout, "%s\n", serial_number.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
case Behavior::GENERATE_ALL_PRODUCTS: {
|
||||
auto products = generate_all_products();
|
||||
fprintf(stdout, "%zu (0x%zX) products found\n", products.size(), products.size());
|
||||
for (const auto& it : products) {
|
||||
fprintf(stdout, "Valid product: %08" PRIX32, it.first);
|
||||
case Behavior::GENERATE_ALL_DC_SERIAL_NUMBERS: {
|
||||
auto serial_numbers = generate_all_dc_serial_numbers();
|
||||
fprintf(stdout, "%zu (0x%zX) serial numbers found\n", serial_numbers.size(), serial_numbers.size());
|
||||
for (const auto& it : serial_numbers) {
|
||||
fprintf(stdout, "Valid serial number: %08" PRIX32, it.first);
|
||||
for (uint8_t where : it.second) {
|
||||
fprintf(stdout, " (domain=%hhu, subdomain=%hhu)",
|
||||
static_cast<uint8_t>((where >> 2) & 3),
|
||||
@@ -1810,22 +1821,22 @@ int main(int argc, char** argv) {
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
|
||||
atomic<uint64_t> num_valid_products = 0;
|
||||
atomic<uint64_t> num_valid_serial_numbers = 0;
|
||||
mutex output_lock;
|
||||
auto thread_fn = [&](uint64_t product, size_t) -> bool {
|
||||
auto thread_fn = [&](uint64_t serial_number, size_t) -> bool {
|
||||
for (uint8_t domain = 0; domain < 3; domain++) {
|
||||
for (uint8_t subdomain = 0; subdomain < 3; subdomain++) {
|
||||
if (product_is_valid_fast(product, domain, subdomain)) {
|
||||
num_valid_products++;
|
||||
if (dc_serial_number_is_valid_fast(serial_number, domain, subdomain)) {
|
||||
num_valid_serial_numbers++;
|
||||
lock_guard g(output_lock);
|
||||
fprintf(stdout, "Valid product: %08" PRIX64 " (domain=%hhu, subdomain=%hhu)\n", product, domain, subdomain);
|
||||
fprintf(stdout, "Valid serial number: %08" PRIX64 " (domain=%hhu, subdomain=%hhu)\n", serial_number, domain, subdomain);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
auto progress_fn = [&](uint64_t, uint64_t, uint64_t current_value, uint64_t) -> void {
|
||||
uint64_t num_found = num_valid_products.load();
|
||||
uint64_t num_found = num_valid_serial_numbers.load();
|
||||
fprintf(stderr, "... %08" PRIX64 " %" PRId64 " (0x%" PRIX64 ") found\r",
|
||||
current_value, num_found, num_found);
|
||||
};
|
||||
@@ -1833,14 +1844,14 @@ int main(int argc, char** argv) {
|
||||
break;
|
||||
}
|
||||
|
||||
case Behavior::INSPECT_PRODUCT: {
|
||||
case Behavior::INSPECT_DC_SERIAL_NUMBER: {
|
||||
if (!input_filename) {
|
||||
throw invalid_argument("no product given");
|
||||
throw invalid_argument("no serial number given");
|
||||
}
|
||||
size_t num_valid_subdomains = 0;
|
||||
for (uint8_t domain = 0; domain < 3; domain++) {
|
||||
for (uint8_t subdomain = 0; subdomain < 3; subdomain++) {
|
||||
if (product_is_valid_fast(input_filename, domain, subdomain)) {
|
||||
if (dc_serial_number_is_valid_fast(input_filename, domain, subdomain)) {
|
||||
fprintf(stdout, "%s is valid in domain %hhu subdomain %hhu\n", input_filename, domain, subdomain);
|
||||
num_valid_subdomains++;
|
||||
}
|
||||
@@ -1852,11 +1863,11 @@ int main(int argc, char** argv) {
|
||||
break;
|
||||
}
|
||||
|
||||
case Behavior::PRODUCT_SPEED_TEST:
|
||||
case Behavior::DC_SERIAL_NUMBER_SPEED_TEST:
|
||||
if (seed.empty()) {
|
||||
product_speed_test();
|
||||
dc_serial_number_speed_test();
|
||||
} else {
|
||||
product_speed_test(stoul(seed, nullptr, 16));
|
||||
dc_serial_number_speed_test(stoul(seed, nullptr, 16));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
// product_is_valid_slow is Sega's implementation; product_is_valid_fast
|
||||
// produces identical results but is about 7000 times faster.
|
||||
bool product_is_valid_slow(
|
||||
const std::string& s, uint8_t domain, uint8_t subdomain = 0xFF);
|
||||
bool product_is_valid_fast(
|
||||
const std::string& s, uint8_t domain, uint8_t subdomain = 0xFF);
|
||||
bool product_is_valid_fast(
|
||||
uint32_t product, uint8_t domain, uint8_t subdomain = 0xFF);
|
||||
bool decoded_product_is_valid_fast(
|
||||
uint32_t product, uint8_t domain, uint8_t subdomain = 0xFF);
|
||||
|
||||
std::string generate_product(uint8_t domain, uint8_t subdomain = 0xFF);
|
||||
std::unordered_map<uint32_t, std::string> generate_all_products(uint8_t domain = 0xFF, uint8_t subdomain = 0xFF);
|
||||
|
||||
void product_speed_test(uint64_t seed = 0xFFFFFFFFFFFFFFFF);
|
||||
Reference in New Issue
Block a user