diff --git a/CMakeLists.txt b/CMakeLists.txt
index be785d145..86028020b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -84,7 +84,7 @@ add_sourcepp_library(dmxpp)
add_sourcepp_library(fgdpp)
add_sourcepp_library(kvpp)
add_sourcepp_library(mdlpp)
-add_sourcepp_library(steampp)
+add_sourcepp_library(steampp C)
add_sourcepp_library(vicepp C CSHARP)
add_sourcepp_library(vpkpp C CSHARP)
add_sourcepp_library(vtfpp)
diff --git a/README.md b/README.md
index 480d1500b..cfcd336a1 100644
--- a/README.md
+++ b/README.md
@@ -89,7 +89,7 @@ Several modern C++20 libraries for sanely parsing Valve formats, rolled into one
n/a |
✅ |
n/a |
- |
+ C |
Based on the SteamAppPathProvider library by @Trice Everfire and Momentum Mod contributors. |
diff --git a/lang/c/include/sourceppc/Convert.hpp b/lang/c/include/sourceppc/Convert.hpp
index f045b9e6d..5867fbd47 100644
--- a/lang/c/include/sourceppc/Convert.hpp
+++ b/lang/c/include/sourceppc/Convert.hpp
@@ -10,6 +10,7 @@
#include
#include
+#include
#include
#include "Buffer.h"
@@ -27,4 +28,16 @@ size_t writeStringToMem(std::string_view str, char* buffer, size_t bufferLen);
size_t writeVectorToMem(const std::vector& vec, unsigned char* buffer, size_t bufferLen);
+// requires clause copied from BufferStream - not including here because that header is HEAVY
+template
+requires std::is_trivial_v && std::is_standard_layout_v && (!std::is_pointer_v)
+size_t writeVectorToMem(const std::vector& vec, T* buffer, size_t bufferLen) {
+ if (vec.size() >= bufferLen) {
+ std::memcpy(buffer, vec.data(), sizeof(T) * bufferLen);
+ return bufferLen;
+ }
+ std::memcpy(buffer, vec.data(), sizeof(T) * vec.size());
+ return vec.size();
+}
+
} // namespace Convert
diff --git a/lang/c/include/steamppc/Convert.hpp b/lang/c/include/steamppc/Convert.hpp
new file mode 100644
index 000000000..32f308739
--- /dev/null
+++ b/lang/c/include/steamppc/Convert.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+/*
+ * This is a header designed to be included in C++ source code.
+ * It should not be included in applications using any C wrapper libraries!
+ */
+#ifndef __cplusplus
+#error "This header can only be used in C++!"
+#endif
+
+#include "steampp.h"
+
+namespace steampp {
+
+class Steam;
+
+} // namespace steampp
+
+namespace Convert {
+
+steampp::Steam* steam(steampp_steam_handle_t handle);
+
+} // namespace Convert
diff --git a/lang/c/include/steamppc/steampp.h b/lang/c/include/steamppc/steampp.h
new file mode 100644
index 000000000..8066f2ad2
--- /dev/null
+++ b/lang/c/include/steamppc/steampp.h
@@ -0,0 +1,56 @@
+#pragma once
+
+#include
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef uint32_t AppID;
+
+typedef void* steampp_steam_handle_t;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+// REQUIRES MANUAL FREE: steampp_steam_free
+SOURCEPP_API steampp_steam_handle_t steampp_steam_new();
+
+SOURCEPP_API void steampp_steam_free(steampp_steam_handle_t* handle);
+
+SOURCEPP_API const char* steampp_get_install_dir(steampp_steam_handle_t handle);
+
+// REQUIRES MANUAL FREE: sourcepp_string_array_free
+SOURCEPP_API sourcepp_string_array_t steampp_get_library_dirs(steampp_steam_handle_t handle);
+
+// REQUIRES MANUAL FREE: sourcepp_string_free
+SOURCEPP_API sourcepp_string_t steampp_get_sourcemod_dir(steampp_steam_handle_t handle);
+
+SOURCEPP_API size_t steampp_get_installed_apps(steampp_steam_handle_t handle, AppID* array, size_t arrayLen);
+
+SOURCEPP_API size_t steampp_get_installed_apps_count(steampp_steam_handle_t handle);
+
+SOURCEPP_API bool steampp_is_app_installed(steampp_steam_handle_t handle, AppID appID);
+
+SOURCEPP_API const char* steampp_get_app_name(steampp_steam_handle_t handle, AppID appID);
+
+// REQUIRES MANUAL FREE: sourcepp_string_free
+SOURCEPP_API sourcepp_string_t steampp_get_app_install_dir(steampp_steam_handle_t handle, AppID appID);
+
+// REQUIRES MANUAL FREE: sourcepp_string_free
+SOURCEPP_API sourcepp_string_t steampp_get_app_icon_path(steampp_steam_handle_t handle, AppID appID);
+
+// REQUIRES MANUAL FREE: sourcepp_string_free
+SOURCEPP_API sourcepp_string_t steampp_get_app_logo_path(steampp_steam_handle_t handle, AppID appID);
+
+// REQUIRES MANUAL FREE: sourcepp_string_free
+SOURCEPP_API sourcepp_string_t steampp_get_app_box_art_path(steampp_steam_handle_t handle, AppID appID);
+
+// REQUIRES MANUAL FREE: sourcepp_string_free
+SOURCEPP_API sourcepp_string_t steampp_get_app_store_art_path(steampp_steam_handle_t handle, AppID appID);
+
+SOURCEPP_API bool steampp_is_app_using_source_engine(steampp_steam_handle_t handle, AppID appID);
+
+SOURCEPP_API bool steampp_is_app_using_source_2_engine(steampp_steam_handle_t handle, AppID appID);
diff --git a/lang/c/include/vpkppc/Convert.hpp b/lang/c/include/vpkppc/Convert.hpp
index 2cb0a8d01..8aa49c6b3 100644
--- a/lang/c/include/vpkppc/Convert.hpp
+++ b/lang/c/include/vpkppc/Convert.hpp
@@ -9,8 +9,9 @@
#endif
#include
-#include
-#include
+
+#include "Entry.h"
+#include "PackFile.h"
namespace vpkpp {
diff --git a/lang/c/include/vpkppc/PackFile.h b/lang/c/include/vpkppc/PackFile.h
index 9127b4b51..35538b953 100644
--- a/lang/c/include/vpkppc/PackFile.h
+++ b/lang/c/include/vpkppc/PackFile.h
@@ -18,8 +18,10 @@ typedef void* vpkpp_pack_file_handle_t;
} // extern "C"
#endif
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_open_with_options(const char* path, vpkpp_pack_file_options_t options);
SOURCEPP_API vpkpp_pack_file_type_e vpkpp_get_type(vpkpp_pack_file_handle_t handle);
diff --git a/lang/c/include/vpkppc/format/BSP.h b/lang/c/include/vpkppc/format/BSP.h
index 75cf70915..820003ff2 100644
--- a/lang/c/include/vpkppc/format/BSP.h
+++ b/lang/c/include/vpkppc/format/BSP.h
@@ -2,6 +2,8 @@
#include "../PackFile.h"
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_bsp_open_with_options(const char* path, vpkpp_pack_file_options_t options);
diff --git a/lang/c/include/vpkppc/format/FPX.h b/lang/c/include/vpkppc/format/FPX.h
index 937db1d3d..93b7f13c1 100644
--- a/lang/c/include/vpkppc/format/FPX.h
+++ b/lang/c/include/vpkppc/format/FPX.h
@@ -2,6 +2,8 @@
#include "../PackFile.h"
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_fpx_open_with_options(const char* path, vpkpp_pack_file_options_t options);
diff --git a/lang/c/include/vpkppc/format/GCF.h b/lang/c/include/vpkppc/format/GCF.h
index 3b31a4ab8..45725972c 100644
--- a/lang/c/include/vpkppc/format/GCF.h
+++ b/lang/c/include/vpkppc/format/GCF.h
@@ -2,6 +2,8 @@
#include "../PackFile.h"
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gcf_open_with_options(const char* path, vpkpp_pack_file_options_t options);
diff --git a/lang/c/include/vpkppc/format/GMA.h b/lang/c/include/vpkppc/format/GMA.h
index bf0b71314..a8b43cc5d 100644
--- a/lang/c/include/vpkppc/format/GMA.h
+++ b/lang/c/include/vpkppc/format/GMA.h
@@ -2,6 +2,8 @@
#include "../PackFile.h"
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_gma_open_with_options(const char* path, vpkpp_pack_file_options_t options);
diff --git a/lang/c/include/vpkppc/format/GRP.h b/lang/c/include/vpkppc/format/GRP.h
index ec3190a0d..44be3fc04 100644
--- a/lang/c/include/vpkppc/format/GRP.h
+++ b/lang/c/include/vpkppc/format/GRP.h
@@ -2,6 +2,8 @@
#include "../PackFile.h"
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_grp_open(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_grp_open_with_options(const char* path, vpkpp_pack_file_options_t options);
diff --git a/lang/c/include/vpkppc/format/PAK.h b/lang/c/include/vpkppc/format/PAK.h
index c26332e59..1bdb75333 100644
--- a/lang/c/include/vpkppc/format/PAK.h
+++ b/lang/c/include/vpkppc/format/PAK.h
@@ -2,6 +2,8 @@
#include "../PackFile.h"
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pak_open_with_options(const char* path, vpkpp_pack_file_options_t options);
diff --git a/lang/c/include/vpkppc/format/PCK.h b/lang/c/include/vpkppc/format/PCK.h
index 4aef9e58a..0a89f60bd 100644
--- a/lang/c/include/vpkppc/format/PCK.h
+++ b/lang/c/include/vpkppc/format/PCK.h
@@ -2,6 +2,8 @@
#include "../PackFile.h"
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_pck_open_with_options(const char* path, vpkpp_pack_file_options_t options);
diff --git a/lang/c/include/vpkppc/format/VPK.h b/lang/c/include/vpkppc/format/VPK.h
index 2501a29f1..91a5934c5 100644
--- a/lang/c/include/vpkppc/format/VPK.h
+++ b/lang/c/include/vpkppc/format/VPK.h
@@ -2,16 +2,22 @@
#include "../PackFile.h"
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_empty(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_empty_with_options(const char* path, vpkpp_pack_file_options_t options);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory(const char* vpkPath, const char* contentPath, bool saveToDir);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_create_from_directory_with_options(const char* vpkPath, const char* contentPath, bool saveToDir, vpkpp_pack_file_options_t options);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_vpk_open_with_options(const char* path, vpkpp_pack_file_options_t options);
SOURCEPP_API bool vpkpp_vpk_generate_keypair_files(const char* path);
diff --git a/lang/c/include/vpkppc/format/ZIP.h b/lang/c/include/vpkppc/format/ZIP.h
index 00646fa05..f472c05ea 100644
--- a/lang/c/include/vpkppc/format/ZIP.h
+++ b/lang/c/include/vpkppc/format/ZIP.h
@@ -2,6 +2,8 @@
#include "../PackFile.h"
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open(const char* path);
+// REQUIRES MANUAL FREE: vpkpp_close
SOURCEPP_API vpkpp_pack_file_handle_t vpkpp_zip_open_with_options(const char* path, vpkpp_pack_file_options_t options);
diff --git a/lang/c/src/steamppc/Convert.cpp b/lang/c/src/steamppc/Convert.cpp
new file mode 100644
index 000000000..6dfd93483
--- /dev/null
+++ b/lang/c/src/steamppc/Convert.cpp
@@ -0,0 +1,9 @@
+#include
+
+#include
+
+using namespace steampp;
+
+Steam* Convert::steam(steampp_steam_handle_t handle) {
+ return static_cast(handle);
+}
diff --git a/lang/c/src/steamppc/_steamppc.cmake b/lang/c/src/steamppc/_steamppc.cmake
new file mode 100644
index 000000000..f0890e997
--- /dev/null
+++ b/lang/c/src/steamppc/_steamppc.cmake
@@ -0,0 +1,5 @@
+add_pretty_parser(steampp C SOURCES
+ "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/steamppc/Convert.hpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/lang/c/include/steamppc/steampp.h"
+ "${CMAKE_CURRENT_LIST_DIR}/Convert.cpp"
+ "${CMAKE_CURRENT_LIST_DIR}/steampp.cpp")
diff --git a/lang/c/src/steamppc/steampp.cpp b/lang/c/src/steamppc/steampp.cpp
new file mode 100644
index 000000000..6b65d2670
--- /dev/null
+++ b/lang/c/src/steamppc/steampp.cpp
@@ -0,0 +1,111 @@
+#include
+
+#include
+
+#include
+#include
+#include
+
+using namespace steampp;
+
+SOURCEPP_API steampp_steam_handle_t steampp_steam_new() {
+ auto* steam = new Steam{};
+ if (!*steam) {
+ delete steam;
+ steam = nullptr;
+ }
+ return steam;
+}
+
+SOURCEPP_API void steampp_steam_free(steampp_steam_handle_t* handle) {
+ SOURCEPP_EARLY_RETURN(handle);
+
+ delete Convert::steam(*handle);
+ *handle = nullptr;
+}
+
+SOURCEPP_API const char* steampp_get_install_dir(steampp_steam_handle_t handle) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, "");
+
+ return Convert::steam(handle)->getInstallDir().data();
+}
+
+SOURCEPP_API sourcepp_string_array_t steampp_get_library_dirs(steampp_steam_handle_t handle) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_ARRAY_INVALID);
+
+ return Convert::toStringArray(Convert::steam(handle)->getLibraryDirs());
+}
+
+SOURCEPP_API sourcepp_string_t steampp_get_sourcemod_dir(steampp_steam_handle_t handle) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID);
+
+ return Convert::toString(Convert::steam(handle)->getSourceModDir());
+}
+
+SOURCEPP_API size_t steampp_get_installed_apps(steampp_steam_handle_t handle, AppID* array, size_t arrayLen) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, 0);
+ SOURCEPP_EARLY_RETURN_VAL(array, 0);
+ SOURCEPP_EARLY_RETURN_VAL(arrayLen, 0);
+
+ return Convert::writeVectorToMem(Convert::steam(handle)->getInstalledApps(), array, arrayLen);
+}
+
+SOURCEPP_API size_t steampp_get_installed_apps_count(steampp_steam_handle_t handle) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, 0);
+
+ return Convert::steam(handle)->getInstalledApps().size();
+}
+
+SOURCEPP_API bool steampp_is_app_installed(steampp_steam_handle_t handle, AppID appID) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, false);
+
+ return Convert::steam(handle)->isAppInstalled(appID);
+}
+
+SOURCEPP_API const char* steampp_get_app_name(steampp_steam_handle_t handle, AppID appID) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, "");
+
+ return Convert::steam(handle)->getAppName(appID).data();
+}
+
+SOURCEPP_API sourcepp_string_t steampp_get_app_install_dir(steampp_steam_handle_t handle, AppID appID) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID);
+
+ return Convert::toString(Convert::steam(handle)->getAppInstallDir(appID));
+}
+
+SOURCEPP_API sourcepp_string_t steampp_get_app_icon_path(steampp_steam_handle_t handle, AppID appID) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID);
+
+ return Convert::toString(Convert::steam(handle)->getAppIconPath(appID));
+}
+
+SOURCEPP_API sourcepp_string_t steampp_get_app_logo_path(steampp_steam_handle_t handle, AppID appID) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID);
+
+ return Convert::toString(Convert::steam(handle)->getAppLogoPath(appID));
+}
+
+SOURCEPP_API sourcepp_string_t steampp_get_app_box_art_path(steampp_steam_handle_t handle, AppID appID) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID);
+
+ return Convert::toString(Convert::steam(handle)->getAppBoxArtPath(appID));
+}
+
+SOURCEPP_API sourcepp_string_t steampp_get_app_store_art_path(steampp_steam_handle_t handle, AppID appID) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, SOURCEPP_STRING_INVALID);
+
+ return Convert::toString(Convert::steam(handle)->getAppStoreArtPath(appID));
+}
+
+SOURCEPP_API bool steampp_is_app_using_source_engine(steampp_steam_handle_t handle, AppID appID) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, false);
+
+ return Convert::steam(handle)->isAppUsingSourceEngine(appID);
+}
+
+SOURCEPP_API bool steampp_is_app_using_source_2_engine(steampp_steam_handle_t handle, AppID appID) {
+ SOURCEPP_EARLY_RETURN_VAL(handle, false);
+
+ return Convert::steam(handle)->isAppUsingSource2Engine(appID);
+}