Skip to content

Commit 4eac616

Browse files
Maullerxezon
authored andcommitted
chore(filesystem): Implement getExtension() and TheFileSystem::removeExtension() functions (TheSuperHackers#2635)
1 parent 46df28f commit 4eac616

4 files changed

Lines changed: 133 additions & 0 deletions

File tree

Core/GameEngine/Include/Common/FileSystem.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ struct FileInfo {
134134
// TheSuperHackers @bugfix xezon 26/10/2025 Adds a mutex to the file exist map to try prevent
135135
// application hangs during level load after the file exist map was corrupted because of writes
136136
// from multiple threads.
137+
//
138+
// TheSuperHackers @feature Mauller 24/04/2026 Add extension removal functions
137139
//===============================
138140
class FileSystem : public SubsystemInterface
139141
{
@@ -158,6 +160,9 @@ class FileSystem : public SubsystemInterface
158160
static AsciiString normalizePath(const AsciiString& path); ///< normalizes a file path. The path can refer to a directory. File path must be absolute, but does not need to exist. Returns an empty string on failure.
159161
static Bool isPathInDirectory(const AsciiString& testPath, const AsciiString& basePath); ///< determines if a file path is within a base path. Both paths must be absolute, but do not need to exist.
160162

163+
static bool removeExtension(AsciiString& path);
164+
static bool removeExtension(UnicodeString& path);
165+
161166
protected:
162167
#if ENABLE_FILESYSTEM_EXISTENCE_CACHE
163168
struct FileExistData

Core/GameEngine/Source/Common/System/FileSystem.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
#include "Common/LocalFileSystem.h"
5555
#include "Common/PerfTimer.h"
5656

57+
#include "Lib/PathUtil.h"
58+
5759

5860
DECLARE_PERF_TIMER(FileSystem)
5961

@@ -378,3 +380,31 @@ Bool FileSystem::isPathInDirectory(const AsciiString& testPath, const AsciiStrin
378380

379381
return true;
380382
}
383+
384+
//============================================================================
385+
// FileSystem::removeExtension - Ascii handling variant
386+
//============================================================================
387+
bool FileSystem::removeExtension(AsciiString& path)
388+
{
389+
if (const Char* ext = getExtension(path.str()))
390+
{
391+
path.truncateTo(ext - path.str());
392+
return true;
393+
}
394+
395+
return false;
396+
}
397+
398+
//============================================================================
399+
// FileSystem::removeExtension - Unicode handling variant
400+
//============================================================================
401+
bool FileSystem::removeExtension(UnicodeString& path)
402+
{
403+
if (const WideChar* ext = getExtension(path.str()))
404+
{
405+
path.truncateTo(ext - path.str());
406+
return true;
407+
}
408+
409+
return false;
410+
}

Core/Libraries/Include/Lib/BaseType.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,40 @@ inline NUM highestBit(NUM x)
7474
return static_cast<NUM>(y & ~(y >> 1));
7575
}
7676

77+
template <typename PTR>
78+
inline PTR maxPtr(PTR x, PTR y) noexcept
79+
{
80+
static_assert(std::is_pointer<PTR>::value, "maxPtr is for pointer types only!");
81+
82+
if (x == nullptr)
83+
return y;
84+
85+
if (y == nullptr)
86+
return x;
87+
88+
if (x > y)
89+
return x;
90+
91+
return y;
92+
}
93+
94+
template <typename PTR>
95+
inline PTR minPtr(PTR x, PTR y) noexcept
96+
{
97+
static_assert(std::is_pointer<PTR>::value, "minPtr is for pointer types only!");
98+
99+
if (x == nullptr)
100+
return y;
101+
102+
if (y == nullptr)
103+
return x;
104+
105+
if (x < y)
106+
return x;
107+
108+
return y;
109+
}
110+
77111
// TheSuperHackers @refactor JohnsterID 24/01/2026 Add lowercase min/max templates for GameEngine layer.
78112
// GameEngine code typically uses BaseType.h, but may include WWVegas headers (which define min/max in always.h).
79113
// Header guard prevents duplicate definitions. VC6's <algorithm> lacks std::min/std::max.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
** Command & Conquer Generals Zero Hour(tm)
3+
** Copyright 2026 TheSuperHackers
4+
**
5+
** This program is free software: you can redistribute it and/or modify
6+
** it under the terms of the GNU General Public License as published by
7+
** the Free Software Foundation, either version 3 of the License, or
8+
** (at your option) any later version.
9+
**
10+
** This program is distributed in the hope that it will be useful,
11+
** but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
** GNU General Public License for more details.
14+
**
15+
** You should have received a copy of the GNU General Public License
16+
** along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
// This file contains macros and functions to help with path handling.
20+
21+
#pragma once
22+
23+
#include "BaseType.h"
24+
#include <string.h>
25+
26+
inline const char* getExtension(const char* path)
27+
{
28+
const char* lastDot = strrchr(path, '.');
29+
30+
if (!lastDot)
31+
{
32+
return nullptr;
33+
}
34+
35+
const char* lastSeparator = maxPtr(strrchr(path, '/'), strrchr(path, '\\'));
36+
37+
// Check if the dot is contained in the filename
38+
if (lastSeparator && lastDot < lastSeparator)
39+
{
40+
return nullptr;
41+
}
42+
43+
return lastDot;
44+
}
45+
46+
inline const wchar_t* getExtension(const wchar_t* path)
47+
{
48+
const wchar_t* lastDot = wcsrchr(path, L'.');
49+
50+
if (!lastDot)
51+
{
52+
return nullptr;
53+
}
54+
55+
const wchar_t* lastSeparator = maxPtr(wcsrchr(path, L'/'), wcsrchr(path, L'\\'));
56+
57+
// Check if the dot is contained in the filename
58+
if (lastSeparator && lastDot < lastSeparator)
59+
{
60+
return nullptr;
61+
}
62+
63+
return lastDot;
64+
}

0 commit comments

Comments
 (0)