diff --git a/Makefile b/Makefile index ac52e1d..30cba65 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,8 @@ BINARY := cppneofetch UTILS_DIR := Utils INCLUDE_DIR := include INCLUDE_FLAGS := -I${UTILS_DIR} -I${INCLUDE_DIR} -UTILS_HEADERS := $(wildcard ${UTILS_DIR}/*.hpp) +UTILS_GENERATED_HEADERS := PciIds +UTILS_HEADERS := $(patsubst %,${UTILS_DIR}/%.hpp,${UTILS_GENERATED_HEADERS}) $(wildcard ${UTILS_DIR}/*.hpp) SRC_DIR := src SOURCES := $(wildcard ${SRC_DIR}/*.cpp) @@ -27,31 +28,42 @@ endif all: build +${UTILS_DIR}/PciIds.hpp: + @bash -c 'if [ ! -f pci.ids ]; then curl -o pci.ids https://pci-ids.ucw.cz/v2.2/pci.ids; fi' + @printf '[INFO] Generate %s\n' $@ + @python gen-pci-ids-header.py pci.ids + ${OBJECT_PATH}: - mkdir -p $@ + @mkdir -p $@ ${BIN_PATH}: - mkdir -p $@ + @mkdir -p $@ ${OBJECT_PATH}/%.o: ${SRC_DIR}/%.cpp ${INCLUDE_DIR}/%.hpp ${UTILS_HEADERS} | ${OBJECT_PATH} - $(CXX) -c $< ${COMPILE_SWITCHES} ${INCLUDE_FLAGS} ${CXXFLAGS} -o $@ + @printf '[INFO] Build object of %s\n' $< + @$(CXX) -c $< ${COMPILE_SWITCHES} ${INCLUDE_FLAGS} ${CXXFLAGS} -o $@ ${OBJECT_PATH}/main.o: main.cpp ${UTILS_HEADERS} | ${OBJECT_PATH} - $(CXX) -c $< ${COMPILE_SWITCHES} ${INCLUDE_FLAGS} ${CXXFLAGS} -o $@ + @printf '[INFO] Build object %s\n' $< + @$(CXX) -c $< ${COMPILE_SWITCHES} ${INCLUDE_FLAGS} ${CXXFLAGS} -o $@ ${BIN_PATH}/${BINARY}: ${OBJECTS} ${OBJECT_PATH}/main.o | ${BIN_PATH} - $(CXX) $^ ${COMPILE_SWITCHES} ${INCLUDE_FLAGS} ${CXXFLAGS} -o $@ + @printf '[INFO] Link objects\n' + @$(CXX) $^ ${COMPILE_SWITCHES} ${INCLUDE_FLAGS} ${CXXFLAGS} -o $@ install: build @mkdir -p $(PREFIX)/bin - install ${BIN_PATH}/${BINARY} ${PREFIX}/bin/${BINARY} + @printf "[INFO] Install binary to %s\n" ${PREFIX}/bin/${BINARY} + @install ${BIN_PATH}/${BINARY} ${PREFIX}/bin/${BINARY} build: ${BIN_PATH}/${BINARY} @strip $< + @printf '[INFO] Build successfully\n' run: @./${BIN_PATH}/${BINARY} .PHONY: clean clean: - rm -rf ${BUILD_DIR} \ No newline at end of file + rm -rf ${BUILD_DIR} + rm -f ${UTILS_DIR}/PciIds.hpp \ No newline at end of file diff --git a/Utils/Definitions.hpp b/Utils/Definitions.hpp index 465f74a..506d907 100644 --- a/Utils/Definitions.hpp +++ b/Utils/Definitions.hpp @@ -14,5 +14,7 @@ #define PATH_PROC_STAT_FORMAT "/proc/%d/stat" #define PATH_PROC_COMM_FORMAT "/proc/%d/comm" +#define PATH_SYS_PCI_DEV_BUS "/sys/bus/pci/devices" +#define PCI_CLASS_ID_DISPLAY_CTRL 0x00000003 #endif diff --git a/gen-pci-ids-header.py b/gen-pci-ids-header.py new file mode 100644 index 0000000..1866467 --- /dev/null +++ b/gen-pci-ids-header.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +"""Generate C headers from PCI.ids database file.""" + +import sys + + +class PciDeviceModel: + """Represents a PCI device entry.""" + + def __init__(self, id_: int, name: str): + self.id = id_ + self.name = name + + +class PciVendorModel: + """Represents a PCI vendor with its devices.""" + + def __init__(self, id_: int, name: str): + self.id = id_ + self.name = name + self.devices: list[PciDeviceModel] = [] + +def get_substring_between(text: str, char_start: str, char_end: str) -> str: + """Eextract subtring the first char_start and the last char_end""" + start = text.find(char_start) + 1 + end = text.rfind(char_end) + if start > 0: + return text[start:end] + return "" + +def shorten_vendor_name(name: str) -> str: + """Extract clean vendor name""" + shorten_name = get_substring_between(name, '[', ']') + if shorten_name == "": + redundant_substrings = [ + "Inc", + "Corporation", + "Corp.", + "Co.,Ltd", + "Technology" + ] + shorten_name = name + for substr in redundant_substrings: + shorten_name = shorten_name.replace(substr, "") + shorten_name = shorten_name.strip(",. ") + return shorten_name + + +def parse_pci_ids(filename: str) -> list[PciVendorModel]: + """Parse the pci.ids file and return a list of vendors with their devices.""" + with open(filename, 'r') as f: + full_text = f.read() + + if not full_text.strip(): + sys.exit(f'Error: {filename} is empty') + + # Remove trailing sections (known classes) by finding the last double-newline + dev_list_text = full_text[:full_text.rfind('\n\n\n')] + + vendors: list[PciVendorModel] = [] + + for line in dev_list_text.split('\n'): + # Skip empty lines and comments + if not line or line[0] == '#': + continue + + # Main vendor entries start at column 1 (tab) + if len(line) > 1 and line[0] != '\t': + id_str, name = line.split(' ', maxsplit=1) + vendor = PciVendorModel(int(id_str, 16), shorten_vendor_name(name)) + vendors.append(vendor) + + # Device entries are indented with two tabs (or one tab then space) + elif len(line) > 2 and line[0] == '\t' and line[1] != '\t': + id_str, name = line[1:].split(' ', maxsplit=1) + vendors[-1].devices.append(PciDeviceModel(int(id_str, 16), name)) + + + return vendors + + +def generate_c_code(vendors: list[PciVendorModel], keep_vendor_ids: set[int] | None = None) -> str: + """Generate C header code for the PCI vendor/device database.""" + + # Filter vendors if needed (e.g., only specific hardware manufacturers) + if keep_vendor_ids is not None: + filtered_vendors = [vendor for vendor in vendors if vendor.id in keep_vendor_ids] + else: + filtered_vendors = vendors[:] + + header = ''' +// SPDX-License-Identifier: BSD-3-Clause +// https://opensource.org/license/BSD-3-Clause +// Generated from https://pci-ids.ucw.cz/v2.2/pci.ids + +#ifndef _PCIIDS_HPP_ +#define _PCIIDS_HPP_ + +#include +#include +#include + +typedef struct { + const char* kpc_Name; + const std::map* kpMap_PciDevs; +} pci_vendor_t; +''' + + # Generate device arrays for each vendor that has at least one device + for vendor in filtered_vendors: + if vendor.devices: + # Build the comma-separated list of device + piece = ',\n '.join( + '{{ 0x{:04X}, "{}" }}'.format( + device.id, + device.name.replace('"', '\\"') + ) for device in vendor.devices + ) + header += f''' +// {vendor.name} +static const std::map skMap_PciDevs_{vendor.id:04X} = {{ + {piece}, +}}; +''' + + # Generate the final vendors array + piece = ',\n '.join( + '{{ 0x{:04X}, {{ "{}", {} }} }}'.format( + vendor.id, + vendor.name.replace('"', '\\"'), + f"&skMap_PciDevs_{vendor.id:04X}" if vendor.devices else "nullptr" + ) for vendor in filtered_vendors + ) + + header += f''' +const std::map kMap_PciVendors = {{ + {piece}, +}}; +''' + header += "\n#endif\n" + + return header + + +def main(keep_vendor_ids: set[int] | None, pci_ids_path: str) -> None: + """Main entry point for generating PCI database C headers.""" + + vendors = parse_pci_ids(pci_ids_path) + header_path = "Utils/PciIds.hpp" + fd = open(header_path, 'w') + fd.write(generate_c_code(vendors, keep_vendor_ids)) + fd.close() + +if __name__ == '__main__': + if len(sys.argv) != 2: + sys.exit('Usage: gen-pciids.py ') + + # Vendor IDs to include (common hardware manufacturers): + KEEP_VENDOR_IDS = { + 0x106b, # Apple + 0x1002, # AMD + 0x1022, # AMD legacy + 0x8086, # Intel + 0x8087, # Intel GMA + 0x03e7, # ATI/AMD + 0x0955, # Nvidia legacy + 0x10de, # Nvidia + 0x12d2, # Broadcom/Nvidia + 0x1ed5, # MThreads + 0x5143, # Qualcomm + 0x14c3, # MediaTek + 0x15ad, # VMware + 0x1af4, # RedHat + 0x1ab8, # Parallel + 0x1414, # Microsoft + 0x108e, # Oracle VM + } + + main(KEEP_VENDOR_IDS, sys.argv[1]) + diff --git a/include/CGpuInfo.hpp b/include/CGpuInfo.hpp new file mode 100644 index 0000000..a90e0ef --- /dev/null +++ b/include/CGpuInfo.hpp @@ -0,0 +1,57 @@ +#ifndef _CGPUINFO_HPP_ +#define _CGPUINFO_HPP_ +#include +#include +#include + +/** + * @brief A utility class that manages and provides access to GPU information. + */ +class CGpuInfo +{ +private: + std::vector mvecstr_GpuNames; + std::vector mvecstr_PciBusAddresses; + std::vector mvecui32_MaxGpuFreqMhz; + + void fv_FindPciBusAddress(); + + bool fb_GetDecimalValue(const std::string& kstr_Path, uint32_t& rui32_Out); + + /** + * Reads a PCI device ID or Vendor ID from hex file. + * + * @param path Path to the hex value file (e.g., "/sys/bus/pci/devices/xxx/vendor") + * + * @return true if read successful, false otherwise + */ + bool fb_GetHexValue(const std::string& kstr_Path, uint32_t& rui32_Out); + + public: + CGpuInfo(); + CGpuInfo(const CGpuInfo&) = default; + CGpuInfo& operator=(const CGpuInfo&) = default; + CGpuInfo(CGpuInfo&&) = default; + CGpuInfo& operator=(CGpuInfo&&) = default; + virtual ~CGpuInfo() = default; + + /** + * @brief Retrieves a list containing all detected GPU names from the system. + * + * @param None + * + * @return A std::vector - contains strings representing each GPU's name, returns empty vector if no GPUs found + */ + const std::vector& fvecstr_GetNames(); + + /** + * @brief Retrieves the maximum integrated GPU clock speed from stored data. + * + * @param None + * + * @return A float - represents the maximum integrated GPU clock frequency in MHz, returns zero if no valid data available + */ + const std::vector& fvecui32_GetMaxGpuFreqMhz(); +}; + +#endif diff --git a/include/CStringHelper.hpp b/include/CStringHelper.hpp index 3d64b01..0fc27b4 100644 --- a/include/CStringHelper.hpp +++ b/include/CStringHelper.hpp @@ -120,6 +120,20 @@ class CStringHelper { * @return None */ static void fv_Format(std::string& rstr_Dest, const std::string& kstr_Format, ...); + + /** + * @brief Extract substring between two delimiter characters from input string + * + * @param kstr - The source string to extract the substring from (unchanged) + * @param c_start - The starting delimiter character + * @param c_end - The ending delimiter character + * + * @return None + */ + static std::string fstr_GetSubstringBetween(const std::string& kstr, + char c_start, + char c_end); + }; #endif diff --git a/main.cpp b/main.cpp index e375c22..a4779ba 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,5 @@ #include "CCpuInfo.hpp" +#include "CGpuInfo.hpp" #include "CMemInfo.hpp" #include "CProductInfo.hpp" #include "CTerminalInfo.hpp" @@ -6,6 +7,7 @@ #include "CPackageManInfo.hpp" #include "CHostInfo.hpp" #include "CStringHelper.hpp" +#include "Definitions.hpp" #include "Colors.hpp" #include "Logo.hpp" #include @@ -77,6 +79,7 @@ static std::string sfstr_GetColorBar() { int main() { CCpuInfo cpu; + CGpuInfo gpu; CMemInfo mem; CProductInfo product; CTerminalInfo terminal; @@ -84,44 +87,56 @@ int main() { CPackageManInfo pkgman; CHostInfo host; - std::vector vecstr_Fields = { - "", // user@host - "", // --------- - "OS: ", - "Model: ", - "Vendor: ", - "Kernel: ", - "Uptime: ", - "", // emtpy line - "Packages: ", - "Shell: ", - "Terminal: ", - "", // emtpy line - "CPU: ", - // "GPU: ", // TODO: implement a class to get GPU info based on product ID - "Memnory: ", - "", // emtpy line - "" // color bar - }; + std::vector vecstr_Fields; + vecstr_Fields.emplace_back(""); // user@host + vecstr_Fields.emplace_back(""); // --------- + vecstr_Fields.emplace_back("OS: "); + vecstr_Fields.emplace_back("Model: "); + vecstr_Fields.emplace_back("Vendor: "); + vecstr_Fields.emplace_back("Kernel: "); + vecstr_Fields.emplace_back("Uptime: "); + vecstr_Fields.emplace_back("Packages: "); + vecstr_Fields.emplace_back("Shell: "); + vecstr_Fields.emplace_back("Terminal: "); + vecstr_Fields.emplace_back("CPU: "); + if (1 < gpu.fvecstr_GetNames().size()) { + for (size_t ui64_Idx = 0; ui64_Idx < gpu.fvecstr_GetNames().size(); ++ui64_Idx) { + vecstr_Fields.emplace_back(CStringHelper::fstr_Format("GPU %d: ", ui64_Idx + 1)); + } + } else if (1 == gpu.fvecstr_GetNames().size()) { + vecstr_Fields.emplace_back("GPU: "); + } + vecstr_Fields.emplace_back("Memnory: "); + vecstr_Fields.emplace_back(""); // empty line + vecstr_Fields.emplace_back(""); // color bar - std::vector vecstr_Infos = { - sfstr_FormatHost(host), - std::string(host.fstr_GetUser().size() + host.fstr_GetName().size() + 1, '-'), - os.fstr_GetName(), - product.fstr_GetName(), - product.fstr_GetVendor(), - os.fstr_GetKernel(), - sfstr_FormatUptime(os.fui64_GetUptime()), - "", - sfstr_FormatPkgMan(pkgman), - os.fstr_GetShell(), - terminal.fstr_GetName(), - "", - sfstr_FormatCpu(cpu), - // sfstr_FormatGpu(gpu), // TODO: implement a class to get GPU info based on product ID - sfstr_FormatMem(mem), - "", - sfstr_GetColorBar()}; + std::vector vecstr_Infos; + vecstr_Infos.emplace_back(sfstr_FormatHost(host)); + vecstr_Infos.emplace_back(std::string(host.fstr_GetUser().size() + host.fstr_GetName().size() + 1, '-')); + vecstr_Infos.emplace_back(os.fstr_GetName()); + vecstr_Infos.emplace_back(product.fstr_GetName()); + vecstr_Infos.emplace_back(product.fstr_GetVendor()); + vecstr_Infos.emplace_back(os.fstr_GetKernel()); + vecstr_Infos.emplace_back(sfstr_FormatUptime(os.fui64_GetUptime())); + vecstr_Infos.emplace_back(sfstr_FormatPkgMan(pkgman)); + vecstr_Infos.emplace_back(os.fstr_GetShell()); + vecstr_Infos.emplace_back(terminal.fstr_GetName()); + vecstr_Infos.emplace_back(sfstr_FormatCpu(cpu)); + if (0 < gpu.fvecstr_GetNames().size()) { + for (size_t ui64_Idx = 0; ui64_Idx < gpu.fvecstr_GetNames().size(); ++ui64_Idx) { + if (UINT32_INIT != gpu.fvecui32_GetMaxGpuFreqMhz()[ui64_Idx]) { + vecstr_Infos.emplace_back( + CStringHelper::fstr_Format("%s @ %.2fGHz", + gpu.fvecstr_GetNames()[ui64_Idx].c_str(), + gpu.fvecui32_GetMaxGpuFreqMhz()[ui64_Idx] / 1000.0)); + } else { + vecstr_Infos.emplace_back(gpu.fvecstr_GetNames()[ui64_Idx]); + } + } + } + vecstr_Infos.emplace_back(sfstr_FormatMem(mem)); + vecstr_Infos.emplace_back(""); + vecstr_Infos.emplace_back(sfstr_GetColorBar()); if (vecstr_Fields.size() != vecstr_Infos.size()) { std::cerr << "Number of fields are different from number of infos" << std::endl; diff --git a/src/CCpuInfo.cpp b/src/CCpuInfo.cpp index 55bd0d1..1b68370 100644 --- a/src/CCpuInfo.cpp +++ b/src/CCpuInfo.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #define PATH_CPUINFO_VFS "/proc/cpuinfo" #define PATH_CPUMAXFREQ_FMT "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq" @@ -98,7 +99,7 @@ void CCpuInfo::_fv_ReadCpuMinFreqFS(uint32_t ui32_Processor) const { std::string str_CpuFreqPath; CStringHelper::fv_Format(str_CpuFreqPath, PATH_CPUMINFREQ_FMT, ui32_Processor); if (EXIT_SUCCESS == CFileReader::fi64_GetLine_nth(str_Line, str_CpuFreqPath)) { - mui32_MaxFreq = strtoul(str_Line.c_str(), NULL, BASE_DECIMAL); + mui32_MinFreq = strtoul(str_Line.c_str(), NULL, BASE_DECIMAL); } } diff --git a/src/CGpuInfo.cpp b/src/CGpuInfo.cpp new file mode 100644 index 0000000..1601984 --- /dev/null +++ b/src/CGpuInfo.cpp @@ -0,0 +1,155 @@ +#include "PciIds.hpp" +#include "CStringHelper.hpp" +#include "CFileReader.hpp" +#include "Definitions.hpp" +#include "CGpuInfo.hpp" +#include // opendir(), readdir() - Linux specific +#include // R_OK for access() check +#include // access() +#include // FILE +#include // strtol() +#include +#include + +CGpuInfo::CGpuInfo(): + mvecstr_GpuNames({}), + mvecstr_PciBusAddresses({}), + mvecui32_MaxGpuFreqMhz({}) +{ +} + +void CGpuInfo::fv_FindPciBusAddress() { + if (!mvecstr_PciBusAddresses.empty()) { + return; + } + DIR* dirp = opendir(PATH_SYS_PCI_DEV_BUS); + if (dirp != nullptr) { + struct dirent* entry; + uint32_t ui32_Class; + std::string str_FilePath; + while ((entry = readdir(dirp)) != nullptr) { + // Directory names like "0000:01:00.0" represent PCI devices + std::string devName(entry->d_name); + if (devName.find(':') == std::string::npos) { + continue; // Skip non-PCI entries + } + CStringHelper::fv_Format(str_FilePath, "%s/%s/class", PATH_SYS_PCI_DEV_BUS, devName.c_str()); + if (!fb_GetHexValue(str_FilePath, ui32_Class)) { + continue; + } + if ((ui32_Class >> 16) != PCI_CLASS_ID_DISPLAY_CTRL) { + continue; + } + mvecstr_PciBusAddresses.push_back(devName); + } + } + closedir(dirp); +} + +bool CGpuInfo::fb_GetDecimalValue(const std::string& kstr_Path, uint32_t& rui32_Out) { + std::string str_Line; + if (EXIT_SUCCESS != CFileReader::fi64_GetLine_nth(str_Line, kstr_Path)) { + return false; + } + rui32_Out = static_cast(strtoul(str_Line.c_str(), nullptr, 10)); + return true; +} + +bool CGpuInfo::fb_GetHexValue(const std::string& kstr_Path, uint32_t& rui32_Out) { + std::string str_Line; + if (EXIT_SUCCESS != CFileReader::fi64_GetLine_nth(str_Line, kstr_Path)) { + return false; + } + + // if (0 != access(kstr_Path.c_str(), R_OK)) { + // return false; + // } + // FILE* fp = fopen(kstr_Path.c_str(), "r"); + // if (fp) { + // const int ki32_HexSize = 10; + // char ac_Buffer[ki32_HexSize + 1]; + // int i32_ReadSize = fread(ac_Buffer, 1, ki32_HexSize, fp); + // fclose(fp); + // ac_Buffer[i32_ReadSize] = '\0'; + // rui32_Out = static_cast(strtol(ac_Buffer, nullptr, 16)); + // } + rui32_Out = static_cast(strtoul(str_Line.c_str(), nullptr, 16)); + return true; +} + +const std::vector& CGpuInfo::fvecstr_GetNames() { + if (mvecstr_GpuNames.empty()) { + fv_FindPciBusAddress(); + std::vector vec16_VendorIds; + std::vector vec16_DeviceIds; + std::string str_FilePath; + uint32_t ui32_VendorId, ui32_DeviceId; + for (const std::string& krstr_BusAddress : mvecstr_PciBusAddresses) { + CStringHelper::fv_Format(str_FilePath, "%s/%s/vendor", PATH_SYS_PCI_DEV_BUS, krstr_BusAddress.c_str()); + if (!fb_GetHexValue(str_FilePath, ui32_VendorId)) { + continue; + } + CStringHelper::fv_Format(str_FilePath, "%s/%s/device", PATH_SYS_PCI_DEV_BUS, krstr_BusAddress.c_str()); + if (!fb_GetHexValue(str_FilePath, ui32_DeviceId)) { + continue; + } + vec16_VendorIds.push_back(static_cast(ui32_VendorId)); + vec16_DeviceIds.push_back(static_cast(ui32_DeviceId)); + } + for (size_t ui64_Idx = 0; ui64_Idx < vec16_VendorIds.size(); ++ui64_Idx) { + const auto iter_PciVendor = kMap_PciVendors.find(vec16_VendorIds[ui64_Idx]); + if (kMap_PciVendors.cend() == iter_PciVendor) { + continue; + } + + const auto* pMap_PciDevs = iter_PciVendor->second.kpMap_PciDevs; + const auto iter_PciDev = pMap_PciDevs->find(vec16_DeviceIds[ui64_Idx]); + if (pMap_PciDevs->cend() == iter_PciDev) { + continue; + } + + const pci_vendor_t& kr_PciVendor = iter_PciVendor->second; + const std::string kstr_PciDevName = iter_PciDev->second; + std::string str_GpuName; + std::string str_ShortenName = CStringHelper::fstr_GetSubstringBetween(kstr_PciDevName, '[', ']'); + if (!str_ShortenName.empty()) { + CStringHelper::fv_Format(str_GpuName, "%s %s", kr_PciVendor.kpc_Name, str_ShortenName.c_str()); + } else { + CStringHelper::fv_Format(str_GpuName, "%s %s", kr_PciVendor.kpc_Name, kstr_PciDevName.c_str()); + } + + mvecstr_GpuNames.push_back(str_GpuName); + } + } + + return mvecstr_GpuNames; +} + +const std::vector& CGpuInfo::fvecui32_GetMaxGpuFreqMhz() { + if (mvecui32_MaxGpuFreqMhz.empty()) { + fv_FindPciBusAddress(); + const uint32_t ui32_GpuCount = mvecstr_PciBusAddresses.size(); + mvecui32_MaxGpuFreqMhz.assign(ui32_GpuCount, UINT32_INIT); + std::string str_FilePath; + uint32_t ui32_MaxFreqMhz; + for (uint32_t ui32_Idx = 0; ui32_Idx < ui32_GpuCount; ++ui32_Idx) { + ui32_MaxFreqMhz = UINT32_INIT; + for (uint32_t ui32_CardNo = 0; ui32_CardNo < ui32_GpuCount; ++ui32_CardNo) { + CStringHelper::fv_Format(str_FilePath, + "%s/%s/drm/card%d/gt_max_freq_mhz", + PATH_SYS_PCI_DEV_BUS, + mvecstr_PciBusAddresses[ui32_Idx].c_str(), + ui32_CardNo); + if (fb_GetDecimalValue(str_FilePath, ui32_MaxFreqMhz)) { + std::cout << "GPU " << ui32_Idx + 1 << ": Max freq = " << ui32_MaxFreqMhz << " MHz" << std::endl; + mvecui32_MaxGpuFreqMhz[ui32_Idx] = ui32_MaxFreqMhz; + break; + } + } + if (UINT32_INIT != ui32_MaxFreqMhz) { + break; + } + } + } + return mvecui32_MaxGpuFreqMhz; +} diff --git a/src/CStringHelper.cpp b/src/CStringHelper.cpp index 77cbec9..82d0064 100644 --- a/src/CStringHelper.cpp +++ b/src/CStringHelper.cpp @@ -120,4 +120,20 @@ void CStringHelper::fv_Format(std::string& rstr_Dest, const std::string& kstr_Fo va_end(args); rstr_Dest.assign(ac); } +} + +std::string CStringHelper::fstr_GetSubstringBetween(const std::string& kstr, char c_start, char c_end) { + std::string str; + size_t ui32_Start = kstr.find(c_start); + if (std::string::npos != ui32_Start) { + size_t ui32_End = kstr.rfind(c_end); + if (ui32_End > ui32_Start) { + size_t length = ui32_End - ui32_Start - 1; + if (length > 0) { + str = kstr.substr(ui32_Start + 1, length); + } + } + } + + return str; } \ No newline at end of file