From 61ac7bdbd436c7c77b5704cd75a9fe198bf7776d Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Sat, 19 Apr 2025 12:33:57 +0200 Subject: [PATCH 1/8] [CORE] Move matchbot tool to Core (#706) --- Core/Tools/CMakeLists.txt | 1 + .../Tools/matchbot/CMakeLists.txt | 12 +- .../Code => Core}/Tools/matchbot/debug.cpp | 0 .../Code => Core}/Tools/matchbot/debug.h | 0 .../Code => Core}/Tools/matchbot/encrypt.cpp | 0 .../Code => Core}/Tools/matchbot/encrypt.h | 0 .../Code => Core}/Tools/matchbot/generals.cpp | 0 .../Code => Core}/Tools/matchbot/generals.h | 0 .../Code => Core}/Tools/matchbot/global.cpp | 0 .../Code => Core}/Tools/matchbot/global.h | 0 .../Code => Core}/Tools/matchbot/main.cpp | 0 .../Code => Core}/Tools/matchbot/matcher.h | 0 .../Code => Core}/Tools/matchbot/mydebug.cpp | 0 .../Code => Core}/Tools/matchbot/mydebug.h | 0 .../Code => Core}/Tools/matchbot/rand.cpp | 0 .../Code => Core}/Tools/matchbot/rand.h | 0 .../Tools/matchbot/wlib/arraylist.h | 0 .../Tools/matchbot/wlib/configfile.cpp | 0 .../Tools/matchbot/wlib/configfile.h | 0 .../Tools/matchbot/wlib/critsec.cpp | 0 .../Tools/matchbot/wlib/critsec.h | 0 .../Tools/matchbot/wlib/dictionary.h | 0 .../Code => Core}/Tools/matchbot/wlib/filed.h | 0 .../Tools/matchbot/wlib/linkedlist.h | 0 .../Code => Core}/Tools/matchbot/wlib/mboxd.h | 0 .../Tools/matchbot/wlib/monod.cpp | 0 .../Code => Core}/Tools/matchbot/wlib/monod.h | 0 .../Tools/matchbot/wlib/odevice.h | 0 .../Tools/matchbot/wlib/sem4.cpp | 0 .../Code => Core}/Tools/matchbot/wlib/sem4.h | 0 .../Tools/matchbot/wlib/stderrd.h | 0 .../Tools/matchbot/wlib/stdoutd.h | 0 .../Tools/matchbot/wlib/streamer.cpp | 0 .../Tools/matchbot/wlib/streamer.h | 0 .../Tools/matchbot/wlib/syslogd.cpp | 0 .../Tools/matchbot/wlib/syslogd.h | 0 .../Tools/matchbot/wlib/threadfac.cpp | 0 .../Tools/matchbot/wlib/threadfac.h | 0 .../Tools/matchbot/wlib/threadsafe.h | 0 .../Tools/matchbot/wlib/timezone.cpp | 0 .../Tools/matchbot/wlib/timezone.h | 0 .../Tools/matchbot/wlib/ustring.h | 0 .../Tools/matchbot/wlib/wdebug.cpp | 0 .../Tools/matchbot/wlib/wdebug.h | 0 .../Tools/matchbot/wlib/wstring.cpp | 0 .../Tools/matchbot/wlib/wstring.h | 0 .../Tools/matchbot/wlib/wstypes.h | 0 .../Tools/matchbot/wlib/wtime.cpp | 0 .../Code => Core}/Tools/matchbot/wlib/wtime.h | 0 .../Tools/matchbot/wlib/xtime.cpp | 0 .../Code => Core}/Tools/matchbot/wlib/xtime.h | 0 .../Tools/matchbot/wnet/field.cpp | 0 .../Code => Core}/Tools/matchbot/wnet/field.h | 0 .../Tools/matchbot/wnet/packet.cpp | 0 .../Tools/matchbot/wnet/packet.h | 0 .../Code => Core}/Tools/matchbot/wnet/tcp.cpp | 0 .../Code => Core}/Tools/matchbot/wnet/tcp.h | 0 .../Code => Core}/Tools/matchbot/wnet/udp.cpp | 0 .../Code => Core}/Tools/matchbot/wnet/udp.h | 0 Generals/Code/Tools/CMakeLists.txt | 1 - Generals/Code/Tools/matchbot/debug.cpp | 61 - Generals/Code/Tools/matchbot/debug.h | 46 - Generals/Code/Tools/matchbot/encrypt.cpp | 94 -- Generals/Code/Tools/matchbot/encrypt.h | 28 - Generals/Code/Tools/matchbot/generals.cpp | 1322 ----------------- Generals/Code/Tools/matchbot/generals.h | 194 --- Generals/Code/Tools/matchbot/global.cpp | 51 - Generals/Code/Tools/matchbot/global.h | 57 - Generals/Code/Tools/matchbot/main.cpp | 409 ----- Generals/Code/Tools/matchbot/matcher.cpp | 421 ------ Generals/Code/Tools/matchbot/matcher.h | 93 -- Generals/Code/Tools/matchbot/mydebug.cpp | 101 -- Generals/Code/Tools/matchbot/mydebug.h | 134 -- Generals/Code/Tools/matchbot/rand.cpp | 132 -- Generals/Code/Tools/matchbot/rand.h | 45 - Generals/Code/Tools/matchbot/wlib/arraylist.h | 709 --------- .../Code/Tools/matchbot/wlib/configfile.cpp | 490 ------ .../Code/Tools/matchbot/wlib/configfile.h | 76 - Generals/Code/Tools/matchbot/wlib/critsec.cpp | 129 -- Generals/Code/Tools/matchbot/wlib/critsec.h | 62 - .../Code/Tools/matchbot/wlib/dictionary.h | 681 --------- Generals/Code/Tools/matchbot/wlib/filed.h | 51 - .../Code/Tools/matchbot/wlib/linkedlist.h | 495 ------ Generals/Code/Tools/matchbot/wlib/mboxd.h | 40 - Generals/Code/Tools/matchbot/wlib/monod.cpp | 57 - Generals/Code/Tools/matchbot/wlib/monod.h | 74 - Generals/Code/Tools/matchbot/wlib/odevice.h | 32 - Generals/Code/Tools/matchbot/wlib/sem4.cpp | 182 --- Generals/Code/Tools/matchbot/wlib/sem4.h | 64 - Generals/Code/Tools/matchbot/wlib/stderrd.h | 39 - Generals/Code/Tools/matchbot/wlib/stdoutd.h | 40 - .../Code/Tools/matchbot/wlib/streamer.cpp | 154 -- Generals/Code/Tools/matchbot/wlib/streamer.h | 67 - Generals/Code/Tools/matchbot/wlib/syslogd.cpp | 39 - Generals/Code/Tools/matchbot/wlib/syslogd.h | 48 - .../Code/Tools/matchbot/wlib/threadfac.cpp | 221 --- Generals/Code/Tools/matchbot/wlib/threadfac.h | 124 -- .../Code/Tools/matchbot/wlib/threadsafe.h | 62 - .../Code/Tools/matchbot/wlib/timezone.cpp | 71 - Generals/Code/Tools/matchbot/wlib/timezone.h | 41 - Generals/Code/Tools/matchbot/wlib/ustring.h | 61 - Generals/Code/Tools/matchbot/wlib/wdebug.cpp | 190 --- Generals/Code/Tools/matchbot/wlib/wdebug.h | 314 ---- Generals/Code/Tools/matchbot/wlib/wstring.cpp | 597 -------- Generals/Code/Tools/matchbot/wlib/wstring.h | 93 -- Generals/Code/Tools/matchbot/wlib/wstypes.h | 118 -- Generals/Code/Tools/matchbot/wlib/wtime.cpp | 802 ---------- Generals/Code/Tools/matchbot/wlib/wtime.h | 128 -- Generals/Code/Tools/matchbot/wlib/xtime.cpp | 1015 ------------- Generals/Code/Tools/matchbot/wlib/xtime.h | 146 -- Generals/Code/Tools/matchbot/wnet/field.cpp | 319 ---- Generals/Code/Tools/matchbot/wnet/field.h | 101 -- Generals/Code/Tools/matchbot/wnet/packet.cpp | 522 ------- Generals/Code/Tools/matchbot/wnet/packet.h | 108 -- Generals/Code/Tools/matchbot/wnet/tcp.cpp | 1242 ---------------- Generals/Code/Tools/matchbot/wnet/tcp.h | 199 --- Generals/Code/Tools/matchbot/wnet/udp.cpp | 372 ----- Generals/Code/Tools/matchbot/wnet/udp.h | 114 -- GeneralsMD/Code/Tools/CMakeLists.txt | 1 - GeneralsMD/Code/Tools/matchbot/CMakeLists.txt | 78 - 120 files changed, 7 insertions(+), 13763 deletions(-) rename {Generals/Code => Core}/Tools/matchbot/CMakeLists.txt (79%) rename {GeneralsMD/Code => Core}/Tools/matchbot/debug.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/debug.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/encrypt.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/encrypt.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/generals.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/generals.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/global.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/global.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/main.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/matcher.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/mydebug.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/mydebug.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/rand.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/rand.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/arraylist.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/configfile.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/configfile.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/critsec.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/critsec.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/dictionary.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/filed.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/linkedlist.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/mboxd.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/monod.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/monod.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/odevice.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/sem4.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/sem4.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/stderrd.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/stdoutd.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/streamer.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/streamer.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/syslogd.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/syslogd.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/threadfac.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/threadfac.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/threadsafe.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/timezone.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/timezone.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/ustring.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/wdebug.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/wdebug.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/wstring.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/wstring.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/wstypes.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/wtime.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/wtime.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/xtime.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wlib/xtime.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wnet/field.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wnet/field.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wnet/packet.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wnet/packet.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wnet/tcp.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wnet/tcp.h (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wnet/udp.cpp (100%) rename {GeneralsMD/Code => Core}/Tools/matchbot/wnet/udp.h (100%) delete mode 100644 Generals/Code/Tools/matchbot/debug.cpp delete mode 100644 Generals/Code/Tools/matchbot/debug.h delete mode 100644 Generals/Code/Tools/matchbot/encrypt.cpp delete mode 100644 Generals/Code/Tools/matchbot/encrypt.h delete mode 100644 Generals/Code/Tools/matchbot/generals.cpp delete mode 100644 Generals/Code/Tools/matchbot/generals.h delete mode 100644 Generals/Code/Tools/matchbot/global.cpp delete mode 100644 Generals/Code/Tools/matchbot/global.h delete mode 100644 Generals/Code/Tools/matchbot/main.cpp delete mode 100644 Generals/Code/Tools/matchbot/matcher.cpp delete mode 100644 Generals/Code/Tools/matchbot/matcher.h delete mode 100644 Generals/Code/Tools/matchbot/mydebug.cpp delete mode 100644 Generals/Code/Tools/matchbot/mydebug.h delete mode 100644 Generals/Code/Tools/matchbot/rand.cpp delete mode 100644 Generals/Code/Tools/matchbot/rand.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/arraylist.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/configfile.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/configfile.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/critsec.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/critsec.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/dictionary.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/filed.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/linkedlist.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/mboxd.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/monod.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/monod.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/odevice.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/sem4.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/sem4.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/stderrd.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/stdoutd.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/streamer.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/streamer.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/syslogd.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/syslogd.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/threadfac.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/threadfac.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/threadsafe.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/timezone.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/timezone.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/ustring.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/wdebug.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/wdebug.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/wstring.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/wstring.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/wstypes.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/wtime.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/wtime.h delete mode 100644 Generals/Code/Tools/matchbot/wlib/xtime.cpp delete mode 100644 Generals/Code/Tools/matchbot/wlib/xtime.h delete mode 100644 Generals/Code/Tools/matchbot/wnet/field.cpp delete mode 100644 Generals/Code/Tools/matchbot/wnet/field.h delete mode 100644 Generals/Code/Tools/matchbot/wnet/packet.cpp delete mode 100644 Generals/Code/Tools/matchbot/wnet/packet.h delete mode 100644 Generals/Code/Tools/matchbot/wnet/tcp.cpp delete mode 100644 Generals/Code/Tools/matchbot/wnet/tcp.h delete mode 100644 Generals/Code/Tools/matchbot/wnet/udp.cpp delete mode 100644 Generals/Code/Tools/matchbot/wnet/udp.h delete mode 100644 GeneralsMD/Code/Tools/matchbot/CMakeLists.txt diff --git a/Core/Tools/CMakeLists.txt b/Core/Tools/CMakeLists.txt index ee6e776fd66..6e3a1cdcb02 100644 --- a/Core/Tools/CMakeLists.txt +++ b/Core/Tools/CMakeLists.txt @@ -9,5 +9,6 @@ endif() if(RTS_BUILD_CORE_EXTRAS) add_subdirectory(Compress) add_subdirectory(CRCDiff) + add_subdirectory(matchbot) add_subdirectory(WW3D) endif() diff --git a/Generals/Code/Tools/matchbot/CMakeLists.txt b/Core/Tools/matchbot/CMakeLists.txt similarity index 79% rename from Generals/Code/Tools/matchbot/CMakeLists.txt rename to Core/Tools/matchbot/CMakeLists.txt index 316701119f9..d29e055db7d 100644 --- a/Generals/Code/Tools/matchbot/CMakeLists.txt +++ b/Core/Tools/matchbot/CMakeLists.txt @@ -59,14 +59,14 @@ set(MATCHBOT_SRC "wnet/udp.h" ) -add_executable(g_matchbot WIN32) -set_target_properties(g_matchbot PROPERTIES OUTPUT_NAME matchbot) +add_executable(core_matchbot WIN32) +set_target_properties(core_matchbot PROPERTIES OUTPUT_NAME matchbot) -target_sources(g_matchbot PRIVATE ${MATCHBOT_SRC}) +target_sources(core_matchbot PRIVATE ${MATCHBOT_SRC}) -target_include_directories(g_matchbot PRIVATE . wlib wnet) +target_include_directories(core_matchbot PRIVATE . wlib wnet) -target_link_libraries(g_matchbot PRIVATE +target_link_libraries(core_matchbot PRIVATE gamespy::gamespy core_config core_utility @@ -74,5 +74,5 @@ target_link_libraries(g_matchbot PRIVATE ) if(WIN32 OR "${CMAKE_SYSTEM}" MATCHES "Windows") - target_link_options(g_matchbot PRIVATE /subsystem:console) + target_link_options(core_matchbot PRIVATE /subsystem:console) endif() diff --git a/GeneralsMD/Code/Tools/matchbot/debug.cpp b/Core/Tools/matchbot/debug.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/debug.cpp rename to Core/Tools/matchbot/debug.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/debug.h b/Core/Tools/matchbot/debug.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/debug.h rename to Core/Tools/matchbot/debug.h diff --git a/GeneralsMD/Code/Tools/matchbot/encrypt.cpp b/Core/Tools/matchbot/encrypt.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/encrypt.cpp rename to Core/Tools/matchbot/encrypt.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/encrypt.h b/Core/Tools/matchbot/encrypt.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/encrypt.h rename to Core/Tools/matchbot/encrypt.h diff --git a/GeneralsMD/Code/Tools/matchbot/generals.cpp b/Core/Tools/matchbot/generals.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/generals.cpp rename to Core/Tools/matchbot/generals.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/generals.h b/Core/Tools/matchbot/generals.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/generals.h rename to Core/Tools/matchbot/generals.h diff --git a/GeneralsMD/Code/Tools/matchbot/global.cpp b/Core/Tools/matchbot/global.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/global.cpp rename to Core/Tools/matchbot/global.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/global.h b/Core/Tools/matchbot/global.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/global.h rename to Core/Tools/matchbot/global.h diff --git a/GeneralsMD/Code/Tools/matchbot/main.cpp b/Core/Tools/matchbot/main.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/main.cpp rename to Core/Tools/matchbot/main.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/matcher.h b/Core/Tools/matchbot/matcher.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/matcher.h rename to Core/Tools/matchbot/matcher.h diff --git a/GeneralsMD/Code/Tools/matchbot/mydebug.cpp b/Core/Tools/matchbot/mydebug.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/mydebug.cpp rename to Core/Tools/matchbot/mydebug.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/mydebug.h b/Core/Tools/matchbot/mydebug.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/mydebug.h rename to Core/Tools/matchbot/mydebug.h diff --git a/GeneralsMD/Code/Tools/matchbot/rand.cpp b/Core/Tools/matchbot/rand.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/rand.cpp rename to Core/Tools/matchbot/rand.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/rand.h b/Core/Tools/matchbot/rand.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/rand.h rename to Core/Tools/matchbot/rand.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/arraylist.h b/Core/Tools/matchbot/wlib/arraylist.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/arraylist.h rename to Core/Tools/matchbot/wlib/arraylist.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/configfile.cpp b/Core/Tools/matchbot/wlib/configfile.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/configfile.cpp rename to Core/Tools/matchbot/wlib/configfile.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/configfile.h b/Core/Tools/matchbot/wlib/configfile.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/configfile.h rename to Core/Tools/matchbot/wlib/configfile.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/critsec.cpp b/Core/Tools/matchbot/wlib/critsec.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/critsec.cpp rename to Core/Tools/matchbot/wlib/critsec.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/critsec.h b/Core/Tools/matchbot/wlib/critsec.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/critsec.h rename to Core/Tools/matchbot/wlib/critsec.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/dictionary.h b/Core/Tools/matchbot/wlib/dictionary.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/dictionary.h rename to Core/Tools/matchbot/wlib/dictionary.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/filed.h b/Core/Tools/matchbot/wlib/filed.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/filed.h rename to Core/Tools/matchbot/wlib/filed.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/linkedlist.h b/Core/Tools/matchbot/wlib/linkedlist.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/linkedlist.h rename to Core/Tools/matchbot/wlib/linkedlist.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/mboxd.h b/Core/Tools/matchbot/wlib/mboxd.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/mboxd.h rename to Core/Tools/matchbot/wlib/mboxd.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/monod.cpp b/Core/Tools/matchbot/wlib/monod.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/monod.cpp rename to Core/Tools/matchbot/wlib/monod.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/monod.h b/Core/Tools/matchbot/wlib/monod.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/monod.h rename to Core/Tools/matchbot/wlib/monod.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/odevice.h b/Core/Tools/matchbot/wlib/odevice.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/odevice.h rename to Core/Tools/matchbot/wlib/odevice.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/sem4.cpp b/Core/Tools/matchbot/wlib/sem4.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/sem4.cpp rename to Core/Tools/matchbot/wlib/sem4.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/sem4.h b/Core/Tools/matchbot/wlib/sem4.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/sem4.h rename to Core/Tools/matchbot/wlib/sem4.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/stderrd.h b/Core/Tools/matchbot/wlib/stderrd.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/stderrd.h rename to Core/Tools/matchbot/wlib/stderrd.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/stdoutd.h b/Core/Tools/matchbot/wlib/stdoutd.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/stdoutd.h rename to Core/Tools/matchbot/wlib/stdoutd.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/streamer.cpp b/Core/Tools/matchbot/wlib/streamer.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/streamer.cpp rename to Core/Tools/matchbot/wlib/streamer.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/streamer.h b/Core/Tools/matchbot/wlib/streamer.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/streamer.h rename to Core/Tools/matchbot/wlib/streamer.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/syslogd.cpp b/Core/Tools/matchbot/wlib/syslogd.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/syslogd.cpp rename to Core/Tools/matchbot/wlib/syslogd.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/syslogd.h b/Core/Tools/matchbot/wlib/syslogd.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/syslogd.h rename to Core/Tools/matchbot/wlib/syslogd.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/threadfac.cpp b/Core/Tools/matchbot/wlib/threadfac.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/threadfac.cpp rename to Core/Tools/matchbot/wlib/threadfac.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/threadfac.h b/Core/Tools/matchbot/wlib/threadfac.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/threadfac.h rename to Core/Tools/matchbot/wlib/threadfac.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/threadsafe.h b/Core/Tools/matchbot/wlib/threadsafe.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/threadsafe.h rename to Core/Tools/matchbot/wlib/threadsafe.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/timezone.cpp b/Core/Tools/matchbot/wlib/timezone.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/timezone.cpp rename to Core/Tools/matchbot/wlib/timezone.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/timezone.h b/Core/Tools/matchbot/wlib/timezone.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/timezone.h rename to Core/Tools/matchbot/wlib/timezone.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/ustring.h b/Core/Tools/matchbot/wlib/ustring.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/ustring.h rename to Core/Tools/matchbot/wlib/ustring.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/wdebug.cpp b/Core/Tools/matchbot/wlib/wdebug.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/wdebug.cpp rename to Core/Tools/matchbot/wlib/wdebug.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/wdebug.h b/Core/Tools/matchbot/wlib/wdebug.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/wdebug.h rename to Core/Tools/matchbot/wlib/wdebug.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/wstring.cpp b/Core/Tools/matchbot/wlib/wstring.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/wstring.cpp rename to Core/Tools/matchbot/wlib/wstring.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/wstring.h b/Core/Tools/matchbot/wlib/wstring.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/wstring.h rename to Core/Tools/matchbot/wlib/wstring.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/wstypes.h b/Core/Tools/matchbot/wlib/wstypes.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/wstypes.h rename to Core/Tools/matchbot/wlib/wstypes.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/wtime.cpp b/Core/Tools/matchbot/wlib/wtime.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/wtime.cpp rename to Core/Tools/matchbot/wlib/wtime.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/wtime.h b/Core/Tools/matchbot/wlib/wtime.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/wtime.h rename to Core/Tools/matchbot/wlib/wtime.h diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/xtime.cpp b/Core/Tools/matchbot/wlib/xtime.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/xtime.cpp rename to Core/Tools/matchbot/wlib/xtime.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wlib/xtime.h b/Core/Tools/matchbot/wlib/xtime.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wlib/xtime.h rename to Core/Tools/matchbot/wlib/xtime.h diff --git a/GeneralsMD/Code/Tools/matchbot/wnet/field.cpp b/Core/Tools/matchbot/wnet/field.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wnet/field.cpp rename to Core/Tools/matchbot/wnet/field.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wnet/field.h b/Core/Tools/matchbot/wnet/field.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wnet/field.h rename to Core/Tools/matchbot/wnet/field.h diff --git a/GeneralsMD/Code/Tools/matchbot/wnet/packet.cpp b/Core/Tools/matchbot/wnet/packet.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wnet/packet.cpp rename to Core/Tools/matchbot/wnet/packet.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wnet/packet.h b/Core/Tools/matchbot/wnet/packet.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wnet/packet.h rename to Core/Tools/matchbot/wnet/packet.h diff --git a/GeneralsMD/Code/Tools/matchbot/wnet/tcp.cpp b/Core/Tools/matchbot/wnet/tcp.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wnet/tcp.cpp rename to Core/Tools/matchbot/wnet/tcp.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wnet/tcp.h b/Core/Tools/matchbot/wnet/tcp.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wnet/tcp.h rename to Core/Tools/matchbot/wnet/tcp.h diff --git a/GeneralsMD/Code/Tools/matchbot/wnet/udp.cpp b/Core/Tools/matchbot/wnet/udp.cpp similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wnet/udp.cpp rename to Core/Tools/matchbot/wnet/udp.cpp diff --git a/GeneralsMD/Code/Tools/matchbot/wnet/udp.h b/Core/Tools/matchbot/wnet/udp.h similarity index 100% rename from GeneralsMD/Code/Tools/matchbot/wnet/udp.h rename to Core/Tools/matchbot/wnet/udp.h diff --git a/Generals/Code/Tools/CMakeLists.txt b/Generals/Code/Tools/CMakeLists.txt index b96d413938f..f1b330cfed3 100644 --- a/Generals/Code/Tools/CMakeLists.txt +++ b/Generals/Code/Tools/CMakeLists.txt @@ -17,7 +17,6 @@ if(RTS_BUILD_GENERALS_EXTRAS) add_subdirectory(buildVersionUpdate) add_subdirectory(Launcher) add_subdirectory(mangler) - add_subdirectory(matchbot) add_subdirectory(PATCHGET) add_subdirectory(textureCompress) add_subdirectory(timingTest) diff --git a/Generals/Code/Tools/matchbot/debug.cpp b/Generals/Code/Tools/matchbot/debug.cpp deleted file mode 100644 index 642fc38d220..00000000000 --- a/Generals/Code/Tools/matchbot/debug.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -// FILE: debug.cpp //////////////////////////////////////////////////////// -// Minmal debug info -// Author: Matthew D. Campbell, Sept 2002 - -#include -#include "debug.h" -#include - -#ifdef DEBUG - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef _WINDOWS - -#include -void DebugCrash( const char *fmt, ... ) {} -char* TheCurrentIgnoreCrashPtr; - -#else - -#endif - -void DebugLog(const char *fmt, ...) -{ - static char buffer[1024]; - va_list va; - va_start( va, fmt ); - vsnprintf(buffer, 1024, fmt, va ); - buffer[1023] = 0; - va_end( va ); - - //printf( buffer ); - DBGMSG(buffer); -} - -#ifdef __cplusplus -} -#endif - -#endif // DEBUG - diff --git a/Generals/Code/Tools/matchbot/debug.h b/Generals/Code/Tools/matchbot/debug.h deleted file mode 100644 index 2749241feef..00000000000 --- a/Generals/Code/Tools/matchbot/debug.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -// FILE: debug.h ////////////////////////////////////////////////////////////// -// Minimal debug info -// Author: Matthew D. Campbell, Sept 2002 - -#ifndef __DEBUG_H__ -#define __DEBUG_H__ - -#ifdef DEBUG - -#include - -#ifdef __cplusplus -extern "C" { -#endif -void DebugLog( const char *fmt, ... ); -#ifdef __cplusplus -} -#endif -#define DEBUG_LOG(x) DebugLog x - -#else // DEBUG - -#define DEBUG_LOG(x) {} - -#endif // DEBUG - -#endif // __DEBUG_H__ - diff --git a/Generals/Code/Tools/matchbot/encrypt.cpp b/Generals/Code/Tools/matchbot/encrypt.cpp deleted file mode 100644 index 06950a90019..00000000000 --- a/Generals/Code/Tools/matchbot/encrypt.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include -#include -#include "encrypt.h" - -#define MAX_CHARS 65 -static char Base_String[MAX_CHARS] = - - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; - -static char Return_Buffer[MAX_ENCRYPTED_STRING + 1]; -static char Temp_Buffer [MAX_ENCRYPTED_STRING + 1]; - -/*******************************************************/ -/* This function is a simple one-way encryption that */ -/* is portable across many platforms */ -/* */ -/* The 'String' to encrypt MUST be up to 8 chars long */ -/* and should be no shorter than 4 characters. */ -/* It can contain letters and numbers and '.' and '/' */ -/*******************************************************/ - -/* String is the original string to encrypt */ -/* Seed is the string to encrypt */ -//char *encrypt(char *String, char *Seed) -char *do_encrypt(char *String) -{ - /* We need a 56 bit key, so use two 32 bit values */ - /* and we'll strip off the high order 8 bits */ - //unsigned long Random_Seed_Value_high = 0; /* 32 bit seed value */ - //unsigned long Random_Seed_Value_low = 0; /* 32 bit seed value */ - //unsigned long Temp_high = 0; /* 32 bit storage value */ - //unsigned long Temp_low = 0; /* 32 bit storage value */ - unsigned int UpCnt = 0, DnCnt = 0, Cnt = 0; - unsigned int Length = strlen(String); - - /* Ok, here is the algorithm: */ - /* */ - - if (Length > MAX_ENCRYPTED_STRING) - Length = MAX_ENCRYPTED_STRING; - - for (UpCnt = 0, DnCnt = Length; UpCnt < Length; UpCnt++, DnCnt--) - if (String[UpCnt] & 0x01) - Temp_Buffer[UpCnt] = (String[UpCnt] << (String[UpCnt] & 0x01)) & - String[DnCnt]; - else - Temp_Buffer[UpCnt] = (String[UpCnt] << (String[UpCnt] & 0x01)) ^ - String[DnCnt]; - - for (Cnt = 0; Cnt < MAX_ENCRYPTED_STRING; Cnt++) - Return_Buffer[Cnt] = Base_String[Temp_Buffer[Cnt] & 0x3F]; - - Return_Buffer[Cnt] = NULL; - return (Return_Buffer); - -} - - -#ifdef UNIT_TEST -void main(void) -{ - char Input_String[9]; - char *new_string; - - while (1) - { - printf ("Enter a string to encrypt:"); - gets(Input_String); - printf("\nString enterred was: %s", Input_String); - new_string = encrypt(Input_String, "ab"); - printf("\nEncrypted string is: %s", new_string); - } -} - -#endif - diff --git a/Generals/Code/Tools/matchbot/encrypt.h b/Generals/Code/Tools/matchbot/encrypt.h deleted file mode 100644 index 97f7d69ed4a..00000000000 --- a/Generals/Code/Tools/matchbot/encrypt.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef ENCRYPT_HEADER -#define ENCRYPT_HEADER - -#define MAX_ENCRYPTED_STRING 8 -//char *do_encrypt(char *, char *); -char *do_encrypt(char *); - -#endif - - diff --git a/Generals/Code/Tools/matchbot/generals.cpp b/Generals/Code/Tools/matchbot/generals.cpp deleted file mode 100644 index 3a72c24d510..00000000000 --- a/Generals/Code/Tools/matchbot/generals.cpp +++ /dev/null @@ -1,1322 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifdef _WIN32 -#include -#endif - -#include -#include -#include -#include "mydebug.h" - -#include - -#include -#include -#include -#include - -#ifdef _UNIX -using namespace std; -#endif - -/* -#ifdef IN -#undef IN -#endif -#define IN const -*/ -#include "generals.h" -/* -#ifdef IN -#undef IN -#endif -#define IN const -*/ -#include "global.h" -/* -#ifdef IN -#undef IN -#endif -#define IN const -*/ - -std::string intToString(int val) -{ - std::string s = ""; - bool neg = (val < 0); - if (val < 0) - { - val = -val; - } - if (val == 0) - return "0"; - - char buf[2]; - buf[1] = 0; - while (val) - { - buf[0] = '0' + val%10; - val /= 10; - s.insert(0, buf); - } - if (neg) - s.insert(0, "-"); - return s; -} - -std::string uintToString(unsigned int val) -{ - std::string s = ""; - if (val == 0) - return "0"; - - char buf[2]; - buf[1] = 0; - while (val) - { - buf[0] = '0' + val%10; - val /= 10; - s.insert(0, buf); - } - return s; -} - -MapBitSet MapSetUnion(const MapBitSet& a, const MapBitSet& b) -{ - MapBitSet c; - if (a.size() != b.size()) - return c; - - for (int i=0; i<(int)a.size(); ++i) - { - c.push_back(a[i] && b[i]); - } - - return c; -} - -int MapSetCount(const MapBitSet& a) -{ - int count=0; - for (int i=0; i<(int)a.size(); ++i) - { - //DBGMSG(a[i]); - if (a[i]) - ++count; - } - return count; -} - -// ===================================================================== -// Users -// ===================================================================== - -GeneralsUser::GeneralsUser(void) -{ - status = STATUS_INCHANNEL; - points = 1; - minPoints = maxPoints = 100; - country = color = -1; - pseudoPing.clear(); - matchStart = time(NULL); - timeToWiden = 0; - widened = false; - numPlayers = 2; - discons = maxDiscons = 2; - maps.clear(); - maxPing = 1000; -} - -static const int MaxPingValue = 255*255*2; - -int calcPingDelta(const GeneralsUser *a, const GeneralsUser *b) -{ - if (!a || !b || a->pseudoPing.size() != b->pseudoPing.size()) - return MaxPingValue; // Max ping - - int bestPing = MaxPingValue; - for (int i=0; i<(int)a->pseudoPing.size(); ++i) - { - int p1, p2; - p1 = a->pseudoPing[i]; - p2 = b->pseudoPing[i]; - - if (p1 * p1 + p2 * p2 < bestPing) - bestPing = p1 * p1 + p2 * p2; - } - - return (int)sqrt(bestPing); -} - - -// ===================================================================== -// Matcher thread -// ===================================================================== - -GeneralsMatcher::GeneralsMatcher() -{ - // Read some values from the config file - int quietTMP = 0; - Global.config.getInt("NOECHO", quietTMP); - if (quietTMP) - quiet = true; - else - quiet = false; - - // Grab the weights for different parameters - Global.config.getInt("MATCH_WEIGHT_LOWPING", weightLowPing, "GENERALS"); - Global.config.getInt("MATCH_WEIGHT_AVGPOINTS", weightAvgPoints, "GENERALS"); - totalWeight = weightLowPing + weightAvgPoints; - - INFMSG("weightLowPing = " << weightLowPing); - INFMSG("weightAvgPoints = " << weightAvgPoints); - INFMSG("totalWeight = " << totalWeight); - - Global.config.getInt("SecondsBetweenPoolSizeAnnouncements", m_secondsBetweenPoolSizeAnnouncements, NULL); - if (m_secondsBetweenPoolSizeAnnouncements < 10) - { - m_secondsBetweenPoolSizeAnnouncements = 10; - } - m_nextPoolSizeAnnouncement = time(NULL); -} - -void GeneralsMatcher::init(void) -{} - - -#define W(x) setw(x) << -void GeneralsMatcher::dumpUsers(void) -{} - - - -void GeneralsMatcher::sendMatchInfo(std::string name1, std::string name2, std::string name3, std::string name4, - std::string name5, std::string name6, std::string name7, std::string name8, - GeneralsUser *user1, GeneralsUser *user2, GeneralsUser *user3, GeneralsUser *user4, - GeneralsUser *user5, GeneralsUser *user6, GeneralsUser *user7, GeneralsUser *user8, - int numPlayers, int ladderID) -{ - MapBitSet tmp = MapSetUnion(user1->maps, user2->maps); - if (numPlayers > 2) - { - tmp = MapSetUnion(tmp, user3->maps); - tmp = MapSetUnion(tmp, user4->maps); - } - if (numPlayers > 4) - { - tmp = MapSetUnion(tmp, user5->maps); - tmp = MapSetUnion(tmp, user6->maps); - } - if (numPlayers > 6) - { - tmp = MapSetUnion(tmp, user7->maps); - tmp = MapSetUnion(tmp, user8->maps); - } - - int numMaps = MapSetCount(tmp); - - if (!numMaps) - { - DBGMSG("No maps!"); - user1->status = STATUS_WORKING; - user2->status = STATUS_WORKING; - if (numPlayers > 2) - { - user3->status = STATUS_WORKING; - user4->status = STATUS_WORKING; - } - if (numPlayers > 4) - { - user5->status = STATUS_WORKING; - user6->status = STATUS_WORKING; - } - if (numPlayers > 6) - { - user7->status = STATUS_WORKING; - user8->status = STATUS_WORKING; - } - return; - } - user1->status = STATUS_MATCHED; - user2->status = STATUS_MATCHED; - if (numPlayers > 2) - { - user3->status = STATUS_MATCHED; - user4->status = STATUS_MATCHED; - } - if (numPlayers > 4) - { - user5->status = STATUS_MATCHED; - user6->status = STATUS_MATCHED; - } - if (numPlayers > 6) - { - user7->status = STATUS_MATCHED; - user8->status = STATUS_MATCHED; - } - - int whichMap = Global.rnd.Int(0, RAND_MAX-1); - DBGMSG(whichMap); - whichMap = whichMap%numMaps; - DBGMSG(whichMap); - ++whichMap; - DBGMSG(whichMap); - DBGMSG("Random map #" << whichMap << "/" << numMaps); - - int i; - for (i=0; i<(int)user1->maps.size(); ++i) - { - if (tmp[i]) - --whichMap; - if (whichMap == 0) - break; - } - DBGMSG("Playing on map in pos " << i); - - std::string s; - s = "MBOT:MATCHED "; - s.append(intToString(i)); - s.append(" "); - s.append(intToString( Global.rnd.Int(0, RAND_MAX-1) )); - s.append(" "); - s.append(name1); - s.append(" "); - s.append(uintToString(user1->IP)); - s.append(" "); - s.append(intToString(user1->country)); - s.append(" "); - s.append(intToString(user1->color)); - s.append(" "); - s.append(intToString(user1->NAT)); - s.append(" "); - s.append(name2); - s.append(" "); - s.append(uintToString(user2->IP)); - s.append(" "); - s.append(intToString(user2->country)); - s.append(" "); - s.append(intToString(user2->color)); - s.append(" "); - s.append(intToString(user2->NAT)); - if (user3) - { - s.append(" "); - s.append(name3); - s.append(" "); - s.append(uintToString(user3->IP)); - s.append(" "); - s.append(intToString(user3->country)); - s.append(" "); - s.append(intToString(user3->color)); - s.append(" "); - s.append(intToString(user3->NAT)); - } - if (user4) - { - s.append(" "); - s.append(name4); - s.append(" "); - s.append(uintToString(user4->IP)); - s.append(" "); - s.append(intToString(user4->country)); - s.append(" "); - s.append(intToString(user4->color)); - s.append(" "); - s.append(intToString(user4->NAT)); - } - if (user5) - { - s.append(" "); - s.append(name5); - s.append(" "); - s.append(uintToString(user5->IP)); - s.append(" "); - s.append(intToString(user5->country)); - s.append(" "); - s.append(intToString(user5->color)); - s.append(" "); - s.append(intToString(user5->NAT)); - } - if (user6) - { - s.append(" "); - s.append(name6); - s.append(" "); - s.append(uintToString(user6->IP)); - s.append(" "); - s.append(intToString(user6->country)); - s.append(" "); - s.append(intToString(user6->color)); - s.append(" "); - s.append(intToString(user6->NAT)); - } - if (user7) - { - s.append(" "); - s.append(name7); - s.append(" "); - s.append(uintToString(user7->IP)); - s.append(" "); - s.append(intToString(user7->country)); - s.append(" "); - s.append(intToString(user7->color)); - s.append(" "); - s.append(intToString(user7->NAT)); - } - if (user8) - { - s.append(" "); - s.append(name8); - s.append(" "); - s.append(uintToString(user8->IP)); - s.append(" "); - s.append(intToString(user8->country)); - s.append(" "); - s.append(intToString(user8->color)); - s.append(" "); - s.append(intToString(user8->NAT)); - } - - std::string n; - n = name1; - n.append(","); - n.append(name2); - if (user3) - { - n.append(","); - n.append(name3); - } - if (user4) - { - n.append(","); - n.append(name4); - } - if (user5) - { - n.append(","); - n.append(name5); - } - if (user6) - { - n.append(","); - n.append(name6); - } - if (user7) - { - n.append(","); - n.append(name7); - } - if (user8) - { - n.append(","); - n.append(name8); - } - peerMessagePlayer(m_peer, n.c_str(), s.c_str(), NormalMessage); -} - -void GeneralsMatcher::checkMatches(void) -{ - bool showPoolSize = false; - time_t now = time(NULL); - if (now > m_nextPoolSizeAnnouncement) - { - m_nextPoolSizeAnnouncement = now + m_secondsBetweenPoolSizeAnnouncements; - showPoolSize = true; - } - checkMatchesInUserMap(m_nonLadderUsers1v1, 0, 2, showPoolSize); - checkMatchesInUserMap(m_nonLadderUsers2v2, 0, 4, showPoolSize); - checkMatchesInUserMap(m_nonLadderUsers3v3, 0, 6, showPoolSize); - checkMatchesInUserMap(m_nonLadderUsers4v4, 0, 8, showPoolSize); - - for (LadderMap::iterator it = m_ladders.begin(); it != m_ladders.end(); ++it) - { - checkMatchesInUserMap(it->second, it->first, 2, showPoolSize); - } -} - -double GeneralsMatcher::computeMatchFitness(const std::string& i1, const GeneralsUser *u1, const std::string& i2, const GeneralsUser *u2) -{ - //DBGMSG("matching "<status != STATUS_WORKING || u2->status != STATUS_WORKING) - return 0.0; - - // see if they pinged the same # of servers (sanity). - if (u1->pseudoPing.size() != u2->pseudoPing.size()) - return 0.0; - - // check point percentage ranges - int p1 = max(1,u1->points), p2 = max(1,u2->points); - double p1percent = (double)p2/(double)p1; - double p2percent = (double)p1/(double)p2; - //DBGMSG("points: " << p1 << "," << p2 << " - " << p1percent << "," << p2percent); - if (!u1->widened && ( p1percent < u1->minPoints || p1percent > u1->maxPoints )) - return 0.0; - - if (!u2->widened && ( p2percent < u2->minPoints || p2percent > u2->maxPoints )) - return 0.0; - - - int minP = min(p1, p2); - int maxP = max(p1, p2); - double pointPercent = (double)minP/(double)maxP; - //DBGMSG("\tpointPercent = "<widened && pingDelta > u1->maxPing) - return 0.0; - if (!u2->widened && pingDelta > u2->maxPing) - return 0.0; - //DBGMSG("pingDelta="<discons << "," << u1->maxDiscons << " " << u2->discons << "," << u2->maxDiscons); - // check discons - if (u1->maxDiscons && (!u1->widened && u2->discons > u1->maxDiscons)) - return 0.0; - if (u2->maxDiscons && (!u2->widened && u1->discons > u2->maxDiscons)) - return 0.0; - //DBGMSG("Made it through discons"); - - { - MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); - if (!MapSetCount(tmp)) - return 0.0; - } - - // they have something in common. calculate match fitness. - double matchFitness = ( weightAvgPoints * (1-pointPercent) + - weightLowPing * (MaxPingValue - pingDelta)/MaxPingValue ) / (double)totalWeight; - //DBGMSG("Match fitness: "<first << " vs " << i2->first << " has fitness " << matchFitness << "\n" - "\tpointPercent: " << pointPercent << "\n" - "\tpingDelta: " << pingDelta << "\n" - "\twidened: " << u1->widened << u2->widened << "\n" - "\tweightAvgPoints: " << weightAvgPoints << "\n" - "\tweightLowPing: " << weightLowPing << "\n" - "\ttotalWeight: " << totalWeight - ); - */ - - return matchFitness; -} - -void GeneralsMatcher::checkMatchesInUserMap(UserMap& userMap, int ladderID, int numPlayers, bool showPoolSize) -{ - UserMap::iterator i1, i2, i3, i4, i5, i6, i7, i8; - GeneralsUser *u1, *u2, *u3, *u4, *u5, *u6, *u7, *u8; - static const double fitnessThreshold = 0.3; - time_t now = time(NULL); - - std::string s; - if (showPoolSize) - { - s = "MBOT:POOLSIZE "; - s.append(intToString(userMap.size())); - } - - // iterate through users, timing them out as neccessary - for (i1 = userMap.begin(); i1 != userMap.end(); ++i1) - { - if (showPoolSize) - { - peerMessagePlayer(m_peer, i1->first.c_str(), s.c_str(), NormalMessage); - } - - u1 = i1->second; - if (u1->timeToWiden && u1->timeToWiden < now) - { - u1->timeToWiden = 0; - u1->widened = true; - for (int m=0; m<(int)u1->maps.size(); ++m) - u1->maps[m] = 1; - DBGMSG("Widening search for " << i1->first); - peerMessagePlayer(m_peer, i1->first.c_str(), "MBOT:WIDENINGSEARCH", NormalMessage); - } - } - - // iterate through all users, looking for a match - for (i1 = userMap.begin(); i1 != userMap.end(); ++i1) - { - u1 = i1->second; - if (u1->status != STATUS_WORKING) - continue; - - GeneralsUser *bestUser = NULL; - double bestMatchFitness = 0.0; - std::string bestName = ""; - - // look at everybody left - i2 = i1; - for (++i2; i2 != userMap.end(); ++i2) - { - u2 = i2->second; - if (u2->status != STATUS_WORKING) - continue; - - double matchFitness = computeMatchFitness(i1->first, u1, i2->first, u2); - if (matchFitness > fitnessThreshold) - { - if (numPlayers == 2) - { - if (matchFitness > bestMatchFitness) - { - // possibly match 2 players - bestMatchFitness = matchFitness; - bestUser = u2; - bestName = i2->first; - } - } - else - { - i3 = i2; - for (++i3; i3 != userMap.end(); ++i3) - { - u3 = i3->second; - if (u3->status != STATUS_WORKING) - continue; - - double matchFitness1 = computeMatchFitness(i1->first, u1, i3->first, u3); - double matchFitness2 = computeMatchFitness(i2->first, u2, i3->first, u3); - MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); - tmp = MapSetUnion(tmp, u3->maps); - if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold) - { - i4 = i3; - for (++i4; i4 != userMap.end(); ++i4) - { - u4 = i4->second; - if (u4->status != STATUS_WORKING) - continue; - - double matchFitness1 = computeMatchFitness(i1->first, u1, i4->first, u4); - double matchFitness2 = computeMatchFitness(i2->first, u2, i4->first, u4); - double matchFitness3 = computeMatchFitness(i3->first, u3, i4->first, u4); - MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); - tmp = MapSetUnion(tmp, u3->maps); - tmp = MapSetUnion(tmp, u4->maps); - if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold) - { - if (numPlayers == 4) - { - // match 4 players - sendMatchInfo(i1->first, i2->first, i3->first, i4->first, "", "", "", "", - u1, u2, u3, u4, NULL, NULL, NULL, NULL, 4, ladderID); - break; - } - else - { - - i5 = i4; - for (++i5; i5 != userMap.end(); ++i3) - { - u5 = i5->second; - if (u5->status != STATUS_WORKING) - continue; - - double matchFitness1 = computeMatchFitness(i1->first, u1, i5->first, u5); - double matchFitness2 = computeMatchFitness(i2->first, u2, i5->first, u5); - double matchFitness3 = computeMatchFitness(i3->first, u3, i5->first, u5); - double matchFitness4 = computeMatchFitness(i4->first, u4, i5->first, u5); - MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); - tmp = MapSetUnion(tmp, u3->maps); - tmp = MapSetUnion(tmp, u4->maps); - tmp = MapSetUnion(tmp, u5->maps); - if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold) - { - i6 = i5; - for (++i6; i6 != userMap.end(); ++i6) - { - u6 = i6->second; - if (u6->status != STATUS_WORKING) - continue; - - double matchFitness1 = computeMatchFitness(i1->first, u1, i6->first, u6); - double matchFitness2 = computeMatchFitness(i2->first, u2, i6->first, u6); - double matchFitness3 = computeMatchFitness(i3->first, u3, i6->first, u6); - double matchFitness4 = computeMatchFitness(i4->first, u4, i6->first, u6); - double matchFitness5 = computeMatchFitness(i5->first, u5, i6->first, u6); - MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); - tmp = MapSetUnion(tmp, u3->maps); - tmp = MapSetUnion(tmp, u4->maps); - tmp = MapSetUnion(tmp, u5->maps); - tmp = MapSetUnion(tmp, u6->maps); - if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold) - { - if (numPlayers == 6) - { - // match 6 players - sendMatchInfo(i1->first, i2->first, i3->first, i4->first, i5->first, i6->first, "", "", - u1, u2, u3, u4, u5, u6, NULL, NULL, 6, ladderID); - break; - } - else - { - - i7 = i6; - for (++i7; i7 != userMap.end(); ++i7) - { - u7 = i7->second; - if (u7->status != STATUS_WORKING) - continue; - - double matchFitness1 = computeMatchFitness(i1->first, u1, i7->first, u7); - double matchFitness2 = computeMatchFitness(i2->first, u2, i7->first, u7); - double matchFitness3 = computeMatchFitness(i3->first, u3, i7->first, u7); - double matchFitness4 = computeMatchFitness(i4->first, u4, i7->first, u7); - double matchFitness5 = computeMatchFitness(i5->first, u5, i7->first, u7); - double matchFitness6 = computeMatchFitness(i6->first, u6, i7->first, u7); - MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); - tmp = MapSetUnion(tmp, u3->maps); - tmp = MapSetUnion(tmp, u4->maps); - tmp = MapSetUnion(tmp, u5->maps); - tmp = MapSetUnion(tmp, u6->maps); - tmp = MapSetUnion(tmp, u7->maps); - if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold && matchFitness6 > fitnessThreshold) - { - i8 = i7; - for (++i8; i8 != userMap.end(); ++i8) - { - u8 = i8->second; - if (u8->status != STATUS_WORKING) - continue; - - double matchFitness1 = computeMatchFitness(i1->first, u1, i8->first, u8); - double matchFitness2 = computeMatchFitness(i2->first, u2, i8->first, u8); - double matchFitness3 = computeMatchFitness(i3->first, u3, i8->first, u8); - double matchFitness4 = computeMatchFitness(i4->first, u4, i8->first, u8); - double matchFitness5 = computeMatchFitness(i5->first, u5, i8->first, u8); - double matchFitness6 = computeMatchFitness(i6->first, u6, i8->first, u8); - double matchFitness7 = computeMatchFitness(i7->first, u7, i8->first, u8); - MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); - tmp = MapSetUnion(tmp, u3->maps); - tmp = MapSetUnion(tmp, u4->maps); - tmp = MapSetUnion(tmp, u5->maps); - tmp = MapSetUnion(tmp, u6->maps); - tmp = MapSetUnion(tmp, u7->maps); - tmp = MapSetUnion(tmp, u8->maps); - if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold && matchFitness6 > fitnessThreshold && matchFitness7 > fitnessThreshold) - { - // match 8 players - sendMatchInfo(i1->first, i2->first, i3->first, i4->first, i5->first, i6->first, i7->first, i8->first, - u1, u2, u3, u4, u5, u6, u7, u8, 8, ladderID); - break; - } - } - } - } - - } - } - } - } - } - - } - } - } - } - } - } - } - } // for i2 - - if (bestUser && numPlayers == 2) - { - // we had a match. send the info. - DBGMSG("Matching " << i1->first << " with " << bestName << ":\n" - "\tmatch fitness: " << bestMatchFitness << "\n" - "\tpoint percentage: " << (1-bestUser->points/(double)u1->points)*100 << "\n" - "\tpoints: " << u1->points << ", " << u2->points << "\n" - "\tping in ms: " << sqrt(1000000 * calcPingDelta(u1, bestUser) / (255*255*2)) << "\n" - "\tprevious attempts: " << u1->widened << ", " << bestUser->widened); - sendMatchInfo(i1->first, bestName, "", "", "", "", "", "", - u1, bestUser, NULL, NULL, NULL, NULL, NULL, NULL, 2, ladderID); - break; - } - } // for i1 - - dumpUsers(); -} - -// return false for possible hack attempt -bool GeneralsMatcher::handleUserWiden(const char *nick) -{ - GeneralsUser *userInfo = findUserInAnyLadder(nick); - if (!userInfo) - { - userInfo = findNonLadderUser(nick); - } - - if (!userInfo) - { - DBGMSG("Got Widen from nick not needing one!"); - peerMessagePlayer(m_peer, nick, "MBOT:CANTSENDWIDENNOW", NormalMessage); - return false; - } - DBGMSG("Widening search for " << nick); - peerMessagePlayer(m_peer, nick, "MBOT:WIDENINGSEARCH", NormalMessage); - - userInfo->widened = true; - return true; -} - -bool GeneralsMatcher::handleUserInfo(const char *nick, const std::string& msg) -{ - DBGMSG("Got user info [" << msg << "] from " << nick); - GeneralsUser *userInfo = removeNonMatchingUser(nick); - if (!userInfo) - { - DBGMSG("Got UserInfo from nick not needing one!"); - peerMessagePlayer(m_peer, nick, "MBOT:CANTSENDCINFONOW", NormalMessage); - return false; - } - DBGMSG("Looking at " << nick << " with user info [" << msg << "]"); - - int ladderID = 0; - unsigned int ladderPassCRC = 0; - - int offset = 0; - while (1) - { - int firstMarker = msg.find_first_of('\\', offset); - if (firstMarker < 0) - break; - int secondMarker = msg.find_first_of('\\', firstMarker + 1); - if (secondMarker < 0) - break; - int thirdMarker = msg.find_first_of('\\', secondMarker + 1); - if (thirdMarker < 0) - break; - std::string k = msg.substr(firstMarker + 1, secondMarker - firstMarker - 1); - std::string v = msg.substr(secondMarker + 1, thirdMarker - secondMarker - 1); - offset = thirdMarker - 1; - - if (k == "Widen") - { - int val = atoi(v.c_str()); - if (val > 0) - userInfo->timeToWiden = time(NULL) + val; - else - userInfo->timeToWiden = 0; - } - else if (k == "LadID") - { - ladderID = atoi(v.c_str()); - } - else if (k == "LadPass") - { - ladderPassCRC = atoi(v.c_str()); - } - else if (k == "PointsMin") - { - userInfo->minPoints = atoi(v.c_str()); - } - else if (k == "PointsMax") - { - userInfo->maxPoints = atoi(v.c_str()); - } - else if (k == "PingMax") - { - userInfo->maxPing = atoi(v.c_str()); - } - else if (k == "DisconMax") - { - userInfo->maxDiscons = atoi(v.c_str()); - } - else if (k == "Maps") - { -#ifdef DEBUG - //int curMaps = userInfo->maps.size(); -#endif - - //DBGMSG("map cur size is " << curMaps); - userInfo->maps.clear(); - if (!v.length()) - { - INFMSG("Bad maps from " << nick << ": [" << v << "]"); - peerMessagePlayer(m_peer, nick, "MBOT:BADMAPS", NormalMessage); - return false; - } - const char *buf = v.c_str(); - int pos = 0; - while (*buf) - { - bool hasMap = (*buf != '0'); - //DBGMSG("Setting map " << pos << " to " << hasMap); - userInfo->maps.push_back( hasMap ); - ++pos; - ++buf; - } - } - else if (k == "NumPlayers") - { - userInfo->numPlayers = atoi(v.c_str()); - if (userInfo->numPlayers != 2 && userInfo->numPlayers != 4 && - userInfo->numPlayers != 6 && userInfo->numPlayers != 8) - { - INFMSG("Bad numPlayers from " << nick << ": [" << userInfo->numPlayers << "]"); - peerMessagePlayer(m_peer, nick, "MBOT:BADCINFO", NormalMessage); - return false; - } - } - else if (k == "IP") - { - userInfo->IP = atoi(v.c_str()); - } - else if (k == "NAT") - { - userInfo->NAT = atoi(v.c_str()); - } - else if (k == "Side") - { - userInfo->country = atoi(v.c_str()); - } - else if (k == "Color") - { - userInfo->color = atoi(v.c_str()); - } - else if (k == "Pings") - { - if (!v.length() || (v.length() % 2)) - { - INFMSG("Bad pings from " << nick << ": [" << v << "]"); - peerMessagePlayer(m_peer, nick, "MBOT:BADPINGS", NormalMessage); - return false; - } - int ping = 0; - const char *buf = v.c_str(); - char buf2[3]; - buf2[2] = '\0'; - // We've already assured that pingStr has non-zero even length. - while (*buf) - { - buf2[0] = *buf++; - buf2[1] = *buf++; - ping = (int)strtol(buf2, NULL, 16); - userInfo->pseudoPing.push_back(ping); - } - } - else if (k == "Points") - { - userInfo->points = max(1, atoi(v.c_str())); - } - else if (k == "Discons") - { - userInfo->discons = atoi(v.c_str()); - } - else - { - INFMSG("Unknown key/value pair in user info [" << k << "]/[" << v << "]"); - peerMessagePlayer(m_peer, nick, "MBOT:BADCINFO", NormalMessage); - return false; - } - } - - std::string s = "MBOT:WORKING "; - - if (ladderID) - { - addUserInLadder(nick, ladderID, userInfo); - s.append(intToString(m_ladders[ladderID].size())); - } - else - { - addNonLadderUser(nick, userInfo); - switch (userInfo->numPlayers) - { - case 2: - s.append(intToString(m_nonLadderUsers1v1.size())); - break; - case 4: - s.append(intToString(m_nonLadderUsers2v2.size())); - break; - case 6: - s.append(intToString(m_nonLadderUsers3v3.size())); - break; - case 8: - s.append(intToString(m_nonLadderUsers4v4.size())); - break; - } - } - - userInfo->status = STATUS_WORKING; - userInfo->matchStart = time(NULL); - peerMessagePlayer(m_peer, nick, s.c_str(), NormalMessage); - - DBGMSG("Player " << nick << " is matching now, ack was [" << s << "]"); - return true; -} - -GeneralsUser* GeneralsMatcher::findUser(const std::string& who) -{ - GeneralsUser *user; - user = findNonLadderUser(who); - if (user) - return user; - user = findNonMatchingUser(who); - if (user) - return user; - user = findUserInAnyLadder(who); - if (user) - return user; - - return NULL; -} - -GeneralsUser* GeneralsMatcher::findUserInAnyLadder(const std::string& who) -{ - for (LadderMap::iterator lIt = m_ladders.begin(); lIt != m_ladders.end(); ++lIt) - { - UserMap::iterator uIt = lIt->second.find(who); - if (uIt != lIt->second.end()) - return uIt->second; - } - return NULL; -} - -GeneralsUser* GeneralsMatcher::findUserInLadder(const std::string& who, int ladderID) -{ - LadderMap::iterator lIt = m_ladders.find(ladderID); - if (lIt == m_ladders.end()) - return NULL; - - UserMap::iterator uIt = lIt->second.find(who); - if (uIt == lIt->second.end()) - return NULL; - - return uIt->second; -} - -GeneralsUser* GeneralsMatcher::findNonLadderUser(const std::string& who) -{ - UserMap::iterator it = m_nonLadderUsers1v1.find(who); - if (it != m_nonLadderUsers1v1.end()) - return it->second; - - it = m_nonLadderUsers2v2.find(who); - if (it != m_nonLadderUsers2v2.end()) - return it->second; - - it = m_nonLadderUsers3v3.find(who); - if (it != m_nonLadderUsers3v3.end()) - return it->second; - - it = m_nonLadderUsers4v4.find(who); - if (it != m_nonLadderUsers4v4.end()) - return it->second; - - return NULL; -} - -GeneralsUser* GeneralsMatcher::findNonMatchingUser(const std::string& who) -{ - UserMap::iterator it = m_nonMatchingUsers.find(who); - if (it == m_nonMatchingUsers.end()) - return NULL; - - return it->second; -} - -void GeneralsMatcher::addUser(const std::string& who) -{ - if (findUser(who)) - { - ERRMSG("Re-adding " << who); - return; - } - - addNonMatchingUser(who, new GeneralsUser); -} - -void GeneralsMatcher::addUserInLadder(const std::string& who, int ladderID, GeneralsUser *user) -{ - m_ladders[ladderID][who] = user; -} - -void GeneralsMatcher::addNonLadderUser(const std::string& who, GeneralsUser *user) -{ - switch (user->numPlayers) - { - case 2: - m_nonLadderUsers1v1[who] = user; - break; - case 4: - m_nonLadderUsers2v2[who] = user; - break; - case 6: - m_nonLadderUsers3v3[who] = user; - break; - case 8: - m_nonLadderUsers4v4[who] = user; - break; - } -} - -void GeneralsMatcher::addNonMatchingUser(const std::string& who, GeneralsUser *user) -{ - m_nonMatchingUsers[who] = user; -} - - -bool GeneralsMatcher::removeUser(const std::string& who) -{ - GeneralsUser *user; - user = removeUserInAnyLadder(who); - if (user) - { - delete user; - return true; - } - user = removeNonLadderUser(who); - if (user) - { - delete user; - return true; - } - user = removeNonMatchingUser(who); - if (user) - { - delete user; - return true; - } - - return false; -} - -GeneralsUser* GeneralsMatcher::removeUserInLadder(const std::string& who, int ladderID) -{ - LadderMap::iterator lIt = m_ladders.find(ladderID); - if (lIt == m_ladders.end()) - return NULL; - - UserMap::iterator uIt = lIt->second.find(who); - if (uIt == lIt->second.end()) - return NULL; - - GeneralsUser *user = uIt->second; - lIt->second.erase(uIt); - return user; -} - -GeneralsUser* GeneralsMatcher::removeUserInAnyLadder(const std::string& who) -{ - for (LadderMap::iterator lIt = m_ladders.begin(); lIt != m_ladders.end(); ++lIt) - { - UserMap::iterator uIt = lIt->second.find(who); - if (uIt != lIt->second.end()) - { - GeneralsUser *user = uIt->second; - lIt->second.erase(uIt); - return user; - } - } - return NULL; -} - -GeneralsUser* GeneralsMatcher::removeNonLadderUser(const std::string& who) -{ - UserMap::iterator it = m_nonLadderUsers1v1.find(who); - if (it != m_nonLadderUsers1v1.end()) - { - GeneralsUser *user = it->second; - m_nonLadderUsers1v1.erase(it); - return user; - } - - it = m_nonLadderUsers2v2.find(who); - if (it != m_nonLadderUsers2v2.end()) - { - GeneralsUser *user = it->second; - m_nonLadderUsers2v2.erase(it); - return user; - } - - it = m_nonLadderUsers3v3.find(who); - if (it != m_nonLadderUsers3v3.end()) - { - GeneralsUser *user = it->second; - m_nonLadderUsers3v3.erase(it); - return user; - } - - it = m_nonLadderUsers4v4.find(who); - if (it != m_nonLadderUsers4v4.end()) - { - GeneralsUser *user = it->second; - m_nonLadderUsers4v4.erase(it); - return user; - } - - return NULL; -} - -GeneralsUser* GeneralsMatcher::removeNonMatchingUser(const std::string& who) -{ - UserMap::iterator it = m_nonMatchingUsers.find(who); - if (it == m_nonMatchingUsers.end()) - return NULL; - - GeneralsUser *user = it->second; - m_nonMatchingUsers.erase(it); - return user; -} - - -void GeneralsMatcher::handleDisconnect( const char *reason ) -{ - ERRMSG("Disconnected"); - done = true; - exit(0); -} - -void GeneralsMatcher::handleRoomMessage( const char *nick, const char *message, MessageType messageType ) -{ - if (messageType == ActionMessage) - { - PARANOIDMSG(nick << " " << message); - } - else - { - PARANOIDMSG("[" << nick << "] " << message); - } -} - -void GeneralsMatcher::handlePlayerMessage( const char *nick, const char *message, MessageType messageType ) -{ - if (messageType == ActionMessage) - { - DBGMSG(nick << " " << message); - } - else - { - DBGMSG("[" << nick << "] " << message); - } - - if (messageType != NormalMessage) - return; - - std::string line = message; - line.append("\\"); - - int offset = 0; - int firstMarker = line.find_first_of('\\', offset); - int secondMarker = line.find_first_of('\\', firstMarker + 1); - if (firstMarker >= 0 && secondMarker >= 0) - { - std::string marker = line.substr(firstMarker + 1, secondMarker - firstMarker - 1); - if (marker == "CINFO") - { - handleUserInfo(nick, line.substr(secondMarker));//, std::string::npos)); - } - else if (marker == "WIDEN") - { - handleUserWiden(nick); - } - else - { - INFMSG("Unknown marker [" << marker << "] in line [" << line << "] from " << nick); - } - } - else - { - INFMSG("Failed to parse line [" << line << "] from " << nick); - } -} - -void GeneralsMatcher::handlePlayerJoined( const char *nick ) -{ - DBGMSG("Player " << nick << " joined"); - addUser(nick); -} - -void GeneralsMatcher::handlePlayerLeft( const char *nick ) -{ - DBGMSG("Player " << nick << " left"); - if (m_nick != nick) - removeUser(nick); -} - -void GeneralsMatcher::handlePlayerChangedNick( const char *oldNick, const char *newNick ) -{ - DBGMSG("Player " << oldNick << " changed nick to " << newNick << " - resetting to non-matching state"); - removeUser(oldNick); - addUser(newNick); -} - -void GeneralsMatcher::handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags ) -{ - if (!nick) - nick = ""; - DBGMSG("PlayerEnum: success=" << success << " index=" << gameSpyIndex << ", nick=" << nick << ", flags=" << flags); - - if (success && gameSpyIndex >= 0 && m_nick != nick) - { - addUser(nick); - } -} - - -// ===================================================================== -// TEST Client Matcher class -// ===================================================================== - -GeneralsClientMatcher::GeneralsClientMatcher() -{ - // Read some values from the config file - int quietTMP = 0; - Global.config.getInt("NOECHO", quietTMP); - if (quietTMP) - quiet = true; - else - quiet = false; -} - -void GeneralsClientMatcher::init(void) -{ - m_baseNick.setFormatted("qmBot%d", time(NULL)); - m_profileID = 0; -} - -void GeneralsClientMatcher::checkMatches(void) -{} - -void GeneralsClientMatcher::handleDisconnect( const char *reason ) -{} -void GeneralsClientMatcher::handleRoomMessage( const char *nick, const char *message, MessageType messageType ) -{} -void GeneralsClientMatcher::handlePlayerMessage( const char *nick, const char *message, MessageType messageType ) -{} -void GeneralsClientMatcher::handlePlayerJoined( const char *nick ) -{} -void GeneralsClientMatcher::handlePlayerLeft( const char *nick ) -{} -void GeneralsClientMatcher::handlePlayerChangedNick( const char *oldNick, const char *newNick ) -{} -void GeneralsClientMatcher::handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags ) -{} - - -// ===================================================================== -// End of File -// ===================================================================== - - diff --git a/Generals/Code/Tools/matchbot/generals.h b/Generals/Code/Tools/matchbot/generals.h deleted file mode 100644 index e0761e110e2..00000000000 --- a/Generals/Code/Tools/matchbot/generals.h +++ /dev/null @@ -1,194 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef __GENERALS_H__ -#define __GENERALS_H__ - -#ifdef _WIN32 -#include -#endif -//#include -//#include -//#include -#include "matcher.h" -#include "global.h" - -#include -#include -#include -#include -#include - -typedef std::vector MapBitSet; - -// ===================================================================== -// Users -// ===================================================================== - -// Here are the states a matcher can be in: -typedef enum -{ - STATUS_INVAL = 0, - STATUS_INCHANNEL, // Just entered the channel - STATUS_WORKING, // Sent info, needs to be matched - STATUS_MATCHED, // Been matched, but is still in the channel -} UserStatus; - -class GeneralsUser -{ -public: - GeneralsUser(void); - UserStatus status; - - int points, minPoints, maxPoints; - - int discons, maxDiscons; - - int country; - int color; - - bool widened; - time_t timeToWiden; - time_t matchStart; // when did we request a match? - - // This is a ping to a designated 3rd-party server who just - // responds to pings. The idea is that if the game server is - // behind a firewall, the client will have a 1000ms ping to it, - // even though he might be very close. To combat this, we have - // clients & servers ping some 3rd-party servers & calculate a - // pesudo-ping based on the sum of pings server-->3rd-->client. - std::vector pseudoPing; - int maxPing; - - unsigned int IP; - int NAT; - - MapBitSet maps; - - int numPlayers; -}; - -// ===================================================================== -// Matcher class -// ===================================================================== - -typedef std::map UserMap; -typedef std::map LadderMap; - -class GeneralsMatcher : public MatcherClass -{ -public: - GeneralsMatcher(); - virtual ~GeneralsMatcher() - {} - - virtual void init(void); - virtual void checkMatches(void); - - virtual void handleDisconnect( const char *reason ); - virtual void handleRoomMessage( const char *nick, const char *message, MessageType messageType ); - virtual void handlePlayerMessage( const char *nick, const char *message, MessageType messageType ); - virtual void handlePlayerJoined( const char *nick ); - virtual void handlePlayerLeft( const char *nick ); - virtual void handlePlayerChangedNick( const char *oldNick, const char *newNick ); - virtual void handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags ); - -private: - LadderMap m_ladders; - UserMap m_nonLadderUsers1v1; - UserMap m_nonLadderUsers2v2; - UserMap m_nonLadderUsers3v3; - UserMap m_nonLadderUsers4v4; - UserMap m_nonMatchingUsers; - - double computeMatchFitness(const std::string& i1, const GeneralsUser *u1, const std::string& i2, const GeneralsUser *u2); - - GeneralsUser* findUser(const std::string& who); - GeneralsUser* findUserInLadder(const std::string& who, int ladderID); - GeneralsUser* findUserInAnyLadder(const std::string& who); - GeneralsUser* findNonLadderUser(const std::string& who); - GeneralsUser* findNonMatchingUser(const std::string& who); - - void addUser(const std::string& who); - void addUserInLadder(const std::string& who, int ladderID, GeneralsUser *user); - void addUserInAnyLadder(const std::string& who, GeneralsUser *user); - void addNonLadderUser(const std::string& who, GeneralsUser *user); - void addNonMatchingUser(const std::string& who, GeneralsUser *user); - - bool removeUser(const std::string& who); - GeneralsUser* removeUserInLadder(const std::string& who, int ladderID); - GeneralsUser* removeUserInAnyLadder(const std::string& who); - GeneralsUser* removeNonLadderUser(const std::string& who); - GeneralsUser* removeNonMatchingUser(const std::string& who); - - void checkMatchesInUserMap(UserMap& userMap, int ladderID, int numPlayers, bool showPoolSize); - - void dumpUsers(void); - - void sendMatchInfo(std::string name1, std::string name2, std::string name3, std::string name4, - std::string name5, std::string name6, std::string name7, std::string name8, - GeneralsUser *user1, GeneralsUser *user2, GeneralsUser *user3, GeneralsUser *user4, - GeneralsUser *user5, GeneralsUser *user6, GeneralsUser *user7, GeneralsUser *user8, - int numPlayers, int ladderID); - - // Command handlers for above privmsg commands (offset is the - // offset for the getToken() past the command token) - bool handleUserInfo(const char *nick, const std::string& msg); - bool handleUserWiden(const char *nick); - - // Weights for various matching parameters - int weightLowPing; - int weightAvgPoints; - int totalWeight; - - time_t m_nextPoolSizeAnnouncement; - int m_secondsBetweenPoolSizeAnnouncements; - - //typedef std::vector StringVec; - //StringVec mapFileList; -} -; - -// ===================================================================== -// TEST Client Matcher class -// ===================================================================== - -class GeneralsClientMatcher : public MatcherClass -{ -public: - GeneralsClientMatcher(); - virtual ~GeneralsClientMatcher() - {} - - virtual void init(void); - virtual void checkMatches(void); - - virtual void handleDisconnect( const char *reason ); - virtual void handleRoomMessage( const char *nick, const char *message, MessageType messageType ); - virtual void handlePlayerMessage( const char *nick, const char *message, MessageType messageType ); - virtual void handlePlayerJoined( const char *nick ); - virtual void handlePlayerLeft( const char *nick ); - virtual void handlePlayerChangedNick( const char *oldNick, const char *newNick ); - virtual void handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags ); - -private: -} -; - -#endif /* __GENERALS_H__ */ - diff --git a/Generals/Code/Tools/matchbot/global.cpp b/Generals/Code/Tools/matchbot/global.cpp deleted file mode 100644 index 232cee6310d..00000000000 --- a/Generals/Code/Tools/matchbot/global.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include // for FILE ops -#include "global.h" - -GlobalClass Global; - -GlobalClass::GlobalClass(void) -{} - -bool GlobalClass::ReadFile(const char *fname) -{ - FILE *fp; - if ((fp = fopen(fname, "r")) == NULL) - return false; - config.readFile(fp); - fclose(fp); - - return true; -} - -bool GlobalClass::GetString(const Wstring& key, Wstring& val) -{ - val = ""; - config.getString(key, val, "STRINGS"); - if (val == "") - { - val.setFormatted("MISSING:%s", key.get()); - return false; - } - - return true; - -} - diff --git a/Generals/Code/Tools/matchbot/global.h b/Generals/Code/Tools/matchbot/global.h deleted file mode 100644 index 5dc7856dca3..00000000000 --- a/Generals/Code/Tools/matchbot/global.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef __GLOBAL_H__ -#define __GLOBAL_H__ - -#ifdef _WIN32 -#include -#ifdef IN -#undef IN -#endif -#define IN const -#endif -#include -#include -#include -#include -#include -#include "matcher.h" -#include "rand.h" - -class GlobalClass -{ -public: - GlobalClass(); - - ConfigFile config; - bool ReadFile(const char *fname); - - bool GetString(const Wstring& key, Wstring& val); - - RandClass rnd; -}; - -extern GlobalClass Global; - -// Log rotation functions -void rotateOutput(void); -void rotateParanoid(void); - -#endif - diff --git a/Generals/Code/Tools/matchbot/main.cpp b/Generals/Code/Tools/matchbot/main.cpp deleted file mode 100644 index 0290ac55be2..00000000000 --- a/Generals/Code/Tools/matchbot/main.cpp +++ /dev/null @@ -1,409 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifdef _WIN32 -#include -#endif -#include -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -//#define THREADSAFE_HEADER - -#include -#include -#include -#include -#include -#include -#include "global.h" -#include "generals.h" -#include "timezone.h" -#include - -#include -#include "mydebug.h" - -#ifdef _UNIX -using namespace std; -#else -#define sleep(x) Sleep(1000 * (x)) -#endif - -static const char *Program_Usage = "A config filename can be given on the command line (default=matchbot.cfg)\n"; -void logMonitor(void *); -void paranoidLogMonitor(void *); - -OutputDevice * output_device = NULL; -OutputDevice * paranoid_output_device = NULL; - - -void Signal_Quit(int) -{ - INFMSG("Exiting due to signal."); - exit(2); -} - -void Setup_Signals(void) -{ -#ifdef _UNIX - struct sigaction act, oact; - act.sa_handler = Signal_Quit; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - sigaction(SIGTERM, &act, &oact); - sigaction(SIGINT, &act, &oact); -#endif -} - -int VerifyFileDescriptors(int requested) -{ -#ifdef _UNIX - struct rlimit limit; - if (!getrlimit(_SC_OPEN_MAX, &limit)) - { - INFMSG("Hard limit on file descriptors: " << limit.rlim_max); - if (limit.rlim_max < (unsigned int)requested) - { - ERRMSG("Too many file descriptors requested"); - ERRMSG("Hard Limit: " << limit.rlim_max << ", requested: " << requested); - requested = limit.rlim_max; - } - - limit.rlim_cur = limit.rlim_max; /* make soft limit the max */ - if (setrlimit(_SC_OPEN_MAX, &limit) == -1) - { - ERRMSG("Error setting max file descriptors to " << limit.rlim_cur); - exit(-1); - } - INFMSG("Soft limit on file descriptors: " << limit.rlim_cur); - } - else - { - ERRMSG("Couldn't get limit for _SC_OPEN_MAX"); - exit(-1); - } -#endif - return requested; -} - - - -GeneralsMatcher *s_generalsMatcher = NULL; -GeneralsClientMatcher *s_generalsClientMatcher = NULL; - -int main(int argc, char ** argv) -{ - Wstring config_fname = "matchbot.cfg"; - - // You can specify the config file on the command line - if (argc == 2) - config_fname = argv[1]; - - // Read the config file - FILE *fp; - if ((fp = fopen(config_fname.get(), "r")) == NULL) - { - cerr << "\nCan't open the config file '" << config_fname.get() << "'\n\n"; - cerr << Program_Usage << endl; - exit( -1); - } - fclose(fp); - - Global.ReadFile(config_fname.get()); - - // Setup debugging & logging output - Wstring output_file; - output_file.set("matchbot.log"); - Global.config.getString("OUTPUTFILE", output_file); - if (output_file != "STDOUT") - { - int append = 1; - Global.config.getInt("APPENDTOLOG", append); - if (append) - output_device = new FileD(output_file.get(), "a"); - else - output_device = new FileD(output_file.get(), "w"); - if (!output_device) - { - cerr << "Could not open " << output_file.get() << " for writing!" << endl; - exit( -1); - } - } - else - { - output_device = new StdoutD; - } - MsgManager::setAllStreams(output_device); - DBGMSG("Matching bot started!"); - INFMSG("Matching bot " << argv[0] << " started!"); - - // Setup logging of suspicious activity - Wstring paranoid_output_file; - paranoid_output_file.set("hacks.log"); - Global.config.getString("PARANOIDFILE", paranoid_output_file); - if (paranoid_output_file != "STDOUT") - { - paranoid_output_device = new FileD(paranoid_output_file.get(), "a"); - if (!paranoid_output_device) - { - cerr << "Could not open " << paranoid_output_file.get() << " for writing!" << endl; - exit( -1); - } - } - else - { - paranoid_output_device = new StdoutD; - } - MyMsgManager::setParanoidStream(paranoid_output_device); - DBGMSG("Hack log started!"); - PARANOIDMSG("Hack log started!"); - - Setup_Signals(); - -#ifdef _WINDOWS - // ----- Initialize Winsock ----- - WORD verReq = MAKEWORD(2, 2); - WSADATA wsadata; - - int err = WSAStartup(verReq, &wsadata); - if (err != 0) - { - ERRMSG("Winsock Init failed."); - return 1; - } - - if ((LOBYTE(wsadata.wVersion) != 2) || (HIBYTE(wsadata.wVersion) !=2)) - { - ERRMSG("Winsock DLL is not 2.2"); - WSACleanup(); - ERRMSG("Winsock Init failed."); - return 1; - } - INFMSG("Winsock Init done."); -#endif - - // Check game type & start matcher - Wstring gametype = "unknown"; - Global.config.getString("GAME", gametype); - - // Command-line override for gamemode. - // This is for test suites, so they can use - // the same config file as the corresponding - // matchbot. - const char *s = argv[0] + strlen(argv[0]); - while (s > argv[0] && *s != '/') - --s; - if (*s == '/') - ++s; - Wstring exe = s; - exe.toLower(); - DBGMSG("Executable file is [" << exe.get() << "]"); - - if (gametype == "Generals") - { - DBGMSG("Generals matching behavior"); - s_generalsMatcher = new GeneralsMatcher; - s_generalsMatcher->connectAndLoop(); - } - else if (gametype == "GeneralsClient") - { - DBGMSG("Generals TEST client matching behavior"); - s_generalsClientMatcher = new GeneralsClientMatcher; - s_generalsClientMatcher->connectAndLoop(); - } - else - { - cerr << "\nNo valid GAME entry found!" << endl; - exit( -1); - } - - if (s_generalsMatcher) - delete s_generalsMatcher; - if (s_generalsClientMatcher) - delete s_generalsClientMatcher; - - return 0; -} - -/*----------------------------------------------------------------------+ -| THREAD: logMonitor | -| This thread is spawned once per execution. It will activate after | -| midnight and create a new log file. The old one gets put into the | -| logfiles directory. | -`----------------------------------------------------------------------*/ -void logMonitor(void *) -{ -#ifdef _UNIX - Xtime xtime; - time_t curtime; - //char timebuf[40]; - char filenamebuf[128]; - int delay = -1; - Global.config.getInt("ROTATEDELAY", delay); - DBGMSG("ROTATEDELAY: " << delay); - if (delay == -1) - return ; - while (1) - { - curtime = time(NULL); - // get the number of seconds that have passed since midnight - // of the current day. - curtime -= TimezoneOffset(); - time_t timeofday = curtime % (delay); - if ((timeofday > 0) && (timeofday <= 300)) - { - // We're within 5 minutes of midnight, switch the files. - - DBGMSG("about to switch."); - Wstring logfilename = "matchbot.log"; - Global.config.getString("OUTPUTFILE", logfilename); - Wstring newfilename = "tmp.log"; - Global.config.getString("ROTATEFILE", newfilename); - MsgManager::ReplaceAllStreams((FileD*)output_device, logfilename.get(), newfilename.get()); - Wstring logpath = "logs"; - Global.config.getString("LOGPATH", logpath); - xtime.update(); - sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(), xtime.getMonth(), - xtime.getMDay(), xtime.getYear(), xtime.getHour(), xtime.getMinute(), xtime.getSecond()); - rename(newfilename.get(), filenamebuf); - DBGMSG("Normal: Just been switched. " << logfilename.get() << ", " << newfilename.get()); - sleep(60*60*23); // sleep the next 23 hours - } - sleep(300); - } -#endif -} - -void rotateOutput(void) -{ - Xtime xtime; - char filenamebuf[128]; - Wstring logfilename = "matchbot.log"; - Wstring newfilename = "tmp.log"; - Wstring logpath = "logs"; - - DBGMSG("About to switch."); - - Global.config.getString("OUTPUTFILE", logfilename); - Global.config.getString("ROTATEFILE", newfilename); - Global.config.getString("LOGPATH", logpath); - - // This grabs the semaphore, renames the file, and switches the output device - MsgManager::ReplaceAllStreams((FileD*)output_device, logfilename.get(), - newfilename.get()); - - // clean up the tmp filename and move it to the log dir. - sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(), - xtime.getMonth(), xtime.getMDay(), xtime.getYear(), xtime.getHour(), - xtime.getMinute(), xtime.getSecond()); -#ifdef _WINDOWS - mkdir(logpath.get()); -#else - mkdir(logpath.get(), 00666); -#endif - rename(newfilename.get(), filenamebuf); - - DBGMSG("Normal: Just been switched. " << logfilename.get() << ", " << - newfilename.get()); -} - -void paranoidLogMonitor(void *) -{ -#ifdef _UNIX - Xtime xtime; - time_t curtime; - //char timebuf[40]; - char filenamebuf[128]; - int delay = -1; - Global.config.getInt("ROTATEDELAY", delay); - PARANOIDMSG("ROTATEDELAY: " << delay); - if (delay == -1) - return ; - while (1) - { - curtime = time(NULL); - // get the number of seconds that have passed since midnight - // of the current day. - curtime -= TimezoneOffset(); - time_t timeofday = curtime % (delay); - if ((timeofday > 0) && (timeofday <= 300)) - { - // We're within 5 minutes of midnight, switch the files. - - PARANOIDMSG("about to switch."); - Wstring logfilename = "matchbot.log"; - Global.config.getString("PARANOIDFILE", logfilename); - Wstring newfilename = "tmp.log"; - Global.config.getString("ROTATEPARANOIDFILE", newfilename); - MyMsgManager::ReplaceAllStreams((FileD*)paranoid_output_device, logfilename.get(), newfilename.get()); - Wstring logpath = "logs"; - Global.config.getString("PARANOIDLOGPATH", logpath); - xtime.update(); - sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(), xtime.getMonth(), - xtime.getMDay(), xtime.getYear(), xtime.getHour(), xtime.getMinute(), xtime.getSecond()); - rename(newfilename.get(), filenamebuf); - PARANOIDMSG("Paranoid: Just been switched. " << logfilename.get() << ", " << newfilename.get()); - sleep(60*60*23); // sleep the next 23 hours - } - sleep(300); - } -#endif -} - -void rotateParanoid(void) -{ - Xtime xtime; - char filenamebuf[128]; - Wstring logfilename = "matchbot.log"; - Wstring newfilename = "tmp.log"; - Wstring logpath = "logs"; - - PARANOIDMSG("About to switch."); - - Global.config.getString("PARANOIDFILE", logfilename); - Global.config.getString("ROTATEPARANOIDFILE", newfilename); - Global.config.getString("PARANOIDLOGPATH", logpath); - - // This grabs the semaphore, renames the file, and switches the output device - MyMsgManager::ReplaceAllStreams((FileD*)output_device, logfilename.get(), - newfilename.get()); - - // clean up the tmp filename and move it to the log dir. - sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(), - xtime.getMonth(), xtime.getMDay(), xtime.getYear(), xtime.getHour(), - xtime.getMinute(), xtime.getSecond()); -#ifdef _WINDOWS - mkdir(logpath.get()); -#else - mkdir(logpath.get(), 00666); -#endif - rename(newfilename.get(), filenamebuf); - - PARANOIDMSG("Paranoid: Just been switched. " << logfilename.get() << ", " << - newfilename.get()); -} - - diff --git a/Generals/Code/Tools/matchbot/matcher.cpp b/Generals/Code/Tools/matchbot/matcher.cpp deleted file mode 100644 index 2b27968ad96..00000000000 --- a/Generals/Code/Tools/matchbot/matcher.cpp +++ /dev/null @@ -1,421 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include "global.h" -#include "matcher.h" -#include "encrypt.h" -#include "timezone.h" -#include "debug.h" - -#ifdef _WINDOWS -#define usleep(x) Sleep((x)/100000) -#endif - -MatcherClass::MatcherClass(void) -{ - m_lastRotation = 0; - m_baseNick = "matcher"; - m_joinSuccess = false; - done = 0; - - m_rotateLogs = false; - quiet = false; -} - -Wstring MatcherClass::getString(const Wstring& key) -{ - Wstring res; - Global.GetString(key, res); - return res; -} - -void logIt(const char *Txt) -{ - // intentionally crash if we can't open it - FILE *fp = fopen("logIt.txt", "w"); - fputs(Txt, fp); - fclose(fp); -} - -void MatcherClass::readLoop(void) -{ - int delay = -1; - Global.config.getInt("ROTATEDELAY", delay); - DBGMSG("ROTATEDELAY: " << delay); - - do - { - static time_t lastLogTime = 0; - time_t now = time(NULL); - if (now > lastLogTime + 300) - { - lastLogTime = now; - INFMSG("still here" << endl); - } - - logIt("peerThink\n"); - peerThink(m_peer); - - logIt("peerIsConnected\n"); - if (peerIsConnected(m_peer)) - { - logIt("checkMatches()\n"); - checkMatches(); - logIt("checkMatches() done\n"); - } - else - done = true; - - msleep(1); - - // rotate logs if it's time - if (delay != -1) - { -#ifdef _UNIX - Xtime xtime; - time_t curtime; - curtime = time(NULL); - // get the number of seconds that have passed since midnight - // of the current day. - curtime -= TimezoneOffset(); - time_t timeofday = curtime % (delay); - if ((timeofday > 0) && (timeofday <= 300)) - { - rotateOutput(); - rotateParanoid(); - } -#endif - } - } - while (!done); - DBGMSG("Bailing out of readLoop!" << endl); - INFMSG("Bailing out of readLoop!" << endl); - ERRMSG("Bailing out of readLoop!" << endl); -} - -///////////////////////////////////////////////////////////////////////////// - -static void DisconnectedCallback ( PEER peer, const char * reason, void * param) -{ - DBGMSG("Disconnected: " << reason); - MatcherClass *matcher = (MatcherClass *)param; - if (matcher) - matcher->handleDisconnect( reason ); -} - -static void RoomMessageCallback ( PEER peer, RoomType roomType, const char * nick, const char * message, MessageType messageType, void * param) -{ - DBGMSG("(PUBLIC) " << nick << ": " << message); - MatcherClass *matcher = (MatcherClass *)param; - if (matcher) - matcher->handleRoomMessage( nick, message, messageType ); -} - -static void PlayerMessageCallback ( PEER peer, const char * nick, const char * message, MessageType messageType, void * param) -{ - DBGMSG("(PRIVATE) " << nick << ": " << message); - MatcherClass *matcher = (MatcherClass *)param; - if (matcher) - matcher->handlePlayerMessage( nick, message, messageType ); -} - -static void PlayerJoinedCallback ( PEER peer, RoomType roomType, const char * nick, void * param) -{ - DBGMSG(nick << " joined the room"); - MatcherClass *matcher = (MatcherClass *)param; - if (matcher) - matcher->handlePlayerJoined( nick ); -} - -static void PlayerLeftCallback ( PEER peer, RoomType roomType, const char * nick, const char * reason, void * param) -{ - DBGMSG(nick << " left the room"); - MatcherClass *matcher = (MatcherClass *)param; - if (matcher) - matcher->handlePlayerLeft( nick ); -} - -static void PlayerChangedNickCallback ( PEER peer, RoomType roomType, const char * oldNick, const char * newNick, void * param) -{ - INFMSG(oldNick << " changed nicks to " << newNick); - MatcherClass *matcher = (MatcherClass *)param; - if (matcher) - matcher->handlePlayerChangedNick( oldNick, newNick ); -} - -static void EnumPlayersCallback ( PEER peer, PEERBool success, RoomType roomType, int index, const char * nick, int flags, void * param) -{ - MatcherClass *matcher = (MatcherClass *)param; - if (matcher) - matcher->handlePlayerEnum( success == PEERTrue, index, nick, flags); -} - -static int s_groupID = 0; -static void ListGroupRoomsCallback ( PEER peer, PEERBool success, int groupID, SBServer server, const char * name, int numWaiting, int maxWaiting, int numGames, int numPlaying, void * param) -{ - if (success && name && !strcasecmp(name, "QuickMatch")) - { - s_groupID = groupID; - } -} - -static void ConnectCallback ( PEER peer, PEERBool success, int failureReason, void * param ) -{ - MatcherClass *matcher = (MatcherClass *)param; - if (matcher) - matcher->handleConnect( success == PEERTrue ); -} - -static void JoinCallback ( PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void * param) -{ - MatcherClass *matcher = (MatcherClass *)param; - if (matcher) - matcher->handleJoin( success == PEERTrue ); -} - -static void NickErrorCallback ( PEER peer, int type, const char * badNick, int numSuggestedNicks, const const char ** suggestedNicks, void * param) -{ - ERRMSG("Nick error with " << badNick); - - if(type == PEER_IN_USE) - { - int len = strlen(badNick); - std::string nickStr = badNick; - int newVal = 0; - if (badNick[len-1] == '}' && badNick[len-3] == '{' && isdigit(badNick[len-2])) - { - newVal = badNick[len-2] - '0' + 1; - nickStr.erase(len-3, 3); - } - - nickStr.append("{"); - char tmp[2]; - tmp[0] = '0'+newVal; - tmp[1] = '\0'; - nickStr.append(tmp); - nickStr.append("}"); - DBGMSG("Nickname taken: was "<= 1); - } -} - -void MatcherClass::connectAndLoop(void) -{ - // Game-specific initializations, if neccessary - init(); - - // Check for possible quit from init()-based self-tests - if (done) - return ; - - // Defaults. - //////////// - Wstring title = "gmtest"; - Wstring secretKey = "HA6zkS"; - Wstring serialNo = ""; - m_profileID = 0; - Global.config.getString("Nick", m_baseNick, "LOGIN"); - DBGMSG("base nick is " << m_baseNick.get()); - m_baseNick.toLower(); - Global.config.getString("Title", title, "LOGIN"); - Global.config.getString("SecretKey", secretKey, "LOGIN"); - Global.config.getInt("ProfileID", m_profileID, "LOGIN"); - Global.config.getString("CDKey", serialNo, "LOGIN"); - PEERCallbacks callbacks; - PEERBool pingRooms[NumRooms]; - PEERBool crossPingRooms[NumRooms]; - - // Setup the callbacks. - /////////////////////// - memset(&callbacks, 0, sizeof(PEERCallbacks)); - callbacks.disconnected = DisconnectedCallback; - callbacks.playerChangedNick = PlayerChangedNickCallback; - callbacks.playerJoined = PlayerJoinedCallback; - callbacks.playerLeft = PlayerLeftCallback; - callbacks.roomMessage = RoomMessageCallback; - callbacks.playerMessage = PlayerMessageCallback; - callbacks.param = this; - - // Init. - //////// - m_peer = peerInitialize(&callbacks); - if(!m_peer) - { - ERRMSG("Failed to init peer object" << endl); - return; - } - - // Ping/cross-ping in no room. - ///////////////////////////////// - pingRooms[TitleRoom] = PEERFalse; - pingRooms[GroupRoom] = PEERFalse; - pingRooms[StagingRoom] = PEERFalse; - crossPingRooms[TitleRoom] = PEERFalse; - crossPingRooms[GroupRoom] = PEERFalse; - crossPingRooms[StagingRoom] = PEERFalse; - - // Set the title. - ///////////////// - if(!peerSetTitle(m_peer, title.get(), secretKey.get(), title.get(), secretKey.get(), 0, 30, PEERTrue, pingRooms, crossPingRooms)) - { - peerShutdown(m_peer); - ERRMSG("Failed to set the title" << endl); - return; - } - - // Connect. - /////////// - m_connectSuccess = false; - m_nick = m_baseNick.get(); - peerConnect(m_peer, m_baseNick.get(), m_profileID, NickErrorCallback, ConnectCallback, this, PEERTrue); - if(!m_connectSuccess) - { - peerShutdown(m_peer); - ERRMSG("Failed to connect" << endl); - return; - } - - bool cdOk = false; - peerAuthenticateCDKey(m_peer, serialNo.get(), AuthenticateCDKeyCallback, &cdOk, PEERTrue); - if (!cdOk) - { - peerShutdown(m_peer); - ERRMSG("Failed to auth CDKey " << serialNo.get() << endl); - return; - } - - m_groupID = 0; - peerListGroupRooms(m_peer, NULL, ListGroupRoomsCallback, &m_groupID, PEERTrue); - m_groupID = s_groupID; - - DBGMSG("QuickMatch room is " << m_groupID); - - // Join the title room. - /////////////////////// - peerJoinGroupRoom(m_peer, m_groupID, JoinCallback, this, PEERTrue); - if(!m_joinSuccess) - { - peerDisconnect(m_peer); - peerShutdown(m_peer); - ERRMSG("Failed to join the title room" << endl); - return; - } - - // Connected, so lets do our thing - readLoop(); - - peerDisconnect(m_peer); - peerShutdown(m_peer); -} - diff --git a/Generals/Code/Tools/matchbot/matcher.h b/Generals/Code/Tools/matchbot/matcher.h deleted file mode 100644 index 398ca33d6c2..00000000000 --- a/Generals/Code/Tools/matchbot/matcher.h +++ /dev/null @@ -1,93 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef __MATCHER_H__ -#define __MATCHER_H__ - -#ifdef _WIN32 -#include -#endif -#include -#include -#include -#include -#include -#include - -#include - -#include - -class MatcherClass -{ -public: - MatcherClass(); - virtual ~MatcherClass() - {} - - virtual void init(void) - {} - - virtual void checkMatches(void) - {} - - virtual void handleDisconnect( const char *reason ) - {} - virtual void handleRoomMessage( const char *nick, const char *message, MessageType messageType ) - {} - virtual void handlePlayerMessage( const char *nick, const char *message, MessageType messageType ) - {} - virtual void handlePlayerJoined( const char *nick ) - {} - virtual void handlePlayerLeft( const char *nick ) - {} - virtual void handlePlayerChangedNick( const char *oldNick, const char *newNick ) - {} - virtual void handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags ) - {} - - void handleConnect( bool success ); - void handleGroupRoomList( bool success, int groupID, const char *name ); - void handleJoin( bool success ); - void handleNickError( const char *badNick ); - - void connectAndLoop( void ); - -protected: - - Wstring getString(const Wstring& key); - - Wstring m_baseNick; - std::string m_nick; - int m_profileID; - PEER m_peer; - bool m_connectSuccess; - bool m_joinSuccess; - void readLoop( void ); - - int done; // 0=no, neg=quit;error, pos=quit;success - bool quiet; - int m_groupID; - - bool m_rotateLogs; // check for log rotation in this thread? - time_t m_lastRotation; - -}; - -#endif /* __MATCHER_H__ */ - diff --git a/Generals/Code/Tools/matchbot/mydebug.cpp b/Generals/Code/Tools/matchbot/mydebug.cpp deleted file mode 100644 index 4090f27125a..00000000000 --- a/Generals/Code/Tools/matchbot/mydebug.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include -#include "mydebug.h" -#include "streamer.h" -#include "odevice.h" - - -// static MyMsgManager *msg_manager=NULL; - -// static int paranoid_enabled=0; -static ostream *paranoid_ostream=NULL; -static Streamer paranoid_streamer; - -// Don't dare touch this semaphore in application code! -#ifdef USE_SEM -Sem4 MyDebugLibSemaphore; -#else -CritSec MyDebugLibSemaphore; -#endif - - -int MyMsgManager::setAllStreams(OutputDevice *device) -{ - if (device==NULL) - return(1); - - MYDEBUGLOCK; - paranoid_streamer.setOutputDevice(device); - delete(paranoid_ostream); - paranoid_ostream=new ostream(¶noid_streamer); - - MYDEBUGUNLOCK; - - return(0); -} - - -int MyMsgManager::setParanoidStream(OutputDevice *device) -{ - if (device==NULL) - return(1); - - MYDEBUGLOCK; - paranoid_streamer.setOutputDevice(device); - delete(paranoid_ostream); - paranoid_ostream=new ostream(¶noid_streamer); - MYDEBUGUNLOCK; - - return(0); -} - - - - -ostream *MyMsgManager::paranoidStream(void) -{ - return(paranoid_ostream); -} - - -int MyMsgManager::ReplaceAllStreams(FileD * output_device, const char *device_filename, const char *copy_filename) -{ - MYDEBUGLOCK; - - delete(paranoid_ostream); - - if (output_device != NULL) - { - delete(output_device); - output_device = NULL; - } - - rename(device_filename, copy_filename); - - // FileD new_device(device_filename); - output_device = new FileD(device_filename); - - paranoid_streamer.setOutputDevice(output_device); - paranoid_ostream = new ostream(¶noid_streamer); - - MYDEBUGUNLOCK; - - return(0); -} diff --git a/Generals/Code/Tools/matchbot/mydebug.h b/Generals/Code/Tools/matchbot/mydebug.h deleted file mode 100644 index da38b12fb89..00000000000 --- a/Generals/Code/Tools/matchbot/mydebug.h +++ /dev/null @@ -1,134 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/*****************************************************************************\ -wdebug Neal Kettler - -MT-LEVEL - MT-Safe - -The debugging module is pretty good for debugging and it has some message -printing stuff as well. The basic idea is that you write a class that -inherits from OutputDevice (several are provided) and assign that output -device to a stream. There are seperate streams for debugging, information, -warning, and error messages. Each one can have a seperate output device, -or they can all have the same one. Debugging messages only get compiled -in if your module defines 'DEBUG'. If you don't define debug, then not even -the text of the debugging message gets into the binary. All the other -output streams get printed regardless of whether DEBUG is defined. - -Sample usage: -FileD debug_device("gameres.debug"); // create a file device -MsgManager::setDebugStream(&debug_device); -DBGMSG("This debug message #" << 1 << " you use C++ streams"); - -Note that since these are defines you really don't need to put a semicolon -at the end, and it can be bad in situations like this: - -if (x) - DBGMSG("Stuff is broken"); -else - DBGMSG("Stuff is NOT broken"); - -This won't compile, read the code until you figure it out. Only then -will you be ready to leave grasshopper. - -\*****************************************************************************/ - -#ifndef MYDEBUG_HEADER -#define MYDEBUG_HEADER - -#define USE_SEM - -#include "wstypes.h" - -#include -#include - -#ifdef USE_SEM -#include "sem4.h" -#else -#include "critsec.h" -#endif -#include "odevice.h" -#include "streamer.h" -#include "xtime.h" -#include "timezone.h" // MDC -#include "filed.h" - -// This is needed because the streams return a pointer. Every time you -// change the output device the old stream is deleted, and a new one -// is created. -// MDC: Changed from semaphores to critical sections because Windows doesn't provide -// a good way of doing semaphores (I think) -// MDC: Never mind, they seem to be working now! I'm leaving it in, though, so anyone can -// flip a switch between the two. - -#ifdef USE_SEM -extern Sem4 MyDebugLibSemaphore; -#define MYDEBUGLOCK MyDebugLibSemaphore.Wait() -#define MYDEBUGUNLOCK MyDebugLibSemaphore.Post() -#else -extern CritSec MyDebugLibSemaphore; -#define MYDEBUGLOCK MyDebugLibSemaphore.lock() -#define MYDEBUGUNLOCK MyDebugLibSemaphore.unlock() -#endif - -// Print an information message -#define PARANOIDMSG(X)\ -{\ -char timebuf[40]; \ -Xtime now; \ -now -= TimezoneOffset(); \ -now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \ -MYDEBUGLOCK; \ -if (MyMsgManager::paranoidStream()) \ -(*(MyMsgManager::paranoidStream())) << "HACK " << timebuf << " [" << \ -__FILE__ << " " << __LINE__ << "] " << X << endl; \ -MYDEBUGUNLOCK; \ -} - -// Just get a stream to the information device, no extra junk -#define PARANOIDSTREAM(X)\ -{\ -MYDEBUGLOCK; \ -if (MyMsgManager::paranoidStream()) \ -(*(MyMsgManager::paranoidStream())) << X;\ -MYDEBUGUNLOCK; \ -} - - -//#undef MYDEBUGLOCK -//#undef MYDEBUGUNLOCK - -class MyMsgManager -{ -protected: - MyMsgManager(); - -public: - static int setAllStreams(OutputDevice *device); - static int setParanoidStream(OutputDevice *device); - static int ReplaceAllStreams(FileD *output_device, const char *device_filename, const char *copy_filename); - - static void enableParanoid(int flag); - - static ostream *paranoidStream(void); -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/rand.cpp b/Generals/Code/Tools/matchbot/rand.cpp deleted file mode 100644 index 87a64698162..00000000000 --- a/Generals/Code/Tools/matchbot/rand.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include "rand.h" -#include - -static const double theMultFactor = 1.0 / (pow(2, 8 * sizeof(unsigned int)) - 1.0); - -RandClass::RandClass(int start) -{ - seed[0] = 0xf22d0e56L; - seed[1] = 0x883126e9L; - seed[2] = 0xc624dd2fL; - seed[3] = 0x702c49cL; - seed[4] = 0x9e353f7dL; - seed[5] = 0x6fdf3b64L; - - unsigned int ax; - - ax = start; /* mov eax,SEED */ - ax += 0xf22d0e56; /* add eax,0f22d0e56h */ - seed[0] = ax; /* mov seed,eax */ - ax += 0x883126e9 - 0xf22d0e56; /* add eax,0883126e9h-0f22d0e56h */ - seed[1] = ax; /* mov seed+4,eax */ - ax += 0xc624dd2f - 0x883126e9; /* add eax,0c624dd2fh-0883126e9h */ - seed[2] = ax; /* mov seed+8,eax */ - ax += 0x0702c49c - 0xc624dd2f; /* add eax,00702c49ch-0c624dd2fh */ - seed[3] = ax; /* mov seed+12,eax */ - ax += 0x9e353f7d - 0x0702c49c; /* add eax,09e353f7dh-00702c49ch */ - seed[4] = ax; /* mov seed+16,eax */ - ax += 0x6fdf3b64 - 0x9e353f7d; /* add eax,06fdf3b64h-09e353f7dh */ - seed[5] = ax; /* mov seed+20,eax */ -} - -// Add with carry. SUM is replaced with A + B + C, C is replaced with 1 if there was a carry, 0 if there wasn't. A carry occurred if the sum is less than one of the inputs. This is addition, so carry can never be more than one. -#define ADC(SUM, A, B, C) SUM = (A) + (B) + (C); C = ((SUM < (A)) || (SUM < (B))) - -unsigned int RandClass::randomValue( void ) -{ - unsigned int ax; - unsigned int c = 0; - - - ADC(ax, seed[5], seed[4], c); /* mov ax,seed+20 */ - /* add ax,seed+16 */ - seed[4] = ax; /* mov seed+8,ax */ - - ADC(ax, ax, seed[3], c); /* adc ax,seed+12 */ - seed[3] = ax; /* mov seed+12,ax */ - - ADC(ax, ax, seed[2], c); /* adc ax,seed+8 */ - seed[2] = ax; /* mov seed+8,ax */ - - ADC(ax, ax, seed[1], c); /* adc ax,seed+4 */ - seed[1] = ax; /* mov seed+4,ax */ - - ADC(ax, ax, seed[0], c); /* adc ax,seed+0 */ - seed[0] = ax; /* mov seed+0,ax */ - - /* Increment seed array, bubbling up the carries. */ - if (!++seed[5]) - { - if (!++seed[4]) - { - if (!++seed[3]) - { - if (!++seed[2]) - { - if (!++seed[1]) - { - ++seed[0]; - ++ax; - } - } - } - } - } - return(ax); -} - - -int RandClass::Int(void) -{ - return (int)randomValue(); -} - -int RandClass::Int(int low, int high) -{ - unsigned int delta = high - low + 1; - int rval; - - if (delta == 0) - return high; - - rval = ((int)(randomValue()%delta)) + low; - - return rval; -} - -double RandClass::Double(void) -{ - return Double(0.0, 1.0); -} - -double RandClass::Double(double low, double high) -{ - double delta = high - low; - double rval; - - if (delta <= 0.0) - return high; - - rval = ((double)(randomValue()) * theMultFactor) * delta + low; - return rval; -} - - diff --git a/Generals/Code/Tools/matchbot/rand.h b/Generals/Code/Tools/matchbot/rand.h deleted file mode 100644 index d5a1f4d6912..00000000000 --- a/Generals/Code/Tools/matchbot/rand.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef __RAND_H__ -#define __RAND_H__ - -#include - -class RandClass -{ -public: - RandClass(int start = 0); - ~RandClass() - {} - - - int Int(void); - double Double(void); - int Int(int low, int high); - double Double(double low, double high); - -private: - - unsigned int randomValue( void ); - unsigned int seed[6]; - -}; - -#endif /* __RAND_H__ */ - diff --git a/Generals/Code/Tools/matchbot/wlib/arraylist.h b/Generals/Code/Tools/matchbot/wlib/arraylist.h deleted file mode 100644 index efcd3abd650..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/arraylist.h +++ /dev/null @@ -1,709 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * -****************************************************************************** -Project Name: -File Name : arraylist.h -Author : Neal Kettler -Start Date : Jan 19, 1997 -Last Update : Jan 19, 1997 ------------------------------------------------------------------------------- - -Array implementation of a list. Note: There are some freaky C++ memory tricks -going on here. If you think there's a leak, see me before changing it. -The way this works is that it allocates an array to hold 'N' items on the -first list add. It doesn't call the constructors for those 'N' items until -necessary (for efficiency). When an item is added to a slot then a new -class is constructed inside the array element using the placement new operator -and the class's copy constructor. When elements are removed the destructor -is then manually called on this memory location. - -All data added to the list is copied so feel free to delete/destroy/modify -the original after an add. - -You _must_ have a good copy constructor for any classes that you use this template -for! A good copy constructor is one that won't blindly duplicate pointers -that don't belong to them, etc... - -\****************************************************************************/ - -#ifndef ARRAYLIST_HEADER -#define ARRAYLIST_HEADER - -#include -#include -#include -#include -#include -#include - -#include "wstypes.h" - -// -// Usage: ArrayList TheList; -// -template -class ArrayList -{ - public: - ArrayList(); - ArrayList(ArrayList &other); - ~ArrayList(); - - // Remove all entries from the lsit - void clear(void); - - // Add a node after the zero based 'pos' - bit8 add(IN T &node,sint32 pos); - bit8 addTail(IN T &node); - bit8 addHead(IN T &node); - bit8 addSortedAsc(IN T &node); // Ascending - bit8 addSortedDes(IN T &node); // Descending - /*bit8 addNumSortedAsc(IN T &node); // Ascending - bit8 addNumSortedDes(IN T &node); // Descending*/ - - // Remove a node - bit8 remove(OUT T &node,sint32 pos); - bit8 remove(sint32 pos); - bit8 removeHead(OUT T &node); - bit8 removeTail(OUT T &node); - - // Replace one obj with another - bit8 replace(IN T &node, sint32 pos); - - - // Get a node without removing from the list - bit8 get(OUT T &node,sint32 pos) RO; - bit8 getHead(OUT T &node) RO; - bit8 getTail(OUT T &node) RO; - - // Get a pointer to the interally managed copy (careful!) - bit8 getPointer(OUT T **node,sint32 pos) RO; - - // Get the number of entries in the list - sint32 length(void) RO; - - // UNSAFE! for classes, see note below! - bit8 setSize(sint32 newsize, IN T &filler); - - // Print information on the list - void print(FILE *out); - - // assignment operator - ArrayList &operator=(IN ArrayList &other); - - private: - sint32 _sortedLookup(IN T &target, int ascending); - sint32 Entries_; // Number of entries - sint32 Slots_; // Number of available slots - - T *Vector_; // The actual memory where the list is held - - enum - { - INITIAL_SIZE = 10 - }; - - bit8 growVector(void); // Expand the number of slots - bit8 shrinkVector(void); // Reduce the number of slots -}; - - -//Create the empty list -template -ArrayList::ArrayList() -{ - Entries_=0; - Slots_=0; - Vector_=NULL; -} - -// copy constructor -template -ArrayList::ArrayList(ArrayList &other) -{ - Entries_=0; - Slots_=0; - Vector_=NULL; - (*this)=other; -} - -//Free all the memory... -template -ArrayList::~ArrayList() -{ - clear(); // Remove the entries & call destructors on them - - delete[]((uint8*)Vector_); // this will prevent the destructors from - // gettting called on elements not - // containing valid objects. - - //fprintf(stderr,"Arraylist destructor\n"); -} - -// assignment operator -template -ArrayList &ArrayList::operator=(IN ArrayList &other) -{ - T node; - clear(); - for (int i=0; i -void ArrayList::clear() -{ - for (int i=0; i~T(); // Call the destructor manually. Don't try this - // at home kiddies! - } - Entries_=0; -} - -// ************************* UNSAFE UNSAFE UNSAFE ************************* -// Note - Don't call this on any type with a constructor/destructor since this -// is really dumb and just puts a new one of filler in. Well, it's kindof safe -// just be careful. -// It's fine for simple classes like ints though.. -// -// Add/remove entries in a stupid manner... -// -// ************************************************************************** -template -bit8 ArrayList::setSize(sint32 newsize, IN T &filler) -{ - int oldEntries=Entries_; - Entries_ = newsize; - - if (newsize<0) - return(false); - - // Grow the vector as much as we need to - while (newsize > Slots_) - growVector(); - - // Create new objects in the blank holes - for (int i=oldEntries; i -bit8 ArrayList::add(IN T &node,sint32 pos) -{ - if (pos > Entries_) // You can only access one of the end of the vector - pos=Entries_; - if (pos >= Slots_) // If we're at the end, grow the list - growVector(); - if (Entries_ >= Slots_) // enuff space? - growVector(); - - // If we are insering into the middle or front of the list we have to - // slide the old objects forward. - if (pos < Entries_) // If there are elements after the add point - memmove(Vector_+pos+1,Vector_+pos,sizeof(T)*(Entries_-pos)); // move them forward - - //fprintf(stderr,"Placement new to %p\n",(Vector_+pos)); - - // This uses the placement new operator. placement new allows us to - // specify the memory address for the new object. In this case we - // want it at the 'pos' index into our array. - new((void *)(Vector_+pos)) T((T &)node); // Trust me, this isn't a memory leak - Entries_++; // one new entry - return(TRUE); -} - - -// Add to the first node, all others get shifted down one slot -template -bit8 ArrayList::addHead(IN T &node) -{ - return(add(node,0)); -} - - -// Append to the end of the list -template -bit8 ArrayList::addTail(IN T &node) -{ - return(add(node,length())); -} - - -// addSortedX only works (properly) if evrerything else in the list is added -// using addSorted. -template -bit8 ArrayList::addSortedAsc(IN T &node) -{ - sint32 pos = _sortedLookup(node, 1); - return(add(node, pos)); -} - - -// addSortedX only works (properly) if evrerything else in the list is added -// using addSorted. -template -bit8 ArrayList::addSortedDes(IN T &node) -{ - sint32 pos = _sortedLookup(node, 0); - return(add(node, pos)); -} - - -// This is the binary search used by addSorted -template -sint32 ArrayList::_sortedLookup(IN T &target, int ascending) -{ - int low, mid, high; - T* lowtarget; - T* hightarget; - T* midtarget; - - - // Trivial cases - if( Entries_ == 0 ) - return 0; - - low = 0; - high = Entries_ - 1; - while( 1 ) - { - assert( low <= high ); - mid = low + (int)(floor(((double)high - (double)low) / (double)2)); - - getPointer(&lowtarget, low); - getPointer(&hightarget, high); - getPointer(&midtarget, mid); - - // Exact match - if( *midtarget == target ) return mid; - - // Single element - if( high == low ) - { - if( ascending ) - { - if( target <= *lowtarget ) - return low; - else - return low + 1; - } - else - { - if( target <= *lowtarget ) - return low + 1; - else - return low; - } - } - - // Two elemsnts - if( (high - low) == 1 ) - { - if( ascending ) - { - if( target <= *lowtarget ) - return low; - else if( target <= *hightarget ) - return high; - else - return high + 1; - } - else - { - if( target <= *hightarget ) - return high + 1; - else if( target <= *lowtarget ) - return high; - else - return low; - } - } - - // Sorry, try again... - if( ascending ) - { - if( target < *midtarget ) - high = mid; - else - low = mid; - } - else - { - if( target < *midtarget ) - low = mid; - else - high = mid; - } - } -} - - -/*// addNumSortedX works in much the same way as addSortedX, except that I needed -// it for a very specific thing. I needed a list of strings numerically sorted, -// not alphabetically sorted. Furthermore these strings were composed of numbers -// delimited by underscores. In the interest of keeping it generic, these -// functions take as args a node, a delimiting character, and a count of the -// number of fields to include in a sort. If this is the list of strings: -// -// 55_100, 2_5, 23_32, 98_445, 2_48, 8_88, 2_3, 2_4 -// -// An alphabetical sort is: -// -// 2_3, 2_4, 2_48, 2_5, 55_100, 8_88, 98_445 -// -// But a numerical sort by calling addNumSortedAsc(, "_", 2) will result in: -// -// 2_3, 2_4, 2_5, 2_48, 8_88, 55_100, 98_445 -// -// Yes...now that you mention it I am on crack... -// -template -bit8 ArrayList::addNumSortedAsc(IN T &node, char delim, int fields) -{ - sint32 pos = _numSortedLookup(node, delim, fields, 1); - return(add(node, pos)); -} - - -// See addNumSortedAsc comment above. -template -bit8 ArrayList::addSortedDes(IN T &node, char delim, int fields) -{ - sint32 pos = _sortedLookup(node, delim, fields, 0); - return(add(node, pos)); -} - - -// This is the binary search used by addSorted -template -sint32 ArrayList::_numSortedLookup(IN T &target, char delim, int fields, int ascending) -{ - int low, mid, high; - T* lowtarget; - T* hightarget; - T* midtarget; - - - // Trivial case - if( Entries_ == 0 ) - return 0; - - low = 0; - high = Entries_; - while( 1 ) - { - assert( low <= high ); - mid = low + (int)(floor(((double)high - (double)low) / (double)2)); - - getPointer(&lowtarget, low); - getPointer(&hightarget, high); - getPointer(&midtarget, mid); - - // Exact match - if( *midtarget == target ) return mid; - - // Single element - if( high == low ) - { - if( ascending ) - { - if( target <= *lowtarget ) - return low; - else - return low + 1; - } - else - { - if( target <= *lowtarget ) - return low + 1; - else - return low; - } - } - - // Two elemsnts - if( (high - low) == 1 ) - { - if( ascending ) - { - if( target <= *lowtarget ) - return low; - else - return high; - } - else - { - if( target <= *lowtarget ) - return high; - else - return low; - } - } - - // Sorry, try again... - if( ascending ) - { - if( target < *midtarget ) - high = mid; - else - low = mid; - } - else - { - if( target < *midtarget ) - low = mid; - else - high = mid; - } - } -}*/ - - -// -// Delete an item at this index and construct a new one in it's place -// -template -bit8 ArrayList::replace(IN T &node, sint32 pos) -{ - if (Entries_==0) - return(FALSE); - if (pos<0) - pos=0; - if (pos >= Entries_) - pos=Entries_-1; - - (Vector_+pos)->~T(); // Call the destructor manually. Don't try this - // at home kiddies! - - // Now put the replacement object in there... - new((void *)(Vector_+pos)) T(node); // Trust me, this isn't a memory leak - - return(TRUE); -} - - - -// Remove at the zero based index specified by 'pos'. When removing from -// a slot, all others get shifted up by one. -template -bit8 ArrayList::remove(sint32 pos) -{ - if (Entries_==0) - return(FALSE); - if (pos<0) - pos=0; - if (pos >= Entries_) - pos=Entries_-1; - - (Vector_+pos)->~T(); // Call the destructor manually. Don't try this - // at home kiddies! - - memmove(Vector_+pos,Vector_+pos+1,sizeof(T)*(Entries_-pos-1)); - - Entries_--; - - // If we're at 33% usage or less, shrink the vector - if ( (Entries_*3) <= Slots_) - shrinkVector(); - - return(TRUE); -} - - -// Remove at the zero based index specified by 'pos'. When removing from -// a slot, all others get shifted up by one. -template -bit8 ArrayList::remove(OUT T &node, sint32 pos) -{ - bit8 retval; - retval=get(node,pos); - if (retval==FALSE) - return(FALSE); - return(remove(pos)); -} - - -// Remove the first node of the list -template -bit8 ArrayList::removeHead(OUT T &node) -{ - return(remove(node,0)); -} - - -// Remove the last node of the list -template -bit8 ArrayList::removeTail(OUT T &node) -{ - return(remove(node,Entries_-1)); -} - -// get a pointer to the internally managed object. Try and avoid this, but -// sometimes efficiency requires it... -// get a copy of an item -template -bit8 ArrayList::getPointer(OUT T **node,sint32 pos) RO -{ - if ((pos < 0)||(pos >= Entries_)) - return(FALSE); - *node=&(Vector_[pos]); - return(TRUE); -} - - -// get a copy of an item -template -bit8 ArrayList::get(OUT T &node,sint32 pos) RO -{ - if ((pos < 0)||(pos >= Entries_)) - return(FALSE); - node=Vector_[pos]; - return(TRUE); -} - - -// get a copy of the first node of the list -template -bit8 ArrayList::getHead(OUT T &node) RO -{ - return(get(node,0)); -} - - -// get a copy of the last node -template -bit8 ArrayList::getTail(OUT T &node) RO -{ - return(get(node,Entries_-1)); -} - -// just for debugging -template -void ArrayList::print(FILE *out) -{ - fprintf(out,"--------------------\n"); - //for (int i=0; i -sint32 ArrayList::length(void) RO -{ - return(Entries_); -} - -// Grow the vector by a factor of 2X -template -bit8 ArrayList::growVector(void) -{ - if (Entries_ < Slots_) // Don't grow until we're at 100% usage - return(FALSE); - - int newSlots=Entries_*2; - if(newSlots < INITIAL_SIZE) - newSlots=INITIAL_SIZE; - - //fprintf(stderr,"Growing vector to: %d\n",newSlots); - - // The goofy looking new below prevents operator new from getting called - // unnecessarily. This is severall times faster than allocating all of - // the slots as objects and then calling the assignment operator on them - // when they actually get used. - // - T *newVector=(T *)(new uint8[newSlots * sizeof(T)]); - memset(newVector,0,newSlots * sizeof(T)); // zero just to be safe - - if (Vector_ != NULL) - memcpy(newVector,Vector_,Entries_*sizeof(T)); - - delete[]((uint8 *)Vector_); // Get rid of the old vector without calling - // destructors - - Vector_=newVector; - Slots_=newSlots; - - return(TRUE); -} - - -// Shrink the vector by a factor of 2X -template -bit8 ArrayList::shrinkVector(void) -{ - //fprintf(stderr,"Shrink called\n"); - - // Don't need to shrink until usage goes below 33% - if ( (Entries_*3) > Slots_) - return(FALSE); - - int newSlots=Slots_/2; - if(newSlots < INITIAL_SIZE) // never shrink past initial size - newSlots=INITIAL_SIZE; - - if (newSlots >= Slots_) // don't need to shrink - return(FALSE); - - //fprintf(stderr,"Shrinking vector to: %d\n",newSlots); - - // The goofy looking new below prevents operator new from getting called - // unnecessarily. This is severall times faster than allocating all of - // the slots as objects and then calling the assignment operator on them - // when they actually get used. - // - T *newVector=(T *)(new uint8[newSlots * sizeof(T)]); - - if (Vector_ != NULL) // Vector_ better not be NULL! - memcpy(newVector,Vector_,Entries_*sizeof(T)); - - delete[]((uint8 *)Vector_); // Get rid of the old vector without calling - // destructors - - Vector_=newVector; - Slots_=newSlots; - - return(TRUE); -} - - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/configfile.cpp b/Generals/Code/Tools/matchbot/wlib/configfile.cpp deleted file mode 100644 index b0f9e5a91ca..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/configfile.cpp +++ /dev/null @@ -1,490 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * -****************************************************************************** -Project Name: Carpenter (The RedAlert ladder creator) -File Name : configfile.cpp -Author : Neal Kettler -Start Date : June 9, 1997 -Last Update : June 17, 1997 - - -This class will read in a config file and store the key value pairs for -later access. This is a fairly simple class, the config file is assumed -to be of the form: - -#comment -key = value - -The value can then be retrieved as a string or an integer. The key on -the left is used for retrieval and it must be specified in uppercase -for the 'get' functions. E.g. getString("KEY",valWstring); -\***************************************************************************/ - -#include -#include -#include - -#include "configfile.h" -#include "wdebug.h" - -static uint32 Wstring_Hash(const Wstring &string); -static char *Eat_Spaces(char *string); - -ConfigFile::ConfigFile() : Dictionary_(Wstring_Hash) -{ } - -ConfigFile::~ConfigFile() -{ } - -// Read and parse the config file. The key value pairs will be stored -// for later access by the getString/getInt functions. -bit8 ConfigFile::readFile(FILE *in) -{ - char string[256]; - char sectionname[256]; // section name like '[user parameters]' - Wstring key; - Wstring value; - char *cptr; - - memset(string,0,256); - memset(sectionname,0,256); - sectionList.clear(); - - while (fgets(string,256,in)) - { - cptr=Eat_Spaces(string); - if ((*cptr==0)||(*cptr=='#')) // '#' signals a comment - continue; - - if (*cptr=='[') // new section - { - key=cptr; - key.truncate(']'); // remove after & including the ] - key.cat("]"); // put the ] back - strcpy(sectionname,key.get()); // set the current section name - Wstring wssectionname; - - if (strlen(sectionname)==2) // clear section with a "[]" - { - sectionname[0]=0; - wssectionname.set(""); - } - else - wssectionname.set(sectionname+1); - - wssectionname.truncate(']'); - sectionList.addTail(wssectionname); - - - continue; - } - - if (strchr(cptr,'=')==NULL) // All config entries must have a '=' - continue; - key=cptr; - key.truncate('='); - key.removeSpaces(); // No spaces allowed in the key - key.toUpper(); // make key all caps - - // Add the section name to the end of the key - if (strlen(sectionname)) - key.cat(sectionname); - - cptr=Eat_Spaces(strchr(cptr,'=')+1); // Jump to after the '=' - value=cptr; - value.truncate('\r'); - value.truncate('\n'); - value.truncate('#'); - - // TheSuperHackers @compile xezon 25/03/2025 Re-implement algorithm to avoid writing into string buffer directly. - // Remove trailing spaces - { - const char* valueStr = value.get(); - const uint32 valueLen = value.length(); - uint32 spaceIdx = valueLen; - for (; spaceIdx > 0 && isgraph(valueStr[spaceIdx - 1])==0; --spaceIdx); - if (spaceIdx != valueLen) - value.truncate(spaceIdx); - } - - Critsec_.lock(); - Dictionary_.add(key,value); - Critsec_.unlock(); - } - return(TRUE); -} - - -// -// Enum through the config strings. To start, index & offset should be 0 -// If retval is false you're done, ignore whatever's in key & value. -// -// Section specifies the configfile section. Set to NULL if you don't care. -// -bit8 ConfigFile::enumerate(int &index, int &offset, Wstring &key, Wstring &value, IN char *section) const -{ - int seclen = strlen(section); - while(1) - { - Critsec_.lock(); - if (Dictionary_.iterate(index,offset,key,value)==FALSE) // out of keys? - { - Critsec_.unlock(); - return(FALSE); - } - Critsec_.unlock(); - - if (section==NULL) // no specified section, so any will do... - break; - - if (strlen(section)+2 >= strlen(key.get())) // key should have form: X[section] - continue; - - // Is this key part of our section? - const char *keystr = key.get() + strlen(key.get())-seclen-1; - if (strncmp(keystr,section,strlen(section))==0) - break; - } - key.truncate('['); // remove the section name - return(TRUE); -} - - - -// Get a config entry as a string -bit8 ConfigFile::getString(IN Wstring &_key, Wstring &value, IN char *section) const -{ - Wstring key(_key); - key.toUpper(); - - if (section) // append section name to key - { - key+="["; - key+=section; - key+="]"; - } - - Critsec_.lock(); - bit8 retval=Dictionary_.getValue(key,value); - Critsec_.unlock(); - - if (retval==FALSE) - { - DBGMSG("Config entry missing: "<>24); // ROL 8 - } - return(retval); -} - -static char *Eat_Spaces(char *string) -{ - char *retval=string; - while (isspace(*retval)) - retval++; - return(retval); -} diff --git a/Generals/Code/Tools/matchbot/wlib/configfile.h b/Generals/Code/Tools/matchbot/wlib/configfile.h deleted file mode 100644 index b622f1354e4..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/configfile.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * -****************************************************************************** -Project Name: Carpenter (The RedAlert ladder creator) -File Name : configfile.h -Author : Neal Kettler -Start Date : June 9, 1997 -Last Update : May 13, 1999 -\***************************************************************************/ - -#ifndef CONFIGFILE_HEADER -#define CONFIGFILE_HEADER - -#include "wstypes.h" -#include "dictionary.h" -#include "wstring.h" -#include "critsec.h" -#include "arraylist.h" - -class ConfigFile -{ - public: - ConfigFile(); - ~ConfigFile(); - bit8 readFile(FILE *config); - bit8 getString(IN Wstring &key,OUT Wstring &value, IN char *section=NULL) const; - bit8 getString(IN char *key,OUT Wstring &value, IN char *section=NULL) const; - - bit8 getInt(IN Wstring &key,OUT sint32 &value, IN char *section=NULL) const; - bit8 getInt(IN char *key,OUT sint32 &value, IN char *section=NULL) const; - - bit8 getInt(IN Wstring &key,OUT sint16 &value, IN char *section=NULL) const; - bit8 getInt(IN char *key,OUT sint16 &value, IN char *section=NULL) const; - - // Enumerate through the config lines - bit8 enumerate(int &index, int &offset, Wstring &key, Wstring &value, IN char *section=NULL) const; - - // Manual update of config file - bit8 setString(IN Wstring &key,IN Wstring &value, IN char *section=NULL); - bit8 setString(IN char *key,IN Wstring &value, IN char *section=NULL); - bit8 setInt(IN Wstring &key,IN sint32 &value, IN char *section=NULL); - bit8 setInt(IN char *key,IN sint32 &value, IN char *section=NULL); - bit8 removeEntry(IN Wstring &key, IN char *section=NULL); - bit8 removeEntry(IN char *key, IN char *section=NULL); - bit8 writeFile(FILE *config); // Does not preserve comments, etc - - ArrayList sectionList; // stores the names of all sections - - private: - Dictionary Dictionary_; // stores the mappings from keys - // to value strings - - // The lock is only needed around the immediate access to the dictionary, no writes - // are allowed so you don't need to worry about an outer lock around the enumerate - CritSec Critsec_; // lock around dictionary -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/critsec.cpp b/Generals/Code/Tools/matchbot/wlib/critsec.cpp deleted file mode 100644 index 21b8ced2b79..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/critsec.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include "critsec.h" -#include -#include "wlib/wdebug.h" - - - -CritSec::CritSec() -{ -#ifdef _UNIX - pthread_mutex_init(&Mutex_, NULL); - RefCount_ = 0; -#elif defined(_WIN32) - InitializeCriticalSection(&CritSec_); -#endif -} - -CritSec::~CritSec() -{ - #ifdef _UNIX - pthread_mutex_destroy(&Mutex_); - #elif defined(_WIN32) - DeleteCriticalSection(&CritSec_); - #endif -} - -// The "lock" function blocks until the mutex is available. -// Returns 0 on success, error code on error. -// -// A thread that already has a lock will increment a reference count if it calls -// lock again. It must then call unlock() enough times to get the reference to 0. -// -// If refcount is not null you can get the current ref counter after the lock. -// -sint32 CritSec::lock(int *refcount) RO -{ - #ifdef _UNIX - sint32 status; - - // I TRY to get the lock. IF I succeed, OR if I fail because - // I already have the lock, I just increment the reference - // count and return. - if (((status = pthread_mutex_trylock(&Mutex_)) == 0) || - ((status == EBUSY) && (ThreadId_ == pthread_self()))) - { - ThreadId_ = pthread_self(); - RefCount_++; - if (refcount) - *refcount=RefCount_; - return(0); - } - - // Otherwise, I wait for the lock. - if ((status = pthread_mutex_lock(&Mutex_)) == 0) - { - assert(RefCount_ == 0); - ThreadId_ = pthread_self(); - RefCount_++; - } - else - { - ERRMSG("pthread_mutex_lock: " << strerror(errno)); - } - - if (refcount) - *refcount=RefCount_; - - return(status); - #elif defined(_WIN32) - // TOFIX update the refcount - EnterCriticalSection(&CritSec_); - return(0); - #else - #error Must define either _WIN32 or _UNIX - #endif -} - -// The "unlock" function release the critical section. -sint32 CritSec::unlock(void) RO -{ - #ifdef _UNIX - sint32 status = 0; - - assert(RefCount_ >= 0); - if (RefCount_ <= 0) - { - //ERRMSG("unlocking unlocked mutex!"); - return(-1); - } - - ///assert(ThreadId_ == pthread_self()); - if (ThreadId_ != pthread_self()) - { - WRNMSG("tried to unlock a mutex not owned by self"); - return(-1); - } - - if (--RefCount_ == 0) - { - // Set thread id to zero -- we're going to release mutex - ThreadId_ = (pthread_t)0; - - // Unlock the mutex. - if ((status = pthread_mutex_unlock(&Mutex_)) != 0) - ERRMSG("pthread_mutex_lock: " << strerror(errno)); - } - return status; - #elif defined(_WIN32) - LeaveCriticalSection(&CritSec_); - return(0); - #endif -} diff --git a/Generals/Code/Tools/matchbot/wlib/critsec.h b/Generals/Code/Tools/matchbot/wlib/critsec.h deleted file mode 100644 index c25759cf047..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/critsec.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef CRITSEC_HEADER -#define CRITSEC_HEADER - -#include "wstypes.h" -#ifdef _WIN32 - #include - #include -#elif defined(_UNIX) - #include - #include -#endif - -// Windows headers have a tendency to redefine IN -#ifdef IN -#undef IN -#endif -#define IN const - -// -// Critical Section built either on a POSIX Mutex, or a Win32 Critical Section -// -// POSIX version is done by keeping a thread_id and a reference count. Win32 version -// just calls the built in functions. -// -class CritSec -{ - public: - CritSec(); - ~CritSec(); - - sint32 lock(int *refcount=NULL) RO; - sint32 unlock(void) RO; - - protected: - #ifdef _WIN32 - mutable CRITICAL_SECTION CritSec_; - #else - mutable pthread_mutex_t Mutex_; // Mutex lock - mutable pthread_t ThreadId_; // Owner of mutex - mutable int RefCount_; // Reference count - #endif -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/dictionary.h b/Generals/Code/Tools/matchbot/wlib/dictionary.h deleted file mode 100644 index 56e1cc346b4..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/dictionary.h +++ /dev/null @@ -1,681 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * -****************************************************************************** -Project Name: Carpenter (The RedAlert ladder creator) -File Name : dictionary.h -Author : Neal Kettler -Start Date : June 1, 1997 -Last Update : June 17, 1997 - -This template file implements a hash dictionary. A hash dictionary is -used to quickly match a value with a key. This works well for very -large sets of data. A table is constructed that has some power of two -number of pointers in it. Any value to be put in the table has a hashing -function applied to the key. That key/value pair is then put in the -linked list at the slot the hashing function specifies. If everything -is working well, this is much faster than a linked list, but only if -your hashing function is good. -\****************************************************************************/ - - -#ifndef DICTIONARY_HEADER -#define DICTIONARY_HEADER - - -#include -#include -#include -#include -#include - -#include "wstypes.h" - -// Every entry in the hash dictionary must be an instance of the DNode -// template. 'K' and 'V' denote Key and Value. -template -class DNode -{ - public: - K key; - V value; - DNode *hashNext; -}; - -template -class Dictionary -{ - public: - ////////////////Dictionary(uint32 (* hashFn)(K &key)); - - -// Note: I had to put this inside the class definition because VC5 sucks butt - -//Create the empty hash dictionary -Dictionary(uint32 (*hashFn)(const K &key)) : - SHRINK_THRESHOLD(0.20), // When table is only 20% full shrink it - EXPAND_THRESHOLD(0.80), // When table is 80% full grow it - MIN_TABLE_SIZE(128) // must be a power of 2 -{ - log2Size=MIN_TABLE_SIZE; - size=MIN_TABLE_SIZE; - assert(size>=4); - tableBits=0; - while(log2Size) { tableBits++; log2Size>>=1; } - tableBits--; - size=1< **)new DNode* [size]; - assert(table!=NULL); - - memset((void *)table,0,size*sizeof(void *)); - hashFunc=hashFn; -} - - - ~Dictionary(); - - void clear(void); - bit8 add(IN K &key,IN V &value); - bool getValue(IN K &key, OUT V &value) RO; - bool getPointer(IN K &key, OUT V **value) RO; // ptr to internal storage (Careful!) - void print(FILE *out) RO; - uint32 getSize(void) RO; - uint32 getEntries(void) RO; - bit8 contains(IN K &key) RO; - bit8 updateValue(IN K &key,IN V &value); - bit8 remove(IN K &key,OUT V &value); - bit8 remove(IN K &key); - bit8 removeAny(OUT K &key,OUT V &value); - bit8 iterate(INOUT int &index,INOUT int &offset, OUT V &value) RO; - bit8 iterate(INOUT int &index,INOUT int &offset, OUT K &key, OUT V &value) RO; - Dictionary &operator=(Dictionary &other); - - private: - void shrink(void); // halve the number of slots - void expand(void); // double the number of slots - - - DNode **table; // This stores the lists at each slot - - uint32 entries; // number of entries - uint32 size; // size of table - uint32 tableBits; // table is 2^tableBits big - uint32 log2Size; // Junk variable - bit8 keepSize; // If true don't shrink or expand - - uint32 (* hashFunc)(IN K &key); // User provided hash function - uint32 keyHash(IN K &key) RO; // This will reduce to correct range - - - // See initilizer list of constructor for values - const double SHRINK_THRESHOLD; // When table is this % full shrink it - const double EXPAND_THRESHOLD; // When table is this % full grow it - const int MIN_TABLE_SIZE; // must be a power of 2 -}; - - - -//Free all the memory... -template -Dictionary::~Dictionary() -{ - clear(); // Remove the entries - delete[](table); // And the table as well -} - -// Remove all the entries and free the memory -template -void Dictionary::clear() -{ - DNode *temp,*del; - uint32 i; - //free all the data - for (i=0; ihashNext; - delete(del); - } - table[i]=NULL; - } - entries=0; - - while ((getSize()>(uint32)MIN_TABLE_SIZE)&&(keepSize==FALSE)) - shrink(); -} - -template -uint32 Dictionary::keyHash(IN K &key) RO -{ - uint32 retval=hashFunc(key); - retval &= ((1< -void Dictionary::print(FILE *out) RO -{ - DNode *temp; - uint32 i; - - fprintf(out,"--------------------\n"); - for (i=0; ihashNext; - } - fprintf(out,"\n"); - } - fprintf(out,"--------------------\n"); -} - - -template -Dictionary &Dictionary::operator=(Dictionary &other) -{ - _ASSERTE(0); -} - - - -// -// Iterate through all the records. Index is for the table, offset specifies the -// element in the linked list. Set both to 0 and continue calling till false -// is returned. -template -bit8 Dictionary::iterate(INOUT int &index,INOUT int &offset, - OUT V &value) RO -{ - DNode *temp; - - // index out of range - if ((index<0)||(index >= (int)getSize())) - return(FALSE); - - temp=table[index]; - while ((temp==NULL)&&((++index) < (int)getSize())) - { - temp=table[index]; - offset=0; - } - - if (temp==NULL) // no more slots with data - return(FALSE); - - uint32 i=0; - while ((temp!=NULL) && ((int)i < offset)) - { - temp=temp->hashNext; - i++; - } - - if (temp==NULL) // should never happen - return(FALSE); - - value=temp->value; - if (temp->hashNext==NULL) - { - index++; - offset=0; - } - else - offset++; - - return(TRUE); -} - - - - -// -// Iterate through all the records. Index is for the table, offset specifies the -// element in the linked list. Set both to 0 and continue calling till false -// is returned. -template -bit8 Dictionary::iterate(INOUT int &index,INOUT int &offset, - OUT K &key, OUT V &value) RO -{ - DNode *temp; - - // index out of range - if ((index<0)||(index >= (int)getSize())) - return(FALSE); - - temp=table[index]; - while ((temp==NULL)&&((++index) < (int)getSize())) - { - temp=table[index]; - offset=0; - } - - if (temp==NULL) // no more slots with data - return(FALSE); - - uint32 i=0; - while ((temp!=NULL) && ((int)i < offset)) - { - temp=temp->hashNext; - i++; - } - - if (temp==NULL) // should never happen - return(FALSE); - - value=temp->value; - key=temp->key; - if (temp->hashNext==NULL) - { - index++; - offset=0; - } - else - offset++; - - return(TRUE); -} - - - - -// Return the current size of the hash table -template -uint32 Dictionary::getSize(void) RO -{ return(size); } - - -// Return the current number of entries in the table -template -uint32 Dictionary::getEntries(void) RO -{ return(entries); } - - -// Does the Dictionary contain the key? -template -bit8 Dictionary::contains(IN K &key) RO -{ - int offset; - DNode *node; - - offset=keyHash(key); - - node=table[offset]; - - if (node==NULL) - { return(FALSE); } // can't find it - - while(node!=NULL) - { - if ((node->key)==key) - { return(TRUE); } - node=node->hashNext; - } - return(FALSE); -} - - -// Try and update the value of an already existing object -template -bit8 Dictionary::updateValue(IN K &key,IN V &value) -{ - sint32 retval; - - retval=remove(key); - if (retval==FALSE) - return(FALSE); - - add(key,value); - return(TRUE); -} - - -// Add to the dictionary (if key exists, value is updated with the new V) -template -bit8 Dictionary::add(IN K &key,IN V &value) -{ - int offset; - DNode *node,*item,*temp; - float percent; - - item=(DNode *)new DNode; - assert(item!=NULL); - - #ifdef KEY_MEM_OPS - memcpy(&(item->key),&key,sizeof(K)); - #else - item->key=key; - #endif - - #ifdef VALUE_MEM_OPS - memcpy(&(item->value),&value,sizeof(V)); - #else - item->value=value; - #endif - - item->hashNext=NULL; - - //If key already exists, it will be overwritten - remove(key); // Hopefully this will be false... - - offset=keyHash(key); - - node=table[offset]; - - if (node==NULL) - { table[offset]=item; } - else - { - temp=table[offset]; - table[offset]=item; - item->hashNext=temp; - } - - entries++; - percent=(float)entries; - percent/=(float)getSize(); - if (percent>= EXPAND_THRESHOLD ) expand(); - - return(TRUE); -} - -// Remove an item from the dictionary -template -bit8 Dictionary::remove(IN K &key,OUT V &value) -{ - int offset; - DNode *node,*last,*temp; - float percent; - - if (entries==0) - return(FALSE); - - percent=(float)(entries-1); - percent/=(float)getSize(); - - offset=keyHash(key); - node=table[offset]; - - last=node; - if (node==NULL) - return(FALSE); - - //special case table points to thing to delete - - #ifdef KEY_MEM_OPS - if (0==memcmp(&(node->key),&key,sizeof(K))) - #else - if ((node->key)==key) - #endif - { - #ifdef VALUE_MEM_OPS - memcpy(&value,&(node->value),sizeof(V)); - #else - value=node->value; - #endif - temp=table[offset]->hashNext; - delete(table[offset]); - table[offset]=temp; - entries--; - if (percent <= SHRINK_THRESHOLD) - shrink(); - return(TRUE); - } - node=node->hashNext; - - bit8 retval=FALSE; // wow, didn't add this for years... (DOH!) - - //Now the case if the thing to delete is not the first - while (node!=NULL) - { - #ifdef KEY_MEM_OPS - if (0==memcmp(&(node->key),&key,sizeof(K))) - #else - if (node->key==key) - #endif - { - #ifdef VALUE_MEM_OPS - memcpy(&value,&(node->value),sizeof(V)); - #else - value=node->value; - #endif - last->hashNext=node->hashNext; - entries--; - delete(node); - retval=TRUE; // yes, we deleted something - break; - } - last=node; - node=node->hashNext; - } - - if (percent <= SHRINK_THRESHOLD) - shrink(); - return(retval); -} - - -template -bit8 Dictionary::remove(IN K &key) -{ - V temp; - return(remove(key,temp)); -} - - -// Remove some random K/V pair that's in the Dictionary -template -bit8 Dictionary::removeAny(OUT K &key,OUT V &value) -{ - int offset; - DNode *node,*last,*temp; - float percent; - - if (entries==0) - return(FALSE); - - percent=(entries-1); - percent/=(float)getSize(); - - int i; - offset=-1; - for (i=0; i<(int)getSize(); i++) - if (table[i]!=NULL) - { - offset=i; - break; - } - - if (offset==-1) // Nothing there - return(FALSE); - - node=table[offset]; - last=node; - - #ifdef KEY_MEM_OPS - memcpy(&key,&(node->key),sizeof(K)); - #else - key=node->key; - #endif - #ifdef VALUE_MEM_OPS - memcpy(&value,&(node->value),sizeof(V)); - #else - value=node->value; - #endif - - temp=table[offset]->hashNext; - delete(table[offset]); - table[offset]=temp; - entries--; - if (percent <= SHRINK_THRESHOLD) - shrink(); - return(TRUE); -} - - -template -bool Dictionary::getValue(IN K &key,OUT V &value) RO -{ - V *valptr=NULL; - bool retval=getPointer(key,&valptr); - if (retval && valptr) - { - #ifdef VALUE_MEM_OPS - assert(0); - #else - value=*valptr; - #endif - } - return(retval); -} - -// Try and avoid this since you're getting a pointer to the internally -// managed data! -template -bool Dictionary::getPointer(IN K &key,OUT V **valptr) RO -{ - int offset; - DNode *node; - - if (entries==0) - return(FALSE); - - offset=keyHash(key); - - node=table[offset]; - - if (node==NULL) - return(FALSE); - - #ifdef KEY_MEM_OPS - while ((node!=NULL)&&(memcmp(&(node->key),&key,sizeof(K)))) - #else - while ((node!=NULL)&&( ! ((node->key)==key)) ) // odd syntax so you don't - #endif // have to do oper != - { node=node->hashNext; } - - if (node==NULL) - { return(FALSE); } - - *valptr=&(node->value); - - return(TRUE); -} - - -//A note about Shrink and Expand: They are never necessary, they are -//only here to improve performance of the hash table by reducing -//the length of the linked list at each table entry. - -// Shrink the hash table by a factor of 2 (and relocate entries) -template -void Dictionary::shrink(void) -{ - int i; - int oldsize; - uint32 offset; - DNode **oldtable,*temp,*first,*next; - - if ((size<=(uint32)MIN_TABLE_SIZE)||(keepSize==TRUE)) - return; - - //fprintf(stderr,"Shrinking....\n"); - - oldtable=table; - oldsize=size; - size/=2; - tableBits--; - - table=(DNode **)new DNode*[size]; - assert(table!=NULL); - memset((void *)table,0,size*sizeof(void *)); - - for (i=0; ikey); - first=table[offset]; - table[offset]=temp; - next=temp->hashNext; - temp->hashNext=first; - temp=next; - } - } - delete[](oldtable); -} - - -template -void Dictionary::expand(void) -{ - int i; - int oldsize; - uint32 offset; - DNode **oldtable,*temp,*first,*next; - - if (keepSize==TRUE) - return; - - //fprintf(stderr,"Expanding...\n"); - - oldtable=table; - oldsize=size; - size*=2; - tableBits++; - - table=(DNode **)new DNode* [size]; - assert(table!=NULL); - memset((void *)table,0,size*sizeof(void *)); - - for (i=0; ikey); - first=table[offset]; - table[offset]=temp; - next=temp->hashNext; - temp->hashNext=first; - temp=next; - } - } - delete[](oldtable); -} - - -#endif - diff --git a/Generals/Code/Tools/matchbot/wlib/filed.h b/Generals/Code/Tools/matchbot/wlib/filed.h deleted file mode 100644 index ac68d7c7054..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/filed.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef FILED_HEADER -#define FILED_HEADER - -#include "odevice.h" - -class FileD : public OutputDevice -{ - public: - FileD(IN char *filename, IN char *mode = "w") - { - out=fopen(filename,mode); - if (out==NULL) - out=fopen("FileDev.out",mode); - } - - virtual ~FileD() - { fclose(out); } - - virtual int print(const char *str,int len) - { - char *string=new char[len+1]; - memset(string,0,len+1); - memcpy(string,str,len); - fprintf(out,"%s",string); - delete[](string); - fflush(out); - return(len); - } - - FILE *out; -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/linkedlist.h b/Generals/Code/Tools/matchbot/wlib/linkedlist.h deleted file mode 100644 index d546d3e96c6..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/linkedlist.h +++ /dev/null @@ -1,495 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * -****************************************************************************** -Project Name: -File Name : linkedlist.h -Author : Neal Kettler -Start Date : June 19, 1997 -Last Update : June 19, 1997 - -Linked list template. This is a fairly standard doubly linked list that -allows insertion and removal at any point in the list. A current pointer -is used to quickly access items when they are examined sequentially. -Copies of the data are stored instead of a pointer to the original. - -If you want to store pointers then the template should be of a pointer type. -\****************************************************************************/ - -#ifndef LINKEDLIST_HEADER -#define LINKEDLIST_HEADER - -#include -#include -#include -#include - -#include "wstypes.h" - -template -class LNode -{ - public: - T Node; - LNode *Next; - LNode *Prev; -}; - -template -class LinkedList -{ - public: - LinkedList(); - LinkedList(LinkedList &other); - ~LinkedList(); - - // Remove all entries from the lsit - void clear(void); - - // Add a node after the zero based 'pos' - bit8 add(IN T &node,sint32 pos, OUT T **newnodeptr=NULL); - bit8 addTail(IN T &node, OUT T **newnodeptr=NULL); - bit8 addHead(IN T &node, OUT T **newnodeptr=NULL); - - // Remove a node - bit8 remove(OUT T &node,sint32 pos); - bit8 remove(sint32 pos); - bit8 removeHead(OUT T &node); - bit8 removeTail(OUT T &node); - - - // Get a node without removing from the list - bit8 get(OUT T &node,sint32 pos); - bit8 getHead(OUT T &node); - bit8 getTail(OUT T &node); - - // Get a pointer to the internally managed data (careful!) - bit8 getPointer(OUT T **node, sint32 pos); - - // Get the number of entries in the list - sint32 length(void); - - // Print information on the list - void print(IN FILE *out); - - // assignment operator - LinkedList &operator=(LinkedList &other); - - private: - sint32 Entries; // Number of entries - LNode *Head; // Head of the list - LNode *Tail; // Tail of the list - - LNode *Current; // Current pointer & index for speed only - sint32 CurIndex; -}; - - -//Create the empty list -template -LinkedList::LinkedList() -{ - Entries=0; - Head=Tail=Current=NULL; - CurIndex=-1; // Not valid when 0 entries -} - -// copy constructor -template -LinkedList::LinkedList(LinkedList &other) -{ - Entries=0; - Head=Tail=Current=NULL; - CurIndex=-1; // Not valid when 0 entries - (*this)=other; -} - -//Free all the memory... -template -LinkedList::~LinkedList() -{ - clear(); // Remove the entries -} - -// assignment operator -template -LinkedList &LinkedList::operator=(LinkedList &other) -{ - T node; - clear(); - for (int i=0; i -void LinkedList::clear() -{ - LNode *temp,*del; - - temp=Head; - while (temp) { - del=temp; - temp=temp->Next; - delete(del); - } - Entries=0; - CurIndex=-1; - Head=Tail=Current=NULL; -} - -// When adding into a position, the new node goes at the zero based slot -// specified by pos. All other nodes get moved one slot down. -template -bit8 LinkedList::add(IN T &node,sint32 pos, OUT T **newnodeptr) -{ - LNode *temp; - LNode *item; - - if (pos<0) - pos=0; - if (pos>Entries) - pos=Entries; - - item=(LNode *)new LNode; - assert(item!=NULL); - item->Node=node; // copy the passed in object - item->Prev=NULL; - item->Next=NULL; - - if (newnodeptr) - *newnodeptr=&(item->Node); - - if ((pos==0)||(pos==Entries)) { // Both cases can be true for a new list! - if (pos==0) { - item->Next=Head; - if (Head) - Head->Prev=item; - Head=item; - } - if (pos==Entries) { - item->Prev=Tail; - if (Tail) - Tail->Next=item; - Tail=item; - } - Entries++; - Current=item; - CurIndex=pos; - return(TRUE); - } - - // If control is here, we know the new node is not an endpoint - - // Check for possible speedup, so we don't have to scan the list - if (pos==CurIndex) { - item->Next=Current; - item->Prev=Current->Prev; - Current->Prev=item; - item->Prev->Next=item; - Current=item; - Entries++; - return(TRUE); - } - // Check the other possible speedup (adding after CurIndex) - if (pos==CurIndex+1) { - item->Next=Current->Next; - item->Prev=Current; - Current->Next=item; - item->Next->Prev=item; - Current=item; - CurIndex++; - Entries++; - return(TRUE); - } - - // If control reaches here we have to scan the whole thing - temp=Head->Next; // Can start at node '1' because head was special cased - for (int i=1; iNext; - assert(temp!=NULL); - } - item->Next=temp; - item->Prev=temp->Prev; - temp->Prev=item; - item->Prev->Next=item; - Current=item; - CurIndex=pos; - Entries++; - - return(TRUE); -} - - -// Add to the first node, all others get shifted down one slot -template -bit8 LinkedList::addHead(IN T &node, OUT T **newnodeptr) -{ - return(add(node,0,newnodeptr)); -} - - -// Append to the end of the list -template -bit8 LinkedList::addTail(IN T &node, OUT T **newnodeptr) -{ - return(add(node,length(),newnodeptr)); -} - - -// Remove at the zero based index specified by 'pos'. When removing from -// a slot, all others get shifted up by one. -template -bit8 LinkedList::remove(OUT T &node, sint32 pos) -{ - ////////LNode *temp; - LNode *item; - - if (Entries==0) - return(FALSE); - - if (pos<0) - pos=0; - if (pos>=Entries) - pos=Entries-1; - - if ((pos==0)||(pos==Entries-1)) { // Both can be true for a 1 item list - if (pos==0) { - item=Head; - if (item->Next) - item->Next->Prev=NULL; - Head=item->Next; - node=item->Node; - Current=Head; - CurIndex=0; - } - if (pos==Entries-1) { - item=Tail; - if (item->Prev) - item->Prev->Next=NULL; - Tail=item->Prev; - node=item->Node; - Current=Tail; - CurIndex=Entries-2; - } - delete(item); - Entries--; - - if (Entries==0) { // Super paranoia check - assert(Current==NULL); - assert(CurIndex==-1); - assert(Head==NULL); - assert(Tail==NULL); - } - return(TRUE); - } - // If control is here, we know the target node is not an endpoint - - // Check for possible speedup, so we don't have to scan the list - if (pos==CurIndex) { - item=Current; - item->Prev->Next=item->Next; - item->Next->Prev=item->Prev; - Current=item->Next; - // CurIndex stays the same - node=item->Node; - delete(item); - Entries--; - return(TRUE); - } - - // Check the other possible speedup (removing after CurIndex) - if (pos==CurIndex+1) { - item=Current->Next; - item->Prev->Next=item->Next; - item->Next->Prev=item->Prev; - Current=item->Next; - CurIndex++; - node=item->Node; - delete(item); - Entries--; - return(TRUE); - } - - - // If control reaches here we have to scan the whole thing - item=Head->Next; // Can start at node '1' because head was special cased - for (int i=1; iNext; - assert(item!=NULL); - } - - item->Prev->Next=item->Next; - item->Next->Prev=item->Prev; - Current=item->Next; - CurIndex=pos; - node=item->Node; - delete(item); - Entries--; - - return(TRUE); -} - - - - -// Remove at the zero based index specified by 'pos'. When removing from -// a slot, all others get shifted up by one. -template -bit8 LinkedList::remove(sint32 pos) -{ - T temp_node; - return(remove(temp_node,pos)); -} - - -// Remove the first node of the list -template -bit8 LinkedList::removeHead(OUT T &node) -{ - return(remove(node,0)); -} - - -// Remove the last node of the list -template -bit8 LinkedList::removeTail(OUT T &node) -{ - return(remove(node,Entries-1)); -} - - - -template -bit8 LinkedList::get(OUT T &node, sint32 pos) -{ - T *objptr; - bool retval=getPointer(&objptr,pos); - if (retval && objptr) - node=*objptr; - - return(retval); -} - - -template -bit8 LinkedList::getPointer(OUT T **node,sint32 pos) -{ - if ((node==0)||(Entries==0)) - return(FALSE); - - LNode *item; - - if (pos<0) - { - //pos=0; - return(FALSE); - } - if (pos>=Entries) - { - //pos=Entries-1; - return(FALSE); - } - - if (pos==0) { - *node=&(Head->Node); - return(TRUE); - } else if (pos==Entries-1) { - *node=&(Tail->Node); - return(TRUE); - } - // If control reaches here, we know target is not an endpoint - - // Check for possible speedup, so we don't have to scan the list - if (pos==CurIndex) { - *node=&(Current->Node); - return(TRUE); - } else if (pos==CurIndex+1) { - *node=&(Current->Next->Node); - CurIndex++; - Current=Current->Next; - return(TRUE); - } else if (pos==CurIndex-1) { - *node=&(Current->Prev->Node); - CurIndex--; - Current=Current->Prev; - return(TRUE); - } - - // If control reaches here we have to scan the whole thing - item=Head->Next; // Can start at node '1' because head was special cased - for (int i=1; iNext; - assert(item!=NULL); - } - *node=&(item->Node); - CurIndex=pos; - Current=item; - - return(TRUE); -} - - -// Remove the first node of the list -template -bit8 LinkedList::getHead(OUT T &node) -{ - return(get(node,0)); -} - - -// Remove the last node of the list -template -bit8 LinkedList::getTail(OUT T &node) -{ - return(get(node,Entries-1)); -} - - -template -void LinkedList::print(IN FILE *out) -{ - LNode *temp; - - fprintf(out,"--------------------\n"); - fprintf(out,"Entries = %d\n",length()); - fprintf(out,"H = %8p C = %8p (%d) T = %8p\n",Head,Current,CurIndex,Tail); - - temp=Head; - while (temp) { - fprintf(out," %8p<-((%8p))->%8p \n",temp->Prev,temp,temp->Next); - temp=temp->Next; - } - - fprintf(out,"--------------------\n"); -} - -// Return the current length of the list -template -sint32 LinkedList::length(void) { - return(Entries); -} - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/mboxd.h b/Generals/Code/Tools/matchbot/wlib/mboxd.h deleted file mode 100644 index 4bd1e7f1214..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/mboxd.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef MBOXD_HEADER -#define MBOXD_HEADER - -#include "odevice.h" - -class MboxD : public OutputDevice -{ - public: - - virtual int print(const char *str,int len) - { - char *string=new char[len+1]; - memset(string,0,len+1); - memcpy(string,str,len); - MessageBox(NULL,string,"Debug Message", MB_OK | MB_ICONINFORMATION); - delete[](string); - return(len); - } - -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/monod.cpp b/Generals/Code/Tools/matchbot/wlib/monod.cpp deleted file mode 100644 index 5448e627f15..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/monod.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include "monod.h" - -MonoD::MonoD(void) -{ -#ifdef _WIN32 - unsigned long retval; - handle = CreateFile("\\\\.\\MONO", GENERIC_READ|GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - - if (handle != INVALID_HANDLE_VALUE) - { - DeviceIoControl(handle, (DWORD)IOCTL_MONO_CLEAR_SCREEN, NULL, 0, NULL, 0, - &retval,0); - } -#endif -} - -MonoD::~MonoD() -{ - #ifdef _WIN32 - CloseHandle(handle); - handle=NULL; - #endif -} - -int MonoD::print(const char *str, int len) -{ - #ifdef _WIN32 - unsigned long retval; - WriteFile(handle, str, len, &retval, NULL); - ////DeviceIoControl(handle, (DWORD)IOCTL_MONO_PRINT_RAW, (void *)str, len, NULL, 0, - //// &retval,0); - return(len); - #else - for (int i=0; i. -*/ - -#ifndef MONOD_HEADER -#define MONOD_HEADER - - -#include -#include -#include "odevice.h" - -///////////////////////// WIN32 ONLY /////////////////////////////////// - -#ifdef _WIN32 -#include -#include - - -/* -** This is the identifier for the Monochrome Display Driver -*/ -#define FILE_DEVICE_MONO 0x00008000 - -/* -** These are the IOCTL commands supported by the Monochrome Display Driver. -*/ -#define IOCTL_MONO_HELP_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x800, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_CLEAR_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x801, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_PRINT_RAW CTL_CODE(FILE_DEVICE_MONO, 0x802, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_SET_CURSOR CTL_CODE(FILE_DEVICE_MONO, 0x803, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_SCROLL CTL_CODE(FILE_DEVICE_MONO, 0x804, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_BRING_TO_TOP CTL_CODE(FILE_DEVICE_MONO, 0x805, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_SET_ATTRIBUTE CTL_CODE(FILE_DEVICE_MONO, 0x806, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_PAN CTL_CODE(FILE_DEVICE_MONO, 0x807, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_LOCK CTL_CODE(FILE_DEVICE_MONO, 0x808, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_UNLOCK CTL_CODE(FILE_DEVICE_MONO, 0x809, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_SET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80A, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_RESET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80B, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_SET_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80C, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_CLEAR_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80D, METHOD_BUFFERED, FILE_WRITE_DATA) -#define IOCTL_MONO_FILL_ATTRIB CTL_CODE(FILE_DEVICE_MONO, 0x80E, METHOD_BUFFERED, FILE_WRITE_DATA) - -#endif // ifdef _WIN32 - -class MonoD : public OutputDevice -{ - public: - MonoD(); - ~MonoD(); - - virtual int print(const char *str,int len); - - private: - #ifdef _WIN32 - HANDLE handle; - #endif -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/odevice.h b/Generals/Code/Tools/matchbot/wlib/odevice.h deleted file mode 100644 index f04bffaec12..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/odevice.h +++ /dev/null @@ -1,32 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef ODEVICE_HEADER -#define ODEVICE_HEADER - -// This virtual base class provides an interface for output devices -// that can be used for the debugging package. -class OutputDevice -{ - public: - OutputDevice() {} - virtual ~OutputDevice() {}; - virtual int print(const char *s,int len)=0; -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/sem4.cpp b/Generals/Code/Tools/matchbot/wlib/sem4.cpp deleted file mode 100644 index 9c7f7e42a1c..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/sem4.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/******************************************************************************\ -sem4.cpp Neal Kettler - -Simple Posix semaphore class -This is useful because the constructor will automatically call sem_init - and you don't have to worry about it. It also allows for other semaphore - libraries if you don't have posix. -\******************************************************************************/ - -#include "sem4.h" - -#ifdef _REENTRANT - -Sem4::Sem4() -{ -#ifndef _WINDOWS - sem_init(&sem,1,1); -#else - sem = CreateSemaphore(NULL, 1, 1, NULL); -#endif -} - -Sem4::Sem4(uint32 value) -{ -#ifndef _WINDOWS - sem_init(&sem,1,value); -#else - sem = CreateSemaphore(NULL, value, value, NULL); -#endif -} - -Sem4::~Sem4() -{ -#ifndef _WINDOWS - sem_destroy(&sem); -#else - if (sem) CloseHandle(sem); -#endif -} - -sint32 Sem4::Wait(void) const -{ -#ifndef _WINDOWS - return(sem_wait((sem_t *)&sem)); -#else - if (!sem) - return -1; // no semaphore! - - DWORD dwWaitResult = WaitForSingleObject(sem, INFINITE); - switch (dwWaitResult) { - case WAIT_OBJECT_0: // The semaphore object was signaled. - return 0; - break; - case WAIT_TIMEOUT: // Should not happen ;) - return -1; - break; - } - return -1; -#endif -} - -sint32 Sem4::Post(void) const -{ -#ifndef _WINDOWS - return(sem_post((sem_t *)&sem)); -#else - if (!sem) - return -1; - if (!ReleaseSemaphore(sem, 1 ,NULL)) - return -1; - return 0; -#endif -} - -sint32 Sem4::TryWait(void) const -{ -#ifndef _WINDOWS - return(sem_trywait((sem_t *)&sem)); -#else - if (!sem) - return -1; - DWORD dwWaitResult = WaitForSingleObject(sem, 0L); - switch (dwWaitResult) { - case WAIT_OBJECT_0: // The semaphore object was signaled. - return 0; - break; - case WAIT_TIMEOUT: - return -1; - break; - } - return -1; -#endif -} - -sint32 Sem4::GetValue(int *sval) const -{ -#ifndef _WINDOWS - return(sem_getvalue((sem_t *)&sem,sval)); -#else - if (!sem) - return -1; - long prev; - if (!ReleaseSemaphore(sem, 0, &prev)) - return -1; - if (sval) - *sval = prev; - return 0; -#endif -} - -sint32 Sem4::Destroy(void) -{ -#ifndef _WINDOWS - return(sem_destroy(&sem)); -#else - return CloseHandle(sem); -#endif -} - -#else - -/**************************************************************************** -non threaded versions that do nothing -*****************************************************************************/ - -Sem4::Sem4() -{ -} - -Sem4::Sem4(uint32) -{ -} - -Sem4::~Sem4() -{ -} - -sint32 Sem4::Wait(void) const -{ - return(0); -} - -sint32 Sem4::Post(void) const -{ - return(0); -} - -sint32 Sem4::TryWait(void) const -{ - return(0); -} - -sint32 Sem4::GetValue(int *) const -{ - return(0); -} - -sint32 Sem4::Destroy(void) -{ - return(0); -} - -#endif - diff --git a/Generals/Code/Tools/matchbot/wlib/sem4.h b/Generals/Code/Tools/matchbot/wlib/sem4.h deleted file mode 100644 index c96f2f7b786..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/sem4.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef SEM4_HEADER -#define SEM4_HEADER - -#include -#ifndef _WINDOWS -#include -#endif -#include "wstypes.h" - -#ifdef _REENTRANT -#ifndef _WINDOWS -#include -#else -#include -#endif // _WINDOWS -#endif // _REENTRANT - -// Windows headers have a tendency to redefine IN -#ifdef IN -#undef IN -#endif -#define IN const - -class Sem4 -{ - private: - #ifdef _REENTRANT -#ifndef _WINDOWS - sem_t sem; -#else - HANDLE sem; -#endif - #endif - public: - Sem4(); - Sem4(uint32 value); - ~Sem4(); - - sint32 Wait(void) const; - sint32 TryWait(void) const; - sint32 Post(void) const; - sint32 GetValue(int *sval) const; - sint32 Destroy(void); -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/stderrd.h b/Generals/Code/Tools/matchbot/wlib/stderrd.h deleted file mode 100644 index 4559bea747c..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/stderrd.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef STDERRD_HEADER -#define STDERRD_HEADER - -#include "odevice.h" - -class StderrD : public OutputDevice -{ - public: - - virtual int print(const char *str,int len) - { - char *string=new char[len+1]; - memset(string,0,len+1); - memcpy(string,str,len); - fprintf(stderr,"%s",string); - delete[](string); - return(len); - } -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/stdoutd.h b/Generals/Code/Tools/matchbot/wlib/stdoutd.h deleted file mode 100644 index a3d20fc3c58..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/stdoutd.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef STDOUTD_HEADER -#define STDOUTD_HEADER - -#include "odevice.h" - -class StdoutD : public OutputDevice -{ - public: - - virtual int print(const char *str,int len) - { - char *string=new char[len+1]; - memcpy(string,str,len); - string[len]=0; - fprintf(stdout,"%s",string); - fflush(stdout); - delete[](string); - return(len); - } -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/streamer.cpp b/Generals/Code/Tools/matchbot/wlib/streamer.cpp deleted file mode 100644 index b8a8eca1c1e..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/streamer.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include "streamer.h" -#ifdef _WIN32 - #include -#endif - -#if defined(USING_STLPORT) || (defined(_MSC_VER) && _MSC_VER < 1300) -#define STREAMER_UNBUFFERED unbuffered() -#else -#define STREAMER_UNBUFFERED 0 -#endif - -Streamer::Streamer() : streambuf(), Output_Device(NULL), Buf(NULL) -{ -#if defined(USING_STLPORT) || (defined(_MSC_VER) && _MSC_VER < 1300) - int state=unbuffered(); - unbuffered(0); // 0 = buffered, 1 = unbuffered -#else - static_assert(STREAMER_UNBUFFERED==0, "std::streambuf is assumed to be buffered by default"); -#endif -} - -Streamer::~Streamer() -{ - ///////// calling sync seems to cause crashes here on Win32 - //sync(); - ///////// - if (Buf) - delete[] Buf; -} - -int Streamer::setOutputDevice(OutputDevice *device) -{ - Output_Device=device; - return(0); -} - - -// put n chars from string into buffer -int Streamer::xsputn(const char* buf, int size) //implementation of sputn -{ - if (size<=0) // Nothing to do - return(0); - - const unsigned char *ptr=(const unsigned char *)buf; - for (int i=0; i= epptr()) && (sync()==EOF)) - return(EOF); - else { - sputc(c); - if ((STREAMER_UNBUFFERED && c=='\n' || pptr() >= epptr()) - && sync()==EOF) { - return(EOF); - } - return(c); - } -} - -// This is a write only stream, this should never happen -int Streamer::underflow(void) -{ - return(EOF); -} - -int Streamer::doallocate() -{ - if (Buf==NULL) - { - Buf=new char[(2*STREAMER_BUFSIZ)]; // deleted by destructor - memset(Buf,0,2*STREAMER_BUFSIZ); - - // Buffer -#if defined(USING_STLPORT) || (defined(_MSC_VER) && _MSC_VER < 1300) - setb( - Buf, // base pointer - Buf+STREAMER_BUFSIZ, // ebuf pointer (end of buffer); - 0); // 0 = manual deletion of buff -#else - pubsetbuf(Buf, 2*STREAMER_BUFSIZ); -#endif - - // Get area - setg( - Buf, // eback - Buf, // gptr - Buf); // egptr - - Buf+=STREAMER_BUFSIZ; - // Put area - setp(Buf,Buf+STREAMER_BUFSIZ); - return(1); - } - else - return(0); -} - - -int Streamer::sync() -{ - if (pptr()<=pbase()) { - return(0); - } - - int wlen=pptr()-pbase(); - - if (Output_Device) - { - Output_Device->print(pbase(),wlen); - } - - if (STREAMER_UNBUFFERED) { - setp(pbase(),pbase()); - } - else { - setp(pbase(),pbase()+STREAMER_BUFSIZ); - } - return(0); -} diff --git a/Generals/Code/Tools/matchbot/wlib/streamer.h b/Generals/Code/Tools/matchbot/wlib/streamer.h deleted file mode 100644 index 8dce9298f98..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/streamer.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef STREAMER_HEADER -#define STREAMER_HEADER - -#include -#include -#include -#include -#include - -// Windows headers have a tendency to redefine IN -#ifdef IN -#undef IN -#endif -#define IN const - -#include "odevice.h" - -#ifndef STREAMER_BUFSIZ -// This limits the number of characters that can be sent to a single 'print' -// call. If your debug message is bigger than this, it will get split over -// multiple 'print' calls. That's usually not a problem. - #define STREAMER_BUFSIZ 2048 -#endif - - -// Provide a streambuf interface for a class that can 'print' -class Streamer : public streambuf -{ - public: - Streamer(); - virtual ~Streamer(); - - int setOutputDevice(OutputDevice *output_device); - - protected: - // Virtual methods from streambuf - int xsputn(const char* s, int n); // buffer some characters - int overflow(int = EOF); // flush buffer and make more room - int underflow(void); // Does nothing - int sync(); - - int doallocate(); // allocate a buffer - - - OutputDevice *Output_Device; - char* Buf; -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/syslogd.cpp b/Generals/Code/Tools/matchbot/wlib/syslogd.cpp deleted file mode 100644 index 7568868b240..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/syslogd.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include "syslogd.h" - -SyslogD::SyslogD(char *ident,int logopt,int facility,int _priority) -{ -#ifndef _WINDOWS - openlog(ident,logopt,facility); - priority=_priority; -#endif -} - -int SyslogD::print(const char *str, int len) -{ -#ifndef _WINDOWS - char *temp_str=new char[len+1]; - memset(temp_str,0,len+1); - strncpy(temp_str,str,len); - syslog(priority,temp_str); - delete[](temp_str); -#endif - return(len); -} diff --git a/Generals/Code/Tools/matchbot/wlib/syslogd.h b/Generals/Code/Tools/matchbot/wlib/syslogd.h deleted file mode 100644 index 569f2b1d5bc..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/syslogd.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef SYSLOGD_HEADER -#define SYSLOGD_HEADER - -#include -#include -#ifndef _WINDOWS -#include -#endif -#include - -// Windows headers have a tendency to redefine IN -#ifdef IN -#undef IN -#endif -#define IN const - -#include "odevice.h" - -// Windows doesn't have a syslog equivalent (does it?), so this class does little there -class SyslogD : public OutputDevice -{ - public: - SyslogD(char *ident,int logopt,int facility,int priority); - virtual int print(const char *str,int len); - - private: - int priority; -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/threadfac.cpp b/Generals/Code/Tools/matchbot/wlib/threadfac.cpp deleted file mode 100644 index 0f42558536a..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/threadfac.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#define THREADFAC_CODE - -#include "threadfac.h" - -int Runnable::ThreadCount_ = 0; -CritSec Runnable::CritSec_; // to protect ThreadCount_ - -// MDC: Made all this dependent on _REENTRANT being defined so VC++ doesn't complain on -// single-threaded programs... - - -// -// Note: I chose the following type signature for thread functions -// void function(void *data); -// -// -// Since Win32 & POSIX have different type signatures for the thread entry points -// an intermediate system-dependent function in this file gets called first. -// That function then calls the system independent version. So the system dependent -// version needs 2 Items 1) The address of the _real_ thread func 2) the data -// to pass. We only have 1 argument available, so you figure out the rest... -// -// This is for internal use only -// -struct ThreadInformation -{ - void *startPoint; // The address of the _real_ thread function, or class - void *data; // data to pass to real thread function or class - bit8 destroy; // only applies to classes, should delete after execution? -}; - - - -// -// Start a thread inside a class -// -bit8 ThreadFactory::startThread(Runnable &runable, void *data, bit8 destroy) -{ -#ifdef _REENTRANT - - { - Runnable::CritSec_.lock(); - Runnable::ThreadCount_++; - Runnable::CritSec_.unlock(); - } - - - ThreadInformation *tInfo=new ThreadInformation; - tInfo->startPoint=(void *)&runable; - tInfo->data=data; - tInfo->destroy=destroy; - - #ifdef _WIN32 - // Under windows call _beginthreadex instead of CreateThread so you can - // use all the normal C library stuff. (IMPORTANT!!!) - uint32 handle; - uint32 stup1d; - handle=_beginthreadex(NULL,0, threadClassLauncher, tInfo, 0, &stup1d); - if (handle!=NULL) - return(TRUE); - else - { - { - runable.CritSec_.lock(); - runable.ThreadCount_--; // Ok, so it didn't really start - runable.CritSec_.unlock(); - } - return(FALSE); - } - #else // UNIX - // Setup thread attributes for client threads - int retval; - pthread_attr_t threadAttr; - pthread_attr_init(&threadAttr); - pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); - pthread_attr_setscope(&threadAttr,PTHREAD_SCOPE_SYSTEM); - retval=pthread_create(NULL,&threadAttr, threadClassLauncher, tInfo); - if (retval==0) - return(TRUE); - else - { - { - runable.CritSec_.lock(); - runable.ThreadCount_--; // Ok, so it didn't really start - runable.CritSec_.unlock(); - } - return(FALSE); - } - #endif -#else - return (FALSE); -#endif /* _REENTRANT */ -} - - -// -// Start a thread inside a function -// -bit8 ThreadFactory::startThread(void (*start_func)(void *), void *data) -{ -#ifdef _REENTRANT - ThreadInformation *tInfo=new ThreadInformation; - tInfo->startPoint=start_func; - tInfo->data=data; - - #ifdef _WIN32 - // Under windows call _beginthreadex instead of CreateThread so you can - // use all the normal C library stuff. (IMPORTANT!!!) - uint32 handle; - unsigned temp; - handle=_beginthreadex(NULL,0, threadFuncLauncher, tInfo, 0, &temp); - if (handle!=NULL) - return(TRUE); - return(FALSE); - #else // UNIX - // Setup thread attributes for client threads - int retval; - pthread_attr_t threadAttr; - pthread_attr_init(&threadAttr); - pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); - pthread_attr_setscope(&threadAttr,PTHREAD_SCOPE_SYSTEM); - retval=pthread_create(NULL,&threadAttr, threadFuncLauncher, tInfo); - if (retval==0) - return(TRUE); - else - return(FALSE); - #endif -#else - return(FALSE); -#endif /* REENTRANT */ -} - -#ifdef _WIN32 - unsigned __stdcall threadFuncLauncher(void *temp) -#else // UNIX - void *threadFuncLauncher(void *temp) -#endif -{ - ThreadInformation *tInfo=(ThreadInformation *)temp; - - void (*start_func)(void *); - start_func=(void (*)(void *))tInfo->startPoint; - - void *data=tInfo->data; - delete(tInfo); - - start_func(data); - return(0); -} - -#ifdef _WIN32 - unsigned __stdcall threadClassLauncher(void *temp) -#else // UNIX - void *threadClassLauncher(void *temp) -#endif -{ - ThreadInformation *tInfo=(ThreadInformation *)temp; - - Runnable *thrClass=(Runnable *)tInfo->startPoint; - void *data=tInfo->data; - bit8 destroy=tInfo->destroy; - delete(tInfo); - - thrClass->run(data); - - if (destroy) // May want to free memory after thread finishes - delete(thrClass); - - { - Runnable::CritSec_.lock(); - Runnable::ThreadCount_--; - Runnable::CritSec_.unlock(); - } - - #ifdef _WIN32 - ExitThread(0); // is this really needed? - #endif - return(0); -} - -Runnable::Runnable() -{ } - -Runnable::~Runnable() -{ } - -// Is there a thread running in this class -bit8 Runnable::isRunning(void) -{ - // Don't need to lock a simple assignment - int temp=ThreadCount_; - return((temp>0)?TRUE:FALSE); -} - -// How many threads are running in this class -int Runnable::getThreadCount(void) -{ - // Don't need to lock a simple assignment - int temp=ThreadCount_; - return(temp); -} - -#undef THREADFAC_CODE diff --git a/Generals/Code/Tools/matchbot/wlib/threadfac.h b/Generals/Code/Tools/matchbot/wlib/threadfac.h deleted file mode 100644 index a4edbc36804..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/threadfac.h +++ /dev/null @@ -1,124 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -// -// Platform independent thread creation (Win32 & POSIX) -// - -#ifndef THREADFAC_HEADER -#define THREADFAC_HEADER - -#ifdef _WIN32 - #include -#endif -#include "wstypes.h" -#include - -#ifdef _WIN32 - #include -#else // UNIX - #include -#endif - -// Windows headers have a tendency to redefine IN -#ifdef IN -#undef IN -#endif -#define IN const - -#include "critsec.h" - - - -#ifdef THREADFAC_CODE - // This is the fake thread entry point for functions - #ifdef _WIN32 - static unsigned __stdcall threadFuncLauncher(void *temp); - #else // UNIX - static void *threadFuncLauncher(void *temp); - #endif - - // Fake entry point for classes - #ifdef _WIN32 - static unsigned __stdcall threadClassLauncher(void *temp); - #else // UNIX - static void *threadClassLauncher(void *temp); - #endif -#endif - - - - - -// Forward definition of base class for threaded classes -class Runnable; - -// -// Call the static method startThread to begin a new thread. -// -class ThreadFactory -{ - public: - static bit8 startThread(void (*start_func)(void *), void *data); - static bit8 startThread(Runnable &runable, void *data, bit8 destroy=FALSE); -}; - - - -// -// Base class for when you want a thread to execute inside a class -// instead of a function. -// -class Runnable -{ - public: - Runnable(); - virtual ~Runnable(); - - - // ThreadFactory needs to be able to access the private - // IsRunning_ field. - friend class ThreadFactory; - - // So do the threadClassLaunchers - #ifdef _WIN32 - friend static unsigned __stdcall threadClassLauncher(void *temp); - #else // UNIX - friend void *threadClassLauncher(void *temp); - #endif - - virtual void run(void *data)=0; // Thread entry point - - void startThread(void *data,bit8 destroy=FALSE) // nice way to start a thread - { - ThreadFactory::startThread(*this,data,destroy); - }; - - // Is there a thread running in this class? - static bit8 isRunning(void); - - // Get the count of threads running inside this class - static int getThreadCount(); - - - private: - static int ThreadCount_; - static CritSec CritSec_; // to protect ThreadCount_ -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/threadsafe.h b/Generals/Code/Tools/matchbot/wlib/threadsafe.h deleted file mode 100644 index 8a2addac60f..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/threadsafe.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -// -// threadsafe.h -// -// If you include this file and call non-threadsafe functions, it'll -// prevent your program from compiling. It's meant to be a slap -// on the wrist in case you forget and call an unsafe function -// from a threadsafe program. -// - -// -// Reminder to self - use sigwait, not signal handlers! -// - -#ifdef _REENTRANT - -#ifndef THREADSAFE_HEADER -#define THREADSAFE_HEADER - -#define strtok ("strtok() is not MT-SAFE!") -#define ascctime ("asctime() is not MT-SAFE!") -// Can't just do ctime, as Windows' objidl.h uses it as a FILETIME thingie -#define ctime(x) ("ctime() is not MT-SAFE!") -#define gmtime ("gmtime() is not MT-SAFE!") -#define localtime ("localtime() is not MT-SAFE!") -#define tzset ("tzset() is not MT-SAFE!") -#define tzsetwall ("tzsetwall() is not MT-SAFE!") -#define readdir ("readdir() is not MT-SAFE!") - -#define rand ("rand() is not MT-SAFE!") -#define srand ("srand() is not MT-SAFE!") -#define random ("random() is not MT-SAFE!") -#define srandom ("srandom() is not MT-SAFE!") - -#define tmpnam ("tmpnam() is not MT-SAFE!") -#define vfork ("vfork() is not MT-SAFE!") - -#define system ("system() is not MT-SAFE!") -#define popen ("popen() is not MT-SAFE!") -#define pclose ("pclose() is not MT-SAFE!") -#define ctermid ("ctermid() is not MT-SAFE!") -#define getlogin ("getlogin() is not MT-SAFE!"); - -#endif // THREADSAFE_HEADER -#endif // _REENTRANT diff --git a/Generals/Code/Tools/matchbot/wlib/timezone.cpp b/Generals/Code/Tools/matchbot/wlib/timezone.cpp deleted file mode 100644 index 6e191ebb17f..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/timezone.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include "wlib/xtime.h" -#include "timezone.h" - -void GetTimezoneInfo(const char * &timezone_str, int &timezone_offset) { - timezone_str = "Unknown Timezone"; - timezone_offset = 0; -#ifdef _WINDOWS - struct _timeb wintime; - _ftime(&wintime); - - if (wintime.dstflag) { - // Daylight savings time - if (_daylight) { - timezone_str = _tzname[1]; - } - } else { - timezone_str = _tzname[0]; - } - timezone_offset = wintime.timezone * 60; // its in minutes... - -#endif -#ifndef _WINDOWS - struct timeval unixtime; - struct timezone unixtzone; - gettimeofday(&unixtime,&unixtzone); - - struct tm unixtm; - localtime_r(&unixtime.tv_sec, &unixtm); - - if (unixtm.tm_isdst) { - // Daylight savings time - if (daylight) timezone_str = tzname[1]; - timezone_offset = altzone; - } else { - timezone_str = tzname[0]; - timezone_offset = timezone; - } -#endif -} - -const char * TimezoneString(void) { - const char *timezone_str; - int timezone_offset; - GetTimezoneInfo(timezone_str, timezone_offset); - return timezone_str; -} - -int TimezoneOffset(void) { - const char *timezone_str; - int timezone_offset; - GetTimezoneInfo(timezone_str, timezone_offset); - return timezone_offset; -} diff --git a/Generals/Code/Tools/matchbot/wlib/timezone.h b/Generals/Code/Tools/matchbot/wlib/timezone.h deleted file mode 100644 index 95e4c37f384..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/timezone.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -timezone Matthew D. Campbell - -This is just a couple of convenience functions for determining what timezone -we are now in. It even accounts for daylight savings! One caveat is that it -only tells you info about what the daylight savings info is now, not 5 minutes -from now, not 2 hours ago. Oh well. -\****************************************************************************/ - -#ifndef _TIMEZONE_H_ -#define _TIMEZONE_H_ - -// Just fill in both the timezone description and its offset from GMT -void GetTimezoneInfo(const char * &timezone_str, int &timezone_offset); - -// Returns the description of the current timezone (daylight savings included) -const char * TimezoneString(void); - -// Returns the offset from GMT of the current timezone -int TimezoneOffset(void); - -#endif // _TIMEZONE_H_ - diff --git a/Generals/Code/Tools/matchbot/wlib/ustring.h b/Generals/Code/Tools/matchbot/wlib/ustring.h deleted file mode 100644 index 12fee83b683..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/ustring.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#ifndef USTRING_HEADER -#define USTRING_HEADER - -#include -#include -#include -#include - -// Windows headers have a tendency to redefine IN -#ifdef IN -#undef IN -#endif -#define IN const - -#define MAX_BYTES_PER_CHAR 1 - -template -class UstringT : public basic_string > -{ - public: - explicit UstringT(int max_charlength) { - set_max_bytelength(max_charlength*MAX_BYTES_PER_CHAR); - } - - UstringT() { max_bytelength=4000; } - - size_t get_max_bytelength(void) { return(max_bytelength); } - void set_max_bytelength(size_t max) { max_bytelength=max; } - - bool operator==(const UstringT &other) - { - const basic_string > *other_basic=&other; - const basic_string > *this_basic=this; - return((*other_basic)==(*this_basic)); - } - - private: - size_t max_bytelength; -}; - -typedef UstringT Ustring; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/wdebug.cpp b/Generals/Code/Tools/matchbot/wlib/wdebug.cpp deleted file mode 100644 index 8e2d14f7e47..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/wdebug.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include -#include "wdebug.h" -#include "streamer.h" -#include "odevice.h" - - -static MsgManager *msg_manager=NULL; - -static int debug_enabled=0; -static ostream *debug_ostream=NULL; -static Streamer debug_streamer; - -static int info_enabled=0; -static ostream *info_ostream=NULL; -static Streamer info_streamer; - -static int warn_enabled=0; -static ostream *warn_ostream=NULL; -static Streamer warn_streamer; - -static int error_enabled=0; -static ostream *error_ostream=NULL; -static Streamer error_streamer; - - -// Don't dare touch this semaphore in application code! -#ifdef USE_DEBUG_SEM -Sem4 DebugLibSemaphore; -#else -CritSec DebugLibSemaphore; -#endif - - -int MsgManager::setAllStreams(OutputDevice *device) -{ - if (device==NULL) - return(1); - - DEBUGLOCK; - debug_streamer.setOutputDevice(device); - delete(debug_ostream); - debug_ostream=new ostream(&debug_streamer); - - info_streamer.setOutputDevice(device); - delete(info_ostream); - info_ostream=new ostream(&info_streamer); - - warn_streamer.setOutputDevice(device); - delete(warn_ostream); - warn_ostream=new ostream(&warn_streamer); - - error_streamer.setOutputDevice(device); - delete(error_ostream); - error_ostream=new ostream(&error_streamer); - - DEBUGUNLOCK; - - return(0); -} - - -int MsgManager::ReplaceAllStreams(FileD * output_device, IN char *device_filename, IN char *copy_filename) -{ - DebugLibSemaphore.Wait(); - - delete(debug_ostream); - delete(info_ostream); - delete(warn_ostream); - delete(error_ostream); - - if (output_device != NULL) - { - delete(output_device); - output_device = NULL; - } - - rename(device_filename, copy_filename); - -// FileD new_device(device_filename); - output_device = new FileD(device_filename); - - debug_streamer.setOutputDevice(output_device); - debug_ostream = new ostream(&debug_streamer); - - info_streamer.setOutputDevice(output_device); - info_ostream=new ostream(&info_streamer); - - warn_streamer.setOutputDevice(output_device); - warn_ostream = new ostream(&warn_streamer); - - error_streamer.setOutputDevice(output_device); - error_ostream = new ostream(&error_streamer); - - DebugLibSemaphore.Post(); - - return(0); -} - - -int MsgManager::setDebugStream(OutputDevice *device) -{ - if (device==NULL) - return(1); - - DEBUGLOCK; - debug_streamer.setOutputDevice(device); - delete(debug_ostream); - debug_ostream=new ostream(&debug_streamer); - DEBUGUNLOCK; - return(0); -} - -int MsgManager::setInfoStream(OutputDevice *device) -{ - if (device==NULL) - return(1); - - DEBUGLOCK; - info_streamer.setOutputDevice(device); - delete(info_ostream); - info_ostream=new ostream(&info_streamer); - DEBUGUNLOCK; - return(0); -} - -int MsgManager::setWarnStream(OutputDevice *device) -{ - if (device==NULL) - return(1); - - DEBUGLOCK; - warn_streamer.setOutputDevice(device); - delete(warn_ostream); - warn_ostream=new ostream(&warn_streamer); - DEBUGUNLOCK; - return(0); -} - -int MsgManager::setErrorStream(OutputDevice *device) -{ - if (device==NULL) - return(1); - - DEBUGLOCK; - error_streamer.setOutputDevice(device); - delete(error_ostream); - error_ostream=new ostream(&error_streamer); - DEBUGUNLOCK; - return(0); -} - - - -ostream *MsgManager::debugStream(void) -{ - return(debug_ostream); -} - -ostream *MsgManager::infoStream(void) -{ - return(info_ostream); -} - -ostream *MsgManager::warnStream(void) -{ - return(warn_ostream); -} - -ostream *MsgManager::errorStream(void) -{ - return(error_ostream); -} diff --git a/Generals/Code/Tools/matchbot/wlib/wdebug.h b/Generals/Code/Tools/matchbot/wlib/wdebug.h deleted file mode 100644 index a37c9ee75ff..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/wdebug.h +++ /dev/null @@ -1,314 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/*****************************************************************************\ -wdebug Neal Kettler - -MT-LEVEL - MT-Safe - -The debugging module is pretty good for debugging and it has some message -printing stuff as well. The basic idea is that you write a class that -inherits from OutputDevice (several are provided) and assign that output -device to a stream. There are seperate streams for debugging, information, -warning, and error messages. Each one can have a seperate output device, -or they can all have the same one. Debugging messages only get compiled -in if your module defines 'DEBUG'. If you don't define debug, then not even -the text of the debugging message gets into the binary. All the other -output streams get printed regardless of whether DEBUG is defined. - -Sample usage: -FileD debug_device("gameres.debug"); // create a file device -MsgManager::setDebugStream(&debug_device); -DBGMSG("This debug message #" << 1 << " you use C++ streams"); - -Note that since these are defines you really don't need to put a semicolon -at the end, and it can be bad in situations like this: - -if (x) - DBGMSG("Stuff is broken"); -else - DBGMSG("Stuff is NOT broken"); - -This won't compile, read the code until you figure it out. Only then -will you be ready to leave grasshopper. - -\*****************************************************************************/ - -#ifndef WDEBUG_HEADER -#define WDEBUG_HEADER - -#define USE_DEBUG_SEM - -#include "wstypes.h" - -#include -#include - -#if !defined(_WINDOWS) -// Windows headers have a tendency to redefine IN -#ifdef IN -#undef IN -#endif -#define IN const -#endif - -#ifdef USE_DEBUG_SEM -#include "sem4.h" -#else -#include "critsec.h" -#endif -#include "odevice.h" -#include "streamer.h" -#include "xtime.h" -#include "timezone.h" // MDC -#include "filed.h" - -// This is needed because the streams return a pointer. Every time you -// change the output device the old stream is deleted, and a new one -// is created. -// MDC: Added a macro to switch between semaphores & critsecs to debug a -// problem in Win32. - -#ifdef USE_DEBUG_SEM -extern Sem4 DebugLibSemaphore; -#define DEBUGLOCK DebugLibSemaphore.Wait() -#define DEBUGUNLOCK DebugLibSemaphore.Post() -#else -extern CritSec DebugLibSemaphore; -#define DEBUGLOCK DebugLibSemaphore.lock() -#define DEBUGUNLOCK DebugLibSemaphore.unlock() -#endif - -// Print an information message -#define INFMSG(X)\ -{\ - char timebuf[40]; \ - Xtime now; \ - now -= TimezoneOffset(); \ - now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \ - DEBUGLOCK; \ - if (MsgManager::infoStream()) \ - (*(MsgManager::infoStream())) << "INF " << timebuf << " [" << \ - __FILE__ << " " << __LINE__ << "] " << X << endl; \ - DEBUGUNLOCK; \ -} - -// Print a warning message -#define WRNMSG(X)\ -{\ - char timebuf[40]; \ - Xtime now; \ - now -= TimezoneOffset(); \ - now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \ - DEBUGLOCK; \ - if (MsgManager::warnStream()) \ - (*(MsgManager::warnStream())) << "WRN " << timebuf << " [" << \ - __FILE__ << " " << __LINE__ << "] " << X << endl; \ - DEBUGUNLOCK; \ -} - -// Print an error message -#define ERRMSG(X)\ -{\ - char timebuf[40]; \ - Xtime now; \ - now -= TimezoneOffset(); \ - now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \ - DEBUGLOCK; \ - if (MsgManager::errorStream()) \ - (*(MsgManager::errorStream())) << "ERR " << timebuf << " [" << \ - __FILE__ << " " << __LINE__ << "] " << X << endl; \ - DEBUGUNLOCK; \ -} - - -// Just get a stream to the information device, no extra junk -#define INFSTREAM(X)\ -{\ - DEBUGLOCK; \ - if (MsgManager::infoStream()) \ - (*(MsgManager::infoStream())) << X;\ - DEBUGUNLOCK; \ -} - -// Just get a stream to the warning device, no extra junk -#define WRNSTREAM(X)\ -{\ - DEBUGLOCK; \ - if (MsgManager::warnStream()) \ - (*(MsgManager::warnStream())) << X;\ - DEBUGUNLOCK; \ -} - -// Just get a stream to the error device, no extra junk -#define ERRSTREAM(X)\ -{\ - DEBUGLOCK; \ - if (MsgManager::errorStream()) \ - (*(MsgManager::errorStream())) << X;\ - DEBUGUNLOCK; \ -} - -#ifndef DEBUG - -// No debugging, no debug messages. -// Note that anything enclosed in "DBG()" will NOT get executed -// unless DEBUG is defined. -// They are defined to {} for consistency when DEBUG is defined - -#define DBG(X) -#define DBGSTREAM(X) {} -#define PVAR(v) {} -#define DBGMSG(X) {} -#define VERBOSE(X) {} - -#else // DEBUG _is_ defined - -// Execute only if in debugging mode -#define DBG(X) X - -// In Windows, send a copy to the debugger window -#ifdef _WINDOWS - -// Print a variable -#define PVAR(v) \ -{ \ - DEBUGLOCK; \ - if (MsgManager::debugStream()) \ - (*(MsgManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ - "]: " << ##V << " = " << V << endl; \ - strstream __s;\ - __s << __FILE__ << "[" << __LINE__ << \ - "]: " << ##V << " = " << V << '\n' << '\0';\ - OutputDebugString(STRSTREAM_CSTR(__s));\ - DEBUGUNLOCK; \ -} - - -#define DBGMSG(X)\ -{\ - DEBUGLOCK; \ - if (MsgManager::debugStream()) \ - (*(MsgManager::debugStream())) << "DBG [" << __FILE__ << \ - " " << __LINE__ << "] " << X << endl;\ - strstream __s;\ - __s << "DBG [" << __FILE__ << \ - " " << __LINE__ << "] " << X << '\n' << '\0';\ - OutputDebugString(STRSTREAM_CSTR(__s));\ - DEBUGUNLOCK; \ -} - -// Just get a stream to the debugging device, no extra junk -#define DBGSTREAM(X)\ -{\ - DEBUGLOCK; \ - if (MsgManager::debugStream()) \ - (*(MsgManager::debugStream())) << X;\ - strstream __s;\ - __s << X << '\0';\ - OutputDebugString(STRSTREAM_CSTR(__s));\ - DEBUGUNLOCK; \ -} - -// Verbosely execute a statement -#define VERBOSE(X)\ -{ \ - DEBUGLOCK; \ - if (MsgManager::debugStream()) \ - (*(DebugManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ - "]: " << ##X << endl; X \ - strstream __s;\ - __s << __FILE__ << "[" << __LINE__ << \ - "]: " << ##X << '\n' << '\0';\ - OutputDebugString(STRSTREAM_CSTR(__s));\ - DEBUGUNLOCK; \ -} - -#else // _WINDOWS - -// Print a variable -#define PVAR(v) \ -{ \ - DEBUGLOCK; \ - if (MsgManager::debugStream()) \ - (*(MsgManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ - "]: " << ##V << " = " << V << endl; \ - DEBUGUNLOCK; \ -} - - -#define DBGMSG(X)\ -{\ - DEBUGLOCK; \ - if (MsgManager::debugStream()) \ - (*(MsgManager::debugStream())) << "DBG [" << __FILE__ << \ - " " << __LINE__ << "] " << X << endl;\ - DEBUGUNLOCK; \ -} - -// Just get a stream to the debugging device, no extra junk -#define DBGSTREAM(X)\ -{\ - DEBUGLOCK; \ - if (MsgManager::debugStream()) \ - (*(MsgManager::debugStream())) << X;\ - DEBUGUNLOCK; \ -} - -// Verbosely execute a statement -#define VERBOSE(X)\ -{ \ - DEBUGLOCK; \ - if (MsgManager::debugStream()) \ - (*(DebugManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ - "]: " << ##X << endl; X \ - DEBUGUNLOCK; \ -} -#endif // _WINDOWS - -#endif // DEBUG - -//#undef DEBUGLOCK -//#undef DEBUGUNLOCK - -class MsgManager -{ - protected: - MsgManager(); - - public: - static int setAllStreams(OutputDevice *device); - static int ReplaceAllStreams(FileD *output_device, IN char *device_filename, IN char *copy_filename); - static int setDebugStream(OutputDevice *device); - static int setInfoStream(OutputDevice *device); - static int setWarnStream(OutputDevice *device); - static int setErrorStream(OutputDevice *device); - - static void enableDebug(int flag); - static void enableInfo(int flag); - static void enableWarn(int flag); - static void enableError(int flag); - - static ostream *debugStream(void); - static ostream *infoStream(void); - static ostream *warnStream(void); - static ostream *errorStream(void); -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/wstring.cpp b/Generals/Code/Tools/matchbot/wlib/wstring.cpp deleted file mode 100644 index e7fa32cff35..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/wstring.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * -****************************************************************************** -Project Name: Carpenter (The RedAlert ladder creator) -File Name : string.cpp -Author : Neal Kettler -Start Date : June 1, 1997 -Last Update : June 17, 1997 - -A fairly typical string class. This string class always copies any input -string to it's own memory (for assignment or construction). -\***************************************************************************/ - -#include -#include -#include -#include -#include - -#include "wstring.h" - -#define PADSIZE 32 // include a little padding on alloc for future growth - -Wstring::Wstring() : str(NULL), strsize(0) -{ } - -Wstring::Wstring(const char *string):str(NULL), strsize(0) -{ set(string); } - -Wstring::Wstring(const Wstring &other):str(NULL), strsize(0) -{ - if (other.str!=NULL) - { - str=new char[strlen(other.str)+PADSIZE+1]; - strsize=strlen(other.str)+PADSIZE+1; - strcpy(str,other.str); - } -} - -Wstring::~Wstring() -{ - clear(); -} - -bool Wstring::operator<(const Wstring &other) const -{ - if (str == NULL && other.str == NULL) - return false; - - if (str == NULL) - return true; - - return ( strcmp(str, other.str) < 0 ); -} - -bit8 Wstring::operator==(const char *other) const -{ - if ((str==NULL)&&(other==NULL)) - return(TRUE); - if(strcmp(str, other) != 0) - return(FALSE); - else - return(TRUE); -} - -bit8 Wstring::operator==(const Wstring &other) const -{ - if((str == NULL) && (other.str == NULL)) - return(TRUE); - - if((str == NULL) || (other.str == NULL)) - return(FALSE); - - if(strcmp(str, other.str) != 0) - return(FALSE); - else - return(TRUE); -} - - -bit8 Wstring::operator!=(const char *other) const -{ - if(strcmp(str, other) != 0) - return(TRUE); - else - return(FALSE); -} - - -bit8 Wstring::operator!=(const Wstring &other) const -{ - if((str == NULL) && (other.str == NULL)) - return(FALSE); - - if((str == NULL) || (other.str == NULL)) - return(TRUE); - - if(strcmp(str, other.str) != 0) - return(TRUE); - else - return(FALSE); -} - - -Wstring &Wstring::operator=(const char *other) -{ - set(other); - return(*this); -} - - -Wstring &Wstring::operator=(const Wstring &other) -{ - if(*this == other) - return(*this); - - set(other.get()); - return(*this); -} - - -bit8 Wstring::cat(const char *s) -{ - uint32 len; - - if (s==NULL) // it's OK to cat nothing - return(TRUE); - - // Determine the length of the resultant string. - len = strlen(s) + 1; - if(str) - len += strlen(str); - - // Space check - strgrow(len); - - strcat(str, s); - - return(TRUE); -} - - -bit8 Wstring::cat(uint32 size, const char *s) -{ - uint32 len; - - // Determine the length of the resultant string. - len = size + 1; - if(str) - len += strlen(str); - - // Allocate memory for the new string. - strgrow(len); - - strncat(str, s, size); - str[len-1]=0; // make sure null term'd - - return(TRUE); -} - -bit8 Wstring::cat(const Wstring &other) -{ - return cat(other.get()); -} - -Wstring &Wstring::operator+=(const char *string) -{ - cat(string); - return(*this); -} - -Wstring &Wstring::operator+=(const Wstring &other) -{ - cat(other.get()); - return(*this); -} - -Wstring Wstring::operator+(const char *string) -{ - Wstring temp = *this; - temp.cat(string); - return(temp); -} - -Wstring Wstring::operator+(const Wstring &s) -{ - Wstring temp = *this; - temp.cat(s); - return(temp); -} - -// -// This function deletes 'count' characters indexed by `pos' from the Wstring. -// If `pos'+'count' is > the length of the array, the last 'count' characters -// of the string are removed. If an error occurs, FALSE is returned. -// Otherwise, TRUE is returned. Note: count has a default value of 1. -// -// -char Wstring::remove(sint32 pos,sint32 count) -{ - //char *s; - sint32 len; - - len = (sint32)strlen(str); - - if(pos+count > len) - pos = len - count; - if (pos < 0) - { - count+=pos; // If they remove before 0, ignore up till beginning - pos=0; - } - if (count<=0) - return(FALSE); - - memmove(str+pos,str+pos+count,len-pos-count+1); - - return(TRUE); -} - -// Remove all instances of a char from the string -bit8 Wstring::removeChar(char c) -{ - int len=0; - char *cptr=NULL; - bit8 removed=FALSE; - - if (str==NULL) - return(FALSE); - - len=strlen(str); - while ((cptr=strchr(str,c)) !=NULL) - { - memmove(cptr,cptr+1,len-1-((int)(cptr-str))); - len--; - str[len]=0; - removed=TRUE; - } - return(removed); -} - -void Wstring::removeSpaces(void) -{ - removeChar(' '); - removeChar('\t'); -} - -void Wstring::clear(void) -{ - if(str) - delete[](str); - strsize=0; - str=NULL; -} - -// This is usually used for raw storage instead of string ops... -void Wstring::setSize(sint32 size) -{ - clear(); - if (size<0) - return; - - str=new char[size]; - strsize=size; - memset(str,0,size); -} - -void Wstring::cellCopy(char *dest, uint32 len) -{ - uint32 i; - - strncpy(dest, str, len); - for(i = (uint32)strlen(str); i < len; i++) - dest[i] = ' '; - dest[len] = 0; -} - -const char *Wstring::get(void) const -{ - if(!str) - return ""; - return str; -} - -char Wstring::get(uint32 index) const -{ - if(index < strlen(str)) - return str[index]; - return(0); -} - -uint32 Wstring::length(void) const -{ - if(str == NULL) - return(0); - return((uint32)strlen(str)); -} - - -// Insert at given position and shift old stuff to right -bit8 Wstring::insert(const char *instring, uint32 pos) -{ - if (str==NULL) - return(set(instring)); - if (pos>strlen(str)) - pos=strlen(str); - - strgrow(strlen(str)+strlen(instring)+1); - memmove(str+pos+strlen(instring),str+pos,strlen(str)-pos+1); - memmove(str+pos,instring,strlen(instring)); - - return(TRUE); -} - -// This function inserts the character specified by `k' into the string at the -// position indexed by `pos'. If `pos' is >= the length of the string, it is -// appended to the string. If an error occurs, FALSE is returned. Otherwise, -// TRUE is returned. -bit8 Wstring::insert(char k, uint32 pos) -{ - char temp[2]; - temp[0]=k; - temp[1]=0; - return(insert(temp,pos)); -} - - -// Joe Howes (05/19/2000): This function inserts commas to nicely format a -// large number (i.e. 1234567890 -> 1,234,567,890). It doesn't really care -// if the string is really a number or not. -bit8 Wstring::beautifyNumber() -{ - int len = length(); - int accum = 3 - (len % 3); - int numcommas = 0; - - - if( accum == 3 ) accum = -1; - for(int i = 0; i < len; i++) - { - if( accum == 3 ) - { - insert(',', i + numcommas); - numcommas++; - } - accum = ( accum == 3 || accum == -1 ) ? 1 : accum + 1; - } - - return(TRUE); -} - - -// This function replaces any occurences of the string pointed to by -// `replaceThis' with the string pointed to by `withThis'. If an error -// occurs, FALSE is returned. Otherwise, TRUE is returned. -bit8 Wstring::replace(const char *replaceThis,const char *withThis) -{ - Wstring dest; - const char *foundStr, *src; - uint32 len; - - src=get(); - while(src && src[0]) - { - foundStr = strstr(src, replaceThis); - if(foundStr) - { - len = (uint32)foundStr - (uint32)src; - if(len) - { - if(!dest.cat(len, src)) - return(FALSE); - } - if(!dest.cat(withThis)) - return(FALSE); - src = foundStr + strlen(replaceThis); - } - else - { - if(!dest.cat(src)) - return(FALSE); - - src=NULL; - } - } - return(set(dest.get())); -} - - -bit8 Wstring::set(const char *s) -{ - //uint32 len; - - strgrow(strlen(s)+1); - strcpy(str,s); - - return(TRUE); -} - - -bit8 Wstring::set(char c, uint32 index) -{ - if(index >= (uint32)strlen(str)) - return FALSE; - - str[index] = c; - - return TRUE; -} - - -char Wstring::set(uint32 size, const char *string) -{ - //uint32 len; - - strgrow(size+1); - strncpy(str,string,size); - str[size]=0; - - return(TRUE); -} - - -// Added by Joe Howes. Takes a printf formatted string and a set of args. -// The expanded string must not exceed 1k or twice the length of the format -// string, whichever is larger. It would probably be better to traverse -// the format string and properly calculate, the length so this will -// work in all cases, but this should be good enough for 99% of Wstring usage. -char Wstring::setFormatted(const char *msg, ...) -{ - if( msg == NULL || strlen(msg) <= 0 ) return FALSE; - - char* string; - va_list args; - int len = (strlen(msg) < 1024) ? 1024 : (strlen(msg)*2); - - string = new char[len]; - va_start(args, msg); - vsprintf(string, msg, args); - va_end(args); - set(string); - delete[] string; - return(TRUE); -} - - -// This function converts all alphabetical characters in the string to lower -// case. -void Wstring::toLower(void) -{ - uint32 i; - int strlength=length(); - - for(i = 0; i < (uint32)strlength; i++) - { - if((str[i] >= 'A') && (str[i] <= 'Z')) - str[i] = (sint8)tolower(str[i]); - } -} - - -// This function converts all alphabetical characters in the string to upper -// case. -void Wstring::toUpper(void) -{ - uint32 i; - int strlength=length(); - - for(i = 0; i < (uint32)strlength; i++) - { - if((str[i] >= 'a') && (str[i] <= 'z')) - str[i] = (sint8)toupper(str[i]); - } -} - - -// This function truncates the string so its length will match the specified -// `len'. If an error occurs, FALSE is returned. Otherwise, TRUE is returned. -bit8 Wstring::truncate(uint32 len) -{ - strgrow(len+1); - str[len]=0; - return(TRUE); -} - -// Truncate the string after the character 'c' (gets rid of 'c' as well) -// Do nothing if 'c' isn't in the string -bit8 Wstring::truncate(char c) -{ - sint32 len; - - if (str==NULL) - return(FALSE); - - char *cptr=strchr(str,c); - if (cptr==NULL) - return(FALSE); - len=(sint32)(cptr-str); - truncate((uint32)len); - return(TRUE); -} - -// Get a token from this string that's seperated by one or more -// chars from the 'delim' string , start at offset & return offset -sint32 Wstring::getToken(int offset,const char *delim,Wstring &out) const -{ - int i; - sint32 start; - sint32 stop; - - if (offset<0) // check for bad input - return(-1); - - for (i=offset; i<(int)length(); i++) { - if(strchr(delim,str[i])==NULL) - break; - } - if (i>=(int)length()) - return(-1); - start=i; - - for (; i<(int)length(); i++) { - if(strchr(delim,str[i])!=NULL) - break; - } - stop=i-1; - out.set(str+start); - out.truncate((uint32)stop-start+1); - return(stop+1); -} - -// Get the first line of text after offset. Lines are terminated by '\r\n' or '\n' -sint32 Wstring::getLine(int offset, Wstring &out) -{ - int i; - sint32 start; - sint32 stop; - - start=i=offset; - if (start >= (sint32)length()) - return(-1); - - for (; i<(int)length(); i++) { - if(strchr("\r\n",str[i])!=NULL) - break; - } - stop=i; - if ((str[stop]=='\r')&&(str[stop+1]=='\n')) - stop++; - - out.set(str+start); - out.truncate((uint32)stop-start+1); - return(stop+1); -} - -// -// Make sure there's AT LEAST length bytes in this string object -// -void Wstring::strgrow(int length) -{ - if (str==NULL) - { - str=new char[length+PADSIZE]; - str[0]=0; - strsize=length+PADSIZE; - return; - } - else if (strsize >= length) // no need to alloc more data - return; - else // bah, gotta grow... - { - char *newstr=new char[length+PADSIZE]; - strsize=length+PADSIZE; - strcpy(newstr,str); - delete[](str); - str=newstr; - return; - } -} diff --git a/Generals/Code/Tools/matchbot/wlib/wstring.h b/Generals/Code/Tools/matchbot/wlib/wstring.h deleted file mode 100644 index 484815c1bac..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/wstring.h +++ /dev/null @@ -1,93 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * -****************************************************************************** -Project Name: Carpenter (The RedAlert ladder creator) -File Name : main.cpp -Author : Neal Kettler -Start Date : June 1, 1997 -Last Update : June 17, 1997 -\****************************************************************************/ - -#ifndef WSTRING_HEADER -#define WSTRING_HEADER - -#include -#include -#include "wstypes.h" - -class Wstring -{ - public: - Wstring(); - Wstring(const Wstring &other); - Wstring(const char *string); - ~Wstring(); - - void clear(void); - - bit8 cat(const char *string); - bit8 cat(uint32 size,const char *string); - bit8 cat(const Wstring &string); - - void cellCopy(OUT char *dest, uint32 len); - char remove(sint32 pos, sint32 count); - bit8 removeChar(char c); - void removeSpaces(void); - const char *get(void) const; - char get(uint32 index) const; - uint32 length(void) const; - bit8 insert(char c, uint32 pos); - bit8 insert(const char *instring, uint32 pos); - bit8 beautifyNumber(); - bit8 replace(const char *replaceThis,const char *withThis); - char set(const char *str); - char set(uint32 size,const char *str); - bit8 set(char c, uint32 index); - char setFormatted(const char *str, ...); // Added by Joe Howes - void setSize(sint32 bytes); // create an empty string - void toLower(void); - void toUpper(void); - bit8 truncate(uint32 len); - bit8 truncate(char c); // trunc after char c - sint32 getToken(int offset,const char *delim,Wstring &out) const; - sint32 getLine(int offset, Wstring &out); - void strgrow(int length); - - bit8 operator==(const char *other) const; - bit8 operator==(const Wstring &other) const; - bit8 operator!=(const char *other) const; - bit8 operator!=(const Wstring &other) const; - - Wstring &operator=(const char *other); - Wstring &operator=(const Wstring &other); - Wstring &operator+=(const char *other); - Wstring &operator+=(const Wstring &other); - Wstring operator+(const char *other); - Wstring operator+(const Wstring &other); - - bool operator<(const Wstring &other) const; - - private: - char *str; // Pointer to allocated string. - int strsize; // allocated data length -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/wstypes.h b/Generals/Code/Tools/matchbot/wlib/wstypes.h deleted file mode 100644 index dbc58245638..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/wstypes.h +++ /dev/null @@ -1,118 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * -****************************************************************************** -Project Name: Carpenter (The RedAlert ladder creator) -File Name : wstypes.h -Author : Neal Kettler -Start Date : June 3, 1997 -Last Update : June 17, 1997 - -Standard type definitions for the sake of portability and readability. -\***************************************************************************/ - -#ifndef WSTYPES_HEADER -#define WSTYPES_HEADER - -#ifdef _REENTRANT // reentrant = threaded -// Headers with non threadsafe libs need to come before my hacky -// threadsafe.h otherwise they won't compile - -#include -#ifndef _WINDOWS -#define _POSIX_C_SOURCE 199506L -#define _POSIX_PTHREAD_SEMANTICS -#define __EXTENSIONS__ -#endif -#include -#include -#include -#ifndef _WINDOWS -#include -#include -#include -#else -#include -#include -#endif -#include "threadsafe.h" // enforce threadsafe-only calls -#endif - -#define adelete(X) (delete[](X)) - -#ifndef TRUE -#define TRUE 1 -#endif - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef MIN -#define MIN(x,y) (((x)<(y))?(x):(y)) -#endif - -#ifndef MAX -#define MAX(x,y) (((x)>(y))?(x):(y)) -#endif - -#ifndef NULL -#define NULL 0 -#endif - -//These are used for readability purposes mostly, when a method takes a -// pointer or reference these help specify what will happen to the data -// that is sent in. -#ifdef IN -#undef IN -#endif -#define IN const -#define OUT -#define INOUT -#define _IN_ const - -// Used to declare a function or method as const or Read Only -#define RO const - -typedef char bit8; -typedef char sint8; -typedef unsigned char uint8; -typedef signed short int sint16; -typedef unsigned short int uint16; -typedef signed int sint32; -typedef unsigned int uint32; - -typedef float float32; -typedef double float64; - -#define MAX_BIT8 0x1 -#define MAX_UINT32 0xFFFFFFFF -#define MAX_UINT16 0xFFFF -#define MAX_UINT8 0xFF -#define MAX_SINT32 0x7FFFFFFF -#define MAX_SINT16 0x7FFF -#define MAX_SINT8 0x7F - -#ifdef _WINDOWS -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#endif - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/wtime.cpp b/Generals/Code/Tools/matchbot/wlib/wtime.cpp deleted file mode 100644 index 9843a25d94d..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/wtime.cpp +++ /dev/null @@ -1,802 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -wtime Neal Kettler -\****************************************************************************/ - -#include -#include "wtime.h" - -static const char *DAYS[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; - -static const char *FULLDAYS[]={"Sunday","Monday","Tuesday","Wednesday","Thursday", - "Friday","Saturday"}; - -static const char *MONTHS[]={"Jan","Feb","Mar","Apr","May","Jun","Jul", - "Aug","Sep","Oct","Nov","Dec"}; - -static const char *FULLMONTHS[]={"January","February","March","April","May","June", - "July","August","September","October","November","December"}; - -// MDC: Windows doesn't provide a localtime_r, so make our own... -#ifdef _WINDOWS -#ifdef _REENTRANT -#include "critsec.h" -static CritSec localtime_critsec; -#undef localtime -_CRTIMP struct tm *localtime(const time_t *clockval); -#endif // _REENTRANT - -static struct tm *localtime_r(const time_t *clockval, struct tm *res) { -#ifdef _REENTRANT - localtime_critsec.lock(); -#endif - struct tm *static_tm = localtime(clockval); - res = (struct tm *)memcpy(res, static_tm, sizeof(tm)); -#ifdef _REENTRANT - localtime_critsec.unlock(); -#endif - return res; -} -#endif // _WINDOWS - -Wtime::Wtime(void) -{ - Update(); -} - -Wtime::Wtime( Wtime &other ) -{ - sign=other.sign; - sec=other.sec; - usec=other.usec; -} - -Wtime::Wtime( uint32 other ) -{ - sign=POSITIVE; - sec=other; - usec=0; -} - -Wtime::~Wtime() -{ -} - -void Wtime::Update(void) -{ - sign=POSITIVE; - #ifdef _WINDOWS - struct _timeb wintime; - _ftime(&wintime); - sec=wintime.time; - usec=(wintime.millitm)*1000; - #endif - #ifndef _WINDOWS - struct timeval unixtime; - struct timezone unixtzone; - gettimeofday(&unixtime,&unixtzone); - sec=unixtime.tv_sec; - usec=unixtime.tv_usec; - #endif -} - - -// Parses a date string that's in modified RFC 1123 format -// Can have a +minutes after the normal time -// eg: Thu, 20 Jun 1996 17:33:49 +100 -// Returns true if successfully parsed, false otherwise -bit8 Wtime::ParseDate(char *in) -{ - int i; - uint32 minOffset; - struct tm t; - char *ptr=in; - while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of string - if (*ptr==0) return(FALSE); - t.tm_wday=-1; - for (i=0; i<7; i++) // parse day of week - if (strncmp(ptr,DAYS[i],strlen(DAYS[i]))==0) - t.tm_wday=i; - if (t.tm_wday==-1) - return(FALSE); - while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to day of month - if (*ptr==0) return(FALSE); - t.tm_mday=atoi(ptr); - while ((!isalpha(*ptr))&&(*ptr!=0)) ptr++; // skip to month - if (*ptr==0) return(FALSE); - t.tm_mon=-1; - for (i=0; i<12; i++) // match month - if (strncmp(ptr,MONTHS[i],strlen(MONTHS[i]))==0) t.tm_mon=i; - if (t.tm_mon==-1) return(FALSE); - while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; - if (*ptr==0) return(FALSE); - t.tm_year=atoi(ptr); - if (t.tm_year<70) // if they specify a 2 digit year, we'll be nice - t.tm_year+=2000; - else if (t.tm_year<100) - t.tm_year+=1900; - if (t.tm_year>2200) // I doubt my code will be around for another 203 years - return(FALSE); - while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to end of year - if (*ptr==0) return(FALSE); - - while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of time - if (*ptr==0) return(FALSE); - - t.tm_hour=atoi(ptr); - while ((*ptr!=':')&&(*ptr!=0)) ptr++; - ptr++; // skip past colon - if (*ptr==0) return(FALSE); - t.tm_min=atoi(ptr); - while ((*ptr!=':')&&(*ptr!=0)) ptr++; - ptr++; // skip past colon - if (*ptr==0) return(FALSE); - t.tm_sec=atoi(ptr); - t.tm_year%=100; // 1996 is stored as 96, not 1996 - t.tm_isdst=-1; // daylight savings info isn't available - - sec=(uint32)(mktime(&t)); - if ((sint32)sec==-1) - return(FALSE); - - - // The next part of the time is OPTIONAL (+minutes) - - // first skip past the seconds - while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; - if (*ptr==0) return(TRUE); - - // skip past any spaces - while ((isspace(*ptr))&&(*ptr!=0)) ptr++; - if (*ptr!='+') - { - //printf("\nNOPE ptr was '%s'\n",ptr); - return(TRUE); - } - ptr++; - if (*ptr==0) - { - //printf("\nPTR WAS 0\n"); - return(TRUE); - } - - minOffset=atol(ptr); - //printf("\n\nAdding %d minutes!\n\n",minOffset); - sec+=minOffset*60; // add the minutes as seconds - return(TRUE); -} - - -// This takes the standard Microsoft time formatting string -// make sure the out string is big enough -// An example format would be "mm/dd/yy hh:mm:ss" -// CHANGE: Joe Howes 06/30/99 -// To specify 12-hour format, use "aa" instead of "hh". -// The hours will be 12 hour and the string will be -// appended with " AM" or " PM". -bit8 Wtime::FormatTime(char *out, const char *format) -{ - int lastWasH=0; - int ampmflag = 0; - out[0]=0; - const char *ptr=format; - - if (*ptr=='"') ptr++; // skip past open quote if exists - - while (*ptr!=0) - { - if (lastWasH>0) - lastWasH--; - - if (isspace(*ptr)) - { - if (lastWasH==1) lastWasH=2; - sprintf(out+strlen(out),"%c",*ptr); - ptr+=1; - } - else if (strncmp(ptr,"\"",1)==0) - { - break; - } - else if (strncmp(ptr,":",1)==0) - { - if (lastWasH==1) lastWasH=2; - sprintf(out+strlen(out),":"); - ptr+=1; - } - else if (strncmp(ptr,"/",1)==0) - { - sprintf(out+strlen(out),"/"); - ptr+=1; - } - else if (strncmp(ptr,"c",1)==0) - { - sprintf(out+strlen(out),"%ld/%ld/%02ld %ld:%02ld:%02ld",GetMonth(), - GetMDay(),GetYear()%100,GetHour(),GetMinute(),GetSecond()); - ptr+=1; - } - else if (strncmp(ptr,"dddddd",6)==0) - { - sprintf(out+strlen(out),"%s %02ld, %ld",FULLMONTHS[GetMonth()-1], - GetMDay(),GetYear()); - ptr+=6; - } - else if (strncmp(ptr,"ddddd",5)==0) - { - sprintf(out+strlen(out),"%ld/%ld/%02ld",GetMonth(),GetMDay(), - GetYear()%100); - ptr+=5; - } - else if (strncmp(ptr,"dddd",4)==0) - { - sprintf(out+strlen(out),"%s",FULLDAYS[GetWDay()-1]); - ptr+=4; - } - else if (strncmp(ptr,"ddd",3)==0) - { - sprintf(out+strlen(out),"%s",DAYS[GetWDay()-1]); - ptr+=3; - } - else if (strncmp(ptr,"dd",2)==0) - { - sprintf(out+strlen(out),"%02ld",GetMDay()); - ptr+=2; - } - else if (strncmp(ptr,"d",1)==0) - { - sprintf(out+strlen(out),"%ld",GetMDay()); - ptr+=1; - } - else if (strncmp(ptr,"ww",2)==0) - { - sprintf(out+strlen(out),"%02ld",GetYWeek()); - ptr+=2; - } - else if (strncmp(ptr,"w",1)==0) - { - sprintf(out+strlen(out),"%ld",GetWDay()); - ptr+=1; - } - else if (strncmp(ptr,"mmmm",4)==0) - { - sprintf(out+strlen(out),"%s",FULLMONTHS[GetMonth()-1]); - ptr+=4; - } - else if (strncmp(ptr,"mmm",3)==0) - { - sprintf(out+strlen(out),"%s",MONTHS[GetMonth()-1]); - ptr+=3; - } - else if (strncmp(ptr,"mm",2)==0) - { - if (lastWasH==1) - sprintf(out+strlen(out),"%02ld",GetMinute()); - else - sprintf(out+strlen(out),"%02ld",GetMonth()); - ptr+=2; - } - else if (strncmp(ptr,"m",1)==0) - { - if (lastWasH==1) - sprintf(out+strlen(out),"%ld",GetMinute()); - else - sprintf(out+strlen(out),"%ld",GetMonth()); - ptr+=1; - } - else if (strncmp(ptr,"q",1)==0) - { - sprintf(out+strlen(out),"%ld",((GetMonth()-1)/4)+1); // GetQuarter - ptr+=1; - } - else if (strncmp(ptr,"yyyy",4)==0) - { - sprintf(out+strlen(out),"%ld",GetYear()); - ptr+=4; - } - else if (strncmp(ptr,"yy",2)==0) - { - sprintf(out+strlen(out),"%02ld",GetYear()%100); - ptr+=2; - } - else if (strncmp(ptr,"y",1)==0) - { - sprintf(out+strlen(out),"%ld",GetYDay()); - ptr+=1; - } - else if (strncmp(ptr,"hh",2)==0) - { - sprintf(out+strlen(out),"%02ld",GetHour()); - lastWasH=2; // needs to be 1 after top of loop decs it - ptr+=2; - } - else if (strncmp(ptr,"h",1)==0) - { - sprintf(out+strlen(out),"%ld",GetHour()); - lastWasH=2; // needs to be 1 after top of loop decs it - ptr+=1; - } - else if (strncmp(ptr,"nn",2)==0) - { - sprintf(out+strlen(out),"%02ld",GetMinute()); - ptr+=2; - } - else if (strncmp(ptr,"n",1)==0) - { - sprintf(out+strlen(out),"%ld",GetMinute()); - ptr+=1; - } - else if (strncmp(ptr,"ss",2)==0) - { - sprintf(out+strlen(out),"%02ld",GetSecond()); - ptr+=2; - } - else if (strncmp(ptr,"s",1)==0) - { - sprintf(out+strlen(out),"%ld",GetSecond()); - ptr+=1; - } - else if (strncmp(ptr,"ttttt",5)==0) - { - sprintf(out+strlen(out),"%ld:%02ld:%02ld",GetHour(),GetMinute(), - GetSecond()); - ptr+=5; - } - else if (strncmp(ptr,"aa",2)==0) - { - uint32 tmp = (GetHour() <= 12) ? GetHour() : GetHour() - 12; - sprintf(out+strlen(out),"%02ld", tmp); - lastWasH=2; // needs to be 1 after top of loop decs it - ptr+=2; - ampmflag = 1; - } - else // an unknown char, move to next - ptr++; - } - if(ampmflag) - { - char ampm[4]; - if( GetHour() < 12 ) - strcpy(ampm, " AM"); - else - strcpy(ampm, " PM"); - sprintf(out+strlen(out), "%s", ampm); - } - return(TRUE); -} - - - -// In addition to PrintTime & PrintDate there is the 'Print' function -// which prints both in RFC 1123 format - -void Wtime::PrintTime(FILE *out) const -{ - char string[80]; - PrintTime(string); - fprintf(out,"%s",string); -} - -void Wtime::PrintTime(char *out) const -{ - sprintf(out," %02lu:%02lu:%02lu",GetHour(),GetMinute(),GetSecond()); -} - -void Wtime::PrintDate(FILE *out) const -{ - char string[80]; - PrintDate(string); - fprintf(out,"%s",string); -} - -void Wtime::PrintDate(char *out) const -{ - sprintf(out,"%s, %lu %s %lu",DAYS[GetWDay()-1],GetMDay(),MONTHS[GetMonth()-1], - GetYear()); -} - -uint32 Wtime::GetSec(void) const -{ - return(sec); -} - -uint32 Wtime::GetUsec(void) const -{ - return(usec); -} - -void Wtime::SetSec(uint32 newsec) -{ - sec=newsec; -} - -void Wtime::SetUsec(uint32 newusec) -{ - usec=newusec; -} - -void Wtime::Set(uint32 newsec, uint32 newusec) -{ - sec=newsec; - usec=newusec; -} - -// Get a timeval ptr from a Wtime class -struct timeval *Wtime::GetTimeval(void) -{ - static struct timeval tv; - tv.tv_sec=sec; - tv.tv_usec=usec; - return(&tv); -} - -// Get a timeval ptr from a Wtime class -void Wtime::GetTimevalMT(struct timeval &tv) -{ - tv.tv_sec=sec; - tv.tv_usec=usec; -} - - -uint32 Wtime::GetSecond(void) const -{ - struct tm t; - struct tm *tptr; - tptr=localtime_r((time_t *)&sec,&t); - return(tptr->tm_sec); -} -uint32 Wtime::GetMinute(void) const -{ - struct tm t; - struct tm *tptr; - tptr=localtime_r((time_t *)&sec,&t); - return(tptr->tm_min); -} -uint32 Wtime::GetHour(void) const -{ - struct tm t; - struct tm *tptr; - tptr=localtime_r((time_t *)&sec,&t); - return(tptr->tm_hour); -} -uint32 Wtime::GetMDay(void) const -{ - struct tm t; - struct tm *tptr; - tptr=localtime_r((time_t *)&sec,&t); - return(tptr->tm_mday); -} -uint32 Wtime::GetWDay(void) const -{ - struct tm t; - struct tm *tptr; - tptr=localtime_r((time_t *)&sec,&t); - return(tptr->tm_wday+1); -} -uint32 Wtime::GetYDay(void) const -{ - struct tm t; - struct tm *tptr; - tptr=localtime_r((time_t *)&sec,&t); - return(tptr->tm_yday+1); -} -uint32 Wtime::GetYWeek(void) const -{ - uint32 yweek; - uint32 yday=GetYDay(); - uint32 wday=GetWDay(); - //phase holds the first weekday of the year. If (Jan 1 = Sun) phase = 0 - sint32 phase=((wday-yday)%7); - if (phase<0) phase+=7; - yweek=((yday+phase-1)/7)+1; - return(yweek); -} -uint32 Wtime::GetMonth(void) const -{ - struct tm t; - struct tm *tptr; - tptr=localtime_r((time_t *)&sec,&t); - return(tptr->tm_mon+1); -} - -uint32 Wtime::GetYear(void) const -{ - struct tm t; - struct tm *tptr; - tptr=localtime_r((time_t *)&sec,&t); - if ((tptr->tm_year)>=70) - return((tptr->tm_year)+1900); - else - return((tptr->tm_year)+2000); -} - - -bit8 Wtime::GetSign(void) const -{ - return(sign); -} - -// 1 = *this > other -//-1 = *this < other -// 0 = *this == other -int Wtime::Compare(const Wtime &other) const -{ - if ((sec==other.sec)&&(usec==other.usec)) - return(0); // equal - - else if (sec>other.sec) - return(1); - else if (secother.usec) - return(1); - else - return(-1); -} - - -bit8 Wtime::operator == ( const Wtime &other ) const -{ - bit8 retval=Compare(other); - if (retval==0) - return(TRUE); - else - return(FALSE); -} - -bit8 Wtime::operator != ( const Wtime &other ) const -{ - bit8 retval=Compare(other); - if (retval==0) - return(FALSE); - else - return(TRUE); -} - -bit8 Wtime::operator < ( const Wtime &other ) const -{ - int retval=Compare(other); - if (retval==-1) - return(TRUE); - else - return(FALSE); -} - -bit8 Wtime::operator > ( const Wtime &other ) const -{ - int retval=Compare(other); - if (retval==1) - return(TRUE); - else - return(FALSE); -} - -bit8 Wtime::operator <= ( const Wtime &other ) const -{ - int retval=Compare(other); - if ((retval==-1)||(retval==0)) - return(TRUE); - else - return(FALSE); -} - -bit8 Wtime::operator >= ( const Wtime &other ) const -{ - int retval=Compare(other); - if ((retval==1)||(retval==0)) - return(TRUE); - else - return(FALSE); -} - - -// None of the operators pay attention to sign -// only the functions that begin with 'Signed' -void Wtime::SignedAdd(const Wtime &other) -{ - Wtime temp; - - if ((sign==POSITIVE)&&(other.sign==POSITIVE)) - { - *this+=other; - sign=POSITIVE; - } - else if ((sign==POSITIVE)&&(other.sign==NEGATIVE)) - { - if (*this>other) - { - *this-=other; - sign=POSITIVE; - } - else - { - temp=other; - temp-=*this; - *this=temp; - sign=NEGATIVE; - } - } - else if ((sign==NEGATIVE)&&(other.sign==POSITIVE)) - { - if (*thisother) - { - *this-=other; - sign=POSITIVE; - } - else - { - temp=other; - temp-=*this; - *this=temp; - sign=NEGATIVE; - } - } - else if ((sign==NEGATIVE)&&(other.sign==NEGATIVE)) - { - if (*this1000000) - { - sec++; - usec-=1000000; - } - return *this; -} - -Wtime &Wtime::operator -= (const Wtime &other) -{ - sint32 temp; - if (Compare(other)==-1) - { - sec=0; // can't handle negative time - usec=0; - return *this; - } - sec-=other.sec; - temp=(sint32)usec; - temp-=(sint32)other.usec; - if (temp<0) - { - sec--; - temp+=1000000; - } - usec=temp; - return *this; -} - -Wtime Wtime::operator - (Wtime &other) -{ - Wtime temp(*this); - temp-=other; - return(temp); -} - -Wtime Wtime::operator + (Wtime &other) -{ - Wtime temp(*this); - temp+=other; - return(temp); -} - - -Wtime &Wtime::operator = (const Wtime &other) -{ - sign=other.sign; - sec=other.sec; - usec=other.usec; - return *this; -} - - -Wtime &Wtime::operator += (const uint32 other) -{ - sec+=other; - return *this; -} - - -Wtime &Wtime::operator -= (const uint32 other) -{ - sec-=other; - return *this; -} - - -Wtime Wtime::operator - (uint32 other) -{ - Wtime temp(*this); - temp-=other; - return(temp); -} - - -Wtime Wtime::operator + (uint32 other) -{ - Wtime temp(*this); - temp+=other; - return(temp); -} - - -Wtime &Wtime::operator = (const uint32 other) -{ - sign=POSITIVE; - sec=other; - usec=0; - return *this; -} diff --git a/Generals/Code/Tools/matchbot/wlib/wtime.h b/Generals/Code/Tools/matchbot/wlib/wtime.h deleted file mode 100644 index 3f0d1cf723a..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/wtime.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -wtime Neal Kettler - -\****************************************************************************/ -#ifndef WTIME_HEADER -#define WTIME_HEADER - - -#include -#include -#include -#include - -#ifndef _WINDOWS -#include -#include -#include -#else -#include -#include -#endif - -#include -#include - -#include "wstypes.h" - -class Wtime -{ - public: - - enum - { - POSITIVE=0, - NEGATIVE=1 - }; - - Wtime(); // init to system time - Wtime( Wtime &other ); - Wtime( uint32 other ); - ~Wtime(); - - void Update(); // Update members sec & usec to system time - - - void PrintTime(FILE *out) const; - void PrintTime(char *out) const; - void PrintDate(FILE *out) const; - void PrintDate(char *out) const; - - uint32 GetSec(void) const; // Get member variable 'sec' - uint32 GetUsec(void) const; // Get member variable 'usec' - void SetSec(uint32 newsec); - void SetUsec(uint32 newusec); - void Set(uint32 newsec,uint32 newusec); - bit8 ParseDate(char *in); - bit8 FormatTime(char *out, const char *format); - - struct timeval *GetTimeval(void); - void GetTimevalMT(struct timeval &tv); - - uint32 GetSecond(void) const; // Second (0- 60) (60 is for a leap second) - uint32 GetMinute(void) const; // Minute (0 - 59) - uint32 GetHour(void) const; // Hour (0-23) - uint32 GetMDay(void) const; // Day of Month (1-31) - uint32 GetWDay(void) const; // Day of Week (1-7) - uint32 GetYDay(void) const; // Day of Year (1-366) - uint32 GetMonth(void) const; // Month (1-12) - uint32 GetYWeek(void) const; // Week of Year (1-53) - uint32 GetYear(void) const; // Year (e.g. 1997) - - bit8 GetSign(void) const; // 0 = pos 1 = neg - - int Compare(const Wtime &other) const; - - // comparisons - bit8 operator == ( const Wtime &other ) const; - bit8 operator != ( const Wtime &other ) const; - bit8 operator < ( const Wtime &other ) const; - bit8 operator > ( const Wtime &other ) const; - bit8 operator <= ( const Wtime &other ) const; - bit8 operator >= ( const Wtime &other ) const; - - // assignments - Wtime &operator = (const Wtime &other); - Wtime &operator = (const uint32 other); - - // math - // signed - void SignedAdd(const Wtime &other); - void SignedSubtract(const Wtime &other); - - // unsigned - Wtime &operator += (const Wtime &other); - Wtime &operator -= (const Wtime &other); - Wtime operator + (Wtime &other); - Wtime operator - (Wtime &other); - - Wtime &operator += (const uint32 other); - Wtime &operator -= (const uint32 other); - Wtime operator + (uint32 other); - Wtime operator - (uint32 other); - - protected: - uint32 sec; // seconds since Jan 1, 1970 - uint32 usec; // microseconds (millionths of a second) - bit8 sign; // for time differences 0 = pos 1 = neg -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wlib/xtime.cpp b/Generals/Code/Tools/matchbot/wlib/xtime.cpp deleted file mode 100644 index 28181d6f36b..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/xtime.cpp +++ /dev/null @@ -1,1015 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ - -xtime Neal Kettler - -Improved version of the Wtime library (V->W->X...) - -Handles signed times better and dates long -long long after you'll be dead. - -\****************************************************************************/ - -#include -#include -#ifndef _WINDOWS -#include -#endif -#include "xtime.h" - -static const char *DAYS[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; - -static const char *FULLDAYS[]={"Sunday","Monday","Tuesday","Wednesday","Thursday", - "Friday","Saturday"}; - -static const char *MONTHS[]={"Jan","Feb","Mar","Apr","May","Jun","Jul", - "Aug","Sep","Oct","Nov","Dec"}; - -static const char *FULLMONTHS[]={"January","February","March","April","May","June", - "July","August","September","October","November","December"}; - - -#define IS_LEAP(y) ((y) % 4) == 0 && (! ((y) % 100) == 0 || ((y) % 400) == 0) -#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) - - -/////////////// Utility functions /////////////////// - -// -// Return the daycount since year 0 for the specified date. -// month = 1-12, day = 1-31 year = 0... -// -static sint32 Get_Day(int month, int day, int year) -{ - time_t days; - - static int DaysAtMonth[] = - {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; - - /* Add up number of straight days + number of leap days + days */ - /* up to the month + the day of month. */ - - days = (year * 365) + (year / 4) - (year / 100) + (year / 400) + - DaysAtMonth[month-1] + day; - - /* If we haven't hit Feb 29, and this is a leap year, we need to */ - /* subtract out the leap day that was added above for this year */ - - if (month < 3 && IS_LEAP(year)) - --days; - return(days); -} - - - -// -// Get the year from a daycount since year 0 -// Also get the daycount since the start of the year -// -// Ayecarumba what a pain in the ass! -// -static bit8 Get_Date_From_Day(sint32 days, OUT sint32 &year, OUT sint32 &yday) -{ - //register long int rem; - register long int y; - //register const unsigned short int *ip; - - if (days <= 365) - { - year=0; - yday=days+1; // 1 based - return(TRUE); - } - - y = 1; - days-=365; - - days--; // zero based - - // - // As far as I can tell there's no non-iteritive way to - // do this... - // - while (days < 0 || days >= (IS_LEAP (y) ? 366 : 365)) - { - /* Guess a corrected year, assuming 365 days per year. */ - long int yg = y + days / 365 - (days % 365 < 0); - - /* Adjust DAYS and Y to match the guessed year. */ - days -= ((yg - y) * 365 - + LEAPS_THRU_END_OF (yg - 1) - - LEAPS_THRU_END_OF (y - 1)); - y = yg; - } - year=y; - yday=days+1; // 1 based - return(TRUE); -} - - -// -// Get the max day of a given month in a given year -// -int Max_Day(int month, int year) -{ - static char dayTable[2][13] = { - {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} - }; - bit8 isleap=IS_LEAP(year); - return(dayTable[isleap][month]); -} - - - -/********************************************************** -int main(int argc, char *argv[]) -{ - Xtime wtime; - int month,mday,year,hour,minute,second,day; - int i; - int dayoffset; - int yr; - int yday; - int ydaycount; - - wtime.set(719528,0); - -*********************************************** - for (year=0; year<10000; year++) - { - ydaycount=1; - for (month=1; month<=12; month++) - { - for (day=1; day<=Max_Day(month,year); day++) - { - dayoffset=Get_Day(month,day,year); - assert (GetDateFromDay(dayoffset,yr,yday)==TRUE); - - //printf("Yday=%d YdayCount=%d\n",yday,ydaycount); - - if (yr!=year) - { - printf("MO=%d DAY=%d YEAR=%d YR=%d\n",month,day,year,yr); - assert(0); - } - if (yday != ydaycount) - { - printf("MO=%d DAY=%d YEAR=%d YR=%d\n",month,day,year,yr); - printf("Yday=%d YdayCount=%d\n",yday,ydaycount); - assert(0); - } - ydaycount++; - } - } - printf("(%d) ",year); - } -*************************************** - - ///////wtime.addSeconds((60*60*24)-(60*60*8)); - ////////wtime.addSeconds(-(60*60*8)); // timezone delta - - dayoffset=Get_Day(1,1,1970); - printf("DAYOFFSET = %d\n",dayoffset); - - - wtime.getTime(month, mday, year, hour, minute, second); - printf("\n%s %d %d %d:%02d:%02d\n\n", - MONTHS[month-1],mday,year,hour,minute,second); - - struct timeval unixtime; - struct timezone unixtzone; - time_t ttime; - tm tmtime; - - memset(&tmtime,0,sizeof(tmtime)); - ttime=0; - unixtime.tv_sec=0; - unixtime.tv_usec=0; - - //gettimeofday(&unixtime,&unixtzone); - //ttime=time(NULL); - tmtime=*gmtime(&ttime); - - printf("TIME->CTIME = %s\n",ctime(&ttime)); - printf("GTOD->CTIE = %s\n",ctime(&(unixtime.tv_sec))); - printf("TIME->GMT->ASCTIME = %s\n",asctime(&tmtime)); -} -***************************************************************/ - - - -// -// Construct with current clock time -// -Xtime::Xtime(void) -{ - update(); -} - -// -// Copy constructor -// -Xtime::Xtime( Xtime &other ) -{ - day_=other.day_; - msec_=other.msec_; -} - -// -// Set to a time_t (1970-2038) -// -Xtime::Xtime( time_t other ) -{ - day_=719528; // days from year 0 to Jan1, 1970 - // Add seconds from time_t - addSeconds(other); - msec_=0; -} - -Xtime::~Xtime() -{ -} - -// -// Add some number of seconds to the time (seconds can be negative) -// -void Xtime::addSeconds(sint32 seconds) -{ - // Add to day counter first - day_+=(seconds/86400); - msec_+=(seconds % 86400)*1000; - - // Now normalize in case msec is > 1 days worth - normalize(); -} - -// -// If msec is > 1 days worth, adjust the day count -// & decrement the msec counter until OK. -// -void Xtime::normalize(void) -{ - day_+=(msec_/86400000); - msec_%=86400000; - - while (msec_ < 0) - { - day_--; - msec_+=86400000; - } -} - -// -// Update time to hold the current clock time -// (breaks in 2038 :-) -// -void Xtime::update(void) -{ - day_=719528; // day_s from year 0 to Jan1, 1970 - msec_=0; - - #ifdef _WINDOWS - struct _timeb wintime; - _ftime(&wintime); - addSeconds(wintime.time); - msec_+=wintime.millitm; - #endif - #ifndef _WINDOWS - struct timeval unixtime; - struct timezone unixtzone; - gettimeofday(&unixtime,&unixtzone); - addSeconds(unixtime.tv_sec); - msec_+=(unixtime.tv_usec/1000); - #endif - - // Now normalize in case msec is > 1 days worth - normalize(); -} - - -// This takes the standard Microsoft time formatting string -// make sure the out string is big enough -// An example format would be "mm/dd/yy hh:mm:ss" -// CHANGE: Joe Howes 06/30/99 -// To specify 12-hour format, use "aa" instead of "hh". -// The hours will be 12 hour and the string will be -// appended with " AM" or " PM". -bit8 Xtime::FormatTime(char *out, const char *format) -{ - int lastWasH=0; - int ampmflag = 0; - out[0]=0; - const char *ptr=format; - - if (*ptr=='"') ptr++; // skip past open quote if exists - - while (*ptr!=0) - { - if (lastWasH>0) - lastWasH--; - - if (isspace(*ptr)) - { - if (lastWasH==1) lastWasH=2; - sprintf(out+strlen(out),"%c",*ptr); - ptr+=1; - } - else if (strncmp(ptr,"\"",1)==0) - { - break; - } - else if (strncmp(ptr,":",1)==0) - { - if (lastWasH==1) lastWasH=2; - sprintf(out+strlen(out),":"); - ptr+=1; - } - else if (strncmp(ptr,"/",1)==0) - { - sprintf(out+strlen(out),"/"); - ptr+=1; - } - else if (strncmp(ptr,"c",1)==0) - { - sprintf(out+strlen(out),"%ld/%ld/%02ld %ld:%02ld:%02ld",getMonth(), - getMDay(),getYear()%100,getHour(),getMinute(),getSecond()); - ptr+=1; - } - else if (strncmp(ptr,"dddddd",6)==0) - { - sprintf(out+strlen(out),"%s %02ld, %ld",FULLMONTHS[getMonth()-1], - getMDay(),getYear()); - ptr+=6; - } - else if (strncmp(ptr,"ddddd",5)==0) - { - sprintf(out+strlen(out),"%ld/%ld/%02ld",getMonth(),getMDay(), - getYear()%100); - ptr+=5; - } - /*else if (strncmp(ptr,"dddd",4)==0) - { - sprintf(out+strlen(out),"%s",FULLDAYS[getWDay()-1]); - ptr+=4; - } - else if (strncmp(ptr,"ddd",3)==0) - { - sprintf(out+strlen(out),"%s",DAYS[getWDay()-1]); - ptr+=3; - }*/ - else if (strncmp(ptr,"dd",2)==0) - { - sprintf(out+strlen(out),"%02ld",getMDay()); - ptr+=2; - } - else if (strncmp(ptr,"d",1)==0) - { - sprintf(out+strlen(out),"%ld",getMDay()); - ptr+=1; - } - /*else if (strncmp(ptr,"ww",2)==0) - { - sprintf(out+strlen(out),"%02ld",getYWeek()); - ptr+=2; - }*/ - /*else if (strncmp(ptr,"w",1)==0) - { - sprintf(out+strlen(out),"%ld",getWDay()); - ptr+=1; - }*/ - else if (strncmp(ptr,"mmmm",4)==0) - { - sprintf(out+strlen(out),"%s",FULLMONTHS[getMonth()-1]); - ptr+=4; - } - else if (strncmp(ptr,"mmm",3)==0) - { - sprintf(out+strlen(out),"%s",MONTHS[getMonth()-1]); - ptr+=3; - } - else if (strncmp(ptr,"mm",2)==0) - { - if (lastWasH==1) - sprintf(out+strlen(out),"%02ld",getMinute()); - else - sprintf(out+strlen(out),"%02ld",getMonth()); - ptr+=2; - } - else if (strncmp(ptr,"m",1)==0) - { - if (lastWasH==1) - sprintf(out+strlen(out),"%ld",getMinute()); - else - sprintf(out+strlen(out),"%ld",getMonth()); - ptr+=1; - } - else if (strncmp(ptr,"q",1)==0) - { - sprintf(out+strlen(out),"%ld",((getMonth()-1)/4)+1); // GetQuarter - ptr+=1; - } - else if (strncmp(ptr,"yyyy",4)==0) - { - sprintf(out+strlen(out),"%ld",getYear()); - ptr+=4; - } - else if (strncmp(ptr,"yy",2)==0) - { - sprintf(out+strlen(out),"%02ld",getYear()%100); - ptr+=2; - } - /*else if (strncmp(ptr,"y",1)==0) - { - sprintf(out+strlen(out),"%ld",getYDay()); - ptr+=1; - }*/ - else if (strncmp(ptr,"hh",2)==0) - { - sprintf(out+strlen(out),"%02ld",getHour()); - lastWasH=2; // needs to be 1 after top of loop decs it - ptr+=2; - } - else if (strncmp(ptr,"h",1)==0) - { - sprintf(out+strlen(out),"%ld",getHour()); - lastWasH=2; // needs to be 1 after top of loop decs it - ptr+=1; - } - else if (strncmp(ptr,"nn",2)==0) - { - sprintf(out+strlen(out),"%02ld",getMinute()); - ptr+=2; - } - else if (strncmp(ptr,"n",1)==0) - { - sprintf(out+strlen(out),"%ld",getMinute()); - ptr+=1; - } - else if (strncmp(ptr,"ss",2)==0) - { - sprintf(out+strlen(out),"%02ld",getSecond()); - ptr+=2; - } - else if (strncmp(ptr,"s",1)==0) - { - sprintf(out+strlen(out),"%ld",getSecond()); - ptr+=1; - } - else if (strncmp(ptr,"ttttt",5)==0) - { - sprintf(out+strlen(out),"%ld:%02ld:%02ld",getHour(),getMinute(), - getSecond()); - ptr+=5; - } - else if (strncmp(ptr,"aa",2)==0) - { - uint32 tmp = (getHour() <= 12) ? getHour() : getHour() - 12; - sprintf(out+strlen(out),"%02ld", tmp); - lastWasH=2; // needs to be 1 after top of loop decs it - ptr+=2; - ampmflag = 1; - } - else // an unknown char, move to next - ptr++; - } - if(ampmflag) - { - char ampm[4]; - if( getHour() < 12 ) - strcpy(ampm, " AM"); - else - strcpy(ampm, " PM"); - sprintf(out+strlen(out), "%s", ampm); - } - return(TRUE); -} - -/************************************** - - REPLACE THIS CRAP - -// Parses a date string that's in modified RFC 1123 format -// Can have a +minutes after the normal time -// eg: Thu, 20 Jun 1996 17:33:49 +100 -// Returns true if successfully parsed, false otherwise -bit8 Xtime::ParseDate(char *in) -{ - int i; - uint32 minOffset; - struct tm t; - char *ptr=in; - while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of string - if (*ptr==0) return(FALSE); - t.tm_wday_=-1; - for (i=0; i<7; i++) // parse day_ of week - if (strncmp(ptr,DAYS[i],strlen(DAYS[i]))==0) - t.tm_wday_=i; - if (t.tm_wday_==-1) - return(FALSE); - while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to day_ of month - if (*ptr==0) return(FALSE); - t.tm_mday_=atoi(ptr); - while ((!isalpha(*ptr))&&(*ptr!=0)) ptr++; // skip to month - if (*ptr==0) return(FALSE); - t.tm_mon=-1; - for (i=0; i<12; i++) // match month - if (strncmp(ptr,MONTHS[i],strlen(MONTHS[i]))==0) t.tm_mon=i; - if (t.tm_mon==-1) return(FALSE); - while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; - if (*ptr==0) return(FALSE); - t.tm_year=atoi(ptr); - if (t.tm_year<70) // if they specify a 2 digit year, we'll be nice - t.tm_year+=2000; - else if (t.tm_year<100) - t.tm_year+=1900; - if (t.tm_year>2200) // I doubt my code will be around for another 203 years - return(FALSE); - while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to end of year - if (*ptr==0) return(FALSE); - - while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of time - if (*ptr==0) return(FALSE); - - t.tm_hour=atoi(ptr); - while ((*ptr!=':')&&(*ptr!=0)) ptr++; - ptr++; // skip past colon - if (*ptr==0) return(FALSE); - t.tm_min=atoi(ptr); - while ((*ptr!=':')&&(*ptr!=0)) ptr++; - ptr++; // skip past colon - if (*ptr==0) return(FALSE); - t.tm_sec=atoi(ptr); - t.tm_year%=100; // 1996 is stored as 96, not 1996 - t.tm_isdst=-1; // day_light savings info isn't available - - sec=(uint32)(mktime(&t)); - if ((sint32)sec==-1) - return(FALSE); - - - // The next part of the time is OPTIONAL (+minutes) - - // first skip past the seconds - while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; - if (*ptr==0) return(TRUE); - - // skip past any spaces - while ((isspace(*ptr))&&(*ptr!=0)) ptr++; - if (*ptr!='+') - { - //printf("\nNOPE ptr was '%s'\n",ptr); - return(TRUE); - } - ptr++; - if (*ptr==0) - { - //printf("\nPTR WAS 0\n"); - return(TRUE); - } - - minOffset=atol(ptr); - //printf("\n\nAdding %d minutes!\n\n",minOffset); - sec+=minOffset*60; // add the minutes as seconds - return(TRUE); -} - - - -// In addition to PrintTime & PrintDate there is the 'Print' function -// which prints both in RFC 1123 format - -void Xtime::PrintTime(FILE *out) const -{ - char string[80]; - PrintTime(string); - fprintf(out,"%s",string); -} - -void Xtime::PrintTime(char *out) const -{ - sprintf(out," %02lu:%02lu:%02lu",GetHour(),GetMinute(),GetSecond()); -} - -void Xtime::PrintDate(FILE *out) const -{ - char string[80]; - PrintDate(string); - fprintf(out,"%s",string); -} - -void Xtime::PrintDate(char *out) const -{ - sprintf(out,"%s, %lu %s %lu",DAYS[GetWDay()-1],GetMDay(),MONTHS[GetMonth()-1], - GetYear()); -} -********************************************/ - -// Get day_s since year 0 -sint32 Xtime::getDay(void) const -{ - return(day_); -} - -// Get msecs since start of day -sint32 Xtime::getMsec(void) const -{ - return(msec_); -} - -// Set days since year 0 -void Xtime::setDay(sint32 newday) -{ - day_=newday; -} - -// Set msec since start of this day -void Xtime::setMsec(sint32 newmsec) -{ - msec_=newmsec; -} - -// Set both -void Xtime::set(sint32 newday, sint32 newmsec) -{ - day_=newday; - msec_=newmsec; -} - - -// -// Get a timeval ptr from a Xtime class -// May fail if timeval can't hold a year this big or small -// -bit8 Xtime::getTimeval(struct timeval &tv) -{ - // A timeval can only hold dates from 1970-2038 - if ((day_ < 719528) || (day_ >= 719528+24855)) - return(FALSE); - - // Compute seconds since Jan 1, 1970 - uint32 seconds=day_-719528; - seconds*=(60*60*24); - seconds+=(msec_/1000); - - tv.tv_sec=seconds; - tv.tv_usec=(msec_%1000)*1000; - return(TRUE); -} - -// -// Set the time -// -bit8 Xtime::setTime(int month, int mday, int year, int hour, int minute, int second) -{ - day_=Get_Day(month,mday,year); - msec_=(hour*1000*60*60)+(minute*1000*60)+(second*1000); - return(TRUE); -} - - - -int Xtime::getYDay(void) const // Day of Year (1-366) (366 = leap yr) -{ - int year; - sint32 dayofyear; - if (Get_Date_From_Day(day_,year,dayofyear)==FALSE) - return(-1); - return dayofyear; -} - - -// -// Get all the components of the time in the usual normalized format. -// -// Most of the uglyness is in Get_Date_From_Day() -// -bit8 Xtime::getTime(int &month, int &mday, int &year, int &hour, int &minute, int &second) const -{ - int i; - sint32 dayofyear; - if (Get_Date_From_Day(day_,year,dayofyear)==FALSE) - return(FALSE); - - static int DaysAtMonth[2][12] = { - {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, // normal - {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} // leap year - }; - - month=0; - - bit8 isleap=IS_LEAP(year); - for (i=0; i<12; i++) - { - if (DaysAtMonth[isleap][i] >= dayofyear) - break; - month=i; - } - month++; // 1 based - - mday=dayofyear-DaysAtMonth[isleap][month-1]; - - // Whew! Now all we have to do is figure out H/M/S from the msec! - hour=(msec_/3600000)%24; // 1000*60*60 - minute=(msec_/60000)%60; // 1000*60 - second=(msec_/ 1000)%60; // 1000 - - return(TRUE); -} - -// -// These are for getting components of the time in the -// standard ranges. Like Day 1-31, Second 0-59, etc... -// -int Xtime::getSecond(void) const -{ - int month,mday,year,hour,minute,second; - getTime(month, mday, year, hour, minute, second); - return(second); -} -int Xtime::getMinute(void) const -{ - int month,mday,year,hour,minute,second; - getTime(month, mday, year, hour, minute, second); - return(minute); -} -int Xtime::getHour(void) const -{ - int month,mday,year,hour,minute,second; - getTime(month, mday, year, hour, minute, second); - return(hour); -} -int Xtime::getMDay(void) const -{ - int month,mday,year,hour,minute,second; - getTime(month, mday, year, hour, minute, second); - return(mday); -} - -int Xtime::getMonth(void) const -{ - int month,mday,year,hour,minute,second; - getTime(month, mday, year, hour, minute, second); - return(month); -} - -// based at year 0 (real 0, not 1970) -int Xtime::getYear(void) const -{ - int month,mday,year,hour,minute,second; - getTime(month, mday, year, hour, minute, second); - return(year); -} - - -// -// Set the seconds value (0-59) -// -bit8 Xtime::setSecond(sint32 sec) -{ - sint32 second=(msec_/ 1000)%60; - msec_-=(second*1000); - msec_+=(sec*1000); - return(TRUE); -} - -// -// Set the minutes value (0-59) -// -bit8 Xtime::setMinute(sint32 min) -{ - sint32 minute=(msec_/60000)%60; // 1000*60 - msec_-=(minute*60000); - msec_+=(min*60000); - return(TRUE); -} - -// -// Set the minutes value (0-23) -// -bit8 Xtime::setHour(sint32 hour) -{ - hour=(msec_/3600000)%24; // 1000*60*60 - msec_-=(hour*3600000); - msec_+=(hour*3600000); - return(TRUE); -} - -// -// Set the year value -// Results are undefined if you're moving from Feb 29, to a non leap year -// -bit8 Xtime::setYear(sint32 _year) -{ - // extract the date - int month,mday,year,hour,min,sec; - getTime(month,mday,year,hour,min,sec); - - // modify & rebuild - year=_year; - day_=Get_Day(month,mday,year); - return(TRUE); -} - -// -// Modify the month -// -bit8 Xtime::setMonth(sint32 _month) -{ - // extract the date - int month,mday,year,hour,min,sec; - getTime(month,mday,year,hour,min,sec); - - // modify & rebuild - month=_month; - day_=Get_Day(month,mday,year); - return(TRUE); -} - - -// -// Modify the day of the month -// -bit8 Xtime::setMDay(sint32 _mday) -{ - // extract the date - int month,mday,year,hour,min,sec; - getTime(month,mday,year,hour,min,sec); - - // modify & rebuild - mday=_mday; - day_=Get_Day(month,mday,year); - return(TRUE); -} - - -// -// Compare two times. The time better be normalized -// which it would be unless you've put bad stuff in it! -// -// 1 = *this > other -//-1 = *this < other -// 0 = *this == other -int Xtime::compare(const Xtime &other) const -{ - if ((day_==other.day_)&&(msec_==other.msec_)) - return(0); // equal - - else if (day_>other.day_) - return(1); - else if (day_other.msec_) - return(1); - else - return(-1); -} - - -bit8 Xtime::operator == ( const Xtime &other ) const -{ - bit8 retval=compare(other); - if (retval==0) - return(TRUE); - else - return(FALSE); -} - -bit8 Xtime::operator != ( const Xtime &other ) const -{ - bit8 retval=compare(other); - if (retval==0) - return(FALSE); - else - return(TRUE); -} - -bit8 Xtime::operator < ( const Xtime &other ) const -{ - int retval=compare(other); - if (retval==-1) - return(TRUE); - else - return(FALSE); -} - -bit8 Xtime::operator > ( const Xtime &other ) const -{ - int retval=compare(other); - if (retval==1) - return(TRUE); - else - return(FALSE); -} - -bit8 Xtime::operator <= ( const Xtime &other ) const -{ - int retval=compare(other); - if ((retval==-1)||(retval==0)) - return(TRUE); - else - return(FALSE); -} - -bit8 Xtime::operator >= ( const Xtime &other ) const -{ - int retval=compare(other); - if ((retval==1)||(retval==0)) - return(TRUE); - else - return(FALSE); -} - - -Xtime &Xtime::operator += (const Xtime &other) -{ - day_+=other.day_; - msec_+=other.msec_; - normalize(); - return *this; -} - -Xtime &Xtime::operator -= (const Xtime &other) -{ - day_-=other.day_; - msec_-=other.msec_; - normalize(); - return *this; -} - -Xtime Xtime::operator - (Xtime &other) -{ - Xtime temp(*this); - temp-=other; - return(temp); -} - -Xtime Xtime::operator + (Xtime &other) -{ - Xtime temp(*this); - temp+=other; - return(temp); -} - - -Xtime &Xtime::operator = (const Xtime &other) -{ - day_=other.day_; - msec_=other.msec_; - return *this; -} - - -Xtime &Xtime::operator += (const time_t other) -{ - addSeconds(other); - return *this; -} - - -Xtime &Xtime::operator -= (const time_t other) -{ - addSeconds(-((sint32)other)); - return *this; -} - - -Xtime Xtime::operator - (time_t other) -{ - Xtime temp(*this); - temp-=other; - return(temp); -} - - -Xtime Xtime::operator + (time_t other) -{ - Xtime temp(*this); - temp+=other; - return(temp); -} - -Xtime &Xtime::operator = (const time_t other) -{ - msec_=0; - day_=719528; // Jan 1, 1970 - addSeconds(other); - return *this; -} diff --git a/Generals/Code/Tools/matchbot/wlib/xtime.h b/Generals/Code/Tools/matchbot/wlib/xtime.h deleted file mode 100644 index b6210eee2cf..00000000000 --- a/Generals/Code/Tools/matchbot/wlib/xtime.h +++ /dev/null @@ -1,146 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -xtime Neal Kettler - -This is version 2 of the Wtime library (now xtime). It now supports -time storage from the year 0 to well after the sun -will have gone supernova (OK, OK I admit it'll break in the -year 5 Million.) - -The call to update the current time will break in 2038. -Hopefully by then somebody will replace the lame time() -function :-) -\****************************************************************************/ - -#ifndef XTIME_HEADER -#define XTIME_HEADER - - -#include -#include -#include -#include - -#ifndef _WINDOWS -#include -#include -#include -#else -#include -#include -#endif - -#include -#include - -#include "wstypes.h" - -class Xtime -{ - public: - - Xtime(); // init to system time - Xtime( Xtime &other ); - Xtime( time_t other ); // 1970-2038 - ~Xtime(); - - void addSeconds(sint32 seconds); - - bit8 getTime(int &month, int &mday, int &year, int &hour, int &minute, - int &second) const; - - bit8 setTime(int month, int mday, int year, int hour, int minute, - int second); - - void update(); // Update members sec & usec to system time - // This will break after 2038 - -/******** - void PrintTime(FILE *out) const; - void PrintTime(char *out) const; - void PrintDate(FILE *out) const; - void PrintDate(char *out) const; -**********/ - - sint32 getDay(void) const; // Get days since year 0 - sint32 getMsec(void) const; // Get milliseconds into the day - - void setDay(sint32 day); - void setMsec(sint32 msec); - - void set(sint32 newday, sint32 newmsec); - bit8 ParseDate(char *in); - bit8 FormatTime(char *out, const char *format); - - bit8 getTimeval(struct timeval &tv); - - // All of these may return -1 if the time is invalid - int getSecond(void) const; // Second (0-60) (60 is for a leap second) - int getMinute(void) const; // Minute (0-59) - int getHour(void) const; // Hour (0-23) - int getMDay(void) const; // Day of Month (1-31) - int getWDay(void) const; // Day of Week (1-7) - int getYDay(void) const; // Day of Year (1-366) (366 = leap yr) - int getMonth(void) const; // Month (1-12) - int getYWeek(void) const; // Week of Year (1-53) - int getYear(void) const; // Year (e.g. 1997) - - // Modify the time components. Return FALSE if fail - bit8 setSecond(sint32 sec); - bit8 setMinute(sint32 min); - bit8 setHour(sint32 hour); - bit8 setYear(sint32 year); - bit8 setMonth(sint32 month); - bit8 setMDay(sint32 mday); - - void normalize(void); // move msec overflows to the day - - // Compare two times - int compare(const Xtime &other) const; - - // comparisons - bit8 operator == ( const Xtime &other ) const; - bit8 operator != ( const Xtime &other ) const; - bit8 operator < ( const Xtime &other ) const; - bit8 operator > ( const Xtime &other ) const; - bit8 operator <= ( const Xtime &other ) const; - bit8 operator >= ( const Xtime &other ) const; - - // assignments - Xtime &operator = (const Xtime &other); - Xtime &operator = (const time_t other); - - // signed - Xtime &operator += (const Xtime &other); - Xtime &operator -= (const Xtime &other); - Xtime operator + (Xtime &other); - Xtime operator - (Xtime &other); - - Xtime &operator += (const time_t other); - Xtime &operator -= (const time_t other); - Xtime operator + (time_t other); - Xtime operator - (time_t other); - - protected: - sint32 day_; // days since Jan 1, 0 - sint32 msec_; // milliseconds (thousandths of a sec) -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wnet/field.cpp b/Generals/Code/Tools/matchbot/wnet/field.cpp deleted file mode 100644 index 35973709371..00000000000 --- a/Generals/Code/Tools/matchbot/wnet/field.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/*************************************************************************** - * * - * Project Name : Westwood Auto Registration App * - * * - * File Name : FIELD.CPP * - * * - * Programmer : Philip W. Gorrow * - * * - * Start Date : 04/22/96 * - * * - * Last Update : April 22, 1996 [PWG] * - * * - * Actual member function for the field class. * - *-------------------------------------------------------------------------* - * Functions: * - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -#include -#include -#ifndef _WINDOWS -#include -#else -#define Win32_Winsock -#include -#endif -#include "field.h" - - -// private member func -void FieldClass::Clear(void) -{ - delete[](Data); - - strcpy(ID,""); - DataType=0; - Size=0; - Data=NULL; - Next=NULL; -} - - -FieldClass::FieldClass(char *id, char data) -{ - Data=NULL; - Set(id,data); -} - -FieldClass::FieldClass(char *id, unsigned char data) -{ - Data=NULL; - Set(id,data); -} - -FieldClass::FieldClass(char *id, short data) -{ - Data=NULL; - Set(id,data); -} - -FieldClass::FieldClass(char *id, unsigned short data) -{ - Data=NULL; - Set(id,data); -} - -FieldClass::FieldClass(char *id, long data) -{ - Data=NULL; - Set(id,data); -} - -FieldClass::FieldClass(char *id, unsigned long data) -{ - Data=NULL; - Set(id,data); -} - -FieldClass::FieldClass(char *id, char *data) -{ - Data=NULL; - Set(id,data); -} - -FieldClass::FieldClass(char *id, void *data, int length) -{ - Data=NULL; - Set(id,data,length); -} - -void FieldClass::Set(char *id, char data) -{ - FieldClass *Nextsave=Next; - - Clear(); - strncpy(ID, id, sizeof(ID)); - DataType = TYPE_CHAR; - Size = sizeof(data); - Data = new char[Size]; - memcpy(Data, &data, Size); - Next = Nextsave; -} - -void FieldClass::Set(char *id, unsigned char data) -{ - FieldClass *Nextsave=Next; - Clear(); - strncpy(ID, id, sizeof(ID)); - DataType = TYPE_UNSIGNED_CHAR; - Size = sizeof(data); - Data = new char[Size]; - memcpy(Data, &data, Size); - Next = Nextsave; -} - -void FieldClass::Set(char *id, short data) -{ - FieldClass *Nextsave=Next; - Clear(); - strncpy(ID, id, sizeof(ID)); - DataType = TYPE_SHORT; - Size = sizeof(data); - Data = new char[Size]; - memcpy(Data, &data, Size); - Next = Nextsave; -} - -void FieldClass::Set(char *id, unsigned short data) -{ - FieldClass *Nextsave=Next; - Clear(); - strncpy(ID, id, sizeof(ID)); - DataType = TYPE_UNSIGNED_SHORT; - Size = sizeof(data); - Data = new char[Size]; - memcpy(Data, &data, Size); - Next = Nextsave; -} - -void FieldClass::Set(char *id, long data) -{ - FieldClass *Nextsave=Next; - Clear(); - strncpy(ID, id, sizeof(ID)); - DataType = TYPE_LONG; - Size = sizeof(data); - Data = new char[Size]; - memcpy(Data, &data, Size); - Next = Nextsave; -} - -void FieldClass::Set(char *id, unsigned long data) -{ - FieldClass *Nextsave=Next; - Clear(); - strncpy(ID, id, sizeof(ID)); - DataType = TYPE_UNSIGNED_LONG; - Size = sizeof(data); - Data = new char[Size]; - memcpy(Data, &data, Size); - Next = Nextsave; -} - -void FieldClass::Set(char *id, char *data) -{ - FieldClass *Nextsave=Next; - Clear(); - strncpy(ID, id, sizeof(ID)); - DataType = TYPE_STRING; - Size = (unsigned short)(strlen(data)+1); - Data = new char[Size]; - memcpy(Data, data, Size); - Next = Nextsave; -} - - -void FieldClass::Set(char *id, void *data, int length) -{ - FieldClass *Nextsave=Next; - Clear(); - strncpy(ID, id, sizeof(ID)); - DataType = TYPE_CHUNK; - Size = (unsigned short)length; - Data = new char[Size]; - memcpy(Data, data, Size); - Next = Nextsave; -} - - -FieldClass::~FieldClass() -{ - Clear(); -} - -// Fetch the datatype -int FieldClass::Get_Type(void) -{ - return(DataType); -} - -void *FieldClass::Get_Data(void) -{ -return(Data); -} - -char *FieldClass::Get_ID(void) -{ -return(ID); -} - -/************************************************************************** - * PACKETCLASS::HOST_TO_NET_FIELD -- Converts host field to net format * - * * - * INPUT: FIELD * to the data field we need to convert * - * * - * OUTPUT: none * - * * - * HISTORY: * - * 04/22/1996 PWG : Created. * - *========================================================================*/ -void FieldClass::Host_To_Net(void) -{ - // - // Before we convert the data type, we should convert the actual data - // sent. - // - switch (DataType) { - case TYPE_CHAR: - case TYPE_UNSIGNED_CHAR: - case TYPE_STRING: - break; - - case TYPE_SHORT: - case TYPE_UNSIGNED_SHORT: - *((unsigned short *)Data) = htons(*((unsigned short *)Data)); - break; - - case TYPE_LONG: - case TYPE_UNSIGNED_LONG: - *((unsigned long *)Data) = htonl(*((unsigned long *)Data)); - break; - - // - // Might be good to insert some type of error message here for unknown - // datatypes -- but will leave that for later. - // - default: - break; - } - // - // Finally convert over the data type and the size of the packet. - // - DataType = htons(DataType); - Size = htons(Size); -} - -/************************************************************************** - * PACKETCLASS::NET_TO_HOST_FIELD -- Converts net field to host format * - * * - * INPUT: FIELD * to the data field we need to convert * - * * - * OUTPUT: none * - * * - * HISTORY: * - * 04/22/1996 PWG : Created. * - *========================================================================*/ -void FieldClass::Net_To_Host(void) -{ - // - // Finally convert over the data type and the size of the packet. - // - DataType = ntohs(DataType); - Size = ntohs(Size); - - // - // Before we convert the data type, we should convert the actual data - // sent. - // - switch (DataType) { - case TYPE_CHAR: - case TYPE_UNSIGNED_CHAR: - case TYPE_STRING: - break; - - case TYPE_SHORT: - case TYPE_UNSIGNED_SHORT: - *((unsigned short *)Data) = ntohs(*((unsigned short *)Data)); - break; - - case TYPE_LONG: - case TYPE_UNSIGNED_LONG: - *((unsigned long *)Data) = ntohl(*((unsigned long *)Data)); - break; - - // - // Might be good to insert some type of error message here for unknown - // datatypes -- but will leave that for later. - // - default: - break; - } -} - diff --git a/Generals/Code/Tools/matchbot/wnet/field.h b/Generals/Code/Tools/matchbot/wnet/field.h deleted file mode 100644 index 8a58f68f551..00000000000 --- a/Generals/Code/Tools/matchbot/wnet/field.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/*************************************************************************** - * * - * Project Name : Westwood Auto Registration App * - * * - * File Name : FIELD.H * - * * - * Programmer : Philip W. Gorrow * - * * - * Start Date : 04/22/96 * - * * - * Last Update : April 22, 1996 [PWG] * - * * - * This module takes care of maintaining the field list used to process * - * packets. * - * * - *-------------------------------------------------------------------------* - * Functions: * - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - -#define FIELD_HEADER_SIZE (sizeof(FieldClass) - (sizeof(void *) * 2)) - -#define TYPE_CHAR 1 -#define TYPE_UNSIGNED_CHAR 2 -#define TYPE_SHORT 3 -#define TYPE_UNSIGNED_SHORT 4 -#define TYPE_LONG 5 -#define TYPE_UNSIGNED_LONG 6 -#define TYPE_STRING 7 -#define TYPE_CHUNK 20 - -class PacketClass; - -class FieldClass -{ - public: - friend PacketClass; - // - // Define constructors to be able to create all the different kinds - // of fields. - // - FieldClass(void) {}; - FieldClass(char *id, char data); - FieldClass(char *id, unsigned char data); - FieldClass(char *id, short data); - FieldClass(char *id, unsigned short data); - FieldClass(char *id, long data); - FieldClass(char *id, unsigned long data); - FieldClass(char *id, char *data); - FieldClass(char *id, void *data, int length); - - ~FieldClass(); - - // Change the field contents - void Set(char *id, char data); - void Set(char *id, unsigned char data); - void Set(char *id, short data); - void Set(char *id, unsigned short data); - void Set(char *id, long data); - void Set(char *id, unsigned long data); - void Set(char *id, char *data); - void Set(char *id, void *data, int length); - - int Get_Type(void); // get the datatype of this field - unsigned short Get_Size(void) { return Size; } - void * Get_Data(void); // get the datatype of this field - char * Get_ID(void); // get the datatype of this field - - void Host_To_Net(void); - void Net_To_Host(void); - - private: - - void Clear(void); // dealloc mem & zero safely - - char ID[4]; // id value of this field - unsigned short DataType; // id of the data type we are using - unsigned short Size; // size of the data portion of this field - char *Data; // pointer to the data portion of this field - FieldClass *Next; // pointer to the next field in the field list -}; - - - diff --git a/Generals/Code/Tools/matchbot/wnet/packet.cpp b/Generals/Code/Tools/matchbot/wnet/packet.cpp deleted file mode 100644 index a2741dbe481..00000000000 --- a/Generals/Code/Tools/matchbot/wnet/packet.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/*************************************************************************** - * * - * Project Name : Westwood Auto Registration App * - * * - * File Name : PACKET.CPP * - * * - * Programmer : Philip W. Gorrow * - * * - * Start Date : 04/22/96 * - * * - * Last Update : April 24, 1996 [PWG] * - * * - *-------------------------------------------------------------------------* - * Functions: * - * *PacketClass::Find_Field -- Finds a field if it exists in the packets * - * Get_Field -- Find specified name and returns data * - * PacketClass::~PacketClass -- destroys a packet class be freeing list * - * PacketClass::Add_Field -- Adds a FieldClass entry to head of packet li* - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -#include -#include -#include -#include -#ifndef _WINDOWS -#include -#else -#define Win32_Winsock -#include -#endif - -#include "packet.h" - - -/************************************************************************** - * PACKETCLASS::~PACKETCLASS -- destroys a packet class be freeing list * - * * - * INPUT: none * - * * - * OUTPUT: none * - * * - * HISTORY: * - * 04/24/1996 PWG : Created. * - *========================================================================*/ -PacketClass::~PacketClass() -{ - FieldClass *current; - FieldClass *next; - - // - // Loop through the entire field list and delete each entry. - // - for (current = Head; current; current = next) { - next = current->Next; - delete(current); - } -} - - -/************************************************************************** - * PACKETCLASS::ADD_FIELD -- Adds a FieldClass entry to head of packet li * - * * - * INPUT: FieldClass * - a properly constructed field class entry. * - * * - * OUTPUT: none * - * * - * HISTORY: * - * 04/24/1996 PWG : Created. * - *========================================================================*/ -void PacketClass::Add_Field(FieldClass *field) -{ - field->Next = Head; - Head = field; -} - -/************************************************************************** - * PACKETCLASS::PACKETCLASS -- Creates a Packet object from a COMMS packe * - * * - * INPUT: * - * * - * OUTPUT: * - * * - * WARNINGS: * - * * - * HISTORY: * - * 04/22/1996 PWG : Created. * - *========================================================================*/ -PacketClass::PacketClass(char *curbuf) -{ - int remaining_size; - // - // Pull the size and packet ID out of the linear packet stream. - // - Size = *((unsigned short *)curbuf); - curbuf += sizeof(unsigned short); - Size = ntohs(Size); - ID = *((short *)curbuf); - curbuf += sizeof(unsigned short); - ID = ntohs(ID); - Head = NULL; - - // - // Calculate the remaining size so that we can loop through the - // packets and extract them. - // - remaining_size = Size - 4; - - // - // Loop through the linear packet until we run out of room and - // create a field for each. - // - while (remaining_size > 0) - { - FieldClass *field = new FieldClass; - - // - // Copy the adjusted header into the buffer and then advance the buffer - // - memcpy(field, curbuf, FIELD_HEADER_SIZE); - curbuf += FIELD_HEADER_SIZE; - remaining_size -= FIELD_HEADER_SIZE; - - // - // Copy the data into the buffer - // - int size = ntohs(field->Size); - field->Data = new char[size]; - memcpy(field->Data, curbuf, size); - curbuf += size; - remaining_size -= size; - - // - // Make sure we allow for the pad bytes. - // - int pad = (4 - (ntohs(field->Size) & 3)) & 3; - curbuf += pad; - remaining_size -= pad; - - // - // Convert the field back to the host format - // - field->Net_To_Host(); - - // - // Finally add the field to the field list in the packet - // structure. - // - Add_Field(field); - } -} - -/************************************************************************** - * CREATE_COMMS_PACKET -- Walks field list creating a packet * - * * - * INPUT: short - the id of the packet so the server can identify it * - * unsigned short & - the size of the packet returned here * - * * - * OUTPUT: void * pointer to the linear packet data * - * * - * WARNINGS: This routine allocates memory that the user is responsible * - * for freeing. * - * * - * HISTORY: * - * 04/22/1996 PWG : Created. * - *========================================================================*/ -char *PacketClass::Create_Comms_Packet(int &size) -{ - FieldClass *current; - - // - // Size starts at four because that is the size of the packet header. - // - size = 4; - - // - // Take a quick spin through and calculate the size of the packet we - // are building. - // - for (current = Head; current; current=current->Next) - { - size += (unsigned short)FIELD_HEADER_SIZE; // add in packet header size - size += current->Size; // add in data size - size += (4 - (size & 3)) & 3; // add in pad value to dword align next packet - } - - // - // Now that we know the size allocate a buffer big enough to hold the - // packet. - // - char *retval = new char[size]; - char *curbuf = retval; - - // - // write the size into the packet header - // - *((unsigned short *)curbuf) = (unsigned short)htons(size); - curbuf += sizeof(unsigned short); - *((short *)curbuf) = htons(ID); - curbuf += sizeof(unsigned short); - - // - // Ok now that the actual header information has been written we need to write out - // field information. - // - for (current = Head; current; current = current->Next) - { - // - // Temporarily convert the packet to net format (this saves alot of - // effort, and seems safe...) - // - current->Host_To_Net(); - - // - // Copy the adjusted header into the buffer and then advance the buffer - // - memcpy(curbuf, current, FIELD_HEADER_SIZE); - curbuf += FIELD_HEADER_SIZE; - - // - // Copy the data into the buffer and then advance the buffer - // - memcpy(curbuf, current->Data, ntohs(current->Size)); - curbuf += ntohs(current->Size); - - // - // Finally take care of any pad bytes by setting them to 0 - // - int pad = (4 - (ntohs(current->Size) & 3)) & 3; - curbuf += pad; - - current->Net_To_Host(); - } - return(retval); -} - - -/************************************************************************** - * PACKETCLASS::FIND_FIELD -- Finds a field if it exists in the packets * - * * - * INPUT: char * - the id of the field we are looking for. * - * * - * OUTPUT: FieldClass * pointer to the field class * - * * - * HISTORY: * - * 04/23/1996 PWG : Created. * - *========================================================================*/ -FieldClass *PacketClass::Find_Field(char *id) -{ - for (FieldClass *current = Head; current; current = current->Next) - { - if ( strncmp(id, current->ID, 4) == 0) - return current; - } - return NULL; -} - -// gks 9/25/2000 -FieldClass *PacketClass::Get_Field_At(int position) -{ - int i = 0; - FieldClass *current = Head; - for (; (current && (i < position)); current = current->Next, i++) { - } - - if (current) return current; - else return NULL; -} - -// gks 9/25/2000 -int PacketClass::Get_Num_Fields() -{ - int i = 0; - - for (FieldClass *current = Head; current; current = current->Next, i++) {} - - return i; - -} - - -/************************************************************************** - * GET_FIELD -- Find specified name and returns data * - * * - * INPUT: char * - the id of the field that holds the data. * - * char & - the reference to store the data into * - * * - * OUTPUT: true if the field was found, false if it was not. * - * * - * WARNINGS: The data reference is not changed if the field is not * - * found. * - * * - * HISTORY: * - * 04/23/1996 PWG : Created. * - *========================================================================*/ -bit8 PacketClass::Get_Field(char *id, char &data) -{ - FieldClass *field = Find_Field(id); - if (field) { - data = *((char *)field->Data); - } - return((field) ? true : false); -} - - -/************************************************************************** - * GET_FIELD -- Find specified name and returns data * - * * - * INPUT: char * - the id of the field that holds the data. * - * unsigned char & - the reference to store the data into * - * * - * OUTPUT: true if the field was found, false if it was not. * - * * - * WARNINGS: The data reference is not changed if the field is not * - * found. * - * * - * HISTORY: * - * 04/23/1996 PWG : Created. * - *========================================================================*/ -bit8 PacketClass::Get_Field(char *id, unsigned char &data) -{ - FieldClass *field = Find_Field(id); - if (field) { - data = *((unsigned char *)field->Data); - } - return((field) ? true : false); -} - - -/************************************************************************** - * GET_FIELD -- Find specified name and returns data * - * * - * INPUT: char * - the id of the field that holds the data. * - * short & - the reference to store the data into * - * * - * OUTPUT: true if the field was found, false if it was not. * - * * - * WARNINGS: The data reference is not changed if the field is not * - * found. * - * * - * HISTORY: * - * 04/23/1996 PWG : Created. * - *========================================================================*/ -bit8 PacketClass::Get_Field(char *id, short &data) -{ - FieldClass *field = Find_Field(id); - if (field) { - data = *((short *)field->Data); - } - return((field) ? true : false); -} - - -/************************************************************************** - * GET_FIELD -- Find specified name and returns data * - * * - * INPUT: char * - the id of the field that holds the data. * - * unsigned short & - the reference to store the data into * - * * - * OUTPUT: true if the field was found, false if it was not. * - * * - * WARNINGS: The data reference is not changed if the field is not * - * found. * - * * - * HISTORY: * - * 04/23/1996 PWG : Created. * - *========================================================================*/ -bit8 PacketClass::Get_Field(char *id, unsigned short &data) -{ - FieldClass *field = Find_Field(id); - if (field) { - data = *((unsigned short *)field->Data); - } - return((field) ? true : false); -} - - -/************************************************************************** - * GET_FIELD -- Find specified name and returns data * - * * - * INPUT: char * - the id of the field that holds the data. * - * long & - the reference to store the data into * - * * - * OUTPUT: true if the field was found, false if it was not. * - * * - * WARNINGS: The data reference is not changed if the field is not * - * found. * - * * - * HISTORY: * - * 04/23/1996 PWG : Created. * - *========================================================================*/ -bit8 PacketClass::Get_Field(char *id, long &data) -{ - FieldClass *field = Find_Field(id); - if (field) { - data = *((long *)field->Data); - } - return((field) ? true : false); -} - - - -bit8 PacketClass::Get_Field(char *id, int &data) -{ - FieldClass *field = Find_Field(id); - if (field) { - data = *((int *)field->Data); - } - return((field) ? true : false); -} - - - -/************************************************************************** - * GET_FIELD -- Find specified name and returns data as a string * - * * - * INPUT: char * - the id of the field that holds the data. * - * char * - the string to store the data into * - * * - * OUTPUT: true if the field was found, false if it was not. * - * * - * WARNINGS: The string is not changed if the field is not found. It * - * is assumed that the string variabled specified by the * - * pointer is large enough to hold the data. * - * * - * HISTORY: * - * 04/23/1996 PWG : Created. * - *========================================================================*/ -bit8 PacketClass::Get_Field(char *id, char *data) -{ - FieldClass *field = Find_Field(id); - if (field) { - strcpy(data, (char *)field->Data); - } - return((field) ? true : false); -} - -/************************************************************************** - * GET_FIELD -- Find specified name and returns data * - * * - * INPUT: char * - the id of the field that holds the data. * - * unsigned long & - the reference to store the data into * - * * - * OUTPUT: true if the field was found, false if it was not. * - * * - * WARNINGS: The data reference is not changed if the field is not * - * found. * - * * - * HISTORY: * - * 04/23/1996 PWG : Created. * - *========================================================================*/ -bit8 PacketClass::Get_Field(char *id, unsigned long &data) -{ - FieldClass *field = Find_Field(id); - if (field) { - data = *((unsigned long *)field->Data); - } - return((field) ? true : false); -} - -bit8 PacketClass::Get_Field(char *id, unsigned &data) -{ - FieldClass *field = Find_Field(id); - if (field) { - data = *((unsigned *)field->Data); - } - return((field) ? true : false); -} - - - -/************************************************************************** - * GET_FIELD -- Find specified name and returns data * - * * - * INPUT: char * - the id of the field that holds the data. * - * void * - the reference to store the data into * - * int - the length of the buffer passed in * - * * - * OUTPUT: true if the field was found, false if it was not. * - * * - * WARNINGS: The data reference is not changed if the field is not * - * found. * - * * - * HISTORY: * - * 6/4/96 4:46PM ST : Created * - *========================================================================*/ -bit8 PacketClass::Get_Field(char *id, void *data, int &length) -{ - FieldClass *field = Find_Field(id); - if (field) { - memcpy (data, field->Data, MIN(field->Size, length)); - length = (int) field->Size; - } - return((field) ? true : false); -} - - -unsigned short PacketClass::Get_Field_Size(char* id) -{ - FieldClass *field = Find_Field(id); - if (field) - return field->Get_Size(); - else - return 0; -} - diff --git a/Generals/Code/Tools/matchbot/wnet/packet.h b/Generals/Code/Tools/matchbot/wnet/packet.h deleted file mode 100644 index 1bd2141833c..00000000000 --- a/Generals/Code/Tools/matchbot/wnet/packet.h +++ /dev/null @@ -1,108 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/*************************************************************************** - * * - * Project Name : Westwood Auto Registration App * - * * - * File Name : PACKET.H * - * * - * Programmer : Philip W. Gorrow * - * * - * Start Date : 04/19/96 * - * * - * Last Update : April 19, 1996 [PWG] * - * * - * This header defines the functions for the PacketClass. The packet * - * class is used to create a linked list of field entries which can be * - * converted to a linear packet in a COMMS API compatible format. * - * * - * Packets can be created empty and then have fields added to them or can * - * be created from an existing linear packet. * - * * - *-------------------------------------------------------------------------* - * Functions: * - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - -#include "field.h" -#include - - -class PacketClass -{ - public: - - PacketClass(short id = 0) - { - Size = 0; - ID = id; - Head = 0; - } - PacketClass(char *cur_buf); - ~PacketClass(); - - // - // This function allows us to add a field to the start of the list. As the field is just - // a big linked list it makes no difference which end we add a member to. - // - void Add_Field(FieldClass *field); - - // - // These conveniance functions allow us to add a field directly to the list without - // having to worry about newing one first. - // - void Add_Field(char *field, char data) {Add_Field(new FieldClass(field, data));}; - void Add_Field(char *field, unsigned char data) {Add_Field(new FieldClass(field, data));}; - void Add_Field(char *field, short data) {Add_Field(new FieldClass(field, data));}; - void Add_Field(char *field, unsigned short data) {Add_Field(new FieldClass(field, data));}; - void Add_Field(char *field, long data) {Add_Field(new FieldClass(field, data));}; - void Add_Field(char *field, unsigned long data) {Add_Field(new FieldClass(field, data));}; - void Add_Field(char *field, char *data) {Add_Field(new FieldClass(field, data));}; - void Add_Field(char *field, void *data, int length) {Add_Field(new FieldClass(field, data, length));}; - - // - // These functions search for a field of a given name in the list and - // return the data via a reference value. - // - FieldClass *Find_Field(char *id); - - bit8 Get_Field(char *id, int &data); - bit8 Get_Field(char *id, char &data); - bit8 Get_Field(char *id, unsigned char &data); - bit8 Get_Field(char *id, short &data); - bit8 Get_Field(char *id, unsigned short &data); - bit8 Get_Field(char *id, long &data); - bit8 Get_Field(char *id, unsigned long &data); - bit8 Get_Field(char *id, unsigned &data); - bit8 Get_Field(char *id, char *data); - bit8 Get_Field(char *id, void *data, int &length); - unsigned short Get_Field_Size(char* id); - - // gks 9/25/2000 - FieldClass *Get_Field_At(int position); - int Get_Num_Fields(); - - char *Create_Comms_Packet(int &size); - - private: - unsigned short Size; - short ID; - FieldClass *Head; - FieldClass *Current; -}; - diff --git a/Generals/Code/Tools/matchbot/wnet/tcp.cpp b/Generals/Code/Tools/matchbot/wnet/tcp.cpp deleted file mode 100644 index 4c26d3231dd..00000000000 --- a/Generals/Code/Tools/matchbot/wnet/tcp.cpp +++ /dev/null @@ -1,1242 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -TCP Neal Kettler neal@westwood.com -****************************************************************************** - -A general purpose TCP class that can be used in either CLIENT or -SERVER mode. Note that this uses non-blocking sockets. - -The FD_* macros: - - FD_CLR(int fd, fd_set *set); // clear a single FD - FD_ISSET(int fd, fd_set *set); // check whether a single FD is set - FD_SET(int fd, fd_set *set); // set a single FD - FD_ZERO(fd_set * set); // clear the entire set - -NOTE: The fd_set returned by 'Wait' is static, don't call delete -on it! - - -If you are writing a CLIENT: - The last argument to many functions is an integer whichFD, this is used -only by SERVER mode, so you can omit this argument. Sample Code: - -fd_set *fdSet; -uint8 *buff=new uint8[1024]; -int retval; -TCP tcp(CLIENT); - -tcp.Bind((uint32)0,(uint16)0); // let system pick local IP and a Port for you -tcp.Connect("tango",13); // can connect by name or "10.1.1.10" - // or the integer in host byte order - -fdSet=tcp.Wait(10,0); // wait for UP TO 10 sec and 0 microseconds -if (FD_ISSET(tcp.GetFD(),fdSet)) // Is there something to read? -{ - retval=tcp.Read(buff,1024); // Read something - // Retval will contain the number of - // bytes read, or... - // 0 = remote end closed connection - // -1 = nothing to read - fprintf(stderr,"%s",buff); -} -else - fprintf(stderr,"Nothing was read!\n"); - - -If you are writing a SERVER: - - The structure called 'clientList' contains all the File Descriptors -that have connected to the server. Make sure you look at the FD_* -functions so you can use this sort of structure. When you are writing -a server, you need to specify the 'whichFD' arguments to all the -functions. Sample Code: - -fd_set *fdSet; -uint8 *buff=new uint8[1024]; -int retval; -TCP tcp(SERVER); - -tcp.Bind((uint32)0,(uint16)2121); // You need to bind to a well defined - // port number or nobody will know where - // to connect to. - -while (1) -{ - fdSet=tcp.Wait(-1,-1); // Wait until there is something on the socket - if (FD_ISSET(tcp.GetFD(),fdSet)) // somebody must want a connection - { - retval=tcp.GetConnection(); // Get a connection if somebody's trying - if (retval!=-1) - { - tcp.Write("Hello World!\n",strlen("Hello World!\n"),retval); - tcp.Close(retval); - } - } -} - - -\****************************************************************************/ - - -#include "tcp.h" -#include - -#ifndef _WINDOWS -#include -#define closesocket close -#endif - -// newMode should be either CLIENT or SERVER -TCP::TCP(int new_mode) -{ - mode=CLIENT; - maxFD=0; - fd = -1; - clientCount=0; - if ((new_mode==CLIENT)||(new_mode==SERVER)) - mode=new_mode; - FD_ZERO(&clientList); - connectionState=CLOSED; - inputDelay=5; - outputDelay=5; -} - -// Create a TCP object on a pre-existing socket -TCP::TCP(int new_mode,sint16 socket) -{ - sint32 retval; - - mode=CLIENT; - maxFD= socket; - fd = socket; - clientCount=0; - if ((new_mode==CLIENT)||(new_mode==SERVER)) - mode=new_mode; - FD_ZERO(&clientList); - - inputDelay=5; - outputDelay=5; - - - retval=SetBlocking(FALSE,socket); // set to NB mode - //DBGMSG("Setblocking: "<0)&&(c!=0)) - { - string[i]=c; - if (c=='\n') - return(string); - i++; - } - else if ((retval==0)&&(i==0)) - { - DBGMSG("Remote endpoint closed (1)"); - return(NULL); - } - else if (retval==0) - return(string); - } - return(string); -} - - -// only specify whichFD if this is a server -sint32 TCP::Read(uint8 *msg,uint32 len,sint32 whichFD) -{ - sint32 retval; - //DBGMSG("In read, mode: "<0) - bytes_read+=retval; - // otherwise some error - } - return(bytes_read); -} - - - -// only specify whichFD if this is a server -// Peek at data in system buffer -sint32 TCP::Peek(uint8 *msg,uint32 len,sint32 whichFD) -{ - sint32 retval; - if (mode==CLIENT) - { - retval=recv(fd,(char *)msg,len,MSG_PEEK); - if (retval==0) - Close(); - return(retval); - } - else if (mode==SERVER) - { - if ((whichFD<=maxFD) && (FD_ISSET(whichFD,&clientList))) - { - retval=recv(whichFD,(char *)msg,len,MSG_PEEK); - if (retval==0) - Close(whichFD); - return(retval); - } - else - return(0); // closed - } - return(-1); -} - - -// only specify whichFD if this is a server -// (this is used for non-8 bit clean pipes, you probably don't -// want to use it!) -sint32 TCP::EncapsulatedRead(uint8 *msg,uint32 len,sint32 whichFD) -{ - sint32 retval,bytesRead=0; - uint32 i; - char data; - - if (mode==CLIENT) - whichFD=fd; - else if (mode==SERVER) - { - if ((whichFD>maxFD) || (!FD_ISSET(whichFD,&clientList))) - return(0); - } - else - return(-1); - - for (i=0; i=0; i--) - if (FD_ISSET(i,&clientList)) - { - maxFD=i; - break; - } - } - FD_CLR((uint32)whichFD,&clientList); - clientCount--; - return(closesocket(whichFD)); - } - } - return(-1); -} - - - -// if 'sec' AND 'usec' are -1 then this will sleep until -// there is socket activity - -int TCP::Wait(sint32 sec,sint32 usec,fd_set &returnSet,sint32 whichFD) -{ - fd_set inputSet; - - FD_ZERO(&inputSet); - - if (mode==SERVER) - { - if (whichFD==0) - { - inputSet=clientList; - if (fd > 0) - FD_SET(fd,&inputSet); - } - else if (whichFD > 0) - FD_SET(whichFD,&inputSet); - } - else if (mode==CLIENT) - { - if (whichFD==0) - whichFD=fd; - if (whichFD > 0) - FD_SET(whichFD,&inputSet); - } - - return(Wait(sec,usec,inputSet,returnSet)); -} - -int TCP::Wait(sint32 sec,sint32 usec,fd_set &givenSet,fd_set &returnSet) -{ - Wtime timeout; - Wtime timenow; - Wtime timethen; - fd_set backupSet; - int retval=0,done,givenMax; - bit8 noTimeout=FALSE; - timeval tv; - - returnSet=givenSet; - backupSet=returnSet; - - if ((sec==-1)&&(usec==-1)) - noTimeout=TRUE; - - timeout.SetSec(sec); - timeout.SetUsec(usec); - timethen+=timeout; - - givenMax=maxFD; - for (uint32 i=0; i<(sizeof(fd_set)*8); i++) // i=maxFD+1 - { - if (FD_ISSET(i,&givenSet)) - givenMax=i; - } - - done=0; - while( ! done) - { - if (noTimeout) - retval=select(givenMax+1,&returnSet,0,0,NULL); - else - { - timeout.GetTimevalMT(tv); - retval=select(givenMax+1,&returnSet,0,0,&tv); - } - - if (retval>=0) - done=1; - - else if ((retval==-1)&&(errno==EINTR)) // in case of signal - { - if (noTimeout==FALSE) - { - timenow.Update(); - timeout=timethen-timenow; - } - if ((noTimeout==FALSE)&&(timenow.GetSec()==0)&&(timenow.GetUsec()==0)) - done=1; - else - returnSet=backupSet; - } - else // maybe out of memory? - { - done=1; - } - } - return(retval); -} - - -void TCP::WaitWrite(sint32 whichFD) -{ - fd_set backupSet; - int retval=0,done; - fd_set outputSet; - - if (whichFD==0) - whichFD=fd; - - if (whichFD==-1) - return; - - FD_ZERO(&outputSet); - FD_SET(whichFD,&outputSet); - backupSet=outputSet; - - done=0; - while( ! done) - { - retval=select(maxFD+1,0,&outputSet,0,NULL); - - if (retval>=0) - done=1; - - else if ((retval==-1)&&(errno==EINTR)) // in case of signal - outputSet=backupSet; - else // maybe out of memory? - done=1; - } -} - -// Can a FD be written to? -bit8 TCP::CanWrite(sint32 whichFD) -{ - int retval=0; - fd_set outputSet; - Wtime timeout; - timeval tv; - - timeout.SetSec(0); - timeout.SetUsec(0); - - if (whichFD==0) - whichFD=fd; - - FD_ZERO(&outputSet); - FD_SET(whichFD,&outputSet); - - timeout.GetTimevalMT(tv); - retval=select(whichFD+1,0,&outputSet,0,&tv); - if (retval>0) - return(TRUE); - else - return(FALSE); -} - - -bit8 TCP::Bind(char *Host,uint16 port,bit8 reuseAddr) -{ - char hostName[100]; - struct hostent *hostStruct; - struct in_addr *hostNode; - - if (isdigit(Host[0])) - return ( Bind( ntohl(inet_addr(Host)), port,reuseAddr) ); - - strcpy(hostName, Host); - - hostStruct = gethostbyname(Host); - if (hostStruct == NULL) - return (0); - hostNode = (struct in_addr *) hostStruct->h_addr; - return ( Bind(ntohl(hostNode->s_addr),port,reuseAddr) ); -} - - -// You must call bind, implicit binding is for sissies -// Well... you can get implicit binding if you pass 0 for either arg - -bit8 TCP::Bind(uint32 IP,uint16 Port,bit8 reuseAddr) -{ - int retval; - int status; - - IP=htonl(IP); - Port=htons(Port); - - addr.sin_family=AF_INET; - addr.sin_port=Port; - addr.sin_addr.s_addr=IP; - fd=socket(AF_INET,SOCK_STREAM,DEFAULT_PROTOCOL); - if (fd==-1) - return(FALSE); - - retval=SetBlocking(FALSE,fd); - if (retval==-1) - ERRMSG("Couldn't set nonblocking mode!"); - - if (reuseAddr==TRUE) - { - uint32 opval; - - #ifdef SO_REUSEPORT -/****************** this may make the socket get garbage data?? - opval=1; - retval=setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&opval,sizeof(opval)); - if (retval!=0) - fprintf(stderr,"Could not set socket to SO_REUSEPORT\n"); -**********************/ - #endif - #ifdef SO_REUSEADDR - opval=1; - retval=setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&opval,sizeof(opval)); - if (retval!=0) - fprintf(stderr,"Could not set socket to SO_REUSEADDR\n"); - #endif - } - - retval=bind(fd,(struct sockaddr *)&addr,sizeof(addr)); - #ifdef _WINDOWS - if (retval==SOCKET_ERROR) - retval=-1; - #endif - - if (retval==-1) - { - status=GetStatus(); - DBGMSG("Bind failure (" << status << ") IP "<< IP <<" PORT "<< ntohs(Port)); - return(FALSE); - } - myIP=IP; - myPort=Port; - maxFD=fd; - - if (mode==SERVER) - listen(fd,64); //Solaris needs lots of listen slots for some reason - - return(TRUE); -} - - -// This is only for clients - -bit8 TCP::Connect(char *Host,uint16 port) -{ - char hostName[100]; - struct hostent *hostStruct; - struct in_addr *hostNode; - - if (isdigit(Host[0])) - return ( Connect( ntohl(inet_addr(Host)), port) ); - - strcpy(hostName, Host); - - hostStruct = gethostbyname(Host); - if (hostStruct == NULL) - {ERRMSG("Can't resolve host");return (0);} - hostNode = (struct in_addr *) hostStruct->h_addr; - return ( Connect(ntohl(hostNode->s_addr),port) ); -} - -bit8 TCP::Connect(uint32 IP,uint16 Port) -{ - int tries,result; - struct timeval sleep_time; - struct sockaddr_in serverAddr; - int status; - - IP=htonl(IP); - Port=htons(Port); - - serverAddr.sin_family=AF_INET; - serverAddr.sin_port=Port; - serverAddr.sin_addr.s_addr=IP; - - if (mode!=CLIENT) - {ERRMSG("Can't connect in server mode");return(FALSE);} - - tries=0; - result=-1; - - - // try 10 connects with a greater and greater sleep time after each one - // this can go on for upto 5.4 seconds - while ((tries < 10) && (result == -1)) - { - ClearStatus(); - result = connect(fd,(struct sockaddr *)&serverAddr, sizeof(serverAddr)); - status=GetStatus(); - - #ifdef _WINDOWS - if (result==SOCKET_ERROR) - result=-1; - #endif - - if ((status == ISCONN) && (result == -1)) - { - result = 0; - } - if (result == -1) - { - if ((status!=INPROGRESS)&&(status!=ALREADY)&&(status!=AGAIN)&& - (status!=WOULDBLOCK)) - { - Close(); - Bind(myIP,myPort); - } - tries++; - sleep_time.tv_sec = 0; - sleep_time.tv_usec = (100000*(tries+1)); - #ifdef WIN32 - Sleep((sleep_time.tv_usec)/1000); - #else - select(0, 0, 0, 0, &sleep_time); - #endif - } - } - - if (result == -1) - { - return(FALSE); - } - connectionState=CONNECTED; - return (TRUE); -} - - - -// Asynchronous Connection -bit8 TCP::ConnectAsync(char *Host,uint16 port) -{ - char hostName[100]; - struct hostent *hostStruct; - struct in_addr *hostNode; - - if (isdigit(Host[0])) - return ( ConnectAsync( ntohl(inet_addr(Host)), port) ); - - strcpy(hostName, Host); - - hostStruct = gethostbyname(Host); - if (hostStruct == NULL) - return (0); - hostNode = (struct in_addr *) hostStruct->h_addr; - return ( ConnectAsync(ntohl(hostNode->s_addr),port) ); -} - -// Asynchronous Connection -bit8 TCP::ConnectAsync(uint32 IP,uint16 Port) -{ - int result; - struct sockaddr_in serverAddr; - int status,connectErrno; - int retval; - - IP=htonl(IP); - Port=htons(Port); - - serverAddr.sin_family=AF_INET; - serverAddr.sin_port=Port; - serverAddr.sin_addr.s_addr=IP; - - if (mode!=CLIENT) - return(FALSE); - - result=-1; - - if (connectionState==CONNECTING) - { - if (IsConnected(fd)) - { - DBGMSG("CONNECTION COMPLETE at point 1"); - connectionState=CONNECTED; - return(TRUE); - } - else - return(TRUE); // Still trying - } - - ClearStatus(); - result = connect(fd,(struct sockaddr *)&serverAddr, sizeof(serverAddr)); - connectErrno=errno; - status=GetStatus(); - - #ifdef _WINDOWS - if (result==SOCKET_ERROR) - { - DBGMSG("Socket error 1 " << status); - result=-1; - } - #endif - - // If we have a bogus FD, try again after closing and re-binding - if ((result==-1)&&((status==BADF)||(status==NOTSOCK)||(status==INVAL))) - { - Close(); - retval=Bind(myIP,myPort); - DBGMSG("BIND = "<maxFD) - maxFD=clientFD; - FD_SET(clientFD,&clientList); - clientCount++; - } - return(clientFD); -} - -sint32 TCP::GetConnection(struct sockaddr *clientAddr) -{ - if (mode!=SERVER) - return(-1); - - sint32 clientFD; - int addrlen=sizeof(struct sockaddr); - - clientFD=accept(fd,(struct sockaddr *)clientAddr,&addrlen); - if (clientFD!=-1) - { - if (clientFD>maxFD) - maxFD=clientFD; - FD_SET(clientFD,&clientList); - clientCount++; - } - return(clientFD); -} - diff --git a/Generals/Code/Tools/matchbot/wnet/tcp.h b/Generals/Code/Tools/matchbot/wnet/tcp.h deleted file mode 100644 index 3f63e4e9d36..00000000000 --- a/Generals/Code/Tools/matchbot/wnet/tcp.h +++ /dev/null @@ -1,199 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -/****************************************************************************\ -TCP Neal Kettler neal@westwood.com - -\****************************************************************************/ - -#ifndef TCP_HEADER -#define TCP_HEADER - -#include -#include -#include -#include -#include -#include - -#ifdef _WINDOWS - -#include -#include -#define close _close -#define read _read -#define write _write - -#else //UNIX -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef signed int SOCKET; - -#endif - - -#ifdef AIX -#include -#endif - -#define DEFAULT_PROTOCOL 0 - -#include "wlib/wstypes.h" -#include "wlib/wdebug.h" -#include "wlib/wtime.h" - -class TCP -{ - -// DATA --------------- - -private: - int mode; // client or server - sint32 fd; // the primary FD - - uint32 myIP; // after bind myIP & myPort will be - uint16 myPort; // whatever we bound to - - struct sockaddr_in addr; - int maxFD; // value of the biggest FD - int clientCount; // how many clients open - - - sint32 inputDelay; // default delay for semi-blocking reads - sint32 outputDelay; // default delay for semi-blocking writes - - enum ConnectionState - { - CLOSED, - CONNECTING, - CONNECTED - } connectionState; // What state is client FD in - -public: - - enum - { - CLIENT = 1, - SERVER = 2 - }; - - // These defines specify a system independent way to - // get error codes for socket services. - enum - { - OK, // Everything's cool - UNKNOWN, // There was an error of unknown type - ISCONN, // The socket is already connected - INPROGRESS, // The socket is non-blocking and the operation - // isn't done yet - ALREADY, // The socket is already attempting a connection - // but isn't done yet - AGAIN, // Try again. - ADDRINUSE, // Address already in use - ADDRNOTAVAIL, // That address is not available on the remote host - BADF, // Not a valid FD - CONNREFUSED, // Connection was refused - INTR, // Operation was interrupted - NOTSOCK, // FD wasn't a socket - PIPE, // That operation just made a SIGPIPE - WOULDBLOCK, // That operation would block - INVAL, // Invalid - TIMEDOUT // Timeout - }; - - // for client list (if this is a server) - fd_set clientList; - - -// CODE ---------------- - -public: - TCP(int newMode); - TCP(int newMode,sint16 socket); - ~TCP(); - bit8 Bind(uint32 IP,uint16 port,bit8 reuseAddr=FALSE); - bit8 Bind(char *Host,uint16 port,bit8 reuseAddr=FALSE); - - sint32 GetMaxFD(void); - - bit8 Connect(uint32 IP,uint16 port); - bit8 Connect(char *Host,uint16 port); - bit8 ConnectAsync(uint32 IP,uint16 port); - bit8 ConnectAsync(char *Host,uint16 port); - - bit8 IsConnected(sint32 whichFD=0); - - sint32 GetFD(void); - sint32 GetClientCount(void) { return(clientCount); } - - // Get IP or Port of a connected endpoint - uint32 GetRemoteIP(sint32 whichFD=0); - uint16 GetRemotePort(sint32 whichFD=0); - - sint32 GetConnection(void); - sint32 GetConnection(struct sockaddr *clientAddr); - void WaitWrite(sint32 whichFD=0); - bit8 CanWrite(sint32 whichFD=0); - sint32 Write(const uint8 *msg,uint32 len,sint32 whichFD=0); - sint32 WriteNB(uint8 *msg,uint32 len,sint32 whichFD=0); - sint32 EncapsulatedWrite(uint8 *msg,uint32 len,sint32 whichFD=0); - sint32 WriteString(char *msg,sint32 whichFD=0); - sint32 Printf(sint32 whichFD,const char *format,...); - sint32 Read(uint8 *msg,uint32 len,sint32 whichFD=0); - sint32 TimedRead(uint8 *msg,uint32 len,int seconds,sint32 whichFD=0); - sint32 Peek(uint8 *msg,uint32 len,sint32 whichFD=0); - sint32 EncapsulatedRead(uint8 *msg,uint32 len,sint32 whichFD=0); - - char *Gets(char *string,int n,int whichFD=0); - - // Wait on all sockets (or a specified one) - // return when ready for reading (or timeout occurs) - int Wait(sint32 sec,sint32 usec,fd_set &returnSet,sint32 whichFD=0); - int Wait(sint32 sec,sint32 usec,fd_set &inputSet,fd_set &returnSet); - - int GetStatus(void); - void ClearStatus(void); - - //sint32 GetSockStatus(sint32 whichFD=0); - - // give up ownership of the socket without closing it - void DisownSocket(void); - - sint32 Close(sint32 whichFD=0); - sint32 CloseAll(void); // close all sockets (same as close for client) - - sint32 SetBlocking(bit8 block,sint32 whichFD=0); - - // Set default delays for semi-blocking reads & writes - // default input = 5, output = 5 - // this is new and not used everywhere - // - bit8 SetInputDelay(sint32 delay) { inputDelay=delay; return(TRUE); }; - bit8 SetOutputDelay(sint32 delay) { outputDelay=delay; return(TRUE); }; - -}; - -#endif diff --git a/Generals/Code/Tools/matchbot/wnet/udp.cpp b/Generals/Code/Tools/matchbot/wnet/udp.cpp deleted file mode 100644 index afa6d16150f..00000000000 --- a/Generals/Code/Tools/matchbot/wnet/udp.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/* -** Command & Conquer Generals(tm) -** Copyright 2025 Electronic Arts Inc. -** -** This program is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program. If not, see . -*/ - -#include "udp.h" -#include "wlib/wdebug.h" - -UDP::UDP() -{ - fd=0; -} - -UDP::~UDP() -{ -} - -sint32 UDP::Bind(char *Host,uint16 port) -{ - char hostName[100]; - struct hostent *hostStruct; - struct in_addr *hostNode; - - if (isdigit(Host[0])) - return ( Bind( ntohl(inet_addr(Host)), port) ); - - strcpy(hostName, Host); - - hostStruct = gethostbyname(Host); - if (hostStruct == NULL) - return (0); - hostNode = (struct in_addr *) hostStruct->h_addr; - return ( Bind(ntohl(hostNode->s_addr),port) ); -} - -// You must call bind, implicit binding is for sissies -// Well... you can get implicit binding if you pass 0 for either arg -sint32 UDP::Bind(uint32 IP,uint16 Port) -{ - int retval; - int status; - - IP=htonl(IP); - Port=htons(Port); - - addr.sin_family=AF_INET; - addr.sin_port=Port; - addr.sin_addr.s_addr=IP; - fd=socket(AF_INET,SOCK_DGRAM,DEFAULT_PROTOCOL); - #ifdef _WINDOWS - if (fd==SOCKET_ERROR) - fd=-1; - #endif - if (fd==-1) - return(UNKNOWN); - - retval=bind(fd,(struct sockaddr *)&addr,sizeof(addr)); - - #ifdef _WINDOWS - if (retval==SOCKET_ERROR) - retval=-1; - #endif - if (retval==-1) - { - status=GetStatus(); - //CERR("Bind failure (" << status << ") IP " << IP << " PORT " << Port ) - return(status); - } - - int namelen=sizeof(addr); - getsockname(fd, (struct sockaddr *)&addr, &namelen); - - myIP=ntohl(addr.sin_addr.s_addr); - myPort=ntohs(addr.sin_port); - - retval=SetBlocking(FALSE); - if (retval==-1) - fprintf(stderr,"Couldn't set nonblocking mode!\n"); - - return(OK); -} - -bit8 UDP::getLocalAddr(uint32 &ip, uint16 &port) -{ - ip=myIP; - port=myPort; - return(OK); -} - - -// private function -sint32 UDP::SetBlocking(bit8 block) -{ - #ifdef _WINDOWS - unsigned long flag=1; - if (block) - flag=0; - int retval; - retval=ioctlsocket(fd,FIONBIO,&flag); - if (retval==SOCKET_ERROR) - return(UNKNOWN); - else - return(OK); - #else // UNIX - int flags = fcntl(fd, F_GETFL, 0); - if (block==FALSE) // set nonblocking - flags |= O_NONBLOCK; - else // set blocking - flags &= ~(O_NONBLOCK); - - if (fcntl(fd, F_SETFL, flags) < 0) - { - return(UNKNOWN); - } - return(OK); - #endif -} - - -sint32 UDP::Write(uint8 *msg,uint32 len,uint32 IP,uint16 port) -{ - sint32 retval; - struct sockaddr_in to; - - // This happens frequently - if ((IP==0)||(port==0)) return(ADDRNOTAVAIL); - - errno=0; - to.sin_port=htons(port); - to.sin_addr.s_addr=htonl(IP); - to.sin_family=AF_INET; - - ClearStatus(); - retval=sendto(fd,(char *)msg,len,0,(struct sockaddr *)&to,sizeof(to)); - #ifdef _WINDOWS - if (retval==SOCKET_ERROR) - retval=-1; - #endif - - return(retval); -} - -sint32 UDP::Read(uint8 *msg,uint32 len,sockaddr_in *from) -{ - sint32 retval; - int alen=sizeof(sockaddr_in); - - if (from!=NULL) - { - retval=recvfrom(fd,(char *)msg,len,0,(struct sockaddr *)from,&alen); - #ifdef _WINDOWS - if (retval==SOCKET_ERROR) - retval=-1; - #endif - } - else - { - retval=recvfrom(fd,(char *)msg,len,0,NULL,NULL); - #ifdef _WINDOWS - if (retval==SOCKET_ERROR) - retval=-1; - #endif - } - return(retval); -} - - -void UDP::ClearStatus(void) -{ - #ifndef _WINDOWS - errno=0; - #endif -} - -UDP::sockStat UDP::GetStatus(void) -{ - #ifdef _WINDOWS - int status=WSAGetLastError(); - if (status==0) return(OK); - else if (status==WSAEINTR) return(INTR); - else if (status==WSAEINPROGRESS) return(INPROGRESS); - else if (status==WSAECONNREFUSED) return(CONNREFUSED); - else if (status==WSAEINVAL) return(INVAL); - else if (status==WSAEISCONN) return(ISCONN); - else if (status==WSAENOTSOCK) return(NOTSOCK); - else if (status==WSAETIMEDOUT) return(TIMEDOUT); - else if (status==WSAEALREADY) return(ALREADY); - else if (status==WSAEWOULDBLOCK) return(WOULDBLOCK); - else if (status==WSAEBADF) return(BADF); - else return(UNKNOWN); - #else - int status=errno; - if (status==0) return(OK); - else if (status==EINTR) return(INTR); - else if (status==EINPROGRESS) return(INPROGRESS); - else if (status==ECONNREFUSED) return(CONNREFUSED); - else if (status==EINVAL) return(INVAL); - else if (status==EISCONN) return(ISCONN); - else if (status==ENOTSOCK) return(NOTSOCK); - else if (status==ETIMEDOUT) return(TIMEDOUT); - else if (status==EALREADY) return(ALREADY); - else if (status==EAGAIN) return(AGAIN); - else if (status==EWOULDBLOCK) return(WOULDBLOCK); - else if (status==EBADF) return(BADF); - else return(UNKNOWN); - #endif -} - - - -// -// Wait for net activity on this socket -// -int UDP::Wait(sint32 sec,sint32 usec,fd_set &returnSet) -{ - fd_set inputSet; - - FD_ZERO(&inputSet); - FD_SET(fd,&inputSet); - - return(Wait(sec,usec,inputSet,returnSet)); -} - -// -// Wait for net activity on a list of sockets -// -int UDP::Wait(sint32 sec,sint32 usec,fd_set &givenSet,fd_set &returnSet) -{ - Wtime timeout,timenow,timethen; - fd_set backupSet; - int retval=0,done,givenMax; - bit8 noTimeout=FALSE; - timeval tv; - - returnSet=givenSet; - backupSet=returnSet; - - if ((sec==-1)&&(usec==-1)) - noTimeout=TRUE; - - timeout.SetSec(sec); - timeout.SetUsec(usec); - timethen+=timeout; - - givenMax=fd; - for (uint32 i=0; i<(sizeof(fd_set)*8); i++) // i=maxFD+1 - { - if (FD_ISSET(i,&givenSet)) - givenMax=i; - } - ///DBGMSG("WAIT fd="<. -*/ - -#ifndef UDP_HEADER -#define UDP_HEADER - -#include -#include -#include -#include -#include - -#ifdef _WINDOWS -#include -#include -#define close _close -#define read _read -#define write _write - -#else //UNIX -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#ifdef AIX -#include -#endif - -#define DEFAULT_PROTOCOL 0 - -#include -#include - -class UDP -{ - // DATA - private: - sint32 fd; - uint32 myIP; - uint16 myPort; - struct sockaddr_in addr; - - // These defines specify a system independent way to - // get error codes for socket services. - enum sockStat - { - OK = 0, // Everything's cool - UNKNOWN = -1, // There was an error of unknown type - ISCONN = -2, // The socket is already connected - INPROGRESS = -3, // The socket is non-blocking and the operation - // isn't done yet - ALREADY = -4, // The socket is already attempting a connection - // but isn't done yet - AGAIN = -5, // Try again. - ADDRINUSE = -6, // Address already in use - ADDRNOTAVAIL = -7, // That address is not available on the remote host - BADF = -8, // Not a valid FD - CONNREFUSED = -9, // Connection was refused - INTR =-10, // Operation was interrupted - NOTSOCK =-11, // FD wasn't a socket - PIPE =-12, // That operation just made a SIGPIPE - WOULDBLOCK =-13, // That operation would block - INVAL =-14, // Invalid - TIMEDOUT =-15 // Timeout - }; - -// CODE - private: - sint32 SetBlocking(bit8 block); - - public: - UDP(); - ~UDP(); - sint32 Bind(uint32 IP,uint16 port); - sint32 Bind(char *Host,uint16 port); - sint32 Write(uint8 *msg,uint32 len,uint32 IP,uint16 port); - sint32 Read(uint8 *msg,uint32 len,sockaddr_in *from); - sockStat GetStatus(void); - void ClearStatus(void); - int Wait(sint32 sec,sint32 usec,fd_set &returnSet); - int Wait(sint32 sec,sint32 usec,fd_set &givenSet,fd_set &returnSet); - - bit8 getLocalAddr(uint32 &ip, uint16 &port); - sint32 getFD(void) { return(fd); } - - bit8 SetInputBuffer(uint32 bytes); - bit8 SetOutputBuffer(uint32 bytes); - int GetInputBuffer(void); - int GetOutputBuffer(void); -}; - -#endif diff --git a/GeneralsMD/Code/Tools/CMakeLists.txt b/GeneralsMD/Code/Tools/CMakeLists.txt index 2b4cab04584..99fb9d7ee40 100644 --- a/GeneralsMD/Code/Tools/CMakeLists.txt +++ b/GeneralsMD/Code/Tools/CMakeLists.txt @@ -20,7 +20,6 @@ if(RTS_BUILD_ZEROHOUR_EXTRAS) add_subdirectory(buildVersionUpdate) add_subdirectory(Launcher) add_subdirectory(mangler) - add_subdirectory(matchbot) add_subdirectory(PATCHGET) add_subdirectory(textureCompress) add_subdirectory(timingTest) diff --git a/GeneralsMD/Code/Tools/matchbot/CMakeLists.txt b/GeneralsMD/Code/Tools/matchbot/CMakeLists.txt deleted file mode 100644 index d5d4183f70d..00000000000 --- a/GeneralsMD/Code/Tools/matchbot/CMakeLists.txt +++ /dev/null @@ -1,78 +0,0 @@ -set(MATCHBOT_SRC - "debug.cpp" - "debug.h" - "encrypt.cpp" - "encrypt.h" - "generals.cpp" - "generals.h" - "global.cpp" - "global.h" - "main.cpp" - "matcher.cpp" - "matcher.h" - "mydebug.cpp" - "mydebug.h" - "rand.cpp" - "rand.h" - "wlib/arraylist.h" - "wlib/configfile.cpp" - "wlib/configfile.h" - "wlib/critsec.cpp" - "wlib/critsec.h" - "wlib/dictionary.h" - "wlib/filed.h" - "wlib/linkedlist.h" - "wlib/mboxd.h" - "wlib/monod.cpp" - "wlib/monod.h" - "wlib/odevice.h" - "wlib/sem4.cpp" - "wlib/sem4.h" - "wlib/stderrd.h" - "wlib/stdoutd.h" - "wlib/streamer.cpp" - "wlib/streamer.h" - "wlib/syslogd.cpp" - "wlib/syslogd.h" - "wlib/threadfac.cpp" - "wlib/threadfac.h" - "wlib/threadsafe.h" - "wlib/timezone.cpp" - "wlib/timezone.h" - "wlib/ustring.h" - "wlib/wdebug.cpp" - "wlib/wdebug.h" - "wlib/wstring.cpp" - "wlib/wstring.h" - "wlib/wstypes.h" - "wlib/wtime.cpp" - "wlib/wtime.h" - "wlib/xtime.cpp" - "wlib/xtime.h" - "wnet/field.cpp" - "wnet/field.h" - "wnet/packet.cpp" - "wnet/packet.h" - "wnet/tcp.cpp" - "wnet/tcp.h" - "wnet/udp.cpp" - "wnet/udp.h" -) - -add_executable(z_matchbot WIN32) -set_target_properties(z_matchbot PROPERTIES OUTPUT_NAME matchbot) - -target_sources(z_matchbot PRIVATE ${MATCHBOT_SRC}) - -target_include_directories(z_matchbot PRIVATE . wlib wnet) - -target_link_libraries(z_matchbot PRIVATE - gamespy::gamespy - core_config - core_utility - stlport -) - -if(WIN32 OR "${CMAKE_SYSTEM}" MATCHES "Windows") - target_link_options(z_matchbot PRIVATE /subsystem:console) -endif() From bcfe2e4b5ece570c6235c4f27b814ac6613c2213 Mon Sep 17 00:00:00 2001 From: Toms Date: Sat, 19 Apr 2025 14:11:10 +0300 Subject: [PATCH 2/8] [GEN] Backport BlurTime functionality in WW3D2's ParticleBufferClass (#711) --- .../Source/WWVegas/WW3D2/part_buf.cpp | 292 +++++++++++++++++- .../Libraries/Source/WWVegas/WW3D2/part_buf.h | 13 +- .../Source/WWVegas/WW3D2/part_emt.cpp | 21 +- .../Libraries/Source/WWVegas/WW3D2/part_emt.h | 5 +- 4 files changed, 325 insertions(+), 6 deletions(-) diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.cpp b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.cpp index 7f51891c29f..55caafc76b9 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.cpp +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.cpp @@ -87,6 +87,7 @@ ParticleBufferClass::ParticleBufferClass ParticlePropertyStruct &rotation, float orient_rnd, ParticlePropertyStruct &frame, + ParticlePropertyStruct &blurtime, Vector3 accel, float max_age, TextureClass *tex, @@ -135,6 +136,10 @@ ParticleBufferClass::ParticleBufferClass FrameKeyFrameTimes(NULL), FrameKeyFrameValues(NULL), FrameKeyFrameDeltas(NULL), + NumBlurTimeKeyFrames(0), + BlurTimeKeyFrameTimes(NULL), + BlurTimeKeyFrameValues(NULL), + BlurTimeKeyFrameDeltas(NULL), NumRandomColorEntriesMinus1(0), RandomColorEntries(NULL), NumRandomAlphaEntriesMinus1(0), @@ -153,6 +158,8 @@ ParticleBufferClass::ParticleBufferClass RandomOrientationEntries(NULL), NumRandomFrameEntriesMinus1(0), RandomFrameEntries(NULL), + NumRandomBlurTimeEntriesMinus1(0), + RandomBlurTimeEntries(NULL), PointGroup(NULL), LineRenderer(NULL), Diffuse(NULL), @@ -161,6 +168,7 @@ ParticleBufferClass::ParticleBufferClass Size(NULL), Orientation(NULL), Frame(NULL), + TailPosition(NULL), APT(NULL), PingPongPosition(pingpong), Velocity(NULL), @@ -190,6 +198,9 @@ ParticleBufferClass::ParticleBufferClass // Create the frame array, keyframes, and randomizer table (if needed) Reset_Frames(frame); + // Create the blur time array, keyframes, and randomizer table if needed + Reset_Blur_Times(blurtime); + // We do not add a ref for the emitter (see DTor for detailed explanation) // if (Emitter) Emitter->Add_Ref(); @@ -295,6 +306,10 @@ ParticleBufferClass::ParticleBufferClass(const ParticleBufferClass & src) : FrameKeyFrameTimes(NULL), FrameKeyFrameValues(NULL), FrameKeyFrameDeltas(NULL), + NumBlurTimeKeyFrames(src.NumBlurTimeKeyFrames), + BlurTimeKeyFrameTimes(NULL), + BlurTimeKeyFrameValues(NULL), + BlurTimeKeyFrameDeltas(NULL), RandomColorEntries(NULL), RandomAlphaEntries(NULL), RandomSizeEntries(NULL), @@ -310,14 +325,17 @@ ParticleBufferClass::ParticleBufferClass(const ParticleBufferClass & src) : RandomOrientationEntries(NULL), NumRandomFrameEntriesMinus1(0), RandomFrameEntries(NULL), + NumRandomBlurTimeEntriesMinus1(0), + RandomBlurTimeEntries(NULL), PointGroup(NULL), LineRenderer(NULL), - Diffuse(NULL), + Diffuse(NULL), Color(NULL), Alpha(NULL), Size(NULL), Orientation(NULL), Frame(NULL), + TailPosition(NULL), APT(NULL), PingPongPosition(src.PingPongPosition), Velocity(NULL), @@ -488,6 +506,31 @@ ParticleBufferClass::ParticleBufferClass(const ParticleBufferClass & src) : FrameKeyFrameValues[0] = src.FrameKeyFrameValues[0]; } + // Set up the blur times keyframes + NumRandomBlurTimeEntriesMinus1 = src.NumRandomBlurTimeEntriesMinus1; + if (NumBlurTimeKeyFrames > 0) { + // Copy blur time keyframes + BlurTimeKeyFrameTimes = new unsigned int [NumBlurTimeKeyFrames]; + BlurTimeKeyFrameValues = new float [NumBlurTimeKeyFrames]; + BlurTimeKeyFrameDeltas = new float [NumBlurTimeKeyFrames]; + for (i = 0; i < NumBlurTimeKeyFrames; i++) { + BlurTimeKeyFrameTimes[i] = src.BlurTimeKeyFrameTimes[i]; + BlurTimeKeyFrameValues[i] = src.BlurTimeKeyFrameValues[i]; + BlurTimeKeyFrameDeltas[i] = src.BlurTimeKeyFrameDeltas[i]; + } + + // Copy blur time randomizer table + if (src.RandomBlurTimeEntries) { + RandomBlurTimeEntries = new float [NumRandomBlurTimeEntriesMinus1 + 1]; + for (unsigned int j = 0; j <= NumRandomBlurTimeEntriesMinus1; j++) { + RandomBlurTimeEntries[j] = src.RandomBlurTimeEntries[j]; + } + } + } else { + BlurTimeKeyFrameValues = new float [1]; + BlurTimeKeyFrameValues[0] = src.BlurTimeKeyFrameValues[0]; + } + // We do not add a ref for the emitter (see DTor for detailed explanation) // if (Emitter) Emitter->Add_Ref(); @@ -574,12 +617,16 @@ ParticleBufferClass::~ParticleBufferClass(void) if (FrameKeyFrameTimes) delete [] FrameKeyFrameTimes; if (FrameKeyFrameValues) delete [] FrameKeyFrameValues; if (FrameKeyFrameDeltas) delete [] FrameKeyFrameDeltas; + if (BlurTimeKeyFrameTimes) delete [] BlurTimeKeyFrameTimes; + if (BlurTimeKeyFrameValues) delete [] BlurTimeKeyFrameValues; + if (BlurTimeKeyFrameDeltas) delete [] BlurTimeKeyFrameDeltas; if (RandomColorEntries) delete [] RandomColorEntries; if (RandomAlphaEntries) delete [] RandomAlphaEntries; if (RandomSizeEntries) delete [] RandomSizeEntries; if (RandomRotationEntries) delete [] RandomRotationEntries; if (RandomOrientationEntries) delete [] RandomOrientationEntries; if (RandomFrameEntries) delete [] RandomFrameEntries; + if (RandomBlurTimeEntries) delete [] RandomBlurTimeEntries; if (PointGroup) delete PointGroup; @@ -1920,6 +1967,154 @@ void ParticleBufferClass::Reset_Frames(ParticlePropertyStruct &new_props) } +void ParticleBufferClass::Reset_Blur_Times(ParticlePropertyStruct &new_blur_times) +{ + + unsigned int i; // Used in loops + float oo_intmax = 1.0f / (float)INT_MAX; + unsigned int ui_previous_key_time = 0; + unsigned int ui_current_key_time = 0; + + BlurTimeRandom = new_blur_times.Rand; + + // If the randomizer is effectively zero and there are no keyframes, then we just create a + // values array with one entry and store the starting value in it (the keyframes and random + // table will not be used in this case). + static const float eps_blur = 1e-5f; // Epsilon is equivalent to 1e-5 units per second + bool blurtime_rand_zero = (fabs(new_blur_times.Rand) < eps_blur); + if (blurtime_rand_zero && new_blur_times.NumKeyFrames == 0) { + + // Release Arrays, Reuse KeyFrameValues if the right size, + // otherwise release and reallocate. + if (BlurTimeKeyFrameTimes) { + delete [] BlurTimeKeyFrameTimes; + BlurTimeKeyFrameTimes = NULL; + } + if (BlurTimeKeyFrameDeltas) { + delete [] BlurTimeKeyFrameDeltas; + BlurTimeKeyFrameDeltas = NULL; + } + if (BlurTimeKeyFrameValues) { + if (NumBlurTimeKeyFrames > 1) { + delete [] BlurTimeKeyFrameValues; + BlurTimeKeyFrameValues = new float [1]; + } + } else { + BlurTimeKeyFrameValues = new float [1]; + } + + NumBlurTimeKeyFrames = 0; + NumRandomBlurTimeEntriesMinus1 = 0; + BlurTimeKeyFrameValues[0] = new_blur_times.Start; + + } else { + + // Check times of the keyframes (each keytime must be larger than the + // previous one by at least a millisecond, and we stop at the first + // keytime of MaxAge or larger. (If all keyframes below MaxAge, the value is + // constant during the last segment between last keyframe and MaxAge). + ui_previous_key_time = 0; + unsigned int key = 0; + for (; key < new_blur_times.NumKeyFrames; key++) { + ui_current_key_time = (unsigned int)(new_blur_times.KeyTimes[key] * 1000.0f); + WWASSERT(ui_current_key_time > ui_previous_key_time); + if (ui_current_key_time >= MaxAge) break; + ui_previous_key_time = ui_current_key_time; + } + bool blurtime_constant_at_end = (key == new_blur_times.NumKeyFrames); + + // Reuse BlurTimeKeyFrameValues, BlurTimeKeyFrameTimes and BlurTimeKeyFrameDeltas if the right size, + // otherwise release and reallocate. + unsigned int new_num_key_frames = key + 1;// Includes start keyframe (keytime == 0). + if (new_num_key_frames != NumBlurTimeKeyFrames) { + + if (BlurTimeKeyFrameTimes) { + delete [] BlurTimeKeyFrameTimes; + BlurTimeKeyFrameTimes = NULL; + } + if (BlurTimeKeyFrameValues) { + delete [] BlurTimeKeyFrameValues; + BlurTimeKeyFrameValues = NULL; + } + if (BlurTimeKeyFrameDeltas) { + delete [] BlurTimeKeyFrameDeltas; + BlurTimeKeyFrameDeltas = NULL; + } + + NumBlurTimeKeyFrames = new_num_key_frames; + BlurTimeKeyFrameTimes = new unsigned int [NumBlurTimeKeyFrames]; + BlurTimeKeyFrameValues = new float [NumBlurTimeKeyFrames]; + BlurTimeKeyFrameDeltas = new float [NumBlurTimeKeyFrames]; + } + + // Set keyframes (deltas will be set later) + BlurTimeKeyFrameTimes[0] = 0; + BlurTimeKeyFrameValues[0] = new_blur_times.Start; + for (i = 1; i < NumBlurTimeKeyFrames; i++) { + unsigned int im1 = i - 1; + BlurTimeKeyFrameTimes[i] = (unsigned int)(new_blur_times.KeyTimes[im1] * 1000.0f); + BlurTimeKeyFrameValues[i] = new_blur_times.Values[im1]; + } + + // Do deltas for all frame keyframes except last + for (i = 0; i < NumBlurTimeKeyFrames - 1; i++) { + BlurTimeKeyFrameDeltas[i] = (BlurTimeKeyFrameValues[i + 1] - BlurTimeKeyFrameValues[i]) / + (float)(BlurTimeKeyFrameTimes[i + 1] - BlurTimeKeyFrameTimes[i]); + } + + // Do delta for last frame keyframe (i is NumBlurTimeKeyFrames - 1) + if (blurtime_constant_at_end) { + BlurTimeKeyFrameDeltas[i] = 0.0f; + } else { + // This is OK because if frame_constant_at_end is false, NumBlurTimeKeyFrames is equal or + // smaller than new_props.NumKeyFrames so new_props.Values[NumBlurTimeKeyFrames - 1] and + // new_props.KeyTimes[NumBlurTimeKeyFrames - 1] exist. + BlurTimeKeyFrameDeltas[i] = (new_blur_times.Values[i] - BlurTimeKeyFrameValues[i]) / + (new_blur_times.KeyTimes[i] * 1000.0f - (float)BlurTimeKeyFrameTimes[i]); + } + + // Set up frame randomizer table + if (blurtime_rand_zero) { + + if (RandomBlurTimeEntries) { + // Reuse RandomBlurTimeEntries if the right size, otherwise release and reallocate. + if (NumRandomBlurTimeEntriesMinus1 != 0) { + delete [] RandomBlurTimeEntries; + RandomBlurTimeEntries = new float [1]; + } + } else { + RandomBlurTimeEntries = new float [1]; + } + + NumRandomBlurTimeEntriesMinus1 = 0; + RandomBlurTimeEntries[0] = 0.0f; + } else { + + // Default size of randomizer tables (tables for non-zero randomizers will be this size) + unsigned int pot_num = Find_POT(MaxNum); + unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES); + + if (RandomBlurTimeEntries) { + // Reuse RandomBlurTimeEntries if the right size, otherwise release and reallocate. + if (NumRandomBlurTimeEntriesMinus1 != (default_randomizer_entries - 1)) { + delete [] RandomBlurTimeEntries; + RandomBlurTimeEntries = new float [default_randomizer_entries]; + } + } else { + RandomBlurTimeEntries = new float [default_randomizer_entries]; + } + + NumRandomBlurTimeEntriesMinus1 = default_randomizer_entries - 1; + + float scale = new_blur_times.Rand * oo_intmax; + for (unsigned int j = 0; j <= NumRandomBlurTimeEntriesMinus1; j++) { + RandomBlurTimeEntries[j] = rand_gen * scale; + } + } + } +} + + // This informs the buffer that the emitter is dead, so it can release // its pointer to it and be removed itself after all its particles dies // out. @@ -2046,6 +2241,7 @@ void ParticleBufferClass::Update_Visual_Particle_State(void) unsigned int skey = NumSizeKeyFrames - 1; unsigned int rkey = NumRotationKeyFrames - 1; unsigned int fkey = NumFrameKeyFrames - 1; + unsigned int bkey = NumBlurTimeKeyFrames -1; unsigned int part; Vector3 *color = Color ? Color->Get_Array(): NULL; @@ -2053,6 +2249,16 @@ void ParticleBufferClass::Update_Visual_Particle_State(void) float *size = Size ? Size->Get_Array(): NULL; uint8 *orientation = Orientation ? Orientation->Get_Array(): NULL; uint8 *frame = Frame ? Frame->Get_Array(): NULL; + Vector3 *tailposition = TailPosition ? TailPosition->Get_Array() : NULL; + + Vector3 *position=NULL; + + if (PingPongPosition) { + int pingpong = WW3D::Get_Frame_Count() & 0x1; + position = Position[pingpong]->Get_Array(); + } else { + position = Position[0]->Get_Array(); + } for (part = Start; part < sub1_end; part++) { @@ -2123,6 +2329,19 @@ void ParticleBufferClass::Update_Visual_Particle_State(void) frame[part] = (uint)(((int)(tmp_frame)) & 0xFF); } + + if (tailposition) { + // We go from older to younger particles, so we go backwards from the last keyframe until + // age >= keytime. This loop must terminate because the 0th keytime is 0. + float blur_time = BlurTimeKeyFrameValues[0]; + if (BlurTimeKeyFrameTimes) { + for (; part_age < BlurTimeKeyFrameTimes[bkey]; bkey--); + blur_time = BlurTimeKeyFrameValues[bkey] + + BlurTimeKeyFrameDeltas[bkey] * (float)(part_age - BlurTimeKeyFrameTimes[bkey]) + + RandomBlurTimeEntries[part & NumRandomBlurTimeEntriesMinus1]; + } + tailposition[part]=position[part]-Velocity[part]*blur_time*1000; + } } for (part = sub2_start; part < End; part++) { @@ -2194,6 +2413,19 @@ void ParticleBufferClass::Update_Visual_Particle_State(void) frame[part] = (uint)(((int)(tmp_frame)) & 0xFF); } + + if (tailposition) { + // We go from older to younger particles, so we go backwards from the last keyframe until + // age >= keytime. This loop must terminate because the 0th keytime is 0. + float blur_time = BlurTimeKeyFrameValues[0]; + if (BlurTimeKeyFrameTimes) { + for (; part_age < BlurTimeKeyFrameTimes[bkey]; bkey--); + blur_time = BlurTimeKeyFrameValues[bkey] + + BlurTimeKeyFrameDeltas[bkey] * (float)(part_age - BlurTimeKeyFrameTimes[bkey]) + + RandomBlurTimeEntries[part & NumRandomBlurTimeEntriesMinus1]; + } + tailposition[part]=position[part]-Velocity[part]*blur_time*1000; + } } } @@ -2710,7 +2942,7 @@ void ParticleBufferClass::Get_Frame_Key_Frames (ParticlePropertyStruct &f // // If we have more than just the start rotation, build - // an array of key times and rotation values + // an array of key times and frame values // if (real_keyframe_count > 0) { frames.KeyTimes = W3DNEWARRAY float[real_keyframe_count]; @@ -2744,6 +2976,62 @@ void ParticleBufferClass::Get_Frame_Key_Frames (ParticlePropertyStruct &f return ; } +void ParticleBufferClass::Get_Blur_Time_Key_Frames (ParticlePropertyStruct &blurtimes) const +{ + int real_keyframe_count = (NumBlurTimeKeyFrames > 0) ? (NumBlurTimeKeyFrames - 1) : 0; + bool create_last_keyframe = false; + + // + // Determine if there is a keyframe at the very end of the particle's lifetime + // + if ((BlurTimeKeyFrameDeltas != NULL) && + (BlurTimeKeyFrameDeltas[NumBlurTimeKeyFrames - 1] != 0)) { + real_keyframe_count ++; + create_last_keyframe = true; + } + + blurtimes.Start = BlurTimeKeyFrameValues[0]; + blurtimes.Rand = BlurTimeRandom; + blurtimes.NumKeyFrames = real_keyframe_count; + blurtimes.KeyTimes = NULL; + blurtimes.Values = NULL; + + // + // If we have more than just the start rotation, build + // an array of key times and blur time values + // + if (real_keyframe_count > 0) { + blurtimes.KeyTimes = new float[real_keyframe_count]; + blurtimes.Values = new float[real_keyframe_count]; + + // + // Copy the keytimes and frame values + // + unsigned int index; + for (index = 1; index < NumBlurTimeKeyFrames; index ++) { + blurtimes.KeyTimes[index - 1] = ((float)BlurTimeKeyFrameTimes[index]) / 1000; + blurtimes.Values[index - 1] = BlurTimeKeyFrameValues[index]; + } + + // + // Add a keyframe at the very end of the timeline if necessary + // + if (create_last_keyframe) { + blurtimes.KeyTimes[index - 1] = ((float)MaxAge / 1000); + + // + // Determine what the value of the last keyframe should be + // + float start_blurtime = BlurTimeKeyFrameValues[index - 1]; + float &delta = BlurTimeKeyFrameDeltas[NumBlurTimeKeyFrames - 1]; + float time_delta = MaxAge - BlurTimeKeyFrameTimes[index - 1]; + blurtimes.Values[index - 1] = start_blurtime + (delta * time_delta); + } + } + + return ; +} + void ParticleBufferClass::Set_LOD_Max_Screen_Size(int lod_level,float max_screen_size) { if ((lod_level <0) || (lod_level > 17)) { diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.h b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.h index 5c9828bd042..ec8e0cebfbd 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.h +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.h @@ -89,7 +89,8 @@ class ParticleBufferClass : public RenderObjClass ParticleBufferClass(ParticleEmitterClass *emitter, unsigned int buffer_size, ParticlePropertyStruct &color, ParticlePropertyStruct &opacity, ParticlePropertyStruct &size, ParticlePropertyStruct &rotation, - float orient_rnd, ParticlePropertyStruct &frame, Vector3 accel, + float orient_rnd, ParticlePropertyStruct &frame, + ParticlePropertyStruct &blurtime, Vector3 accel, float max_age, TextureClass *tex, ShaderClass shader, bool pingpong, int render_mode, int frame_mode, const W3dEmitterLinePropertiesStruct * line_props); @@ -151,6 +152,7 @@ class ParticleBufferClass : public RenderObjClass void Reset_Size(ParticlePropertyStruct &new_props); void Reset_Rotations(ParticlePropertyStruct &new_rotations, float orient_rnd); void Reset_Frames(ParticlePropertyStruct &new_frames); + void Reset_Blur_Times(ParticlePropertyStruct &new_blur_times); // This informs the buffer that the emitter is dead, so it can release // its pointer to it and be removed itself after all its particles dies @@ -217,6 +219,7 @@ class ParticleBufferClass : public RenderObjClass void Get_Size_Key_Frames (ParticlePropertyStruct &sizes) const; void Get_Rotation_Key_Frames (ParticlePropertyStruct &rotations) const; void Get_Frame_Key_Frames (ParticlePropertyStruct &frames) const; + void Get_Blur_Time_Key_Frames (ParticlePropertyStruct &blurtimes) const; float Get_Initial_Orientation_Random (void) const { return InitialOrientationRandom; } // Total Active Particle Buffer Count @@ -329,6 +332,10 @@ class ParticleBufferClass : public RenderObjClass unsigned int * FrameKeyFrameTimes; // 0th entry is always 0 float * FrameKeyFrameValues; float * FrameKeyFrameDeltas; + unsigned int NumBlurTimeKeyFrames; + unsigned int * BlurTimeKeyFrameTimes; // 0th entry is always 0 + float * BlurTimeKeyFrameValues; + float * BlurTimeKeyFrameDeltas; // These tables are indexed by the array position in the particle buffer. // The table size is either the smallest power of two equal or larger @@ -350,12 +357,15 @@ class ParticleBufferClass : public RenderObjClass float * RandomOrientationEntries; unsigned int NumRandomFrameEntriesMinus1; // 2^n - 1 so can be used as a mask also float * RandomFrameEntries; + unsigned int NumRandomBlurTimeEntriesMinus1; // 2^n - 1 so can be used as a mask also + float * RandomBlurTimeEntries; Vector3 ColorRandom; float OpacityRandom; float SizeRandom; float RotationRandom; float FrameRandom; + float BlurTimeRandom; float InitialOrientationRandom; // This object implements particle rendering @@ -375,6 +385,7 @@ class ParticleBufferClass : public RenderObjClass ShareBufferClass * Alpha; ShareBufferClass * Size; ShareBufferClass * Frame; + ShareBufferClass * TailPosition; // Only used for line groups ShareBufferClass * Orientation; ShareBufferClass * APT; diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.cpp b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.cpp index 46d691a97b4..2ed27f6d4ef 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.cpp +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.cpp @@ -73,6 +73,7 @@ ParticleEmitterClass::ParticleEmitterClass(float emit_rate, unsigned int burst_s ParticlePropertyStruct &size, ParticlePropertyStruct &rotation, float orient_rnd, ParticlePropertyStruct &frames, + ParticlePropertyStruct &blur_times, Vector3 accel, float max_age, TextureClass *tex, ShaderClass shader, int max_particles, int max_buffer_size, bool pingpong,int render_mode,int frame_mode, const W3dEmitterLinePropertiesStruct * line_props @@ -111,7 +112,7 @@ ParticleEmitterClass::ParticleEmitterClass(float emit_rate, unsigned int burst_s max_num = MAX(max_num, 2); // max_num of 1 causes problems Buffer = W3DNEW ParticleBufferClass(this, max_num, color, opacity, size, rotation, orient_rnd, - frames, accel/1000000.0f,max_age, tex, shader, pingpong, render_mode, frame_mode, + frames, blur_times, accel/1000000.0f,max_age, tex, shader, pingpong, render_mode, frame_mode, line_props); SET_REF_OWNER( Buffer ); BufferSceneNeeded = true; @@ -207,7 +208,8 @@ ParticleEmitterClass::Create_From_Definition (const ParticleEmitterDefClass &def const char *ptexture_filename = definition.Get_Texture_Filename (); TextureClass *ptexture = NULL; if (ptexture_filename && ptexture_filename[0]) { - ptexture = WW3DAssetManager::Get_Instance()->Get_Texture( + ptexture = WW3DAssetManager::Get_Instance()->Get_Texture + ( ptexture_filename, MIP_LEVELS_ALL, WW3D_FORMAT_UNKNOWN, @@ -237,12 +239,14 @@ ParticleEmitterClass::Create_From_Definition (const ParticleEmitterDefClass &def ParticlePropertyStruct size_keys; ParticlePropertyStruct rotation_keys; ParticlePropertyStruct frame_keys; + ParticlePropertyStruct blur_time_keys; definition.Get_Color_Keyframes (color_keys); definition.Get_Opacity_Keyframes (opacity_keys); definition.Get_Size_Keyframes (size_keys); definition.Get_Rotation_Keyframes (rotation_keys); definition.Get_Frame_Keyframes (frame_keys); + definition.Get_Blur_Time_Keyframes (blur_time_keys); // // Create the emitter @@ -260,6 +264,7 @@ ParticleEmitterClass::Create_From_Definition (const ParticleEmitterDefClass &def rotation_keys, definition.Get_Initial_Orientation_Random(), frame_keys, + blur_time_keys, definition.Get_Acceleration (), definition.Get_Lifetime (), ptexture, @@ -281,6 +286,8 @@ ParticleEmitterClass::Create_From_Definition (const ParticleEmitterDefClass &def if (rotation_keys.Values != NULL) delete [] rotation_keys.Values; if (frame_keys.KeyTimes != NULL) delete [] frame_keys.KeyTimes; if (frame_keys.Values != NULL) delete [] frame_keys.Values; + if (blur_time_keys.KeyTimes != NULL) delete [] blur_time_keys.KeyTimes; + if (blur_time_keys.Values != NULL) delete [] blur_time_keys.Values; // Pass the name along to the emitter pemitter->Set_Name (definition.Get_Name ()); @@ -775,6 +782,16 @@ ParticleEmitterClass::Build_Definition (void) const if (frames.KeyTimes != NULL) delete [] frames.KeyTimes; if (frames.Values != NULL) delete [] frames.Values; + // + // Pass the blur time keyframes onto the definition + // + ParticlePropertyStruct blur_times; + Get_Blur_Time_Key_Frames (blur_times); + pdefinition->Set_Blur_Time_Keyframes (blur_times); + if (blur_times.KeyTimes != NULL) delete [] blur_times.KeyTimes; + if (blur_times.Values != NULL) delete [] blur_times.Values; + + // // Set up the line parameters // diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.h b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.h index f5729f3d84d..485355ced53 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.h +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.h @@ -115,6 +115,7 @@ class ParticleEmitterClass : public RenderObjClass ParticlePropertyStruct &size, ParticlePropertyStruct &rotation, float orient_rnd, ParticlePropertyStruct &frames, + ParticlePropertyStruct &blur_times, Vector3 accel, float max_age, TextureClass *tex, ShaderClass shader = ShaderClass::_PresetAdditiveSpriteShader, int max_particles = 0, int max_buffer_size = -1, bool pingpong = false, @@ -159,7 +160,7 @@ class ParticleEmitterClass : public RenderObjClass virtual void Set_Animation_Hidden(int onoff) { RenderObjClass::Set_Animation_Hidden (onoff); Update_On_Visibilty (); } virtual void Set_Force_Visible(int onoff) { RenderObjClass::Set_Force_Visible (onoff); Update_On_Visibilty (); } - virtual void Set_LOD_Bias(float bias) { if (Buffer) Buffer->Set_LOD_Bias(bias); } + virtual void Set_LOD_Bias(float bias) { if (Buffer) Buffer->Set_LOD_Bias(bias); } // These are not part of the renderobject interface: @@ -184,6 +185,7 @@ class ParticleEmitterClass : public RenderObjClass void Reset_Size(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Size(new_props); } void Reset_Rotations(ParticlePropertyStruct &new_props, float orient_rnd) { if (Buffer) Buffer->Reset_Rotations(new_props, orient_rnd); } void Reset_Frames(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Frames(new_props); } + void Reset_Blur_Times(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Blur_Times(new_props); } // Change emission/burst rate, or tell the emitter to emit a one-time burst. // NOTE: default buffer size fits the emission/burst rate that the emitter was created with. @@ -252,6 +254,7 @@ class ParticleEmitterClass : public RenderObjClass void Get_Size_Key_Frames (ParticlePropertyStruct &sizes) const { Buffer->Get_Size_Key_Frames (sizes); } void Get_Rotation_Key_Frames (ParticlePropertyStruct &rotations) const { Buffer->Get_Rotation_Key_Frames (rotations); } void Get_Frame_Key_Frames (ParticlePropertyStruct &frames) const { Buffer->Get_Frame_Key_Frames (frames); } + void Get_Blur_Time_Key_Frames (ParticlePropertyStruct &blurtimes) const { Buffer->Get_Blur_Time_Key_Frames (blurtimes); } float Get_Initial_Orientation_Random (void) const { return Buffer->Get_Initial_Orientation_Random(); } // Line rendering accessors From 3d4b5c78a33dd96f84fd0ffbc5c01bdc7938d4e1 Mon Sep 17 00:00:00 2001 From: Mauller <26652186+Mauller@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:37:14 +0100 Subject: [PATCH 3/8] [GEN][ZH] Remove unused granny related code (#708) --- .../W3DDevice/GameClient/W3DAssetManager.h | 3 - .../GameClient/Shadow/W3DVolumetricShadow.cpp | 39 +--- .../W3DDevice/GameClient/W3DAssetManager.cpp | 173 +++--------------- .../Source/W3DDevice/GameClient/W3DScene.cpp | 10 - .../W3DDevice/GameClient/W3DTerrainVisual.cpp | 13 -- .../W3DDevice/GameClient/W3DAssetManager.h | 3 - .../GameClient/Shadow/W3DVolumetricShadow.cpp | 39 +--- .../W3DDevice/GameClient/W3DAssetManager.cpp | 173 +++--------------- .../Source/W3DDevice/GameClient/W3DScene.cpp | 10 - .../W3DDevice/GameClient/W3DTerrainVisual.cpp | 13 -- 10 files changed, 50 insertions(+), 426 deletions(-) diff --git a/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h b/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h index 88c863ea4e3..3403e1d4bef 100644 --- a/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h +++ b/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h @@ -54,7 +54,6 @@ class Vector3; class VertexMaterialClass; -class GrannyAnimManagerClass; class W3DAssetManager: public WW3DAssetManager { @@ -103,8 +102,6 @@ class W3DAssetManager: public WW3DAssetManager int replaceHLODTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex); int replaceMeshTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex); - GrannyAnimManagerClass *m_GrannyAnimManager; - //'E&B' customizations /* virtual RenderObjClass * Create_Render_Obj(const char * name, float scale, const Vector3 &hsv_shift); TextureClass * Get_Texture_With_HSV_Shift(const char * filename, const Vector3 &hsv_shift, MipCountType mip_level_count = MIP_LEVELS_ALL); diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp index 3b5cedcfb33..73fea85867e 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp @@ -748,43 +748,6 @@ Int W3DShadowGeometry::initFromMesh(RenderObjClass *robj) Int W3DShadowGeometry::init(RenderObjClass *robj) { return TRUE; -// m_robj=robj; -/* //code to deal with granny - don't think we'll use shadow volumes on these!? - granny_file *fileInfo=robj->getPrototype().m_file; - - for (Int modelIndex=0; modelIndexModelCount; modelIndex++) - { - granny_model *sourceModel = fileInfo->Models[modelIndex]; - if (stricmp(sourceModel->Name,"AABOX") == 0) - { //found a collision box, copy out data - int MeshCount = sourceModel->MeshBindingCount; - if (MeshCount==1) - { - granny_mesh *sourceMesh = sourceModel->MeshBindings[0].Mesh; - granny_pn33_vertex *Vertices = (granny_pn33_vertex *)sourceMesh->PrimaryVertexData->Vertices; - Vector3 points[24]; - - assert (sourceMesh->PrimaryVertexData->VertexCount <= 24); - - for (Int boxVertex=0; boxVertexPrimaryVertexData->VertexCount; boxVertex++) - { points[boxVertex].Set(Vertices[boxVertex].Position[0], - Vertices[boxVertex].Position[1], - Vertices[boxVertex].Position[2]); - } - box.Init(points,sourceMesh->PrimaryVertexData->VertexCount); - } - } - else - { //mesh is part of model - int meshCount = sourceModel->MeshBindingCount; - for (Int meshIndex=0; meshIndexMeshBindings[meshIndex].Mesh; - if (sourceMesh->PrimaryVertexData) - vertexCount+=sourceMesh->PrimaryVertexData->VertexCount; - } - } - }*/ } /////////////////////////////////////////////////////////////////////////////// @@ -3944,7 +3907,7 @@ int W3DShadowGeometryManager::Load_Geom(RenderObjClass *robj, const char *name) } /* -** Iterator converter from HashableClass to GrannyAnimClass +** Iterator converter from HashableClass to W3DShadowGeometry */ W3DShadowGeometry * W3DShadowGeometryManagerIterator::Get_Current_Geom( void ) { diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp index 531cf18e23e..e0f13757d2f 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp @@ -150,17 +150,11 @@ RenderObjClass * W3DPrototypeClass::Create(void) //--------------------------------------------------------------------- W3DAssetManager::W3DAssetManager(void) { -#ifdef INCLUDE_GRANNY_IN_BUILD - m_GrannyAnimManager = NEW GrannyAnimManagerClass; -#endif } //--------------------------------------------------------------------- W3DAssetManager::~W3DAssetManager(void) { -#ifdef INCLUDE_GRANNY_IN_BUILD - delete m_GrannyAnimManager; -#endif } #ifdef DUMP_PERF_STATS @@ -700,12 +694,6 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( GetPrecisionTimer(&startTime64); #endif -#ifdef INCLUDE_GRANNY_IN_BUILD - Bool isGranny = false; - char *pext=strrchr(name,'.'); //find file extension - if (pext) - isGranny=(strnicmp(pext,".GR2",4) == 0); -#endif Bool reallyscale = (WWMath::Fabs(scale - ident_scale) > scale_epsilon); Bool reallycolor = (color & 0xFFFFFF) != 0; //black is not a valid color and assumes no custom coloring. Bool reallytexture = (oldTexture != NULL && newTexture != NULL); @@ -727,12 +715,6 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( // see if we got a cached version RenderObjClass *rendobj = NULL; -#ifdef INCLUDE_GRANNY_IN_BUILD - if (isGranny) - { //Granny objects share the same prototype since they allow instance scaling. - strcpy(newname,name); //use same name for all granny objects at any scale. - } -#endif Set_WW3D_Load_On_Demand(false); // munged name will never be found in a file. rendobj = WW3DAssetManager::Create_Render_Obj(newname); if (rendobj) @@ -740,11 +722,6 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( //when we need to save this render object to a file. Used during saving //of fog of war ghost objects. rendobj->Set_ObjectColor(color); -#ifdef INCLUDE_GRANNY_IN_BUILD - if (isGranny) - ///@todo Granny objects are realtime scaled - fix to scale like W3D. - rendobj->Set_ObjectScale(scale); -#endif Set_WW3D_Load_On_Demand(true); // Auto Load. #ifdef DUMP_PERF_STATS @@ -767,16 +744,11 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( { // If we didn't find one, try to load on demand char filename [MAX_PATH]; - const char *mesh_name = ::strchr (name, '.'); + const char *mesh_name = strchr (name, '.'); if (mesh_name != NULL) { - ::lstrcpyn(filename, name, ((int)mesh_name) - ((int)name) + 1); -#ifdef INCLUDE_GRANNY_IN_BUILD - if (isGranny) - ::lstrcat(filename, ".gr2"); - else -#endif - ::lstrcat(filename, ".w3d"); + lstrcpyn(filename, name, ((int)mesh_name) - ((int)name) + 1); + lstrcat(filename, ".w3d"); } else { sprintf( filename, "%s.w3d", name); } @@ -785,15 +757,7 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( if ( Load_3D_Assets( filename ) == false ) { StringClass new_filename = StringClass("..\\") + filename; - if (Load_3D_Assets( new_filename ) == false) - { -#ifdef INCLUDE_GRANNY_IN_BUILD - char *mesh_name = ::strchr (filename, '.'); - ::lstrcpyn (mesh_name, ".gr2",5); - Load_3D_Assets( filename ); - isGranny=true; -#endif - } + Load_3D_Assets( new_filename ); } proto = Find_Prototype(name); // try again @@ -823,33 +787,21 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( #endif return NULL; } -#ifdef INCLUDE_GRANNY_IN_BUILD - if (!isGranny) -#endif - { - Make_Unique(rendobj,reallyscale,reallycolor); - if (reallytexture) - { - TextureClass *oldTex = Get_Texture(oldTexture); - TextureClass *newTex = Get_Texture(newTexture); - replaceAssetTexture(rendobj,oldTex,newTex); - REF_PTR_RELEASE(newTex); - REF_PTR_RELEASE(oldTex); - } - if (reallyscale) - rendobj->Scale(scale); - if (reallycolor) - Recolor_Asset(rendobj,color); - } -#ifdef INCLUDE_GRANNY_IN_BUILD - else + Make_Unique(rendobj,reallyscale,reallycolor); + if (reallytexture) { - ///@todo Granny objects are realtime scaled - fix to scale like W3D. - rendobj->Set_ObjectScale(scale); - return rendobj; + TextureClass *oldTex = Get_Texture(oldTexture); + TextureClass *newTex = Get_Texture(newTexture); + replaceAssetTexture(rendobj,oldTex,newTex); + REF_PTR_RELEASE(newTex); + REF_PTR_RELEASE(oldTex); } -#endif + if (reallyscale) + rendobj->Scale(scale); + + if (reallycolor) + Recolor_Asset(rendobj,color); W3DPrototypeClass *w3dproto = newInstance(W3DPrototypeClass)(rendobj, newname); rendobj->Release_Ref(); @@ -986,14 +938,6 @@ bool W3DAssetManager::Load_3D_Assets( const char * filename ) GetPrecisionTimer(&startTime64); #endif -#ifdef INCLUDE_GRANNY_IN_BUILD - Bool isGranny = false; - char *pext=strrchr(filename,'.'); //find file extension - if (pext) - isGranny=(strnicmp(pext,".GR2",4) == 0); - if (!isGranny) -#endif - // Try to find an existing prototype char basename[512]; strcpy(basename, filename); @@ -1038,40 +982,6 @@ bool W3DAssetManager::Load_3D_Assets( const char * filename ) #endif return result; -#ifdef INCLUDE_GRANNY_IN_BUILD - //Loading assets for Granny File - PrototypeClass * newproto = NULL; - newproto = _GrannyLoader.Load_W3D(filename); - /* - ** Now, see if the prototype that we loaded has a duplicate - ** name with any of our currently loaded prototypes (can't have that!) - */ - if (newproto != NULL) { - if (!Render_Obj_Exists(newproto->Get_Name())) { - /* - ** Add the new, unique prototype to our list - */ - Add_Prototype(newproto); - } else { - /* - ** Warn the user about a name collision with this prototype - ** and dump it - */ - WWDEBUG_SAY(("Render Object Name Collision: %s\r\n",newproto->Get_Name())); - delete newproto; - newproto = NULL; - return false; - } - } else { - /* - ** Warn user that a prototype was not generated from this - ** chunk type - */ - WWDEBUG_SAY(("Could not generate Granny prototype! File = %d\r\n",filename)); - return false; - } - return true; -#endif } #ifdef DUMP_PERF_STATS @@ -1089,47 +999,17 @@ HAnimClass * W3DAssetManager::Get_HAnim(const char * name) #endif WWPROFILE( "WW3DAssetManager::Get_HAnim" ); -#ifdef INCLUDE_GRANNY_IN_BUILD - Bool isGranny = false; - char *pext=strrchr(name,'.'); //find file extension - if (pext) - isGranny=(strnicmp(pext,".GR2",4) == 0); - if (!isGranny) -#endif - { - HAnimClass *anim=WW3DAssetManager::Get_HAnim(name); //we only do custom granny processing. + HAnimClass *anim=WW3DAssetManager::Get_HAnim(name); #ifdef DUMP_PERF_STATS - if (HAnim_Recursions == 1) - { - GetPrecisionTimer(&endTime64); - Total_Get_HAnim_Time += endTime64-startTime64; - } - HAnim_Recursions--; -#endif - return anim; - } - -#ifdef INCLUDE_GRANNY_IN_BUILD - // Try to find the hanim - HAnimClass * anim = m_GrannyAnimManager->Get_Anim(name); - if (WW3D_Load_On_Demand && anim == NULL) { // If we didn't find it, try to load on demand - if ( !m_GrannyAnimManager->Is_Missing( name ) ) { // if this is NOT a known missing anim - - // If we can't find it, try the parent directory - if ( m_GrannyAnimManager->Load_Anim(name) == 1 ) { - StringClass new_filename = StringClass("..\\") + name; - m_GrannyAnimManager->Load_Anim( new_filename ); - } - - anim = m_GrannyAnimManager->Get_Anim(name); // Try again - if (anim == NULL) { -// WWDEBUG_SAY(("WARNING: Animation %s not found!\n", name)); - m_GrannyAnimManager->Register_Missing( name ); // This is now a KNOWN missing anim - } - } + if (HAnim_Recursions == 1) + { + GetPrecisionTimer(&endTime64); + Total_Get_HAnim_Time += endTime64-startTime64; } - return anim; + HAnim_Recursions--; #endif + return anim; + } //--------------------------------------------------------------------- @@ -1420,11 +1300,6 @@ static inline void Munge_Texture_Name(char *newname, const char *oldname, const RenderObjClass * W3DAssetManager::Create_Render_Obj(const char * name,float scale, const Vector3 &hsv_shift) { Bool isGranny = false; -#ifdef INCLUDE_GRANNY_IN_BUILD - char *pext=strrchr(name,'.'); //find file extension - if (pext) - isGranny=(strnicmp(pext,".GR2",4) == 0); -#endif Bool reallyscale = (WWMath::Fabs(scale - ident_scale) > scale_epsilon); Bool reallyhsv_shift = (WWMath::Fabs(hsv_shift.X - ident_HSV.X) > H_epsilon || WWMath::Fabs(hsv_shift.Y - ident_HSV.Y) > S_epsilon || WWMath::Fabs(hsv_shift.Z - ident_HSV.Z) > V_epsilon); diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp index ce47b3c99aa..4c5905b0296 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp @@ -1124,16 +1124,6 @@ void RTS3DScene::Customized_Render( RenderInfoClass &rinfo ) renderOneObject(rinfo, robj, localPlayerIndex); } } - -#ifdef INCLUDE_GRANNY_IN_BUILD - if (TheGrannyRenderObjSystem) - { //we only want to update granny animations once per frame, so only queue - //them up if this is not a mirror pass. - if (!ShaderClass::Is_Backface_Culling_Inverted()) - TheGrannyRenderObjSystem->queueUpdate(); - TheGrannyRenderObjSystem->Flush(); - } -#endif //Tell shadow manager to render shadows at the end of this frame //Don't draw shadows if there is no terrain present. diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp index a1229875079..e94dc4114d8 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp @@ -84,14 +84,6 @@ W3DTerrainVisual::~W3DTerrainVisual() TheTerrainTracksRenderObjClassSystem=NULL; } -#ifdef INCLUDE_GRANNY_IN_BUILD - if (TheGrannyRenderObjSystem) - { - delete TheGrannyRenderObjSystem; - TheGrannyRenderObjSystem=NULL; - } -#endif - if (TheW3DShadowManager) { delete TheW3DShadowManager; @@ -121,11 +113,6 @@ void W3DTerrainVisual::init( void ) TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem; TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene); -#ifdef INCLUDE_GRANNY_IN_BUILD - // initialize Granny model drawing system - TheGrannyRenderObjSystem = NEW GrannyRenderObjSystem; -#endif - // initialize object shadow drawing system TheW3DShadowManager = NEW W3DShadowManager; TheW3DShadowManager->init(); diff --git a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h index ca9bbf46fce..06e48232761 100644 --- a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h +++ b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h @@ -54,7 +54,6 @@ class Vector3; class VertexMaterialClass; -class GrannyAnimManagerClass; class W3DAssetManager: public WW3DAssetManager { @@ -108,8 +107,6 @@ class W3DAssetManager: public WW3DAssetManager int replaceHLODTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex); int replaceMeshTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex); - GrannyAnimManagerClass *m_GrannyAnimManager; - //'E&B' customizations /* virtual RenderObjClass * Create_Render_Obj(const char * name, float scale, const Vector3 &hsv_shift); TextureClass * Get_Texture_With_HSV_Shift(const char * filename, const Vector3 &hsv_shift, TextureClass::MipCountType mip_level_count = TextureClass::MIP_LEVELS_ALL); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp index 90647da5e2c..4b4eb326065 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp @@ -850,43 +850,6 @@ Int W3DShadowGeometry::initFromMesh(RenderObjClass *robj) Int W3DShadowGeometry::init(RenderObjClass *robj) { return TRUE; -// m_robj=robj; -/* //code to deal with granny - don't think we'll use shadow volumes on these!? - granny_file *fileInfo=robj->getPrototype().m_file; - - for (Int modelIndex=0; modelIndexModelCount; modelIndex++) - { - granny_model *sourceModel = fileInfo->Models[modelIndex]; - if (stricmp(sourceModel->Name,"AABOX") == 0) - { //found a collision box, copy out data - int MeshCount = sourceModel->MeshBindingCount; - if (MeshCount==1) - { - granny_mesh *sourceMesh = sourceModel->MeshBindings[0].Mesh; - granny_pn33_vertex *Vertices = (granny_pn33_vertex *)sourceMesh->PrimaryVertexData->Vertices; - Vector3 points[24]; - - assert (sourceMesh->PrimaryVertexData->VertexCount <= 24); - - for (Int boxVertex=0; boxVertexPrimaryVertexData->VertexCount; boxVertex++) - { points[boxVertex].Set(Vertices[boxVertex].Position[0], - Vertices[boxVertex].Position[1], - Vertices[boxVertex].Position[2]); - } - box.Init(points,sourceMesh->PrimaryVertexData->VertexCount); - } - } - else - { //mesh is part of model - int meshCount = sourceModel->MeshBindingCount; - for (Int meshIndex=0; meshIndexMeshBindings[meshIndex].Mesh; - if (sourceMesh->PrimaryVertexData) - vertexCount+=sourceMesh->PrimaryVertexData->VertexCount; - } - } - }*/ } /////////////////////////////////////////////////////////////////////////////// @@ -4088,7 +4051,7 @@ int W3DShadowGeometryManager::Load_Geom(RenderObjClass *robj, const char *name) } /* -** Iterator converter from HashableClass to GrannyAnimClass +** Iterator converter from HashableClass to W3DShadowGeometry */ W3DShadowGeometry * W3DShadowGeometryManagerIterator::Get_Current_Geom( void ) { diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp index dd79e49b621..43c02567710 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp @@ -151,17 +151,11 @@ RenderObjClass * W3DPrototypeClass::Create(void) //--------------------------------------------------------------------- W3DAssetManager::W3DAssetManager(void) { -#ifdef INCLUDE_GRANNY_IN_BUILD - m_GrannyAnimManager = NEW GrannyAnimManagerClass; -#endif } //--------------------------------------------------------------------- W3DAssetManager::~W3DAssetManager(void) { -#ifdef INCLUDE_GRANNY_IN_BUILD - delete m_GrannyAnimManager; -#endif } #ifdef DUMP_PERF_STATS @@ -730,12 +724,6 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( GetPrecisionTimer(&startTime64); #endif -#ifdef INCLUDE_GRANNY_IN_BUILD - Bool isGranny = false; - char *pext=strrchr(name,'.'); //find file extension - if (pext) - isGranny=(strnicmp(pext,".GR2",4) == 0); -#endif Bool reallyscale = (WWMath::Fabs(scale - ident_scale) > scale_epsilon); Bool reallycolor = (color & 0xFFFFFF) != 0; //black is not a valid color and assumes no custom coloring. Bool reallytexture = (oldTexture != NULL && newTexture != NULL); @@ -757,12 +745,6 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( // see if we got a cached version RenderObjClass *rendobj = NULL; -#ifdef INCLUDE_GRANNY_IN_BUILD - if (isGranny) - { //Granny objects share the same prototype since they allow instance scaling. - strcpy(newname,name); //use same name for all granny objects at any scale. - } -#endif Set_WW3D_Load_On_Demand(false); // munged name will never be found in a file. rendobj = WW3DAssetManager::Create_Render_Obj(newname); if (rendobj) @@ -770,11 +752,6 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( //when we need to save this render object to a file. Used during saving //of fog of war ghost objects. rendobj->Set_ObjectColor(color); -#ifdef INCLUDE_GRANNY_IN_BUILD - if (isGranny) - ///@todo Granny objects are realtime scaled - fix to scale like W3D. - rendobj->Set_ObjectScale(scale); -#endif Set_WW3D_Load_On_Demand(true); // Auto Load. #ifdef DUMP_PERF_STATS @@ -797,16 +774,11 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( { // If we didn't find one, try to load on demand char filename [MAX_PATH]; - const char *mesh_name = ::strchr (name, '.'); + const char *mesh_name = strchr (name, '.'); if (mesh_name != NULL) { - ::lstrcpyn(filename, name, ((int)mesh_name) - ((int)name) + 1); -#ifdef INCLUDE_GRANNY_IN_BUILD - if (isGranny) - ::lstrcat(filename, ".gr2"); - else -#endif - ::lstrcat(filename, ".w3d"); + lstrcpyn(filename, name, ((int)mesh_name) - ((int)name) + 1); + lstrcat(filename, ".w3d"); } else { sprintf( filename, "%s.w3d", name); } @@ -815,15 +787,7 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( if ( Load_3D_Assets( filename ) == false ) { StringClass new_filename = StringClass("..\\") + filename; - if (Load_3D_Assets( new_filename ) == false) - { -#ifdef INCLUDE_GRANNY_IN_BUILD - char *mesh_name = ::strchr (filename, '.'); - ::lstrcpyn (mesh_name, ".gr2",5); - Load_3D_Assets( filename ); - isGranny=true; -#endif - } + Load_3D_Assets(new_filename); } proto = Find_Prototype(name); // try again @@ -853,35 +817,23 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( #endif return NULL; } -#ifdef INCLUDE_GRANNY_IN_BUILD - if (!isGranny) -#endif - { - if (reallyscale) - rendobj->Scale(scale); //this also makes it unique - Make_Unique(rendobj,reallyscale,reallycolor); + if (reallyscale) + rendobj->Scale(scale); //this also makes it unique - if (reallytexture) - { - TextureClass *oldTex = Get_Texture(oldTexture); - TextureClass *newTex = Get_Texture(newTexture); - replaceAssetTexture(rendobj,oldTex,newTex); - REF_PTR_RELEASE(newTex); - REF_PTR_RELEASE(oldTex); - } + Make_Unique(rendobj,reallyscale,reallycolor); - if (reallycolor) - Recolor_Asset(rendobj,color); - } -#ifdef INCLUDE_GRANNY_IN_BUILD - else + if (reallytexture) { - ///@todo Granny objects are realtime scaled - fix to scale like W3D. - rendobj->Set_ObjectScale(scale); - return rendobj; + TextureClass *oldTex = Get_Texture(oldTexture); + TextureClass *newTex = Get_Texture(newTexture); + replaceAssetTexture(rendobj,oldTex,newTex); + REF_PTR_RELEASE(newTex); + REF_PTR_RELEASE(oldTex); } -#endif + + if (reallycolor) + Recolor_Asset(rendobj,color); W3DPrototypeClass *w3dproto = newInstance(W3DPrototypeClass)(rendobj, newname); rendobj->Release_Ref(); @@ -1018,14 +970,6 @@ bool W3DAssetManager::Load_3D_Assets( const char * filename ) GetPrecisionTimer(&startTime64); #endif -#ifdef INCLUDE_GRANNY_IN_BUILD - Bool isGranny = false; - char *pext=strrchr(filename,'.'); //find file extension - if (pext) - isGranny=(strnicmp(pext,".GR2",4) == 0); - if (!isGranny) -#endif - // Try to find an existing prototype char basename[512]; strcpy(basename, filename); @@ -1070,40 +1014,6 @@ bool W3DAssetManager::Load_3D_Assets( const char * filename ) #endif return result; -#ifdef INCLUDE_GRANNY_IN_BUILD - //Loading assets for Granny File - PrototypeClass * newproto = NULL; - newproto = _GrannyLoader.Load_W3D(filename); - /* - ** Now, see if the prototype that we loaded has a duplicate - ** name with any of our currently loaded prototypes (can't have that!) - */ - if (newproto != NULL) { - if (!Render_Obj_Exists(newproto->Get_Name())) { - /* - ** Add the new, unique prototype to our list - */ - Add_Prototype(newproto); - } else { - /* - ** Warn the user about a name collision with this prototype - ** and dump it - */ - WWDEBUG_SAY(("Render Object Name Collision: %s\r\n",newproto->Get_Name())); - delete newproto; - newproto = NULL; - return false; - } - } else { - /* - ** Warn user that a prototype was not generated from this - ** chunk type - */ - WWDEBUG_SAY(("Could not generate Granny prototype! File = %d\r\n",filename)); - return false; - } - return true; -#endif } #ifdef DUMP_PERF_STATS @@ -1121,47 +1031,17 @@ HAnimClass * W3DAssetManager::Get_HAnim(const char * name) #endif WWPROFILE( "WW3DAssetManager::Get_HAnim" ); -#ifdef INCLUDE_GRANNY_IN_BUILD - Bool isGranny = false; - char *pext=strrchr(name,'.'); //find file extension - if (pext) - isGranny=(strnicmp(pext,".GR2",4) == 0); - if (!isGranny) -#endif - { - HAnimClass *anim=WW3DAssetManager::Get_HAnim(name); //we only do custom granny processing. + HAnimClass *anim=WW3DAssetManager::Get_HAnim(name); #ifdef DUMP_PERF_STATS - if (HAnim_Recursions == 1) - { - GetPrecisionTimer(&endTime64); - Total_Get_HAnim_Time += endTime64-startTime64; - } - HAnim_Recursions--; -#endif - return anim; - } - -#ifdef INCLUDE_GRANNY_IN_BUILD - // Try to find the hanim - HAnimClass * anim = m_GrannyAnimManager->Get_Anim(name); - if (WW3D_Load_On_Demand && anim == NULL) { // If we didn't find it, try to load on demand - if ( !m_GrannyAnimManager->Is_Missing( name ) ) { // if this is NOT a known missing anim - - // If we can't find it, try the parent directory - if ( m_GrannyAnimManager->Load_Anim(name) == 1 ) { - StringClass new_filename = StringClass("..\\") + name; - m_GrannyAnimManager->Load_Anim( new_filename ); - } - - anim = m_GrannyAnimManager->Get_Anim(name); // Try again - if (anim == NULL) { -// WWDEBUG_SAY(("WARNING: Animation %s not found!\n", name)); - m_GrannyAnimManager->Register_Missing( name ); // This is now a KNOWN missing anim - } - } + if (HAnim_Recursions == 1) + { + GetPrecisionTimer(&endTime64); + Total_Get_HAnim_Time += endTime64-startTime64; } - return anim; + HAnim_Recursions--; #endif + return anim; + } //--------------------------------------------------------------------- @@ -1447,11 +1327,6 @@ static inline void Munge_Texture_Name(char *newname, const char *oldname, const RenderObjClass * W3DAssetManager::Create_Render_Obj(const char * name,float scale, const Vector3 &hsv_shift) { Bool isGranny = false; -#ifdef INCLUDE_GRANNY_IN_BUILD - char *pext=strrchr(name,'.'); //find file extension - if (pext) - isGranny=(strnicmp(pext,".GR2",4) == 0); -#endif Bool reallyscale = (WWMath::Fabs(scale - ident_scale) > scale_epsilon); Bool reallyhsv_shift = (WWMath::Fabs(hsv_shift.X - ident_HSV.X) > H_epsilon || WWMath::Fabs(hsv_shift.Y - ident_HSV.Y) > S_epsilon || WWMath::Fabs(hsv_shift.Z - ident_HSV.Z) > V_epsilon); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp index 315df93aefe..e3e2e57add5 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp @@ -1173,16 +1173,6 @@ void RTS3DScene::Customized_Render( RenderInfoClass &rinfo ) renderOneObject(rinfo, robj, localPlayerIndex); } } - -#ifdef INCLUDE_GRANNY_IN_BUILD - if (TheGrannyRenderObjSystem) - { //we only want to update granny animations once per frame, so only queue - //them up if this is not a mirror pass. - if (!ShaderClass::Is_Backface_Culling_Inverted()) - TheGrannyRenderObjSystem->queueUpdate(); - TheGrannyRenderObjSystem->Flush(); - } -#endif //Tell shadow manager to render shadows at the end of this frame //Don't draw shadows if there is no terrain present. diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp index ec2be7658ef..41b22b57fa0 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp @@ -197,14 +197,6 @@ W3DTerrainVisual::~W3DTerrainVisual() TheTerrainTracksRenderObjClassSystem=NULL; } -#ifdef INCLUDE_GRANNY_IN_BUILD - if (TheGrannyRenderObjSystem) - { - delete TheGrannyRenderObjSystem; - TheGrannyRenderObjSystem=NULL; - } -#endif - if (TheW3DShadowManager) { delete TheW3DShadowManager; @@ -244,11 +236,6 @@ void W3DTerrainVisual::init( void ) TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem; TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene); -#ifdef INCLUDE_GRANNY_IN_BUILD - // initialize Granny model drawing system - TheGrannyRenderObjSystem = NEW GrannyRenderObjSystem; -#endif - // initialize object shadow drawing system TheW3DShadowManager = NEW W3DShadowManager; TheW3DShadowManager->init(); From 36bd7572de245f3dc2d58b3e537a8d61734cffbf Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 19 Apr 2025 14:40:51 +0200 Subject: [PATCH 4/8] Fix drawable leak in ParticleUplinkCannonUpdate::createGroundToOrbitLaser --- .../GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index ea56a2e4da8..94900fdb47c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -942,7 +942,10 @@ void ParticleUplinkCannonUpdate::createGroundToOrbitLaser( UnsignedInt growthFra if( beam ) { TheGameClient->destroyDrawable( beam ); - m_orbitToTargetBeamID = INVALID_DRAWABLE_ID; + + // TheSuperHackers @fix helmutbuhler 04/19/2025 + // This originally mistakenly assigned to m_orbitToTargetBeamID and thus caused a leak. + m_groundToOrbitBeamID = INVALID_DRAWABLE_ID; } if( data->m_particleBeamLaserName.isNotEmpty() ) From c63172279c250b4891019d5d17de8c7bc5009f2d Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 19 Apr 2025 14:47:18 +0200 Subject: [PATCH 5/8] Fix wrong iterator usage (would crash on VS22) --- .../Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp | 2 +- .../Source/GameLogic/Object/Update/AIUpdate/MissileAIUpdate.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp index b7e1252d733..f6f1b6cc384 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Behavior/DumbProjectileBehavior.cpp @@ -490,7 +490,7 @@ Bool DumbProjectileBehavior::projectileHandleCollision( Object *other ) const ContainedItemsList* items = contain->getContainedItemsList(); if (items) { - for (ContainedItemsList::const_iterator it = items->begin(); *it != NULL && numKilled < d->m_garrisonHitKillCount; ) + for (ContainedItemsList::const_iterator it = items->begin(); it != items->end() && numKilled < d->m_garrisonHitKillCount; ) { Object* thingToKill = *it++; if (!thingToKill->isEffectivelyDead() && thingToKill->isKindOfMulti(d->m_garrisonHitKillKindof, d->m_garrisonHitKillKindofNot)) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/MissileAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/MissileAIUpdate.cpp index 4da9001972d..1fda944b9dc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/MissileAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/MissileAIUpdate.cpp @@ -348,7 +348,7 @@ Bool MissileAIUpdate::projectileHandleCollision( Object *other ) const ContainedItemsList* items = contain->getContainedItemsList(); if (items) { - for (ContainedItemsList::const_iterator it = items->begin(); *it != NULL && numKilled < d->m_garrisonHitKillCount; ) + for (ContainedItemsList::const_iterator it = items->begin(); it != items->end() && numKilled < d->m_garrisonHitKillCount; ) { Object* thingToKill = *it++; if (!thingToKill->isEffectivelyDead() && thingToKill->isKindOfMulti(d->m_garrisonHitKillKindof, d->m_garrisonHitKillKindofNot)) From f5e086fde1a8c998ceb78d2ea4f0a3c4175e25b2 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sat, 19 Apr 2025 15:20:15 +0200 Subject: [PATCH 6/8] Fix uninitialized memory access in DockUpdate::loadDockPositions() # Conflicts: # GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp --- .../GameLogic/Object/Update/DockUpdate/DockUpdate.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp index b3571f64639..05639520995 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/DockUpdate/DockUpdate.cpp @@ -510,7 +510,14 @@ void DockUpdate::loadDockPositions() if( m_numberApproachPositions != DYNAMIC_APPROACH_VECTOR_FLAG ) { // Dynamic means no bones - Coord3D approachBones[DEFAULT_APPROACH_VECTOR_SIZE]; + // TheSuperHackers @fix helmutbuhler 04/19/2025 + // approachBones was originally not initialized. It needs to be initialized because + // myDrawable->getPristineBonePositions only sets a certain amount of elements + // (m_numberApproachPositionBones), but the following code copies a different amount + // (m_numberApproachPositions) into m_approachPositions (which CRC indirectly depends on). + // Luckily, just setting it to zero seems to work. + + Coord3D approachBones[DEFAULT_APPROACH_VECTOR_SIZE] = {0}; m_numberApproachPositionBones = myDrawable->getPristineBonePositions( "DockWaiting", 1, approachBones, NULL, m_numberApproachPositions); if( m_numberApproachPositions == m_approachPositions.size() )//safeguard: will always be true { From 770ae1825a956b53f4465c52a78fcea3b3a731a6 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sun, 13 Apr 2025 19:37:55 +0200 Subject: [PATCH 7/8] Fix draw assignment in StealthUpdate::changeVisualDisguise() --- .../Source/GameLogic/Object/Update/StealthUpdate.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp index 260f86224cb..26c37c4b5c5 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/StealthUpdate.cpp @@ -1055,7 +1055,9 @@ void StealthUpdate::changeVisualDisguise() const ThingTemplate *tTemplate = self->getTemplate(); - TheThingFactory->newDrawable( tTemplate ); + // TheSuperHackers @bugfix helmutbuhler 04/13/2025 + // draw was originally not assigned here and potentially caused memory corruption + draw = TheThingFactory->newDrawable( tTemplate ); if( draw ) { TheGameLogic->bindObjectAndDrawable(self, draw); From 48d6592824133eda53b5c777dfc3c4539eae63a7 Mon Sep 17 00:00:00 2001 From: Helmut Buhler Date: Sun, 20 Apr 2025 01:12:47 +0200 Subject: [PATCH 8/8] Revert "[GEN][ZH] Remove unused granny related code (#708)" This reverts commit 3d4b5c78a33dd96f84fd0ffbc5c01bdc7938d4e1. Revert "[GEN] Backport BlurTime functionality in WW3D2's ParticleBufferClass (#711)" This reverts commit bcfe2e4b5ece570c6235c4f27b814ac6613c2213. Revert "[CORE] Move matchbot tool to Core (#706)" This reverts commit 61ac7bdbd436c7c77b5704cd75a9fe198bf7776d. --- Core/Tools/CMakeLists.txt | 1 - .../W3DDevice/GameClient/W3DAssetManager.h | 3 + .../GameClient/Shadow/W3DVolumetricShadow.cpp | 39 +- .../W3DDevice/GameClient/W3DAssetManager.cpp | 173 ++- .../Source/W3DDevice/GameClient/W3DScene.cpp | 10 + .../W3DDevice/GameClient/W3DTerrainVisual.cpp | 13 + .../Source/WWVegas/WW3D2/part_buf.cpp | 292 +--- .../Libraries/Source/WWVegas/WW3D2/part_buf.h | 13 +- .../Source/WWVegas/WW3D2/part_emt.cpp | 21 +- .../Libraries/Source/WWVegas/WW3D2/part_emt.h | 5 +- Generals/Code/Tools/CMakeLists.txt | 1 + .../Code}/Tools/matchbot/CMakeLists.txt | 12 +- Generals/Code/Tools/matchbot/debug.cpp | 61 + Generals/Code/Tools/matchbot/debug.h | 46 + Generals/Code/Tools/matchbot/encrypt.cpp | 94 ++ Generals/Code/Tools/matchbot/encrypt.h | 28 + Generals/Code/Tools/matchbot/generals.cpp | 1322 +++++++++++++++++ Generals/Code/Tools/matchbot/generals.h | 194 +++ Generals/Code/Tools/matchbot/global.cpp | 51 + Generals/Code/Tools/matchbot/global.h | 57 + Generals/Code/Tools/matchbot/main.cpp | 409 +++++ Generals/Code/Tools/matchbot/matcher.cpp | 421 ++++++ Generals/Code/Tools/matchbot/matcher.h | 93 ++ Generals/Code/Tools/matchbot/mydebug.cpp | 101 ++ Generals/Code/Tools/matchbot/mydebug.h | 134 ++ Generals/Code/Tools/matchbot/rand.cpp | 132 ++ Generals/Code/Tools/matchbot/rand.h | 45 + Generals/Code/Tools/matchbot/wlib/arraylist.h | 709 +++++++++ .../Code/Tools/matchbot/wlib/configfile.cpp | 490 ++++++ .../Code/Tools/matchbot/wlib/configfile.h | 76 + Generals/Code/Tools/matchbot/wlib/critsec.cpp | 129 ++ Generals/Code/Tools/matchbot/wlib/critsec.h | 62 + .../Code/Tools/matchbot/wlib/dictionary.h | 681 +++++++++ Generals/Code/Tools/matchbot/wlib/filed.h | 51 + .../Code/Tools/matchbot/wlib/linkedlist.h | 495 ++++++ Generals/Code/Tools/matchbot/wlib/mboxd.h | 40 + Generals/Code/Tools/matchbot/wlib/monod.cpp | 57 + Generals/Code/Tools/matchbot/wlib/monod.h | 74 + Generals/Code/Tools/matchbot/wlib/odevice.h | 32 + Generals/Code/Tools/matchbot/wlib/sem4.cpp | 182 +++ Generals/Code/Tools/matchbot/wlib/sem4.h | 64 + Generals/Code/Tools/matchbot/wlib/stderrd.h | 39 + Generals/Code/Tools/matchbot/wlib/stdoutd.h | 40 + .../Code/Tools/matchbot/wlib/streamer.cpp | 154 ++ Generals/Code/Tools/matchbot/wlib/streamer.h | 67 + Generals/Code/Tools/matchbot/wlib/syslogd.cpp | 39 + Generals/Code/Tools/matchbot/wlib/syslogd.h | 48 + .../Code/Tools/matchbot/wlib/threadfac.cpp | 221 +++ Generals/Code/Tools/matchbot/wlib/threadfac.h | 124 ++ .../Code/Tools/matchbot/wlib/threadsafe.h | 62 + .../Code/Tools/matchbot/wlib/timezone.cpp | 71 + Generals/Code/Tools/matchbot/wlib/timezone.h | 41 + Generals/Code/Tools/matchbot/wlib/ustring.h | 61 + Generals/Code/Tools/matchbot/wlib/wdebug.cpp | 190 +++ Generals/Code/Tools/matchbot/wlib/wdebug.h | 314 ++++ Generals/Code/Tools/matchbot/wlib/wstring.cpp | 597 ++++++++ Generals/Code/Tools/matchbot/wlib/wstring.h | 93 ++ Generals/Code/Tools/matchbot/wlib/wstypes.h | 118 ++ Generals/Code/Tools/matchbot/wlib/wtime.cpp | 802 ++++++++++ Generals/Code/Tools/matchbot/wlib/wtime.h | 128 ++ Generals/Code/Tools/matchbot/wlib/xtime.cpp | 1015 +++++++++++++ Generals/Code/Tools/matchbot/wlib/xtime.h | 146 ++ Generals/Code/Tools/matchbot/wnet/field.cpp | 319 ++++ Generals/Code/Tools/matchbot/wnet/field.h | 101 ++ Generals/Code/Tools/matchbot/wnet/packet.cpp | 522 +++++++ Generals/Code/Tools/matchbot/wnet/packet.h | 108 ++ Generals/Code/Tools/matchbot/wnet/tcp.cpp | 1242 ++++++++++++++++ Generals/Code/Tools/matchbot/wnet/tcp.h | 199 +++ Generals/Code/Tools/matchbot/wnet/udp.cpp | 372 +++++ Generals/Code/Tools/matchbot/wnet/udp.h | 114 ++ .../W3DDevice/GameClient/W3DAssetManager.h | 3 + .../GameClient/Shadow/W3DVolumetricShadow.cpp | 39 +- .../W3DDevice/GameClient/W3DAssetManager.cpp | 173 ++- .../Source/W3DDevice/GameClient/W3DScene.cpp | 10 + .../W3DDevice/GameClient/W3DTerrainVisual.cpp | 13 + GeneralsMD/Code/Tools/CMakeLists.txt | 1 + GeneralsMD/Code/Tools/matchbot/CMakeLists.txt | 78 + .../Code}/Tools/matchbot/debug.cpp | 0 .../Code}/Tools/matchbot/debug.h | 0 .../Code}/Tools/matchbot/encrypt.cpp | 0 .../Code}/Tools/matchbot/encrypt.h | 0 .../Code}/Tools/matchbot/generals.cpp | 0 .../Code}/Tools/matchbot/generals.h | 0 .../Code}/Tools/matchbot/global.cpp | 0 .../Code}/Tools/matchbot/global.h | 0 .../Code}/Tools/matchbot/main.cpp | 0 .../Code}/Tools/matchbot/matcher.h | 0 .../Code}/Tools/matchbot/mydebug.cpp | 0 .../Code}/Tools/matchbot/mydebug.h | 0 .../Code}/Tools/matchbot/rand.cpp | 0 .../Code}/Tools/matchbot/rand.h | 0 .../Code}/Tools/matchbot/wlib/arraylist.h | 0 .../Code}/Tools/matchbot/wlib/configfile.cpp | 0 .../Code}/Tools/matchbot/wlib/configfile.h | 0 .../Code}/Tools/matchbot/wlib/critsec.cpp | 0 .../Code}/Tools/matchbot/wlib/critsec.h | 0 .../Code}/Tools/matchbot/wlib/dictionary.h | 0 .../Code}/Tools/matchbot/wlib/filed.h | 0 .../Code}/Tools/matchbot/wlib/linkedlist.h | 0 .../Code}/Tools/matchbot/wlib/mboxd.h | 0 .../Code}/Tools/matchbot/wlib/monod.cpp | 0 .../Code}/Tools/matchbot/wlib/monod.h | 0 .../Code}/Tools/matchbot/wlib/odevice.h | 0 .../Code}/Tools/matchbot/wlib/sem4.cpp | 0 .../Code}/Tools/matchbot/wlib/sem4.h | 0 .../Code}/Tools/matchbot/wlib/stderrd.h | 0 .../Code}/Tools/matchbot/wlib/stdoutd.h | 0 .../Code}/Tools/matchbot/wlib/streamer.cpp | 0 .../Code}/Tools/matchbot/wlib/streamer.h | 0 .../Code}/Tools/matchbot/wlib/syslogd.cpp | 0 .../Code}/Tools/matchbot/wlib/syslogd.h | 0 .../Code}/Tools/matchbot/wlib/threadfac.cpp | 0 .../Code}/Tools/matchbot/wlib/threadfac.h | 0 .../Code}/Tools/matchbot/wlib/threadsafe.h | 0 .../Code}/Tools/matchbot/wlib/timezone.cpp | 0 .../Code}/Tools/matchbot/wlib/timezone.h | 0 .../Code}/Tools/matchbot/wlib/ustring.h | 0 .../Code}/Tools/matchbot/wlib/wdebug.cpp | 0 .../Code}/Tools/matchbot/wlib/wdebug.h | 0 .../Code}/Tools/matchbot/wlib/wstring.cpp | 0 .../Code}/Tools/matchbot/wlib/wstring.h | 0 .../Code}/Tools/matchbot/wlib/wstypes.h | 0 .../Code}/Tools/matchbot/wlib/wtime.cpp | 0 .../Code}/Tools/matchbot/wlib/wtime.h | 0 .../Code}/Tools/matchbot/wlib/xtime.cpp | 0 .../Code}/Tools/matchbot/wlib/xtime.h | 0 .../Code}/Tools/matchbot/wnet/field.cpp | 0 .../Code}/Tools/matchbot/wnet/field.h | 0 .../Code}/Tools/matchbot/wnet/packet.cpp | 0 .../Code}/Tools/matchbot/wnet/packet.h | 0 .../Code}/Tools/matchbot/wnet/tcp.cpp | 0 .../Code}/Tools/matchbot/wnet/tcp.h | 0 .../Code}/Tools/matchbot/wnet/udp.cpp | 0 .../Code}/Tools/matchbot/wnet/udp.h | 0 134 files changed, 14195 insertions(+), 382 deletions(-) rename {Core => Generals/Code}/Tools/matchbot/CMakeLists.txt (79%) create mode 100644 Generals/Code/Tools/matchbot/debug.cpp create mode 100644 Generals/Code/Tools/matchbot/debug.h create mode 100644 Generals/Code/Tools/matchbot/encrypt.cpp create mode 100644 Generals/Code/Tools/matchbot/encrypt.h create mode 100644 Generals/Code/Tools/matchbot/generals.cpp create mode 100644 Generals/Code/Tools/matchbot/generals.h create mode 100644 Generals/Code/Tools/matchbot/global.cpp create mode 100644 Generals/Code/Tools/matchbot/global.h create mode 100644 Generals/Code/Tools/matchbot/main.cpp create mode 100644 Generals/Code/Tools/matchbot/matcher.cpp create mode 100644 Generals/Code/Tools/matchbot/matcher.h create mode 100644 Generals/Code/Tools/matchbot/mydebug.cpp create mode 100644 Generals/Code/Tools/matchbot/mydebug.h create mode 100644 Generals/Code/Tools/matchbot/rand.cpp create mode 100644 Generals/Code/Tools/matchbot/rand.h create mode 100644 Generals/Code/Tools/matchbot/wlib/arraylist.h create mode 100644 Generals/Code/Tools/matchbot/wlib/configfile.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/configfile.h create mode 100644 Generals/Code/Tools/matchbot/wlib/critsec.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/critsec.h create mode 100644 Generals/Code/Tools/matchbot/wlib/dictionary.h create mode 100644 Generals/Code/Tools/matchbot/wlib/filed.h create mode 100644 Generals/Code/Tools/matchbot/wlib/linkedlist.h create mode 100644 Generals/Code/Tools/matchbot/wlib/mboxd.h create mode 100644 Generals/Code/Tools/matchbot/wlib/monod.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/monod.h create mode 100644 Generals/Code/Tools/matchbot/wlib/odevice.h create mode 100644 Generals/Code/Tools/matchbot/wlib/sem4.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/sem4.h create mode 100644 Generals/Code/Tools/matchbot/wlib/stderrd.h create mode 100644 Generals/Code/Tools/matchbot/wlib/stdoutd.h create mode 100644 Generals/Code/Tools/matchbot/wlib/streamer.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/streamer.h create mode 100644 Generals/Code/Tools/matchbot/wlib/syslogd.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/syslogd.h create mode 100644 Generals/Code/Tools/matchbot/wlib/threadfac.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/threadfac.h create mode 100644 Generals/Code/Tools/matchbot/wlib/threadsafe.h create mode 100644 Generals/Code/Tools/matchbot/wlib/timezone.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/timezone.h create mode 100644 Generals/Code/Tools/matchbot/wlib/ustring.h create mode 100644 Generals/Code/Tools/matchbot/wlib/wdebug.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/wdebug.h create mode 100644 Generals/Code/Tools/matchbot/wlib/wstring.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/wstring.h create mode 100644 Generals/Code/Tools/matchbot/wlib/wstypes.h create mode 100644 Generals/Code/Tools/matchbot/wlib/wtime.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/wtime.h create mode 100644 Generals/Code/Tools/matchbot/wlib/xtime.cpp create mode 100644 Generals/Code/Tools/matchbot/wlib/xtime.h create mode 100644 Generals/Code/Tools/matchbot/wnet/field.cpp create mode 100644 Generals/Code/Tools/matchbot/wnet/field.h create mode 100644 Generals/Code/Tools/matchbot/wnet/packet.cpp create mode 100644 Generals/Code/Tools/matchbot/wnet/packet.h create mode 100644 Generals/Code/Tools/matchbot/wnet/tcp.cpp create mode 100644 Generals/Code/Tools/matchbot/wnet/tcp.h create mode 100644 Generals/Code/Tools/matchbot/wnet/udp.cpp create mode 100644 Generals/Code/Tools/matchbot/wnet/udp.h create mode 100644 GeneralsMD/Code/Tools/matchbot/CMakeLists.txt rename {Core => GeneralsMD/Code}/Tools/matchbot/debug.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/debug.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/encrypt.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/encrypt.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/generals.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/generals.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/global.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/global.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/main.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/matcher.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/mydebug.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/mydebug.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/rand.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/rand.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/arraylist.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/configfile.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/configfile.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/critsec.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/critsec.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/dictionary.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/filed.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/linkedlist.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/mboxd.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/monod.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/monod.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/odevice.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/sem4.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/sem4.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/stderrd.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/stdoutd.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/streamer.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/streamer.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/syslogd.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/syslogd.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/threadfac.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/threadfac.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/threadsafe.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/timezone.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/timezone.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/ustring.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/wdebug.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/wdebug.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/wstring.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/wstring.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/wstypes.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/wtime.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/wtime.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/xtime.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wlib/xtime.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wnet/field.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wnet/field.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wnet/packet.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wnet/packet.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wnet/tcp.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wnet/tcp.h (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wnet/udp.cpp (100%) rename {Core => GeneralsMD/Code}/Tools/matchbot/wnet/udp.h (100%) diff --git a/Core/Tools/CMakeLists.txt b/Core/Tools/CMakeLists.txt index 6e3a1cdcb02..ee6e776fd66 100644 --- a/Core/Tools/CMakeLists.txt +++ b/Core/Tools/CMakeLists.txt @@ -9,6 +9,5 @@ endif() if(RTS_BUILD_CORE_EXTRAS) add_subdirectory(Compress) add_subdirectory(CRCDiff) - add_subdirectory(matchbot) add_subdirectory(WW3D) endif() diff --git a/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h b/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h index 3403e1d4bef..88c863ea4e3 100644 --- a/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h +++ b/Generals/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h @@ -54,6 +54,7 @@ class Vector3; class VertexMaterialClass; +class GrannyAnimManagerClass; class W3DAssetManager: public WW3DAssetManager { @@ -102,6 +103,8 @@ class W3DAssetManager: public WW3DAssetManager int replaceHLODTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex); int replaceMeshTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex); + GrannyAnimManagerClass *m_GrannyAnimManager; + //'E&B' customizations /* virtual RenderObjClass * Create_Render_Obj(const char * name, float scale, const Vector3 &hsv_shift); TextureClass * Get_Texture_With_HSV_Shift(const char * filename, const Vector3 &hsv_shift, MipCountType mip_level_count = MIP_LEVELS_ALL); diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp index 73fea85867e..3b5cedcfb33 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp @@ -748,6 +748,43 @@ Int W3DShadowGeometry::initFromMesh(RenderObjClass *robj) Int W3DShadowGeometry::init(RenderObjClass *robj) { return TRUE; +// m_robj=robj; +/* //code to deal with granny - don't think we'll use shadow volumes on these!? + granny_file *fileInfo=robj->getPrototype().m_file; + + for (Int modelIndex=0; modelIndexModelCount; modelIndex++) + { + granny_model *sourceModel = fileInfo->Models[modelIndex]; + if (stricmp(sourceModel->Name,"AABOX") == 0) + { //found a collision box, copy out data + int MeshCount = sourceModel->MeshBindingCount; + if (MeshCount==1) + { + granny_mesh *sourceMesh = sourceModel->MeshBindings[0].Mesh; + granny_pn33_vertex *Vertices = (granny_pn33_vertex *)sourceMesh->PrimaryVertexData->Vertices; + Vector3 points[24]; + + assert (sourceMesh->PrimaryVertexData->VertexCount <= 24); + + for (Int boxVertex=0; boxVertexPrimaryVertexData->VertexCount; boxVertex++) + { points[boxVertex].Set(Vertices[boxVertex].Position[0], + Vertices[boxVertex].Position[1], + Vertices[boxVertex].Position[2]); + } + box.Init(points,sourceMesh->PrimaryVertexData->VertexCount); + } + } + else + { //mesh is part of model + int meshCount = sourceModel->MeshBindingCount; + for (Int meshIndex=0; meshIndexMeshBindings[meshIndex].Mesh; + if (sourceMesh->PrimaryVertexData) + vertexCount+=sourceMesh->PrimaryVertexData->VertexCount; + } + } + }*/ } /////////////////////////////////////////////////////////////////////////////// @@ -3907,7 +3944,7 @@ int W3DShadowGeometryManager::Load_Geom(RenderObjClass *robj, const char *name) } /* -** Iterator converter from HashableClass to W3DShadowGeometry +** Iterator converter from HashableClass to GrannyAnimClass */ W3DShadowGeometry * W3DShadowGeometryManagerIterator::Get_Current_Geom( void ) { diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp index e0f13757d2f..531cf18e23e 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp @@ -150,11 +150,17 @@ RenderObjClass * W3DPrototypeClass::Create(void) //--------------------------------------------------------------------- W3DAssetManager::W3DAssetManager(void) { +#ifdef INCLUDE_GRANNY_IN_BUILD + m_GrannyAnimManager = NEW GrannyAnimManagerClass; +#endif } //--------------------------------------------------------------------- W3DAssetManager::~W3DAssetManager(void) { +#ifdef INCLUDE_GRANNY_IN_BUILD + delete m_GrannyAnimManager; +#endif } #ifdef DUMP_PERF_STATS @@ -694,6 +700,12 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( GetPrecisionTimer(&startTime64); #endif +#ifdef INCLUDE_GRANNY_IN_BUILD + Bool isGranny = false; + char *pext=strrchr(name,'.'); //find file extension + if (pext) + isGranny=(strnicmp(pext,".GR2",4) == 0); +#endif Bool reallyscale = (WWMath::Fabs(scale - ident_scale) > scale_epsilon); Bool reallycolor = (color & 0xFFFFFF) != 0; //black is not a valid color and assumes no custom coloring. Bool reallytexture = (oldTexture != NULL && newTexture != NULL); @@ -715,6 +727,12 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( // see if we got a cached version RenderObjClass *rendobj = NULL; +#ifdef INCLUDE_GRANNY_IN_BUILD + if (isGranny) + { //Granny objects share the same prototype since they allow instance scaling. + strcpy(newname,name); //use same name for all granny objects at any scale. + } +#endif Set_WW3D_Load_On_Demand(false); // munged name will never be found in a file. rendobj = WW3DAssetManager::Create_Render_Obj(newname); if (rendobj) @@ -722,6 +740,11 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( //when we need to save this render object to a file. Used during saving //of fog of war ghost objects. rendobj->Set_ObjectColor(color); +#ifdef INCLUDE_GRANNY_IN_BUILD + if (isGranny) + ///@todo Granny objects are realtime scaled - fix to scale like W3D. + rendobj->Set_ObjectScale(scale); +#endif Set_WW3D_Load_On_Demand(true); // Auto Load. #ifdef DUMP_PERF_STATS @@ -744,11 +767,16 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( { // If we didn't find one, try to load on demand char filename [MAX_PATH]; - const char *mesh_name = strchr (name, '.'); + const char *mesh_name = ::strchr (name, '.'); if (mesh_name != NULL) { - lstrcpyn(filename, name, ((int)mesh_name) - ((int)name) + 1); - lstrcat(filename, ".w3d"); + ::lstrcpyn(filename, name, ((int)mesh_name) - ((int)name) + 1); +#ifdef INCLUDE_GRANNY_IN_BUILD + if (isGranny) + ::lstrcat(filename, ".gr2"); + else +#endif + ::lstrcat(filename, ".w3d"); } else { sprintf( filename, "%s.w3d", name); } @@ -757,7 +785,15 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( if ( Load_3D_Assets( filename ) == false ) { StringClass new_filename = StringClass("..\\") + filename; - Load_3D_Assets( new_filename ); + if (Load_3D_Assets( new_filename ) == false) + { +#ifdef INCLUDE_GRANNY_IN_BUILD + char *mesh_name = ::strchr (filename, '.'); + ::lstrcpyn (mesh_name, ".gr2",5); + Load_3D_Assets( filename ); + isGranny=true; +#endif + } } proto = Find_Prototype(name); // try again @@ -787,21 +823,33 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( #endif return NULL; } +#ifdef INCLUDE_GRANNY_IN_BUILD + if (!isGranny) +#endif + { + Make_Unique(rendobj,reallyscale,reallycolor); + if (reallytexture) + { + TextureClass *oldTex = Get_Texture(oldTexture); + TextureClass *newTex = Get_Texture(newTexture); + replaceAssetTexture(rendobj,oldTex,newTex); + REF_PTR_RELEASE(newTex); + REF_PTR_RELEASE(oldTex); + } + if (reallyscale) + rendobj->Scale(scale); - Make_Unique(rendobj,reallyscale,reallycolor); - if (reallytexture) + if (reallycolor) + Recolor_Asset(rendobj,color); + } +#ifdef INCLUDE_GRANNY_IN_BUILD + else { - TextureClass *oldTex = Get_Texture(oldTexture); - TextureClass *newTex = Get_Texture(newTexture); - replaceAssetTexture(rendobj,oldTex,newTex); - REF_PTR_RELEASE(newTex); - REF_PTR_RELEASE(oldTex); + ///@todo Granny objects are realtime scaled - fix to scale like W3D. + rendobj->Set_ObjectScale(scale); + return rendobj; } - if (reallyscale) - rendobj->Scale(scale); - - if (reallycolor) - Recolor_Asset(rendobj,color); +#endif W3DPrototypeClass *w3dproto = newInstance(W3DPrototypeClass)(rendobj, newname); rendobj->Release_Ref(); @@ -938,6 +986,14 @@ bool W3DAssetManager::Load_3D_Assets( const char * filename ) GetPrecisionTimer(&startTime64); #endif +#ifdef INCLUDE_GRANNY_IN_BUILD + Bool isGranny = false; + char *pext=strrchr(filename,'.'); //find file extension + if (pext) + isGranny=(strnicmp(pext,".GR2",4) == 0); + if (!isGranny) +#endif + // Try to find an existing prototype char basename[512]; strcpy(basename, filename); @@ -982,6 +1038,40 @@ bool W3DAssetManager::Load_3D_Assets( const char * filename ) #endif return result; +#ifdef INCLUDE_GRANNY_IN_BUILD + //Loading assets for Granny File + PrototypeClass * newproto = NULL; + newproto = _GrannyLoader.Load_W3D(filename); + /* + ** Now, see if the prototype that we loaded has a duplicate + ** name with any of our currently loaded prototypes (can't have that!) + */ + if (newproto != NULL) { + if (!Render_Obj_Exists(newproto->Get_Name())) { + /* + ** Add the new, unique prototype to our list + */ + Add_Prototype(newproto); + } else { + /* + ** Warn the user about a name collision with this prototype + ** and dump it + */ + WWDEBUG_SAY(("Render Object Name Collision: %s\r\n",newproto->Get_Name())); + delete newproto; + newproto = NULL; + return false; + } + } else { + /* + ** Warn user that a prototype was not generated from this + ** chunk type + */ + WWDEBUG_SAY(("Could not generate Granny prototype! File = %d\r\n",filename)); + return false; + } + return true; +#endif } #ifdef DUMP_PERF_STATS @@ -999,17 +1089,47 @@ HAnimClass * W3DAssetManager::Get_HAnim(const char * name) #endif WWPROFILE( "WW3DAssetManager::Get_HAnim" ); - HAnimClass *anim=WW3DAssetManager::Get_HAnim(name); -#ifdef DUMP_PERF_STATS - if (HAnim_Recursions == 1) +#ifdef INCLUDE_GRANNY_IN_BUILD + Bool isGranny = false; + char *pext=strrchr(name,'.'); //find file extension + if (pext) + isGranny=(strnicmp(pext,".GR2",4) == 0); + if (!isGranny) +#endif { - GetPrecisionTimer(&endTime64); - Total_Get_HAnim_Time += endTime64-startTime64; - } - HAnim_Recursions--; + HAnimClass *anim=WW3DAssetManager::Get_HAnim(name); //we only do custom granny processing. +#ifdef DUMP_PERF_STATS + if (HAnim_Recursions == 1) + { + GetPrecisionTimer(&endTime64); + Total_Get_HAnim_Time += endTime64-startTime64; + } + HAnim_Recursions--; #endif - return anim; + return anim; + } +#ifdef INCLUDE_GRANNY_IN_BUILD + // Try to find the hanim + HAnimClass * anim = m_GrannyAnimManager->Get_Anim(name); + if (WW3D_Load_On_Demand && anim == NULL) { // If we didn't find it, try to load on demand + if ( !m_GrannyAnimManager->Is_Missing( name ) ) { // if this is NOT a known missing anim + + // If we can't find it, try the parent directory + if ( m_GrannyAnimManager->Load_Anim(name) == 1 ) { + StringClass new_filename = StringClass("..\\") + name; + m_GrannyAnimManager->Load_Anim( new_filename ); + } + + anim = m_GrannyAnimManager->Get_Anim(name); // Try again + if (anim == NULL) { +// WWDEBUG_SAY(("WARNING: Animation %s not found!\n", name)); + m_GrannyAnimManager->Register_Missing( name ); // This is now a KNOWN missing anim + } + } + } + return anim; +#endif } //--------------------------------------------------------------------- @@ -1300,6 +1420,11 @@ static inline void Munge_Texture_Name(char *newname, const char *oldname, const RenderObjClass * W3DAssetManager::Create_Render_Obj(const char * name,float scale, const Vector3 &hsv_shift) { Bool isGranny = false; +#ifdef INCLUDE_GRANNY_IN_BUILD + char *pext=strrchr(name,'.'); //find file extension + if (pext) + isGranny=(strnicmp(pext,".GR2",4) == 0); +#endif Bool reallyscale = (WWMath::Fabs(scale - ident_scale) > scale_epsilon); Bool reallyhsv_shift = (WWMath::Fabs(hsv_shift.X - ident_HSV.X) > H_epsilon || WWMath::Fabs(hsv_shift.Y - ident_HSV.Y) > S_epsilon || WWMath::Fabs(hsv_shift.Z - ident_HSV.Z) > V_epsilon); diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp index 4c5905b0296..ce47b3c99aa 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp @@ -1124,6 +1124,16 @@ void RTS3DScene::Customized_Render( RenderInfoClass &rinfo ) renderOneObject(rinfo, robj, localPlayerIndex); } } + +#ifdef INCLUDE_GRANNY_IN_BUILD + if (TheGrannyRenderObjSystem) + { //we only want to update granny animations once per frame, so only queue + //them up if this is not a mirror pass. + if (!ShaderClass::Is_Backface_Culling_Inverted()) + TheGrannyRenderObjSystem->queueUpdate(); + TheGrannyRenderObjSystem->Flush(); + } +#endif //Tell shadow manager to render shadows at the end of this frame //Don't draw shadows if there is no terrain present. diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp index e94dc4114d8..a1229875079 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp @@ -84,6 +84,14 @@ W3DTerrainVisual::~W3DTerrainVisual() TheTerrainTracksRenderObjClassSystem=NULL; } +#ifdef INCLUDE_GRANNY_IN_BUILD + if (TheGrannyRenderObjSystem) + { + delete TheGrannyRenderObjSystem; + TheGrannyRenderObjSystem=NULL; + } +#endif + if (TheW3DShadowManager) { delete TheW3DShadowManager; @@ -113,6 +121,11 @@ void W3DTerrainVisual::init( void ) TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem; TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene); +#ifdef INCLUDE_GRANNY_IN_BUILD + // initialize Granny model drawing system + TheGrannyRenderObjSystem = NEW GrannyRenderObjSystem; +#endif + // initialize object shadow drawing system TheW3DShadowManager = NEW W3DShadowManager; TheW3DShadowManager->init(); diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.cpp b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.cpp index 55caafc76b9..7f51891c29f 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.cpp +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.cpp @@ -87,7 +87,6 @@ ParticleBufferClass::ParticleBufferClass ParticlePropertyStruct &rotation, float orient_rnd, ParticlePropertyStruct &frame, - ParticlePropertyStruct &blurtime, Vector3 accel, float max_age, TextureClass *tex, @@ -136,10 +135,6 @@ ParticleBufferClass::ParticleBufferClass FrameKeyFrameTimes(NULL), FrameKeyFrameValues(NULL), FrameKeyFrameDeltas(NULL), - NumBlurTimeKeyFrames(0), - BlurTimeKeyFrameTimes(NULL), - BlurTimeKeyFrameValues(NULL), - BlurTimeKeyFrameDeltas(NULL), NumRandomColorEntriesMinus1(0), RandomColorEntries(NULL), NumRandomAlphaEntriesMinus1(0), @@ -158,8 +153,6 @@ ParticleBufferClass::ParticleBufferClass RandomOrientationEntries(NULL), NumRandomFrameEntriesMinus1(0), RandomFrameEntries(NULL), - NumRandomBlurTimeEntriesMinus1(0), - RandomBlurTimeEntries(NULL), PointGroup(NULL), LineRenderer(NULL), Diffuse(NULL), @@ -168,7 +161,6 @@ ParticleBufferClass::ParticleBufferClass Size(NULL), Orientation(NULL), Frame(NULL), - TailPosition(NULL), APT(NULL), PingPongPosition(pingpong), Velocity(NULL), @@ -198,9 +190,6 @@ ParticleBufferClass::ParticleBufferClass // Create the frame array, keyframes, and randomizer table (if needed) Reset_Frames(frame); - // Create the blur time array, keyframes, and randomizer table if needed - Reset_Blur_Times(blurtime); - // We do not add a ref for the emitter (see DTor for detailed explanation) // if (Emitter) Emitter->Add_Ref(); @@ -306,10 +295,6 @@ ParticleBufferClass::ParticleBufferClass(const ParticleBufferClass & src) : FrameKeyFrameTimes(NULL), FrameKeyFrameValues(NULL), FrameKeyFrameDeltas(NULL), - NumBlurTimeKeyFrames(src.NumBlurTimeKeyFrames), - BlurTimeKeyFrameTimes(NULL), - BlurTimeKeyFrameValues(NULL), - BlurTimeKeyFrameDeltas(NULL), RandomColorEntries(NULL), RandomAlphaEntries(NULL), RandomSizeEntries(NULL), @@ -325,17 +310,14 @@ ParticleBufferClass::ParticleBufferClass(const ParticleBufferClass & src) : RandomOrientationEntries(NULL), NumRandomFrameEntriesMinus1(0), RandomFrameEntries(NULL), - NumRandomBlurTimeEntriesMinus1(0), - RandomBlurTimeEntries(NULL), PointGroup(NULL), LineRenderer(NULL), - Diffuse(NULL), + Diffuse(NULL), Color(NULL), Alpha(NULL), Size(NULL), Orientation(NULL), Frame(NULL), - TailPosition(NULL), APT(NULL), PingPongPosition(src.PingPongPosition), Velocity(NULL), @@ -506,31 +488,6 @@ ParticleBufferClass::ParticleBufferClass(const ParticleBufferClass & src) : FrameKeyFrameValues[0] = src.FrameKeyFrameValues[0]; } - // Set up the blur times keyframes - NumRandomBlurTimeEntriesMinus1 = src.NumRandomBlurTimeEntriesMinus1; - if (NumBlurTimeKeyFrames > 0) { - // Copy blur time keyframes - BlurTimeKeyFrameTimes = new unsigned int [NumBlurTimeKeyFrames]; - BlurTimeKeyFrameValues = new float [NumBlurTimeKeyFrames]; - BlurTimeKeyFrameDeltas = new float [NumBlurTimeKeyFrames]; - for (i = 0; i < NumBlurTimeKeyFrames; i++) { - BlurTimeKeyFrameTimes[i] = src.BlurTimeKeyFrameTimes[i]; - BlurTimeKeyFrameValues[i] = src.BlurTimeKeyFrameValues[i]; - BlurTimeKeyFrameDeltas[i] = src.BlurTimeKeyFrameDeltas[i]; - } - - // Copy blur time randomizer table - if (src.RandomBlurTimeEntries) { - RandomBlurTimeEntries = new float [NumRandomBlurTimeEntriesMinus1 + 1]; - for (unsigned int j = 0; j <= NumRandomBlurTimeEntriesMinus1; j++) { - RandomBlurTimeEntries[j] = src.RandomBlurTimeEntries[j]; - } - } - } else { - BlurTimeKeyFrameValues = new float [1]; - BlurTimeKeyFrameValues[0] = src.BlurTimeKeyFrameValues[0]; - } - // We do not add a ref for the emitter (see DTor for detailed explanation) // if (Emitter) Emitter->Add_Ref(); @@ -617,16 +574,12 @@ ParticleBufferClass::~ParticleBufferClass(void) if (FrameKeyFrameTimes) delete [] FrameKeyFrameTimes; if (FrameKeyFrameValues) delete [] FrameKeyFrameValues; if (FrameKeyFrameDeltas) delete [] FrameKeyFrameDeltas; - if (BlurTimeKeyFrameTimes) delete [] BlurTimeKeyFrameTimes; - if (BlurTimeKeyFrameValues) delete [] BlurTimeKeyFrameValues; - if (BlurTimeKeyFrameDeltas) delete [] BlurTimeKeyFrameDeltas; if (RandomColorEntries) delete [] RandomColorEntries; if (RandomAlphaEntries) delete [] RandomAlphaEntries; if (RandomSizeEntries) delete [] RandomSizeEntries; if (RandomRotationEntries) delete [] RandomRotationEntries; if (RandomOrientationEntries) delete [] RandomOrientationEntries; if (RandomFrameEntries) delete [] RandomFrameEntries; - if (RandomBlurTimeEntries) delete [] RandomBlurTimeEntries; if (PointGroup) delete PointGroup; @@ -1967,154 +1920,6 @@ void ParticleBufferClass::Reset_Frames(ParticlePropertyStruct &new_props) } -void ParticleBufferClass::Reset_Blur_Times(ParticlePropertyStruct &new_blur_times) -{ - - unsigned int i; // Used in loops - float oo_intmax = 1.0f / (float)INT_MAX; - unsigned int ui_previous_key_time = 0; - unsigned int ui_current_key_time = 0; - - BlurTimeRandom = new_blur_times.Rand; - - // If the randomizer is effectively zero and there are no keyframes, then we just create a - // values array with one entry and store the starting value in it (the keyframes and random - // table will not be used in this case). - static const float eps_blur = 1e-5f; // Epsilon is equivalent to 1e-5 units per second - bool blurtime_rand_zero = (fabs(new_blur_times.Rand) < eps_blur); - if (blurtime_rand_zero && new_blur_times.NumKeyFrames == 0) { - - // Release Arrays, Reuse KeyFrameValues if the right size, - // otherwise release and reallocate. - if (BlurTimeKeyFrameTimes) { - delete [] BlurTimeKeyFrameTimes; - BlurTimeKeyFrameTimes = NULL; - } - if (BlurTimeKeyFrameDeltas) { - delete [] BlurTimeKeyFrameDeltas; - BlurTimeKeyFrameDeltas = NULL; - } - if (BlurTimeKeyFrameValues) { - if (NumBlurTimeKeyFrames > 1) { - delete [] BlurTimeKeyFrameValues; - BlurTimeKeyFrameValues = new float [1]; - } - } else { - BlurTimeKeyFrameValues = new float [1]; - } - - NumBlurTimeKeyFrames = 0; - NumRandomBlurTimeEntriesMinus1 = 0; - BlurTimeKeyFrameValues[0] = new_blur_times.Start; - - } else { - - // Check times of the keyframes (each keytime must be larger than the - // previous one by at least a millisecond, and we stop at the first - // keytime of MaxAge or larger. (If all keyframes below MaxAge, the value is - // constant during the last segment between last keyframe and MaxAge). - ui_previous_key_time = 0; - unsigned int key = 0; - for (; key < new_blur_times.NumKeyFrames; key++) { - ui_current_key_time = (unsigned int)(new_blur_times.KeyTimes[key] * 1000.0f); - WWASSERT(ui_current_key_time > ui_previous_key_time); - if (ui_current_key_time >= MaxAge) break; - ui_previous_key_time = ui_current_key_time; - } - bool blurtime_constant_at_end = (key == new_blur_times.NumKeyFrames); - - // Reuse BlurTimeKeyFrameValues, BlurTimeKeyFrameTimes and BlurTimeKeyFrameDeltas if the right size, - // otherwise release and reallocate. - unsigned int new_num_key_frames = key + 1;// Includes start keyframe (keytime == 0). - if (new_num_key_frames != NumBlurTimeKeyFrames) { - - if (BlurTimeKeyFrameTimes) { - delete [] BlurTimeKeyFrameTimes; - BlurTimeKeyFrameTimes = NULL; - } - if (BlurTimeKeyFrameValues) { - delete [] BlurTimeKeyFrameValues; - BlurTimeKeyFrameValues = NULL; - } - if (BlurTimeKeyFrameDeltas) { - delete [] BlurTimeKeyFrameDeltas; - BlurTimeKeyFrameDeltas = NULL; - } - - NumBlurTimeKeyFrames = new_num_key_frames; - BlurTimeKeyFrameTimes = new unsigned int [NumBlurTimeKeyFrames]; - BlurTimeKeyFrameValues = new float [NumBlurTimeKeyFrames]; - BlurTimeKeyFrameDeltas = new float [NumBlurTimeKeyFrames]; - } - - // Set keyframes (deltas will be set later) - BlurTimeKeyFrameTimes[0] = 0; - BlurTimeKeyFrameValues[0] = new_blur_times.Start; - for (i = 1; i < NumBlurTimeKeyFrames; i++) { - unsigned int im1 = i - 1; - BlurTimeKeyFrameTimes[i] = (unsigned int)(new_blur_times.KeyTimes[im1] * 1000.0f); - BlurTimeKeyFrameValues[i] = new_blur_times.Values[im1]; - } - - // Do deltas for all frame keyframes except last - for (i = 0; i < NumBlurTimeKeyFrames - 1; i++) { - BlurTimeKeyFrameDeltas[i] = (BlurTimeKeyFrameValues[i + 1] - BlurTimeKeyFrameValues[i]) / - (float)(BlurTimeKeyFrameTimes[i + 1] - BlurTimeKeyFrameTimes[i]); - } - - // Do delta for last frame keyframe (i is NumBlurTimeKeyFrames - 1) - if (blurtime_constant_at_end) { - BlurTimeKeyFrameDeltas[i] = 0.0f; - } else { - // This is OK because if frame_constant_at_end is false, NumBlurTimeKeyFrames is equal or - // smaller than new_props.NumKeyFrames so new_props.Values[NumBlurTimeKeyFrames - 1] and - // new_props.KeyTimes[NumBlurTimeKeyFrames - 1] exist. - BlurTimeKeyFrameDeltas[i] = (new_blur_times.Values[i] - BlurTimeKeyFrameValues[i]) / - (new_blur_times.KeyTimes[i] * 1000.0f - (float)BlurTimeKeyFrameTimes[i]); - } - - // Set up frame randomizer table - if (blurtime_rand_zero) { - - if (RandomBlurTimeEntries) { - // Reuse RandomBlurTimeEntries if the right size, otherwise release and reallocate. - if (NumRandomBlurTimeEntriesMinus1 != 0) { - delete [] RandomBlurTimeEntries; - RandomBlurTimeEntries = new float [1]; - } - } else { - RandomBlurTimeEntries = new float [1]; - } - - NumRandomBlurTimeEntriesMinus1 = 0; - RandomBlurTimeEntries[0] = 0.0f; - } else { - - // Default size of randomizer tables (tables for non-zero randomizers will be this size) - unsigned int pot_num = Find_POT(MaxNum); - unsigned int default_randomizer_entries = MIN(pot_num, MAX_RANDOM_ENTRIES); - - if (RandomBlurTimeEntries) { - // Reuse RandomBlurTimeEntries if the right size, otherwise release and reallocate. - if (NumRandomBlurTimeEntriesMinus1 != (default_randomizer_entries - 1)) { - delete [] RandomBlurTimeEntries; - RandomBlurTimeEntries = new float [default_randomizer_entries]; - } - } else { - RandomBlurTimeEntries = new float [default_randomizer_entries]; - } - - NumRandomBlurTimeEntriesMinus1 = default_randomizer_entries - 1; - - float scale = new_blur_times.Rand * oo_intmax; - for (unsigned int j = 0; j <= NumRandomBlurTimeEntriesMinus1; j++) { - RandomBlurTimeEntries[j] = rand_gen * scale; - } - } - } -} - - // This informs the buffer that the emitter is dead, so it can release // its pointer to it and be removed itself after all its particles dies // out. @@ -2241,7 +2046,6 @@ void ParticleBufferClass::Update_Visual_Particle_State(void) unsigned int skey = NumSizeKeyFrames - 1; unsigned int rkey = NumRotationKeyFrames - 1; unsigned int fkey = NumFrameKeyFrames - 1; - unsigned int bkey = NumBlurTimeKeyFrames -1; unsigned int part; Vector3 *color = Color ? Color->Get_Array(): NULL; @@ -2249,16 +2053,6 @@ void ParticleBufferClass::Update_Visual_Particle_State(void) float *size = Size ? Size->Get_Array(): NULL; uint8 *orientation = Orientation ? Orientation->Get_Array(): NULL; uint8 *frame = Frame ? Frame->Get_Array(): NULL; - Vector3 *tailposition = TailPosition ? TailPosition->Get_Array() : NULL; - - Vector3 *position=NULL; - - if (PingPongPosition) { - int pingpong = WW3D::Get_Frame_Count() & 0x1; - position = Position[pingpong]->Get_Array(); - } else { - position = Position[0]->Get_Array(); - } for (part = Start; part < sub1_end; part++) { @@ -2329,19 +2123,6 @@ void ParticleBufferClass::Update_Visual_Particle_State(void) frame[part] = (uint)(((int)(tmp_frame)) & 0xFF); } - - if (tailposition) { - // We go from older to younger particles, so we go backwards from the last keyframe until - // age >= keytime. This loop must terminate because the 0th keytime is 0. - float blur_time = BlurTimeKeyFrameValues[0]; - if (BlurTimeKeyFrameTimes) { - for (; part_age < BlurTimeKeyFrameTimes[bkey]; bkey--); - blur_time = BlurTimeKeyFrameValues[bkey] + - BlurTimeKeyFrameDeltas[bkey] * (float)(part_age - BlurTimeKeyFrameTimes[bkey]) + - RandomBlurTimeEntries[part & NumRandomBlurTimeEntriesMinus1]; - } - tailposition[part]=position[part]-Velocity[part]*blur_time*1000; - } } for (part = sub2_start; part < End; part++) { @@ -2413,19 +2194,6 @@ void ParticleBufferClass::Update_Visual_Particle_State(void) frame[part] = (uint)(((int)(tmp_frame)) & 0xFF); } - - if (tailposition) { - // We go from older to younger particles, so we go backwards from the last keyframe until - // age >= keytime. This loop must terminate because the 0th keytime is 0. - float blur_time = BlurTimeKeyFrameValues[0]; - if (BlurTimeKeyFrameTimes) { - for (; part_age < BlurTimeKeyFrameTimes[bkey]; bkey--); - blur_time = BlurTimeKeyFrameValues[bkey] + - BlurTimeKeyFrameDeltas[bkey] * (float)(part_age - BlurTimeKeyFrameTimes[bkey]) + - RandomBlurTimeEntries[part & NumRandomBlurTimeEntriesMinus1]; - } - tailposition[part]=position[part]-Velocity[part]*blur_time*1000; - } } } @@ -2942,7 +2710,7 @@ void ParticleBufferClass::Get_Frame_Key_Frames (ParticlePropertyStruct &f // // If we have more than just the start rotation, build - // an array of key times and frame values + // an array of key times and rotation values // if (real_keyframe_count > 0) { frames.KeyTimes = W3DNEWARRAY float[real_keyframe_count]; @@ -2976,62 +2744,6 @@ void ParticleBufferClass::Get_Frame_Key_Frames (ParticlePropertyStruct &f return ; } -void ParticleBufferClass::Get_Blur_Time_Key_Frames (ParticlePropertyStruct &blurtimes) const -{ - int real_keyframe_count = (NumBlurTimeKeyFrames > 0) ? (NumBlurTimeKeyFrames - 1) : 0; - bool create_last_keyframe = false; - - // - // Determine if there is a keyframe at the very end of the particle's lifetime - // - if ((BlurTimeKeyFrameDeltas != NULL) && - (BlurTimeKeyFrameDeltas[NumBlurTimeKeyFrames - 1] != 0)) { - real_keyframe_count ++; - create_last_keyframe = true; - } - - blurtimes.Start = BlurTimeKeyFrameValues[0]; - blurtimes.Rand = BlurTimeRandom; - blurtimes.NumKeyFrames = real_keyframe_count; - blurtimes.KeyTimes = NULL; - blurtimes.Values = NULL; - - // - // If we have more than just the start rotation, build - // an array of key times and blur time values - // - if (real_keyframe_count > 0) { - blurtimes.KeyTimes = new float[real_keyframe_count]; - blurtimes.Values = new float[real_keyframe_count]; - - // - // Copy the keytimes and frame values - // - unsigned int index; - for (index = 1; index < NumBlurTimeKeyFrames; index ++) { - blurtimes.KeyTimes[index - 1] = ((float)BlurTimeKeyFrameTimes[index]) / 1000; - blurtimes.Values[index - 1] = BlurTimeKeyFrameValues[index]; - } - - // - // Add a keyframe at the very end of the timeline if necessary - // - if (create_last_keyframe) { - blurtimes.KeyTimes[index - 1] = ((float)MaxAge / 1000); - - // - // Determine what the value of the last keyframe should be - // - float start_blurtime = BlurTimeKeyFrameValues[index - 1]; - float &delta = BlurTimeKeyFrameDeltas[NumBlurTimeKeyFrames - 1]; - float time_delta = MaxAge - BlurTimeKeyFrameTimes[index - 1]; - blurtimes.Values[index - 1] = start_blurtime + (delta * time_delta); - } - } - - return ; -} - void ParticleBufferClass::Set_LOD_Max_Screen_Size(int lod_level,float max_screen_size) { if ((lod_level <0) || (lod_level > 17)) { diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.h b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.h index ec8e0cebfbd..5c9828bd042 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.h +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_buf.h @@ -89,8 +89,7 @@ class ParticleBufferClass : public RenderObjClass ParticleBufferClass(ParticleEmitterClass *emitter, unsigned int buffer_size, ParticlePropertyStruct &color, ParticlePropertyStruct &opacity, ParticlePropertyStruct &size, ParticlePropertyStruct &rotation, - float orient_rnd, ParticlePropertyStruct &frame, - ParticlePropertyStruct &blurtime, Vector3 accel, + float orient_rnd, ParticlePropertyStruct &frame, Vector3 accel, float max_age, TextureClass *tex, ShaderClass shader, bool pingpong, int render_mode, int frame_mode, const W3dEmitterLinePropertiesStruct * line_props); @@ -152,7 +151,6 @@ class ParticleBufferClass : public RenderObjClass void Reset_Size(ParticlePropertyStruct &new_props); void Reset_Rotations(ParticlePropertyStruct &new_rotations, float orient_rnd); void Reset_Frames(ParticlePropertyStruct &new_frames); - void Reset_Blur_Times(ParticlePropertyStruct &new_blur_times); // This informs the buffer that the emitter is dead, so it can release // its pointer to it and be removed itself after all its particles dies @@ -219,7 +217,6 @@ class ParticleBufferClass : public RenderObjClass void Get_Size_Key_Frames (ParticlePropertyStruct &sizes) const; void Get_Rotation_Key_Frames (ParticlePropertyStruct &rotations) const; void Get_Frame_Key_Frames (ParticlePropertyStruct &frames) const; - void Get_Blur_Time_Key_Frames (ParticlePropertyStruct &blurtimes) const; float Get_Initial_Orientation_Random (void) const { return InitialOrientationRandom; } // Total Active Particle Buffer Count @@ -332,10 +329,6 @@ class ParticleBufferClass : public RenderObjClass unsigned int * FrameKeyFrameTimes; // 0th entry is always 0 float * FrameKeyFrameValues; float * FrameKeyFrameDeltas; - unsigned int NumBlurTimeKeyFrames; - unsigned int * BlurTimeKeyFrameTimes; // 0th entry is always 0 - float * BlurTimeKeyFrameValues; - float * BlurTimeKeyFrameDeltas; // These tables are indexed by the array position in the particle buffer. // The table size is either the smallest power of two equal or larger @@ -357,15 +350,12 @@ class ParticleBufferClass : public RenderObjClass float * RandomOrientationEntries; unsigned int NumRandomFrameEntriesMinus1; // 2^n - 1 so can be used as a mask also float * RandomFrameEntries; - unsigned int NumRandomBlurTimeEntriesMinus1; // 2^n - 1 so can be used as a mask also - float * RandomBlurTimeEntries; Vector3 ColorRandom; float OpacityRandom; float SizeRandom; float RotationRandom; float FrameRandom; - float BlurTimeRandom; float InitialOrientationRandom; // This object implements particle rendering @@ -385,7 +375,6 @@ class ParticleBufferClass : public RenderObjClass ShareBufferClass * Alpha; ShareBufferClass * Size; ShareBufferClass * Frame; - ShareBufferClass * TailPosition; // Only used for line groups ShareBufferClass * Orientation; ShareBufferClass * APT; diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.cpp b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.cpp index 2ed27f6d4ef..46d691a97b4 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.cpp +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.cpp @@ -73,7 +73,6 @@ ParticleEmitterClass::ParticleEmitterClass(float emit_rate, unsigned int burst_s ParticlePropertyStruct &size, ParticlePropertyStruct &rotation, float orient_rnd, ParticlePropertyStruct &frames, - ParticlePropertyStruct &blur_times, Vector3 accel, float max_age, TextureClass *tex, ShaderClass shader, int max_particles, int max_buffer_size, bool pingpong,int render_mode,int frame_mode, const W3dEmitterLinePropertiesStruct * line_props @@ -112,7 +111,7 @@ ParticleEmitterClass::ParticleEmitterClass(float emit_rate, unsigned int burst_s max_num = MAX(max_num, 2); // max_num of 1 causes problems Buffer = W3DNEW ParticleBufferClass(this, max_num, color, opacity, size, rotation, orient_rnd, - frames, blur_times, accel/1000000.0f,max_age, tex, shader, pingpong, render_mode, frame_mode, + frames, accel/1000000.0f,max_age, tex, shader, pingpong, render_mode, frame_mode, line_props); SET_REF_OWNER( Buffer ); BufferSceneNeeded = true; @@ -208,8 +207,7 @@ ParticleEmitterClass::Create_From_Definition (const ParticleEmitterDefClass &def const char *ptexture_filename = definition.Get_Texture_Filename (); TextureClass *ptexture = NULL; if (ptexture_filename && ptexture_filename[0]) { - ptexture = WW3DAssetManager::Get_Instance()->Get_Texture - ( + ptexture = WW3DAssetManager::Get_Instance()->Get_Texture( ptexture_filename, MIP_LEVELS_ALL, WW3D_FORMAT_UNKNOWN, @@ -239,14 +237,12 @@ ParticleEmitterClass::Create_From_Definition (const ParticleEmitterDefClass &def ParticlePropertyStruct size_keys; ParticlePropertyStruct rotation_keys; ParticlePropertyStruct frame_keys; - ParticlePropertyStruct blur_time_keys; definition.Get_Color_Keyframes (color_keys); definition.Get_Opacity_Keyframes (opacity_keys); definition.Get_Size_Keyframes (size_keys); definition.Get_Rotation_Keyframes (rotation_keys); definition.Get_Frame_Keyframes (frame_keys); - definition.Get_Blur_Time_Keyframes (blur_time_keys); // // Create the emitter @@ -264,7 +260,6 @@ ParticleEmitterClass::Create_From_Definition (const ParticleEmitterDefClass &def rotation_keys, definition.Get_Initial_Orientation_Random(), frame_keys, - blur_time_keys, definition.Get_Acceleration (), definition.Get_Lifetime (), ptexture, @@ -286,8 +281,6 @@ ParticleEmitterClass::Create_From_Definition (const ParticleEmitterDefClass &def if (rotation_keys.Values != NULL) delete [] rotation_keys.Values; if (frame_keys.KeyTimes != NULL) delete [] frame_keys.KeyTimes; if (frame_keys.Values != NULL) delete [] frame_keys.Values; - if (blur_time_keys.KeyTimes != NULL) delete [] blur_time_keys.KeyTimes; - if (blur_time_keys.Values != NULL) delete [] blur_time_keys.Values; // Pass the name along to the emitter pemitter->Set_Name (definition.Get_Name ()); @@ -782,16 +775,6 @@ ParticleEmitterClass::Build_Definition (void) const if (frames.KeyTimes != NULL) delete [] frames.KeyTimes; if (frames.Values != NULL) delete [] frames.Values; - // - // Pass the blur time keyframes onto the definition - // - ParticlePropertyStruct blur_times; - Get_Blur_Time_Key_Frames (blur_times); - pdefinition->Set_Blur_Time_Keyframes (blur_times); - if (blur_times.KeyTimes != NULL) delete [] blur_times.KeyTimes; - if (blur_times.Values != NULL) delete [] blur_times.Values; - - // // Set up the line parameters // diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.h b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.h index 485355ced53..f5729f3d84d 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.h +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_emt.h @@ -115,7 +115,6 @@ class ParticleEmitterClass : public RenderObjClass ParticlePropertyStruct &size, ParticlePropertyStruct &rotation, float orient_rnd, ParticlePropertyStruct &frames, - ParticlePropertyStruct &blur_times, Vector3 accel, float max_age, TextureClass *tex, ShaderClass shader = ShaderClass::_PresetAdditiveSpriteShader, int max_particles = 0, int max_buffer_size = -1, bool pingpong = false, @@ -160,7 +159,7 @@ class ParticleEmitterClass : public RenderObjClass virtual void Set_Animation_Hidden(int onoff) { RenderObjClass::Set_Animation_Hidden (onoff); Update_On_Visibilty (); } virtual void Set_Force_Visible(int onoff) { RenderObjClass::Set_Force_Visible (onoff); Update_On_Visibilty (); } - virtual void Set_LOD_Bias(float bias) { if (Buffer) Buffer->Set_LOD_Bias(bias); } + virtual void Set_LOD_Bias(float bias) { if (Buffer) Buffer->Set_LOD_Bias(bias); } // These are not part of the renderobject interface: @@ -185,7 +184,6 @@ class ParticleEmitterClass : public RenderObjClass void Reset_Size(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Size(new_props); } void Reset_Rotations(ParticlePropertyStruct &new_props, float orient_rnd) { if (Buffer) Buffer->Reset_Rotations(new_props, orient_rnd); } void Reset_Frames(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Frames(new_props); } - void Reset_Blur_Times(ParticlePropertyStruct &new_props) { if (Buffer) Buffer->Reset_Blur_Times(new_props); } // Change emission/burst rate, or tell the emitter to emit a one-time burst. // NOTE: default buffer size fits the emission/burst rate that the emitter was created with. @@ -254,7 +252,6 @@ class ParticleEmitterClass : public RenderObjClass void Get_Size_Key_Frames (ParticlePropertyStruct &sizes) const { Buffer->Get_Size_Key_Frames (sizes); } void Get_Rotation_Key_Frames (ParticlePropertyStruct &rotations) const { Buffer->Get_Rotation_Key_Frames (rotations); } void Get_Frame_Key_Frames (ParticlePropertyStruct &frames) const { Buffer->Get_Frame_Key_Frames (frames); } - void Get_Blur_Time_Key_Frames (ParticlePropertyStruct &blurtimes) const { Buffer->Get_Blur_Time_Key_Frames (blurtimes); } float Get_Initial_Orientation_Random (void) const { return Buffer->Get_Initial_Orientation_Random(); } // Line rendering accessors diff --git a/Generals/Code/Tools/CMakeLists.txt b/Generals/Code/Tools/CMakeLists.txt index f1b330cfed3..b96d413938f 100644 --- a/Generals/Code/Tools/CMakeLists.txt +++ b/Generals/Code/Tools/CMakeLists.txt @@ -17,6 +17,7 @@ if(RTS_BUILD_GENERALS_EXTRAS) add_subdirectory(buildVersionUpdate) add_subdirectory(Launcher) add_subdirectory(mangler) + add_subdirectory(matchbot) add_subdirectory(PATCHGET) add_subdirectory(textureCompress) add_subdirectory(timingTest) diff --git a/Core/Tools/matchbot/CMakeLists.txt b/Generals/Code/Tools/matchbot/CMakeLists.txt similarity index 79% rename from Core/Tools/matchbot/CMakeLists.txt rename to Generals/Code/Tools/matchbot/CMakeLists.txt index d29e055db7d..316701119f9 100644 --- a/Core/Tools/matchbot/CMakeLists.txt +++ b/Generals/Code/Tools/matchbot/CMakeLists.txt @@ -59,14 +59,14 @@ set(MATCHBOT_SRC "wnet/udp.h" ) -add_executable(core_matchbot WIN32) -set_target_properties(core_matchbot PROPERTIES OUTPUT_NAME matchbot) +add_executable(g_matchbot WIN32) +set_target_properties(g_matchbot PROPERTIES OUTPUT_NAME matchbot) -target_sources(core_matchbot PRIVATE ${MATCHBOT_SRC}) +target_sources(g_matchbot PRIVATE ${MATCHBOT_SRC}) -target_include_directories(core_matchbot PRIVATE . wlib wnet) +target_include_directories(g_matchbot PRIVATE . wlib wnet) -target_link_libraries(core_matchbot PRIVATE +target_link_libraries(g_matchbot PRIVATE gamespy::gamespy core_config core_utility @@ -74,5 +74,5 @@ target_link_libraries(core_matchbot PRIVATE ) if(WIN32 OR "${CMAKE_SYSTEM}" MATCHES "Windows") - target_link_options(core_matchbot PRIVATE /subsystem:console) + target_link_options(g_matchbot PRIVATE /subsystem:console) endif() diff --git a/Generals/Code/Tools/matchbot/debug.cpp b/Generals/Code/Tools/matchbot/debug.cpp new file mode 100644 index 00000000000..642fc38d220 --- /dev/null +++ b/Generals/Code/Tools/matchbot/debug.cpp @@ -0,0 +1,61 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +// FILE: debug.cpp //////////////////////////////////////////////////////// +// Minmal debug info +// Author: Matthew D. Campbell, Sept 2002 + +#include +#include "debug.h" +#include + +#ifdef DEBUG + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WINDOWS + +#include +void DebugCrash( const char *fmt, ... ) {} +char* TheCurrentIgnoreCrashPtr; + +#else + +#endif + +void DebugLog(const char *fmt, ...) +{ + static char buffer[1024]; + va_list va; + va_start( va, fmt ); + vsnprintf(buffer, 1024, fmt, va ); + buffer[1023] = 0; + va_end( va ); + + //printf( buffer ); + DBGMSG(buffer); +} + +#ifdef __cplusplus +} +#endif + +#endif // DEBUG + diff --git a/Generals/Code/Tools/matchbot/debug.h b/Generals/Code/Tools/matchbot/debug.h new file mode 100644 index 00000000000..2749241feef --- /dev/null +++ b/Generals/Code/Tools/matchbot/debug.h @@ -0,0 +1,46 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +// FILE: debug.h ////////////////////////////////////////////////////////////// +// Minimal debug info +// Author: Matthew D. Campbell, Sept 2002 + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#ifdef DEBUG + +#include + +#ifdef __cplusplus +extern "C" { +#endif +void DebugLog( const char *fmt, ... ); +#ifdef __cplusplus +} +#endif +#define DEBUG_LOG(x) DebugLog x + +#else // DEBUG + +#define DEBUG_LOG(x) {} + +#endif // DEBUG + +#endif // __DEBUG_H__ + diff --git a/Generals/Code/Tools/matchbot/encrypt.cpp b/Generals/Code/Tools/matchbot/encrypt.cpp new file mode 100644 index 00000000000..06950a90019 --- /dev/null +++ b/Generals/Code/Tools/matchbot/encrypt.cpp @@ -0,0 +1,94 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include +#include +#include "encrypt.h" + +#define MAX_CHARS 65 +static char Base_String[MAX_CHARS] = + + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"; + +static char Return_Buffer[MAX_ENCRYPTED_STRING + 1]; +static char Temp_Buffer [MAX_ENCRYPTED_STRING + 1]; + +/*******************************************************/ +/* This function is a simple one-way encryption that */ +/* is portable across many platforms */ +/* */ +/* The 'String' to encrypt MUST be up to 8 chars long */ +/* and should be no shorter than 4 characters. */ +/* It can contain letters and numbers and '.' and '/' */ +/*******************************************************/ + +/* String is the original string to encrypt */ +/* Seed is the string to encrypt */ +//char *encrypt(char *String, char *Seed) +char *do_encrypt(char *String) +{ + /* We need a 56 bit key, so use two 32 bit values */ + /* and we'll strip off the high order 8 bits */ + //unsigned long Random_Seed_Value_high = 0; /* 32 bit seed value */ + //unsigned long Random_Seed_Value_low = 0; /* 32 bit seed value */ + //unsigned long Temp_high = 0; /* 32 bit storage value */ + //unsigned long Temp_low = 0; /* 32 bit storage value */ + unsigned int UpCnt = 0, DnCnt = 0, Cnt = 0; + unsigned int Length = strlen(String); + + /* Ok, here is the algorithm: */ + /* */ + + if (Length > MAX_ENCRYPTED_STRING) + Length = MAX_ENCRYPTED_STRING; + + for (UpCnt = 0, DnCnt = Length; UpCnt < Length; UpCnt++, DnCnt--) + if (String[UpCnt] & 0x01) + Temp_Buffer[UpCnt] = (String[UpCnt] << (String[UpCnt] & 0x01)) & + String[DnCnt]; + else + Temp_Buffer[UpCnt] = (String[UpCnt] << (String[UpCnt] & 0x01)) ^ + String[DnCnt]; + + for (Cnt = 0; Cnt < MAX_ENCRYPTED_STRING; Cnt++) + Return_Buffer[Cnt] = Base_String[Temp_Buffer[Cnt] & 0x3F]; + + Return_Buffer[Cnt] = NULL; + return (Return_Buffer); + +} + + +#ifdef UNIT_TEST +void main(void) +{ + char Input_String[9]; + char *new_string; + + while (1) + { + printf ("Enter a string to encrypt:"); + gets(Input_String); + printf("\nString enterred was: %s", Input_String); + new_string = encrypt(Input_String, "ab"); + printf("\nEncrypted string is: %s", new_string); + } +} + +#endif + diff --git a/Generals/Code/Tools/matchbot/encrypt.h b/Generals/Code/Tools/matchbot/encrypt.h new file mode 100644 index 00000000000..97f7d69ed4a --- /dev/null +++ b/Generals/Code/Tools/matchbot/encrypt.h @@ -0,0 +1,28 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef ENCRYPT_HEADER +#define ENCRYPT_HEADER + +#define MAX_ENCRYPTED_STRING 8 +//char *do_encrypt(char *, char *); +char *do_encrypt(char *); + +#endif + + diff --git a/Generals/Code/Tools/matchbot/generals.cpp b/Generals/Code/Tools/matchbot/generals.cpp new file mode 100644 index 00000000000..3a72c24d510 --- /dev/null +++ b/Generals/Code/Tools/matchbot/generals.cpp @@ -0,0 +1,1322 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifdef _WIN32 +#include +#endif + +#include +#include +#include +#include "mydebug.h" + +#include + +#include +#include +#include +#include + +#ifdef _UNIX +using namespace std; +#endif + +/* +#ifdef IN +#undef IN +#endif +#define IN const +*/ +#include "generals.h" +/* +#ifdef IN +#undef IN +#endif +#define IN const +*/ +#include "global.h" +/* +#ifdef IN +#undef IN +#endif +#define IN const +*/ + +std::string intToString(int val) +{ + std::string s = ""; + bool neg = (val < 0); + if (val < 0) + { + val = -val; + } + if (val == 0) + return "0"; + + char buf[2]; + buf[1] = 0; + while (val) + { + buf[0] = '0' + val%10; + val /= 10; + s.insert(0, buf); + } + if (neg) + s.insert(0, "-"); + return s; +} + +std::string uintToString(unsigned int val) +{ + std::string s = ""; + if (val == 0) + return "0"; + + char buf[2]; + buf[1] = 0; + while (val) + { + buf[0] = '0' + val%10; + val /= 10; + s.insert(0, buf); + } + return s; +} + +MapBitSet MapSetUnion(const MapBitSet& a, const MapBitSet& b) +{ + MapBitSet c; + if (a.size() != b.size()) + return c; + + for (int i=0; i<(int)a.size(); ++i) + { + c.push_back(a[i] && b[i]); + } + + return c; +} + +int MapSetCount(const MapBitSet& a) +{ + int count=0; + for (int i=0; i<(int)a.size(); ++i) + { + //DBGMSG(a[i]); + if (a[i]) + ++count; + } + return count; +} + +// ===================================================================== +// Users +// ===================================================================== + +GeneralsUser::GeneralsUser(void) +{ + status = STATUS_INCHANNEL; + points = 1; + minPoints = maxPoints = 100; + country = color = -1; + pseudoPing.clear(); + matchStart = time(NULL); + timeToWiden = 0; + widened = false; + numPlayers = 2; + discons = maxDiscons = 2; + maps.clear(); + maxPing = 1000; +} + +static const int MaxPingValue = 255*255*2; + +int calcPingDelta(const GeneralsUser *a, const GeneralsUser *b) +{ + if (!a || !b || a->pseudoPing.size() != b->pseudoPing.size()) + return MaxPingValue; // Max ping + + int bestPing = MaxPingValue; + for (int i=0; i<(int)a->pseudoPing.size(); ++i) + { + int p1, p2; + p1 = a->pseudoPing[i]; + p2 = b->pseudoPing[i]; + + if (p1 * p1 + p2 * p2 < bestPing) + bestPing = p1 * p1 + p2 * p2; + } + + return (int)sqrt(bestPing); +} + + +// ===================================================================== +// Matcher thread +// ===================================================================== + +GeneralsMatcher::GeneralsMatcher() +{ + // Read some values from the config file + int quietTMP = 0; + Global.config.getInt("NOECHO", quietTMP); + if (quietTMP) + quiet = true; + else + quiet = false; + + // Grab the weights for different parameters + Global.config.getInt("MATCH_WEIGHT_LOWPING", weightLowPing, "GENERALS"); + Global.config.getInt("MATCH_WEIGHT_AVGPOINTS", weightAvgPoints, "GENERALS"); + totalWeight = weightLowPing + weightAvgPoints; + + INFMSG("weightLowPing = " << weightLowPing); + INFMSG("weightAvgPoints = " << weightAvgPoints); + INFMSG("totalWeight = " << totalWeight); + + Global.config.getInt("SecondsBetweenPoolSizeAnnouncements", m_secondsBetweenPoolSizeAnnouncements, NULL); + if (m_secondsBetweenPoolSizeAnnouncements < 10) + { + m_secondsBetweenPoolSizeAnnouncements = 10; + } + m_nextPoolSizeAnnouncement = time(NULL); +} + +void GeneralsMatcher::init(void) +{} + + +#define W(x) setw(x) << +void GeneralsMatcher::dumpUsers(void) +{} + + + +void GeneralsMatcher::sendMatchInfo(std::string name1, std::string name2, std::string name3, std::string name4, + std::string name5, std::string name6, std::string name7, std::string name8, + GeneralsUser *user1, GeneralsUser *user2, GeneralsUser *user3, GeneralsUser *user4, + GeneralsUser *user5, GeneralsUser *user6, GeneralsUser *user7, GeneralsUser *user8, + int numPlayers, int ladderID) +{ + MapBitSet tmp = MapSetUnion(user1->maps, user2->maps); + if (numPlayers > 2) + { + tmp = MapSetUnion(tmp, user3->maps); + tmp = MapSetUnion(tmp, user4->maps); + } + if (numPlayers > 4) + { + tmp = MapSetUnion(tmp, user5->maps); + tmp = MapSetUnion(tmp, user6->maps); + } + if (numPlayers > 6) + { + tmp = MapSetUnion(tmp, user7->maps); + tmp = MapSetUnion(tmp, user8->maps); + } + + int numMaps = MapSetCount(tmp); + + if (!numMaps) + { + DBGMSG("No maps!"); + user1->status = STATUS_WORKING; + user2->status = STATUS_WORKING; + if (numPlayers > 2) + { + user3->status = STATUS_WORKING; + user4->status = STATUS_WORKING; + } + if (numPlayers > 4) + { + user5->status = STATUS_WORKING; + user6->status = STATUS_WORKING; + } + if (numPlayers > 6) + { + user7->status = STATUS_WORKING; + user8->status = STATUS_WORKING; + } + return; + } + user1->status = STATUS_MATCHED; + user2->status = STATUS_MATCHED; + if (numPlayers > 2) + { + user3->status = STATUS_MATCHED; + user4->status = STATUS_MATCHED; + } + if (numPlayers > 4) + { + user5->status = STATUS_MATCHED; + user6->status = STATUS_MATCHED; + } + if (numPlayers > 6) + { + user7->status = STATUS_MATCHED; + user8->status = STATUS_MATCHED; + } + + int whichMap = Global.rnd.Int(0, RAND_MAX-1); + DBGMSG(whichMap); + whichMap = whichMap%numMaps; + DBGMSG(whichMap); + ++whichMap; + DBGMSG(whichMap); + DBGMSG("Random map #" << whichMap << "/" << numMaps); + + int i; + for (i=0; i<(int)user1->maps.size(); ++i) + { + if (tmp[i]) + --whichMap; + if (whichMap == 0) + break; + } + DBGMSG("Playing on map in pos " << i); + + std::string s; + s = "MBOT:MATCHED "; + s.append(intToString(i)); + s.append(" "); + s.append(intToString( Global.rnd.Int(0, RAND_MAX-1) )); + s.append(" "); + s.append(name1); + s.append(" "); + s.append(uintToString(user1->IP)); + s.append(" "); + s.append(intToString(user1->country)); + s.append(" "); + s.append(intToString(user1->color)); + s.append(" "); + s.append(intToString(user1->NAT)); + s.append(" "); + s.append(name2); + s.append(" "); + s.append(uintToString(user2->IP)); + s.append(" "); + s.append(intToString(user2->country)); + s.append(" "); + s.append(intToString(user2->color)); + s.append(" "); + s.append(intToString(user2->NAT)); + if (user3) + { + s.append(" "); + s.append(name3); + s.append(" "); + s.append(uintToString(user3->IP)); + s.append(" "); + s.append(intToString(user3->country)); + s.append(" "); + s.append(intToString(user3->color)); + s.append(" "); + s.append(intToString(user3->NAT)); + } + if (user4) + { + s.append(" "); + s.append(name4); + s.append(" "); + s.append(uintToString(user4->IP)); + s.append(" "); + s.append(intToString(user4->country)); + s.append(" "); + s.append(intToString(user4->color)); + s.append(" "); + s.append(intToString(user4->NAT)); + } + if (user5) + { + s.append(" "); + s.append(name5); + s.append(" "); + s.append(uintToString(user5->IP)); + s.append(" "); + s.append(intToString(user5->country)); + s.append(" "); + s.append(intToString(user5->color)); + s.append(" "); + s.append(intToString(user5->NAT)); + } + if (user6) + { + s.append(" "); + s.append(name6); + s.append(" "); + s.append(uintToString(user6->IP)); + s.append(" "); + s.append(intToString(user6->country)); + s.append(" "); + s.append(intToString(user6->color)); + s.append(" "); + s.append(intToString(user6->NAT)); + } + if (user7) + { + s.append(" "); + s.append(name7); + s.append(" "); + s.append(uintToString(user7->IP)); + s.append(" "); + s.append(intToString(user7->country)); + s.append(" "); + s.append(intToString(user7->color)); + s.append(" "); + s.append(intToString(user7->NAT)); + } + if (user8) + { + s.append(" "); + s.append(name8); + s.append(" "); + s.append(uintToString(user8->IP)); + s.append(" "); + s.append(intToString(user8->country)); + s.append(" "); + s.append(intToString(user8->color)); + s.append(" "); + s.append(intToString(user8->NAT)); + } + + std::string n; + n = name1; + n.append(","); + n.append(name2); + if (user3) + { + n.append(","); + n.append(name3); + } + if (user4) + { + n.append(","); + n.append(name4); + } + if (user5) + { + n.append(","); + n.append(name5); + } + if (user6) + { + n.append(","); + n.append(name6); + } + if (user7) + { + n.append(","); + n.append(name7); + } + if (user8) + { + n.append(","); + n.append(name8); + } + peerMessagePlayer(m_peer, n.c_str(), s.c_str(), NormalMessage); +} + +void GeneralsMatcher::checkMatches(void) +{ + bool showPoolSize = false; + time_t now = time(NULL); + if (now > m_nextPoolSizeAnnouncement) + { + m_nextPoolSizeAnnouncement = now + m_secondsBetweenPoolSizeAnnouncements; + showPoolSize = true; + } + checkMatchesInUserMap(m_nonLadderUsers1v1, 0, 2, showPoolSize); + checkMatchesInUserMap(m_nonLadderUsers2v2, 0, 4, showPoolSize); + checkMatchesInUserMap(m_nonLadderUsers3v3, 0, 6, showPoolSize); + checkMatchesInUserMap(m_nonLadderUsers4v4, 0, 8, showPoolSize); + + for (LadderMap::iterator it = m_ladders.begin(); it != m_ladders.end(); ++it) + { + checkMatchesInUserMap(it->second, it->first, 2, showPoolSize); + } +} + +double GeneralsMatcher::computeMatchFitness(const std::string& i1, const GeneralsUser *u1, const std::string& i2, const GeneralsUser *u2) +{ + //DBGMSG("matching "<status != STATUS_WORKING || u2->status != STATUS_WORKING) + return 0.0; + + // see if they pinged the same # of servers (sanity). + if (u1->pseudoPing.size() != u2->pseudoPing.size()) + return 0.0; + + // check point percentage ranges + int p1 = max(1,u1->points), p2 = max(1,u2->points); + double p1percent = (double)p2/(double)p1; + double p2percent = (double)p1/(double)p2; + //DBGMSG("points: " << p1 << "," << p2 << " - " << p1percent << "," << p2percent); + if (!u1->widened && ( p1percent < u1->minPoints || p1percent > u1->maxPoints )) + return 0.0; + + if (!u2->widened && ( p2percent < u2->minPoints || p2percent > u2->maxPoints )) + return 0.0; + + + int minP = min(p1, p2); + int maxP = max(p1, p2); + double pointPercent = (double)minP/(double)maxP; + //DBGMSG("\tpointPercent = "<widened && pingDelta > u1->maxPing) + return 0.0; + if (!u2->widened && pingDelta > u2->maxPing) + return 0.0; + //DBGMSG("pingDelta="<discons << "," << u1->maxDiscons << " " << u2->discons << "," << u2->maxDiscons); + // check discons + if (u1->maxDiscons && (!u1->widened && u2->discons > u1->maxDiscons)) + return 0.0; + if (u2->maxDiscons && (!u2->widened && u1->discons > u2->maxDiscons)) + return 0.0; + //DBGMSG("Made it through discons"); + + { + MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); + if (!MapSetCount(tmp)) + return 0.0; + } + + // they have something in common. calculate match fitness. + double matchFitness = ( weightAvgPoints * (1-pointPercent) + + weightLowPing * (MaxPingValue - pingDelta)/MaxPingValue ) / (double)totalWeight; + //DBGMSG("Match fitness: "<first << " vs " << i2->first << " has fitness " << matchFitness << "\n" + "\tpointPercent: " << pointPercent << "\n" + "\tpingDelta: " << pingDelta << "\n" + "\twidened: " << u1->widened << u2->widened << "\n" + "\tweightAvgPoints: " << weightAvgPoints << "\n" + "\tweightLowPing: " << weightLowPing << "\n" + "\ttotalWeight: " << totalWeight + ); + */ + + return matchFitness; +} + +void GeneralsMatcher::checkMatchesInUserMap(UserMap& userMap, int ladderID, int numPlayers, bool showPoolSize) +{ + UserMap::iterator i1, i2, i3, i4, i5, i6, i7, i8; + GeneralsUser *u1, *u2, *u3, *u4, *u5, *u6, *u7, *u8; + static const double fitnessThreshold = 0.3; + time_t now = time(NULL); + + std::string s; + if (showPoolSize) + { + s = "MBOT:POOLSIZE "; + s.append(intToString(userMap.size())); + } + + // iterate through users, timing them out as neccessary + for (i1 = userMap.begin(); i1 != userMap.end(); ++i1) + { + if (showPoolSize) + { + peerMessagePlayer(m_peer, i1->first.c_str(), s.c_str(), NormalMessage); + } + + u1 = i1->second; + if (u1->timeToWiden && u1->timeToWiden < now) + { + u1->timeToWiden = 0; + u1->widened = true; + for (int m=0; m<(int)u1->maps.size(); ++m) + u1->maps[m] = 1; + DBGMSG("Widening search for " << i1->first); + peerMessagePlayer(m_peer, i1->first.c_str(), "MBOT:WIDENINGSEARCH", NormalMessage); + } + } + + // iterate through all users, looking for a match + for (i1 = userMap.begin(); i1 != userMap.end(); ++i1) + { + u1 = i1->second; + if (u1->status != STATUS_WORKING) + continue; + + GeneralsUser *bestUser = NULL; + double bestMatchFitness = 0.0; + std::string bestName = ""; + + // look at everybody left + i2 = i1; + for (++i2; i2 != userMap.end(); ++i2) + { + u2 = i2->second; + if (u2->status != STATUS_WORKING) + continue; + + double matchFitness = computeMatchFitness(i1->first, u1, i2->first, u2); + if (matchFitness > fitnessThreshold) + { + if (numPlayers == 2) + { + if (matchFitness > bestMatchFitness) + { + // possibly match 2 players + bestMatchFitness = matchFitness; + bestUser = u2; + bestName = i2->first; + } + } + else + { + i3 = i2; + for (++i3; i3 != userMap.end(); ++i3) + { + u3 = i3->second; + if (u3->status != STATUS_WORKING) + continue; + + double matchFitness1 = computeMatchFitness(i1->first, u1, i3->first, u3); + double matchFitness2 = computeMatchFitness(i2->first, u2, i3->first, u3); + MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); + tmp = MapSetUnion(tmp, u3->maps); + if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold) + { + i4 = i3; + for (++i4; i4 != userMap.end(); ++i4) + { + u4 = i4->second; + if (u4->status != STATUS_WORKING) + continue; + + double matchFitness1 = computeMatchFitness(i1->first, u1, i4->first, u4); + double matchFitness2 = computeMatchFitness(i2->first, u2, i4->first, u4); + double matchFitness3 = computeMatchFitness(i3->first, u3, i4->first, u4); + MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); + tmp = MapSetUnion(tmp, u3->maps); + tmp = MapSetUnion(tmp, u4->maps); + if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold) + { + if (numPlayers == 4) + { + // match 4 players + sendMatchInfo(i1->first, i2->first, i3->first, i4->first, "", "", "", "", + u1, u2, u3, u4, NULL, NULL, NULL, NULL, 4, ladderID); + break; + } + else + { + + i5 = i4; + for (++i5; i5 != userMap.end(); ++i3) + { + u5 = i5->second; + if (u5->status != STATUS_WORKING) + continue; + + double matchFitness1 = computeMatchFitness(i1->first, u1, i5->first, u5); + double matchFitness2 = computeMatchFitness(i2->first, u2, i5->first, u5); + double matchFitness3 = computeMatchFitness(i3->first, u3, i5->first, u5); + double matchFitness4 = computeMatchFitness(i4->first, u4, i5->first, u5); + MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); + tmp = MapSetUnion(tmp, u3->maps); + tmp = MapSetUnion(tmp, u4->maps); + tmp = MapSetUnion(tmp, u5->maps); + if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold) + { + i6 = i5; + for (++i6; i6 != userMap.end(); ++i6) + { + u6 = i6->second; + if (u6->status != STATUS_WORKING) + continue; + + double matchFitness1 = computeMatchFitness(i1->first, u1, i6->first, u6); + double matchFitness2 = computeMatchFitness(i2->first, u2, i6->first, u6); + double matchFitness3 = computeMatchFitness(i3->first, u3, i6->first, u6); + double matchFitness4 = computeMatchFitness(i4->first, u4, i6->first, u6); + double matchFitness5 = computeMatchFitness(i5->first, u5, i6->first, u6); + MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); + tmp = MapSetUnion(tmp, u3->maps); + tmp = MapSetUnion(tmp, u4->maps); + tmp = MapSetUnion(tmp, u5->maps); + tmp = MapSetUnion(tmp, u6->maps); + if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold) + { + if (numPlayers == 6) + { + // match 6 players + sendMatchInfo(i1->first, i2->first, i3->first, i4->first, i5->first, i6->first, "", "", + u1, u2, u3, u4, u5, u6, NULL, NULL, 6, ladderID); + break; + } + else + { + + i7 = i6; + for (++i7; i7 != userMap.end(); ++i7) + { + u7 = i7->second; + if (u7->status != STATUS_WORKING) + continue; + + double matchFitness1 = computeMatchFitness(i1->first, u1, i7->first, u7); + double matchFitness2 = computeMatchFitness(i2->first, u2, i7->first, u7); + double matchFitness3 = computeMatchFitness(i3->first, u3, i7->first, u7); + double matchFitness4 = computeMatchFitness(i4->first, u4, i7->first, u7); + double matchFitness5 = computeMatchFitness(i5->first, u5, i7->first, u7); + double matchFitness6 = computeMatchFitness(i6->first, u6, i7->first, u7); + MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); + tmp = MapSetUnion(tmp, u3->maps); + tmp = MapSetUnion(tmp, u4->maps); + tmp = MapSetUnion(tmp, u5->maps); + tmp = MapSetUnion(tmp, u6->maps); + tmp = MapSetUnion(tmp, u7->maps); + if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold && matchFitness6 > fitnessThreshold) + { + i8 = i7; + for (++i8; i8 != userMap.end(); ++i8) + { + u8 = i8->second; + if (u8->status != STATUS_WORKING) + continue; + + double matchFitness1 = computeMatchFitness(i1->first, u1, i8->first, u8); + double matchFitness2 = computeMatchFitness(i2->first, u2, i8->first, u8); + double matchFitness3 = computeMatchFitness(i3->first, u3, i8->first, u8); + double matchFitness4 = computeMatchFitness(i4->first, u4, i8->first, u8); + double matchFitness5 = computeMatchFitness(i5->first, u5, i8->first, u8); + double matchFitness6 = computeMatchFitness(i6->first, u6, i8->first, u8); + double matchFitness7 = computeMatchFitness(i7->first, u7, i8->first, u8); + MapBitSet tmp = MapSetUnion(u1->maps, u2->maps); + tmp = MapSetUnion(tmp, u3->maps); + tmp = MapSetUnion(tmp, u4->maps); + tmp = MapSetUnion(tmp, u5->maps); + tmp = MapSetUnion(tmp, u6->maps); + tmp = MapSetUnion(tmp, u7->maps); + tmp = MapSetUnion(tmp, u8->maps); + if (MapSetCount(tmp) && matchFitness1 > fitnessThreshold && matchFitness2 > fitnessThreshold && matchFitness3 > fitnessThreshold && matchFitness4 > fitnessThreshold && matchFitness5 > fitnessThreshold && matchFitness6 > fitnessThreshold && matchFitness7 > fitnessThreshold) + { + // match 8 players + sendMatchInfo(i1->first, i2->first, i3->first, i4->first, i5->first, i6->first, i7->first, i8->first, + u1, u2, u3, u4, u5, u6, u7, u8, 8, ladderID); + break; + } + } + } + } + + } + } + } + } + } + + } + } + } + } + } + } + } + } // for i2 + + if (bestUser && numPlayers == 2) + { + // we had a match. send the info. + DBGMSG("Matching " << i1->first << " with " << bestName << ":\n" + "\tmatch fitness: " << bestMatchFitness << "\n" + "\tpoint percentage: " << (1-bestUser->points/(double)u1->points)*100 << "\n" + "\tpoints: " << u1->points << ", " << u2->points << "\n" + "\tping in ms: " << sqrt(1000000 * calcPingDelta(u1, bestUser) / (255*255*2)) << "\n" + "\tprevious attempts: " << u1->widened << ", " << bestUser->widened); + sendMatchInfo(i1->first, bestName, "", "", "", "", "", "", + u1, bestUser, NULL, NULL, NULL, NULL, NULL, NULL, 2, ladderID); + break; + } + } // for i1 + + dumpUsers(); +} + +// return false for possible hack attempt +bool GeneralsMatcher::handleUserWiden(const char *nick) +{ + GeneralsUser *userInfo = findUserInAnyLadder(nick); + if (!userInfo) + { + userInfo = findNonLadderUser(nick); + } + + if (!userInfo) + { + DBGMSG("Got Widen from nick not needing one!"); + peerMessagePlayer(m_peer, nick, "MBOT:CANTSENDWIDENNOW", NormalMessage); + return false; + } + DBGMSG("Widening search for " << nick); + peerMessagePlayer(m_peer, nick, "MBOT:WIDENINGSEARCH", NormalMessage); + + userInfo->widened = true; + return true; +} + +bool GeneralsMatcher::handleUserInfo(const char *nick, const std::string& msg) +{ + DBGMSG("Got user info [" << msg << "] from " << nick); + GeneralsUser *userInfo = removeNonMatchingUser(nick); + if (!userInfo) + { + DBGMSG("Got UserInfo from nick not needing one!"); + peerMessagePlayer(m_peer, nick, "MBOT:CANTSENDCINFONOW", NormalMessage); + return false; + } + DBGMSG("Looking at " << nick << " with user info [" << msg << "]"); + + int ladderID = 0; + unsigned int ladderPassCRC = 0; + + int offset = 0; + while (1) + { + int firstMarker = msg.find_first_of('\\', offset); + if (firstMarker < 0) + break; + int secondMarker = msg.find_first_of('\\', firstMarker + 1); + if (secondMarker < 0) + break; + int thirdMarker = msg.find_first_of('\\', secondMarker + 1); + if (thirdMarker < 0) + break; + std::string k = msg.substr(firstMarker + 1, secondMarker - firstMarker - 1); + std::string v = msg.substr(secondMarker + 1, thirdMarker - secondMarker - 1); + offset = thirdMarker - 1; + + if (k == "Widen") + { + int val = atoi(v.c_str()); + if (val > 0) + userInfo->timeToWiden = time(NULL) + val; + else + userInfo->timeToWiden = 0; + } + else if (k == "LadID") + { + ladderID = atoi(v.c_str()); + } + else if (k == "LadPass") + { + ladderPassCRC = atoi(v.c_str()); + } + else if (k == "PointsMin") + { + userInfo->minPoints = atoi(v.c_str()); + } + else if (k == "PointsMax") + { + userInfo->maxPoints = atoi(v.c_str()); + } + else if (k == "PingMax") + { + userInfo->maxPing = atoi(v.c_str()); + } + else if (k == "DisconMax") + { + userInfo->maxDiscons = atoi(v.c_str()); + } + else if (k == "Maps") + { +#ifdef DEBUG + //int curMaps = userInfo->maps.size(); +#endif + + //DBGMSG("map cur size is " << curMaps); + userInfo->maps.clear(); + if (!v.length()) + { + INFMSG("Bad maps from " << nick << ": [" << v << "]"); + peerMessagePlayer(m_peer, nick, "MBOT:BADMAPS", NormalMessage); + return false; + } + const char *buf = v.c_str(); + int pos = 0; + while (*buf) + { + bool hasMap = (*buf != '0'); + //DBGMSG("Setting map " << pos << " to " << hasMap); + userInfo->maps.push_back( hasMap ); + ++pos; + ++buf; + } + } + else if (k == "NumPlayers") + { + userInfo->numPlayers = atoi(v.c_str()); + if (userInfo->numPlayers != 2 && userInfo->numPlayers != 4 && + userInfo->numPlayers != 6 && userInfo->numPlayers != 8) + { + INFMSG("Bad numPlayers from " << nick << ": [" << userInfo->numPlayers << "]"); + peerMessagePlayer(m_peer, nick, "MBOT:BADCINFO", NormalMessage); + return false; + } + } + else if (k == "IP") + { + userInfo->IP = atoi(v.c_str()); + } + else if (k == "NAT") + { + userInfo->NAT = atoi(v.c_str()); + } + else if (k == "Side") + { + userInfo->country = atoi(v.c_str()); + } + else if (k == "Color") + { + userInfo->color = atoi(v.c_str()); + } + else if (k == "Pings") + { + if (!v.length() || (v.length() % 2)) + { + INFMSG("Bad pings from " << nick << ": [" << v << "]"); + peerMessagePlayer(m_peer, nick, "MBOT:BADPINGS", NormalMessage); + return false; + } + int ping = 0; + const char *buf = v.c_str(); + char buf2[3]; + buf2[2] = '\0'; + // We've already assured that pingStr has non-zero even length. + while (*buf) + { + buf2[0] = *buf++; + buf2[1] = *buf++; + ping = (int)strtol(buf2, NULL, 16); + userInfo->pseudoPing.push_back(ping); + } + } + else if (k == "Points") + { + userInfo->points = max(1, atoi(v.c_str())); + } + else if (k == "Discons") + { + userInfo->discons = atoi(v.c_str()); + } + else + { + INFMSG("Unknown key/value pair in user info [" << k << "]/[" << v << "]"); + peerMessagePlayer(m_peer, nick, "MBOT:BADCINFO", NormalMessage); + return false; + } + } + + std::string s = "MBOT:WORKING "; + + if (ladderID) + { + addUserInLadder(nick, ladderID, userInfo); + s.append(intToString(m_ladders[ladderID].size())); + } + else + { + addNonLadderUser(nick, userInfo); + switch (userInfo->numPlayers) + { + case 2: + s.append(intToString(m_nonLadderUsers1v1.size())); + break; + case 4: + s.append(intToString(m_nonLadderUsers2v2.size())); + break; + case 6: + s.append(intToString(m_nonLadderUsers3v3.size())); + break; + case 8: + s.append(intToString(m_nonLadderUsers4v4.size())); + break; + } + } + + userInfo->status = STATUS_WORKING; + userInfo->matchStart = time(NULL); + peerMessagePlayer(m_peer, nick, s.c_str(), NormalMessage); + + DBGMSG("Player " << nick << " is matching now, ack was [" << s << "]"); + return true; +} + +GeneralsUser* GeneralsMatcher::findUser(const std::string& who) +{ + GeneralsUser *user; + user = findNonLadderUser(who); + if (user) + return user; + user = findNonMatchingUser(who); + if (user) + return user; + user = findUserInAnyLadder(who); + if (user) + return user; + + return NULL; +} + +GeneralsUser* GeneralsMatcher::findUserInAnyLadder(const std::string& who) +{ + for (LadderMap::iterator lIt = m_ladders.begin(); lIt != m_ladders.end(); ++lIt) + { + UserMap::iterator uIt = lIt->second.find(who); + if (uIt != lIt->second.end()) + return uIt->second; + } + return NULL; +} + +GeneralsUser* GeneralsMatcher::findUserInLadder(const std::string& who, int ladderID) +{ + LadderMap::iterator lIt = m_ladders.find(ladderID); + if (lIt == m_ladders.end()) + return NULL; + + UserMap::iterator uIt = lIt->second.find(who); + if (uIt == lIt->second.end()) + return NULL; + + return uIt->second; +} + +GeneralsUser* GeneralsMatcher::findNonLadderUser(const std::string& who) +{ + UserMap::iterator it = m_nonLadderUsers1v1.find(who); + if (it != m_nonLadderUsers1v1.end()) + return it->second; + + it = m_nonLadderUsers2v2.find(who); + if (it != m_nonLadderUsers2v2.end()) + return it->second; + + it = m_nonLadderUsers3v3.find(who); + if (it != m_nonLadderUsers3v3.end()) + return it->second; + + it = m_nonLadderUsers4v4.find(who); + if (it != m_nonLadderUsers4v4.end()) + return it->second; + + return NULL; +} + +GeneralsUser* GeneralsMatcher::findNonMatchingUser(const std::string& who) +{ + UserMap::iterator it = m_nonMatchingUsers.find(who); + if (it == m_nonMatchingUsers.end()) + return NULL; + + return it->second; +} + +void GeneralsMatcher::addUser(const std::string& who) +{ + if (findUser(who)) + { + ERRMSG("Re-adding " << who); + return; + } + + addNonMatchingUser(who, new GeneralsUser); +} + +void GeneralsMatcher::addUserInLadder(const std::string& who, int ladderID, GeneralsUser *user) +{ + m_ladders[ladderID][who] = user; +} + +void GeneralsMatcher::addNonLadderUser(const std::string& who, GeneralsUser *user) +{ + switch (user->numPlayers) + { + case 2: + m_nonLadderUsers1v1[who] = user; + break; + case 4: + m_nonLadderUsers2v2[who] = user; + break; + case 6: + m_nonLadderUsers3v3[who] = user; + break; + case 8: + m_nonLadderUsers4v4[who] = user; + break; + } +} + +void GeneralsMatcher::addNonMatchingUser(const std::string& who, GeneralsUser *user) +{ + m_nonMatchingUsers[who] = user; +} + + +bool GeneralsMatcher::removeUser(const std::string& who) +{ + GeneralsUser *user; + user = removeUserInAnyLadder(who); + if (user) + { + delete user; + return true; + } + user = removeNonLadderUser(who); + if (user) + { + delete user; + return true; + } + user = removeNonMatchingUser(who); + if (user) + { + delete user; + return true; + } + + return false; +} + +GeneralsUser* GeneralsMatcher::removeUserInLadder(const std::string& who, int ladderID) +{ + LadderMap::iterator lIt = m_ladders.find(ladderID); + if (lIt == m_ladders.end()) + return NULL; + + UserMap::iterator uIt = lIt->second.find(who); + if (uIt == lIt->second.end()) + return NULL; + + GeneralsUser *user = uIt->second; + lIt->second.erase(uIt); + return user; +} + +GeneralsUser* GeneralsMatcher::removeUserInAnyLadder(const std::string& who) +{ + for (LadderMap::iterator lIt = m_ladders.begin(); lIt != m_ladders.end(); ++lIt) + { + UserMap::iterator uIt = lIt->second.find(who); + if (uIt != lIt->second.end()) + { + GeneralsUser *user = uIt->second; + lIt->second.erase(uIt); + return user; + } + } + return NULL; +} + +GeneralsUser* GeneralsMatcher::removeNonLadderUser(const std::string& who) +{ + UserMap::iterator it = m_nonLadderUsers1v1.find(who); + if (it != m_nonLadderUsers1v1.end()) + { + GeneralsUser *user = it->second; + m_nonLadderUsers1v1.erase(it); + return user; + } + + it = m_nonLadderUsers2v2.find(who); + if (it != m_nonLadderUsers2v2.end()) + { + GeneralsUser *user = it->second; + m_nonLadderUsers2v2.erase(it); + return user; + } + + it = m_nonLadderUsers3v3.find(who); + if (it != m_nonLadderUsers3v3.end()) + { + GeneralsUser *user = it->second; + m_nonLadderUsers3v3.erase(it); + return user; + } + + it = m_nonLadderUsers4v4.find(who); + if (it != m_nonLadderUsers4v4.end()) + { + GeneralsUser *user = it->second; + m_nonLadderUsers4v4.erase(it); + return user; + } + + return NULL; +} + +GeneralsUser* GeneralsMatcher::removeNonMatchingUser(const std::string& who) +{ + UserMap::iterator it = m_nonMatchingUsers.find(who); + if (it == m_nonMatchingUsers.end()) + return NULL; + + GeneralsUser *user = it->second; + m_nonMatchingUsers.erase(it); + return user; +} + + +void GeneralsMatcher::handleDisconnect( const char *reason ) +{ + ERRMSG("Disconnected"); + done = true; + exit(0); +} + +void GeneralsMatcher::handleRoomMessage( const char *nick, const char *message, MessageType messageType ) +{ + if (messageType == ActionMessage) + { + PARANOIDMSG(nick << " " << message); + } + else + { + PARANOIDMSG("[" << nick << "] " << message); + } +} + +void GeneralsMatcher::handlePlayerMessage( const char *nick, const char *message, MessageType messageType ) +{ + if (messageType == ActionMessage) + { + DBGMSG(nick << " " << message); + } + else + { + DBGMSG("[" << nick << "] " << message); + } + + if (messageType != NormalMessage) + return; + + std::string line = message; + line.append("\\"); + + int offset = 0; + int firstMarker = line.find_first_of('\\', offset); + int secondMarker = line.find_first_of('\\', firstMarker + 1); + if (firstMarker >= 0 && secondMarker >= 0) + { + std::string marker = line.substr(firstMarker + 1, secondMarker - firstMarker - 1); + if (marker == "CINFO") + { + handleUserInfo(nick, line.substr(secondMarker));//, std::string::npos)); + } + else if (marker == "WIDEN") + { + handleUserWiden(nick); + } + else + { + INFMSG("Unknown marker [" << marker << "] in line [" << line << "] from " << nick); + } + } + else + { + INFMSG("Failed to parse line [" << line << "] from " << nick); + } +} + +void GeneralsMatcher::handlePlayerJoined( const char *nick ) +{ + DBGMSG("Player " << nick << " joined"); + addUser(nick); +} + +void GeneralsMatcher::handlePlayerLeft( const char *nick ) +{ + DBGMSG("Player " << nick << " left"); + if (m_nick != nick) + removeUser(nick); +} + +void GeneralsMatcher::handlePlayerChangedNick( const char *oldNick, const char *newNick ) +{ + DBGMSG("Player " << oldNick << " changed nick to " << newNick << " - resetting to non-matching state"); + removeUser(oldNick); + addUser(newNick); +} + +void GeneralsMatcher::handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags ) +{ + if (!nick) + nick = ""; + DBGMSG("PlayerEnum: success=" << success << " index=" << gameSpyIndex << ", nick=" << nick << ", flags=" << flags); + + if (success && gameSpyIndex >= 0 && m_nick != nick) + { + addUser(nick); + } +} + + +// ===================================================================== +// TEST Client Matcher class +// ===================================================================== + +GeneralsClientMatcher::GeneralsClientMatcher() +{ + // Read some values from the config file + int quietTMP = 0; + Global.config.getInt("NOECHO", quietTMP); + if (quietTMP) + quiet = true; + else + quiet = false; +} + +void GeneralsClientMatcher::init(void) +{ + m_baseNick.setFormatted("qmBot%d", time(NULL)); + m_profileID = 0; +} + +void GeneralsClientMatcher::checkMatches(void) +{} + +void GeneralsClientMatcher::handleDisconnect( const char *reason ) +{} +void GeneralsClientMatcher::handleRoomMessage( const char *nick, const char *message, MessageType messageType ) +{} +void GeneralsClientMatcher::handlePlayerMessage( const char *nick, const char *message, MessageType messageType ) +{} +void GeneralsClientMatcher::handlePlayerJoined( const char *nick ) +{} +void GeneralsClientMatcher::handlePlayerLeft( const char *nick ) +{} +void GeneralsClientMatcher::handlePlayerChangedNick( const char *oldNick, const char *newNick ) +{} +void GeneralsClientMatcher::handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags ) +{} + + +// ===================================================================== +// End of File +// ===================================================================== + + diff --git a/Generals/Code/Tools/matchbot/generals.h b/Generals/Code/Tools/matchbot/generals.h new file mode 100644 index 00000000000..e0761e110e2 --- /dev/null +++ b/Generals/Code/Tools/matchbot/generals.h @@ -0,0 +1,194 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef __GENERALS_H__ +#define __GENERALS_H__ + +#ifdef _WIN32 +#include +#endif +//#include +//#include +//#include +#include "matcher.h" +#include "global.h" + +#include +#include +#include +#include +#include + +typedef std::vector MapBitSet; + +// ===================================================================== +// Users +// ===================================================================== + +// Here are the states a matcher can be in: +typedef enum +{ + STATUS_INVAL = 0, + STATUS_INCHANNEL, // Just entered the channel + STATUS_WORKING, // Sent info, needs to be matched + STATUS_MATCHED, // Been matched, but is still in the channel +} UserStatus; + +class GeneralsUser +{ +public: + GeneralsUser(void); + UserStatus status; + + int points, minPoints, maxPoints; + + int discons, maxDiscons; + + int country; + int color; + + bool widened; + time_t timeToWiden; + time_t matchStart; // when did we request a match? + + // This is a ping to a designated 3rd-party server who just + // responds to pings. The idea is that if the game server is + // behind a firewall, the client will have a 1000ms ping to it, + // even though he might be very close. To combat this, we have + // clients & servers ping some 3rd-party servers & calculate a + // pesudo-ping based on the sum of pings server-->3rd-->client. + std::vector pseudoPing; + int maxPing; + + unsigned int IP; + int NAT; + + MapBitSet maps; + + int numPlayers; +}; + +// ===================================================================== +// Matcher class +// ===================================================================== + +typedef std::map UserMap; +typedef std::map LadderMap; + +class GeneralsMatcher : public MatcherClass +{ +public: + GeneralsMatcher(); + virtual ~GeneralsMatcher() + {} + + virtual void init(void); + virtual void checkMatches(void); + + virtual void handleDisconnect( const char *reason ); + virtual void handleRoomMessage( const char *nick, const char *message, MessageType messageType ); + virtual void handlePlayerMessage( const char *nick, const char *message, MessageType messageType ); + virtual void handlePlayerJoined( const char *nick ); + virtual void handlePlayerLeft( const char *nick ); + virtual void handlePlayerChangedNick( const char *oldNick, const char *newNick ); + virtual void handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags ); + +private: + LadderMap m_ladders; + UserMap m_nonLadderUsers1v1; + UserMap m_nonLadderUsers2v2; + UserMap m_nonLadderUsers3v3; + UserMap m_nonLadderUsers4v4; + UserMap m_nonMatchingUsers; + + double computeMatchFitness(const std::string& i1, const GeneralsUser *u1, const std::string& i2, const GeneralsUser *u2); + + GeneralsUser* findUser(const std::string& who); + GeneralsUser* findUserInLadder(const std::string& who, int ladderID); + GeneralsUser* findUserInAnyLadder(const std::string& who); + GeneralsUser* findNonLadderUser(const std::string& who); + GeneralsUser* findNonMatchingUser(const std::string& who); + + void addUser(const std::string& who); + void addUserInLadder(const std::string& who, int ladderID, GeneralsUser *user); + void addUserInAnyLadder(const std::string& who, GeneralsUser *user); + void addNonLadderUser(const std::string& who, GeneralsUser *user); + void addNonMatchingUser(const std::string& who, GeneralsUser *user); + + bool removeUser(const std::string& who); + GeneralsUser* removeUserInLadder(const std::string& who, int ladderID); + GeneralsUser* removeUserInAnyLadder(const std::string& who); + GeneralsUser* removeNonLadderUser(const std::string& who); + GeneralsUser* removeNonMatchingUser(const std::string& who); + + void checkMatchesInUserMap(UserMap& userMap, int ladderID, int numPlayers, bool showPoolSize); + + void dumpUsers(void); + + void sendMatchInfo(std::string name1, std::string name2, std::string name3, std::string name4, + std::string name5, std::string name6, std::string name7, std::string name8, + GeneralsUser *user1, GeneralsUser *user2, GeneralsUser *user3, GeneralsUser *user4, + GeneralsUser *user5, GeneralsUser *user6, GeneralsUser *user7, GeneralsUser *user8, + int numPlayers, int ladderID); + + // Command handlers for above privmsg commands (offset is the + // offset for the getToken() past the command token) + bool handleUserInfo(const char *nick, const std::string& msg); + bool handleUserWiden(const char *nick); + + // Weights for various matching parameters + int weightLowPing; + int weightAvgPoints; + int totalWeight; + + time_t m_nextPoolSizeAnnouncement; + int m_secondsBetweenPoolSizeAnnouncements; + + //typedef std::vector StringVec; + //StringVec mapFileList; +} +; + +// ===================================================================== +// TEST Client Matcher class +// ===================================================================== + +class GeneralsClientMatcher : public MatcherClass +{ +public: + GeneralsClientMatcher(); + virtual ~GeneralsClientMatcher() + {} + + virtual void init(void); + virtual void checkMatches(void); + + virtual void handleDisconnect( const char *reason ); + virtual void handleRoomMessage( const char *nick, const char *message, MessageType messageType ); + virtual void handlePlayerMessage( const char *nick, const char *message, MessageType messageType ); + virtual void handlePlayerJoined( const char *nick ); + virtual void handlePlayerLeft( const char *nick ); + virtual void handlePlayerChangedNick( const char *oldNick, const char *newNick ); + virtual void handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags ); + +private: +} +; + +#endif /* __GENERALS_H__ */ + diff --git a/Generals/Code/Tools/matchbot/global.cpp b/Generals/Code/Tools/matchbot/global.cpp new file mode 100644 index 00000000000..232cee6310d --- /dev/null +++ b/Generals/Code/Tools/matchbot/global.cpp @@ -0,0 +1,51 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include // for FILE ops +#include "global.h" + +GlobalClass Global; + +GlobalClass::GlobalClass(void) +{} + +bool GlobalClass::ReadFile(const char *fname) +{ + FILE *fp; + if ((fp = fopen(fname, "r")) == NULL) + return false; + config.readFile(fp); + fclose(fp); + + return true; +} + +bool GlobalClass::GetString(const Wstring& key, Wstring& val) +{ + val = ""; + config.getString(key, val, "STRINGS"); + if (val == "") + { + val.setFormatted("MISSING:%s", key.get()); + return false; + } + + return true; + +} + diff --git a/Generals/Code/Tools/matchbot/global.h b/Generals/Code/Tools/matchbot/global.h new file mode 100644 index 00000000000..5dc7856dca3 --- /dev/null +++ b/Generals/Code/Tools/matchbot/global.h @@ -0,0 +1,57 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef __GLOBAL_H__ +#define __GLOBAL_H__ + +#ifdef _WIN32 +#include +#ifdef IN +#undef IN +#endif +#define IN const +#endif +#include +#include +#include +#include +#include +#include "matcher.h" +#include "rand.h" + +class GlobalClass +{ +public: + GlobalClass(); + + ConfigFile config; + bool ReadFile(const char *fname); + + bool GetString(const Wstring& key, Wstring& val); + + RandClass rnd; +}; + +extern GlobalClass Global; + +// Log rotation functions +void rotateOutput(void); +void rotateParanoid(void); + +#endif + diff --git a/Generals/Code/Tools/matchbot/main.cpp b/Generals/Code/Tools/matchbot/main.cpp new file mode 100644 index 00000000000..0290ac55be2 --- /dev/null +++ b/Generals/Code/Tools/matchbot/main.cpp @@ -0,0 +1,409 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifdef _WIN32 +#include +#endif +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +//#define THREADSAFE_HEADER + +#include +#include +#include +#include +#include +#include +#include "global.h" +#include "generals.h" +#include "timezone.h" +#include + +#include +#include "mydebug.h" + +#ifdef _UNIX +using namespace std; +#else +#define sleep(x) Sleep(1000 * (x)) +#endif + +static const char *Program_Usage = "A config filename can be given on the command line (default=matchbot.cfg)\n"; +void logMonitor(void *); +void paranoidLogMonitor(void *); + +OutputDevice * output_device = NULL; +OutputDevice * paranoid_output_device = NULL; + + +void Signal_Quit(int) +{ + INFMSG("Exiting due to signal."); + exit(2); +} + +void Setup_Signals(void) +{ +#ifdef _UNIX + struct sigaction act, oact; + act.sa_handler = Signal_Quit; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGTERM, &act, &oact); + sigaction(SIGINT, &act, &oact); +#endif +} + +int VerifyFileDescriptors(int requested) +{ +#ifdef _UNIX + struct rlimit limit; + if (!getrlimit(_SC_OPEN_MAX, &limit)) + { + INFMSG("Hard limit on file descriptors: " << limit.rlim_max); + if (limit.rlim_max < (unsigned int)requested) + { + ERRMSG("Too many file descriptors requested"); + ERRMSG("Hard Limit: " << limit.rlim_max << ", requested: " << requested); + requested = limit.rlim_max; + } + + limit.rlim_cur = limit.rlim_max; /* make soft limit the max */ + if (setrlimit(_SC_OPEN_MAX, &limit) == -1) + { + ERRMSG("Error setting max file descriptors to " << limit.rlim_cur); + exit(-1); + } + INFMSG("Soft limit on file descriptors: " << limit.rlim_cur); + } + else + { + ERRMSG("Couldn't get limit for _SC_OPEN_MAX"); + exit(-1); + } +#endif + return requested; +} + + + +GeneralsMatcher *s_generalsMatcher = NULL; +GeneralsClientMatcher *s_generalsClientMatcher = NULL; + +int main(int argc, char ** argv) +{ + Wstring config_fname = "matchbot.cfg"; + + // You can specify the config file on the command line + if (argc == 2) + config_fname = argv[1]; + + // Read the config file + FILE *fp; + if ((fp = fopen(config_fname.get(), "r")) == NULL) + { + cerr << "\nCan't open the config file '" << config_fname.get() << "'\n\n"; + cerr << Program_Usage << endl; + exit( -1); + } + fclose(fp); + + Global.ReadFile(config_fname.get()); + + // Setup debugging & logging output + Wstring output_file; + output_file.set("matchbot.log"); + Global.config.getString("OUTPUTFILE", output_file); + if (output_file != "STDOUT") + { + int append = 1; + Global.config.getInt("APPENDTOLOG", append); + if (append) + output_device = new FileD(output_file.get(), "a"); + else + output_device = new FileD(output_file.get(), "w"); + if (!output_device) + { + cerr << "Could not open " << output_file.get() << " for writing!" << endl; + exit( -1); + } + } + else + { + output_device = new StdoutD; + } + MsgManager::setAllStreams(output_device); + DBGMSG("Matching bot started!"); + INFMSG("Matching bot " << argv[0] << " started!"); + + // Setup logging of suspicious activity + Wstring paranoid_output_file; + paranoid_output_file.set("hacks.log"); + Global.config.getString("PARANOIDFILE", paranoid_output_file); + if (paranoid_output_file != "STDOUT") + { + paranoid_output_device = new FileD(paranoid_output_file.get(), "a"); + if (!paranoid_output_device) + { + cerr << "Could not open " << paranoid_output_file.get() << " for writing!" << endl; + exit( -1); + } + } + else + { + paranoid_output_device = new StdoutD; + } + MyMsgManager::setParanoidStream(paranoid_output_device); + DBGMSG("Hack log started!"); + PARANOIDMSG("Hack log started!"); + + Setup_Signals(); + +#ifdef _WINDOWS + // ----- Initialize Winsock ----- + WORD verReq = MAKEWORD(2, 2); + WSADATA wsadata; + + int err = WSAStartup(verReq, &wsadata); + if (err != 0) + { + ERRMSG("Winsock Init failed."); + return 1; + } + + if ((LOBYTE(wsadata.wVersion) != 2) || (HIBYTE(wsadata.wVersion) !=2)) + { + ERRMSG("Winsock DLL is not 2.2"); + WSACleanup(); + ERRMSG("Winsock Init failed."); + return 1; + } + INFMSG("Winsock Init done."); +#endif + + // Check game type & start matcher + Wstring gametype = "unknown"; + Global.config.getString("GAME", gametype); + + // Command-line override for gamemode. + // This is for test suites, so they can use + // the same config file as the corresponding + // matchbot. + const char *s = argv[0] + strlen(argv[0]); + while (s > argv[0] && *s != '/') + --s; + if (*s == '/') + ++s; + Wstring exe = s; + exe.toLower(); + DBGMSG("Executable file is [" << exe.get() << "]"); + + if (gametype == "Generals") + { + DBGMSG("Generals matching behavior"); + s_generalsMatcher = new GeneralsMatcher; + s_generalsMatcher->connectAndLoop(); + } + else if (gametype == "GeneralsClient") + { + DBGMSG("Generals TEST client matching behavior"); + s_generalsClientMatcher = new GeneralsClientMatcher; + s_generalsClientMatcher->connectAndLoop(); + } + else + { + cerr << "\nNo valid GAME entry found!" << endl; + exit( -1); + } + + if (s_generalsMatcher) + delete s_generalsMatcher; + if (s_generalsClientMatcher) + delete s_generalsClientMatcher; + + return 0; +} + +/*----------------------------------------------------------------------+ +| THREAD: logMonitor | +| This thread is spawned once per execution. It will activate after | +| midnight and create a new log file. The old one gets put into the | +| logfiles directory. | +`----------------------------------------------------------------------*/ +void logMonitor(void *) +{ +#ifdef _UNIX + Xtime xtime; + time_t curtime; + //char timebuf[40]; + char filenamebuf[128]; + int delay = -1; + Global.config.getInt("ROTATEDELAY", delay); + DBGMSG("ROTATEDELAY: " << delay); + if (delay == -1) + return ; + while (1) + { + curtime = time(NULL); + // get the number of seconds that have passed since midnight + // of the current day. + curtime -= TimezoneOffset(); + time_t timeofday = curtime % (delay); + if ((timeofday > 0) && (timeofday <= 300)) + { + // We're within 5 minutes of midnight, switch the files. + + DBGMSG("about to switch."); + Wstring logfilename = "matchbot.log"; + Global.config.getString("OUTPUTFILE", logfilename); + Wstring newfilename = "tmp.log"; + Global.config.getString("ROTATEFILE", newfilename); + MsgManager::ReplaceAllStreams((FileD*)output_device, logfilename.get(), newfilename.get()); + Wstring logpath = "logs"; + Global.config.getString("LOGPATH", logpath); + xtime.update(); + sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(), xtime.getMonth(), + xtime.getMDay(), xtime.getYear(), xtime.getHour(), xtime.getMinute(), xtime.getSecond()); + rename(newfilename.get(), filenamebuf); + DBGMSG("Normal: Just been switched. " << logfilename.get() << ", " << newfilename.get()); + sleep(60*60*23); // sleep the next 23 hours + } + sleep(300); + } +#endif +} + +void rotateOutput(void) +{ + Xtime xtime; + char filenamebuf[128]; + Wstring logfilename = "matchbot.log"; + Wstring newfilename = "tmp.log"; + Wstring logpath = "logs"; + + DBGMSG("About to switch."); + + Global.config.getString("OUTPUTFILE", logfilename); + Global.config.getString("ROTATEFILE", newfilename); + Global.config.getString("LOGPATH", logpath); + + // This grabs the semaphore, renames the file, and switches the output device + MsgManager::ReplaceAllStreams((FileD*)output_device, logfilename.get(), + newfilename.get()); + + // clean up the tmp filename and move it to the log dir. + sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(), + xtime.getMonth(), xtime.getMDay(), xtime.getYear(), xtime.getHour(), + xtime.getMinute(), xtime.getSecond()); +#ifdef _WINDOWS + mkdir(logpath.get()); +#else + mkdir(logpath.get(), 00666); +#endif + rename(newfilename.get(), filenamebuf); + + DBGMSG("Normal: Just been switched. " << logfilename.get() << ", " << + newfilename.get()); +} + +void paranoidLogMonitor(void *) +{ +#ifdef _UNIX + Xtime xtime; + time_t curtime; + //char timebuf[40]; + char filenamebuf[128]; + int delay = -1; + Global.config.getInt("ROTATEDELAY", delay); + PARANOIDMSG("ROTATEDELAY: " << delay); + if (delay == -1) + return ; + while (1) + { + curtime = time(NULL); + // get the number of seconds that have passed since midnight + // of the current day. + curtime -= TimezoneOffset(); + time_t timeofday = curtime % (delay); + if ((timeofday > 0) && (timeofday <= 300)) + { + // We're within 5 minutes of midnight, switch the files. + + PARANOIDMSG("about to switch."); + Wstring logfilename = "matchbot.log"; + Global.config.getString("PARANOIDFILE", logfilename); + Wstring newfilename = "tmp.log"; + Global.config.getString("ROTATEPARANOIDFILE", newfilename); + MyMsgManager::ReplaceAllStreams((FileD*)paranoid_output_device, logfilename.get(), newfilename.get()); + Wstring logpath = "logs"; + Global.config.getString("PARANOIDLOGPATH", logpath); + xtime.update(); + sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(), xtime.getMonth(), + xtime.getMDay(), xtime.getYear(), xtime.getHour(), xtime.getMinute(), xtime.getSecond()); + rename(newfilename.get(), filenamebuf); + PARANOIDMSG("Paranoid: Just been switched. " << logfilename.get() << ", " << newfilename.get()); + sleep(60*60*23); // sleep the next 23 hours + } + sleep(300); + } +#endif +} + +void rotateParanoid(void) +{ + Xtime xtime; + char filenamebuf[128]; + Wstring logfilename = "matchbot.log"; + Wstring newfilename = "tmp.log"; + Wstring logpath = "logs"; + + PARANOIDMSG("About to switch."); + + Global.config.getString("PARANOIDFILE", logfilename); + Global.config.getString("ROTATEPARANOIDFILE", newfilename); + Global.config.getString("PARANOIDLOGPATH", logpath); + + // This grabs the semaphore, renames the file, and switches the output device + MyMsgManager::ReplaceAllStreams((FileD*)output_device, logfilename.get(), + newfilename.get()); + + // clean up the tmp filename and move it to the log dir. + sprintf(filenamebuf, "%s/%02d%02d%04d_%02d%02d%02d_log", logpath.get(), + xtime.getMonth(), xtime.getMDay(), xtime.getYear(), xtime.getHour(), + xtime.getMinute(), xtime.getSecond()); +#ifdef _WINDOWS + mkdir(logpath.get()); +#else + mkdir(logpath.get(), 00666); +#endif + rename(newfilename.get(), filenamebuf); + + PARANOIDMSG("Paranoid: Just been switched. " << logfilename.get() << ", " << + newfilename.get()); +} + + diff --git a/Generals/Code/Tools/matchbot/matcher.cpp b/Generals/Code/Tools/matchbot/matcher.cpp new file mode 100644 index 00000000000..2b27968ad96 --- /dev/null +++ b/Generals/Code/Tools/matchbot/matcher.cpp @@ -0,0 +1,421 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "global.h" +#include "matcher.h" +#include "encrypt.h" +#include "timezone.h" +#include "debug.h" + +#ifdef _WINDOWS +#define usleep(x) Sleep((x)/100000) +#endif + +MatcherClass::MatcherClass(void) +{ + m_lastRotation = 0; + m_baseNick = "matcher"; + m_joinSuccess = false; + done = 0; + + m_rotateLogs = false; + quiet = false; +} + +Wstring MatcherClass::getString(const Wstring& key) +{ + Wstring res; + Global.GetString(key, res); + return res; +} + +void logIt(const char *Txt) +{ + // intentionally crash if we can't open it + FILE *fp = fopen("logIt.txt", "w"); + fputs(Txt, fp); + fclose(fp); +} + +void MatcherClass::readLoop(void) +{ + int delay = -1; + Global.config.getInt("ROTATEDELAY", delay); + DBGMSG("ROTATEDELAY: " << delay); + + do + { + static time_t lastLogTime = 0; + time_t now = time(NULL); + if (now > lastLogTime + 300) + { + lastLogTime = now; + INFMSG("still here" << endl); + } + + logIt("peerThink\n"); + peerThink(m_peer); + + logIt("peerIsConnected\n"); + if (peerIsConnected(m_peer)) + { + logIt("checkMatches()\n"); + checkMatches(); + logIt("checkMatches() done\n"); + } + else + done = true; + + msleep(1); + + // rotate logs if it's time + if (delay != -1) + { +#ifdef _UNIX + Xtime xtime; + time_t curtime; + curtime = time(NULL); + // get the number of seconds that have passed since midnight + // of the current day. + curtime -= TimezoneOffset(); + time_t timeofday = curtime % (delay); + if ((timeofday > 0) && (timeofday <= 300)) + { + rotateOutput(); + rotateParanoid(); + } +#endif + } + } + while (!done); + DBGMSG("Bailing out of readLoop!" << endl); + INFMSG("Bailing out of readLoop!" << endl); + ERRMSG("Bailing out of readLoop!" << endl); +} + +///////////////////////////////////////////////////////////////////////////// + +static void DisconnectedCallback ( PEER peer, const char * reason, void * param) +{ + DBGMSG("Disconnected: " << reason); + MatcherClass *matcher = (MatcherClass *)param; + if (matcher) + matcher->handleDisconnect( reason ); +} + +static void RoomMessageCallback ( PEER peer, RoomType roomType, const char * nick, const char * message, MessageType messageType, void * param) +{ + DBGMSG("(PUBLIC) " << nick << ": " << message); + MatcherClass *matcher = (MatcherClass *)param; + if (matcher) + matcher->handleRoomMessage( nick, message, messageType ); +} + +static void PlayerMessageCallback ( PEER peer, const char * nick, const char * message, MessageType messageType, void * param) +{ + DBGMSG("(PRIVATE) " << nick << ": " << message); + MatcherClass *matcher = (MatcherClass *)param; + if (matcher) + matcher->handlePlayerMessage( nick, message, messageType ); +} + +static void PlayerJoinedCallback ( PEER peer, RoomType roomType, const char * nick, void * param) +{ + DBGMSG(nick << " joined the room"); + MatcherClass *matcher = (MatcherClass *)param; + if (matcher) + matcher->handlePlayerJoined( nick ); +} + +static void PlayerLeftCallback ( PEER peer, RoomType roomType, const char * nick, const char * reason, void * param) +{ + DBGMSG(nick << " left the room"); + MatcherClass *matcher = (MatcherClass *)param; + if (matcher) + matcher->handlePlayerLeft( nick ); +} + +static void PlayerChangedNickCallback ( PEER peer, RoomType roomType, const char * oldNick, const char * newNick, void * param) +{ + INFMSG(oldNick << " changed nicks to " << newNick); + MatcherClass *matcher = (MatcherClass *)param; + if (matcher) + matcher->handlePlayerChangedNick( oldNick, newNick ); +} + +static void EnumPlayersCallback ( PEER peer, PEERBool success, RoomType roomType, int index, const char * nick, int flags, void * param) +{ + MatcherClass *matcher = (MatcherClass *)param; + if (matcher) + matcher->handlePlayerEnum( success == PEERTrue, index, nick, flags); +} + +static int s_groupID = 0; +static void ListGroupRoomsCallback ( PEER peer, PEERBool success, int groupID, SBServer server, const char * name, int numWaiting, int maxWaiting, int numGames, int numPlaying, void * param) +{ + if (success && name && !strcasecmp(name, "QuickMatch")) + { + s_groupID = groupID; + } +} + +static void ConnectCallback ( PEER peer, PEERBool success, int failureReason, void * param ) +{ + MatcherClass *matcher = (MatcherClass *)param; + if (matcher) + matcher->handleConnect( success == PEERTrue ); +} + +static void JoinCallback ( PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void * param) +{ + MatcherClass *matcher = (MatcherClass *)param; + if (matcher) + matcher->handleJoin( success == PEERTrue ); +} + +static void NickErrorCallback ( PEER peer, int type, const char * badNick, int numSuggestedNicks, const const char ** suggestedNicks, void * param) +{ + ERRMSG("Nick error with " << badNick); + + if(type == PEER_IN_USE) + { + int len = strlen(badNick); + std::string nickStr = badNick; + int newVal = 0; + if (badNick[len-1] == '}' && badNick[len-3] == '{' && isdigit(badNick[len-2])) + { + newVal = badNick[len-2] - '0' + 1; + nickStr.erase(len-3, 3); + } + + nickStr.append("{"); + char tmp[2]; + tmp[0] = '0'+newVal; + tmp[1] = '\0'; + nickStr.append(tmp); + nickStr.append("}"); + DBGMSG("Nickname taken: was "<= 1); + } +} + +void MatcherClass::connectAndLoop(void) +{ + // Game-specific initializations, if neccessary + init(); + + // Check for possible quit from init()-based self-tests + if (done) + return ; + + // Defaults. + //////////// + Wstring title = "gmtest"; + Wstring secretKey = "HA6zkS"; + Wstring serialNo = ""; + m_profileID = 0; + Global.config.getString("Nick", m_baseNick, "LOGIN"); + DBGMSG("base nick is " << m_baseNick.get()); + m_baseNick.toLower(); + Global.config.getString("Title", title, "LOGIN"); + Global.config.getString("SecretKey", secretKey, "LOGIN"); + Global.config.getInt("ProfileID", m_profileID, "LOGIN"); + Global.config.getString("CDKey", serialNo, "LOGIN"); + PEERCallbacks callbacks; + PEERBool pingRooms[NumRooms]; + PEERBool crossPingRooms[NumRooms]; + + // Setup the callbacks. + /////////////////////// + memset(&callbacks, 0, sizeof(PEERCallbacks)); + callbacks.disconnected = DisconnectedCallback; + callbacks.playerChangedNick = PlayerChangedNickCallback; + callbacks.playerJoined = PlayerJoinedCallback; + callbacks.playerLeft = PlayerLeftCallback; + callbacks.roomMessage = RoomMessageCallback; + callbacks.playerMessage = PlayerMessageCallback; + callbacks.param = this; + + // Init. + //////// + m_peer = peerInitialize(&callbacks); + if(!m_peer) + { + ERRMSG("Failed to init peer object" << endl); + return; + } + + // Ping/cross-ping in no room. + ///////////////////////////////// + pingRooms[TitleRoom] = PEERFalse; + pingRooms[GroupRoom] = PEERFalse; + pingRooms[StagingRoom] = PEERFalse; + crossPingRooms[TitleRoom] = PEERFalse; + crossPingRooms[GroupRoom] = PEERFalse; + crossPingRooms[StagingRoom] = PEERFalse; + + // Set the title. + ///////////////// + if(!peerSetTitle(m_peer, title.get(), secretKey.get(), title.get(), secretKey.get(), 0, 30, PEERTrue, pingRooms, crossPingRooms)) + { + peerShutdown(m_peer); + ERRMSG("Failed to set the title" << endl); + return; + } + + // Connect. + /////////// + m_connectSuccess = false; + m_nick = m_baseNick.get(); + peerConnect(m_peer, m_baseNick.get(), m_profileID, NickErrorCallback, ConnectCallback, this, PEERTrue); + if(!m_connectSuccess) + { + peerShutdown(m_peer); + ERRMSG("Failed to connect" << endl); + return; + } + + bool cdOk = false; + peerAuthenticateCDKey(m_peer, serialNo.get(), AuthenticateCDKeyCallback, &cdOk, PEERTrue); + if (!cdOk) + { + peerShutdown(m_peer); + ERRMSG("Failed to auth CDKey " << serialNo.get() << endl); + return; + } + + m_groupID = 0; + peerListGroupRooms(m_peer, NULL, ListGroupRoomsCallback, &m_groupID, PEERTrue); + m_groupID = s_groupID; + + DBGMSG("QuickMatch room is " << m_groupID); + + // Join the title room. + /////////////////////// + peerJoinGroupRoom(m_peer, m_groupID, JoinCallback, this, PEERTrue); + if(!m_joinSuccess) + { + peerDisconnect(m_peer); + peerShutdown(m_peer); + ERRMSG("Failed to join the title room" << endl); + return; + } + + // Connected, so lets do our thing + readLoop(); + + peerDisconnect(m_peer); + peerShutdown(m_peer); +} + diff --git a/Generals/Code/Tools/matchbot/matcher.h b/Generals/Code/Tools/matchbot/matcher.h new file mode 100644 index 00000000000..398ca33d6c2 --- /dev/null +++ b/Generals/Code/Tools/matchbot/matcher.h @@ -0,0 +1,93 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef __MATCHER_H__ +#define __MATCHER_H__ + +#ifdef _WIN32 +#include +#endif +#include +#include +#include +#include +#include +#include + +#include + +#include + +class MatcherClass +{ +public: + MatcherClass(); + virtual ~MatcherClass() + {} + + virtual void init(void) + {} + + virtual void checkMatches(void) + {} + + virtual void handleDisconnect( const char *reason ) + {} + virtual void handleRoomMessage( const char *nick, const char *message, MessageType messageType ) + {} + virtual void handlePlayerMessage( const char *nick, const char *message, MessageType messageType ) + {} + virtual void handlePlayerJoined( const char *nick ) + {} + virtual void handlePlayerLeft( const char *nick ) + {} + virtual void handlePlayerChangedNick( const char *oldNick, const char *newNick ) + {} + virtual void handlePlayerEnum( bool success, int gameSpyIndex, const char *nick, int flags ) + {} + + void handleConnect( bool success ); + void handleGroupRoomList( bool success, int groupID, const char *name ); + void handleJoin( bool success ); + void handleNickError( const char *badNick ); + + void connectAndLoop( void ); + +protected: + + Wstring getString(const Wstring& key); + + Wstring m_baseNick; + std::string m_nick; + int m_profileID; + PEER m_peer; + bool m_connectSuccess; + bool m_joinSuccess; + void readLoop( void ); + + int done; // 0=no, neg=quit;error, pos=quit;success + bool quiet; + int m_groupID; + + bool m_rotateLogs; // check for log rotation in this thread? + time_t m_lastRotation; + +}; + +#endif /* __MATCHER_H__ */ + diff --git a/Generals/Code/Tools/matchbot/mydebug.cpp b/Generals/Code/Tools/matchbot/mydebug.cpp new file mode 100644 index 00000000000..4090f27125a --- /dev/null +++ b/Generals/Code/Tools/matchbot/mydebug.cpp @@ -0,0 +1,101 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include +#include "mydebug.h" +#include "streamer.h" +#include "odevice.h" + + +// static MyMsgManager *msg_manager=NULL; + +// static int paranoid_enabled=0; +static ostream *paranoid_ostream=NULL; +static Streamer paranoid_streamer; + +// Don't dare touch this semaphore in application code! +#ifdef USE_SEM +Sem4 MyDebugLibSemaphore; +#else +CritSec MyDebugLibSemaphore; +#endif + + +int MyMsgManager::setAllStreams(OutputDevice *device) +{ + if (device==NULL) + return(1); + + MYDEBUGLOCK; + paranoid_streamer.setOutputDevice(device); + delete(paranoid_ostream); + paranoid_ostream=new ostream(¶noid_streamer); + + MYDEBUGUNLOCK; + + return(0); +} + + +int MyMsgManager::setParanoidStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + MYDEBUGLOCK; + paranoid_streamer.setOutputDevice(device); + delete(paranoid_ostream); + paranoid_ostream=new ostream(¶noid_streamer); + MYDEBUGUNLOCK; + + return(0); +} + + + + +ostream *MyMsgManager::paranoidStream(void) +{ + return(paranoid_ostream); +} + + +int MyMsgManager::ReplaceAllStreams(FileD * output_device, const char *device_filename, const char *copy_filename) +{ + MYDEBUGLOCK; + + delete(paranoid_ostream); + + if (output_device != NULL) + { + delete(output_device); + output_device = NULL; + } + + rename(device_filename, copy_filename); + + // FileD new_device(device_filename); + output_device = new FileD(device_filename); + + paranoid_streamer.setOutputDevice(output_device); + paranoid_ostream = new ostream(¶noid_streamer); + + MYDEBUGUNLOCK; + + return(0); +} diff --git a/Generals/Code/Tools/matchbot/mydebug.h b/Generals/Code/Tools/matchbot/mydebug.h new file mode 100644 index 00000000000..da38b12fb89 --- /dev/null +++ b/Generals/Code/Tools/matchbot/mydebug.h @@ -0,0 +1,134 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*****************************************************************************\ +wdebug Neal Kettler + +MT-LEVEL + MT-Safe + +The debugging module is pretty good for debugging and it has some message +printing stuff as well. The basic idea is that you write a class that +inherits from OutputDevice (several are provided) and assign that output +device to a stream. There are seperate streams for debugging, information, +warning, and error messages. Each one can have a seperate output device, +or they can all have the same one. Debugging messages only get compiled +in if your module defines 'DEBUG'. If you don't define debug, then not even +the text of the debugging message gets into the binary. All the other +output streams get printed regardless of whether DEBUG is defined. + +Sample usage: +FileD debug_device("gameres.debug"); // create a file device +MsgManager::setDebugStream(&debug_device); +DBGMSG("This debug message #" << 1 << " you use C++ streams"); + +Note that since these are defines you really don't need to put a semicolon +at the end, and it can be bad in situations like this: + +if (x) + DBGMSG("Stuff is broken"); +else + DBGMSG("Stuff is NOT broken"); + +This won't compile, read the code until you figure it out. Only then +will you be ready to leave grasshopper. + +\*****************************************************************************/ + +#ifndef MYDEBUG_HEADER +#define MYDEBUG_HEADER + +#define USE_SEM + +#include "wstypes.h" + +#include +#include + +#ifdef USE_SEM +#include "sem4.h" +#else +#include "critsec.h" +#endif +#include "odevice.h" +#include "streamer.h" +#include "xtime.h" +#include "timezone.h" // MDC +#include "filed.h" + +// This is needed because the streams return a pointer. Every time you +// change the output device the old stream is deleted, and a new one +// is created. +// MDC: Changed from semaphores to critical sections because Windows doesn't provide +// a good way of doing semaphores (I think) +// MDC: Never mind, they seem to be working now! I'm leaving it in, though, so anyone can +// flip a switch between the two. + +#ifdef USE_SEM +extern Sem4 MyDebugLibSemaphore; +#define MYDEBUGLOCK MyDebugLibSemaphore.Wait() +#define MYDEBUGUNLOCK MyDebugLibSemaphore.Post() +#else +extern CritSec MyDebugLibSemaphore; +#define MYDEBUGLOCK MyDebugLibSemaphore.lock() +#define MYDEBUGUNLOCK MyDebugLibSemaphore.unlock() +#endif + +// Print an information message +#define PARANOIDMSG(X)\ +{\ +char timebuf[40]; \ +Xtime now; \ +now -= TimezoneOffset(); \ +now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \ +MYDEBUGLOCK; \ +if (MyMsgManager::paranoidStream()) \ +(*(MyMsgManager::paranoidStream())) << "HACK " << timebuf << " [" << \ +__FILE__ << " " << __LINE__ << "] " << X << endl; \ +MYDEBUGUNLOCK; \ +} + +// Just get a stream to the information device, no extra junk +#define PARANOIDSTREAM(X)\ +{\ +MYDEBUGLOCK; \ +if (MyMsgManager::paranoidStream()) \ +(*(MyMsgManager::paranoidStream())) << X;\ +MYDEBUGUNLOCK; \ +} + + +//#undef MYDEBUGLOCK +//#undef MYDEBUGUNLOCK + +class MyMsgManager +{ +protected: + MyMsgManager(); + +public: + static int setAllStreams(OutputDevice *device); + static int setParanoidStream(OutputDevice *device); + static int ReplaceAllStreams(FileD *output_device, const char *device_filename, const char *copy_filename); + + static void enableParanoid(int flag); + + static ostream *paranoidStream(void); +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/rand.cpp b/Generals/Code/Tools/matchbot/rand.cpp new file mode 100644 index 00000000000..87a64698162 --- /dev/null +++ b/Generals/Code/Tools/matchbot/rand.cpp @@ -0,0 +1,132 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "rand.h" +#include + +static const double theMultFactor = 1.0 / (pow(2, 8 * sizeof(unsigned int)) - 1.0); + +RandClass::RandClass(int start) +{ + seed[0] = 0xf22d0e56L; + seed[1] = 0x883126e9L; + seed[2] = 0xc624dd2fL; + seed[3] = 0x702c49cL; + seed[4] = 0x9e353f7dL; + seed[5] = 0x6fdf3b64L; + + unsigned int ax; + + ax = start; /* mov eax,SEED */ + ax += 0xf22d0e56; /* add eax,0f22d0e56h */ + seed[0] = ax; /* mov seed,eax */ + ax += 0x883126e9 - 0xf22d0e56; /* add eax,0883126e9h-0f22d0e56h */ + seed[1] = ax; /* mov seed+4,eax */ + ax += 0xc624dd2f - 0x883126e9; /* add eax,0c624dd2fh-0883126e9h */ + seed[2] = ax; /* mov seed+8,eax */ + ax += 0x0702c49c - 0xc624dd2f; /* add eax,00702c49ch-0c624dd2fh */ + seed[3] = ax; /* mov seed+12,eax */ + ax += 0x9e353f7d - 0x0702c49c; /* add eax,09e353f7dh-00702c49ch */ + seed[4] = ax; /* mov seed+16,eax */ + ax += 0x6fdf3b64 - 0x9e353f7d; /* add eax,06fdf3b64h-09e353f7dh */ + seed[5] = ax; /* mov seed+20,eax */ +} + +// Add with carry. SUM is replaced with A + B + C, C is replaced with 1 if there was a carry, 0 if there wasn't. A carry occurred if the sum is less than one of the inputs. This is addition, so carry can never be more than one. +#define ADC(SUM, A, B, C) SUM = (A) + (B) + (C); C = ((SUM < (A)) || (SUM < (B))) + +unsigned int RandClass::randomValue( void ) +{ + unsigned int ax; + unsigned int c = 0; + + + ADC(ax, seed[5], seed[4], c); /* mov ax,seed+20 */ + /* add ax,seed+16 */ + seed[4] = ax; /* mov seed+8,ax */ + + ADC(ax, ax, seed[3], c); /* adc ax,seed+12 */ + seed[3] = ax; /* mov seed+12,ax */ + + ADC(ax, ax, seed[2], c); /* adc ax,seed+8 */ + seed[2] = ax; /* mov seed+8,ax */ + + ADC(ax, ax, seed[1], c); /* adc ax,seed+4 */ + seed[1] = ax; /* mov seed+4,ax */ + + ADC(ax, ax, seed[0], c); /* adc ax,seed+0 */ + seed[0] = ax; /* mov seed+0,ax */ + + /* Increment seed array, bubbling up the carries. */ + if (!++seed[5]) + { + if (!++seed[4]) + { + if (!++seed[3]) + { + if (!++seed[2]) + { + if (!++seed[1]) + { + ++seed[0]; + ++ax; + } + } + } + } + } + return(ax); +} + + +int RandClass::Int(void) +{ + return (int)randomValue(); +} + +int RandClass::Int(int low, int high) +{ + unsigned int delta = high - low + 1; + int rval; + + if (delta == 0) + return high; + + rval = ((int)(randomValue()%delta)) + low; + + return rval; +} + +double RandClass::Double(void) +{ + return Double(0.0, 1.0); +} + +double RandClass::Double(double low, double high) +{ + double delta = high - low; + double rval; + + if (delta <= 0.0) + return high; + + rval = ((double)(randomValue()) * theMultFactor) * delta + low; + return rval; +} + + diff --git a/Generals/Code/Tools/matchbot/rand.h b/Generals/Code/Tools/matchbot/rand.h new file mode 100644 index 00000000000..d5a1f4d6912 --- /dev/null +++ b/Generals/Code/Tools/matchbot/rand.h @@ -0,0 +1,45 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef __RAND_H__ +#define __RAND_H__ + +#include + +class RandClass +{ +public: + RandClass(int start = 0); + ~RandClass() + {} + + + int Int(void); + double Double(void); + int Int(int low, int high); + double Double(double low, double high); + +private: + + unsigned int randomValue( void ); + unsigned int seed[6]; + +}; + +#endif /* __RAND_H__ */ + diff --git a/Generals/Code/Tools/matchbot/wlib/arraylist.h b/Generals/Code/Tools/matchbot/wlib/arraylist.h new file mode 100644 index 00000000000..efcd3abd650 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/arraylist.h @@ -0,0 +1,709 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * +****************************************************************************** +Project Name: +File Name : arraylist.h +Author : Neal Kettler +Start Date : Jan 19, 1997 +Last Update : Jan 19, 1997 +------------------------------------------------------------------------------ + +Array implementation of a list. Note: There are some freaky C++ memory tricks +going on here. If you think there's a leak, see me before changing it. +The way this works is that it allocates an array to hold 'N' items on the +first list add. It doesn't call the constructors for those 'N' items until +necessary (for efficiency). When an item is added to a slot then a new +class is constructed inside the array element using the placement new operator +and the class's copy constructor. When elements are removed the destructor +is then manually called on this memory location. + +All data added to the list is copied so feel free to delete/destroy/modify +the original after an add. + +You _must_ have a good copy constructor for any classes that you use this template +for! A good copy constructor is one that won't blindly duplicate pointers +that don't belong to them, etc... + +\****************************************************************************/ + +#ifndef ARRAYLIST_HEADER +#define ARRAYLIST_HEADER + +#include +#include +#include +#include +#include +#include + +#include "wstypes.h" + +// +// Usage: ArrayList TheList; +// +template +class ArrayList +{ + public: + ArrayList(); + ArrayList(ArrayList &other); + ~ArrayList(); + + // Remove all entries from the lsit + void clear(void); + + // Add a node after the zero based 'pos' + bit8 add(IN T &node,sint32 pos); + bit8 addTail(IN T &node); + bit8 addHead(IN T &node); + bit8 addSortedAsc(IN T &node); // Ascending + bit8 addSortedDes(IN T &node); // Descending + /*bit8 addNumSortedAsc(IN T &node); // Ascending + bit8 addNumSortedDes(IN T &node); // Descending*/ + + // Remove a node + bit8 remove(OUT T &node,sint32 pos); + bit8 remove(sint32 pos); + bit8 removeHead(OUT T &node); + bit8 removeTail(OUT T &node); + + // Replace one obj with another + bit8 replace(IN T &node, sint32 pos); + + + // Get a node without removing from the list + bit8 get(OUT T &node,sint32 pos) RO; + bit8 getHead(OUT T &node) RO; + bit8 getTail(OUT T &node) RO; + + // Get a pointer to the interally managed copy (careful!) + bit8 getPointer(OUT T **node,sint32 pos) RO; + + // Get the number of entries in the list + sint32 length(void) RO; + + // UNSAFE! for classes, see note below! + bit8 setSize(sint32 newsize, IN T &filler); + + // Print information on the list + void print(FILE *out); + + // assignment operator + ArrayList &operator=(IN ArrayList &other); + + private: + sint32 _sortedLookup(IN T &target, int ascending); + sint32 Entries_; // Number of entries + sint32 Slots_; // Number of available slots + + T *Vector_; // The actual memory where the list is held + + enum + { + INITIAL_SIZE = 10 + }; + + bit8 growVector(void); // Expand the number of slots + bit8 shrinkVector(void); // Reduce the number of slots +}; + + +//Create the empty list +template +ArrayList::ArrayList() +{ + Entries_=0; + Slots_=0; + Vector_=NULL; +} + +// copy constructor +template +ArrayList::ArrayList(ArrayList &other) +{ + Entries_=0; + Slots_=0; + Vector_=NULL; + (*this)=other; +} + +//Free all the memory... +template +ArrayList::~ArrayList() +{ + clear(); // Remove the entries & call destructors on them + + delete[]((uint8*)Vector_); // this will prevent the destructors from + // gettting called on elements not + // containing valid objects. + + //fprintf(stderr,"Arraylist destructor\n"); +} + +// assignment operator +template +ArrayList &ArrayList::operator=(IN ArrayList &other) +{ + T node; + clear(); + for (int i=0; i +void ArrayList::clear() +{ + for (int i=0; i~T(); // Call the destructor manually. Don't try this + // at home kiddies! + } + Entries_=0; +} + +// ************************* UNSAFE UNSAFE UNSAFE ************************* +// Note - Don't call this on any type with a constructor/destructor since this +// is really dumb and just puts a new one of filler in. Well, it's kindof safe +// just be careful. +// It's fine for simple classes like ints though.. +// +// Add/remove entries in a stupid manner... +// +// ************************************************************************** +template +bit8 ArrayList::setSize(sint32 newsize, IN T &filler) +{ + int oldEntries=Entries_; + Entries_ = newsize; + + if (newsize<0) + return(false); + + // Grow the vector as much as we need to + while (newsize > Slots_) + growVector(); + + // Create new objects in the blank holes + for (int i=oldEntries; i +bit8 ArrayList::add(IN T &node,sint32 pos) +{ + if (pos > Entries_) // You can only access one of the end of the vector + pos=Entries_; + if (pos >= Slots_) // If we're at the end, grow the list + growVector(); + if (Entries_ >= Slots_) // enuff space? + growVector(); + + // If we are insering into the middle or front of the list we have to + // slide the old objects forward. + if (pos < Entries_) // If there are elements after the add point + memmove(Vector_+pos+1,Vector_+pos,sizeof(T)*(Entries_-pos)); // move them forward + + //fprintf(stderr,"Placement new to %p\n",(Vector_+pos)); + + // This uses the placement new operator. placement new allows us to + // specify the memory address for the new object. In this case we + // want it at the 'pos' index into our array. + new((void *)(Vector_+pos)) T((T &)node); // Trust me, this isn't a memory leak + Entries_++; // one new entry + return(TRUE); +} + + +// Add to the first node, all others get shifted down one slot +template +bit8 ArrayList::addHead(IN T &node) +{ + return(add(node,0)); +} + + +// Append to the end of the list +template +bit8 ArrayList::addTail(IN T &node) +{ + return(add(node,length())); +} + + +// addSortedX only works (properly) if evrerything else in the list is added +// using addSorted. +template +bit8 ArrayList::addSortedAsc(IN T &node) +{ + sint32 pos = _sortedLookup(node, 1); + return(add(node, pos)); +} + + +// addSortedX only works (properly) if evrerything else in the list is added +// using addSorted. +template +bit8 ArrayList::addSortedDes(IN T &node) +{ + sint32 pos = _sortedLookup(node, 0); + return(add(node, pos)); +} + + +// This is the binary search used by addSorted +template +sint32 ArrayList::_sortedLookup(IN T &target, int ascending) +{ + int low, mid, high; + T* lowtarget; + T* hightarget; + T* midtarget; + + + // Trivial cases + if( Entries_ == 0 ) + return 0; + + low = 0; + high = Entries_ - 1; + while( 1 ) + { + assert( low <= high ); + mid = low + (int)(floor(((double)high - (double)low) / (double)2)); + + getPointer(&lowtarget, low); + getPointer(&hightarget, high); + getPointer(&midtarget, mid); + + // Exact match + if( *midtarget == target ) return mid; + + // Single element + if( high == low ) + { + if( ascending ) + { + if( target <= *lowtarget ) + return low; + else + return low + 1; + } + else + { + if( target <= *lowtarget ) + return low + 1; + else + return low; + } + } + + // Two elemsnts + if( (high - low) == 1 ) + { + if( ascending ) + { + if( target <= *lowtarget ) + return low; + else if( target <= *hightarget ) + return high; + else + return high + 1; + } + else + { + if( target <= *hightarget ) + return high + 1; + else if( target <= *lowtarget ) + return high; + else + return low; + } + } + + // Sorry, try again... + if( ascending ) + { + if( target < *midtarget ) + high = mid; + else + low = mid; + } + else + { + if( target < *midtarget ) + low = mid; + else + high = mid; + } + } +} + + +/*// addNumSortedX works in much the same way as addSortedX, except that I needed +// it for a very specific thing. I needed a list of strings numerically sorted, +// not alphabetically sorted. Furthermore these strings were composed of numbers +// delimited by underscores. In the interest of keeping it generic, these +// functions take as args a node, a delimiting character, and a count of the +// number of fields to include in a sort. If this is the list of strings: +// +// 55_100, 2_5, 23_32, 98_445, 2_48, 8_88, 2_3, 2_4 +// +// An alphabetical sort is: +// +// 2_3, 2_4, 2_48, 2_5, 55_100, 8_88, 98_445 +// +// But a numerical sort by calling addNumSortedAsc(, "_", 2) will result in: +// +// 2_3, 2_4, 2_5, 2_48, 8_88, 55_100, 98_445 +// +// Yes...now that you mention it I am on crack... +// +template +bit8 ArrayList::addNumSortedAsc(IN T &node, char delim, int fields) +{ + sint32 pos = _numSortedLookup(node, delim, fields, 1); + return(add(node, pos)); +} + + +// See addNumSortedAsc comment above. +template +bit8 ArrayList::addSortedDes(IN T &node, char delim, int fields) +{ + sint32 pos = _sortedLookup(node, delim, fields, 0); + return(add(node, pos)); +} + + +// This is the binary search used by addSorted +template +sint32 ArrayList::_numSortedLookup(IN T &target, char delim, int fields, int ascending) +{ + int low, mid, high; + T* lowtarget; + T* hightarget; + T* midtarget; + + + // Trivial case + if( Entries_ == 0 ) + return 0; + + low = 0; + high = Entries_; + while( 1 ) + { + assert( low <= high ); + mid = low + (int)(floor(((double)high - (double)low) / (double)2)); + + getPointer(&lowtarget, low); + getPointer(&hightarget, high); + getPointer(&midtarget, mid); + + // Exact match + if( *midtarget == target ) return mid; + + // Single element + if( high == low ) + { + if( ascending ) + { + if( target <= *lowtarget ) + return low; + else + return low + 1; + } + else + { + if( target <= *lowtarget ) + return low + 1; + else + return low; + } + } + + // Two elemsnts + if( (high - low) == 1 ) + { + if( ascending ) + { + if( target <= *lowtarget ) + return low; + else + return high; + } + else + { + if( target <= *lowtarget ) + return high; + else + return low; + } + } + + // Sorry, try again... + if( ascending ) + { + if( target < *midtarget ) + high = mid; + else + low = mid; + } + else + { + if( target < *midtarget ) + low = mid; + else + high = mid; + } + } +}*/ + + +// +// Delete an item at this index and construct a new one in it's place +// +template +bit8 ArrayList::replace(IN T &node, sint32 pos) +{ + if (Entries_==0) + return(FALSE); + if (pos<0) + pos=0; + if (pos >= Entries_) + pos=Entries_-1; + + (Vector_+pos)->~T(); // Call the destructor manually. Don't try this + // at home kiddies! + + // Now put the replacement object in there... + new((void *)(Vector_+pos)) T(node); // Trust me, this isn't a memory leak + + return(TRUE); +} + + + +// Remove at the zero based index specified by 'pos'. When removing from +// a slot, all others get shifted up by one. +template +bit8 ArrayList::remove(sint32 pos) +{ + if (Entries_==0) + return(FALSE); + if (pos<0) + pos=0; + if (pos >= Entries_) + pos=Entries_-1; + + (Vector_+pos)->~T(); // Call the destructor manually. Don't try this + // at home kiddies! + + memmove(Vector_+pos,Vector_+pos+1,sizeof(T)*(Entries_-pos-1)); + + Entries_--; + + // If we're at 33% usage or less, shrink the vector + if ( (Entries_*3) <= Slots_) + shrinkVector(); + + return(TRUE); +} + + +// Remove at the zero based index specified by 'pos'. When removing from +// a slot, all others get shifted up by one. +template +bit8 ArrayList::remove(OUT T &node, sint32 pos) +{ + bit8 retval; + retval=get(node,pos); + if (retval==FALSE) + return(FALSE); + return(remove(pos)); +} + + +// Remove the first node of the list +template +bit8 ArrayList::removeHead(OUT T &node) +{ + return(remove(node,0)); +} + + +// Remove the last node of the list +template +bit8 ArrayList::removeTail(OUT T &node) +{ + return(remove(node,Entries_-1)); +} + +// get a pointer to the internally managed object. Try and avoid this, but +// sometimes efficiency requires it... +// get a copy of an item +template +bit8 ArrayList::getPointer(OUT T **node,sint32 pos) RO +{ + if ((pos < 0)||(pos >= Entries_)) + return(FALSE); + *node=&(Vector_[pos]); + return(TRUE); +} + + +// get a copy of an item +template +bit8 ArrayList::get(OUT T &node,sint32 pos) RO +{ + if ((pos < 0)||(pos >= Entries_)) + return(FALSE); + node=Vector_[pos]; + return(TRUE); +} + + +// get a copy of the first node of the list +template +bit8 ArrayList::getHead(OUT T &node) RO +{ + return(get(node,0)); +} + + +// get a copy of the last node +template +bit8 ArrayList::getTail(OUT T &node) RO +{ + return(get(node,Entries_-1)); +} + +// just for debugging +template +void ArrayList::print(FILE *out) +{ + fprintf(out,"--------------------\n"); + //for (int i=0; i +sint32 ArrayList::length(void) RO +{ + return(Entries_); +} + +// Grow the vector by a factor of 2X +template +bit8 ArrayList::growVector(void) +{ + if (Entries_ < Slots_) // Don't grow until we're at 100% usage + return(FALSE); + + int newSlots=Entries_*2; + if(newSlots < INITIAL_SIZE) + newSlots=INITIAL_SIZE; + + //fprintf(stderr,"Growing vector to: %d\n",newSlots); + + // The goofy looking new below prevents operator new from getting called + // unnecessarily. This is severall times faster than allocating all of + // the slots as objects and then calling the assignment operator on them + // when they actually get used. + // + T *newVector=(T *)(new uint8[newSlots * sizeof(T)]); + memset(newVector,0,newSlots * sizeof(T)); // zero just to be safe + + if (Vector_ != NULL) + memcpy(newVector,Vector_,Entries_*sizeof(T)); + + delete[]((uint8 *)Vector_); // Get rid of the old vector without calling + // destructors + + Vector_=newVector; + Slots_=newSlots; + + return(TRUE); +} + + +// Shrink the vector by a factor of 2X +template +bit8 ArrayList::shrinkVector(void) +{ + //fprintf(stderr,"Shrink called\n"); + + // Don't need to shrink until usage goes below 33% + if ( (Entries_*3) > Slots_) + return(FALSE); + + int newSlots=Slots_/2; + if(newSlots < INITIAL_SIZE) // never shrink past initial size + newSlots=INITIAL_SIZE; + + if (newSlots >= Slots_) // don't need to shrink + return(FALSE); + + //fprintf(stderr,"Shrinking vector to: %d\n",newSlots); + + // The goofy looking new below prevents operator new from getting called + // unnecessarily. This is severall times faster than allocating all of + // the slots as objects and then calling the assignment operator on them + // when they actually get used. + // + T *newVector=(T *)(new uint8[newSlots * sizeof(T)]); + + if (Vector_ != NULL) // Vector_ better not be NULL! + memcpy(newVector,Vector_,Entries_*sizeof(T)); + + delete[]((uint8 *)Vector_); // Get rid of the old vector without calling + // destructors + + Vector_=newVector; + Slots_=newSlots; + + return(TRUE); +} + + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/configfile.cpp b/Generals/Code/Tools/matchbot/wlib/configfile.cpp new file mode 100644 index 00000000000..b0f9e5a91ca --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/configfile.cpp @@ -0,0 +1,490 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * +****************************************************************************** +Project Name: Carpenter (The RedAlert ladder creator) +File Name : configfile.cpp +Author : Neal Kettler +Start Date : June 9, 1997 +Last Update : June 17, 1997 + + +This class will read in a config file and store the key value pairs for +later access. This is a fairly simple class, the config file is assumed +to be of the form: + +#comment +key = value + +The value can then be retrieved as a string or an integer. The key on +the left is used for retrieval and it must be specified in uppercase +for the 'get' functions. E.g. getString("KEY",valWstring); +\***************************************************************************/ + +#include +#include +#include + +#include "configfile.h" +#include "wdebug.h" + +static uint32 Wstring_Hash(const Wstring &string); +static char *Eat_Spaces(char *string); + +ConfigFile::ConfigFile() : Dictionary_(Wstring_Hash) +{ } + +ConfigFile::~ConfigFile() +{ } + +// Read and parse the config file. The key value pairs will be stored +// for later access by the getString/getInt functions. +bit8 ConfigFile::readFile(FILE *in) +{ + char string[256]; + char sectionname[256]; // section name like '[user parameters]' + Wstring key; + Wstring value; + char *cptr; + + memset(string,0,256); + memset(sectionname,0,256); + sectionList.clear(); + + while (fgets(string,256,in)) + { + cptr=Eat_Spaces(string); + if ((*cptr==0)||(*cptr=='#')) // '#' signals a comment + continue; + + if (*cptr=='[') // new section + { + key=cptr; + key.truncate(']'); // remove after & including the ] + key.cat("]"); // put the ] back + strcpy(sectionname,key.get()); // set the current section name + Wstring wssectionname; + + if (strlen(sectionname)==2) // clear section with a "[]" + { + sectionname[0]=0; + wssectionname.set(""); + } + else + wssectionname.set(sectionname+1); + + wssectionname.truncate(']'); + sectionList.addTail(wssectionname); + + + continue; + } + + if (strchr(cptr,'=')==NULL) // All config entries must have a '=' + continue; + key=cptr; + key.truncate('='); + key.removeSpaces(); // No spaces allowed in the key + key.toUpper(); // make key all caps + + // Add the section name to the end of the key + if (strlen(sectionname)) + key.cat(sectionname); + + cptr=Eat_Spaces(strchr(cptr,'=')+1); // Jump to after the '=' + value=cptr; + value.truncate('\r'); + value.truncate('\n'); + value.truncate('#'); + + // TheSuperHackers @compile xezon 25/03/2025 Re-implement algorithm to avoid writing into string buffer directly. + // Remove trailing spaces + { + const char* valueStr = value.get(); + const uint32 valueLen = value.length(); + uint32 spaceIdx = valueLen; + for (; spaceIdx > 0 && isgraph(valueStr[spaceIdx - 1])==0; --spaceIdx); + if (spaceIdx != valueLen) + value.truncate(spaceIdx); + } + + Critsec_.lock(); + Dictionary_.add(key,value); + Critsec_.unlock(); + } + return(TRUE); +} + + +// +// Enum through the config strings. To start, index & offset should be 0 +// If retval is false you're done, ignore whatever's in key & value. +// +// Section specifies the configfile section. Set to NULL if you don't care. +// +bit8 ConfigFile::enumerate(int &index, int &offset, Wstring &key, Wstring &value, IN char *section) const +{ + int seclen = strlen(section); + while(1) + { + Critsec_.lock(); + if (Dictionary_.iterate(index,offset,key,value)==FALSE) // out of keys? + { + Critsec_.unlock(); + return(FALSE); + } + Critsec_.unlock(); + + if (section==NULL) // no specified section, so any will do... + break; + + if (strlen(section)+2 >= strlen(key.get())) // key should have form: X[section] + continue; + + // Is this key part of our section? + const char *keystr = key.get() + strlen(key.get())-seclen-1; + if (strncmp(keystr,section,strlen(section))==0) + break; + } + key.truncate('['); // remove the section name + return(TRUE); +} + + + +// Get a config entry as a string +bit8 ConfigFile::getString(IN Wstring &_key, Wstring &value, IN char *section) const +{ + Wstring key(_key); + key.toUpper(); + + if (section) // append section name to key + { + key+="["; + key+=section; + key+="]"; + } + + Critsec_.lock(); + bit8 retval=Dictionary_.getValue(key,value); + Critsec_.unlock(); + + if (retval==FALSE) + { + DBGMSG("Config entry missing: "<>24); // ROL 8 + } + return(retval); +} + +static char *Eat_Spaces(char *string) +{ + char *retval=string; + while (isspace(*retval)) + retval++; + return(retval); +} diff --git a/Generals/Code/Tools/matchbot/wlib/configfile.h b/Generals/Code/Tools/matchbot/wlib/configfile.h new file mode 100644 index 00000000000..b622f1354e4 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/configfile.h @@ -0,0 +1,76 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * +****************************************************************************** +Project Name: Carpenter (The RedAlert ladder creator) +File Name : configfile.h +Author : Neal Kettler +Start Date : June 9, 1997 +Last Update : May 13, 1999 +\***************************************************************************/ + +#ifndef CONFIGFILE_HEADER +#define CONFIGFILE_HEADER + +#include "wstypes.h" +#include "dictionary.h" +#include "wstring.h" +#include "critsec.h" +#include "arraylist.h" + +class ConfigFile +{ + public: + ConfigFile(); + ~ConfigFile(); + bit8 readFile(FILE *config); + bit8 getString(IN Wstring &key,OUT Wstring &value, IN char *section=NULL) const; + bit8 getString(IN char *key,OUT Wstring &value, IN char *section=NULL) const; + + bit8 getInt(IN Wstring &key,OUT sint32 &value, IN char *section=NULL) const; + bit8 getInt(IN char *key,OUT sint32 &value, IN char *section=NULL) const; + + bit8 getInt(IN Wstring &key,OUT sint16 &value, IN char *section=NULL) const; + bit8 getInt(IN char *key,OUT sint16 &value, IN char *section=NULL) const; + + // Enumerate through the config lines + bit8 enumerate(int &index, int &offset, Wstring &key, Wstring &value, IN char *section=NULL) const; + + // Manual update of config file + bit8 setString(IN Wstring &key,IN Wstring &value, IN char *section=NULL); + bit8 setString(IN char *key,IN Wstring &value, IN char *section=NULL); + bit8 setInt(IN Wstring &key,IN sint32 &value, IN char *section=NULL); + bit8 setInt(IN char *key,IN sint32 &value, IN char *section=NULL); + bit8 removeEntry(IN Wstring &key, IN char *section=NULL); + bit8 removeEntry(IN char *key, IN char *section=NULL); + bit8 writeFile(FILE *config); // Does not preserve comments, etc + + ArrayList sectionList; // stores the names of all sections + + private: + Dictionary Dictionary_; // stores the mappings from keys + // to value strings + + // The lock is only needed around the immediate access to the dictionary, no writes + // are allowed so you don't need to worry about an outer lock around the enumerate + CritSec Critsec_; // lock around dictionary +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/critsec.cpp b/Generals/Code/Tools/matchbot/wlib/critsec.cpp new file mode 100644 index 00000000000..21b8ced2b79 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/critsec.cpp @@ -0,0 +1,129 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "critsec.h" +#include +#include "wlib/wdebug.h" + + + +CritSec::CritSec() +{ +#ifdef _UNIX + pthread_mutex_init(&Mutex_, NULL); + RefCount_ = 0; +#elif defined(_WIN32) + InitializeCriticalSection(&CritSec_); +#endif +} + +CritSec::~CritSec() +{ + #ifdef _UNIX + pthread_mutex_destroy(&Mutex_); + #elif defined(_WIN32) + DeleteCriticalSection(&CritSec_); + #endif +} + +// The "lock" function blocks until the mutex is available. +// Returns 0 on success, error code on error. +// +// A thread that already has a lock will increment a reference count if it calls +// lock again. It must then call unlock() enough times to get the reference to 0. +// +// If refcount is not null you can get the current ref counter after the lock. +// +sint32 CritSec::lock(int *refcount) RO +{ + #ifdef _UNIX + sint32 status; + + // I TRY to get the lock. IF I succeed, OR if I fail because + // I already have the lock, I just increment the reference + // count and return. + if (((status = pthread_mutex_trylock(&Mutex_)) == 0) || + ((status == EBUSY) && (ThreadId_ == pthread_self()))) + { + ThreadId_ = pthread_self(); + RefCount_++; + if (refcount) + *refcount=RefCount_; + return(0); + } + + // Otherwise, I wait for the lock. + if ((status = pthread_mutex_lock(&Mutex_)) == 0) + { + assert(RefCount_ == 0); + ThreadId_ = pthread_self(); + RefCount_++; + } + else + { + ERRMSG("pthread_mutex_lock: " << strerror(errno)); + } + + if (refcount) + *refcount=RefCount_; + + return(status); + #elif defined(_WIN32) + // TOFIX update the refcount + EnterCriticalSection(&CritSec_); + return(0); + #else + #error Must define either _WIN32 or _UNIX + #endif +} + +// The "unlock" function release the critical section. +sint32 CritSec::unlock(void) RO +{ + #ifdef _UNIX + sint32 status = 0; + + assert(RefCount_ >= 0); + if (RefCount_ <= 0) + { + //ERRMSG("unlocking unlocked mutex!"); + return(-1); + } + + ///assert(ThreadId_ == pthread_self()); + if (ThreadId_ != pthread_self()) + { + WRNMSG("tried to unlock a mutex not owned by self"); + return(-1); + } + + if (--RefCount_ == 0) + { + // Set thread id to zero -- we're going to release mutex + ThreadId_ = (pthread_t)0; + + // Unlock the mutex. + if ((status = pthread_mutex_unlock(&Mutex_)) != 0) + ERRMSG("pthread_mutex_lock: " << strerror(errno)); + } + return status; + #elif defined(_WIN32) + LeaveCriticalSection(&CritSec_); + return(0); + #endif +} diff --git a/Generals/Code/Tools/matchbot/wlib/critsec.h b/Generals/Code/Tools/matchbot/wlib/critsec.h new file mode 100644 index 00000000000..c25759cf047 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/critsec.h @@ -0,0 +1,62 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef CRITSEC_HEADER +#define CRITSEC_HEADER + +#include "wstypes.h" +#ifdef _WIN32 + #include + #include +#elif defined(_UNIX) + #include + #include +#endif + +// Windows headers have a tendency to redefine IN +#ifdef IN +#undef IN +#endif +#define IN const + +// +// Critical Section built either on a POSIX Mutex, or a Win32 Critical Section +// +// POSIX version is done by keeping a thread_id and a reference count. Win32 version +// just calls the built in functions. +// +class CritSec +{ + public: + CritSec(); + ~CritSec(); + + sint32 lock(int *refcount=NULL) RO; + sint32 unlock(void) RO; + + protected: + #ifdef _WIN32 + mutable CRITICAL_SECTION CritSec_; + #else + mutable pthread_mutex_t Mutex_; // Mutex lock + mutable pthread_t ThreadId_; // Owner of mutex + mutable int RefCount_; // Reference count + #endif +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/dictionary.h b/Generals/Code/Tools/matchbot/wlib/dictionary.h new file mode 100644 index 00000000000..56e1cc346b4 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/dictionary.h @@ -0,0 +1,681 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * +****************************************************************************** +Project Name: Carpenter (The RedAlert ladder creator) +File Name : dictionary.h +Author : Neal Kettler +Start Date : June 1, 1997 +Last Update : June 17, 1997 + +This template file implements a hash dictionary. A hash dictionary is +used to quickly match a value with a key. This works well for very +large sets of data. A table is constructed that has some power of two +number of pointers in it. Any value to be put in the table has a hashing +function applied to the key. That key/value pair is then put in the +linked list at the slot the hashing function specifies. If everything +is working well, this is much faster than a linked list, but only if +your hashing function is good. +\****************************************************************************/ + + +#ifndef DICTIONARY_HEADER +#define DICTIONARY_HEADER + + +#include +#include +#include +#include +#include + +#include "wstypes.h" + +// Every entry in the hash dictionary must be an instance of the DNode +// template. 'K' and 'V' denote Key and Value. +template +class DNode +{ + public: + K key; + V value; + DNode *hashNext; +}; + +template +class Dictionary +{ + public: + ////////////////Dictionary(uint32 (* hashFn)(K &key)); + + +// Note: I had to put this inside the class definition because VC5 sucks butt + +//Create the empty hash dictionary +Dictionary(uint32 (*hashFn)(const K &key)) : + SHRINK_THRESHOLD(0.20), // When table is only 20% full shrink it + EXPAND_THRESHOLD(0.80), // When table is 80% full grow it + MIN_TABLE_SIZE(128) // must be a power of 2 +{ + log2Size=MIN_TABLE_SIZE; + size=MIN_TABLE_SIZE; + assert(size>=4); + tableBits=0; + while(log2Size) { tableBits++; log2Size>>=1; } + tableBits--; + size=1< **)new DNode* [size]; + assert(table!=NULL); + + memset((void *)table,0,size*sizeof(void *)); + hashFunc=hashFn; +} + + + ~Dictionary(); + + void clear(void); + bit8 add(IN K &key,IN V &value); + bool getValue(IN K &key, OUT V &value) RO; + bool getPointer(IN K &key, OUT V **value) RO; // ptr to internal storage (Careful!) + void print(FILE *out) RO; + uint32 getSize(void) RO; + uint32 getEntries(void) RO; + bit8 contains(IN K &key) RO; + bit8 updateValue(IN K &key,IN V &value); + bit8 remove(IN K &key,OUT V &value); + bit8 remove(IN K &key); + bit8 removeAny(OUT K &key,OUT V &value); + bit8 iterate(INOUT int &index,INOUT int &offset, OUT V &value) RO; + bit8 iterate(INOUT int &index,INOUT int &offset, OUT K &key, OUT V &value) RO; + Dictionary &operator=(Dictionary &other); + + private: + void shrink(void); // halve the number of slots + void expand(void); // double the number of slots + + + DNode **table; // This stores the lists at each slot + + uint32 entries; // number of entries + uint32 size; // size of table + uint32 tableBits; // table is 2^tableBits big + uint32 log2Size; // Junk variable + bit8 keepSize; // If true don't shrink or expand + + uint32 (* hashFunc)(IN K &key); // User provided hash function + uint32 keyHash(IN K &key) RO; // This will reduce to correct range + + + // See initilizer list of constructor for values + const double SHRINK_THRESHOLD; // When table is this % full shrink it + const double EXPAND_THRESHOLD; // When table is this % full grow it + const int MIN_TABLE_SIZE; // must be a power of 2 +}; + + + +//Free all the memory... +template +Dictionary::~Dictionary() +{ + clear(); // Remove the entries + delete[](table); // And the table as well +} + +// Remove all the entries and free the memory +template +void Dictionary::clear() +{ + DNode *temp,*del; + uint32 i; + //free all the data + for (i=0; ihashNext; + delete(del); + } + table[i]=NULL; + } + entries=0; + + while ((getSize()>(uint32)MIN_TABLE_SIZE)&&(keepSize==FALSE)) + shrink(); +} + +template +uint32 Dictionary::keyHash(IN K &key) RO +{ + uint32 retval=hashFunc(key); + retval &= ((1< +void Dictionary::print(FILE *out) RO +{ + DNode *temp; + uint32 i; + + fprintf(out,"--------------------\n"); + for (i=0; ihashNext; + } + fprintf(out,"\n"); + } + fprintf(out,"--------------------\n"); +} + + +template +Dictionary &Dictionary::operator=(Dictionary &other) +{ + _ASSERTE(0); +} + + + +// +// Iterate through all the records. Index is for the table, offset specifies the +// element in the linked list. Set both to 0 and continue calling till false +// is returned. +template +bit8 Dictionary::iterate(INOUT int &index,INOUT int &offset, + OUT V &value) RO +{ + DNode *temp; + + // index out of range + if ((index<0)||(index >= (int)getSize())) + return(FALSE); + + temp=table[index]; + while ((temp==NULL)&&((++index) < (int)getSize())) + { + temp=table[index]; + offset=0; + } + + if (temp==NULL) // no more slots with data + return(FALSE); + + uint32 i=0; + while ((temp!=NULL) && ((int)i < offset)) + { + temp=temp->hashNext; + i++; + } + + if (temp==NULL) // should never happen + return(FALSE); + + value=temp->value; + if (temp->hashNext==NULL) + { + index++; + offset=0; + } + else + offset++; + + return(TRUE); +} + + + + +// +// Iterate through all the records. Index is for the table, offset specifies the +// element in the linked list. Set both to 0 and continue calling till false +// is returned. +template +bit8 Dictionary::iterate(INOUT int &index,INOUT int &offset, + OUT K &key, OUT V &value) RO +{ + DNode *temp; + + // index out of range + if ((index<0)||(index >= (int)getSize())) + return(FALSE); + + temp=table[index]; + while ((temp==NULL)&&((++index) < (int)getSize())) + { + temp=table[index]; + offset=0; + } + + if (temp==NULL) // no more slots with data + return(FALSE); + + uint32 i=0; + while ((temp!=NULL) && ((int)i < offset)) + { + temp=temp->hashNext; + i++; + } + + if (temp==NULL) // should never happen + return(FALSE); + + value=temp->value; + key=temp->key; + if (temp->hashNext==NULL) + { + index++; + offset=0; + } + else + offset++; + + return(TRUE); +} + + + + +// Return the current size of the hash table +template +uint32 Dictionary::getSize(void) RO +{ return(size); } + + +// Return the current number of entries in the table +template +uint32 Dictionary::getEntries(void) RO +{ return(entries); } + + +// Does the Dictionary contain the key? +template +bit8 Dictionary::contains(IN K &key) RO +{ + int offset; + DNode *node; + + offset=keyHash(key); + + node=table[offset]; + + if (node==NULL) + { return(FALSE); } // can't find it + + while(node!=NULL) + { + if ((node->key)==key) + { return(TRUE); } + node=node->hashNext; + } + return(FALSE); +} + + +// Try and update the value of an already existing object +template +bit8 Dictionary::updateValue(IN K &key,IN V &value) +{ + sint32 retval; + + retval=remove(key); + if (retval==FALSE) + return(FALSE); + + add(key,value); + return(TRUE); +} + + +// Add to the dictionary (if key exists, value is updated with the new V) +template +bit8 Dictionary::add(IN K &key,IN V &value) +{ + int offset; + DNode *node,*item,*temp; + float percent; + + item=(DNode *)new DNode; + assert(item!=NULL); + + #ifdef KEY_MEM_OPS + memcpy(&(item->key),&key,sizeof(K)); + #else + item->key=key; + #endif + + #ifdef VALUE_MEM_OPS + memcpy(&(item->value),&value,sizeof(V)); + #else + item->value=value; + #endif + + item->hashNext=NULL; + + //If key already exists, it will be overwritten + remove(key); // Hopefully this will be false... + + offset=keyHash(key); + + node=table[offset]; + + if (node==NULL) + { table[offset]=item; } + else + { + temp=table[offset]; + table[offset]=item; + item->hashNext=temp; + } + + entries++; + percent=(float)entries; + percent/=(float)getSize(); + if (percent>= EXPAND_THRESHOLD ) expand(); + + return(TRUE); +} + +// Remove an item from the dictionary +template +bit8 Dictionary::remove(IN K &key,OUT V &value) +{ + int offset; + DNode *node,*last,*temp; + float percent; + + if (entries==0) + return(FALSE); + + percent=(float)(entries-1); + percent/=(float)getSize(); + + offset=keyHash(key); + node=table[offset]; + + last=node; + if (node==NULL) + return(FALSE); + + //special case table points to thing to delete + + #ifdef KEY_MEM_OPS + if (0==memcmp(&(node->key),&key,sizeof(K))) + #else + if ((node->key)==key) + #endif + { + #ifdef VALUE_MEM_OPS + memcpy(&value,&(node->value),sizeof(V)); + #else + value=node->value; + #endif + temp=table[offset]->hashNext; + delete(table[offset]); + table[offset]=temp; + entries--; + if (percent <= SHRINK_THRESHOLD) + shrink(); + return(TRUE); + } + node=node->hashNext; + + bit8 retval=FALSE; // wow, didn't add this for years... (DOH!) + + //Now the case if the thing to delete is not the first + while (node!=NULL) + { + #ifdef KEY_MEM_OPS + if (0==memcmp(&(node->key),&key,sizeof(K))) + #else + if (node->key==key) + #endif + { + #ifdef VALUE_MEM_OPS + memcpy(&value,&(node->value),sizeof(V)); + #else + value=node->value; + #endif + last->hashNext=node->hashNext; + entries--; + delete(node); + retval=TRUE; // yes, we deleted something + break; + } + last=node; + node=node->hashNext; + } + + if (percent <= SHRINK_THRESHOLD) + shrink(); + return(retval); +} + + +template +bit8 Dictionary::remove(IN K &key) +{ + V temp; + return(remove(key,temp)); +} + + +// Remove some random K/V pair that's in the Dictionary +template +bit8 Dictionary::removeAny(OUT K &key,OUT V &value) +{ + int offset; + DNode *node,*last,*temp; + float percent; + + if (entries==0) + return(FALSE); + + percent=(entries-1); + percent/=(float)getSize(); + + int i; + offset=-1; + for (i=0; i<(int)getSize(); i++) + if (table[i]!=NULL) + { + offset=i; + break; + } + + if (offset==-1) // Nothing there + return(FALSE); + + node=table[offset]; + last=node; + + #ifdef KEY_MEM_OPS + memcpy(&key,&(node->key),sizeof(K)); + #else + key=node->key; + #endif + #ifdef VALUE_MEM_OPS + memcpy(&value,&(node->value),sizeof(V)); + #else + value=node->value; + #endif + + temp=table[offset]->hashNext; + delete(table[offset]); + table[offset]=temp; + entries--; + if (percent <= SHRINK_THRESHOLD) + shrink(); + return(TRUE); +} + + +template +bool Dictionary::getValue(IN K &key,OUT V &value) RO +{ + V *valptr=NULL; + bool retval=getPointer(key,&valptr); + if (retval && valptr) + { + #ifdef VALUE_MEM_OPS + assert(0); + #else + value=*valptr; + #endif + } + return(retval); +} + +// Try and avoid this since you're getting a pointer to the internally +// managed data! +template +bool Dictionary::getPointer(IN K &key,OUT V **valptr) RO +{ + int offset; + DNode *node; + + if (entries==0) + return(FALSE); + + offset=keyHash(key); + + node=table[offset]; + + if (node==NULL) + return(FALSE); + + #ifdef KEY_MEM_OPS + while ((node!=NULL)&&(memcmp(&(node->key),&key,sizeof(K)))) + #else + while ((node!=NULL)&&( ! ((node->key)==key)) ) // odd syntax so you don't + #endif // have to do oper != + { node=node->hashNext; } + + if (node==NULL) + { return(FALSE); } + + *valptr=&(node->value); + + return(TRUE); +} + + +//A note about Shrink and Expand: They are never necessary, they are +//only here to improve performance of the hash table by reducing +//the length of the linked list at each table entry. + +// Shrink the hash table by a factor of 2 (and relocate entries) +template +void Dictionary::shrink(void) +{ + int i; + int oldsize; + uint32 offset; + DNode **oldtable,*temp,*first,*next; + + if ((size<=(uint32)MIN_TABLE_SIZE)||(keepSize==TRUE)) + return; + + //fprintf(stderr,"Shrinking....\n"); + + oldtable=table; + oldsize=size; + size/=2; + tableBits--; + + table=(DNode **)new DNode*[size]; + assert(table!=NULL); + memset((void *)table,0,size*sizeof(void *)); + + for (i=0; ikey); + first=table[offset]; + table[offset]=temp; + next=temp->hashNext; + temp->hashNext=first; + temp=next; + } + } + delete[](oldtable); +} + + +template +void Dictionary::expand(void) +{ + int i; + int oldsize; + uint32 offset; + DNode **oldtable,*temp,*first,*next; + + if (keepSize==TRUE) + return; + + //fprintf(stderr,"Expanding...\n"); + + oldtable=table; + oldsize=size; + size*=2; + tableBits++; + + table=(DNode **)new DNode* [size]; + assert(table!=NULL); + memset((void *)table,0,size*sizeof(void *)); + + for (i=0; ikey); + first=table[offset]; + table[offset]=temp; + next=temp->hashNext; + temp->hashNext=first; + temp=next; + } + } + delete[](oldtable); +} + + +#endif + diff --git a/Generals/Code/Tools/matchbot/wlib/filed.h b/Generals/Code/Tools/matchbot/wlib/filed.h new file mode 100644 index 00000000000..ac68d7c7054 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/filed.h @@ -0,0 +1,51 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef FILED_HEADER +#define FILED_HEADER + +#include "odevice.h" + +class FileD : public OutputDevice +{ + public: + FileD(IN char *filename, IN char *mode = "w") + { + out=fopen(filename,mode); + if (out==NULL) + out=fopen("FileDev.out",mode); + } + + virtual ~FileD() + { fclose(out); } + + virtual int print(const char *str,int len) + { + char *string=new char[len+1]; + memset(string,0,len+1); + memcpy(string,str,len); + fprintf(out,"%s",string); + delete[](string); + fflush(out); + return(len); + } + + FILE *out; +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/linkedlist.h b/Generals/Code/Tools/matchbot/wlib/linkedlist.h new file mode 100644 index 00000000000..d546d3e96c6 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/linkedlist.h @@ -0,0 +1,495 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * +****************************************************************************** +Project Name: +File Name : linkedlist.h +Author : Neal Kettler +Start Date : June 19, 1997 +Last Update : June 19, 1997 + +Linked list template. This is a fairly standard doubly linked list that +allows insertion and removal at any point in the list. A current pointer +is used to quickly access items when they are examined sequentially. +Copies of the data are stored instead of a pointer to the original. + +If you want to store pointers then the template should be of a pointer type. +\****************************************************************************/ + +#ifndef LINKEDLIST_HEADER +#define LINKEDLIST_HEADER + +#include +#include +#include +#include + +#include "wstypes.h" + +template +class LNode +{ + public: + T Node; + LNode *Next; + LNode *Prev; +}; + +template +class LinkedList +{ + public: + LinkedList(); + LinkedList(LinkedList &other); + ~LinkedList(); + + // Remove all entries from the lsit + void clear(void); + + // Add a node after the zero based 'pos' + bit8 add(IN T &node,sint32 pos, OUT T **newnodeptr=NULL); + bit8 addTail(IN T &node, OUT T **newnodeptr=NULL); + bit8 addHead(IN T &node, OUT T **newnodeptr=NULL); + + // Remove a node + bit8 remove(OUT T &node,sint32 pos); + bit8 remove(sint32 pos); + bit8 removeHead(OUT T &node); + bit8 removeTail(OUT T &node); + + + // Get a node without removing from the list + bit8 get(OUT T &node,sint32 pos); + bit8 getHead(OUT T &node); + bit8 getTail(OUT T &node); + + // Get a pointer to the internally managed data (careful!) + bit8 getPointer(OUT T **node, sint32 pos); + + // Get the number of entries in the list + sint32 length(void); + + // Print information on the list + void print(IN FILE *out); + + // assignment operator + LinkedList &operator=(LinkedList &other); + + private: + sint32 Entries; // Number of entries + LNode *Head; // Head of the list + LNode *Tail; // Tail of the list + + LNode *Current; // Current pointer & index for speed only + sint32 CurIndex; +}; + + +//Create the empty list +template +LinkedList::LinkedList() +{ + Entries=0; + Head=Tail=Current=NULL; + CurIndex=-1; // Not valid when 0 entries +} + +// copy constructor +template +LinkedList::LinkedList(LinkedList &other) +{ + Entries=0; + Head=Tail=Current=NULL; + CurIndex=-1; // Not valid when 0 entries + (*this)=other; +} + +//Free all the memory... +template +LinkedList::~LinkedList() +{ + clear(); // Remove the entries +} + +// assignment operator +template +LinkedList &LinkedList::operator=(LinkedList &other) +{ + T node; + clear(); + for (int i=0; i +void LinkedList::clear() +{ + LNode *temp,*del; + + temp=Head; + while (temp) { + del=temp; + temp=temp->Next; + delete(del); + } + Entries=0; + CurIndex=-1; + Head=Tail=Current=NULL; +} + +// When adding into a position, the new node goes at the zero based slot +// specified by pos. All other nodes get moved one slot down. +template +bit8 LinkedList::add(IN T &node,sint32 pos, OUT T **newnodeptr) +{ + LNode *temp; + LNode *item; + + if (pos<0) + pos=0; + if (pos>Entries) + pos=Entries; + + item=(LNode *)new LNode; + assert(item!=NULL); + item->Node=node; // copy the passed in object + item->Prev=NULL; + item->Next=NULL; + + if (newnodeptr) + *newnodeptr=&(item->Node); + + if ((pos==0)||(pos==Entries)) { // Both cases can be true for a new list! + if (pos==0) { + item->Next=Head; + if (Head) + Head->Prev=item; + Head=item; + } + if (pos==Entries) { + item->Prev=Tail; + if (Tail) + Tail->Next=item; + Tail=item; + } + Entries++; + Current=item; + CurIndex=pos; + return(TRUE); + } + + // If control is here, we know the new node is not an endpoint + + // Check for possible speedup, so we don't have to scan the list + if (pos==CurIndex) { + item->Next=Current; + item->Prev=Current->Prev; + Current->Prev=item; + item->Prev->Next=item; + Current=item; + Entries++; + return(TRUE); + } + // Check the other possible speedup (adding after CurIndex) + if (pos==CurIndex+1) { + item->Next=Current->Next; + item->Prev=Current; + Current->Next=item; + item->Next->Prev=item; + Current=item; + CurIndex++; + Entries++; + return(TRUE); + } + + // If control reaches here we have to scan the whole thing + temp=Head->Next; // Can start at node '1' because head was special cased + for (int i=1; iNext; + assert(temp!=NULL); + } + item->Next=temp; + item->Prev=temp->Prev; + temp->Prev=item; + item->Prev->Next=item; + Current=item; + CurIndex=pos; + Entries++; + + return(TRUE); +} + + +// Add to the first node, all others get shifted down one slot +template +bit8 LinkedList::addHead(IN T &node, OUT T **newnodeptr) +{ + return(add(node,0,newnodeptr)); +} + + +// Append to the end of the list +template +bit8 LinkedList::addTail(IN T &node, OUT T **newnodeptr) +{ + return(add(node,length(),newnodeptr)); +} + + +// Remove at the zero based index specified by 'pos'. When removing from +// a slot, all others get shifted up by one. +template +bit8 LinkedList::remove(OUT T &node, sint32 pos) +{ + ////////LNode *temp; + LNode *item; + + if (Entries==0) + return(FALSE); + + if (pos<0) + pos=0; + if (pos>=Entries) + pos=Entries-1; + + if ((pos==0)||(pos==Entries-1)) { // Both can be true for a 1 item list + if (pos==0) { + item=Head; + if (item->Next) + item->Next->Prev=NULL; + Head=item->Next; + node=item->Node; + Current=Head; + CurIndex=0; + } + if (pos==Entries-1) { + item=Tail; + if (item->Prev) + item->Prev->Next=NULL; + Tail=item->Prev; + node=item->Node; + Current=Tail; + CurIndex=Entries-2; + } + delete(item); + Entries--; + + if (Entries==0) { // Super paranoia check + assert(Current==NULL); + assert(CurIndex==-1); + assert(Head==NULL); + assert(Tail==NULL); + } + return(TRUE); + } + // If control is here, we know the target node is not an endpoint + + // Check for possible speedup, so we don't have to scan the list + if (pos==CurIndex) { + item=Current; + item->Prev->Next=item->Next; + item->Next->Prev=item->Prev; + Current=item->Next; + // CurIndex stays the same + node=item->Node; + delete(item); + Entries--; + return(TRUE); + } + + // Check the other possible speedup (removing after CurIndex) + if (pos==CurIndex+1) { + item=Current->Next; + item->Prev->Next=item->Next; + item->Next->Prev=item->Prev; + Current=item->Next; + CurIndex++; + node=item->Node; + delete(item); + Entries--; + return(TRUE); + } + + + // If control reaches here we have to scan the whole thing + item=Head->Next; // Can start at node '1' because head was special cased + for (int i=1; iNext; + assert(item!=NULL); + } + + item->Prev->Next=item->Next; + item->Next->Prev=item->Prev; + Current=item->Next; + CurIndex=pos; + node=item->Node; + delete(item); + Entries--; + + return(TRUE); +} + + + + +// Remove at the zero based index specified by 'pos'. When removing from +// a slot, all others get shifted up by one. +template +bit8 LinkedList::remove(sint32 pos) +{ + T temp_node; + return(remove(temp_node,pos)); +} + + +// Remove the first node of the list +template +bit8 LinkedList::removeHead(OUT T &node) +{ + return(remove(node,0)); +} + + +// Remove the last node of the list +template +bit8 LinkedList::removeTail(OUT T &node) +{ + return(remove(node,Entries-1)); +} + + + +template +bit8 LinkedList::get(OUT T &node, sint32 pos) +{ + T *objptr; + bool retval=getPointer(&objptr,pos); + if (retval && objptr) + node=*objptr; + + return(retval); +} + + +template +bit8 LinkedList::getPointer(OUT T **node,sint32 pos) +{ + if ((node==0)||(Entries==0)) + return(FALSE); + + LNode *item; + + if (pos<0) + { + //pos=0; + return(FALSE); + } + if (pos>=Entries) + { + //pos=Entries-1; + return(FALSE); + } + + if (pos==0) { + *node=&(Head->Node); + return(TRUE); + } else if (pos==Entries-1) { + *node=&(Tail->Node); + return(TRUE); + } + // If control reaches here, we know target is not an endpoint + + // Check for possible speedup, so we don't have to scan the list + if (pos==CurIndex) { + *node=&(Current->Node); + return(TRUE); + } else if (pos==CurIndex+1) { + *node=&(Current->Next->Node); + CurIndex++; + Current=Current->Next; + return(TRUE); + } else if (pos==CurIndex-1) { + *node=&(Current->Prev->Node); + CurIndex--; + Current=Current->Prev; + return(TRUE); + } + + // If control reaches here we have to scan the whole thing + item=Head->Next; // Can start at node '1' because head was special cased + for (int i=1; iNext; + assert(item!=NULL); + } + *node=&(item->Node); + CurIndex=pos; + Current=item; + + return(TRUE); +} + + +// Remove the first node of the list +template +bit8 LinkedList::getHead(OUT T &node) +{ + return(get(node,0)); +} + + +// Remove the last node of the list +template +bit8 LinkedList::getTail(OUT T &node) +{ + return(get(node,Entries-1)); +} + + +template +void LinkedList::print(IN FILE *out) +{ + LNode *temp; + + fprintf(out,"--------------------\n"); + fprintf(out,"Entries = %d\n",length()); + fprintf(out,"H = %8p C = %8p (%d) T = %8p\n",Head,Current,CurIndex,Tail); + + temp=Head; + while (temp) { + fprintf(out," %8p<-((%8p))->%8p \n",temp->Prev,temp,temp->Next); + temp=temp->Next; + } + + fprintf(out,"--------------------\n"); +} + +// Return the current length of the list +template +sint32 LinkedList::length(void) { + return(Entries); +} + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/mboxd.h b/Generals/Code/Tools/matchbot/wlib/mboxd.h new file mode 100644 index 00000000000..4bd1e7f1214 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/mboxd.h @@ -0,0 +1,40 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef MBOXD_HEADER +#define MBOXD_HEADER + +#include "odevice.h" + +class MboxD : public OutputDevice +{ + public: + + virtual int print(const char *str,int len) + { + char *string=new char[len+1]; + memset(string,0,len+1); + memcpy(string,str,len); + MessageBox(NULL,string,"Debug Message", MB_OK | MB_ICONINFORMATION); + delete[](string); + return(len); + } + +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/monod.cpp b/Generals/Code/Tools/matchbot/wlib/monod.cpp new file mode 100644 index 00000000000..5448e627f15 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/monod.cpp @@ -0,0 +1,57 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "monod.h" + +MonoD::MonoD(void) +{ +#ifdef _WIN32 + unsigned long retval; + handle = CreateFile("\\\\.\\MONO", GENERIC_READ|GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) + { + DeviceIoControl(handle, (DWORD)IOCTL_MONO_CLEAR_SCREEN, NULL, 0, NULL, 0, + &retval,0); + } +#endif +} + +MonoD::~MonoD() +{ + #ifdef _WIN32 + CloseHandle(handle); + handle=NULL; + #endif +} + +int MonoD::print(const char *str, int len) +{ + #ifdef _WIN32 + unsigned long retval; + WriteFile(handle, str, len, &retval, NULL); + ////DeviceIoControl(handle, (DWORD)IOCTL_MONO_PRINT_RAW, (void *)str, len, NULL, 0, + //// &retval,0); + return(len); + #else + for (int i=0; i. +*/ + +#ifndef MONOD_HEADER +#define MONOD_HEADER + + +#include +#include +#include "odevice.h" + +///////////////////////// WIN32 ONLY /////////////////////////////////// + +#ifdef _WIN32 +#include +#include + + +/* +** This is the identifier for the Monochrome Display Driver +*/ +#define FILE_DEVICE_MONO 0x00008000 + +/* +** These are the IOCTL commands supported by the Monochrome Display Driver. +*/ +#define IOCTL_MONO_HELP_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x800, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_CLEAR_SCREEN CTL_CODE(FILE_DEVICE_MONO, 0x801, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_PRINT_RAW CTL_CODE(FILE_DEVICE_MONO, 0x802, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_CURSOR CTL_CODE(FILE_DEVICE_MONO, 0x803, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SCROLL CTL_CODE(FILE_DEVICE_MONO, 0x804, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_BRING_TO_TOP CTL_CODE(FILE_DEVICE_MONO, 0x805, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_ATTRIBUTE CTL_CODE(FILE_DEVICE_MONO, 0x806, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_PAN CTL_CODE(FILE_DEVICE_MONO, 0x807, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_LOCK CTL_CODE(FILE_DEVICE_MONO, 0x808, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_UNLOCK CTL_CODE(FILE_DEVICE_MONO, 0x809, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80A, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_RESET_WINDOW CTL_CODE(FILE_DEVICE_MONO, 0x80B, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_SET_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80C, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_CLEAR_FLAG CTL_CODE(FILE_DEVICE_MONO, 0x80D, METHOD_BUFFERED, FILE_WRITE_DATA) +#define IOCTL_MONO_FILL_ATTRIB CTL_CODE(FILE_DEVICE_MONO, 0x80E, METHOD_BUFFERED, FILE_WRITE_DATA) + +#endif // ifdef _WIN32 + +class MonoD : public OutputDevice +{ + public: + MonoD(); + ~MonoD(); + + virtual int print(const char *str,int len); + + private: + #ifdef _WIN32 + HANDLE handle; + #endif +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/odevice.h b/Generals/Code/Tools/matchbot/wlib/odevice.h new file mode 100644 index 00000000000..f04bffaec12 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/odevice.h @@ -0,0 +1,32 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef ODEVICE_HEADER +#define ODEVICE_HEADER + +// This virtual base class provides an interface for output devices +// that can be used for the debugging package. +class OutputDevice +{ + public: + OutputDevice() {} + virtual ~OutputDevice() {}; + virtual int print(const char *s,int len)=0; +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/sem4.cpp b/Generals/Code/Tools/matchbot/wlib/sem4.cpp new file mode 100644 index 00000000000..9c7f7e42a1c --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/sem4.cpp @@ -0,0 +1,182 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/******************************************************************************\ +sem4.cpp Neal Kettler + +Simple Posix semaphore class +This is useful because the constructor will automatically call sem_init + and you don't have to worry about it. It also allows for other semaphore + libraries if you don't have posix. +\******************************************************************************/ + +#include "sem4.h" + +#ifdef _REENTRANT + +Sem4::Sem4() +{ +#ifndef _WINDOWS + sem_init(&sem,1,1); +#else + sem = CreateSemaphore(NULL, 1, 1, NULL); +#endif +} + +Sem4::Sem4(uint32 value) +{ +#ifndef _WINDOWS + sem_init(&sem,1,value); +#else + sem = CreateSemaphore(NULL, value, value, NULL); +#endif +} + +Sem4::~Sem4() +{ +#ifndef _WINDOWS + sem_destroy(&sem); +#else + if (sem) CloseHandle(sem); +#endif +} + +sint32 Sem4::Wait(void) const +{ +#ifndef _WINDOWS + return(sem_wait((sem_t *)&sem)); +#else + if (!sem) + return -1; // no semaphore! + + DWORD dwWaitResult = WaitForSingleObject(sem, INFINITE); + switch (dwWaitResult) { + case WAIT_OBJECT_0: // The semaphore object was signaled. + return 0; + break; + case WAIT_TIMEOUT: // Should not happen ;) + return -1; + break; + } + return -1; +#endif +} + +sint32 Sem4::Post(void) const +{ +#ifndef _WINDOWS + return(sem_post((sem_t *)&sem)); +#else + if (!sem) + return -1; + if (!ReleaseSemaphore(sem, 1 ,NULL)) + return -1; + return 0; +#endif +} + +sint32 Sem4::TryWait(void) const +{ +#ifndef _WINDOWS + return(sem_trywait((sem_t *)&sem)); +#else + if (!sem) + return -1; + DWORD dwWaitResult = WaitForSingleObject(sem, 0L); + switch (dwWaitResult) { + case WAIT_OBJECT_0: // The semaphore object was signaled. + return 0; + break; + case WAIT_TIMEOUT: + return -1; + break; + } + return -1; +#endif +} + +sint32 Sem4::GetValue(int *sval) const +{ +#ifndef _WINDOWS + return(sem_getvalue((sem_t *)&sem,sval)); +#else + if (!sem) + return -1; + long prev; + if (!ReleaseSemaphore(sem, 0, &prev)) + return -1; + if (sval) + *sval = prev; + return 0; +#endif +} + +sint32 Sem4::Destroy(void) +{ +#ifndef _WINDOWS + return(sem_destroy(&sem)); +#else + return CloseHandle(sem); +#endif +} + +#else + +/**************************************************************************** +non threaded versions that do nothing +*****************************************************************************/ + +Sem4::Sem4() +{ +} + +Sem4::Sem4(uint32) +{ +} + +Sem4::~Sem4() +{ +} + +sint32 Sem4::Wait(void) const +{ + return(0); +} + +sint32 Sem4::Post(void) const +{ + return(0); +} + +sint32 Sem4::TryWait(void) const +{ + return(0); +} + +sint32 Sem4::GetValue(int *) const +{ + return(0); +} + +sint32 Sem4::Destroy(void) +{ + return(0); +} + +#endif + diff --git a/Generals/Code/Tools/matchbot/wlib/sem4.h b/Generals/Code/Tools/matchbot/wlib/sem4.h new file mode 100644 index 00000000000..c96f2f7b786 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/sem4.h @@ -0,0 +1,64 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef SEM4_HEADER +#define SEM4_HEADER + +#include +#ifndef _WINDOWS +#include +#endif +#include "wstypes.h" + +#ifdef _REENTRANT +#ifndef _WINDOWS +#include +#else +#include +#endif // _WINDOWS +#endif // _REENTRANT + +// Windows headers have a tendency to redefine IN +#ifdef IN +#undef IN +#endif +#define IN const + +class Sem4 +{ + private: + #ifdef _REENTRANT +#ifndef _WINDOWS + sem_t sem; +#else + HANDLE sem; +#endif + #endif + public: + Sem4(); + Sem4(uint32 value); + ~Sem4(); + + sint32 Wait(void) const; + sint32 TryWait(void) const; + sint32 Post(void) const; + sint32 GetValue(int *sval) const; + sint32 Destroy(void); +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/stderrd.h b/Generals/Code/Tools/matchbot/wlib/stderrd.h new file mode 100644 index 00000000000..4559bea747c --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/stderrd.h @@ -0,0 +1,39 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef STDERRD_HEADER +#define STDERRD_HEADER + +#include "odevice.h" + +class StderrD : public OutputDevice +{ + public: + + virtual int print(const char *str,int len) + { + char *string=new char[len+1]; + memset(string,0,len+1); + memcpy(string,str,len); + fprintf(stderr,"%s",string); + delete[](string); + return(len); + } +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/stdoutd.h b/Generals/Code/Tools/matchbot/wlib/stdoutd.h new file mode 100644 index 00000000000..a3d20fc3c58 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/stdoutd.h @@ -0,0 +1,40 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef STDOUTD_HEADER +#define STDOUTD_HEADER + +#include "odevice.h" + +class StdoutD : public OutputDevice +{ + public: + + virtual int print(const char *str,int len) + { + char *string=new char[len+1]; + memcpy(string,str,len); + string[len]=0; + fprintf(stdout,"%s",string); + fflush(stdout); + delete[](string); + return(len); + } +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/streamer.cpp b/Generals/Code/Tools/matchbot/wlib/streamer.cpp new file mode 100644 index 00000000000..b8a8eca1c1e --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/streamer.cpp @@ -0,0 +1,154 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "streamer.h" +#ifdef _WIN32 + #include +#endif + +#if defined(USING_STLPORT) || (defined(_MSC_VER) && _MSC_VER < 1300) +#define STREAMER_UNBUFFERED unbuffered() +#else +#define STREAMER_UNBUFFERED 0 +#endif + +Streamer::Streamer() : streambuf(), Output_Device(NULL), Buf(NULL) +{ +#if defined(USING_STLPORT) || (defined(_MSC_VER) && _MSC_VER < 1300) + int state=unbuffered(); + unbuffered(0); // 0 = buffered, 1 = unbuffered +#else + static_assert(STREAMER_UNBUFFERED==0, "std::streambuf is assumed to be buffered by default"); +#endif +} + +Streamer::~Streamer() +{ + ///////// calling sync seems to cause crashes here on Win32 + //sync(); + ///////// + if (Buf) + delete[] Buf; +} + +int Streamer::setOutputDevice(OutputDevice *device) +{ + Output_Device=device; + return(0); +} + + +// put n chars from string into buffer +int Streamer::xsputn(const char* buf, int size) //implementation of sputn +{ + if (size<=0) // Nothing to do + return(0); + + const unsigned char *ptr=(const unsigned char *)buf; + for (int i=0; i= epptr()) && (sync()==EOF)) + return(EOF); + else { + sputc(c); + if ((STREAMER_UNBUFFERED && c=='\n' || pptr() >= epptr()) + && sync()==EOF) { + return(EOF); + } + return(c); + } +} + +// This is a write only stream, this should never happen +int Streamer::underflow(void) +{ + return(EOF); +} + +int Streamer::doallocate() +{ + if (Buf==NULL) + { + Buf=new char[(2*STREAMER_BUFSIZ)]; // deleted by destructor + memset(Buf,0,2*STREAMER_BUFSIZ); + + // Buffer +#if defined(USING_STLPORT) || (defined(_MSC_VER) && _MSC_VER < 1300) + setb( + Buf, // base pointer + Buf+STREAMER_BUFSIZ, // ebuf pointer (end of buffer); + 0); // 0 = manual deletion of buff +#else + pubsetbuf(Buf, 2*STREAMER_BUFSIZ); +#endif + + // Get area + setg( + Buf, // eback + Buf, // gptr + Buf); // egptr + + Buf+=STREAMER_BUFSIZ; + // Put area + setp(Buf,Buf+STREAMER_BUFSIZ); + return(1); + } + else + return(0); +} + + +int Streamer::sync() +{ + if (pptr()<=pbase()) { + return(0); + } + + int wlen=pptr()-pbase(); + + if (Output_Device) + { + Output_Device->print(pbase(),wlen); + } + + if (STREAMER_UNBUFFERED) { + setp(pbase(),pbase()); + } + else { + setp(pbase(),pbase()+STREAMER_BUFSIZ); + } + return(0); +} diff --git a/Generals/Code/Tools/matchbot/wlib/streamer.h b/Generals/Code/Tools/matchbot/wlib/streamer.h new file mode 100644 index 00000000000..8dce9298f98 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/streamer.h @@ -0,0 +1,67 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef STREAMER_HEADER +#define STREAMER_HEADER + +#include +#include +#include +#include +#include + +// Windows headers have a tendency to redefine IN +#ifdef IN +#undef IN +#endif +#define IN const + +#include "odevice.h" + +#ifndef STREAMER_BUFSIZ +// This limits the number of characters that can be sent to a single 'print' +// call. If your debug message is bigger than this, it will get split over +// multiple 'print' calls. That's usually not a problem. + #define STREAMER_BUFSIZ 2048 +#endif + + +// Provide a streambuf interface for a class that can 'print' +class Streamer : public streambuf +{ + public: + Streamer(); + virtual ~Streamer(); + + int setOutputDevice(OutputDevice *output_device); + + protected: + // Virtual methods from streambuf + int xsputn(const char* s, int n); // buffer some characters + int overflow(int = EOF); // flush buffer and make more room + int underflow(void); // Does nothing + int sync(); + + int doallocate(); // allocate a buffer + + + OutputDevice *Output_Device; + char* Buf; +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/syslogd.cpp b/Generals/Code/Tools/matchbot/wlib/syslogd.cpp new file mode 100644 index 00000000000..7568868b240 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/syslogd.cpp @@ -0,0 +1,39 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "syslogd.h" + +SyslogD::SyslogD(char *ident,int logopt,int facility,int _priority) +{ +#ifndef _WINDOWS + openlog(ident,logopt,facility); + priority=_priority; +#endif +} + +int SyslogD::print(const char *str, int len) +{ +#ifndef _WINDOWS + char *temp_str=new char[len+1]; + memset(temp_str,0,len+1); + strncpy(temp_str,str,len); + syslog(priority,temp_str); + delete[](temp_str); +#endif + return(len); +} diff --git a/Generals/Code/Tools/matchbot/wlib/syslogd.h b/Generals/Code/Tools/matchbot/wlib/syslogd.h new file mode 100644 index 00000000000..569f2b1d5bc --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/syslogd.h @@ -0,0 +1,48 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef SYSLOGD_HEADER +#define SYSLOGD_HEADER + +#include +#include +#ifndef _WINDOWS +#include +#endif +#include + +// Windows headers have a tendency to redefine IN +#ifdef IN +#undef IN +#endif +#define IN const + +#include "odevice.h" + +// Windows doesn't have a syslog equivalent (does it?), so this class does little there +class SyslogD : public OutputDevice +{ + public: + SyslogD(char *ident,int logopt,int facility,int priority); + virtual int print(const char *str,int len); + + private: + int priority; +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/threadfac.cpp b/Generals/Code/Tools/matchbot/wlib/threadfac.cpp new file mode 100644 index 00000000000..0f42558536a --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/threadfac.cpp @@ -0,0 +1,221 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#define THREADFAC_CODE + +#include "threadfac.h" + +int Runnable::ThreadCount_ = 0; +CritSec Runnable::CritSec_; // to protect ThreadCount_ + +// MDC: Made all this dependent on _REENTRANT being defined so VC++ doesn't complain on +// single-threaded programs... + + +// +// Note: I chose the following type signature for thread functions +// void function(void *data); +// +// +// Since Win32 & POSIX have different type signatures for the thread entry points +// an intermediate system-dependent function in this file gets called first. +// That function then calls the system independent version. So the system dependent +// version needs 2 Items 1) The address of the _real_ thread func 2) the data +// to pass. We only have 1 argument available, so you figure out the rest... +// +// This is for internal use only +// +struct ThreadInformation +{ + void *startPoint; // The address of the _real_ thread function, or class + void *data; // data to pass to real thread function or class + bit8 destroy; // only applies to classes, should delete after execution? +}; + + + +// +// Start a thread inside a class +// +bit8 ThreadFactory::startThread(Runnable &runable, void *data, bit8 destroy) +{ +#ifdef _REENTRANT + + { + Runnable::CritSec_.lock(); + Runnable::ThreadCount_++; + Runnable::CritSec_.unlock(); + } + + + ThreadInformation *tInfo=new ThreadInformation; + tInfo->startPoint=(void *)&runable; + tInfo->data=data; + tInfo->destroy=destroy; + + #ifdef _WIN32 + // Under windows call _beginthreadex instead of CreateThread so you can + // use all the normal C library stuff. (IMPORTANT!!!) + uint32 handle; + uint32 stup1d; + handle=_beginthreadex(NULL,0, threadClassLauncher, tInfo, 0, &stup1d); + if (handle!=NULL) + return(TRUE); + else + { + { + runable.CritSec_.lock(); + runable.ThreadCount_--; // Ok, so it didn't really start + runable.CritSec_.unlock(); + } + return(FALSE); + } + #else // UNIX + // Setup thread attributes for client threads + int retval; + pthread_attr_t threadAttr; + pthread_attr_init(&threadAttr); + pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); + pthread_attr_setscope(&threadAttr,PTHREAD_SCOPE_SYSTEM); + retval=pthread_create(NULL,&threadAttr, threadClassLauncher, tInfo); + if (retval==0) + return(TRUE); + else + { + { + runable.CritSec_.lock(); + runable.ThreadCount_--; // Ok, so it didn't really start + runable.CritSec_.unlock(); + } + return(FALSE); + } + #endif +#else + return (FALSE); +#endif /* _REENTRANT */ +} + + +// +// Start a thread inside a function +// +bit8 ThreadFactory::startThread(void (*start_func)(void *), void *data) +{ +#ifdef _REENTRANT + ThreadInformation *tInfo=new ThreadInformation; + tInfo->startPoint=start_func; + tInfo->data=data; + + #ifdef _WIN32 + // Under windows call _beginthreadex instead of CreateThread so you can + // use all the normal C library stuff. (IMPORTANT!!!) + uint32 handle; + unsigned temp; + handle=_beginthreadex(NULL,0, threadFuncLauncher, tInfo, 0, &temp); + if (handle!=NULL) + return(TRUE); + return(FALSE); + #else // UNIX + // Setup thread attributes for client threads + int retval; + pthread_attr_t threadAttr; + pthread_attr_init(&threadAttr); + pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); + pthread_attr_setscope(&threadAttr,PTHREAD_SCOPE_SYSTEM); + retval=pthread_create(NULL,&threadAttr, threadFuncLauncher, tInfo); + if (retval==0) + return(TRUE); + else + return(FALSE); + #endif +#else + return(FALSE); +#endif /* REENTRANT */ +} + +#ifdef _WIN32 + unsigned __stdcall threadFuncLauncher(void *temp) +#else // UNIX + void *threadFuncLauncher(void *temp) +#endif +{ + ThreadInformation *tInfo=(ThreadInformation *)temp; + + void (*start_func)(void *); + start_func=(void (*)(void *))tInfo->startPoint; + + void *data=tInfo->data; + delete(tInfo); + + start_func(data); + return(0); +} + +#ifdef _WIN32 + unsigned __stdcall threadClassLauncher(void *temp) +#else // UNIX + void *threadClassLauncher(void *temp) +#endif +{ + ThreadInformation *tInfo=(ThreadInformation *)temp; + + Runnable *thrClass=(Runnable *)tInfo->startPoint; + void *data=tInfo->data; + bit8 destroy=tInfo->destroy; + delete(tInfo); + + thrClass->run(data); + + if (destroy) // May want to free memory after thread finishes + delete(thrClass); + + { + Runnable::CritSec_.lock(); + Runnable::ThreadCount_--; + Runnable::CritSec_.unlock(); + } + + #ifdef _WIN32 + ExitThread(0); // is this really needed? + #endif + return(0); +} + +Runnable::Runnable() +{ } + +Runnable::~Runnable() +{ } + +// Is there a thread running in this class +bit8 Runnable::isRunning(void) +{ + // Don't need to lock a simple assignment + int temp=ThreadCount_; + return((temp>0)?TRUE:FALSE); +} + +// How many threads are running in this class +int Runnable::getThreadCount(void) +{ + // Don't need to lock a simple assignment + int temp=ThreadCount_; + return(temp); +} + +#undef THREADFAC_CODE diff --git a/Generals/Code/Tools/matchbot/wlib/threadfac.h b/Generals/Code/Tools/matchbot/wlib/threadfac.h new file mode 100644 index 00000000000..a4edbc36804 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/threadfac.h @@ -0,0 +1,124 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +// +// Platform independent thread creation (Win32 & POSIX) +// + +#ifndef THREADFAC_HEADER +#define THREADFAC_HEADER + +#ifdef _WIN32 + #include +#endif +#include "wstypes.h" +#include + +#ifdef _WIN32 + #include +#else // UNIX + #include +#endif + +// Windows headers have a tendency to redefine IN +#ifdef IN +#undef IN +#endif +#define IN const + +#include "critsec.h" + + + +#ifdef THREADFAC_CODE + // This is the fake thread entry point for functions + #ifdef _WIN32 + static unsigned __stdcall threadFuncLauncher(void *temp); + #else // UNIX + static void *threadFuncLauncher(void *temp); + #endif + + // Fake entry point for classes + #ifdef _WIN32 + static unsigned __stdcall threadClassLauncher(void *temp); + #else // UNIX + static void *threadClassLauncher(void *temp); + #endif +#endif + + + + + +// Forward definition of base class for threaded classes +class Runnable; + +// +// Call the static method startThread to begin a new thread. +// +class ThreadFactory +{ + public: + static bit8 startThread(void (*start_func)(void *), void *data); + static bit8 startThread(Runnable &runable, void *data, bit8 destroy=FALSE); +}; + + + +// +// Base class for when you want a thread to execute inside a class +// instead of a function. +// +class Runnable +{ + public: + Runnable(); + virtual ~Runnable(); + + + // ThreadFactory needs to be able to access the private + // IsRunning_ field. + friend class ThreadFactory; + + // So do the threadClassLaunchers + #ifdef _WIN32 + friend static unsigned __stdcall threadClassLauncher(void *temp); + #else // UNIX + friend void *threadClassLauncher(void *temp); + #endif + + virtual void run(void *data)=0; // Thread entry point + + void startThread(void *data,bit8 destroy=FALSE) // nice way to start a thread + { + ThreadFactory::startThread(*this,data,destroy); + }; + + // Is there a thread running in this class? + static bit8 isRunning(void); + + // Get the count of threads running inside this class + static int getThreadCount(); + + + private: + static int ThreadCount_; + static CritSec CritSec_; // to protect ThreadCount_ +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/threadsafe.h b/Generals/Code/Tools/matchbot/wlib/threadsafe.h new file mode 100644 index 00000000000..8a2addac60f --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/threadsafe.h @@ -0,0 +1,62 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +// +// threadsafe.h +// +// If you include this file and call non-threadsafe functions, it'll +// prevent your program from compiling. It's meant to be a slap +// on the wrist in case you forget and call an unsafe function +// from a threadsafe program. +// + +// +// Reminder to self - use sigwait, not signal handlers! +// + +#ifdef _REENTRANT + +#ifndef THREADSAFE_HEADER +#define THREADSAFE_HEADER + +#define strtok ("strtok() is not MT-SAFE!") +#define ascctime ("asctime() is not MT-SAFE!") +// Can't just do ctime, as Windows' objidl.h uses it as a FILETIME thingie +#define ctime(x) ("ctime() is not MT-SAFE!") +#define gmtime ("gmtime() is not MT-SAFE!") +#define localtime ("localtime() is not MT-SAFE!") +#define tzset ("tzset() is not MT-SAFE!") +#define tzsetwall ("tzsetwall() is not MT-SAFE!") +#define readdir ("readdir() is not MT-SAFE!") + +#define rand ("rand() is not MT-SAFE!") +#define srand ("srand() is not MT-SAFE!") +#define random ("random() is not MT-SAFE!") +#define srandom ("srandom() is not MT-SAFE!") + +#define tmpnam ("tmpnam() is not MT-SAFE!") +#define vfork ("vfork() is not MT-SAFE!") + +#define system ("system() is not MT-SAFE!") +#define popen ("popen() is not MT-SAFE!") +#define pclose ("pclose() is not MT-SAFE!") +#define ctermid ("ctermid() is not MT-SAFE!") +#define getlogin ("getlogin() is not MT-SAFE!"); + +#endif // THREADSAFE_HEADER +#endif // _REENTRANT diff --git a/Generals/Code/Tools/matchbot/wlib/timezone.cpp b/Generals/Code/Tools/matchbot/wlib/timezone.cpp new file mode 100644 index 00000000000..6e191ebb17f --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/timezone.cpp @@ -0,0 +1,71 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "wlib/xtime.h" +#include "timezone.h" + +void GetTimezoneInfo(const char * &timezone_str, int &timezone_offset) { + timezone_str = "Unknown Timezone"; + timezone_offset = 0; +#ifdef _WINDOWS + struct _timeb wintime; + _ftime(&wintime); + + if (wintime.dstflag) { + // Daylight savings time + if (_daylight) { + timezone_str = _tzname[1]; + } + } else { + timezone_str = _tzname[0]; + } + timezone_offset = wintime.timezone * 60; // its in minutes... + +#endif +#ifndef _WINDOWS + struct timeval unixtime; + struct timezone unixtzone; + gettimeofday(&unixtime,&unixtzone); + + struct tm unixtm; + localtime_r(&unixtime.tv_sec, &unixtm); + + if (unixtm.tm_isdst) { + // Daylight savings time + if (daylight) timezone_str = tzname[1]; + timezone_offset = altzone; + } else { + timezone_str = tzname[0]; + timezone_offset = timezone; + } +#endif +} + +const char * TimezoneString(void) { + const char *timezone_str; + int timezone_offset; + GetTimezoneInfo(timezone_str, timezone_offset); + return timezone_str; +} + +int TimezoneOffset(void) { + const char *timezone_str; + int timezone_offset; + GetTimezoneInfo(timezone_str, timezone_offset); + return timezone_offset; +} diff --git a/Generals/Code/Tools/matchbot/wlib/timezone.h b/Generals/Code/Tools/matchbot/wlib/timezone.h new file mode 100644 index 00000000000..95e4c37f384 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/timezone.h @@ -0,0 +1,41 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +timezone Matthew D. Campbell + +This is just a couple of convenience functions for determining what timezone +we are now in. It even accounts for daylight savings! One caveat is that it +only tells you info about what the daylight savings info is now, not 5 minutes +from now, not 2 hours ago. Oh well. +\****************************************************************************/ + +#ifndef _TIMEZONE_H_ +#define _TIMEZONE_H_ + +// Just fill in both the timezone description and its offset from GMT +void GetTimezoneInfo(const char * &timezone_str, int &timezone_offset); + +// Returns the description of the current timezone (daylight savings included) +const char * TimezoneString(void); + +// Returns the offset from GMT of the current timezone +int TimezoneOffset(void); + +#endif // _TIMEZONE_H_ + diff --git a/Generals/Code/Tools/matchbot/wlib/ustring.h b/Generals/Code/Tools/matchbot/wlib/ustring.h new file mode 100644 index 00000000000..12fee83b683 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/ustring.h @@ -0,0 +1,61 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#ifndef USTRING_HEADER +#define USTRING_HEADER + +#include +#include +#include +#include + +// Windows headers have a tendency to redefine IN +#ifdef IN +#undef IN +#endif +#define IN const + +#define MAX_BYTES_PER_CHAR 1 + +template +class UstringT : public basic_string > +{ + public: + explicit UstringT(int max_charlength) { + set_max_bytelength(max_charlength*MAX_BYTES_PER_CHAR); + } + + UstringT() { max_bytelength=4000; } + + size_t get_max_bytelength(void) { return(max_bytelength); } + void set_max_bytelength(size_t max) { max_bytelength=max; } + + bool operator==(const UstringT &other) + { + const basic_string > *other_basic=&other; + const basic_string > *this_basic=this; + return((*other_basic)==(*this_basic)); + } + + private: + size_t max_bytelength; +}; + +typedef UstringT Ustring; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/wdebug.cpp b/Generals/Code/Tools/matchbot/wlib/wdebug.cpp new file mode 100644 index 00000000000..8e2d14f7e47 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/wdebug.cpp @@ -0,0 +1,190 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include +#include "wdebug.h" +#include "streamer.h" +#include "odevice.h" + + +static MsgManager *msg_manager=NULL; + +static int debug_enabled=0; +static ostream *debug_ostream=NULL; +static Streamer debug_streamer; + +static int info_enabled=0; +static ostream *info_ostream=NULL; +static Streamer info_streamer; + +static int warn_enabled=0; +static ostream *warn_ostream=NULL; +static Streamer warn_streamer; + +static int error_enabled=0; +static ostream *error_ostream=NULL; +static Streamer error_streamer; + + +// Don't dare touch this semaphore in application code! +#ifdef USE_DEBUG_SEM +Sem4 DebugLibSemaphore; +#else +CritSec DebugLibSemaphore; +#endif + + +int MsgManager::setAllStreams(OutputDevice *device) +{ + if (device==NULL) + return(1); + + DEBUGLOCK; + debug_streamer.setOutputDevice(device); + delete(debug_ostream); + debug_ostream=new ostream(&debug_streamer); + + info_streamer.setOutputDevice(device); + delete(info_ostream); + info_ostream=new ostream(&info_streamer); + + warn_streamer.setOutputDevice(device); + delete(warn_ostream); + warn_ostream=new ostream(&warn_streamer); + + error_streamer.setOutputDevice(device); + delete(error_ostream); + error_ostream=new ostream(&error_streamer); + + DEBUGUNLOCK; + + return(0); +} + + +int MsgManager::ReplaceAllStreams(FileD * output_device, IN char *device_filename, IN char *copy_filename) +{ + DebugLibSemaphore.Wait(); + + delete(debug_ostream); + delete(info_ostream); + delete(warn_ostream); + delete(error_ostream); + + if (output_device != NULL) + { + delete(output_device); + output_device = NULL; + } + + rename(device_filename, copy_filename); + +// FileD new_device(device_filename); + output_device = new FileD(device_filename); + + debug_streamer.setOutputDevice(output_device); + debug_ostream = new ostream(&debug_streamer); + + info_streamer.setOutputDevice(output_device); + info_ostream=new ostream(&info_streamer); + + warn_streamer.setOutputDevice(output_device); + warn_ostream = new ostream(&warn_streamer); + + error_streamer.setOutputDevice(output_device); + error_ostream = new ostream(&error_streamer); + + DebugLibSemaphore.Post(); + + return(0); +} + + +int MsgManager::setDebugStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + DEBUGLOCK; + debug_streamer.setOutputDevice(device); + delete(debug_ostream); + debug_ostream=new ostream(&debug_streamer); + DEBUGUNLOCK; + return(0); +} + +int MsgManager::setInfoStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + DEBUGLOCK; + info_streamer.setOutputDevice(device); + delete(info_ostream); + info_ostream=new ostream(&info_streamer); + DEBUGUNLOCK; + return(0); +} + +int MsgManager::setWarnStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + DEBUGLOCK; + warn_streamer.setOutputDevice(device); + delete(warn_ostream); + warn_ostream=new ostream(&warn_streamer); + DEBUGUNLOCK; + return(0); +} + +int MsgManager::setErrorStream(OutputDevice *device) +{ + if (device==NULL) + return(1); + + DEBUGLOCK; + error_streamer.setOutputDevice(device); + delete(error_ostream); + error_ostream=new ostream(&error_streamer); + DEBUGUNLOCK; + return(0); +} + + + +ostream *MsgManager::debugStream(void) +{ + return(debug_ostream); +} + +ostream *MsgManager::infoStream(void) +{ + return(info_ostream); +} + +ostream *MsgManager::warnStream(void) +{ + return(warn_ostream); +} + +ostream *MsgManager::errorStream(void) +{ + return(error_ostream); +} diff --git a/Generals/Code/Tools/matchbot/wlib/wdebug.h b/Generals/Code/Tools/matchbot/wlib/wdebug.h new file mode 100644 index 00000000000..a37c9ee75ff --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/wdebug.h @@ -0,0 +1,314 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*****************************************************************************\ +wdebug Neal Kettler + +MT-LEVEL + MT-Safe + +The debugging module is pretty good for debugging and it has some message +printing stuff as well. The basic idea is that you write a class that +inherits from OutputDevice (several are provided) and assign that output +device to a stream. There are seperate streams for debugging, information, +warning, and error messages. Each one can have a seperate output device, +or they can all have the same one. Debugging messages only get compiled +in if your module defines 'DEBUG'. If you don't define debug, then not even +the text of the debugging message gets into the binary. All the other +output streams get printed regardless of whether DEBUG is defined. + +Sample usage: +FileD debug_device("gameres.debug"); // create a file device +MsgManager::setDebugStream(&debug_device); +DBGMSG("This debug message #" << 1 << " you use C++ streams"); + +Note that since these are defines you really don't need to put a semicolon +at the end, and it can be bad in situations like this: + +if (x) + DBGMSG("Stuff is broken"); +else + DBGMSG("Stuff is NOT broken"); + +This won't compile, read the code until you figure it out. Only then +will you be ready to leave grasshopper. + +\*****************************************************************************/ + +#ifndef WDEBUG_HEADER +#define WDEBUG_HEADER + +#define USE_DEBUG_SEM + +#include "wstypes.h" + +#include +#include + +#if !defined(_WINDOWS) +// Windows headers have a tendency to redefine IN +#ifdef IN +#undef IN +#endif +#define IN const +#endif + +#ifdef USE_DEBUG_SEM +#include "sem4.h" +#else +#include "critsec.h" +#endif +#include "odevice.h" +#include "streamer.h" +#include "xtime.h" +#include "timezone.h" // MDC +#include "filed.h" + +// This is needed because the streams return a pointer. Every time you +// change the output device the old stream is deleted, and a new one +// is created. +// MDC: Added a macro to switch between semaphores & critsecs to debug a +// problem in Win32. + +#ifdef USE_DEBUG_SEM +extern Sem4 DebugLibSemaphore; +#define DEBUGLOCK DebugLibSemaphore.Wait() +#define DEBUGUNLOCK DebugLibSemaphore.Post() +#else +extern CritSec DebugLibSemaphore; +#define DEBUGLOCK DebugLibSemaphore.lock() +#define DEBUGUNLOCK DebugLibSemaphore.unlock() +#endif + +// Print an information message +#define INFMSG(X)\ +{\ + char timebuf[40]; \ + Xtime now; \ + now -= TimezoneOffset(); \ + now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \ + DEBUGLOCK; \ + if (MsgManager::infoStream()) \ + (*(MsgManager::infoStream())) << "INF " << timebuf << " [" << \ + __FILE__ << " " << __LINE__ << "] " << X << endl; \ + DEBUGUNLOCK; \ +} + +// Print a warning message +#define WRNMSG(X)\ +{\ + char timebuf[40]; \ + Xtime now; \ + now -= TimezoneOffset(); \ + now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \ + DEBUGLOCK; \ + if (MsgManager::warnStream()) \ + (*(MsgManager::warnStream())) << "WRN " << timebuf << " [" << \ + __FILE__ << " " << __LINE__ << "] " << X << endl; \ + DEBUGUNLOCK; \ +} + +// Print an error message +#define ERRMSG(X)\ +{\ + char timebuf[40]; \ + Xtime now; \ + now -= TimezoneOffset(); \ + now.FormatTime(timebuf, "mm/dd/yy hh:mm:ss"); \ + DEBUGLOCK; \ + if (MsgManager::errorStream()) \ + (*(MsgManager::errorStream())) << "ERR " << timebuf << " [" << \ + __FILE__ << " " << __LINE__ << "] " << X << endl; \ + DEBUGUNLOCK; \ +} + + +// Just get a stream to the information device, no extra junk +#define INFSTREAM(X)\ +{\ + DEBUGLOCK; \ + if (MsgManager::infoStream()) \ + (*(MsgManager::infoStream())) << X;\ + DEBUGUNLOCK; \ +} + +// Just get a stream to the warning device, no extra junk +#define WRNSTREAM(X)\ +{\ + DEBUGLOCK; \ + if (MsgManager::warnStream()) \ + (*(MsgManager::warnStream())) << X;\ + DEBUGUNLOCK; \ +} + +// Just get a stream to the error device, no extra junk +#define ERRSTREAM(X)\ +{\ + DEBUGLOCK; \ + if (MsgManager::errorStream()) \ + (*(MsgManager::errorStream())) << X;\ + DEBUGUNLOCK; \ +} + +#ifndef DEBUG + +// No debugging, no debug messages. +// Note that anything enclosed in "DBG()" will NOT get executed +// unless DEBUG is defined. +// They are defined to {} for consistency when DEBUG is defined + +#define DBG(X) +#define DBGSTREAM(X) {} +#define PVAR(v) {} +#define DBGMSG(X) {} +#define VERBOSE(X) {} + +#else // DEBUG _is_ defined + +// Execute only if in debugging mode +#define DBG(X) X + +// In Windows, send a copy to the debugger window +#ifdef _WINDOWS + +// Print a variable +#define PVAR(v) \ +{ \ + DEBUGLOCK; \ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ + "]: " << ##V << " = " << V << endl; \ + strstream __s;\ + __s << __FILE__ << "[" << __LINE__ << \ + "]: " << ##V << " = " << V << '\n' << '\0';\ + OutputDebugString(STRSTREAM_CSTR(__s));\ + DEBUGUNLOCK; \ +} + + +#define DBGMSG(X)\ +{\ + DEBUGLOCK; \ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << "DBG [" << __FILE__ << \ + " " << __LINE__ << "] " << X << endl;\ + strstream __s;\ + __s << "DBG [" << __FILE__ << \ + " " << __LINE__ << "] " << X << '\n' << '\0';\ + OutputDebugString(STRSTREAM_CSTR(__s));\ + DEBUGUNLOCK; \ +} + +// Just get a stream to the debugging device, no extra junk +#define DBGSTREAM(X)\ +{\ + DEBUGLOCK; \ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << X;\ + strstream __s;\ + __s << X << '\0';\ + OutputDebugString(STRSTREAM_CSTR(__s));\ + DEBUGUNLOCK; \ +} + +// Verbosely execute a statement +#define VERBOSE(X)\ +{ \ + DEBUGLOCK; \ + if (MsgManager::debugStream()) \ + (*(DebugManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ + "]: " << ##X << endl; X \ + strstream __s;\ + __s << __FILE__ << "[" << __LINE__ << \ + "]: " << ##X << '\n' << '\0';\ + OutputDebugString(STRSTREAM_CSTR(__s));\ + DEBUGUNLOCK; \ +} + +#else // _WINDOWS + +// Print a variable +#define PVAR(v) \ +{ \ + DEBUGLOCK; \ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ + "]: " << ##V << " = " << V << endl; \ + DEBUGUNLOCK; \ +} + + +#define DBGMSG(X)\ +{\ + DEBUGLOCK; \ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << "DBG [" << __FILE__ << \ + " " << __LINE__ << "] " << X << endl;\ + DEBUGUNLOCK; \ +} + +// Just get a stream to the debugging device, no extra junk +#define DBGSTREAM(X)\ +{\ + DEBUGLOCK; \ + if (MsgManager::debugStream()) \ + (*(MsgManager::debugStream())) << X;\ + DEBUGUNLOCK; \ +} + +// Verbosely execute a statement +#define VERBOSE(X)\ +{ \ + DEBUGLOCK; \ + if (MsgManager::debugStream()) \ + (*(DebugManager::debugStream())) << __FILE__ << "[" << __LINE__ << \ + "]: " << ##X << endl; X \ + DEBUGUNLOCK; \ +} +#endif // _WINDOWS + +#endif // DEBUG + +//#undef DEBUGLOCK +//#undef DEBUGUNLOCK + +class MsgManager +{ + protected: + MsgManager(); + + public: + static int setAllStreams(OutputDevice *device); + static int ReplaceAllStreams(FileD *output_device, IN char *device_filename, IN char *copy_filename); + static int setDebugStream(OutputDevice *device); + static int setInfoStream(OutputDevice *device); + static int setWarnStream(OutputDevice *device); + static int setErrorStream(OutputDevice *device); + + static void enableDebug(int flag); + static void enableInfo(int flag); + static void enableWarn(int flag); + static void enableError(int flag); + + static ostream *debugStream(void); + static ostream *infoStream(void); + static ostream *warnStream(void); + static ostream *errorStream(void); +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/wstring.cpp b/Generals/Code/Tools/matchbot/wlib/wstring.cpp new file mode 100644 index 00000000000..e7fa32cff35 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/wstring.cpp @@ -0,0 +1,597 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * +****************************************************************************** +Project Name: Carpenter (The RedAlert ladder creator) +File Name : string.cpp +Author : Neal Kettler +Start Date : June 1, 1997 +Last Update : June 17, 1997 + +A fairly typical string class. This string class always copies any input +string to it's own memory (for assignment or construction). +\***************************************************************************/ + +#include +#include +#include +#include +#include + +#include "wstring.h" + +#define PADSIZE 32 // include a little padding on alloc for future growth + +Wstring::Wstring() : str(NULL), strsize(0) +{ } + +Wstring::Wstring(const char *string):str(NULL), strsize(0) +{ set(string); } + +Wstring::Wstring(const Wstring &other):str(NULL), strsize(0) +{ + if (other.str!=NULL) + { + str=new char[strlen(other.str)+PADSIZE+1]; + strsize=strlen(other.str)+PADSIZE+1; + strcpy(str,other.str); + } +} + +Wstring::~Wstring() +{ + clear(); +} + +bool Wstring::operator<(const Wstring &other) const +{ + if (str == NULL && other.str == NULL) + return false; + + if (str == NULL) + return true; + + return ( strcmp(str, other.str) < 0 ); +} + +bit8 Wstring::operator==(const char *other) const +{ + if ((str==NULL)&&(other==NULL)) + return(TRUE); + if(strcmp(str, other) != 0) + return(FALSE); + else + return(TRUE); +} + +bit8 Wstring::operator==(const Wstring &other) const +{ + if((str == NULL) && (other.str == NULL)) + return(TRUE); + + if((str == NULL) || (other.str == NULL)) + return(FALSE); + + if(strcmp(str, other.str) != 0) + return(FALSE); + else + return(TRUE); +} + + +bit8 Wstring::operator!=(const char *other) const +{ + if(strcmp(str, other) != 0) + return(TRUE); + else + return(FALSE); +} + + +bit8 Wstring::operator!=(const Wstring &other) const +{ + if((str == NULL) && (other.str == NULL)) + return(FALSE); + + if((str == NULL) || (other.str == NULL)) + return(TRUE); + + if(strcmp(str, other.str) != 0) + return(TRUE); + else + return(FALSE); +} + + +Wstring &Wstring::operator=(const char *other) +{ + set(other); + return(*this); +} + + +Wstring &Wstring::operator=(const Wstring &other) +{ + if(*this == other) + return(*this); + + set(other.get()); + return(*this); +} + + +bit8 Wstring::cat(const char *s) +{ + uint32 len; + + if (s==NULL) // it's OK to cat nothing + return(TRUE); + + // Determine the length of the resultant string. + len = strlen(s) + 1; + if(str) + len += strlen(str); + + // Space check + strgrow(len); + + strcat(str, s); + + return(TRUE); +} + + +bit8 Wstring::cat(uint32 size, const char *s) +{ + uint32 len; + + // Determine the length of the resultant string. + len = size + 1; + if(str) + len += strlen(str); + + // Allocate memory for the new string. + strgrow(len); + + strncat(str, s, size); + str[len-1]=0; // make sure null term'd + + return(TRUE); +} + +bit8 Wstring::cat(const Wstring &other) +{ + return cat(other.get()); +} + +Wstring &Wstring::operator+=(const char *string) +{ + cat(string); + return(*this); +} + +Wstring &Wstring::operator+=(const Wstring &other) +{ + cat(other.get()); + return(*this); +} + +Wstring Wstring::operator+(const char *string) +{ + Wstring temp = *this; + temp.cat(string); + return(temp); +} + +Wstring Wstring::operator+(const Wstring &s) +{ + Wstring temp = *this; + temp.cat(s); + return(temp); +} + +// +// This function deletes 'count' characters indexed by `pos' from the Wstring. +// If `pos'+'count' is > the length of the array, the last 'count' characters +// of the string are removed. If an error occurs, FALSE is returned. +// Otherwise, TRUE is returned. Note: count has a default value of 1. +// +// +char Wstring::remove(sint32 pos,sint32 count) +{ + //char *s; + sint32 len; + + len = (sint32)strlen(str); + + if(pos+count > len) + pos = len - count; + if (pos < 0) + { + count+=pos; // If they remove before 0, ignore up till beginning + pos=0; + } + if (count<=0) + return(FALSE); + + memmove(str+pos,str+pos+count,len-pos-count+1); + + return(TRUE); +} + +// Remove all instances of a char from the string +bit8 Wstring::removeChar(char c) +{ + int len=0; + char *cptr=NULL; + bit8 removed=FALSE; + + if (str==NULL) + return(FALSE); + + len=strlen(str); + while ((cptr=strchr(str,c)) !=NULL) + { + memmove(cptr,cptr+1,len-1-((int)(cptr-str))); + len--; + str[len]=0; + removed=TRUE; + } + return(removed); +} + +void Wstring::removeSpaces(void) +{ + removeChar(' '); + removeChar('\t'); +} + +void Wstring::clear(void) +{ + if(str) + delete[](str); + strsize=0; + str=NULL; +} + +// This is usually used for raw storage instead of string ops... +void Wstring::setSize(sint32 size) +{ + clear(); + if (size<0) + return; + + str=new char[size]; + strsize=size; + memset(str,0,size); +} + +void Wstring::cellCopy(char *dest, uint32 len) +{ + uint32 i; + + strncpy(dest, str, len); + for(i = (uint32)strlen(str); i < len; i++) + dest[i] = ' '; + dest[len] = 0; +} + +const char *Wstring::get(void) const +{ + if(!str) + return ""; + return str; +} + +char Wstring::get(uint32 index) const +{ + if(index < strlen(str)) + return str[index]; + return(0); +} + +uint32 Wstring::length(void) const +{ + if(str == NULL) + return(0); + return((uint32)strlen(str)); +} + + +// Insert at given position and shift old stuff to right +bit8 Wstring::insert(const char *instring, uint32 pos) +{ + if (str==NULL) + return(set(instring)); + if (pos>strlen(str)) + pos=strlen(str); + + strgrow(strlen(str)+strlen(instring)+1); + memmove(str+pos+strlen(instring),str+pos,strlen(str)-pos+1); + memmove(str+pos,instring,strlen(instring)); + + return(TRUE); +} + +// This function inserts the character specified by `k' into the string at the +// position indexed by `pos'. If `pos' is >= the length of the string, it is +// appended to the string. If an error occurs, FALSE is returned. Otherwise, +// TRUE is returned. +bit8 Wstring::insert(char k, uint32 pos) +{ + char temp[2]; + temp[0]=k; + temp[1]=0; + return(insert(temp,pos)); +} + + +// Joe Howes (05/19/2000): This function inserts commas to nicely format a +// large number (i.e. 1234567890 -> 1,234,567,890). It doesn't really care +// if the string is really a number or not. +bit8 Wstring::beautifyNumber() +{ + int len = length(); + int accum = 3 - (len % 3); + int numcommas = 0; + + + if( accum == 3 ) accum = -1; + for(int i = 0; i < len; i++) + { + if( accum == 3 ) + { + insert(',', i + numcommas); + numcommas++; + } + accum = ( accum == 3 || accum == -1 ) ? 1 : accum + 1; + } + + return(TRUE); +} + + +// This function replaces any occurences of the string pointed to by +// `replaceThis' with the string pointed to by `withThis'. If an error +// occurs, FALSE is returned. Otherwise, TRUE is returned. +bit8 Wstring::replace(const char *replaceThis,const char *withThis) +{ + Wstring dest; + const char *foundStr, *src; + uint32 len; + + src=get(); + while(src && src[0]) + { + foundStr = strstr(src, replaceThis); + if(foundStr) + { + len = (uint32)foundStr - (uint32)src; + if(len) + { + if(!dest.cat(len, src)) + return(FALSE); + } + if(!dest.cat(withThis)) + return(FALSE); + src = foundStr + strlen(replaceThis); + } + else + { + if(!dest.cat(src)) + return(FALSE); + + src=NULL; + } + } + return(set(dest.get())); +} + + +bit8 Wstring::set(const char *s) +{ + //uint32 len; + + strgrow(strlen(s)+1); + strcpy(str,s); + + return(TRUE); +} + + +bit8 Wstring::set(char c, uint32 index) +{ + if(index >= (uint32)strlen(str)) + return FALSE; + + str[index] = c; + + return TRUE; +} + + +char Wstring::set(uint32 size, const char *string) +{ + //uint32 len; + + strgrow(size+1); + strncpy(str,string,size); + str[size]=0; + + return(TRUE); +} + + +// Added by Joe Howes. Takes a printf formatted string and a set of args. +// The expanded string must not exceed 1k or twice the length of the format +// string, whichever is larger. It would probably be better to traverse +// the format string and properly calculate, the length so this will +// work in all cases, but this should be good enough for 99% of Wstring usage. +char Wstring::setFormatted(const char *msg, ...) +{ + if( msg == NULL || strlen(msg) <= 0 ) return FALSE; + + char* string; + va_list args; + int len = (strlen(msg) < 1024) ? 1024 : (strlen(msg)*2); + + string = new char[len]; + va_start(args, msg); + vsprintf(string, msg, args); + va_end(args); + set(string); + delete[] string; + return(TRUE); +} + + +// This function converts all alphabetical characters in the string to lower +// case. +void Wstring::toLower(void) +{ + uint32 i; + int strlength=length(); + + for(i = 0; i < (uint32)strlength; i++) + { + if((str[i] >= 'A') && (str[i] <= 'Z')) + str[i] = (sint8)tolower(str[i]); + } +} + + +// This function converts all alphabetical characters in the string to upper +// case. +void Wstring::toUpper(void) +{ + uint32 i; + int strlength=length(); + + for(i = 0; i < (uint32)strlength; i++) + { + if((str[i] >= 'a') && (str[i] <= 'z')) + str[i] = (sint8)toupper(str[i]); + } +} + + +// This function truncates the string so its length will match the specified +// `len'. If an error occurs, FALSE is returned. Otherwise, TRUE is returned. +bit8 Wstring::truncate(uint32 len) +{ + strgrow(len+1); + str[len]=0; + return(TRUE); +} + +// Truncate the string after the character 'c' (gets rid of 'c' as well) +// Do nothing if 'c' isn't in the string +bit8 Wstring::truncate(char c) +{ + sint32 len; + + if (str==NULL) + return(FALSE); + + char *cptr=strchr(str,c); + if (cptr==NULL) + return(FALSE); + len=(sint32)(cptr-str); + truncate((uint32)len); + return(TRUE); +} + +// Get a token from this string that's seperated by one or more +// chars from the 'delim' string , start at offset & return offset +sint32 Wstring::getToken(int offset,const char *delim,Wstring &out) const +{ + int i; + sint32 start; + sint32 stop; + + if (offset<0) // check for bad input + return(-1); + + for (i=offset; i<(int)length(); i++) { + if(strchr(delim,str[i])==NULL) + break; + } + if (i>=(int)length()) + return(-1); + start=i; + + for (; i<(int)length(); i++) { + if(strchr(delim,str[i])!=NULL) + break; + } + stop=i-1; + out.set(str+start); + out.truncate((uint32)stop-start+1); + return(stop+1); +} + +// Get the first line of text after offset. Lines are terminated by '\r\n' or '\n' +sint32 Wstring::getLine(int offset, Wstring &out) +{ + int i; + sint32 start; + sint32 stop; + + start=i=offset; + if (start >= (sint32)length()) + return(-1); + + for (; i<(int)length(); i++) { + if(strchr("\r\n",str[i])!=NULL) + break; + } + stop=i; + if ((str[stop]=='\r')&&(str[stop+1]=='\n')) + stop++; + + out.set(str+start); + out.truncate((uint32)stop-start+1); + return(stop+1); +} + +// +// Make sure there's AT LEAST length bytes in this string object +// +void Wstring::strgrow(int length) +{ + if (str==NULL) + { + str=new char[length+PADSIZE]; + str[0]=0; + strsize=length+PADSIZE; + return; + } + else if (strsize >= length) // no need to alloc more data + return; + else // bah, gotta grow... + { + char *newstr=new char[length+PADSIZE]; + strsize=length+PADSIZE; + strcpy(newstr,str); + delete[](str); + str=newstr; + return; + } +} diff --git a/Generals/Code/Tools/matchbot/wlib/wstring.h b/Generals/Code/Tools/matchbot/wlib/wstring.h new file mode 100644 index 00000000000..484815c1bac --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/wstring.h @@ -0,0 +1,93 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * +****************************************************************************** +Project Name: Carpenter (The RedAlert ladder creator) +File Name : main.cpp +Author : Neal Kettler +Start Date : June 1, 1997 +Last Update : June 17, 1997 +\****************************************************************************/ + +#ifndef WSTRING_HEADER +#define WSTRING_HEADER + +#include +#include +#include "wstypes.h" + +class Wstring +{ + public: + Wstring(); + Wstring(const Wstring &other); + Wstring(const char *string); + ~Wstring(); + + void clear(void); + + bit8 cat(const char *string); + bit8 cat(uint32 size,const char *string); + bit8 cat(const Wstring &string); + + void cellCopy(OUT char *dest, uint32 len); + char remove(sint32 pos, sint32 count); + bit8 removeChar(char c); + void removeSpaces(void); + const char *get(void) const; + char get(uint32 index) const; + uint32 length(void) const; + bit8 insert(char c, uint32 pos); + bit8 insert(const char *instring, uint32 pos); + bit8 beautifyNumber(); + bit8 replace(const char *replaceThis,const char *withThis); + char set(const char *str); + char set(uint32 size,const char *str); + bit8 set(char c, uint32 index); + char setFormatted(const char *str, ...); // Added by Joe Howes + void setSize(sint32 bytes); // create an empty string + void toLower(void); + void toUpper(void); + bit8 truncate(uint32 len); + bit8 truncate(char c); // trunc after char c + sint32 getToken(int offset,const char *delim,Wstring &out) const; + sint32 getLine(int offset, Wstring &out); + void strgrow(int length); + + bit8 operator==(const char *other) const; + bit8 operator==(const Wstring &other) const; + bit8 operator!=(const char *other) const; + bit8 operator!=(const Wstring &other) const; + + Wstring &operator=(const char *other); + Wstring &operator=(const Wstring &other); + Wstring &operator+=(const char *other); + Wstring &operator+=(const Wstring &other); + Wstring operator+(const char *other); + Wstring operator+(const Wstring &other); + + bool operator<(const Wstring &other) const; + + private: + char *str; // Pointer to allocated string. + int strsize; // allocated data length +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/wstypes.h b/Generals/Code/Tools/matchbot/wlib/wstypes.h new file mode 100644 index 00000000000..dbc58245638 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/wstypes.h @@ -0,0 +1,118 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S * +****************************************************************************** +Project Name: Carpenter (The RedAlert ladder creator) +File Name : wstypes.h +Author : Neal Kettler +Start Date : June 3, 1997 +Last Update : June 17, 1997 + +Standard type definitions for the sake of portability and readability. +\***************************************************************************/ + +#ifndef WSTYPES_HEADER +#define WSTYPES_HEADER + +#ifdef _REENTRANT // reentrant = threaded +// Headers with non threadsafe libs need to come before my hacky +// threadsafe.h otherwise they won't compile + +#include +#ifndef _WINDOWS +#define _POSIX_C_SOURCE 199506L +#define _POSIX_PTHREAD_SEMANTICS +#define __EXTENSIONS__ +#endif +#include +#include +#include +#ifndef _WINDOWS +#include +#include +#include +#else +#include +#include +#endif +#include "threadsafe.h" // enforce threadsafe-only calls +#endif + +#define adelete(X) (delete[](X)) + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef MIN +#define MIN(x,y) (((x)<(y))?(x):(y)) +#endif + +#ifndef MAX +#define MAX(x,y) (((x)>(y))?(x):(y)) +#endif + +#ifndef NULL +#define NULL 0 +#endif + +//These are used for readability purposes mostly, when a method takes a +// pointer or reference these help specify what will happen to the data +// that is sent in. +#ifdef IN +#undef IN +#endif +#define IN const +#define OUT +#define INOUT +#define _IN_ const + +// Used to declare a function or method as const or Read Only +#define RO const + +typedef char bit8; +typedef char sint8; +typedef unsigned char uint8; +typedef signed short int sint16; +typedef unsigned short int uint16; +typedef signed int sint32; +typedef unsigned int uint32; + +typedef float float32; +typedef double float64; + +#define MAX_BIT8 0x1 +#define MAX_UINT32 0xFFFFFFFF +#define MAX_UINT16 0xFFFF +#define MAX_UINT8 0xFF +#define MAX_SINT32 0x7FFFFFFF +#define MAX_SINT16 0x7FFF +#define MAX_SINT8 0x7F + +#ifdef _WINDOWS +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/wtime.cpp b/Generals/Code/Tools/matchbot/wlib/wtime.cpp new file mode 100644 index 00000000000..9843a25d94d --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/wtime.cpp @@ -0,0 +1,802 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +wtime Neal Kettler +\****************************************************************************/ + +#include +#include "wtime.h" + +static const char *DAYS[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; + +static const char *FULLDAYS[]={"Sunday","Monday","Tuesday","Wednesday","Thursday", + "Friday","Saturday"}; + +static const char *MONTHS[]={"Jan","Feb","Mar","Apr","May","Jun","Jul", + "Aug","Sep","Oct","Nov","Dec"}; + +static const char *FULLMONTHS[]={"January","February","March","April","May","June", + "July","August","September","October","November","December"}; + +// MDC: Windows doesn't provide a localtime_r, so make our own... +#ifdef _WINDOWS +#ifdef _REENTRANT +#include "critsec.h" +static CritSec localtime_critsec; +#undef localtime +_CRTIMP struct tm *localtime(const time_t *clockval); +#endif // _REENTRANT + +static struct tm *localtime_r(const time_t *clockval, struct tm *res) { +#ifdef _REENTRANT + localtime_critsec.lock(); +#endif + struct tm *static_tm = localtime(clockval); + res = (struct tm *)memcpy(res, static_tm, sizeof(tm)); +#ifdef _REENTRANT + localtime_critsec.unlock(); +#endif + return res; +} +#endif // _WINDOWS + +Wtime::Wtime(void) +{ + Update(); +} + +Wtime::Wtime( Wtime &other ) +{ + sign=other.sign; + sec=other.sec; + usec=other.usec; +} + +Wtime::Wtime( uint32 other ) +{ + sign=POSITIVE; + sec=other; + usec=0; +} + +Wtime::~Wtime() +{ +} + +void Wtime::Update(void) +{ + sign=POSITIVE; + #ifdef _WINDOWS + struct _timeb wintime; + _ftime(&wintime); + sec=wintime.time; + usec=(wintime.millitm)*1000; + #endif + #ifndef _WINDOWS + struct timeval unixtime; + struct timezone unixtzone; + gettimeofday(&unixtime,&unixtzone); + sec=unixtime.tv_sec; + usec=unixtime.tv_usec; + #endif +} + + +// Parses a date string that's in modified RFC 1123 format +// Can have a +minutes after the normal time +// eg: Thu, 20 Jun 1996 17:33:49 +100 +// Returns true if successfully parsed, false otherwise +bit8 Wtime::ParseDate(char *in) +{ + int i; + uint32 minOffset; + struct tm t; + char *ptr=in; + while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of string + if (*ptr==0) return(FALSE); + t.tm_wday=-1; + for (i=0; i<7; i++) // parse day of week + if (strncmp(ptr,DAYS[i],strlen(DAYS[i]))==0) + t.tm_wday=i; + if (t.tm_wday==-1) + return(FALSE); + while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to day of month + if (*ptr==0) return(FALSE); + t.tm_mday=atoi(ptr); + while ((!isalpha(*ptr))&&(*ptr!=0)) ptr++; // skip to month + if (*ptr==0) return(FALSE); + t.tm_mon=-1; + for (i=0; i<12; i++) // match month + if (strncmp(ptr,MONTHS[i],strlen(MONTHS[i]))==0) t.tm_mon=i; + if (t.tm_mon==-1) return(FALSE); + while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; + if (*ptr==0) return(FALSE); + t.tm_year=atoi(ptr); + if (t.tm_year<70) // if they specify a 2 digit year, we'll be nice + t.tm_year+=2000; + else if (t.tm_year<100) + t.tm_year+=1900; + if (t.tm_year>2200) // I doubt my code will be around for another 203 years + return(FALSE); + while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to end of year + if (*ptr==0) return(FALSE); + + while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of time + if (*ptr==0) return(FALSE); + + t.tm_hour=atoi(ptr); + while ((*ptr!=':')&&(*ptr!=0)) ptr++; + ptr++; // skip past colon + if (*ptr==0) return(FALSE); + t.tm_min=atoi(ptr); + while ((*ptr!=':')&&(*ptr!=0)) ptr++; + ptr++; // skip past colon + if (*ptr==0) return(FALSE); + t.tm_sec=atoi(ptr); + t.tm_year%=100; // 1996 is stored as 96, not 1996 + t.tm_isdst=-1; // daylight savings info isn't available + + sec=(uint32)(mktime(&t)); + if ((sint32)sec==-1) + return(FALSE); + + + // The next part of the time is OPTIONAL (+minutes) + + // first skip past the seconds + while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; + if (*ptr==0) return(TRUE); + + // skip past any spaces + while ((isspace(*ptr))&&(*ptr!=0)) ptr++; + if (*ptr!='+') + { + //printf("\nNOPE ptr was '%s'\n",ptr); + return(TRUE); + } + ptr++; + if (*ptr==0) + { + //printf("\nPTR WAS 0\n"); + return(TRUE); + } + + minOffset=atol(ptr); + //printf("\n\nAdding %d minutes!\n\n",minOffset); + sec+=minOffset*60; // add the minutes as seconds + return(TRUE); +} + + +// This takes the standard Microsoft time formatting string +// make sure the out string is big enough +// An example format would be "mm/dd/yy hh:mm:ss" +// CHANGE: Joe Howes 06/30/99 +// To specify 12-hour format, use "aa" instead of "hh". +// The hours will be 12 hour and the string will be +// appended with " AM" or " PM". +bit8 Wtime::FormatTime(char *out, const char *format) +{ + int lastWasH=0; + int ampmflag = 0; + out[0]=0; + const char *ptr=format; + + if (*ptr=='"') ptr++; // skip past open quote if exists + + while (*ptr!=0) + { + if (lastWasH>0) + lastWasH--; + + if (isspace(*ptr)) + { + if (lastWasH==1) lastWasH=2; + sprintf(out+strlen(out),"%c",*ptr); + ptr+=1; + } + else if (strncmp(ptr,"\"",1)==0) + { + break; + } + else if (strncmp(ptr,":",1)==0) + { + if (lastWasH==1) lastWasH=2; + sprintf(out+strlen(out),":"); + ptr+=1; + } + else if (strncmp(ptr,"/",1)==0) + { + sprintf(out+strlen(out),"/"); + ptr+=1; + } + else if (strncmp(ptr,"c",1)==0) + { + sprintf(out+strlen(out),"%ld/%ld/%02ld %ld:%02ld:%02ld",GetMonth(), + GetMDay(),GetYear()%100,GetHour(),GetMinute(),GetSecond()); + ptr+=1; + } + else if (strncmp(ptr,"dddddd",6)==0) + { + sprintf(out+strlen(out),"%s %02ld, %ld",FULLMONTHS[GetMonth()-1], + GetMDay(),GetYear()); + ptr+=6; + } + else if (strncmp(ptr,"ddddd",5)==0) + { + sprintf(out+strlen(out),"%ld/%ld/%02ld",GetMonth(),GetMDay(), + GetYear()%100); + ptr+=5; + } + else if (strncmp(ptr,"dddd",4)==0) + { + sprintf(out+strlen(out),"%s",FULLDAYS[GetWDay()-1]); + ptr+=4; + } + else if (strncmp(ptr,"ddd",3)==0) + { + sprintf(out+strlen(out),"%s",DAYS[GetWDay()-1]); + ptr+=3; + } + else if (strncmp(ptr,"dd",2)==0) + { + sprintf(out+strlen(out),"%02ld",GetMDay()); + ptr+=2; + } + else if (strncmp(ptr,"d",1)==0) + { + sprintf(out+strlen(out),"%ld",GetMDay()); + ptr+=1; + } + else if (strncmp(ptr,"ww",2)==0) + { + sprintf(out+strlen(out),"%02ld",GetYWeek()); + ptr+=2; + } + else if (strncmp(ptr,"w",1)==0) + { + sprintf(out+strlen(out),"%ld",GetWDay()); + ptr+=1; + } + else if (strncmp(ptr,"mmmm",4)==0) + { + sprintf(out+strlen(out),"%s",FULLMONTHS[GetMonth()-1]); + ptr+=4; + } + else if (strncmp(ptr,"mmm",3)==0) + { + sprintf(out+strlen(out),"%s",MONTHS[GetMonth()-1]); + ptr+=3; + } + else if (strncmp(ptr,"mm",2)==0) + { + if (lastWasH==1) + sprintf(out+strlen(out),"%02ld",GetMinute()); + else + sprintf(out+strlen(out),"%02ld",GetMonth()); + ptr+=2; + } + else if (strncmp(ptr,"m",1)==0) + { + if (lastWasH==1) + sprintf(out+strlen(out),"%ld",GetMinute()); + else + sprintf(out+strlen(out),"%ld",GetMonth()); + ptr+=1; + } + else if (strncmp(ptr,"q",1)==0) + { + sprintf(out+strlen(out),"%ld",((GetMonth()-1)/4)+1); // GetQuarter + ptr+=1; + } + else if (strncmp(ptr,"yyyy",4)==0) + { + sprintf(out+strlen(out),"%ld",GetYear()); + ptr+=4; + } + else if (strncmp(ptr,"yy",2)==0) + { + sprintf(out+strlen(out),"%02ld",GetYear()%100); + ptr+=2; + } + else if (strncmp(ptr,"y",1)==0) + { + sprintf(out+strlen(out),"%ld",GetYDay()); + ptr+=1; + } + else if (strncmp(ptr,"hh",2)==0) + { + sprintf(out+strlen(out),"%02ld",GetHour()); + lastWasH=2; // needs to be 1 after top of loop decs it + ptr+=2; + } + else if (strncmp(ptr,"h",1)==0) + { + sprintf(out+strlen(out),"%ld",GetHour()); + lastWasH=2; // needs to be 1 after top of loop decs it + ptr+=1; + } + else if (strncmp(ptr,"nn",2)==0) + { + sprintf(out+strlen(out),"%02ld",GetMinute()); + ptr+=2; + } + else if (strncmp(ptr,"n",1)==0) + { + sprintf(out+strlen(out),"%ld",GetMinute()); + ptr+=1; + } + else if (strncmp(ptr,"ss",2)==0) + { + sprintf(out+strlen(out),"%02ld",GetSecond()); + ptr+=2; + } + else if (strncmp(ptr,"s",1)==0) + { + sprintf(out+strlen(out),"%ld",GetSecond()); + ptr+=1; + } + else if (strncmp(ptr,"ttttt",5)==0) + { + sprintf(out+strlen(out),"%ld:%02ld:%02ld",GetHour(),GetMinute(), + GetSecond()); + ptr+=5; + } + else if (strncmp(ptr,"aa",2)==0) + { + uint32 tmp = (GetHour() <= 12) ? GetHour() : GetHour() - 12; + sprintf(out+strlen(out),"%02ld", tmp); + lastWasH=2; // needs to be 1 after top of loop decs it + ptr+=2; + ampmflag = 1; + } + else // an unknown char, move to next + ptr++; + } + if(ampmflag) + { + char ampm[4]; + if( GetHour() < 12 ) + strcpy(ampm, " AM"); + else + strcpy(ampm, " PM"); + sprintf(out+strlen(out), "%s", ampm); + } + return(TRUE); +} + + + +// In addition to PrintTime & PrintDate there is the 'Print' function +// which prints both in RFC 1123 format + +void Wtime::PrintTime(FILE *out) const +{ + char string[80]; + PrintTime(string); + fprintf(out,"%s",string); +} + +void Wtime::PrintTime(char *out) const +{ + sprintf(out," %02lu:%02lu:%02lu",GetHour(),GetMinute(),GetSecond()); +} + +void Wtime::PrintDate(FILE *out) const +{ + char string[80]; + PrintDate(string); + fprintf(out,"%s",string); +} + +void Wtime::PrintDate(char *out) const +{ + sprintf(out,"%s, %lu %s %lu",DAYS[GetWDay()-1],GetMDay(),MONTHS[GetMonth()-1], + GetYear()); +} + +uint32 Wtime::GetSec(void) const +{ + return(sec); +} + +uint32 Wtime::GetUsec(void) const +{ + return(usec); +} + +void Wtime::SetSec(uint32 newsec) +{ + sec=newsec; +} + +void Wtime::SetUsec(uint32 newusec) +{ + usec=newusec; +} + +void Wtime::Set(uint32 newsec, uint32 newusec) +{ + sec=newsec; + usec=newusec; +} + +// Get a timeval ptr from a Wtime class +struct timeval *Wtime::GetTimeval(void) +{ + static struct timeval tv; + tv.tv_sec=sec; + tv.tv_usec=usec; + return(&tv); +} + +// Get a timeval ptr from a Wtime class +void Wtime::GetTimevalMT(struct timeval &tv) +{ + tv.tv_sec=sec; + tv.tv_usec=usec; +} + + +uint32 Wtime::GetSecond(void) const +{ + struct tm t; + struct tm *tptr; + tptr=localtime_r((time_t *)&sec,&t); + return(tptr->tm_sec); +} +uint32 Wtime::GetMinute(void) const +{ + struct tm t; + struct tm *tptr; + tptr=localtime_r((time_t *)&sec,&t); + return(tptr->tm_min); +} +uint32 Wtime::GetHour(void) const +{ + struct tm t; + struct tm *tptr; + tptr=localtime_r((time_t *)&sec,&t); + return(tptr->tm_hour); +} +uint32 Wtime::GetMDay(void) const +{ + struct tm t; + struct tm *tptr; + tptr=localtime_r((time_t *)&sec,&t); + return(tptr->tm_mday); +} +uint32 Wtime::GetWDay(void) const +{ + struct tm t; + struct tm *tptr; + tptr=localtime_r((time_t *)&sec,&t); + return(tptr->tm_wday+1); +} +uint32 Wtime::GetYDay(void) const +{ + struct tm t; + struct tm *tptr; + tptr=localtime_r((time_t *)&sec,&t); + return(tptr->tm_yday+1); +} +uint32 Wtime::GetYWeek(void) const +{ + uint32 yweek; + uint32 yday=GetYDay(); + uint32 wday=GetWDay(); + //phase holds the first weekday of the year. If (Jan 1 = Sun) phase = 0 + sint32 phase=((wday-yday)%7); + if (phase<0) phase+=7; + yweek=((yday+phase-1)/7)+1; + return(yweek); +} +uint32 Wtime::GetMonth(void) const +{ + struct tm t; + struct tm *tptr; + tptr=localtime_r((time_t *)&sec,&t); + return(tptr->tm_mon+1); +} + +uint32 Wtime::GetYear(void) const +{ + struct tm t; + struct tm *tptr; + tptr=localtime_r((time_t *)&sec,&t); + if ((tptr->tm_year)>=70) + return((tptr->tm_year)+1900); + else + return((tptr->tm_year)+2000); +} + + +bit8 Wtime::GetSign(void) const +{ + return(sign); +} + +// 1 = *this > other +//-1 = *this < other +// 0 = *this == other +int Wtime::Compare(const Wtime &other) const +{ + if ((sec==other.sec)&&(usec==other.usec)) + return(0); // equal + + else if (sec>other.sec) + return(1); + else if (secother.usec) + return(1); + else + return(-1); +} + + +bit8 Wtime::operator == ( const Wtime &other ) const +{ + bit8 retval=Compare(other); + if (retval==0) + return(TRUE); + else + return(FALSE); +} + +bit8 Wtime::operator != ( const Wtime &other ) const +{ + bit8 retval=Compare(other); + if (retval==0) + return(FALSE); + else + return(TRUE); +} + +bit8 Wtime::operator < ( const Wtime &other ) const +{ + int retval=Compare(other); + if (retval==-1) + return(TRUE); + else + return(FALSE); +} + +bit8 Wtime::operator > ( const Wtime &other ) const +{ + int retval=Compare(other); + if (retval==1) + return(TRUE); + else + return(FALSE); +} + +bit8 Wtime::operator <= ( const Wtime &other ) const +{ + int retval=Compare(other); + if ((retval==-1)||(retval==0)) + return(TRUE); + else + return(FALSE); +} + +bit8 Wtime::operator >= ( const Wtime &other ) const +{ + int retval=Compare(other); + if ((retval==1)||(retval==0)) + return(TRUE); + else + return(FALSE); +} + + +// None of the operators pay attention to sign +// only the functions that begin with 'Signed' +void Wtime::SignedAdd(const Wtime &other) +{ + Wtime temp; + + if ((sign==POSITIVE)&&(other.sign==POSITIVE)) + { + *this+=other; + sign=POSITIVE; + } + else if ((sign==POSITIVE)&&(other.sign==NEGATIVE)) + { + if (*this>other) + { + *this-=other; + sign=POSITIVE; + } + else + { + temp=other; + temp-=*this; + *this=temp; + sign=NEGATIVE; + } + } + else if ((sign==NEGATIVE)&&(other.sign==POSITIVE)) + { + if (*thisother) + { + *this-=other; + sign=POSITIVE; + } + else + { + temp=other; + temp-=*this; + *this=temp; + sign=NEGATIVE; + } + } + else if ((sign==NEGATIVE)&&(other.sign==NEGATIVE)) + { + if (*this1000000) + { + sec++; + usec-=1000000; + } + return *this; +} + +Wtime &Wtime::operator -= (const Wtime &other) +{ + sint32 temp; + if (Compare(other)==-1) + { + sec=0; // can't handle negative time + usec=0; + return *this; + } + sec-=other.sec; + temp=(sint32)usec; + temp-=(sint32)other.usec; + if (temp<0) + { + sec--; + temp+=1000000; + } + usec=temp; + return *this; +} + +Wtime Wtime::operator - (Wtime &other) +{ + Wtime temp(*this); + temp-=other; + return(temp); +} + +Wtime Wtime::operator + (Wtime &other) +{ + Wtime temp(*this); + temp+=other; + return(temp); +} + + +Wtime &Wtime::operator = (const Wtime &other) +{ + sign=other.sign; + sec=other.sec; + usec=other.usec; + return *this; +} + + +Wtime &Wtime::operator += (const uint32 other) +{ + sec+=other; + return *this; +} + + +Wtime &Wtime::operator -= (const uint32 other) +{ + sec-=other; + return *this; +} + + +Wtime Wtime::operator - (uint32 other) +{ + Wtime temp(*this); + temp-=other; + return(temp); +} + + +Wtime Wtime::operator + (uint32 other) +{ + Wtime temp(*this); + temp+=other; + return(temp); +} + + +Wtime &Wtime::operator = (const uint32 other) +{ + sign=POSITIVE; + sec=other; + usec=0; + return *this; +} diff --git a/Generals/Code/Tools/matchbot/wlib/wtime.h b/Generals/Code/Tools/matchbot/wlib/wtime.h new file mode 100644 index 00000000000..3f0d1cf723a --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/wtime.h @@ -0,0 +1,128 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +wtime Neal Kettler + +\****************************************************************************/ +#ifndef WTIME_HEADER +#define WTIME_HEADER + + +#include +#include +#include +#include + +#ifndef _WINDOWS +#include +#include +#include +#else +#include +#include +#endif + +#include +#include + +#include "wstypes.h" + +class Wtime +{ + public: + + enum + { + POSITIVE=0, + NEGATIVE=1 + }; + + Wtime(); // init to system time + Wtime( Wtime &other ); + Wtime( uint32 other ); + ~Wtime(); + + void Update(); // Update members sec & usec to system time + + + void PrintTime(FILE *out) const; + void PrintTime(char *out) const; + void PrintDate(FILE *out) const; + void PrintDate(char *out) const; + + uint32 GetSec(void) const; // Get member variable 'sec' + uint32 GetUsec(void) const; // Get member variable 'usec' + void SetSec(uint32 newsec); + void SetUsec(uint32 newusec); + void Set(uint32 newsec,uint32 newusec); + bit8 ParseDate(char *in); + bit8 FormatTime(char *out, const char *format); + + struct timeval *GetTimeval(void); + void GetTimevalMT(struct timeval &tv); + + uint32 GetSecond(void) const; // Second (0- 60) (60 is for a leap second) + uint32 GetMinute(void) const; // Minute (0 - 59) + uint32 GetHour(void) const; // Hour (0-23) + uint32 GetMDay(void) const; // Day of Month (1-31) + uint32 GetWDay(void) const; // Day of Week (1-7) + uint32 GetYDay(void) const; // Day of Year (1-366) + uint32 GetMonth(void) const; // Month (1-12) + uint32 GetYWeek(void) const; // Week of Year (1-53) + uint32 GetYear(void) const; // Year (e.g. 1997) + + bit8 GetSign(void) const; // 0 = pos 1 = neg + + int Compare(const Wtime &other) const; + + // comparisons + bit8 operator == ( const Wtime &other ) const; + bit8 operator != ( const Wtime &other ) const; + bit8 operator < ( const Wtime &other ) const; + bit8 operator > ( const Wtime &other ) const; + bit8 operator <= ( const Wtime &other ) const; + bit8 operator >= ( const Wtime &other ) const; + + // assignments + Wtime &operator = (const Wtime &other); + Wtime &operator = (const uint32 other); + + // math + // signed + void SignedAdd(const Wtime &other); + void SignedSubtract(const Wtime &other); + + // unsigned + Wtime &operator += (const Wtime &other); + Wtime &operator -= (const Wtime &other); + Wtime operator + (Wtime &other); + Wtime operator - (Wtime &other); + + Wtime &operator += (const uint32 other); + Wtime &operator -= (const uint32 other); + Wtime operator + (uint32 other); + Wtime operator - (uint32 other); + + protected: + uint32 sec; // seconds since Jan 1, 1970 + uint32 usec; // microseconds (millionths of a second) + bit8 sign; // for time differences 0 = pos 1 = neg +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wlib/xtime.cpp b/Generals/Code/Tools/matchbot/wlib/xtime.cpp new file mode 100644 index 00000000000..28181d6f36b --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/xtime.cpp @@ -0,0 +1,1015 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ + +xtime Neal Kettler + +Improved version of the Wtime library (V->W->X...) + +Handles signed times better and dates long +long long after you'll be dead. + +\****************************************************************************/ + +#include +#include +#ifndef _WINDOWS +#include +#endif +#include "xtime.h" + +static const char *DAYS[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; + +static const char *FULLDAYS[]={"Sunday","Monday","Tuesday","Wednesday","Thursday", + "Friday","Saturday"}; + +static const char *MONTHS[]={"Jan","Feb","Mar","Apr","May","Jun","Jul", + "Aug","Sep","Oct","Nov","Dec"}; + +static const char *FULLMONTHS[]={"January","February","March","April","May","June", + "July","August","September","October","November","December"}; + + +#define IS_LEAP(y) ((y) % 4) == 0 && (! ((y) % 100) == 0 || ((y) % 400) == 0) +#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) + + +/////////////// Utility functions /////////////////// + +// +// Return the daycount since year 0 for the specified date. +// month = 1-12, day = 1-31 year = 0... +// +static sint32 Get_Day(int month, int day, int year) +{ + time_t days; + + static int DaysAtMonth[] = + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + + /* Add up number of straight days + number of leap days + days */ + /* up to the month + the day of month. */ + + days = (year * 365) + (year / 4) - (year / 100) + (year / 400) + + DaysAtMonth[month-1] + day; + + /* If we haven't hit Feb 29, and this is a leap year, we need to */ + /* subtract out the leap day that was added above for this year */ + + if (month < 3 && IS_LEAP(year)) + --days; + return(days); +} + + + +// +// Get the year from a daycount since year 0 +// Also get the daycount since the start of the year +// +// Ayecarumba what a pain in the ass! +// +static bit8 Get_Date_From_Day(sint32 days, OUT sint32 &year, OUT sint32 &yday) +{ + //register long int rem; + register long int y; + //register const unsigned short int *ip; + + if (days <= 365) + { + year=0; + yday=days+1; // 1 based + return(TRUE); + } + + y = 1; + days-=365; + + days--; // zero based + + // + // As far as I can tell there's no non-iteritive way to + // do this... + // + while (days < 0 || days >= (IS_LEAP (y) ? 366 : 365)) + { + /* Guess a corrected year, assuming 365 days per year. */ + long int yg = y + days / 365 - (days % 365 < 0); + + /* Adjust DAYS and Y to match the guessed year. */ + days -= ((yg - y) * 365 + + LEAPS_THRU_END_OF (yg - 1) + - LEAPS_THRU_END_OF (y - 1)); + y = yg; + } + year=y; + yday=days+1; // 1 based + return(TRUE); +} + + +// +// Get the max day of a given month in a given year +// +int Max_Day(int month, int year) +{ + static char dayTable[2][13] = { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} + }; + bit8 isleap=IS_LEAP(year); + return(dayTable[isleap][month]); +} + + + +/********************************************************** +int main(int argc, char *argv[]) +{ + Xtime wtime; + int month,mday,year,hour,minute,second,day; + int i; + int dayoffset; + int yr; + int yday; + int ydaycount; + + wtime.set(719528,0); + +*********************************************** + for (year=0; year<10000; year++) + { + ydaycount=1; + for (month=1; month<=12; month++) + { + for (day=1; day<=Max_Day(month,year); day++) + { + dayoffset=Get_Day(month,day,year); + assert (GetDateFromDay(dayoffset,yr,yday)==TRUE); + + //printf("Yday=%d YdayCount=%d\n",yday,ydaycount); + + if (yr!=year) + { + printf("MO=%d DAY=%d YEAR=%d YR=%d\n",month,day,year,yr); + assert(0); + } + if (yday != ydaycount) + { + printf("MO=%d DAY=%d YEAR=%d YR=%d\n",month,day,year,yr); + printf("Yday=%d YdayCount=%d\n",yday,ydaycount); + assert(0); + } + ydaycount++; + } + } + printf("(%d) ",year); + } +*************************************** + + ///////wtime.addSeconds((60*60*24)-(60*60*8)); + ////////wtime.addSeconds(-(60*60*8)); // timezone delta + + dayoffset=Get_Day(1,1,1970); + printf("DAYOFFSET = %d\n",dayoffset); + + + wtime.getTime(month, mday, year, hour, minute, second); + printf("\n%s %d %d %d:%02d:%02d\n\n", + MONTHS[month-1],mday,year,hour,minute,second); + + struct timeval unixtime; + struct timezone unixtzone; + time_t ttime; + tm tmtime; + + memset(&tmtime,0,sizeof(tmtime)); + ttime=0; + unixtime.tv_sec=0; + unixtime.tv_usec=0; + + //gettimeofday(&unixtime,&unixtzone); + //ttime=time(NULL); + tmtime=*gmtime(&ttime); + + printf("TIME->CTIME = %s\n",ctime(&ttime)); + printf("GTOD->CTIE = %s\n",ctime(&(unixtime.tv_sec))); + printf("TIME->GMT->ASCTIME = %s\n",asctime(&tmtime)); +} +***************************************************************/ + + + +// +// Construct with current clock time +// +Xtime::Xtime(void) +{ + update(); +} + +// +// Copy constructor +// +Xtime::Xtime( Xtime &other ) +{ + day_=other.day_; + msec_=other.msec_; +} + +// +// Set to a time_t (1970-2038) +// +Xtime::Xtime( time_t other ) +{ + day_=719528; // days from year 0 to Jan1, 1970 + // Add seconds from time_t + addSeconds(other); + msec_=0; +} + +Xtime::~Xtime() +{ +} + +// +// Add some number of seconds to the time (seconds can be negative) +// +void Xtime::addSeconds(sint32 seconds) +{ + // Add to day counter first + day_+=(seconds/86400); + msec_+=(seconds % 86400)*1000; + + // Now normalize in case msec is > 1 days worth + normalize(); +} + +// +// If msec is > 1 days worth, adjust the day count +// & decrement the msec counter until OK. +// +void Xtime::normalize(void) +{ + day_+=(msec_/86400000); + msec_%=86400000; + + while (msec_ < 0) + { + day_--; + msec_+=86400000; + } +} + +// +// Update time to hold the current clock time +// (breaks in 2038 :-) +// +void Xtime::update(void) +{ + day_=719528; // day_s from year 0 to Jan1, 1970 + msec_=0; + + #ifdef _WINDOWS + struct _timeb wintime; + _ftime(&wintime); + addSeconds(wintime.time); + msec_+=wintime.millitm; + #endif + #ifndef _WINDOWS + struct timeval unixtime; + struct timezone unixtzone; + gettimeofday(&unixtime,&unixtzone); + addSeconds(unixtime.tv_sec); + msec_+=(unixtime.tv_usec/1000); + #endif + + // Now normalize in case msec is > 1 days worth + normalize(); +} + + +// This takes the standard Microsoft time formatting string +// make sure the out string is big enough +// An example format would be "mm/dd/yy hh:mm:ss" +// CHANGE: Joe Howes 06/30/99 +// To specify 12-hour format, use "aa" instead of "hh". +// The hours will be 12 hour and the string will be +// appended with " AM" or " PM". +bit8 Xtime::FormatTime(char *out, const char *format) +{ + int lastWasH=0; + int ampmflag = 0; + out[0]=0; + const char *ptr=format; + + if (*ptr=='"') ptr++; // skip past open quote if exists + + while (*ptr!=0) + { + if (lastWasH>0) + lastWasH--; + + if (isspace(*ptr)) + { + if (lastWasH==1) lastWasH=2; + sprintf(out+strlen(out),"%c",*ptr); + ptr+=1; + } + else if (strncmp(ptr,"\"",1)==0) + { + break; + } + else if (strncmp(ptr,":",1)==0) + { + if (lastWasH==1) lastWasH=2; + sprintf(out+strlen(out),":"); + ptr+=1; + } + else if (strncmp(ptr,"/",1)==0) + { + sprintf(out+strlen(out),"/"); + ptr+=1; + } + else if (strncmp(ptr,"c",1)==0) + { + sprintf(out+strlen(out),"%ld/%ld/%02ld %ld:%02ld:%02ld",getMonth(), + getMDay(),getYear()%100,getHour(),getMinute(),getSecond()); + ptr+=1; + } + else if (strncmp(ptr,"dddddd",6)==0) + { + sprintf(out+strlen(out),"%s %02ld, %ld",FULLMONTHS[getMonth()-1], + getMDay(),getYear()); + ptr+=6; + } + else if (strncmp(ptr,"ddddd",5)==0) + { + sprintf(out+strlen(out),"%ld/%ld/%02ld",getMonth(),getMDay(), + getYear()%100); + ptr+=5; + } + /*else if (strncmp(ptr,"dddd",4)==0) + { + sprintf(out+strlen(out),"%s",FULLDAYS[getWDay()-1]); + ptr+=4; + } + else if (strncmp(ptr,"ddd",3)==0) + { + sprintf(out+strlen(out),"%s",DAYS[getWDay()-1]); + ptr+=3; + }*/ + else if (strncmp(ptr,"dd",2)==0) + { + sprintf(out+strlen(out),"%02ld",getMDay()); + ptr+=2; + } + else if (strncmp(ptr,"d",1)==0) + { + sprintf(out+strlen(out),"%ld",getMDay()); + ptr+=1; + } + /*else if (strncmp(ptr,"ww",2)==0) + { + sprintf(out+strlen(out),"%02ld",getYWeek()); + ptr+=2; + }*/ + /*else if (strncmp(ptr,"w",1)==0) + { + sprintf(out+strlen(out),"%ld",getWDay()); + ptr+=1; + }*/ + else if (strncmp(ptr,"mmmm",4)==0) + { + sprintf(out+strlen(out),"%s",FULLMONTHS[getMonth()-1]); + ptr+=4; + } + else if (strncmp(ptr,"mmm",3)==0) + { + sprintf(out+strlen(out),"%s",MONTHS[getMonth()-1]); + ptr+=3; + } + else if (strncmp(ptr,"mm",2)==0) + { + if (lastWasH==1) + sprintf(out+strlen(out),"%02ld",getMinute()); + else + sprintf(out+strlen(out),"%02ld",getMonth()); + ptr+=2; + } + else if (strncmp(ptr,"m",1)==0) + { + if (lastWasH==1) + sprintf(out+strlen(out),"%ld",getMinute()); + else + sprintf(out+strlen(out),"%ld",getMonth()); + ptr+=1; + } + else if (strncmp(ptr,"q",1)==0) + { + sprintf(out+strlen(out),"%ld",((getMonth()-1)/4)+1); // GetQuarter + ptr+=1; + } + else if (strncmp(ptr,"yyyy",4)==0) + { + sprintf(out+strlen(out),"%ld",getYear()); + ptr+=4; + } + else if (strncmp(ptr,"yy",2)==0) + { + sprintf(out+strlen(out),"%02ld",getYear()%100); + ptr+=2; + } + /*else if (strncmp(ptr,"y",1)==0) + { + sprintf(out+strlen(out),"%ld",getYDay()); + ptr+=1; + }*/ + else if (strncmp(ptr,"hh",2)==0) + { + sprintf(out+strlen(out),"%02ld",getHour()); + lastWasH=2; // needs to be 1 after top of loop decs it + ptr+=2; + } + else if (strncmp(ptr,"h",1)==0) + { + sprintf(out+strlen(out),"%ld",getHour()); + lastWasH=2; // needs to be 1 after top of loop decs it + ptr+=1; + } + else if (strncmp(ptr,"nn",2)==0) + { + sprintf(out+strlen(out),"%02ld",getMinute()); + ptr+=2; + } + else if (strncmp(ptr,"n",1)==0) + { + sprintf(out+strlen(out),"%ld",getMinute()); + ptr+=1; + } + else if (strncmp(ptr,"ss",2)==0) + { + sprintf(out+strlen(out),"%02ld",getSecond()); + ptr+=2; + } + else if (strncmp(ptr,"s",1)==0) + { + sprintf(out+strlen(out),"%ld",getSecond()); + ptr+=1; + } + else if (strncmp(ptr,"ttttt",5)==0) + { + sprintf(out+strlen(out),"%ld:%02ld:%02ld",getHour(),getMinute(), + getSecond()); + ptr+=5; + } + else if (strncmp(ptr,"aa",2)==0) + { + uint32 tmp = (getHour() <= 12) ? getHour() : getHour() - 12; + sprintf(out+strlen(out),"%02ld", tmp); + lastWasH=2; // needs to be 1 after top of loop decs it + ptr+=2; + ampmflag = 1; + } + else // an unknown char, move to next + ptr++; + } + if(ampmflag) + { + char ampm[4]; + if( getHour() < 12 ) + strcpy(ampm, " AM"); + else + strcpy(ampm, " PM"); + sprintf(out+strlen(out), "%s", ampm); + } + return(TRUE); +} + +/************************************** + + REPLACE THIS CRAP + +// Parses a date string that's in modified RFC 1123 format +// Can have a +minutes after the normal time +// eg: Thu, 20 Jun 1996 17:33:49 +100 +// Returns true if successfully parsed, false otherwise +bit8 Xtime::ParseDate(char *in) +{ + int i; + uint32 minOffset; + struct tm t; + char *ptr=in; + while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of string + if (*ptr==0) return(FALSE); + t.tm_wday_=-1; + for (i=0; i<7; i++) // parse day_ of week + if (strncmp(ptr,DAYS[i],strlen(DAYS[i]))==0) + t.tm_wday_=i; + if (t.tm_wday_==-1) + return(FALSE); + while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to day_ of month + if (*ptr==0) return(FALSE); + t.tm_mday_=atoi(ptr); + while ((!isalpha(*ptr))&&(*ptr!=0)) ptr++; // skip to month + if (*ptr==0) return(FALSE); + t.tm_mon=-1; + for (i=0; i<12; i++) // match month + if (strncmp(ptr,MONTHS[i],strlen(MONTHS[i]))==0) t.tm_mon=i; + if (t.tm_mon==-1) return(FALSE); + while ((!isdigit(*ptr))&&(*ptr!=0)) ptr++; + if (*ptr==0) return(FALSE); + t.tm_year=atoi(ptr); + if (t.tm_year<70) // if they specify a 2 digit year, we'll be nice + t.tm_year+=2000; + else if (t.tm_year<100) + t.tm_year+=1900; + if (t.tm_year>2200) // I doubt my code will be around for another 203 years + return(FALSE); + while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; // skip to end of year + if (*ptr==0) return(FALSE); + + while ((!isgraph(*ptr))&&(*ptr!=0)) ptr++; // skip to start of time + if (*ptr==0) return(FALSE); + + t.tm_hour=atoi(ptr); + while ((*ptr!=':')&&(*ptr!=0)) ptr++; + ptr++; // skip past colon + if (*ptr==0) return(FALSE); + t.tm_min=atoi(ptr); + while ((*ptr!=':')&&(*ptr!=0)) ptr++; + ptr++; // skip past colon + if (*ptr==0) return(FALSE); + t.tm_sec=atoi(ptr); + t.tm_year%=100; // 1996 is stored as 96, not 1996 + t.tm_isdst=-1; // day_light savings info isn't available + + sec=(uint32)(mktime(&t)); + if ((sint32)sec==-1) + return(FALSE); + + + // The next part of the time is OPTIONAL (+minutes) + + // first skip past the seconds + while ((isdigit(*ptr))&&(*ptr!=0)) ptr++; + if (*ptr==0) return(TRUE); + + // skip past any spaces + while ((isspace(*ptr))&&(*ptr!=0)) ptr++; + if (*ptr!='+') + { + //printf("\nNOPE ptr was '%s'\n",ptr); + return(TRUE); + } + ptr++; + if (*ptr==0) + { + //printf("\nPTR WAS 0\n"); + return(TRUE); + } + + minOffset=atol(ptr); + //printf("\n\nAdding %d minutes!\n\n",minOffset); + sec+=minOffset*60; // add the minutes as seconds + return(TRUE); +} + + + +// In addition to PrintTime & PrintDate there is the 'Print' function +// which prints both in RFC 1123 format + +void Xtime::PrintTime(FILE *out) const +{ + char string[80]; + PrintTime(string); + fprintf(out,"%s",string); +} + +void Xtime::PrintTime(char *out) const +{ + sprintf(out," %02lu:%02lu:%02lu",GetHour(),GetMinute(),GetSecond()); +} + +void Xtime::PrintDate(FILE *out) const +{ + char string[80]; + PrintDate(string); + fprintf(out,"%s",string); +} + +void Xtime::PrintDate(char *out) const +{ + sprintf(out,"%s, %lu %s %lu",DAYS[GetWDay()-1],GetMDay(),MONTHS[GetMonth()-1], + GetYear()); +} +********************************************/ + +// Get day_s since year 0 +sint32 Xtime::getDay(void) const +{ + return(day_); +} + +// Get msecs since start of day +sint32 Xtime::getMsec(void) const +{ + return(msec_); +} + +// Set days since year 0 +void Xtime::setDay(sint32 newday) +{ + day_=newday; +} + +// Set msec since start of this day +void Xtime::setMsec(sint32 newmsec) +{ + msec_=newmsec; +} + +// Set both +void Xtime::set(sint32 newday, sint32 newmsec) +{ + day_=newday; + msec_=newmsec; +} + + +// +// Get a timeval ptr from a Xtime class +// May fail if timeval can't hold a year this big or small +// +bit8 Xtime::getTimeval(struct timeval &tv) +{ + // A timeval can only hold dates from 1970-2038 + if ((day_ < 719528) || (day_ >= 719528+24855)) + return(FALSE); + + // Compute seconds since Jan 1, 1970 + uint32 seconds=day_-719528; + seconds*=(60*60*24); + seconds+=(msec_/1000); + + tv.tv_sec=seconds; + tv.tv_usec=(msec_%1000)*1000; + return(TRUE); +} + +// +// Set the time +// +bit8 Xtime::setTime(int month, int mday, int year, int hour, int minute, int second) +{ + day_=Get_Day(month,mday,year); + msec_=(hour*1000*60*60)+(minute*1000*60)+(second*1000); + return(TRUE); +} + + + +int Xtime::getYDay(void) const // Day of Year (1-366) (366 = leap yr) +{ + int year; + sint32 dayofyear; + if (Get_Date_From_Day(day_,year,dayofyear)==FALSE) + return(-1); + return dayofyear; +} + + +// +// Get all the components of the time in the usual normalized format. +// +// Most of the uglyness is in Get_Date_From_Day() +// +bit8 Xtime::getTime(int &month, int &mday, int &year, int &hour, int &minute, int &second) const +{ + int i; + sint32 dayofyear; + if (Get_Date_From_Day(day_,year,dayofyear)==FALSE) + return(FALSE); + + static int DaysAtMonth[2][12] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, // normal + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} // leap year + }; + + month=0; + + bit8 isleap=IS_LEAP(year); + for (i=0; i<12; i++) + { + if (DaysAtMonth[isleap][i] >= dayofyear) + break; + month=i; + } + month++; // 1 based + + mday=dayofyear-DaysAtMonth[isleap][month-1]; + + // Whew! Now all we have to do is figure out H/M/S from the msec! + hour=(msec_/3600000)%24; // 1000*60*60 + minute=(msec_/60000)%60; // 1000*60 + second=(msec_/ 1000)%60; // 1000 + + return(TRUE); +} + +// +// These are for getting components of the time in the +// standard ranges. Like Day 1-31, Second 0-59, etc... +// +int Xtime::getSecond(void) const +{ + int month,mday,year,hour,minute,second; + getTime(month, mday, year, hour, minute, second); + return(second); +} +int Xtime::getMinute(void) const +{ + int month,mday,year,hour,minute,second; + getTime(month, mday, year, hour, minute, second); + return(minute); +} +int Xtime::getHour(void) const +{ + int month,mday,year,hour,minute,second; + getTime(month, mday, year, hour, minute, second); + return(hour); +} +int Xtime::getMDay(void) const +{ + int month,mday,year,hour,minute,second; + getTime(month, mday, year, hour, minute, second); + return(mday); +} + +int Xtime::getMonth(void) const +{ + int month,mday,year,hour,minute,second; + getTime(month, mday, year, hour, minute, second); + return(month); +} + +// based at year 0 (real 0, not 1970) +int Xtime::getYear(void) const +{ + int month,mday,year,hour,minute,second; + getTime(month, mday, year, hour, minute, second); + return(year); +} + + +// +// Set the seconds value (0-59) +// +bit8 Xtime::setSecond(sint32 sec) +{ + sint32 second=(msec_/ 1000)%60; + msec_-=(second*1000); + msec_+=(sec*1000); + return(TRUE); +} + +// +// Set the minutes value (0-59) +// +bit8 Xtime::setMinute(sint32 min) +{ + sint32 minute=(msec_/60000)%60; // 1000*60 + msec_-=(minute*60000); + msec_+=(min*60000); + return(TRUE); +} + +// +// Set the minutes value (0-23) +// +bit8 Xtime::setHour(sint32 hour) +{ + hour=(msec_/3600000)%24; // 1000*60*60 + msec_-=(hour*3600000); + msec_+=(hour*3600000); + return(TRUE); +} + +// +// Set the year value +// Results are undefined if you're moving from Feb 29, to a non leap year +// +bit8 Xtime::setYear(sint32 _year) +{ + // extract the date + int month,mday,year,hour,min,sec; + getTime(month,mday,year,hour,min,sec); + + // modify & rebuild + year=_year; + day_=Get_Day(month,mday,year); + return(TRUE); +} + +// +// Modify the month +// +bit8 Xtime::setMonth(sint32 _month) +{ + // extract the date + int month,mday,year,hour,min,sec; + getTime(month,mday,year,hour,min,sec); + + // modify & rebuild + month=_month; + day_=Get_Day(month,mday,year); + return(TRUE); +} + + +// +// Modify the day of the month +// +bit8 Xtime::setMDay(sint32 _mday) +{ + // extract the date + int month,mday,year,hour,min,sec; + getTime(month,mday,year,hour,min,sec); + + // modify & rebuild + mday=_mday; + day_=Get_Day(month,mday,year); + return(TRUE); +} + + +// +// Compare two times. The time better be normalized +// which it would be unless you've put bad stuff in it! +// +// 1 = *this > other +//-1 = *this < other +// 0 = *this == other +int Xtime::compare(const Xtime &other) const +{ + if ((day_==other.day_)&&(msec_==other.msec_)) + return(0); // equal + + else if (day_>other.day_) + return(1); + else if (day_other.msec_) + return(1); + else + return(-1); +} + + +bit8 Xtime::operator == ( const Xtime &other ) const +{ + bit8 retval=compare(other); + if (retval==0) + return(TRUE); + else + return(FALSE); +} + +bit8 Xtime::operator != ( const Xtime &other ) const +{ + bit8 retval=compare(other); + if (retval==0) + return(FALSE); + else + return(TRUE); +} + +bit8 Xtime::operator < ( const Xtime &other ) const +{ + int retval=compare(other); + if (retval==-1) + return(TRUE); + else + return(FALSE); +} + +bit8 Xtime::operator > ( const Xtime &other ) const +{ + int retval=compare(other); + if (retval==1) + return(TRUE); + else + return(FALSE); +} + +bit8 Xtime::operator <= ( const Xtime &other ) const +{ + int retval=compare(other); + if ((retval==-1)||(retval==0)) + return(TRUE); + else + return(FALSE); +} + +bit8 Xtime::operator >= ( const Xtime &other ) const +{ + int retval=compare(other); + if ((retval==1)||(retval==0)) + return(TRUE); + else + return(FALSE); +} + + +Xtime &Xtime::operator += (const Xtime &other) +{ + day_+=other.day_; + msec_+=other.msec_; + normalize(); + return *this; +} + +Xtime &Xtime::operator -= (const Xtime &other) +{ + day_-=other.day_; + msec_-=other.msec_; + normalize(); + return *this; +} + +Xtime Xtime::operator - (Xtime &other) +{ + Xtime temp(*this); + temp-=other; + return(temp); +} + +Xtime Xtime::operator + (Xtime &other) +{ + Xtime temp(*this); + temp+=other; + return(temp); +} + + +Xtime &Xtime::operator = (const Xtime &other) +{ + day_=other.day_; + msec_=other.msec_; + return *this; +} + + +Xtime &Xtime::operator += (const time_t other) +{ + addSeconds(other); + return *this; +} + + +Xtime &Xtime::operator -= (const time_t other) +{ + addSeconds(-((sint32)other)); + return *this; +} + + +Xtime Xtime::operator - (time_t other) +{ + Xtime temp(*this); + temp-=other; + return(temp); +} + + +Xtime Xtime::operator + (time_t other) +{ + Xtime temp(*this); + temp+=other; + return(temp); +} + +Xtime &Xtime::operator = (const time_t other) +{ + msec_=0; + day_=719528; // Jan 1, 1970 + addSeconds(other); + return *this; +} diff --git a/Generals/Code/Tools/matchbot/wlib/xtime.h b/Generals/Code/Tools/matchbot/wlib/xtime.h new file mode 100644 index 00000000000..b6210eee2cf --- /dev/null +++ b/Generals/Code/Tools/matchbot/wlib/xtime.h @@ -0,0 +1,146 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +xtime Neal Kettler + +This is version 2 of the Wtime library (now xtime). It now supports +time storage from the year 0 to well after the sun +will have gone supernova (OK, OK I admit it'll break in the +year 5 Million.) + +The call to update the current time will break in 2038. +Hopefully by then somebody will replace the lame time() +function :-) +\****************************************************************************/ + +#ifndef XTIME_HEADER +#define XTIME_HEADER + + +#include +#include +#include +#include + +#ifndef _WINDOWS +#include +#include +#include +#else +#include +#include +#endif + +#include +#include + +#include "wstypes.h" + +class Xtime +{ + public: + + Xtime(); // init to system time + Xtime( Xtime &other ); + Xtime( time_t other ); // 1970-2038 + ~Xtime(); + + void addSeconds(sint32 seconds); + + bit8 getTime(int &month, int &mday, int &year, int &hour, int &minute, + int &second) const; + + bit8 setTime(int month, int mday, int year, int hour, int minute, + int second); + + void update(); // Update members sec & usec to system time + // This will break after 2038 + +/******** + void PrintTime(FILE *out) const; + void PrintTime(char *out) const; + void PrintDate(FILE *out) const; + void PrintDate(char *out) const; +**********/ + + sint32 getDay(void) const; // Get days since year 0 + sint32 getMsec(void) const; // Get milliseconds into the day + + void setDay(sint32 day); + void setMsec(sint32 msec); + + void set(sint32 newday, sint32 newmsec); + bit8 ParseDate(char *in); + bit8 FormatTime(char *out, const char *format); + + bit8 getTimeval(struct timeval &tv); + + // All of these may return -1 if the time is invalid + int getSecond(void) const; // Second (0-60) (60 is for a leap second) + int getMinute(void) const; // Minute (0-59) + int getHour(void) const; // Hour (0-23) + int getMDay(void) const; // Day of Month (1-31) + int getWDay(void) const; // Day of Week (1-7) + int getYDay(void) const; // Day of Year (1-366) (366 = leap yr) + int getMonth(void) const; // Month (1-12) + int getYWeek(void) const; // Week of Year (1-53) + int getYear(void) const; // Year (e.g. 1997) + + // Modify the time components. Return FALSE if fail + bit8 setSecond(sint32 sec); + bit8 setMinute(sint32 min); + bit8 setHour(sint32 hour); + bit8 setYear(sint32 year); + bit8 setMonth(sint32 month); + bit8 setMDay(sint32 mday); + + void normalize(void); // move msec overflows to the day + + // Compare two times + int compare(const Xtime &other) const; + + // comparisons + bit8 operator == ( const Xtime &other ) const; + bit8 operator != ( const Xtime &other ) const; + bit8 operator < ( const Xtime &other ) const; + bit8 operator > ( const Xtime &other ) const; + bit8 operator <= ( const Xtime &other ) const; + bit8 operator >= ( const Xtime &other ) const; + + // assignments + Xtime &operator = (const Xtime &other); + Xtime &operator = (const time_t other); + + // signed + Xtime &operator += (const Xtime &other); + Xtime &operator -= (const Xtime &other); + Xtime operator + (Xtime &other); + Xtime operator - (Xtime &other); + + Xtime &operator += (const time_t other); + Xtime &operator -= (const time_t other); + Xtime operator + (time_t other); + Xtime operator - (time_t other); + + protected: + sint32 day_; // days since Jan 1, 0 + sint32 msec_; // milliseconds (thousandths of a sec) +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wnet/field.cpp b/Generals/Code/Tools/matchbot/wnet/field.cpp new file mode 100644 index 00000000000..35973709371 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wnet/field.cpp @@ -0,0 +1,319 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * Actual member function for the field class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +#include +#ifndef _WINDOWS +#include +#else +#define Win32_Winsock +#include +#endif +#include "field.h" + + +// private member func +void FieldClass::Clear(void) +{ + delete[](Data); + + strcpy(ID,""); + DataType=0; + Size=0; + Data=NULL; + Next=NULL; +} + + +FieldClass::FieldClass(char *id, char data) +{ + Data=NULL; + Set(id,data); +} + +FieldClass::FieldClass(char *id, unsigned char data) +{ + Data=NULL; + Set(id,data); +} + +FieldClass::FieldClass(char *id, short data) +{ + Data=NULL; + Set(id,data); +} + +FieldClass::FieldClass(char *id, unsigned short data) +{ + Data=NULL; + Set(id,data); +} + +FieldClass::FieldClass(char *id, long data) +{ + Data=NULL; + Set(id,data); +} + +FieldClass::FieldClass(char *id, unsigned long data) +{ + Data=NULL; + Set(id,data); +} + +FieldClass::FieldClass(char *id, char *data) +{ + Data=NULL; + Set(id,data); +} + +FieldClass::FieldClass(char *id, void *data, int length) +{ + Data=NULL; + Set(id,data,length); +} + +void FieldClass::Set(char *id, char data) +{ + FieldClass *Nextsave=Next; + + Clear(); + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = Nextsave; +} + +void FieldClass::Set(char *id, unsigned char data) +{ + FieldClass *Nextsave=Next; + Clear(); + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = Nextsave; +} + +void FieldClass::Set(char *id, short data) +{ + FieldClass *Nextsave=Next; + Clear(); + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = Nextsave; +} + +void FieldClass::Set(char *id, unsigned short data) +{ + FieldClass *Nextsave=Next; + Clear(); + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = Nextsave; +} + +void FieldClass::Set(char *id, long data) +{ + FieldClass *Nextsave=Next; + Clear(); + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = Nextsave; +} + +void FieldClass::Set(char *id, unsigned long data) +{ + FieldClass *Nextsave=Next; + Clear(); + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = Nextsave; +} + +void FieldClass::Set(char *id, char *data) +{ + FieldClass *Nextsave=Next; + Clear(); + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_STRING; + Size = (unsigned short)(strlen(data)+1); + Data = new char[Size]; + memcpy(Data, data, Size); + Next = Nextsave; +} + + +void FieldClass::Set(char *id, void *data, int length) +{ + FieldClass *Nextsave=Next; + Clear(); + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHUNK; + Size = (unsigned short)length; + Data = new char[Size]; + memcpy(Data, data, Size); + Next = Nextsave; +} + + +FieldClass::~FieldClass() +{ + Clear(); +} + +// Fetch the datatype +int FieldClass::Get_Type(void) +{ + return(DataType); +} + +void *FieldClass::Get_Data(void) +{ +return(Data); +} + +char *FieldClass::Get_ID(void) +{ +return(ID); +} + +/************************************************************************** + * PACKETCLASS::HOST_TO_NET_FIELD -- Converts host field to net format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Host_To_Net(void) +{ + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = htons(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = htonl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } + // + // Finally convert over the data type and the size of the packet. + // + DataType = htons(DataType); + Size = htons(Size); +} + +/************************************************************************** + * PACKETCLASS::NET_TO_HOST_FIELD -- Converts net field to host format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Net_To_Host(void) +{ + // + // Finally convert over the data type and the size of the packet. + // + DataType = ntohs(DataType); + Size = ntohs(Size); + + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = ntohs(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = ntohl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } +} + diff --git a/Generals/Code/Tools/matchbot/wnet/field.h b/Generals/Code/Tools/matchbot/wnet/field.h new file mode 100644 index 00000000000..8a58f68f551 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wnet/field.h @@ -0,0 +1,101 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * This module takes care of maintaining the field list used to process * + * packets. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define FIELD_HEADER_SIZE (sizeof(FieldClass) - (sizeof(void *) * 2)) + +#define TYPE_CHAR 1 +#define TYPE_UNSIGNED_CHAR 2 +#define TYPE_SHORT 3 +#define TYPE_UNSIGNED_SHORT 4 +#define TYPE_LONG 5 +#define TYPE_UNSIGNED_LONG 6 +#define TYPE_STRING 7 +#define TYPE_CHUNK 20 + +class PacketClass; + +class FieldClass +{ + public: + friend PacketClass; + // + // Define constructors to be able to create all the different kinds + // of fields. + // + FieldClass(void) {}; + FieldClass(char *id, char data); + FieldClass(char *id, unsigned char data); + FieldClass(char *id, short data); + FieldClass(char *id, unsigned short data); + FieldClass(char *id, long data); + FieldClass(char *id, unsigned long data); + FieldClass(char *id, char *data); + FieldClass(char *id, void *data, int length); + + ~FieldClass(); + + // Change the field contents + void Set(char *id, char data); + void Set(char *id, unsigned char data); + void Set(char *id, short data); + void Set(char *id, unsigned short data); + void Set(char *id, long data); + void Set(char *id, unsigned long data); + void Set(char *id, char *data); + void Set(char *id, void *data, int length); + + int Get_Type(void); // get the datatype of this field + unsigned short Get_Size(void) { return Size; } + void * Get_Data(void); // get the datatype of this field + char * Get_ID(void); // get the datatype of this field + + void Host_To_Net(void); + void Net_To_Host(void); + + private: + + void Clear(void); // dealloc mem & zero safely + + char ID[4]; // id value of this field + unsigned short DataType; // id of the data type we are using + unsigned short Size; // size of the data portion of this field + char *Data; // pointer to the data portion of this field + FieldClass *Next; // pointer to the next field in the field list +}; + + + diff --git a/Generals/Code/Tools/matchbot/wnet/packet.cpp b/Generals/Code/Tools/matchbot/wnet/packet.cpp new file mode 100644 index 00000000000..a2741dbe481 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wnet/packet.cpp @@ -0,0 +1,522 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 24, 1996 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * *PacketClass::Find_Field -- Finds a field if it exists in the packets * + * Get_Field -- Find specified name and returns data * + * PacketClass::~PacketClass -- destroys a packet class be freeing list * + * PacketClass::Add_Field -- Adds a FieldClass entry to head of packet li* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +#include +#include +#include +#ifndef _WINDOWS +#include +#else +#define Win32_Winsock +#include +#endif + +#include "packet.h" + + +/************************************************************************** + * PACKETCLASS::~PACKETCLASS -- destroys a packet class be freeing list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +PacketClass::~PacketClass() +{ + FieldClass *current; + FieldClass *next; + + // + // Loop through the entire field list and delete each entry. + // + for (current = Head; current; current = next) { + next = current->Next; + delete(current); + } +} + + +/************************************************************************** + * PACKETCLASS::ADD_FIELD -- Adds a FieldClass entry to head of packet li * + * * + * INPUT: FieldClass * - a properly constructed field class entry. * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +void PacketClass::Add_Field(FieldClass *field) +{ + field->Next = Head; + Head = field; +} + +/************************************************************************** + * PACKETCLASS::PACKETCLASS -- Creates a Packet object from a COMMS packe * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +PacketClass::PacketClass(char *curbuf) +{ + int remaining_size; + // + // Pull the size and packet ID out of the linear packet stream. + // + Size = *((unsigned short *)curbuf); + curbuf += sizeof(unsigned short); + Size = ntohs(Size); + ID = *((short *)curbuf); + curbuf += sizeof(unsigned short); + ID = ntohs(ID); + Head = NULL; + + // + // Calculate the remaining size so that we can loop through the + // packets and extract them. + // + remaining_size = Size - 4; + + // + // Loop through the linear packet until we run out of room and + // create a field for each. + // + while (remaining_size > 0) + { + FieldClass *field = new FieldClass; + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(field, curbuf, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + remaining_size -= FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer + // + int size = ntohs(field->Size); + field->Data = new char[size]; + memcpy(field->Data, curbuf, size); + curbuf += size; + remaining_size -= size; + + // + // Make sure we allow for the pad bytes. + // + int pad = (4 - (ntohs(field->Size) & 3)) & 3; + curbuf += pad; + remaining_size -= pad; + + // + // Convert the field back to the host format + // + field->Net_To_Host(); + + // + // Finally add the field to the field list in the packet + // structure. + // + Add_Field(field); + } +} + +/************************************************************************** + * CREATE_COMMS_PACKET -- Walks field list creating a packet * + * * + * INPUT: short - the id of the packet so the server can identify it * + * unsigned short & - the size of the packet returned here * + * * + * OUTPUT: void * pointer to the linear packet data * + * * + * WARNINGS: This routine allocates memory that the user is responsible * + * for freeing. * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +char *PacketClass::Create_Comms_Packet(int &size) +{ + FieldClass *current; + + // + // Size starts at four because that is the size of the packet header. + // + size = 4; + + // + // Take a quick spin through and calculate the size of the packet we + // are building. + // + for (current = Head; current; current=current->Next) + { + size += (unsigned short)FIELD_HEADER_SIZE; // add in packet header size + size += current->Size; // add in data size + size += (4 - (size & 3)) & 3; // add in pad value to dword align next packet + } + + // + // Now that we know the size allocate a buffer big enough to hold the + // packet. + // + char *retval = new char[size]; + char *curbuf = retval; + + // + // write the size into the packet header + // + *((unsigned short *)curbuf) = (unsigned short)htons(size); + curbuf += sizeof(unsigned short); + *((short *)curbuf) = htons(ID); + curbuf += sizeof(unsigned short); + + // + // Ok now that the actual header information has been written we need to write out + // field information. + // + for (current = Head; current; current = current->Next) + { + // + // Temporarily convert the packet to net format (this saves alot of + // effort, and seems safe...) + // + current->Host_To_Net(); + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(curbuf, current, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer and then advance the buffer + // + memcpy(curbuf, current->Data, ntohs(current->Size)); + curbuf += ntohs(current->Size); + + // + // Finally take care of any pad bytes by setting them to 0 + // + int pad = (4 - (ntohs(current->Size) & 3)) & 3; + curbuf += pad; + + current->Net_To_Host(); + } + return(retval); +} + + +/************************************************************************** + * PACKETCLASS::FIND_FIELD -- Finds a field if it exists in the packets * + * * + * INPUT: char * - the id of the field we are looking for. * + * * + * OUTPUT: FieldClass * pointer to the field class * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +FieldClass *PacketClass::Find_Field(char *id) +{ + for (FieldClass *current = Head; current; current = current->Next) + { + if ( strncmp(id, current->ID, 4) == 0) + return current; + } + return NULL; +} + +// gks 9/25/2000 +FieldClass *PacketClass::Get_Field_At(int position) +{ + int i = 0; + FieldClass *current = Head; + for (; (current && (i < position)); current = current->Next, i++) { + } + + if (current) return current; + else return NULL; +} + +// gks 9/25/2000 +int PacketClass::Get_Num_Fields() +{ + int i = 0; + + for (FieldClass *current = Head; current; current = current->Next, i++) {} + + return i; + +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bit8 PacketClass::Get_Field(char *id, char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bit8 PacketClass::Get_Field(char *id, unsigned char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bit8 PacketClass::Get_Field(char *id, short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bit8 PacketClass::Get_Field(char *id, unsigned short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bit8 PacketClass::Get_Field(char *id, long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((long *)field->Data); + } + return((field) ? true : false); +} + + + +bit8 PacketClass::Get_Field(char *id, int &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((int *)field->Data); + } + return((field) ? true : false); +} + + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data as a string * + * * + * INPUT: char * - the id of the field that holds the data. * + * char * - the string to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The string is not changed if the field is not found. It * + * is assumed that the string variabled specified by the * + * pointer is large enough to hold the data. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bit8 PacketClass::Get_Field(char *id, char *data) +{ + FieldClass *field = Find_Field(id); + if (field) { + strcpy(data, (char *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bit8 PacketClass::Get_Field(char *id, unsigned long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned long *)field->Data); + } + return((field) ? true : false); +} + +bit8 PacketClass::Get_Field(char *id, unsigned &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned *)field->Data); + } + return((field) ? true : false); +} + + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * void * - the reference to store the data into * + * int - the length of the buffer passed in * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 6/4/96 4:46PM ST : Created * + *========================================================================*/ +bit8 PacketClass::Get_Field(char *id, void *data, int &length) +{ + FieldClass *field = Find_Field(id); + if (field) { + memcpy (data, field->Data, MIN(field->Size, length)); + length = (int) field->Size; + } + return((field) ? true : false); +} + + +unsigned short PacketClass::Get_Field_Size(char* id) +{ + FieldClass *field = Find_Field(id); + if (field) + return field->Get_Size(); + else + return 0; +} + diff --git a/Generals/Code/Tools/matchbot/wnet/packet.h b/Generals/Code/Tools/matchbot/wnet/packet.h new file mode 100644 index 00000000000..1bd2141833c --- /dev/null +++ b/Generals/Code/Tools/matchbot/wnet/packet.h @@ -0,0 +1,108 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/19/96 * + * * + * Last Update : April 19, 1996 [PWG] * + * * + * This header defines the functions for the PacketClass. The packet * + * class is used to create a linked list of field entries which can be * + * converted to a linear packet in a COMMS API compatible format. * + * * + * Packets can be created empty and then have fields added to them or can * + * be created from an existing linear packet. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "field.h" +#include + + +class PacketClass +{ + public: + + PacketClass(short id = 0) + { + Size = 0; + ID = id; + Head = 0; + } + PacketClass(char *cur_buf); + ~PacketClass(); + + // + // This function allows us to add a field to the start of the list. As the field is just + // a big linked list it makes no difference which end we add a member to. + // + void Add_Field(FieldClass *field); + + // + // These conveniance functions allow us to add a field directly to the list without + // having to worry about newing one first. + // + void Add_Field(char *field, char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, char *data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, void *data, int length) {Add_Field(new FieldClass(field, data, length));}; + + // + // These functions search for a field of a given name in the list and + // return the data via a reference value. + // + FieldClass *Find_Field(char *id); + + bit8 Get_Field(char *id, int &data); + bit8 Get_Field(char *id, char &data); + bit8 Get_Field(char *id, unsigned char &data); + bit8 Get_Field(char *id, short &data); + bit8 Get_Field(char *id, unsigned short &data); + bit8 Get_Field(char *id, long &data); + bit8 Get_Field(char *id, unsigned long &data); + bit8 Get_Field(char *id, unsigned &data); + bit8 Get_Field(char *id, char *data); + bit8 Get_Field(char *id, void *data, int &length); + unsigned short Get_Field_Size(char* id); + + // gks 9/25/2000 + FieldClass *Get_Field_At(int position); + int Get_Num_Fields(); + + char *Create_Comms_Packet(int &size); + + private: + unsigned short Size; + short ID; + FieldClass *Head; + FieldClass *Current; +}; + diff --git a/Generals/Code/Tools/matchbot/wnet/tcp.cpp b/Generals/Code/Tools/matchbot/wnet/tcp.cpp new file mode 100644 index 00000000000..4c26d3231dd --- /dev/null +++ b/Generals/Code/Tools/matchbot/wnet/tcp.cpp @@ -0,0 +1,1242 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +TCP Neal Kettler neal@westwood.com +****************************************************************************** + +A general purpose TCP class that can be used in either CLIENT or +SERVER mode. Note that this uses non-blocking sockets. + +The FD_* macros: + + FD_CLR(int fd, fd_set *set); // clear a single FD + FD_ISSET(int fd, fd_set *set); // check whether a single FD is set + FD_SET(int fd, fd_set *set); // set a single FD + FD_ZERO(fd_set * set); // clear the entire set + +NOTE: The fd_set returned by 'Wait' is static, don't call delete +on it! + + +If you are writing a CLIENT: + The last argument to many functions is an integer whichFD, this is used +only by SERVER mode, so you can omit this argument. Sample Code: + +fd_set *fdSet; +uint8 *buff=new uint8[1024]; +int retval; +TCP tcp(CLIENT); + +tcp.Bind((uint32)0,(uint16)0); // let system pick local IP and a Port for you +tcp.Connect("tango",13); // can connect by name or "10.1.1.10" + // or the integer in host byte order + +fdSet=tcp.Wait(10,0); // wait for UP TO 10 sec and 0 microseconds +if (FD_ISSET(tcp.GetFD(),fdSet)) // Is there something to read? +{ + retval=tcp.Read(buff,1024); // Read something + // Retval will contain the number of + // bytes read, or... + // 0 = remote end closed connection + // -1 = nothing to read + fprintf(stderr,"%s",buff); +} +else + fprintf(stderr,"Nothing was read!\n"); + + +If you are writing a SERVER: + + The structure called 'clientList' contains all the File Descriptors +that have connected to the server. Make sure you look at the FD_* +functions so you can use this sort of structure. When you are writing +a server, you need to specify the 'whichFD' arguments to all the +functions. Sample Code: + +fd_set *fdSet; +uint8 *buff=new uint8[1024]; +int retval; +TCP tcp(SERVER); + +tcp.Bind((uint32)0,(uint16)2121); // You need to bind to a well defined + // port number or nobody will know where + // to connect to. + +while (1) +{ + fdSet=tcp.Wait(-1,-1); // Wait until there is something on the socket + if (FD_ISSET(tcp.GetFD(),fdSet)) // somebody must want a connection + { + retval=tcp.GetConnection(); // Get a connection if somebody's trying + if (retval!=-1) + { + tcp.Write("Hello World!\n",strlen("Hello World!\n"),retval); + tcp.Close(retval); + } + } +} + + +\****************************************************************************/ + + +#include "tcp.h" +#include + +#ifndef _WINDOWS +#include +#define closesocket close +#endif + +// newMode should be either CLIENT or SERVER +TCP::TCP(int new_mode) +{ + mode=CLIENT; + maxFD=0; + fd = -1; + clientCount=0; + if ((new_mode==CLIENT)||(new_mode==SERVER)) + mode=new_mode; + FD_ZERO(&clientList); + connectionState=CLOSED; + inputDelay=5; + outputDelay=5; +} + +// Create a TCP object on a pre-existing socket +TCP::TCP(int new_mode,sint16 socket) +{ + sint32 retval; + + mode=CLIENT; + maxFD= socket; + fd = socket; + clientCount=0; + if ((new_mode==CLIENT)||(new_mode==SERVER)) + mode=new_mode; + FD_ZERO(&clientList); + + inputDelay=5; + outputDelay=5; + + + retval=SetBlocking(FALSE,socket); // set to NB mode + //DBGMSG("Setblocking: "<0)&&(c!=0)) + { + string[i]=c; + if (c=='\n') + return(string); + i++; + } + else if ((retval==0)&&(i==0)) + { + DBGMSG("Remote endpoint closed (1)"); + return(NULL); + } + else if (retval==0) + return(string); + } + return(string); +} + + +// only specify whichFD if this is a server +sint32 TCP::Read(uint8 *msg,uint32 len,sint32 whichFD) +{ + sint32 retval; + //DBGMSG("In read, mode: "<0) + bytes_read+=retval; + // otherwise some error + } + return(bytes_read); +} + + + +// only specify whichFD if this is a server +// Peek at data in system buffer +sint32 TCP::Peek(uint8 *msg,uint32 len,sint32 whichFD) +{ + sint32 retval; + if (mode==CLIENT) + { + retval=recv(fd,(char *)msg,len,MSG_PEEK); + if (retval==0) + Close(); + return(retval); + } + else if (mode==SERVER) + { + if ((whichFD<=maxFD) && (FD_ISSET(whichFD,&clientList))) + { + retval=recv(whichFD,(char *)msg,len,MSG_PEEK); + if (retval==0) + Close(whichFD); + return(retval); + } + else + return(0); // closed + } + return(-1); +} + + +// only specify whichFD if this is a server +// (this is used for non-8 bit clean pipes, you probably don't +// want to use it!) +sint32 TCP::EncapsulatedRead(uint8 *msg,uint32 len,sint32 whichFD) +{ + sint32 retval,bytesRead=0; + uint32 i; + char data; + + if (mode==CLIENT) + whichFD=fd; + else if (mode==SERVER) + { + if ((whichFD>maxFD) || (!FD_ISSET(whichFD,&clientList))) + return(0); + } + else + return(-1); + + for (i=0; i=0; i--) + if (FD_ISSET(i,&clientList)) + { + maxFD=i; + break; + } + } + FD_CLR((uint32)whichFD,&clientList); + clientCount--; + return(closesocket(whichFD)); + } + } + return(-1); +} + + + +// if 'sec' AND 'usec' are -1 then this will sleep until +// there is socket activity + +int TCP::Wait(sint32 sec,sint32 usec,fd_set &returnSet,sint32 whichFD) +{ + fd_set inputSet; + + FD_ZERO(&inputSet); + + if (mode==SERVER) + { + if (whichFD==0) + { + inputSet=clientList; + if (fd > 0) + FD_SET(fd,&inputSet); + } + else if (whichFD > 0) + FD_SET(whichFD,&inputSet); + } + else if (mode==CLIENT) + { + if (whichFD==0) + whichFD=fd; + if (whichFD > 0) + FD_SET(whichFD,&inputSet); + } + + return(Wait(sec,usec,inputSet,returnSet)); +} + +int TCP::Wait(sint32 sec,sint32 usec,fd_set &givenSet,fd_set &returnSet) +{ + Wtime timeout; + Wtime timenow; + Wtime timethen; + fd_set backupSet; + int retval=0,done,givenMax; + bit8 noTimeout=FALSE; + timeval tv; + + returnSet=givenSet; + backupSet=returnSet; + + if ((sec==-1)&&(usec==-1)) + noTimeout=TRUE; + + timeout.SetSec(sec); + timeout.SetUsec(usec); + timethen+=timeout; + + givenMax=maxFD; + for (uint32 i=0; i<(sizeof(fd_set)*8); i++) // i=maxFD+1 + { + if (FD_ISSET(i,&givenSet)) + givenMax=i; + } + + done=0; + while( ! done) + { + if (noTimeout) + retval=select(givenMax+1,&returnSet,0,0,NULL); + else + { + timeout.GetTimevalMT(tv); + retval=select(givenMax+1,&returnSet,0,0,&tv); + } + + if (retval>=0) + done=1; + + else if ((retval==-1)&&(errno==EINTR)) // in case of signal + { + if (noTimeout==FALSE) + { + timenow.Update(); + timeout=timethen-timenow; + } + if ((noTimeout==FALSE)&&(timenow.GetSec()==0)&&(timenow.GetUsec()==0)) + done=1; + else + returnSet=backupSet; + } + else // maybe out of memory? + { + done=1; + } + } + return(retval); +} + + +void TCP::WaitWrite(sint32 whichFD) +{ + fd_set backupSet; + int retval=0,done; + fd_set outputSet; + + if (whichFD==0) + whichFD=fd; + + if (whichFD==-1) + return; + + FD_ZERO(&outputSet); + FD_SET(whichFD,&outputSet); + backupSet=outputSet; + + done=0; + while( ! done) + { + retval=select(maxFD+1,0,&outputSet,0,NULL); + + if (retval>=0) + done=1; + + else if ((retval==-1)&&(errno==EINTR)) // in case of signal + outputSet=backupSet; + else // maybe out of memory? + done=1; + } +} + +// Can a FD be written to? +bit8 TCP::CanWrite(sint32 whichFD) +{ + int retval=0; + fd_set outputSet; + Wtime timeout; + timeval tv; + + timeout.SetSec(0); + timeout.SetUsec(0); + + if (whichFD==0) + whichFD=fd; + + FD_ZERO(&outputSet); + FD_SET(whichFD,&outputSet); + + timeout.GetTimevalMT(tv); + retval=select(whichFD+1,0,&outputSet,0,&tv); + if (retval>0) + return(TRUE); + else + return(FALSE); +} + + +bit8 TCP::Bind(char *Host,uint16 port,bit8 reuseAddr) +{ + char hostName[100]; + struct hostent *hostStruct; + struct in_addr *hostNode; + + if (isdigit(Host[0])) + return ( Bind( ntohl(inet_addr(Host)), port,reuseAddr) ); + + strcpy(hostName, Host); + + hostStruct = gethostbyname(Host); + if (hostStruct == NULL) + return (0); + hostNode = (struct in_addr *) hostStruct->h_addr; + return ( Bind(ntohl(hostNode->s_addr),port,reuseAddr) ); +} + + +// You must call bind, implicit binding is for sissies +// Well... you can get implicit binding if you pass 0 for either arg + +bit8 TCP::Bind(uint32 IP,uint16 Port,bit8 reuseAddr) +{ + int retval; + int status; + + IP=htonl(IP); + Port=htons(Port); + + addr.sin_family=AF_INET; + addr.sin_port=Port; + addr.sin_addr.s_addr=IP; + fd=socket(AF_INET,SOCK_STREAM,DEFAULT_PROTOCOL); + if (fd==-1) + return(FALSE); + + retval=SetBlocking(FALSE,fd); + if (retval==-1) + ERRMSG("Couldn't set nonblocking mode!"); + + if (reuseAddr==TRUE) + { + uint32 opval; + + #ifdef SO_REUSEPORT +/****************** this may make the socket get garbage data?? + opval=1; + retval=setsockopt(fd,SOL_SOCKET,SO_REUSEPORT,(char *)&opval,sizeof(opval)); + if (retval!=0) + fprintf(stderr,"Could not set socket to SO_REUSEPORT\n"); +**********************/ + #endif + #ifdef SO_REUSEADDR + opval=1; + retval=setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&opval,sizeof(opval)); + if (retval!=0) + fprintf(stderr,"Could not set socket to SO_REUSEADDR\n"); + #endif + } + + retval=bind(fd,(struct sockaddr *)&addr,sizeof(addr)); + #ifdef _WINDOWS + if (retval==SOCKET_ERROR) + retval=-1; + #endif + + if (retval==-1) + { + status=GetStatus(); + DBGMSG("Bind failure (" << status << ") IP "<< IP <<" PORT "<< ntohs(Port)); + return(FALSE); + } + myIP=IP; + myPort=Port; + maxFD=fd; + + if (mode==SERVER) + listen(fd,64); //Solaris needs lots of listen slots for some reason + + return(TRUE); +} + + +// This is only for clients + +bit8 TCP::Connect(char *Host,uint16 port) +{ + char hostName[100]; + struct hostent *hostStruct; + struct in_addr *hostNode; + + if (isdigit(Host[0])) + return ( Connect( ntohl(inet_addr(Host)), port) ); + + strcpy(hostName, Host); + + hostStruct = gethostbyname(Host); + if (hostStruct == NULL) + {ERRMSG("Can't resolve host");return (0);} + hostNode = (struct in_addr *) hostStruct->h_addr; + return ( Connect(ntohl(hostNode->s_addr),port) ); +} + +bit8 TCP::Connect(uint32 IP,uint16 Port) +{ + int tries,result; + struct timeval sleep_time; + struct sockaddr_in serverAddr; + int status; + + IP=htonl(IP); + Port=htons(Port); + + serverAddr.sin_family=AF_INET; + serverAddr.sin_port=Port; + serverAddr.sin_addr.s_addr=IP; + + if (mode!=CLIENT) + {ERRMSG("Can't connect in server mode");return(FALSE);} + + tries=0; + result=-1; + + + // try 10 connects with a greater and greater sleep time after each one + // this can go on for upto 5.4 seconds + while ((tries < 10) && (result == -1)) + { + ClearStatus(); + result = connect(fd,(struct sockaddr *)&serverAddr, sizeof(serverAddr)); + status=GetStatus(); + + #ifdef _WINDOWS + if (result==SOCKET_ERROR) + result=-1; + #endif + + if ((status == ISCONN) && (result == -1)) + { + result = 0; + } + if (result == -1) + { + if ((status!=INPROGRESS)&&(status!=ALREADY)&&(status!=AGAIN)&& + (status!=WOULDBLOCK)) + { + Close(); + Bind(myIP,myPort); + } + tries++; + sleep_time.tv_sec = 0; + sleep_time.tv_usec = (100000*(tries+1)); + #ifdef WIN32 + Sleep((sleep_time.tv_usec)/1000); + #else + select(0, 0, 0, 0, &sleep_time); + #endif + } + } + + if (result == -1) + { + return(FALSE); + } + connectionState=CONNECTED; + return (TRUE); +} + + + +// Asynchronous Connection +bit8 TCP::ConnectAsync(char *Host,uint16 port) +{ + char hostName[100]; + struct hostent *hostStruct; + struct in_addr *hostNode; + + if (isdigit(Host[0])) + return ( ConnectAsync( ntohl(inet_addr(Host)), port) ); + + strcpy(hostName, Host); + + hostStruct = gethostbyname(Host); + if (hostStruct == NULL) + return (0); + hostNode = (struct in_addr *) hostStruct->h_addr; + return ( ConnectAsync(ntohl(hostNode->s_addr),port) ); +} + +// Asynchronous Connection +bit8 TCP::ConnectAsync(uint32 IP,uint16 Port) +{ + int result; + struct sockaddr_in serverAddr; + int status,connectErrno; + int retval; + + IP=htonl(IP); + Port=htons(Port); + + serverAddr.sin_family=AF_INET; + serverAddr.sin_port=Port; + serverAddr.sin_addr.s_addr=IP; + + if (mode!=CLIENT) + return(FALSE); + + result=-1; + + if (connectionState==CONNECTING) + { + if (IsConnected(fd)) + { + DBGMSG("CONNECTION COMPLETE at point 1"); + connectionState=CONNECTED; + return(TRUE); + } + else + return(TRUE); // Still trying + } + + ClearStatus(); + result = connect(fd,(struct sockaddr *)&serverAddr, sizeof(serverAddr)); + connectErrno=errno; + status=GetStatus(); + + #ifdef _WINDOWS + if (result==SOCKET_ERROR) + { + DBGMSG("Socket error 1 " << status); + result=-1; + } + #endif + + // If we have a bogus FD, try again after closing and re-binding + if ((result==-1)&&((status==BADF)||(status==NOTSOCK)||(status==INVAL))) + { + Close(); + retval=Bind(myIP,myPort); + DBGMSG("BIND = "<maxFD) + maxFD=clientFD; + FD_SET(clientFD,&clientList); + clientCount++; + } + return(clientFD); +} + +sint32 TCP::GetConnection(struct sockaddr *clientAddr) +{ + if (mode!=SERVER) + return(-1); + + sint32 clientFD; + int addrlen=sizeof(struct sockaddr); + + clientFD=accept(fd,(struct sockaddr *)clientAddr,&addrlen); + if (clientFD!=-1) + { + if (clientFD>maxFD) + maxFD=clientFD; + FD_SET(clientFD,&clientList); + clientCount++; + } + return(clientFD); +} + diff --git a/Generals/Code/Tools/matchbot/wnet/tcp.h b/Generals/Code/Tools/matchbot/wnet/tcp.h new file mode 100644 index 00000000000..3f63e4e9d36 --- /dev/null +++ b/Generals/Code/Tools/matchbot/wnet/tcp.h @@ -0,0 +1,199 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/****************************************************************************\ +TCP Neal Kettler neal@westwood.com + +\****************************************************************************/ + +#ifndef TCP_HEADER +#define TCP_HEADER + +#include +#include +#include +#include +#include +#include + +#ifdef _WINDOWS + +#include +#include +#define close _close +#define read _read +#define write _write + +#else //UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef signed int SOCKET; + +#endif + + +#ifdef AIX +#include +#endif + +#define DEFAULT_PROTOCOL 0 + +#include "wlib/wstypes.h" +#include "wlib/wdebug.h" +#include "wlib/wtime.h" + +class TCP +{ + +// DATA --------------- + +private: + int mode; // client or server + sint32 fd; // the primary FD + + uint32 myIP; // after bind myIP & myPort will be + uint16 myPort; // whatever we bound to + + struct sockaddr_in addr; + int maxFD; // value of the biggest FD + int clientCount; // how many clients open + + + sint32 inputDelay; // default delay for semi-blocking reads + sint32 outputDelay; // default delay for semi-blocking writes + + enum ConnectionState + { + CLOSED, + CONNECTING, + CONNECTED + } connectionState; // What state is client FD in + +public: + + enum + { + CLIENT = 1, + SERVER = 2 + }; + + // These defines specify a system independent way to + // get error codes for socket services. + enum + { + OK, // Everything's cool + UNKNOWN, // There was an error of unknown type + ISCONN, // The socket is already connected + INPROGRESS, // The socket is non-blocking and the operation + // isn't done yet + ALREADY, // The socket is already attempting a connection + // but isn't done yet + AGAIN, // Try again. + ADDRINUSE, // Address already in use + ADDRNOTAVAIL, // That address is not available on the remote host + BADF, // Not a valid FD + CONNREFUSED, // Connection was refused + INTR, // Operation was interrupted + NOTSOCK, // FD wasn't a socket + PIPE, // That operation just made a SIGPIPE + WOULDBLOCK, // That operation would block + INVAL, // Invalid + TIMEDOUT // Timeout + }; + + // for client list (if this is a server) + fd_set clientList; + + +// CODE ---------------- + +public: + TCP(int newMode); + TCP(int newMode,sint16 socket); + ~TCP(); + bit8 Bind(uint32 IP,uint16 port,bit8 reuseAddr=FALSE); + bit8 Bind(char *Host,uint16 port,bit8 reuseAddr=FALSE); + + sint32 GetMaxFD(void); + + bit8 Connect(uint32 IP,uint16 port); + bit8 Connect(char *Host,uint16 port); + bit8 ConnectAsync(uint32 IP,uint16 port); + bit8 ConnectAsync(char *Host,uint16 port); + + bit8 IsConnected(sint32 whichFD=0); + + sint32 GetFD(void); + sint32 GetClientCount(void) { return(clientCount); } + + // Get IP or Port of a connected endpoint + uint32 GetRemoteIP(sint32 whichFD=0); + uint16 GetRemotePort(sint32 whichFD=0); + + sint32 GetConnection(void); + sint32 GetConnection(struct sockaddr *clientAddr); + void WaitWrite(sint32 whichFD=0); + bit8 CanWrite(sint32 whichFD=0); + sint32 Write(const uint8 *msg,uint32 len,sint32 whichFD=0); + sint32 WriteNB(uint8 *msg,uint32 len,sint32 whichFD=0); + sint32 EncapsulatedWrite(uint8 *msg,uint32 len,sint32 whichFD=0); + sint32 WriteString(char *msg,sint32 whichFD=0); + sint32 Printf(sint32 whichFD,const char *format,...); + sint32 Read(uint8 *msg,uint32 len,sint32 whichFD=0); + sint32 TimedRead(uint8 *msg,uint32 len,int seconds,sint32 whichFD=0); + sint32 Peek(uint8 *msg,uint32 len,sint32 whichFD=0); + sint32 EncapsulatedRead(uint8 *msg,uint32 len,sint32 whichFD=0); + + char *Gets(char *string,int n,int whichFD=0); + + // Wait on all sockets (or a specified one) + // return when ready for reading (or timeout occurs) + int Wait(sint32 sec,sint32 usec,fd_set &returnSet,sint32 whichFD=0); + int Wait(sint32 sec,sint32 usec,fd_set &inputSet,fd_set &returnSet); + + int GetStatus(void); + void ClearStatus(void); + + //sint32 GetSockStatus(sint32 whichFD=0); + + // give up ownership of the socket without closing it + void DisownSocket(void); + + sint32 Close(sint32 whichFD=0); + sint32 CloseAll(void); // close all sockets (same as close for client) + + sint32 SetBlocking(bit8 block,sint32 whichFD=0); + + // Set default delays for semi-blocking reads & writes + // default input = 5, output = 5 + // this is new and not used everywhere + // + bit8 SetInputDelay(sint32 delay) { inputDelay=delay; return(TRUE); }; + bit8 SetOutputDelay(sint32 delay) { outputDelay=delay; return(TRUE); }; + +}; + +#endif diff --git a/Generals/Code/Tools/matchbot/wnet/udp.cpp b/Generals/Code/Tools/matchbot/wnet/udp.cpp new file mode 100644 index 00000000000..afa6d16150f --- /dev/null +++ b/Generals/Code/Tools/matchbot/wnet/udp.cpp @@ -0,0 +1,372 @@ +/* +** Command & Conquer Generals(tm) +** Copyright 2025 Electronic Arts Inc. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include "udp.h" +#include "wlib/wdebug.h" + +UDP::UDP() +{ + fd=0; +} + +UDP::~UDP() +{ +} + +sint32 UDP::Bind(char *Host,uint16 port) +{ + char hostName[100]; + struct hostent *hostStruct; + struct in_addr *hostNode; + + if (isdigit(Host[0])) + return ( Bind( ntohl(inet_addr(Host)), port) ); + + strcpy(hostName, Host); + + hostStruct = gethostbyname(Host); + if (hostStruct == NULL) + return (0); + hostNode = (struct in_addr *) hostStruct->h_addr; + return ( Bind(ntohl(hostNode->s_addr),port) ); +} + +// You must call bind, implicit binding is for sissies +// Well... you can get implicit binding if you pass 0 for either arg +sint32 UDP::Bind(uint32 IP,uint16 Port) +{ + int retval; + int status; + + IP=htonl(IP); + Port=htons(Port); + + addr.sin_family=AF_INET; + addr.sin_port=Port; + addr.sin_addr.s_addr=IP; + fd=socket(AF_INET,SOCK_DGRAM,DEFAULT_PROTOCOL); + #ifdef _WINDOWS + if (fd==SOCKET_ERROR) + fd=-1; + #endif + if (fd==-1) + return(UNKNOWN); + + retval=bind(fd,(struct sockaddr *)&addr,sizeof(addr)); + + #ifdef _WINDOWS + if (retval==SOCKET_ERROR) + retval=-1; + #endif + if (retval==-1) + { + status=GetStatus(); + //CERR("Bind failure (" << status << ") IP " << IP << " PORT " << Port ) + return(status); + } + + int namelen=sizeof(addr); + getsockname(fd, (struct sockaddr *)&addr, &namelen); + + myIP=ntohl(addr.sin_addr.s_addr); + myPort=ntohs(addr.sin_port); + + retval=SetBlocking(FALSE); + if (retval==-1) + fprintf(stderr,"Couldn't set nonblocking mode!\n"); + + return(OK); +} + +bit8 UDP::getLocalAddr(uint32 &ip, uint16 &port) +{ + ip=myIP; + port=myPort; + return(OK); +} + + +// private function +sint32 UDP::SetBlocking(bit8 block) +{ + #ifdef _WINDOWS + unsigned long flag=1; + if (block) + flag=0; + int retval; + retval=ioctlsocket(fd,FIONBIO,&flag); + if (retval==SOCKET_ERROR) + return(UNKNOWN); + else + return(OK); + #else // UNIX + int flags = fcntl(fd, F_GETFL, 0); + if (block==FALSE) // set nonblocking + flags |= O_NONBLOCK; + else // set blocking + flags &= ~(O_NONBLOCK); + + if (fcntl(fd, F_SETFL, flags) < 0) + { + return(UNKNOWN); + } + return(OK); + #endif +} + + +sint32 UDP::Write(uint8 *msg,uint32 len,uint32 IP,uint16 port) +{ + sint32 retval; + struct sockaddr_in to; + + // This happens frequently + if ((IP==0)||(port==0)) return(ADDRNOTAVAIL); + + errno=0; + to.sin_port=htons(port); + to.sin_addr.s_addr=htonl(IP); + to.sin_family=AF_INET; + + ClearStatus(); + retval=sendto(fd,(char *)msg,len,0,(struct sockaddr *)&to,sizeof(to)); + #ifdef _WINDOWS + if (retval==SOCKET_ERROR) + retval=-1; + #endif + + return(retval); +} + +sint32 UDP::Read(uint8 *msg,uint32 len,sockaddr_in *from) +{ + sint32 retval; + int alen=sizeof(sockaddr_in); + + if (from!=NULL) + { + retval=recvfrom(fd,(char *)msg,len,0,(struct sockaddr *)from,&alen); + #ifdef _WINDOWS + if (retval==SOCKET_ERROR) + retval=-1; + #endif + } + else + { + retval=recvfrom(fd,(char *)msg,len,0,NULL,NULL); + #ifdef _WINDOWS + if (retval==SOCKET_ERROR) + retval=-1; + #endif + } + return(retval); +} + + +void UDP::ClearStatus(void) +{ + #ifndef _WINDOWS + errno=0; + #endif +} + +UDP::sockStat UDP::GetStatus(void) +{ + #ifdef _WINDOWS + int status=WSAGetLastError(); + if (status==0) return(OK); + else if (status==WSAEINTR) return(INTR); + else if (status==WSAEINPROGRESS) return(INPROGRESS); + else if (status==WSAECONNREFUSED) return(CONNREFUSED); + else if (status==WSAEINVAL) return(INVAL); + else if (status==WSAEISCONN) return(ISCONN); + else if (status==WSAENOTSOCK) return(NOTSOCK); + else if (status==WSAETIMEDOUT) return(TIMEDOUT); + else if (status==WSAEALREADY) return(ALREADY); + else if (status==WSAEWOULDBLOCK) return(WOULDBLOCK); + else if (status==WSAEBADF) return(BADF); + else return(UNKNOWN); + #else + int status=errno; + if (status==0) return(OK); + else if (status==EINTR) return(INTR); + else if (status==EINPROGRESS) return(INPROGRESS); + else if (status==ECONNREFUSED) return(CONNREFUSED); + else if (status==EINVAL) return(INVAL); + else if (status==EISCONN) return(ISCONN); + else if (status==ENOTSOCK) return(NOTSOCK); + else if (status==ETIMEDOUT) return(TIMEDOUT); + else if (status==EALREADY) return(ALREADY); + else if (status==EAGAIN) return(AGAIN); + else if (status==EWOULDBLOCK) return(WOULDBLOCK); + else if (status==EBADF) return(BADF); + else return(UNKNOWN); + #endif +} + + + +// +// Wait for net activity on this socket +// +int UDP::Wait(sint32 sec,sint32 usec,fd_set &returnSet) +{ + fd_set inputSet; + + FD_ZERO(&inputSet); + FD_SET(fd,&inputSet); + + return(Wait(sec,usec,inputSet,returnSet)); +} + +// +// Wait for net activity on a list of sockets +// +int UDP::Wait(sint32 sec,sint32 usec,fd_set &givenSet,fd_set &returnSet) +{ + Wtime timeout,timenow,timethen; + fd_set backupSet; + int retval=0,done,givenMax; + bit8 noTimeout=FALSE; + timeval tv; + + returnSet=givenSet; + backupSet=returnSet; + + if ((sec==-1)&&(usec==-1)) + noTimeout=TRUE; + + timeout.SetSec(sec); + timeout.SetUsec(usec); + timethen+=timeout; + + givenMax=fd; + for (uint32 i=0; i<(sizeof(fd_set)*8); i++) // i=maxFD+1 + { + if (FD_ISSET(i,&givenSet)) + givenMax=i; + } + ///DBGMSG("WAIT fd="<. +*/ + +#ifndef UDP_HEADER +#define UDP_HEADER + +#include +#include +#include +#include +#include + +#ifdef _WINDOWS +#include +#include +#define close _close +#define read _read +#define write _write + +#else //UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifdef AIX +#include +#endif + +#define DEFAULT_PROTOCOL 0 + +#include +#include + +class UDP +{ + // DATA + private: + sint32 fd; + uint32 myIP; + uint16 myPort; + struct sockaddr_in addr; + + // These defines specify a system independent way to + // get error codes for socket services. + enum sockStat + { + OK = 0, // Everything's cool + UNKNOWN = -1, // There was an error of unknown type + ISCONN = -2, // The socket is already connected + INPROGRESS = -3, // The socket is non-blocking and the operation + // isn't done yet + ALREADY = -4, // The socket is already attempting a connection + // but isn't done yet + AGAIN = -5, // Try again. + ADDRINUSE = -6, // Address already in use + ADDRNOTAVAIL = -7, // That address is not available on the remote host + BADF = -8, // Not a valid FD + CONNREFUSED = -9, // Connection was refused + INTR =-10, // Operation was interrupted + NOTSOCK =-11, // FD wasn't a socket + PIPE =-12, // That operation just made a SIGPIPE + WOULDBLOCK =-13, // That operation would block + INVAL =-14, // Invalid + TIMEDOUT =-15 // Timeout + }; + +// CODE + private: + sint32 SetBlocking(bit8 block); + + public: + UDP(); + ~UDP(); + sint32 Bind(uint32 IP,uint16 port); + sint32 Bind(char *Host,uint16 port); + sint32 Write(uint8 *msg,uint32 len,uint32 IP,uint16 port); + sint32 Read(uint8 *msg,uint32 len,sockaddr_in *from); + sockStat GetStatus(void); + void ClearStatus(void); + int Wait(sint32 sec,sint32 usec,fd_set &returnSet); + int Wait(sint32 sec,sint32 usec,fd_set &givenSet,fd_set &returnSet); + + bit8 getLocalAddr(uint32 &ip, uint16 &port); + sint32 getFD(void) { return(fd); } + + bit8 SetInputBuffer(uint32 bytes); + bit8 SetOutputBuffer(uint32 bytes); + int GetInputBuffer(void); + int GetOutputBuffer(void); +}; + +#endif diff --git a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h index 06e48232761..ca9bbf46fce 100644 --- a/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h +++ b/GeneralsMD/Code/GameEngineDevice/Include/W3DDevice/GameClient/W3DAssetManager.h @@ -54,6 +54,7 @@ class Vector3; class VertexMaterialClass; +class GrannyAnimManagerClass; class W3DAssetManager: public WW3DAssetManager { @@ -107,6 +108,8 @@ class W3DAssetManager: public WW3DAssetManager int replaceHLODTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex); int replaceMeshTexture(RenderObjClass *robj, TextureClass *oldTex, TextureClass *newTex); + GrannyAnimManagerClass *m_GrannyAnimManager; + //'E&B' customizations /* virtual RenderObjClass * Create_Render_Obj(const char * name, float scale, const Vector3 &hsv_shift); TextureClass * Get_Texture_With_HSV_Shift(const char * filename, const Vector3 &hsv_shift, TextureClass::MipCountType mip_level_count = TextureClass::MIP_LEVELS_ALL); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp index 4b4eb326065..90647da5e2c 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Shadow/W3DVolumetricShadow.cpp @@ -850,6 +850,43 @@ Int W3DShadowGeometry::initFromMesh(RenderObjClass *robj) Int W3DShadowGeometry::init(RenderObjClass *robj) { return TRUE; +// m_robj=robj; +/* //code to deal with granny - don't think we'll use shadow volumes on these!? + granny_file *fileInfo=robj->getPrototype().m_file; + + for (Int modelIndex=0; modelIndexModelCount; modelIndex++) + { + granny_model *sourceModel = fileInfo->Models[modelIndex]; + if (stricmp(sourceModel->Name,"AABOX") == 0) + { //found a collision box, copy out data + int MeshCount = sourceModel->MeshBindingCount; + if (MeshCount==1) + { + granny_mesh *sourceMesh = sourceModel->MeshBindings[0].Mesh; + granny_pn33_vertex *Vertices = (granny_pn33_vertex *)sourceMesh->PrimaryVertexData->Vertices; + Vector3 points[24]; + + assert (sourceMesh->PrimaryVertexData->VertexCount <= 24); + + for (Int boxVertex=0; boxVertexPrimaryVertexData->VertexCount; boxVertex++) + { points[boxVertex].Set(Vertices[boxVertex].Position[0], + Vertices[boxVertex].Position[1], + Vertices[boxVertex].Position[2]); + } + box.Init(points,sourceMesh->PrimaryVertexData->VertexCount); + } + } + else + { //mesh is part of model + int meshCount = sourceModel->MeshBindingCount; + for (Int meshIndex=0; meshIndexMeshBindings[meshIndex].Mesh; + if (sourceMesh->PrimaryVertexData) + vertexCount+=sourceMesh->PrimaryVertexData->VertexCount; + } + } + }*/ } /////////////////////////////////////////////////////////////////////////////// @@ -4051,7 +4088,7 @@ int W3DShadowGeometryManager::Load_Geom(RenderObjClass *robj, const char *name) } /* -** Iterator converter from HashableClass to W3DShadowGeometry +** Iterator converter from HashableClass to GrannyAnimClass */ W3DShadowGeometry * W3DShadowGeometryManagerIterator::Get_Current_Geom( void ) { diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp index 43c02567710..dd79e49b621 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp @@ -151,11 +151,17 @@ RenderObjClass * W3DPrototypeClass::Create(void) //--------------------------------------------------------------------- W3DAssetManager::W3DAssetManager(void) { +#ifdef INCLUDE_GRANNY_IN_BUILD + m_GrannyAnimManager = NEW GrannyAnimManagerClass; +#endif } //--------------------------------------------------------------------- W3DAssetManager::~W3DAssetManager(void) { +#ifdef INCLUDE_GRANNY_IN_BUILD + delete m_GrannyAnimManager; +#endif } #ifdef DUMP_PERF_STATS @@ -724,6 +730,12 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( GetPrecisionTimer(&startTime64); #endif +#ifdef INCLUDE_GRANNY_IN_BUILD + Bool isGranny = false; + char *pext=strrchr(name,'.'); //find file extension + if (pext) + isGranny=(strnicmp(pext,".GR2",4) == 0); +#endif Bool reallyscale = (WWMath::Fabs(scale - ident_scale) > scale_epsilon); Bool reallycolor = (color & 0xFFFFFF) != 0; //black is not a valid color and assumes no custom coloring. Bool reallytexture = (oldTexture != NULL && newTexture != NULL); @@ -745,6 +757,12 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( // see if we got a cached version RenderObjClass *rendobj = NULL; +#ifdef INCLUDE_GRANNY_IN_BUILD + if (isGranny) + { //Granny objects share the same prototype since they allow instance scaling. + strcpy(newname,name); //use same name for all granny objects at any scale. + } +#endif Set_WW3D_Load_On_Demand(false); // munged name will never be found in a file. rendobj = WW3DAssetManager::Create_Render_Obj(newname); if (rendobj) @@ -752,6 +770,11 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( //when we need to save this render object to a file. Used during saving //of fog of war ghost objects. rendobj->Set_ObjectColor(color); +#ifdef INCLUDE_GRANNY_IN_BUILD + if (isGranny) + ///@todo Granny objects are realtime scaled - fix to scale like W3D. + rendobj->Set_ObjectScale(scale); +#endif Set_WW3D_Load_On_Demand(true); // Auto Load. #ifdef DUMP_PERF_STATS @@ -774,11 +797,16 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( { // If we didn't find one, try to load on demand char filename [MAX_PATH]; - const char *mesh_name = strchr (name, '.'); + const char *mesh_name = ::strchr (name, '.'); if (mesh_name != NULL) { - lstrcpyn(filename, name, ((int)mesh_name) - ((int)name) + 1); - lstrcat(filename, ".w3d"); + ::lstrcpyn(filename, name, ((int)mesh_name) - ((int)name) + 1); +#ifdef INCLUDE_GRANNY_IN_BUILD + if (isGranny) + ::lstrcat(filename, ".gr2"); + else +#endif + ::lstrcat(filename, ".w3d"); } else { sprintf( filename, "%s.w3d", name); } @@ -787,7 +815,15 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( if ( Load_3D_Assets( filename ) == false ) { StringClass new_filename = StringClass("..\\") + filename; - Load_3D_Assets(new_filename); + if (Load_3D_Assets( new_filename ) == false) + { +#ifdef INCLUDE_GRANNY_IN_BUILD + char *mesh_name = ::strchr (filename, '.'); + ::lstrcpyn (mesh_name, ".gr2",5); + Load_3D_Assets( filename ); + isGranny=true; +#endif + } } proto = Find_Prototype(name); // try again @@ -817,23 +853,35 @@ RenderObjClass * W3DAssetManager::Create_Render_Obj( #endif return NULL; } +#ifdef INCLUDE_GRANNY_IN_BUILD + if (!isGranny) +#endif + { + if (reallyscale) + rendobj->Scale(scale); //this also makes it unique - if (reallyscale) - rendobj->Scale(scale); //this also makes it unique + Make_Unique(rendobj,reallyscale,reallycolor); - Make_Unique(rendobj,reallyscale,reallycolor); + if (reallytexture) + { + TextureClass *oldTex = Get_Texture(oldTexture); + TextureClass *newTex = Get_Texture(newTexture); + replaceAssetTexture(rendobj,oldTex,newTex); + REF_PTR_RELEASE(newTex); + REF_PTR_RELEASE(oldTex); + } - if (reallytexture) + if (reallycolor) + Recolor_Asset(rendobj,color); + } +#ifdef INCLUDE_GRANNY_IN_BUILD + else { - TextureClass *oldTex = Get_Texture(oldTexture); - TextureClass *newTex = Get_Texture(newTexture); - replaceAssetTexture(rendobj,oldTex,newTex); - REF_PTR_RELEASE(newTex); - REF_PTR_RELEASE(oldTex); + ///@todo Granny objects are realtime scaled - fix to scale like W3D. + rendobj->Set_ObjectScale(scale); + return rendobj; } - - if (reallycolor) - Recolor_Asset(rendobj,color); +#endif W3DPrototypeClass *w3dproto = newInstance(W3DPrototypeClass)(rendobj, newname); rendobj->Release_Ref(); @@ -970,6 +1018,14 @@ bool W3DAssetManager::Load_3D_Assets( const char * filename ) GetPrecisionTimer(&startTime64); #endif +#ifdef INCLUDE_GRANNY_IN_BUILD + Bool isGranny = false; + char *pext=strrchr(filename,'.'); //find file extension + if (pext) + isGranny=(strnicmp(pext,".GR2",4) == 0); + if (!isGranny) +#endif + // Try to find an existing prototype char basename[512]; strcpy(basename, filename); @@ -1014,6 +1070,40 @@ bool W3DAssetManager::Load_3D_Assets( const char * filename ) #endif return result; +#ifdef INCLUDE_GRANNY_IN_BUILD + //Loading assets for Granny File + PrototypeClass * newproto = NULL; + newproto = _GrannyLoader.Load_W3D(filename); + /* + ** Now, see if the prototype that we loaded has a duplicate + ** name with any of our currently loaded prototypes (can't have that!) + */ + if (newproto != NULL) { + if (!Render_Obj_Exists(newproto->Get_Name())) { + /* + ** Add the new, unique prototype to our list + */ + Add_Prototype(newproto); + } else { + /* + ** Warn the user about a name collision with this prototype + ** and dump it + */ + WWDEBUG_SAY(("Render Object Name Collision: %s\r\n",newproto->Get_Name())); + delete newproto; + newproto = NULL; + return false; + } + } else { + /* + ** Warn user that a prototype was not generated from this + ** chunk type + */ + WWDEBUG_SAY(("Could not generate Granny prototype! File = %d\r\n",filename)); + return false; + } + return true; +#endif } #ifdef DUMP_PERF_STATS @@ -1031,17 +1121,47 @@ HAnimClass * W3DAssetManager::Get_HAnim(const char * name) #endif WWPROFILE( "WW3DAssetManager::Get_HAnim" ); - HAnimClass *anim=WW3DAssetManager::Get_HAnim(name); -#ifdef DUMP_PERF_STATS - if (HAnim_Recursions == 1) +#ifdef INCLUDE_GRANNY_IN_BUILD + Bool isGranny = false; + char *pext=strrchr(name,'.'); //find file extension + if (pext) + isGranny=(strnicmp(pext,".GR2",4) == 0); + if (!isGranny) +#endif { - GetPrecisionTimer(&endTime64); - Total_Get_HAnim_Time += endTime64-startTime64; - } - HAnim_Recursions--; + HAnimClass *anim=WW3DAssetManager::Get_HAnim(name); //we only do custom granny processing. +#ifdef DUMP_PERF_STATS + if (HAnim_Recursions == 1) + { + GetPrecisionTimer(&endTime64); + Total_Get_HAnim_Time += endTime64-startTime64; + } + HAnim_Recursions--; #endif - return anim; + return anim; + } +#ifdef INCLUDE_GRANNY_IN_BUILD + // Try to find the hanim + HAnimClass * anim = m_GrannyAnimManager->Get_Anim(name); + if (WW3D_Load_On_Demand && anim == NULL) { // If we didn't find it, try to load on demand + if ( !m_GrannyAnimManager->Is_Missing( name ) ) { // if this is NOT a known missing anim + + // If we can't find it, try the parent directory + if ( m_GrannyAnimManager->Load_Anim(name) == 1 ) { + StringClass new_filename = StringClass("..\\") + name; + m_GrannyAnimManager->Load_Anim( new_filename ); + } + + anim = m_GrannyAnimManager->Get_Anim(name); // Try again + if (anim == NULL) { +// WWDEBUG_SAY(("WARNING: Animation %s not found!\n", name)); + m_GrannyAnimManager->Register_Missing( name ); // This is now a KNOWN missing anim + } + } + } + return anim; +#endif } //--------------------------------------------------------------------- @@ -1327,6 +1447,11 @@ static inline void Munge_Texture_Name(char *newname, const char *oldname, const RenderObjClass * W3DAssetManager::Create_Render_Obj(const char * name,float scale, const Vector3 &hsv_shift) { Bool isGranny = false; +#ifdef INCLUDE_GRANNY_IN_BUILD + char *pext=strrchr(name,'.'); //find file extension + if (pext) + isGranny=(strnicmp(pext,".GR2",4) == 0); +#endif Bool reallyscale = (WWMath::Fabs(scale - ident_scale) > scale_epsilon); Bool reallyhsv_shift = (WWMath::Fabs(hsv_shift.X - ident_HSV.X) > H_epsilon || WWMath::Fabs(hsv_shift.Y - ident_HSV.Y) > S_epsilon || WWMath::Fabs(hsv_shift.Z - ident_HSV.Z) > V_epsilon); diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp index e3e2e57add5..315df93aefe 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DScene.cpp @@ -1173,6 +1173,16 @@ void RTS3DScene::Customized_Render( RenderInfoClass &rinfo ) renderOneObject(rinfo, robj, localPlayerIndex); } } + +#ifdef INCLUDE_GRANNY_IN_BUILD + if (TheGrannyRenderObjSystem) + { //we only want to update granny animations once per frame, so only queue + //them up if this is not a mirror pass. + if (!ShaderClass::Is_Backface_Culling_Inverted()) + TheGrannyRenderObjSystem->queueUpdate(); + TheGrannyRenderObjSystem->Flush(); + } +#endif //Tell shadow manager to render shadows at the end of this frame //Don't draw shadows if there is no terrain present. diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp index 41b22b57fa0..ec2be7658ef 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp @@ -197,6 +197,14 @@ W3DTerrainVisual::~W3DTerrainVisual() TheTerrainTracksRenderObjClassSystem=NULL; } +#ifdef INCLUDE_GRANNY_IN_BUILD + if (TheGrannyRenderObjSystem) + { + delete TheGrannyRenderObjSystem; + TheGrannyRenderObjSystem=NULL; + } +#endif + if (TheW3DShadowManager) { delete TheW3DShadowManager; @@ -236,6 +244,11 @@ void W3DTerrainVisual::init( void ) TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem; TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene); +#ifdef INCLUDE_GRANNY_IN_BUILD + // initialize Granny model drawing system + TheGrannyRenderObjSystem = NEW GrannyRenderObjSystem; +#endif + // initialize object shadow drawing system TheW3DShadowManager = NEW W3DShadowManager; TheW3DShadowManager->init(); diff --git a/GeneralsMD/Code/Tools/CMakeLists.txt b/GeneralsMD/Code/Tools/CMakeLists.txt index 99fb9d7ee40..2b4cab04584 100644 --- a/GeneralsMD/Code/Tools/CMakeLists.txt +++ b/GeneralsMD/Code/Tools/CMakeLists.txt @@ -20,6 +20,7 @@ if(RTS_BUILD_ZEROHOUR_EXTRAS) add_subdirectory(buildVersionUpdate) add_subdirectory(Launcher) add_subdirectory(mangler) + add_subdirectory(matchbot) add_subdirectory(PATCHGET) add_subdirectory(textureCompress) add_subdirectory(timingTest) diff --git a/GeneralsMD/Code/Tools/matchbot/CMakeLists.txt b/GeneralsMD/Code/Tools/matchbot/CMakeLists.txt new file mode 100644 index 00000000000..d5d4183f70d --- /dev/null +++ b/GeneralsMD/Code/Tools/matchbot/CMakeLists.txt @@ -0,0 +1,78 @@ +set(MATCHBOT_SRC + "debug.cpp" + "debug.h" + "encrypt.cpp" + "encrypt.h" + "generals.cpp" + "generals.h" + "global.cpp" + "global.h" + "main.cpp" + "matcher.cpp" + "matcher.h" + "mydebug.cpp" + "mydebug.h" + "rand.cpp" + "rand.h" + "wlib/arraylist.h" + "wlib/configfile.cpp" + "wlib/configfile.h" + "wlib/critsec.cpp" + "wlib/critsec.h" + "wlib/dictionary.h" + "wlib/filed.h" + "wlib/linkedlist.h" + "wlib/mboxd.h" + "wlib/monod.cpp" + "wlib/monod.h" + "wlib/odevice.h" + "wlib/sem4.cpp" + "wlib/sem4.h" + "wlib/stderrd.h" + "wlib/stdoutd.h" + "wlib/streamer.cpp" + "wlib/streamer.h" + "wlib/syslogd.cpp" + "wlib/syslogd.h" + "wlib/threadfac.cpp" + "wlib/threadfac.h" + "wlib/threadsafe.h" + "wlib/timezone.cpp" + "wlib/timezone.h" + "wlib/ustring.h" + "wlib/wdebug.cpp" + "wlib/wdebug.h" + "wlib/wstring.cpp" + "wlib/wstring.h" + "wlib/wstypes.h" + "wlib/wtime.cpp" + "wlib/wtime.h" + "wlib/xtime.cpp" + "wlib/xtime.h" + "wnet/field.cpp" + "wnet/field.h" + "wnet/packet.cpp" + "wnet/packet.h" + "wnet/tcp.cpp" + "wnet/tcp.h" + "wnet/udp.cpp" + "wnet/udp.h" +) + +add_executable(z_matchbot WIN32) +set_target_properties(z_matchbot PROPERTIES OUTPUT_NAME matchbot) + +target_sources(z_matchbot PRIVATE ${MATCHBOT_SRC}) + +target_include_directories(z_matchbot PRIVATE . wlib wnet) + +target_link_libraries(z_matchbot PRIVATE + gamespy::gamespy + core_config + core_utility + stlport +) + +if(WIN32 OR "${CMAKE_SYSTEM}" MATCHES "Windows") + target_link_options(z_matchbot PRIVATE /subsystem:console) +endif() diff --git a/Core/Tools/matchbot/debug.cpp b/GeneralsMD/Code/Tools/matchbot/debug.cpp similarity index 100% rename from Core/Tools/matchbot/debug.cpp rename to GeneralsMD/Code/Tools/matchbot/debug.cpp diff --git a/Core/Tools/matchbot/debug.h b/GeneralsMD/Code/Tools/matchbot/debug.h similarity index 100% rename from Core/Tools/matchbot/debug.h rename to GeneralsMD/Code/Tools/matchbot/debug.h diff --git a/Core/Tools/matchbot/encrypt.cpp b/GeneralsMD/Code/Tools/matchbot/encrypt.cpp similarity index 100% rename from Core/Tools/matchbot/encrypt.cpp rename to GeneralsMD/Code/Tools/matchbot/encrypt.cpp diff --git a/Core/Tools/matchbot/encrypt.h b/GeneralsMD/Code/Tools/matchbot/encrypt.h similarity index 100% rename from Core/Tools/matchbot/encrypt.h rename to GeneralsMD/Code/Tools/matchbot/encrypt.h diff --git a/Core/Tools/matchbot/generals.cpp b/GeneralsMD/Code/Tools/matchbot/generals.cpp similarity index 100% rename from Core/Tools/matchbot/generals.cpp rename to GeneralsMD/Code/Tools/matchbot/generals.cpp diff --git a/Core/Tools/matchbot/generals.h b/GeneralsMD/Code/Tools/matchbot/generals.h similarity index 100% rename from Core/Tools/matchbot/generals.h rename to GeneralsMD/Code/Tools/matchbot/generals.h diff --git a/Core/Tools/matchbot/global.cpp b/GeneralsMD/Code/Tools/matchbot/global.cpp similarity index 100% rename from Core/Tools/matchbot/global.cpp rename to GeneralsMD/Code/Tools/matchbot/global.cpp diff --git a/Core/Tools/matchbot/global.h b/GeneralsMD/Code/Tools/matchbot/global.h similarity index 100% rename from Core/Tools/matchbot/global.h rename to GeneralsMD/Code/Tools/matchbot/global.h diff --git a/Core/Tools/matchbot/main.cpp b/GeneralsMD/Code/Tools/matchbot/main.cpp similarity index 100% rename from Core/Tools/matchbot/main.cpp rename to GeneralsMD/Code/Tools/matchbot/main.cpp diff --git a/Core/Tools/matchbot/matcher.h b/GeneralsMD/Code/Tools/matchbot/matcher.h similarity index 100% rename from Core/Tools/matchbot/matcher.h rename to GeneralsMD/Code/Tools/matchbot/matcher.h diff --git a/Core/Tools/matchbot/mydebug.cpp b/GeneralsMD/Code/Tools/matchbot/mydebug.cpp similarity index 100% rename from Core/Tools/matchbot/mydebug.cpp rename to GeneralsMD/Code/Tools/matchbot/mydebug.cpp diff --git a/Core/Tools/matchbot/mydebug.h b/GeneralsMD/Code/Tools/matchbot/mydebug.h similarity index 100% rename from Core/Tools/matchbot/mydebug.h rename to GeneralsMD/Code/Tools/matchbot/mydebug.h diff --git a/Core/Tools/matchbot/rand.cpp b/GeneralsMD/Code/Tools/matchbot/rand.cpp similarity index 100% rename from Core/Tools/matchbot/rand.cpp rename to GeneralsMD/Code/Tools/matchbot/rand.cpp diff --git a/Core/Tools/matchbot/rand.h b/GeneralsMD/Code/Tools/matchbot/rand.h similarity index 100% rename from Core/Tools/matchbot/rand.h rename to GeneralsMD/Code/Tools/matchbot/rand.h diff --git a/Core/Tools/matchbot/wlib/arraylist.h b/GeneralsMD/Code/Tools/matchbot/wlib/arraylist.h similarity index 100% rename from Core/Tools/matchbot/wlib/arraylist.h rename to GeneralsMD/Code/Tools/matchbot/wlib/arraylist.h diff --git a/Core/Tools/matchbot/wlib/configfile.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/configfile.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/configfile.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/configfile.cpp diff --git a/Core/Tools/matchbot/wlib/configfile.h b/GeneralsMD/Code/Tools/matchbot/wlib/configfile.h similarity index 100% rename from Core/Tools/matchbot/wlib/configfile.h rename to GeneralsMD/Code/Tools/matchbot/wlib/configfile.h diff --git a/Core/Tools/matchbot/wlib/critsec.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/critsec.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/critsec.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/critsec.cpp diff --git a/Core/Tools/matchbot/wlib/critsec.h b/GeneralsMD/Code/Tools/matchbot/wlib/critsec.h similarity index 100% rename from Core/Tools/matchbot/wlib/critsec.h rename to GeneralsMD/Code/Tools/matchbot/wlib/critsec.h diff --git a/Core/Tools/matchbot/wlib/dictionary.h b/GeneralsMD/Code/Tools/matchbot/wlib/dictionary.h similarity index 100% rename from Core/Tools/matchbot/wlib/dictionary.h rename to GeneralsMD/Code/Tools/matchbot/wlib/dictionary.h diff --git a/Core/Tools/matchbot/wlib/filed.h b/GeneralsMD/Code/Tools/matchbot/wlib/filed.h similarity index 100% rename from Core/Tools/matchbot/wlib/filed.h rename to GeneralsMD/Code/Tools/matchbot/wlib/filed.h diff --git a/Core/Tools/matchbot/wlib/linkedlist.h b/GeneralsMD/Code/Tools/matchbot/wlib/linkedlist.h similarity index 100% rename from Core/Tools/matchbot/wlib/linkedlist.h rename to GeneralsMD/Code/Tools/matchbot/wlib/linkedlist.h diff --git a/Core/Tools/matchbot/wlib/mboxd.h b/GeneralsMD/Code/Tools/matchbot/wlib/mboxd.h similarity index 100% rename from Core/Tools/matchbot/wlib/mboxd.h rename to GeneralsMD/Code/Tools/matchbot/wlib/mboxd.h diff --git a/Core/Tools/matchbot/wlib/monod.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/monod.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/monod.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/monod.cpp diff --git a/Core/Tools/matchbot/wlib/monod.h b/GeneralsMD/Code/Tools/matchbot/wlib/monod.h similarity index 100% rename from Core/Tools/matchbot/wlib/monod.h rename to GeneralsMD/Code/Tools/matchbot/wlib/monod.h diff --git a/Core/Tools/matchbot/wlib/odevice.h b/GeneralsMD/Code/Tools/matchbot/wlib/odevice.h similarity index 100% rename from Core/Tools/matchbot/wlib/odevice.h rename to GeneralsMD/Code/Tools/matchbot/wlib/odevice.h diff --git a/Core/Tools/matchbot/wlib/sem4.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/sem4.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/sem4.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/sem4.cpp diff --git a/Core/Tools/matchbot/wlib/sem4.h b/GeneralsMD/Code/Tools/matchbot/wlib/sem4.h similarity index 100% rename from Core/Tools/matchbot/wlib/sem4.h rename to GeneralsMD/Code/Tools/matchbot/wlib/sem4.h diff --git a/Core/Tools/matchbot/wlib/stderrd.h b/GeneralsMD/Code/Tools/matchbot/wlib/stderrd.h similarity index 100% rename from Core/Tools/matchbot/wlib/stderrd.h rename to GeneralsMD/Code/Tools/matchbot/wlib/stderrd.h diff --git a/Core/Tools/matchbot/wlib/stdoutd.h b/GeneralsMD/Code/Tools/matchbot/wlib/stdoutd.h similarity index 100% rename from Core/Tools/matchbot/wlib/stdoutd.h rename to GeneralsMD/Code/Tools/matchbot/wlib/stdoutd.h diff --git a/Core/Tools/matchbot/wlib/streamer.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/streamer.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/streamer.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/streamer.cpp diff --git a/Core/Tools/matchbot/wlib/streamer.h b/GeneralsMD/Code/Tools/matchbot/wlib/streamer.h similarity index 100% rename from Core/Tools/matchbot/wlib/streamer.h rename to GeneralsMD/Code/Tools/matchbot/wlib/streamer.h diff --git a/Core/Tools/matchbot/wlib/syslogd.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/syslogd.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/syslogd.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/syslogd.cpp diff --git a/Core/Tools/matchbot/wlib/syslogd.h b/GeneralsMD/Code/Tools/matchbot/wlib/syslogd.h similarity index 100% rename from Core/Tools/matchbot/wlib/syslogd.h rename to GeneralsMD/Code/Tools/matchbot/wlib/syslogd.h diff --git a/Core/Tools/matchbot/wlib/threadfac.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/threadfac.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/threadfac.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/threadfac.cpp diff --git a/Core/Tools/matchbot/wlib/threadfac.h b/GeneralsMD/Code/Tools/matchbot/wlib/threadfac.h similarity index 100% rename from Core/Tools/matchbot/wlib/threadfac.h rename to GeneralsMD/Code/Tools/matchbot/wlib/threadfac.h diff --git a/Core/Tools/matchbot/wlib/threadsafe.h b/GeneralsMD/Code/Tools/matchbot/wlib/threadsafe.h similarity index 100% rename from Core/Tools/matchbot/wlib/threadsafe.h rename to GeneralsMD/Code/Tools/matchbot/wlib/threadsafe.h diff --git a/Core/Tools/matchbot/wlib/timezone.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/timezone.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/timezone.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/timezone.cpp diff --git a/Core/Tools/matchbot/wlib/timezone.h b/GeneralsMD/Code/Tools/matchbot/wlib/timezone.h similarity index 100% rename from Core/Tools/matchbot/wlib/timezone.h rename to GeneralsMD/Code/Tools/matchbot/wlib/timezone.h diff --git a/Core/Tools/matchbot/wlib/ustring.h b/GeneralsMD/Code/Tools/matchbot/wlib/ustring.h similarity index 100% rename from Core/Tools/matchbot/wlib/ustring.h rename to GeneralsMD/Code/Tools/matchbot/wlib/ustring.h diff --git a/Core/Tools/matchbot/wlib/wdebug.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/wdebug.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/wdebug.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/wdebug.cpp diff --git a/Core/Tools/matchbot/wlib/wdebug.h b/GeneralsMD/Code/Tools/matchbot/wlib/wdebug.h similarity index 100% rename from Core/Tools/matchbot/wlib/wdebug.h rename to GeneralsMD/Code/Tools/matchbot/wlib/wdebug.h diff --git a/Core/Tools/matchbot/wlib/wstring.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/wstring.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/wstring.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/wstring.cpp diff --git a/Core/Tools/matchbot/wlib/wstring.h b/GeneralsMD/Code/Tools/matchbot/wlib/wstring.h similarity index 100% rename from Core/Tools/matchbot/wlib/wstring.h rename to GeneralsMD/Code/Tools/matchbot/wlib/wstring.h diff --git a/Core/Tools/matchbot/wlib/wstypes.h b/GeneralsMD/Code/Tools/matchbot/wlib/wstypes.h similarity index 100% rename from Core/Tools/matchbot/wlib/wstypes.h rename to GeneralsMD/Code/Tools/matchbot/wlib/wstypes.h diff --git a/Core/Tools/matchbot/wlib/wtime.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/wtime.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/wtime.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/wtime.cpp diff --git a/Core/Tools/matchbot/wlib/wtime.h b/GeneralsMD/Code/Tools/matchbot/wlib/wtime.h similarity index 100% rename from Core/Tools/matchbot/wlib/wtime.h rename to GeneralsMD/Code/Tools/matchbot/wlib/wtime.h diff --git a/Core/Tools/matchbot/wlib/xtime.cpp b/GeneralsMD/Code/Tools/matchbot/wlib/xtime.cpp similarity index 100% rename from Core/Tools/matchbot/wlib/xtime.cpp rename to GeneralsMD/Code/Tools/matchbot/wlib/xtime.cpp diff --git a/Core/Tools/matchbot/wlib/xtime.h b/GeneralsMD/Code/Tools/matchbot/wlib/xtime.h similarity index 100% rename from Core/Tools/matchbot/wlib/xtime.h rename to GeneralsMD/Code/Tools/matchbot/wlib/xtime.h diff --git a/Core/Tools/matchbot/wnet/field.cpp b/GeneralsMD/Code/Tools/matchbot/wnet/field.cpp similarity index 100% rename from Core/Tools/matchbot/wnet/field.cpp rename to GeneralsMD/Code/Tools/matchbot/wnet/field.cpp diff --git a/Core/Tools/matchbot/wnet/field.h b/GeneralsMD/Code/Tools/matchbot/wnet/field.h similarity index 100% rename from Core/Tools/matchbot/wnet/field.h rename to GeneralsMD/Code/Tools/matchbot/wnet/field.h diff --git a/Core/Tools/matchbot/wnet/packet.cpp b/GeneralsMD/Code/Tools/matchbot/wnet/packet.cpp similarity index 100% rename from Core/Tools/matchbot/wnet/packet.cpp rename to GeneralsMD/Code/Tools/matchbot/wnet/packet.cpp diff --git a/Core/Tools/matchbot/wnet/packet.h b/GeneralsMD/Code/Tools/matchbot/wnet/packet.h similarity index 100% rename from Core/Tools/matchbot/wnet/packet.h rename to GeneralsMD/Code/Tools/matchbot/wnet/packet.h diff --git a/Core/Tools/matchbot/wnet/tcp.cpp b/GeneralsMD/Code/Tools/matchbot/wnet/tcp.cpp similarity index 100% rename from Core/Tools/matchbot/wnet/tcp.cpp rename to GeneralsMD/Code/Tools/matchbot/wnet/tcp.cpp diff --git a/Core/Tools/matchbot/wnet/tcp.h b/GeneralsMD/Code/Tools/matchbot/wnet/tcp.h similarity index 100% rename from Core/Tools/matchbot/wnet/tcp.h rename to GeneralsMD/Code/Tools/matchbot/wnet/tcp.h diff --git a/Core/Tools/matchbot/wnet/udp.cpp b/GeneralsMD/Code/Tools/matchbot/wnet/udp.cpp similarity index 100% rename from Core/Tools/matchbot/wnet/udp.cpp rename to GeneralsMD/Code/Tools/matchbot/wnet/udp.cpp diff --git a/Core/Tools/matchbot/wnet/udp.h b/GeneralsMD/Code/Tools/matchbot/wnet/udp.h similarity index 100% rename from Core/Tools/matchbot/wnet/udp.h rename to GeneralsMD/Code/Tools/matchbot/wnet/udp.h