forked from metin-server/m2dev-client-src
Compare commits
181 Commits
particle-b
...
rtw1x1-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5c659d404 | ||
|
|
625849e0e4 | ||
|
|
074a05bb9b | ||
|
|
b4171084c3 | ||
|
|
d5624a8cdd | ||
|
|
fa96b25dad | ||
|
|
9b57cd1414 | ||
|
|
54b6f8c4a9 | ||
|
|
2550008f6d | ||
|
|
4c21fe697c | ||
|
|
3d8a8f8e3b | ||
|
|
3b359393d1 | ||
|
|
117f1234b5 | ||
|
|
6fcf2c58e2 | ||
|
|
de0b8052fe | ||
|
|
6984fef736 | ||
|
|
f702b4953d | ||
|
|
3f0f3c792d | ||
|
|
e55fc4db17 | ||
|
|
0958ea6214 | ||
|
|
049eca38a4 | ||
|
|
fd1218bd4e | ||
|
|
c6aa6b4149 | ||
|
|
7fb832ad6b | ||
|
|
33ac4b69f4 | ||
|
|
6d0fe59d50 | ||
|
|
5fce64f978 | ||
|
|
2372599578 | ||
|
|
efbdf9155e | ||
|
|
9f6348ad8c | ||
|
|
85763c9f81 | ||
|
|
73958b4f62 | ||
|
|
acc83c9aab | ||
|
|
541519d702 | ||
|
|
846fab02dc | ||
|
|
b364d4a39a | ||
|
|
999a0929a6 | ||
|
|
fb4ba5960e | ||
|
|
7c835fb461 | ||
|
|
b201fd6dd6 | ||
|
|
54a5dde037 | ||
|
|
e7a113885a | ||
|
|
e881517775 | ||
|
|
96876420d1 | ||
|
|
18073e7193 | ||
|
|
08228b1ff9 | ||
|
|
d82f72ed41 | ||
|
|
55b2d70459 | ||
|
|
fff15def3c | ||
|
|
387d7bf72d | ||
|
|
a4112cd128 | ||
|
|
67bda2c286 | ||
|
|
abefd13f68 | ||
|
|
2422af51a8 | ||
|
|
977e273764 | ||
|
|
5179261a47 | ||
|
|
308511bd22 | ||
|
|
46f2c9de0f | ||
|
|
2a8d881ef3 | ||
|
|
d3017b0ab0 | ||
|
|
5d73d79eb8 | ||
|
|
4729dafc12 | ||
|
|
a955c50744 | ||
|
|
d37607baa1 | ||
|
|
b56f55ce7c | ||
|
|
e578f05986 | ||
|
|
0208398731 | ||
|
|
9ac8e3e4d7 | ||
|
|
9907febf28 | ||
|
|
f9e1f8b7af | ||
|
|
25601d4b28 | ||
|
|
4b6cdc8003 | ||
|
|
b25de40e07 | ||
|
|
1592ec93f6 | ||
|
|
99bd5103a3 | ||
|
|
ef7c946cfb | ||
|
|
76b0dc793d | ||
|
|
45fdb49d63 | ||
|
|
b344e5505e | ||
|
|
8df71d0bb2 | ||
|
|
7580e4278c | ||
|
|
e0df09ea28 | ||
|
|
2799b72d6d | ||
|
|
0ecfea8e1e | ||
|
|
06c8e6f9b0 | ||
|
|
e3ae5541ba | ||
|
|
08ed73b29c | ||
|
|
a29e43224a | ||
|
|
7b08687023 | ||
|
|
c0548bc98e | ||
|
|
436db01a80 | ||
|
|
1b693488ba | ||
|
|
6227fed5be | ||
|
|
d5c8f05457 | ||
|
|
a49dec1c87 | ||
|
|
2eee9aead2 | ||
|
|
6ea69eb2d5 | ||
|
|
0b60aac3e8 | ||
|
|
c461023c4b | ||
|
|
7caf9639e0 | ||
|
|
01f4417d80 | ||
|
|
de6817c338 | ||
|
|
a808d1d102 | ||
|
|
7c86b64dc3 | ||
|
|
ab0ee95a19 | ||
|
|
133ac6fc41 | ||
|
|
7f42fc2c3f | ||
|
|
c0ea439b6d | ||
|
|
4894b0db9d | ||
|
|
892eb8acd1 | ||
|
|
c5feaaf2d9 | ||
|
|
255212e906 | ||
|
|
f7e9bc41e6 | ||
|
|
252f06bd0c | ||
|
|
f0af67cf96 | ||
|
|
082d8fb3a6 | ||
|
|
acaaf6fc91 | ||
|
|
51ee5feb78 | ||
|
|
ef97bbc54b | ||
|
|
bdcb68619a | ||
|
|
81c61692f1 | ||
|
|
cc98bdb2da | ||
|
|
b4637e4782 | ||
|
|
5b1d3c6bce | ||
|
|
775cb2f927 | ||
|
|
7509eeb93b | ||
|
|
3b1d0fd10a | ||
|
|
2bd4072ad8 | ||
|
|
fb7e53b909 | ||
|
|
df346e156b | ||
|
|
eaecf67d33 | ||
|
|
397e2b1890 | ||
|
|
b3aba3d280 | ||
|
|
dcf45e3a6e | ||
|
|
e1eb805f8a | ||
|
|
cfae08b973 | ||
|
|
b24a3ab17d | ||
|
|
99138293ed | ||
|
|
252df67871 | ||
|
|
1432d4c8ea | ||
|
|
99f04c27d4 | ||
|
|
64ed7f2bd5 | ||
|
|
f6c9422048 | ||
|
|
8c349d4a0f | ||
|
|
d690de0b5a | ||
|
|
85920e442d | ||
|
|
6b97701f0e | ||
|
|
f0ce190550 | ||
|
|
f791ab6d8c | ||
|
|
b66e045d80 | ||
|
|
a418f8616d | ||
|
|
f19cb2f118 | ||
|
|
3c8a5750f5 | ||
|
|
7651d8cf65 | ||
|
|
a83fc7e642 | ||
|
|
06d9a3671d | ||
|
|
d4a1591a50 | ||
|
|
b9783bec79 | ||
|
|
cb0c5d8fc9 | ||
|
|
6315ca3639 | ||
|
|
e5bd21ff75 | ||
|
|
6582927612 | ||
|
|
e4a321698e | ||
|
|
6459c9fa03 | ||
|
|
71e906b8f2 | ||
|
|
eedc2a0748 | ||
|
|
685cc02fc7 | ||
|
|
a479d5a57b | ||
|
|
5aad7c88f5 | ||
|
|
f7e0248edd | ||
|
|
d3991c0775 | ||
|
|
f33d8fc183 | ||
|
|
4385a2c709 | ||
|
|
4e6a7f1b08 | ||
|
|
67cabc3c89 | ||
|
|
f6ac84f91f | ||
|
|
58b1def270 | ||
|
|
15053eb86b | ||
|
|
fb7eba799e | ||
|
|
3090b79139 | ||
|
|
e172d6cff6 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1 +0,0 @@
|
||||
*.lib filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
35
.github/workflows/main.yml
vendored
Normal file
35
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
win:
|
||||
name: "Windows Build"
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build project
|
||||
id: build_project
|
||||
shell: pwsh
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --config RelWithDebInfo
|
||||
|
||||
- name: Collect outputs
|
||||
run: |
|
||||
mkdir _output
|
||||
cp build/bin/* _output
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: output_win
|
||||
path: _output
|
||||
64
.gitignore
vendored
64
.gitignore
vendored
@@ -1 +1,63 @@
|
||||
build
|
||||
/build/
|
||||
|
||||
# =======================================================
|
||||
# FULLY CASE-INSENSITIVE BACKUP EXCLUSIONS
|
||||
# Matches all files and folders ending in:
|
||||
# _BK, _BAK, .BK, .BAK (and all case permutations)
|
||||
# =======================================================
|
||||
|
||||
# -------------------------------------------------------
|
||||
# 1. EXCLUSIONS ENDING IN .BK / _BK
|
||||
# -------------------------------------------------------
|
||||
|
||||
# Files/Folders ending in _BK (e.g., File_Bk, Folder_bK)
|
||||
*_BK
|
||||
*_Bk
|
||||
*_bK
|
||||
*_bk
|
||||
|
||||
# Files ending in .BK (e.g., File.BK, File.bK)
|
||||
*.BK
|
||||
*.Bk
|
||||
*.bK
|
||||
*.bk
|
||||
|
||||
# Files ending in "double extension" .X.BK (e.g., File.txt.Bk)
|
||||
*.*.BK
|
||||
*.*.Bk
|
||||
*.*.bK
|
||||
*.*.bk
|
||||
|
||||
# -------------------------------------------------------
|
||||
# 2. EXCLUSIONS ENDING IN .BAK / _BAK
|
||||
# -------------------------------------------------------
|
||||
|
||||
# Files/Folders ending in _BAK (e.g., File_BAK, Folder_bak)
|
||||
*_BAK
|
||||
*_BAk
|
||||
*_BaK
|
||||
*_Bak
|
||||
*_bAK
|
||||
*_bAk
|
||||
*_baK
|
||||
*_bak
|
||||
|
||||
# Files ending in .BAK (e.g., File.BAK, File.bak)
|
||||
*.BAK
|
||||
*.BAk
|
||||
*.BaK
|
||||
*.Bak
|
||||
*.bAK
|
||||
*.bAk
|
||||
*.baK
|
||||
*.bak
|
||||
|
||||
# Files ending in "double extension" .X.BAK (e.g., File.txt.Bak)
|
||||
*.*.BAK
|
||||
*.*.BAk
|
||||
*.*.BaK
|
||||
*.*.Bak
|
||||
*.*.bAK
|
||||
*.*.bAk
|
||||
*.*.baK
|
||||
*.*.bak
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
|
||||
project(m2dev-client-src)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# ASan support
|
||||
option(ENABLE_ASAN "Enable AddressSanitizer" OFF)
|
||||
|
||||
set(CMAKE_MODULE_PATH
|
||||
${CMAKE_MODULE_PATH}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/buildtool"
|
||||
@@ -23,11 +26,28 @@ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG")
|
||||
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
|
||||
add_compile_options(/MP)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_definitions(UNICODE _UNICODE)
|
||||
endif()
|
||||
|
||||
add_compile_options(
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/wd4828>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/wd4996>
|
||||
)
|
||||
|
||||
# ASan flags
|
||||
if(ENABLE_ASAN)
|
||||
if(MSVC)
|
||||
add_compile_options(/fsanitize=address)
|
||||
add_link_options(/fsanitize=address)
|
||||
add_definitions(-D_DISABLE_VECTOR_ANNOTATION)
|
||||
add_definitions(-D_DISABLE_STRING_ANNOTATION)
|
||||
else()
|
||||
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
|
||||
add_link_options(-fsanitize=address)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_definitions(-DNOMINMAX)
|
||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
20
README.md
20
README.md
@@ -1 +1,19 @@
|
||||
# m2dev-client-src
|
||||
# Client Source Repository
|
||||
|
||||
This repository contains the source code necessary to compile the game client executable.
|
||||
|
||||
## How to build
|
||||
|
||||
> cmake -S . -B build
|
||||
>
|
||||
> cmake --build build
|
||||
|
||||
---
|
||||
|
||||
## 📋 Changelog
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
* **Amun's freeze on drag window**: Fixed a bug where the client window would freeze while we are dragging it around.
|
||||
* **Debug mode:** Fly effects are now registering when using Debug mode.
|
||||
* **Fix effect rendering in low opacity models:** Effects now appear normally on semi-transparent meshes.
|
||||
* **Fly targeting fixed for buff/healing skills:** Fixed an issue where fly target effect would render in the buffer's selected target even if the target was unbuffable (if viewing from another client).
|
||||
|
||||
8421
extern/include/MSS.H
vendored
8421
extern/include/MSS.H
vendored
File diff suppressed because it is too large
Load Diff
2589
extern/include/argparse.hpp
vendored
Normal file
2589
extern/include/argparse.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
extern/include/miniaudio.c
vendored
Normal file
2
extern/include/miniaudio.c
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
93468
extern/include/miniaudio.h
vendored
Normal file
93468
extern/include/miniaudio.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
666
extern/include/pcg_extras.hpp
vendored
Normal file
666
extern/include/pcg_extras.hpp
vendored
Normal file
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* PCG Random Number Generation for C++
|
||||
*
|
||||
* Copyright 2014-2017 Melissa O'Neill <oneill@pcg-random.org>,
|
||||
* and the PCG Project contributors.
|
||||
*
|
||||
* SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (provided in
|
||||
* LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* or under the MIT license (provided in LICENSE-MIT.txt and at
|
||||
* http://opensource.org/licenses/MIT), at your option. This file may not
|
||||
* be copied, modified, or distributed except according to those terms.
|
||||
*
|
||||
* Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either
|
||||
* express or implied. See your chosen license for details.
|
||||
*
|
||||
* For additional information about the PCG random number generation scheme,
|
||||
* visit http://www.pcg-random.org/.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file provides support code that is useful for random-number generation
|
||||
* but not specific to the PCG generation scheme, including:
|
||||
* - 128-bit int support for platforms where it isn't available natively
|
||||
* - bit twiddling operations
|
||||
* - I/O of 128-bit and 8-bit integers
|
||||
* - Handling the evilness of SeedSeq
|
||||
* - Support for efficiently producing random numbers less than a given
|
||||
* bound
|
||||
*/
|
||||
|
||||
#ifndef PCG_EXTRAS_HPP_INCLUDED
|
||||
#define PCG_EXTRAS_HPP_INCLUDED 1
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <locale>
|
||||
#include <iterator>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Abstractions for compiler-specific directives
|
||||
*/
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PCG_NOINLINE __attribute__((noinline))
|
||||
#else
|
||||
#define PCG_NOINLINE
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Some members of the PCG library use 128-bit math. When compiling on 64-bit
|
||||
* platforms, both GCC and Clang provide 128-bit integer types that are ideal
|
||||
* for the job.
|
||||
*
|
||||
* On 32-bit platforms (or with other compilers), we fall back to a C++
|
||||
* class that provides 128-bit unsigned integers instead. It may seem
|
||||
* like we're reinventing the wheel here, because libraries already exist
|
||||
* that support large integers, but most existing libraries provide a very
|
||||
* generic multiprecision code, but here we're operating at a fixed size.
|
||||
* Also, most other libraries are fairly heavyweight. So we use a direct
|
||||
* implementation. Sadly, it's much slower than hand-coded assembly or
|
||||
* direct CPU support.
|
||||
*
|
||||
*/
|
||||
#if __SIZEOF_INT128__ && !PCG_FORCE_EMULATED_128BIT_MATH
|
||||
namespace pcg_extras {
|
||||
typedef __uint128_t pcg128_t;
|
||||
}
|
||||
#define PCG_128BIT_CONSTANT(high,low) \
|
||||
((pcg_extras::pcg128_t(high) << 64) + low)
|
||||
#else
|
||||
#include "pcg_uint128.hpp"
|
||||
namespace pcg_extras {
|
||||
typedef pcg_extras::uint_x4<uint32_t,uint64_t> pcg128_t;
|
||||
}
|
||||
#define PCG_128BIT_CONSTANT(high,low) \
|
||||
pcg_extras::pcg128_t(high,low)
|
||||
#define PCG_EMULATED_128BIT_MATH 1
|
||||
#endif
|
||||
|
||||
|
||||
namespace pcg_extras {
|
||||
|
||||
/*
|
||||
* We often need to represent a "number of bits". When used normally, these
|
||||
* numbers are never greater than 128, so an unsigned char is plenty.
|
||||
* If you're using a nonstandard generator of a larger size, you can set
|
||||
* PCG_BITCOUNT_T to have it define it as a larger size. (Some compilers
|
||||
* might produce faster code if you set it to an unsigned int.)
|
||||
*/
|
||||
|
||||
#ifndef PCG_BITCOUNT_T
|
||||
typedef uint8_t bitcount_t;
|
||||
#else
|
||||
typedef PCG_BITCOUNT_T bitcount_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* C++ requires us to be able to serialize RNG state by printing or reading
|
||||
* it from a stream. Because we use 128-bit ints, we also need to be able
|
||||
* ot print them, so here is code to do so.
|
||||
*
|
||||
* This code provides enough functionality to print 128-bit ints in decimal
|
||||
* and zero-padded in hex. It's not a full-featured implementation.
|
||||
*/
|
||||
|
||||
template <typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT,Traits>&
|
||||
operator<<(std::basic_ostream<CharT,Traits>& out, pcg128_t value)
|
||||
{
|
||||
auto desired_base = out.flags() & out.basefield;
|
||||
bool want_hex = desired_base == out.hex;
|
||||
|
||||
if (want_hex) {
|
||||
uint64_t highpart = uint64_t(value >> 64);
|
||||
uint64_t lowpart = uint64_t(value);
|
||||
auto desired_width = out.width();
|
||||
if (desired_width > 16) {
|
||||
out.width(desired_width - 16);
|
||||
}
|
||||
if (highpart != 0 || desired_width > 16)
|
||||
out << highpart;
|
||||
CharT oldfill = '\0';
|
||||
if (highpart != 0) {
|
||||
out.width(16);
|
||||
oldfill = out.fill('0');
|
||||
}
|
||||
auto oldflags = out.setf(decltype(desired_base){}, out.showbase);
|
||||
out << lowpart;
|
||||
out.setf(oldflags);
|
||||
if (highpart != 0) {
|
||||
out.fill(oldfill);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
constexpr size_t MAX_CHARS_128BIT = 40;
|
||||
|
||||
char buffer[MAX_CHARS_128BIT];
|
||||
char* pos = buffer+sizeof(buffer);
|
||||
*(--pos) = '\0';
|
||||
constexpr auto BASE = pcg128_t(10ULL);
|
||||
do {
|
||||
auto div = value / BASE;
|
||||
auto mod = uint32_t(value - (div * BASE));
|
||||
*(--pos) = '0' + char(mod);
|
||||
value = div;
|
||||
} while(value != pcg128_t(0ULL));
|
||||
return out << pos;
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits>
|
||||
std::basic_istream<CharT,Traits>&
|
||||
operator>>(std::basic_istream<CharT,Traits>& in, pcg128_t& value)
|
||||
{
|
||||
typename std::basic_istream<CharT,Traits>::sentry s(in);
|
||||
|
||||
if (!s)
|
||||
return in;
|
||||
|
||||
constexpr auto BASE = pcg128_t(10ULL);
|
||||
pcg128_t current(0ULL);
|
||||
bool did_nothing = true;
|
||||
bool overflow = false;
|
||||
for(;;) {
|
||||
CharT wide_ch = in.get();
|
||||
if (!in.good()) {
|
||||
in.clear(std::ios::eofbit);
|
||||
break;
|
||||
}
|
||||
auto ch = in.narrow(wide_ch, '\0');
|
||||
if (ch < '0' || ch > '9') {
|
||||
in.unget();
|
||||
break;
|
||||
}
|
||||
did_nothing = false;
|
||||
pcg128_t digit(uint32_t(ch - '0'));
|
||||
pcg128_t timesbase = current*BASE;
|
||||
overflow = overflow || timesbase < current;
|
||||
current = timesbase + digit;
|
||||
overflow = overflow || current < digit;
|
||||
}
|
||||
|
||||
if (did_nothing || overflow) {
|
||||
in.setstate(std::ios::failbit);
|
||||
if (overflow)
|
||||
current = ~pcg128_t(0ULL);
|
||||
}
|
||||
|
||||
value = current;
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise, if people use tiny rngs, we'll be serializing uint8_t.
|
||||
* If we just used the provided IO operators, they'd read/write chars,
|
||||
* not ints, so we need to define our own. We *can* redefine this operator
|
||||
* here because we're in our own namespace.
|
||||
*/
|
||||
|
||||
template <typename CharT, typename Traits>
|
||||
std::basic_ostream<CharT,Traits>&
|
||||
operator<<(std::basic_ostream<CharT,Traits>&out, uint8_t value)
|
||||
{
|
||||
return out << uint32_t(value);
|
||||
}
|
||||
|
||||
template <typename CharT, typename Traits>
|
||||
std::basic_istream<CharT,Traits>&
|
||||
operator>>(std::basic_istream<CharT,Traits>& in, uint8_t& target)
|
||||
{
|
||||
uint32_t value = 0xdecea5edU;
|
||||
in >> value;
|
||||
if (!in && value == 0xdecea5edU)
|
||||
return in;
|
||||
if (value > uint8_t(~0)) {
|
||||
in.setstate(std::ios::failbit);
|
||||
value = ~0U;
|
||||
}
|
||||
target = uint8_t(value);
|
||||
return in;
|
||||
}
|
||||
|
||||
/* Unfortunately, the above functions don't get found in preference to the
|
||||
* built in ones, so we create some more specific overloads that will.
|
||||
* Ugh.
|
||||
*/
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, uint8_t value)
|
||||
{
|
||||
return pcg_extras::operator<< <char>(out, value);
|
||||
}
|
||||
|
||||
inline std::istream& operator>>(std::istream& in, uint8_t& value)
|
||||
{
|
||||
return pcg_extras::operator>> <char>(in, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Useful bitwise operations.
|
||||
*/
|
||||
|
||||
/*
|
||||
* XorShifts are invertable, but they are someting of a pain to invert.
|
||||
* This function backs them out. It's used by the whacky "inside out"
|
||||
* generator defined later.
|
||||
*/
|
||||
|
||||
template <typename itype>
|
||||
inline itype unxorshift(itype x, bitcount_t bits, bitcount_t shift)
|
||||
{
|
||||
if (2*shift >= bits) {
|
||||
return x ^ (x >> shift);
|
||||
}
|
||||
itype lowmask1 = (itype(1U) << (bits - shift*2)) - 1;
|
||||
itype highmask1 = ~lowmask1;
|
||||
itype top1 = x;
|
||||
itype bottom1 = x & lowmask1;
|
||||
top1 ^= top1 >> shift;
|
||||
top1 &= highmask1;
|
||||
x = top1 | bottom1;
|
||||
itype lowmask2 = (itype(1U) << (bits - shift)) - 1;
|
||||
itype bottom2 = x & lowmask2;
|
||||
bottom2 = unxorshift(bottom2, bits - shift, shift);
|
||||
bottom2 &= lowmask1;
|
||||
return top1 | bottom2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rotate left and right.
|
||||
*
|
||||
* In ideal world, compilers would spot idiomatic rotate code and convert it
|
||||
* to a rotate instruction. Of course, opinions vary on what the correct
|
||||
* idiom is and how to spot it. For clang, sometimes it generates better
|
||||
* (but still crappy) code if you define PCG_USE_ZEROCHECK_ROTATE_IDIOM.
|
||||
*/
|
||||
|
||||
template <typename itype>
|
||||
inline itype rotl(itype value, bitcount_t rot)
|
||||
{
|
||||
constexpr bitcount_t bits = sizeof(itype) * 8;
|
||||
constexpr bitcount_t mask = bits - 1;
|
||||
#if PCG_USE_ZEROCHECK_ROTATE_IDIOM
|
||||
return rot ? (value << rot) | (value >> (bits - rot)) : value;
|
||||
#else
|
||||
return (value << rot) | (value >> ((- rot) & mask));
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename itype>
|
||||
inline itype rotr(itype value, bitcount_t rot)
|
||||
{
|
||||
constexpr bitcount_t bits = sizeof(itype) * 8;
|
||||
constexpr bitcount_t mask = bits - 1;
|
||||
#if PCG_USE_ZEROCHECK_ROTATE_IDIOM
|
||||
return rot ? (value >> rot) | (value << (bits - rot)) : value;
|
||||
#else
|
||||
return (value >> rot) | (value << ((- rot) & mask));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Unfortunately, both Clang and GCC sometimes perform poorly when it comes
|
||||
* to properly recognizing idiomatic rotate code, so for we also provide
|
||||
* assembler directives (enabled with PCG_USE_INLINE_ASM). Boo, hiss.
|
||||
* (I hope that these compilers get better so that this code can die.)
|
||||
*
|
||||
* These overloads will be preferred over the general template code above.
|
||||
*/
|
||||
#if PCG_USE_INLINE_ASM && __GNUC__ && (__x86_64__ || __i386__)
|
||||
|
||||
inline uint8_t rotr(uint8_t value, bitcount_t rot)
|
||||
{
|
||||
asm ("rorb %%cl, %0" : "=r" (value) : "0" (value), "c" (rot));
|
||||
return value;
|
||||
}
|
||||
|
||||
inline uint16_t rotr(uint16_t value, bitcount_t rot)
|
||||
{
|
||||
asm ("rorw %%cl, %0" : "=r" (value) : "0" (value), "c" (rot));
|
||||
return value;
|
||||
}
|
||||
|
||||
inline uint32_t rotr(uint32_t value, bitcount_t rot)
|
||||
{
|
||||
asm ("rorl %%cl, %0" : "=r" (value) : "0" (value), "c" (rot));
|
||||
return value;
|
||||
}
|
||||
|
||||
#if __x86_64__
|
||||
inline uint64_t rotr(uint64_t value, bitcount_t rot)
|
||||
{
|
||||
asm ("rorq %%cl, %0" : "=r" (value) : "0" (value), "c" (rot));
|
||||
return value;
|
||||
}
|
||||
#endif // __x86_64__
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
// Use MSVC++ bit rotation intrinsics
|
||||
|
||||
#pragma intrinsic(_rotr, _rotr64, _rotr8, _rotr16)
|
||||
|
||||
inline uint8_t rotr(uint8_t value, bitcount_t rot)
|
||||
{
|
||||
return _rotr8(value, rot);
|
||||
}
|
||||
|
||||
inline uint16_t rotr(uint16_t value, bitcount_t rot)
|
||||
{
|
||||
return _rotr16(value, rot);
|
||||
}
|
||||
|
||||
inline uint32_t rotr(uint32_t value, bitcount_t rot)
|
||||
{
|
||||
return _rotr(value, rot);
|
||||
}
|
||||
|
||||
inline uint64_t rotr(uint64_t value, bitcount_t rot)
|
||||
{
|
||||
return _rotr64(value, rot);
|
||||
}
|
||||
|
||||
#endif // PCG_USE_INLINE_ASM
|
||||
|
||||
|
||||
/*
|
||||
* The C++ SeedSeq concept (modelled by seed_seq) can fill an array of
|
||||
* 32-bit integers with seed data, but sometimes we want to produce
|
||||
* larger or smaller integers.
|
||||
*
|
||||
* The following code handles this annoyance.
|
||||
*
|
||||
* uneven_copy will copy an array of 32-bit ints to an array of larger or
|
||||
* smaller ints (actually, the code is general it only needing forward
|
||||
* iterators). The copy is identical to the one that would be performed if
|
||||
* we just did memcpy on a standard little-endian machine, but works
|
||||
* regardless of the endian of the machine (or the weirdness of the ints
|
||||
* involved).
|
||||
*
|
||||
* generate_to initializes an array of integers using a SeedSeq
|
||||
* object. It is given the size as a static constant at compile time and
|
||||
* tries to avoid memory allocation. If we're filling in 32-bit constants
|
||||
* we just do it directly. If we need a separate buffer and it's small,
|
||||
* we allocate it on the stack. Otherwise, we fall back to heap allocation.
|
||||
* Ugh.
|
||||
*
|
||||
* generate_one produces a single value of some integral type using a
|
||||
* SeedSeq object.
|
||||
*/
|
||||
|
||||
/* uneven_copy helper, case where destination ints are less than 32 bit. */
|
||||
|
||||
template<class SrcIter, class DestIter>
|
||||
SrcIter uneven_copy_impl(
|
||||
SrcIter src_first, DestIter dest_first, DestIter dest_last,
|
||||
std::true_type)
|
||||
{
|
||||
typedef typename std::iterator_traits<SrcIter>::value_type src_t;
|
||||
typedef typename std::iterator_traits<DestIter>::value_type dest_t;
|
||||
|
||||
constexpr bitcount_t SRC_SIZE = sizeof(src_t);
|
||||
constexpr bitcount_t DEST_SIZE = sizeof(dest_t);
|
||||
constexpr bitcount_t DEST_BITS = DEST_SIZE * 8;
|
||||
constexpr bitcount_t SCALE = SRC_SIZE / DEST_SIZE;
|
||||
|
||||
size_t count = 0;
|
||||
src_t value = 0;
|
||||
|
||||
while (dest_first != dest_last) {
|
||||
if ((count++ % SCALE) == 0)
|
||||
value = *src_first++; // Get more bits
|
||||
else
|
||||
value >>= DEST_BITS; // Move down bits
|
||||
|
||||
*dest_first++ = dest_t(value); // Truncates, ignores high bits.
|
||||
}
|
||||
return src_first;
|
||||
}
|
||||
|
||||
/* uneven_copy helper, case where destination ints are more than 32 bit. */
|
||||
|
||||
template<class SrcIter, class DestIter>
|
||||
SrcIter uneven_copy_impl(
|
||||
SrcIter src_first, DestIter dest_first, DestIter dest_last,
|
||||
std::false_type)
|
||||
{
|
||||
typedef typename std::iterator_traits<SrcIter>::value_type src_t;
|
||||
typedef typename std::iterator_traits<DestIter>::value_type dest_t;
|
||||
|
||||
constexpr auto SRC_SIZE = sizeof(src_t);
|
||||
constexpr auto SRC_BITS = SRC_SIZE * 8;
|
||||
constexpr auto DEST_SIZE = sizeof(dest_t);
|
||||
constexpr auto SCALE = (DEST_SIZE+SRC_SIZE-1) / SRC_SIZE;
|
||||
|
||||
while (dest_first != dest_last) {
|
||||
dest_t value(0UL);
|
||||
unsigned int shift = 0;
|
||||
|
||||
for (size_t i = 0; i < SCALE; ++i) {
|
||||
value |= dest_t(*src_first++) << shift;
|
||||
shift += SRC_BITS;
|
||||
}
|
||||
|
||||
*dest_first++ = value;
|
||||
}
|
||||
return src_first;
|
||||
}
|
||||
|
||||
/* uneven_copy, call the right code for larger vs. smaller */
|
||||
|
||||
template<class SrcIter, class DestIter>
|
||||
inline SrcIter uneven_copy(SrcIter src_first,
|
||||
DestIter dest_first, DestIter dest_last)
|
||||
{
|
||||
typedef typename std::iterator_traits<SrcIter>::value_type src_t;
|
||||
typedef typename std::iterator_traits<DestIter>::value_type dest_t;
|
||||
|
||||
constexpr bool DEST_IS_SMALLER = sizeof(dest_t) < sizeof(src_t);
|
||||
|
||||
return uneven_copy_impl(src_first, dest_first, dest_last,
|
||||
std::integral_constant<bool, DEST_IS_SMALLER>{});
|
||||
}
|
||||
|
||||
/* generate_to, fill in a fixed-size array of integral type using a SeedSeq
|
||||
* (actually works for any random-access iterator)
|
||||
*/
|
||||
|
||||
template <size_t size, typename SeedSeq, typename DestIter>
|
||||
inline void generate_to_impl(SeedSeq&& generator, DestIter dest,
|
||||
std::true_type)
|
||||
{
|
||||
generator.generate(dest, dest+size);
|
||||
}
|
||||
|
||||
template <size_t size, typename SeedSeq, typename DestIter>
|
||||
void generate_to_impl(SeedSeq&& generator, DestIter dest,
|
||||
std::false_type)
|
||||
{
|
||||
typedef typename std::iterator_traits<DestIter>::value_type dest_t;
|
||||
constexpr auto DEST_SIZE = sizeof(dest_t);
|
||||
constexpr auto GEN_SIZE = sizeof(uint32_t);
|
||||
|
||||
constexpr bool GEN_IS_SMALLER = GEN_SIZE < DEST_SIZE;
|
||||
constexpr size_t FROM_ELEMS =
|
||||
GEN_IS_SMALLER
|
||||
? size * ((DEST_SIZE+GEN_SIZE-1) / GEN_SIZE)
|
||||
: (size + (GEN_SIZE / DEST_SIZE) - 1)
|
||||
/ ((GEN_SIZE / DEST_SIZE) + GEN_IS_SMALLER);
|
||||
// this odd code ^^^^^^^^^^^^^^^^^ is work-around for
|
||||
// a bug: http://llvm.org/bugs/show_bug.cgi?id=21287
|
||||
|
||||
if (FROM_ELEMS <= 1024) {
|
||||
uint32_t buffer[FROM_ELEMS];
|
||||
generator.generate(buffer, buffer+FROM_ELEMS);
|
||||
uneven_copy(buffer, dest, dest+size);
|
||||
} else {
|
||||
uint32_t* buffer = static_cast<uint32_t*>(malloc(GEN_SIZE * FROM_ELEMS));
|
||||
generator.generate(buffer, buffer+FROM_ELEMS);
|
||||
uneven_copy(buffer, dest, dest+size);
|
||||
free(static_cast<void*>(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t size, typename SeedSeq, typename DestIter>
|
||||
inline void generate_to(SeedSeq&& generator, DestIter dest)
|
||||
{
|
||||
typedef typename std::iterator_traits<DestIter>::value_type dest_t;
|
||||
constexpr bool IS_32BIT = sizeof(dest_t) == sizeof(uint32_t);
|
||||
|
||||
generate_to_impl<size>(std::forward<SeedSeq>(generator), dest,
|
||||
std::integral_constant<bool, IS_32BIT>{});
|
||||
}
|
||||
|
||||
/* generate_one, produce a value of integral type using a SeedSeq
|
||||
* (optionally, we can have it produce more than one and pick which one
|
||||
* we want)
|
||||
*/
|
||||
|
||||
template <typename UInt, size_t i = 0UL, size_t N = i+1UL, typename SeedSeq>
|
||||
inline UInt generate_one(SeedSeq&& generator)
|
||||
{
|
||||
UInt result[N];
|
||||
generate_to<N>(std::forward<SeedSeq>(generator), result);
|
||||
return result[i];
|
||||
}
|
||||
|
||||
template <typename RngType>
|
||||
auto bounded_rand(RngType& rng, typename RngType::result_type upper_bound)
|
||||
-> typename RngType::result_type
|
||||
{
|
||||
typedef typename RngType::result_type rtype;
|
||||
rtype threshold = (RngType::max() - RngType::min() + rtype(1) - upper_bound)
|
||||
% upper_bound;
|
||||
for (;;) {
|
||||
rtype r = rng() - RngType::min();
|
||||
if (r >= threshold)
|
||||
return r % upper_bound;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iter, typename RandType>
|
||||
void shuffle(Iter from, Iter to, RandType&& rng)
|
||||
{
|
||||
typedef typename std::iterator_traits<Iter>::difference_type delta_t;
|
||||
typedef typename std::remove_reference<RandType>::type::result_type result_t;
|
||||
auto count = to - from;
|
||||
while (count > 1) {
|
||||
delta_t chosen = delta_t(bounded_rand(rng, result_t(count)));
|
||||
--count;
|
||||
--to;
|
||||
using std::swap;
|
||||
swap(*(from + chosen), *to);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Although std::seed_seq is useful, it isn't everything. Often we want to
|
||||
* initialize a random-number generator some other way, such as from a random
|
||||
* device.
|
||||
*
|
||||
* Technically, it does not meet the requirements of a SeedSequence because
|
||||
* it lacks some of the rarely-used member functions (some of which would
|
||||
* be impossible to provide). However the C++ standard is quite specific
|
||||
* that actual engines only called the generate method, so it ought not to be
|
||||
* a problem in practice.
|
||||
*/
|
||||
|
||||
template <typename RngType>
|
||||
class seed_seq_from {
|
||||
private:
|
||||
RngType rng_;
|
||||
|
||||
typedef uint_least32_t result_type;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
seed_seq_from(Args&&... args) :
|
||||
rng_(std::forward<Args>(args)...)
|
||||
{
|
||||
// Nothing (else) to do...
|
||||
}
|
||||
|
||||
template<typename Iter>
|
||||
void generate(Iter start, Iter finish)
|
||||
{
|
||||
for (auto i = start; i != finish; ++i)
|
||||
*i = result_type(rng_());
|
||||
}
|
||||
|
||||
constexpr size_t size() const
|
||||
{
|
||||
return (sizeof(typename RngType::result_type) > sizeof(result_type)
|
||||
&& RngType::max() > ~size_t(0UL))
|
||||
? ~size_t(0UL)
|
||||
: size_t(RngType::max());
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Sometimes you might want a distinct seed based on when the program
|
||||
* was compiled. That way, a particular instance of the program will
|
||||
* behave the same way, but when recompiled it'll produce a different
|
||||
* value.
|
||||
*/
|
||||
|
||||
template <typename IntType>
|
||||
struct static_arbitrary_seed {
|
||||
private:
|
||||
static constexpr IntType fnv(IntType hash, const char* pos) {
|
||||
return *pos == '\0'
|
||||
? hash
|
||||
: fnv((hash * IntType(16777619U)) ^ *pos, (pos+1));
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr IntType value = fnv(IntType(2166136261U ^ sizeof(IntType)),
|
||||
__DATE__ __TIME__ __FILE__);
|
||||
};
|
||||
|
||||
// Sometimes, when debugging or testing, it's handy to be able print the name
|
||||
// of a (in human-readable form). This code allows the idiom:
|
||||
//
|
||||
// cout << printable_typename<my_foo_type_t>()
|
||||
//
|
||||
// to print out my_foo_type_t (or its concrete type if it is a synonym)
|
||||
|
||||
#if __cpp_rtti || __GXX_RTTI
|
||||
|
||||
template <typename T>
|
||||
struct printable_typename {};
|
||||
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& out, printable_typename<T>) {
|
||||
const char *implementation_typename = typeid(T).name();
|
||||
#ifdef __GNUC__
|
||||
int status;
|
||||
char* pretty_name =
|
||||
abi::__cxa_demangle(implementation_typename, nullptr, nullptr, &status);
|
||||
if (status == 0)
|
||||
out << pretty_name;
|
||||
free(static_cast<void*>(pretty_name));
|
||||
if (status == 0)
|
||||
return out;
|
||||
#endif
|
||||
out << implementation_typename;
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif // __cpp_rtti || __GXX_RTTI
|
||||
|
||||
} // namespace pcg_extras
|
||||
|
||||
#endif // PCG_EXTRAS_HPP_INCLUDED
|
||||
1951
extern/include/pcg_random.hpp
vendored
Normal file
1951
extern/include/pcg_random.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1006
extern/include/pcg_uint128.hpp
vendored
Normal file
1006
extern/include/pcg_uint128.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
840
extern/include/utf8.h
vendored
Normal file
840
extern/include/utf8.h
vendored
Normal file
@@ -0,0 +1,840 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <EterLocale/Arabic.h>
|
||||
|
||||
// ============================================================================
|
||||
// CONFIGURATION CONSTANTS
|
||||
// ============================================================================
|
||||
|
||||
// Maximum text length for security/performance (prevent DoS attacks)
|
||||
constexpr size_t MAX_TEXT_LENGTH = 65536; // 64KB of text
|
||||
constexpr size_t MAX_CHAT_TEXT_LENGTH = 4096; // 4KB for chat messages
|
||||
|
||||
// Arabic shaping buffer size calculations
|
||||
constexpr size_t ARABIC_SHAPING_EXPANSION_FACTOR = 2;
|
||||
constexpr size_t ARABIC_SHAPING_SAFETY_MARGIN = 16;
|
||||
constexpr size_t ARABIC_SHAPING_EXPANSION_FACTOR_RETRY = 4;
|
||||
constexpr size_t ARABIC_SHAPING_SAFETY_MARGIN_RETRY = 64;
|
||||
|
||||
// ============================================================================
|
||||
// DEBUG LOGGING (Only enabled in Debug builds)
|
||||
// ============================================================================
|
||||
#ifdef _DEBUG
|
||||
#define DEBUG_BIDI // Enabled in debug builds for diagnostics
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_BIDI
|
||||
#include <cstdio>
|
||||
#define BIDI_LOG(fmt, ...) printf("[BiDi] " fmt "\n", __VA_ARGS__)
|
||||
#define BIDI_LOG_SIMPLE(msg) printf("[BiDi] %s\n", msg)
|
||||
#else
|
||||
#define BIDI_LOG(fmt, ...) ((void)0)
|
||||
#define BIDI_LOG_SIMPLE(msg) ((void)0)
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// UNICODE VALIDATION HELPERS
|
||||
// ============================================================================
|
||||
|
||||
// Check if codepoint is a valid Unicode scalar value (not surrogate, not non-character)
|
||||
static inline bool IsValidUnicodeScalar(wchar_t ch)
|
||||
{
|
||||
// Reject surrogate pairs (UTF-16 encoding artifacts, invalid in UTF-8)
|
||||
if (ch >= 0xD800 && ch <= 0xDFFF)
|
||||
return false;
|
||||
|
||||
// Reject non-characters (reserved by Unicode standard)
|
||||
if ((ch >= 0xFDD0 && ch <= 0xFDEF) || // Arabic Presentation Forms non-chars
|
||||
(ch & 0xFFFE) == 0xFFFE) // U+FFFE, U+FFFF, etc.
|
||||
return false;
|
||||
|
||||
// Accept everything else in BMP (0x0000-0xFFFF)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sanitize a wide string by removing invalid Unicode codepoints
|
||||
static inline void SanitizeWideString(std::wstring& ws)
|
||||
{
|
||||
ws.erase(std::remove_if(ws.begin(), ws.end(),
|
||||
[](wchar_t ch) { return !IsValidUnicodeScalar(ch); }),
|
||||
ws.end());
|
||||
}
|
||||
|
||||
// UTF-8 -> UTF-16 (Windows wide)
|
||||
inline std::wstring Utf8ToWide(const std::string& s)
|
||||
{
|
||||
if (s.empty())
|
||||
return L"";
|
||||
|
||||
// Validate size limits (prevent DoS and INT_MAX overflow)
|
||||
if (s.size() > MAX_TEXT_LENGTH || s.size() > INT_MAX)
|
||||
{
|
||||
BIDI_LOG("Utf8ToWide: String too large (%zu bytes)", s.size());
|
||||
return L""; // String too large
|
||||
}
|
||||
|
||||
int wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), (int)s.size(), nullptr, 0);
|
||||
if (wlen <= 0)
|
||||
{
|
||||
BIDI_LOG("Utf8ToWide: Invalid UTF-8 sequence (error %d)", GetLastError());
|
||||
return L"";
|
||||
}
|
||||
|
||||
std::wstring out(wlen, L'\0');
|
||||
int written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), (int)s.size(), out.data(), wlen);
|
||||
if (written <= 0 || written != wlen)
|
||||
{
|
||||
BIDI_LOG("Utf8ToWide: Second conversion failed (written=%d, expected=%d, error=%d)", written, wlen, GetLastError());
|
||||
return L""; // Conversion failed unexpectedly
|
||||
}
|
||||
|
||||
// Optional: Sanitize to remove invalid Unicode codepoints (surrogates, non-characters)
|
||||
// Uncomment if you want strict validation
|
||||
// SanitizeWideString(out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Convenience overload for char*
|
||||
inline std::wstring Utf8ToWide(const char* s)
|
||||
{
|
||||
if (!s || !*s)
|
||||
return L"";
|
||||
|
||||
int wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, nullptr, 0);
|
||||
if (wlen <= 0)
|
||||
return L"";
|
||||
|
||||
// wlen includes terminating NUL
|
||||
std::wstring out(wlen, L'\0');
|
||||
|
||||
int written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s, -1, out.data(), wlen);
|
||||
if (written <= 0 || written != wlen)
|
||||
{
|
||||
BIDI_LOG("Utf8ToWide(char*): Conversion failed (written=%d, expected=%d, error=%d)", written, wlen, GetLastError());
|
||||
return L"";
|
||||
}
|
||||
|
||||
// Drop the terminating NUL from std::wstring length
|
||||
if (!out.empty() && out.back() == L'\0')
|
||||
out.pop_back();
|
||||
|
||||
// Optional: Sanitize to remove invalid Unicode codepoints
|
||||
// SanitizeWideString(out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// UTF-16 (Windows wide) -> UTF-8
|
||||
inline std::string WideToUtf8(const std::wstring& ws)
|
||||
{
|
||||
if (ws.empty())
|
||||
return "";
|
||||
|
||||
// Validate size limits (prevent DoS and INT_MAX overflow)
|
||||
if (ws.size() > MAX_TEXT_LENGTH || ws.size() > INT_MAX)
|
||||
return ""; // String too large
|
||||
|
||||
int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, ws.data(), (int)ws.size(), nullptr, 0, nullptr, nullptr);
|
||||
if (len <= 0)
|
||||
return "";
|
||||
|
||||
std::string out(len, '\0');
|
||||
int written = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, ws.data(), (int)ws.size(), out.data(), len, nullptr, nullptr);
|
||||
if (written <= 0 || written != len)
|
||||
{
|
||||
BIDI_LOG("WideToUtf8: Conversion failed (written=%d, expected=%d, error=%d)", written, len, GetLastError());
|
||||
return ""; // Conversion failed
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Convenience overload for wchar_t*
|
||||
inline std::string WideToUtf8(const wchar_t* ws)
|
||||
{
|
||||
if (!ws)
|
||||
return "";
|
||||
return WideToUtf8(std::wstring(ws));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// RTL & BiDi formatting for RTL UI
|
||||
// ============================================================================
|
||||
|
||||
enum class EBidiDir { LTR, RTL };
|
||||
enum class ECharDir : unsigned char { Neutral, LTR, RTL };
|
||||
|
||||
struct TBidiRun
|
||||
{
|
||||
EBidiDir dir;
|
||||
std::vector<wchar_t> text; // logical order
|
||||
};
|
||||
|
||||
static inline bool IsRTLCodepoint(wchar_t ch)
|
||||
{
|
||||
// Directional marks / isolates / embeddings that affect bidi
|
||||
if (ch == 0x200F || ch == 0x061C) return true; // RLM, ALM
|
||||
if (ch >= 0x202B && ch <= 0x202E) return true; // RLE/RLO/PDF/LRE/LRO
|
||||
if (ch >= 0x2066 && ch <= 0x2069) return true; // isolates
|
||||
|
||||
// Hebrew + Arabic blocks (BMP)
|
||||
if (ch >= 0x0590 && ch <= 0x08FF) return true;
|
||||
|
||||
// Presentation forms
|
||||
if (ch >= 0xFB1D && ch <= 0xFDFF) return true;
|
||||
if (ch >= 0xFE70 && ch <= 0xFEFF) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool IsStrongAlpha(wchar_t ch)
|
||||
{
|
||||
// Use thread-local cache for BMP (Thread safety)
|
||||
thread_local static unsigned char cache[65536] = {}; // 0=unknown, 1=true, 2=false
|
||||
unsigned char& v = cache[(unsigned short)ch];
|
||||
if (v == 1) return true;
|
||||
if (v == 2) return false;
|
||||
|
||||
WORD type = 0;
|
||||
bool ok = GetStringTypeW(CT_CTYPE1, &ch, 1, &type) && (type & C1_ALPHA);
|
||||
v = ok ? 1 : 2;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static inline bool IsDigit(wchar_t ch)
|
||||
{
|
||||
// Fast path for ASCII digits (90%+ of digit checks)
|
||||
if (ch >= L'0' && ch <= L'9')
|
||||
return true;
|
||||
|
||||
// For non-ASCII, use cache (Arabic-Indic digits, etc.)
|
||||
thread_local static unsigned char cache[65536] = {}; // 0=unknown, 1=true, 2=false
|
||||
unsigned char& v = cache[(unsigned short)ch];
|
||||
if (v == 1) return true;
|
||||
if (v == 2) return false;
|
||||
|
||||
WORD type = 0;
|
||||
bool ok = GetStringTypeW(CT_CTYPE1, &ch, 1, &type) && (type & C1_DIGIT);
|
||||
v = ok ? 1 : 2;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static inline bool IsNameTokenPunct(wchar_t ch)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case L'#':
|
||||
case L'@':
|
||||
case L'$':
|
||||
case L'%':
|
||||
case L'&':
|
||||
case L'*':
|
||||
case L'+':
|
||||
case L'-':
|
||||
case L'_':
|
||||
case L'=':
|
||||
case L'.':
|
||||
case L',':
|
||||
case L'/':
|
||||
case L'\\':
|
||||
case L'(':
|
||||
case L')':
|
||||
// Brackets are handled specially - see GetCharDirSmart
|
||||
// case L'[':
|
||||
// case L']':
|
||||
case L'{':
|
||||
case L'}':
|
||||
case L'<':
|
||||
case L'>':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check RTL first to avoid classifying Arabic as LTR
|
||||
static inline bool IsStrongLTR(wchar_t ch)
|
||||
{
|
||||
if (IsRTLCodepoint(ch))
|
||||
return false;
|
||||
return IsStrongAlpha(ch) || IsDigit(ch);
|
||||
}
|
||||
|
||||
static inline bool HasStrongLTRNeighbor(const wchar_t* s, int n, int i)
|
||||
{
|
||||
// Skip neutral characters (spaces, punctuation) to find nearest strong character
|
||||
// This fixes mixed-direction text like "english + arabic"
|
||||
|
||||
// Search backwards for strong character (skip neutrals/whitespace)
|
||||
for (int j = i - 1; j >= 0; --j)
|
||||
{
|
||||
wchar_t ch = s[j];
|
||||
|
||||
// Skip spaces and common neutral punctuation
|
||||
if (ch == L' ' || ch == L'\t' || ch == L'\n')
|
||||
continue;
|
||||
|
||||
// Found strong LTR
|
||||
if (IsStrongLTR(ch))
|
||||
return true;
|
||||
|
||||
// Found strong RTL or other strong character
|
||||
if (IsRTLCodepoint(ch) || IsStrongAlpha(ch))
|
||||
break;
|
||||
}
|
||||
|
||||
// Search forwards for strong character (skip neutrals/whitespace)
|
||||
for (int j = i + 1; j < n; ++j)
|
||||
{
|
||||
wchar_t ch = s[j];
|
||||
|
||||
// Skip spaces and common neutral punctuation
|
||||
if (ch == L' ' || ch == L'\t' || ch == L'\n')
|
||||
continue;
|
||||
|
||||
// Found strong LTR
|
||||
if (IsStrongLTR(ch))
|
||||
return true;
|
||||
|
||||
// Found strong RTL or other strong character
|
||||
if (IsRTLCodepoint(ch) || IsStrongAlpha(ch))
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline ECharDir GetCharDir(wchar_t ch)
|
||||
{
|
||||
if (IsRTLCodepoint(ch))
|
||||
return ECharDir::RTL;
|
||||
|
||||
// Use IsStrongLTR which now correctly excludes RTL
|
||||
if (IsStrongLTR(ch))
|
||||
return ECharDir::LTR;
|
||||
|
||||
return ECharDir::Neutral;
|
||||
}
|
||||
|
||||
static inline ECharDir GetCharDirSmart(const wchar_t* s, int n, int i)
|
||||
{
|
||||
wchar_t ch = s[i];
|
||||
|
||||
// True RTL letters/marks
|
||||
if (IsRTLCodepoint(ch))
|
||||
return ECharDir::RTL;
|
||||
|
||||
// True LTR letters/digits (now correctly excludes RTL)
|
||||
if (IsStrongLTR(ch))
|
||||
return ECharDir::LTR;
|
||||
|
||||
// Parentheses: always LTR to keep them with their content
|
||||
if (ch == L'(' || ch == L')')
|
||||
return ECharDir::LTR;
|
||||
|
||||
// Common punctuation: treat as strong LTR to prevent jumping around in mixed text
|
||||
// This makes "Hello + اختبار" and "اختبار + Hello" both keep punctuation in place
|
||||
if (ch == L'+' || ch == L'-' || ch == L'=' || ch == L'*' || ch == L'/' ||
|
||||
ch == L'<' || ch == L'>' || ch == L'&' || ch == L'|' || ch == L'@' || ch == L'#')
|
||||
return ECharDir::LTR;
|
||||
|
||||
// Percentage sign: attach to numbers (scan nearby for digits/minus/plus)
|
||||
// Handles: "%20", "20%", "-6%", "%d%%", etc.
|
||||
if (ch == L'%')
|
||||
{
|
||||
// Look backward for digit, %, -, or +
|
||||
for (int j = i - 1; j >= 0 && (i - j) < 5; --j)
|
||||
{
|
||||
wchar_t prev = s[j];
|
||||
if (IsDigit(prev) || prev == L'%' || prev == L'-' || prev == L'+')
|
||||
return ECharDir::LTR;
|
||||
if (prev != L' ' && prev != L'\t')
|
||||
break; // Stop if we hit non-numeric character
|
||||
}
|
||||
// Look forward for digit, %, -, or +
|
||||
for (int j = i + 1; j < n && (j - i) < 5; ++j)
|
||||
{
|
||||
wchar_t next = s[j];
|
||||
if (IsDigit(next) || next == L'%' || next == L'-' || next == L'+')
|
||||
return ECharDir::LTR;
|
||||
if (next != L' ' && next != L'\t')
|
||||
break; // Stop if we hit non-numeric character
|
||||
}
|
||||
return ECharDir::Neutral;
|
||||
}
|
||||
|
||||
// Minus/dash: attach to numbers (scan nearby for digits/%)
|
||||
// Handles: "-6", "5-10", "-6%%", etc.
|
||||
if (ch == L'-')
|
||||
{
|
||||
// Look backward for digit or %
|
||||
for (int j = i - 1; j >= 0 && (i - j) < 3; --j)
|
||||
{
|
||||
wchar_t prev = s[j];
|
||||
if (IsDigit(prev) || prev == L'%')
|
||||
return ECharDir::LTR;
|
||||
if (prev != L' ' && prev != L'\t')
|
||||
break;
|
||||
}
|
||||
// Look forward for digit or %
|
||||
for (int j = i + 1; j < n && (j - i) < 3; ++j)
|
||||
{
|
||||
wchar_t next = s[j];
|
||||
if (IsDigit(next) || next == L'%')
|
||||
return ECharDir::LTR;
|
||||
if (next != L' ' && next != L'\t')
|
||||
break;
|
||||
}
|
||||
return ECharDir::Neutral;
|
||||
}
|
||||
|
||||
// Colon: attach to preceding text direction
|
||||
// Look backward to find strong character
|
||||
if (ch == L':')
|
||||
{
|
||||
for (int j = i - 1; j >= 0; --j)
|
||||
{
|
||||
if (s[j] == L' ' || s[j] == L'\t')
|
||||
continue; // Skip spaces
|
||||
if (IsRTLCodepoint(s[j]))
|
||||
return ECharDir::RTL; // Attach to RTL text
|
||||
if (IsStrongLTR(s[j]))
|
||||
return ECharDir::LTR; // Attach to LTR text
|
||||
}
|
||||
return ECharDir::Neutral;
|
||||
}
|
||||
|
||||
// Enhancement marker: '+' followed by digit(s) should attach to preceding text
|
||||
// If preceded by RTL, treat as RTL to keep "+9" with the item name
|
||||
// Otherwise treat as LTR
|
||||
if (ch == L'+' && i + 1 < n && IsDigit(s[i + 1]))
|
||||
{
|
||||
// Look backward for the last strong character
|
||||
for (int j = i - 1; j >= 0; --j)
|
||||
{
|
||||
if (IsRTLCodepoint(s[j]))
|
||||
return ECharDir::RTL; // Attach to preceding RTL text
|
||||
if (IsStrongLTR(s[j]))
|
||||
return ECharDir::LTR; // Attach to preceding LTR text
|
||||
// Skip neutral characters
|
||||
}
|
||||
return ECharDir::LTR; // Default to LTR if no strong character found
|
||||
}
|
||||
|
||||
// Brackets: always attach to the content inside them
|
||||
// This fixes hyperlinks like "[درع فولاذي أسود+9]"
|
||||
if (ch == L'[' || ch == L']')
|
||||
{
|
||||
// Opening bracket '[': look forward for strong character
|
||||
if (ch == L'[')
|
||||
{
|
||||
for (int j = i + 1; j < n; ++j)
|
||||
{
|
||||
wchar_t next = s[j];
|
||||
if (next == L']') break; // End of bracket content
|
||||
if (IsRTLCodepoint(next)) return ECharDir::RTL;
|
||||
if (IsStrongLTR(next)) return ECharDir::LTR;
|
||||
}
|
||||
}
|
||||
// Closing bracket ']': look backward for strong character
|
||||
else if (ch == L']')
|
||||
{
|
||||
for (int j = i - 1; j >= 0; --j)
|
||||
{
|
||||
wchar_t prev = s[j];
|
||||
if (prev == L'[') break; // Start of bracket content
|
||||
if (IsRTLCodepoint(prev)) return ECharDir::RTL;
|
||||
if (IsStrongLTR(prev)) return ECharDir::LTR;
|
||||
}
|
||||
}
|
||||
// If we can't determine, treat as neutral
|
||||
return ECharDir::Neutral;
|
||||
}
|
||||
|
||||
// Spaces should attach to adjacent strong characters to avoid fragmentation
|
||||
// This fixes "english + arabic" by keeping " + " with "english"
|
||||
if (ch == L' ' || ch == L'\t')
|
||||
{
|
||||
if (HasStrongLTRNeighbor(s, n, i))
|
||||
return ECharDir::LTR;
|
||||
// Note: We don't check for RTL neighbor because ResolveNeutralDir handles that
|
||||
}
|
||||
|
||||
// Name-token punctuation: if adjacent to LTR, treat as LTR to keep token intact
|
||||
if (IsNameTokenPunct(ch) && HasStrongLTRNeighbor(s, n, i))
|
||||
return ECharDir::LTR;
|
||||
|
||||
return ECharDir::Neutral;
|
||||
}
|
||||
|
||||
// Pre-computed strong character lookup for O(1) neutral resolution
|
||||
struct TStrongDirCache
|
||||
{
|
||||
std::vector<EBidiDir> nextStrong; // nextStrong[i] = direction of next strong char after position i
|
||||
EBidiDir baseDir;
|
||||
|
||||
TStrongDirCache(const wchar_t* s, int n, EBidiDir base) : nextStrong(n), baseDir(base)
|
||||
{
|
||||
// Build reverse lookup: scan from end to beginning
|
||||
// Use GetCharDirSmart for context-aware character classification
|
||||
EBidiDir lastSeen = baseDir;
|
||||
for (int i = n - 1; i >= 0; --i)
|
||||
{
|
||||
ECharDir cd = GetCharDirSmart(s, n, i);
|
||||
if (cd == ECharDir::LTR)
|
||||
lastSeen = EBidiDir::LTR;
|
||||
else if (cd == ECharDir::RTL)
|
||||
lastSeen = EBidiDir::RTL;
|
||||
|
||||
nextStrong[i] = lastSeen;
|
||||
}
|
||||
}
|
||||
|
||||
EBidiDir GetNextStrong(int i) const
|
||||
{
|
||||
if (i + 1 < (int)nextStrong.size())
|
||||
return nextStrong[i + 1];
|
||||
return baseDir;
|
||||
}
|
||||
};
|
||||
|
||||
static inline EBidiDir ResolveNeutralDir(const wchar_t* s, int n, int i, EBidiDir baseDir, EBidiDir lastStrong, const TStrongDirCache* cache = nullptr)
|
||||
{
|
||||
// Use pre-computed cache if available (O(1) instead of O(n))
|
||||
EBidiDir nextStrong = baseDir;
|
||||
if (cache)
|
||||
{
|
||||
nextStrong = cache->GetNextStrong(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Linear scan (slower, but works without cache)
|
||||
for (int j = i + 1; j < n; ++j)
|
||||
{
|
||||
ECharDir cd = GetCharDirSmart(s, n, j);
|
||||
if (cd == ECharDir::LTR) { nextStrong = EBidiDir::LTR; break; }
|
||||
if (cd == ECharDir::RTL) { nextStrong = EBidiDir::RTL; break; }
|
||||
}
|
||||
}
|
||||
|
||||
// If both sides agree, neutral adopts that direction
|
||||
if (lastStrong == nextStrong)
|
||||
return lastStrong;
|
||||
|
||||
// Handle edge cases for leading/trailing punctuation
|
||||
if (nextStrong == baseDir && lastStrong != baseDir)
|
||||
return lastStrong;
|
||||
|
||||
if (lastStrong == baseDir && nextStrong != baseDir)
|
||||
return nextStrong;
|
||||
|
||||
// Otherwise fall back to base direction
|
||||
return baseDir;
|
||||
}
|
||||
|
||||
static EBidiDir DetectBaseDir_FirstStrong(const wchar_t* s, int n)
|
||||
{
|
||||
if (!s || n <= 0)
|
||||
return EBidiDir::LTR;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
const wchar_t ch = s[i];
|
||||
// Check RTL first, then alpha
|
||||
if (IsRTLCodepoint(ch))
|
||||
return EBidiDir::RTL;
|
||||
|
||||
if (IsStrongAlpha(ch))
|
||||
return EBidiDir::LTR;
|
||||
}
|
||||
|
||||
return EBidiDir::LTR;
|
||||
}
|
||||
|
||||
static std::vector<wchar_t> BuildVisualBidiText_Tagless(const wchar_t* s, int n, bool forceRTL)
|
||||
{
|
||||
if (!s || n <= 0)
|
||||
return {};
|
||||
|
||||
// 1) base direction
|
||||
EBidiDir base = forceRTL ? EBidiDir::RTL : DetectBaseDir_FirstStrong(s, n);
|
||||
|
||||
// Pre-compute strong character positions for O(1) neutral resolution
|
||||
TStrongDirCache strongCache(s, n, base);
|
||||
|
||||
// 2) split into runs
|
||||
// Estimate runs based on text length (~1 per 50 chars, min 4)
|
||||
std::vector<TBidiRun> runs;
|
||||
const size_t estimatedRuns = (size_t)std::max(4, n / 50);
|
||||
runs.reserve(estimatedRuns);
|
||||
|
||||
auto push_run = [&](EBidiDir d)
|
||||
{
|
||||
if (runs.empty() || runs.back().dir != d)
|
||||
runs.push_back(TBidiRun{ d, {} });
|
||||
};
|
||||
|
||||
// start with base so leading neutrals attach predictably
|
||||
push_run(base);
|
||||
|
||||
EBidiDir lastStrong = base;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
wchar_t ch = s[i];
|
||||
|
||||
EBidiDir d;
|
||||
ECharDir cd = GetCharDirSmart(s, n, i);
|
||||
|
||||
if (cd == ECharDir::RTL)
|
||||
{
|
||||
d = EBidiDir::RTL;
|
||||
lastStrong = EBidiDir::RTL;
|
||||
}
|
||||
else if (cd == ECharDir::LTR)
|
||||
{
|
||||
d = EBidiDir::LTR;
|
||||
lastStrong = EBidiDir::LTR;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pass cache for O(1) lookup instead of O(n) scan
|
||||
d = ResolveNeutralDir(s, n, i, base, lastStrong, &strongCache);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_BIDI
|
||||
if (i < 50) // Only log first 50 chars to avoid spam
|
||||
{
|
||||
BIDI_LOG("Char[%d] U+%04X '%lc' → CharDir=%s, RunDir=%s",
|
||||
i, (unsigned int)ch, (ch >= 32 && ch < 127) ? ch : L'?',
|
||||
cd == ECharDir::RTL ? "RTL" : (cd == ECharDir::LTR ? "LTR" : "Neutral"),
|
||||
d == EBidiDir::RTL ? "RTL" : "LTR");
|
||||
}
|
||||
#endif
|
||||
|
||||
push_run(d);
|
||||
runs.back().text.push_back(ch);
|
||||
}
|
||||
|
||||
// 3) shape RTL runs in logical order (Arabic shaping)
|
||||
for (auto& r : runs)
|
||||
{
|
||||
if (r.dir != EBidiDir::RTL)
|
||||
continue;
|
||||
|
||||
if (r.text.empty())
|
||||
continue;
|
||||
|
||||
// Check for potential integer overflow before allocation
|
||||
if (r.text.size() > SIZE_MAX / ARABIC_SHAPING_EXPANSION_FACTOR_RETRY - ARABIC_SHAPING_SAFETY_MARGIN_RETRY)
|
||||
{
|
||||
BIDI_LOG("BuildVisualBidiText: RTL run too large for shaping (%zu chars)", r.text.size());
|
||||
continue; // Text too large to process safely
|
||||
}
|
||||
|
||||
std::vector<wchar_t> shaped(r.text.size() * ARABIC_SHAPING_EXPANSION_FACTOR + ARABIC_SHAPING_SAFETY_MARGIN, 0);
|
||||
|
||||
int outLen = Arabic_MakeShape(r.text.data(), (int)r.text.size(), shaped.data(), (int)shaped.size());
|
||||
if (outLen <= 0)
|
||||
{
|
||||
BIDI_LOG("Arabic_MakeShape FAILED for RTL run of %zu characters", r.text.size());
|
||||
BIDI_LOG(" WARNING: This RTL text segment will NOT be displayed!");
|
||||
BIDI_LOG(" First few characters: U+%04X U+%04X U+%04X U+%04X",
|
||||
r.text.size() > 0 ? (unsigned int)r.text[0] : 0,
|
||||
r.text.size() > 1 ? (unsigned int)r.text[1] : 0,
|
||||
r.text.size() > 2 ? (unsigned int)r.text[2] : 0,
|
||||
r.text.size() > 3 ? (unsigned int)r.text[3] : 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Retry once if buffer too small
|
||||
if (outLen >= (int)shaped.size())
|
||||
{
|
||||
shaped.assign(r.text.size() * ARABIC_SHAPING_EXPANSION_FACTOR_RETRY + ARABIC_SHAPING_SAFETY_MARGIN_RETRY, 0);
|
||||
outLen = Arabic_MakeShape(r.text.data(), (int)r.text.size(), shaped.data(), (int)shaped.size());
|
||||
if (outLen <= 0)
|
||||
continue;
|
||||
// Add error check instead of silent truncation
|
||||
if (outLen > (int)shaped.size())
|
||||
{
|
||||
BIDI_LOG("Arabic_MakeShape: Buffer still too small after retry (%d > %zu)", outLen, shaped.size());
|
||||
// Shaping failed critically, use unshaped text
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
r.text.assign(shaped.begin(), shaped.begin() + outLen);
|
||||
}
|
||||
|
||||
// 4) produce visual order:
|
||||
// - reverse RTL runs internally
|
||||
// - reverse run sequence if base RTL
|
||||
std::vector<wchar_t> visual;
|
||||
visual.reserve((size_t)n);
|
||||
|
||||
auto emit_run = [&](const TBidiRun& r)
|
||||
{
|
||||
if (r.dir == EBidiDir::RTL)
|
||||
{
|
||||
for (int k = (int)r.text.size() - 1; k >= 0; --k)
|
||||
visual.push_back(r.text[(size_t)k]);
|
||||
}
|
||||
else
|
||||
{
|
||||
visual.insert(visual.end(), r.text.begin(), r.text.end());
|
||||
}
|
||||
};
|
||||
|
||||
if (base == EBidiDir::LTR)
|
||||
{
|
||||
for (const auto& r : runs)
|
||||
emit_run(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = (int)runs.size() - 1; i >= 0; --i)
|
||||
emit_run(runs[(size_t)i]);
|
||||
}
|
||||
|
||||
return visual;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Chat Message BiDi Processing (Separate name/message handling)
|
||||
// ============================================================================
|
||||
|
||||
// Build visual BiDi text for chat messages with separate name and message
|
||||
// This avoids fragile " : " detection and handles cases where username contains " : "
|
||||
//
|
||||
// RECOMMENDED USAGE:
|
||||
// Instead of: SetValue("PlayerName : Message")
|
||||
// Use this function with separated components:
|
||||
// - name: "PlayerName" (without " : ")
|
||||
// - msg: "Message" (without " : ")
|
||||
//
|
||||
// INTEGRATION NOTES:
|
||||
// To use this properly, you need to:
|
||||
// 1. Modify the server/network code to send chat name and message separately
|
||||
// 2. Or parse the chat string in PythonNetworkStreamPhaseGame.cpp BEFORE passing to GrpTextInstance
|
||||
// 3. Then call this function instead of BuildVisualBidiText_Tagless
|
||||
//
|
||||
static inline std::vector<wchar_t> BuildVisualChatMessage(
|
||||
const wchar_t* name, int nameLen,
|
||||
const wchar_t* msg, int msgLen,
|
||||
bool forceRTL)
|
||||
{
|
||||
if (!name || !msg || nameLen <= 0 || msgLen <= 0)
|
||||
return {};
|
||||
|
||||
// Check if message contains RTL or hyperlink tags
|
||||
bool msgHasRTL = false;
|
||||
bool msgHasTags = false;
|
||||
for (int i = 0; i < msgLen; ++i)
|
||||
{
|
||||
if (IsRTLCodepoint(msg[i]))
|
||||
msgHasRTL = true;
|
||||
if (msg[i] == L'|')
|
||||
msgHasTags = true;
|
||||
if (msgHasRTL && msgHasTags)
|
||||
break;
|
||||
}
|
||||
|
||||
// Build result based on UI direction (pre-reserve exact size)
|
||||
std::vector<wchar_t> visual;
|
||||
visual.reserve((size_t)(nameLen + msgLen + 3)); // +3 for " : "
|
||||
|
||||
// Decision: UI direction determines order (for visual consistency)
|
||||
// RTL UI: "Message : Name" (message on right, consistent with RTL reading flow)
|
||||
// LTR UI: "Name : Message" (name on left, consistent with LTR reading flow)
|
||||
if (forceRTL)
|
||||
{
|
||||
// RTL UI: "Message : Name"
|
||||
// Don't apply BiDi if message has tags (hyperlinks are pre-formatted)
|
||||
if (msgHasTags)
|
||||
{
|
||||
visual.insert(visual.end(), msg, msg + msgLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply BiDi to message with auto-detection (don't force RTL)
|
||||
// Let the BiDi algorithm detect base direction from first strong character
|
||||
std::vector<wchar_t> msgVisual = BuildVisualBidiText_Tagless(msg, msgLen, false);
|
||||
visual.insert(visual.end(), msgVisual.begin(), msgVisual.end());
|
||||
}
|
||||
visual.push_back(L' ');
|
||||
visual.push_back(L':');
|
||||
visual.push_back(L' ');
|
||||
visual.insert(visual.end(), name, name + nameLen); // Name on left side
|
||||
}
|
||||
else
|
||||
{
|
||||
// LTR UI: "Name : Message"
|
||||
visual.insert(visual.end(), name, name + nameLen); // Name on left side
|
||||
visual.push_back(L' ');
|
||||
visual.push_back(L':');
|
||||
visual.push_back(L' ');
|
||||
// Don't apply BiDi if message has tags (hyperlinks are pre-formatted)
|
||||
if (msgHasTags)
|
||||
{
|
||||
visual.insert(visual.end(), msg, msg + msgLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apply BiDi to message with auto-detection (don't force RTL)
|
||||
// Let the BiDi algorithm detect base direction from first strong character
|
||||
std::vector<wchar_t> msgVisual = BuildVisualBidiText_Tagless(msg, msgLen, false);
|
||||
visual.insert(visual.end(), msgVisual.begin(), msgVisual.end());
|
||||
}
|
||||
}
|
||||
|
||||
return visual;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TextTail formatting for RTL UI
|
||||
// ============================================================================
|
||||
|
||||
enum class EPlaceDir
|
||||
{
|
||||
Left, // place block to the LEFT of the cursor (cursor is a right edge)
|
||||
Right // place block to the RIGHT of the cursor (cursor is a left edge)
|
||||
};
|
||||
|
||||
template <typename TText>
|
||||
inline float TextTailBiDi(TText* t, float cursorX, float y, float z, float fxAdd, EPlaceDir dir)
|
||||
{
|
||||
if (!t)
|
||||
return cursorX;
|
||||
|
||||
int w = 0, h = 0;
|
||||
t->GetTextSize(&w, &h);
|
||||
const float fw = static_cast<float>(w);
|
||||
|
||||
float x;
|
||||
if (dir == EPlaceDir::Left)
|
||||
{
|
||||
x = t->IsRTL() ? cursorX : (cursorX - fw);
|
||||
// advance cursor left
|
||||
cursorX = cursorX - fw - fxAdd;
|
||||
}
|
||||
else
|
||||
{
|
||||
x = t->IsRTL() ? (cursorX + fw) : cursorX;
|
||||
// advance cursor right
|
||||
cursorX = cursorX + fw + fxAdd;
|
||||
}
|
||||
|
||||
// SNAP to pixel grid to avoid "broken pixels"
|
||||
x = floorf(x + 0.5f);
|
||||
y = floorf(y + 0.5f);
|
||||
|
||||
t->SetPosition(x, y, z);
|
||||
t->Update();
|
||||
|
||||
return cursorX;
|
||||
}
|
||||
1
extern/library/CMakeLists.txt
vendored
1
extern/library/CMakeLists.txt
vendored
@@ -1,6 +1,5 @@
|
||||
add_subdirectory(DirectX)
|
||||
add_subdirectory(Granny)
|
||||
add_subdirectory(MilesSoundSystem)
|
||||
add_subdirectory(Python)
|
||||
add_subdirectory(SpeedTree)
|
||||
add_subdirectory(WebView)
|
||||
BIN
extern/library/DirectX/d3d9.lib
vendored
BIN
extern/library/DirectX/d3d9.lib
vendored
Binary file not shown.
BIN
extern/library/DirectX/d3dx9.lib
vendored
BIN
extern/library/DirectX/d3dx9.lib
vendored
Binary file not shown.
BIN
extern/library/DirectX/dinput8.lib
vendored
BIN
extern/library/DirectX/dinput8.lib
vendored
Binary file not shown.
BIN
extern/library/DirectX/dxguid.lib
vendored
BIN
extern/library/DirectX/dxguid.lib
vendored
Binary file not shown.
BIN
extern/library/Granny/granny2_static.lib
vendored
BIN
extern/library/Granny/granny2_static.lib
vendored
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
add_library(MilesSoundSystem STATIC IMPORTED GLOBAL)
|
||||
|
||||
set_target_properties(MilesSoundSystem PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/extern/include"
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/mss64.lib"
|
||||
)
|
||||
3
extern/library/MilesSoundSystem/mss64.lib
vendored
3
extern/library/MilesSoundSystem/mss64.lib
vendored
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ea999a3c3f5e22bea81b4f2658dfbe074fa1c63d3073de789735fc383cbe0f05
|
||||
size 3145894
|
||||
BIN
extern/library/Python/python27_static.lib
vendored
BIN
extern/library/Python/python27_static.lib
vendored
Binary file not shown.
BIN
extern/library/SpeedTree/speedtree_static.lib
vendored
BIN
extern/library/SpeedTree/speedtree_static.lib
vendored
Binary file not shown.
BIN
extern/library/SpeedTree/speedtree_staticd.lib
vendored
BIN
extern/library/SpeedTree/speedtree_staticd.lib
vendored
Binary file not shown.
BIN
extern/library/WebView/WebView2LoaderStatic.lib
vendored
BIN
extern/library/WebView/WebView2LoaderStatic.lib
vendored
Binary file not shown.
10
src/AudioLib/CMakeLists.txt
Normal file
10
src/AudioLib/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
file(GLOB_RECURSE FILE_SOURCES "*.h" "*.c" "*.cpp")
|
||||
|
||||
add_library(AudioLib STATIC ${FILE_SOURCES})
|
||||
|
||||
target_link_libraries(AudioLib
|
||||
cryptopp-static
|
||||
mio
|
||||
)
|
||||
|
||||
GroupSourcesByFolder(AudioLib)
|
||||
177
src/AudioLib/MaSoundInstance.cpp
Normal file
177
src/AudioLib/MaSoundInstance.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "stdafx.h"
|
||||
#include "MaSoundInstance.h"
|
||||
|
||||
#include <miniaudio.c>
|
||||
|
||||
bool MaSoundInstance::InitFromBuffer(ma_engine& engine, const std::vector<uint8_t>& buffer, const std::string& identity)
|
||||
{
|
||||
if (!m_Initialized)
|
||||
{
|
||||
ma_decoder_config decoderConfig = ma_decoder_config_init_default();
|
||||
ma_result result = ma_decoder_init_memory(buffer.data(), buffer.size(),
|
||||
&decoderConfig, &m_Decoder);
|
||||
if (!MD_ASSERT(result == MA_SUCCESS))
|
||||
{
|
||||
TraceError("Failed to initialize decoder memory.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ma_sound_config soundConfig = ma_sound_config_init();
|
||||
soundConfig.pDataSource = &m_Decoder;
|
||||
|
||||
result = ma_sound_init_ex(&engine, &soundConfig, &m_Sound);
|
||||
if (!MD_ASSERT(result == MA_SUCCESS))
|
||||
{
|
||||
TraceError("Failed to initialize sound.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Identity = identity;
|
||||
m_Initialized = true;
|
||||
}
|
||||
return m_Initialized;
|
||||
}
|
||||
|
||||
// Basically c&p
|
||||
bool MaSoundInstance::InitFromFile(ma_engine& engine, const std::string& filePathOnDisk)
|
||||
{
|
||||
if (!m_Initialized)
|
||||
{
|
||||
ma_decoder_config decoderConfig = ma_decoder_config_init_default();
|
||||
ma_result result = ma_decoder_init_file(filePathOnDisk.c_str(), &decoderConfig, &m_Decoder);
|
||||
if (!MD_ASSERT(result == MA_SUCCESS))
|
||||
{
|
||||
TraceError("Failed to initialize sound file decoder.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ma_sound_config soundConfig = ma_sound_config_init();
|
||||
soundConfig.pDataSource = &m_Decoder;
|
||||
|
||||
result = ma_sound_init_ex(&engine, &soundConfig, &m_Sound);
|
||||
if (!MD_ASSERT(result == MA_SUCCESS))
|
||||
{
|
||||
TraceError("Failed to initialize sound.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Identity = filePathOnDisk;
|
||||
m_Initialized = true;
|
||||
}
|
||||
return m_Initialized;
|
||||
}
|
||||
|
||||
void MaSoundInstance::Destroy()
|
||||
{
|
||||
if (m_Initialized)
|
||||
{
|
||||
ma_sound_uninit(&m_Sound);
|
||||
ma_decoder_uninit(&m_Decoder);
|
||||
}
|
||||
m_Initialized = false;
|
||||
m_Identity = "";
|
||||
m_FadeTargetVolume = 0.0f;
|
||||
m_FadeRatePerFrame = 0.0f;
|
||||
}
|
||||
|
||||
bool MaSoundInstance::IsInitialized() const
|
||||
{
|
||||
return m_Initialized;
|
||||
}
|
||||
|
||||
bool MaSoundInstance::IsPlaying() const
|
||||
{
|
||||
return ma_sound_is_playing(&m_Sound) == MA_TRUE;
|
||||
}
|
||||
|
||||
bool MaSoundInstance::Play()
|
||||
{
|
||||
return m_Initialized && ma_sound_seek_to_pcm_frame(&m_Sound, 0) == MA_SUCCESS && ma_sound_start(&m_Sound) == MA_SUCCESS;
|
||||
}
|
||||
|
||||
bool MaSoundInstance::Resume()
|
||||
{
|
||||
return m_Initialized && ma_sound_start(&m_Sound) == MA_SUCCESS;
|
||||
}
|
||||
|
||||
bool MaSoundInstance::Stop()
|
||||
{
|
||||
return m_Initialized && ma_sound_stop(&m_Sound) == MA_SUCCESS;
|
||||
}
|
||||
|
||||
void MaSoundInstance::Loop()
|
||||
{
|
||||
ma_sound_set_looping(&m_Sound, MA_TRUE);
|
||||
}
|
||||
|
||||
float MaSoundInstance::GetVolume() const
|
||||
{
|
||||
return ma_sound_get_volume(&m_Sound);
|
||||
}
|
||||
|
||||
void MaSoundInstance::SetVolume(float volume)
|
||||
{
|
||||
ma_sound_set_volume(&m_Sound, volume);
|
||||
}
|
||||
|
||||
void MaSoundInstance::SetPitch(float pitch)
|
||||
{
|
||||
ma_sound_set_pitch(&m_Sound, pitch);
|
||||
}
|
||||
|
||||
void MaSoundInstance::SetPosition(float x, float y, float z)
|
||||
{
|
||||
ma_sound_set_position(&m_Sound, x, y, z);
|
||||
}
|
||||
|
||||
const std::string& MaSoundInstance::GetIdentity() const
|
||||
{
|
||||
return m_Identity;
|
||||
}
|
||||
|
||||
void MaSoundInstance::Config3D(bool toggle, float minDist, float maxDist)
|
||||
{
|
||||
ma_sound_set_spatialization_enabled(&m_Sound, toggle);
|
||||
ma_sound_set_rolloff(&m_Sound, 1.0f);
|
||||
ma_sound_set_min_distance(&m_Sound, minDist);
|
||||
ma_sound_set_max_distance(&m_Sound, maxDist);
|
||||
ma_sound_set_attenuation_model(&m_Sound, ma_attenuation_model_linear);
|
||||
}
|
||||
|
||||
void MaSoundInstance::Fade(float toVolume, float secDurationFromMinMax)
|
||||
{
|
||||
m_FadeTargetVolume = std::clamp<float>(toVolume, 0.0f, 1.0f);
|
||||
if (m_FadeTargetVolume != GetVolume())
|
||||
{
|
||||
const float rate = 1.0f / CS_CLIENT_FPS / secDurationFromMinMax;
|
||||
m_FadeRatePerFrame = GetVolume() > m_FadeTargetVolume ? -rate : rate;
|
||||
}
|
||||
}
|
||||
|
||||
void MaSoundInstance::StopFading()
|
||||
{
|
||||
m_FadeRatePerFrame = 0.0f;
|
||||
m_FadeTargetVolume = 0.0f;
|
||||
}
|
||||
|
||||
bool MaSoundInstance::IsFading() const
|
||||
{
|
||||
return m_FadeRatePerFrame != 0.0f;
|
||||
}
|
||||
|
||||
void MaSoundInstance::Update()
|
||||
{
|
||||
if (m_FadeRatePerFrame != 0.0f)
|
||||
{
|
||||
float targetVolume = std::clamp<float>(m_FadeTargetVolume, 0.0f, 1.0f);
|
||||
float volume = std::clamp<float>(GetVolume() + m_FadeRatePerFrame, 0.0f, 1.0f);
|
||||
if ((m_FadeRatePerFrame > 0.0f && volume >= targetVolume) || (m_FadeRatePerFrame < 0.0f && volume <= targetVolume))
|
||||
{
|
||||
volume = m_FadeTargetVolume;
|
||||
m_FadeRatePerFrame = 0.0f;
|
||||
if (m_FadeTargetVolume <= 0.0f)
|
||||
ma_sound_stop(&m_Sound);
|
||||
}
|
||||
ma_sound_set_volume(&m_Sound, volume);
|
||||
}
|
||||
}
|
||||
57
src/AudioLib/MaSoundInstance.h
Normal file
57
src/AudioLib/MaSoundInstance.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
#define MA_NO_WASAPI
|
||||
#define MA_ENABLE_DSOUND
|
||||
#define MA_ENABLE_WINMM
|
||||
#include <miniaudio.h>
|
||||
|
||||
inline constexpr float CS_CLIENT_FPS = 61.0f;
|
||||
|
||||
class MaSoundInstance
|
||||
{
|
||||
public:
|
||||
bool InitFromBuffer(ma_engine& engine, const std::vector<uint8_t>& buffer, const std::string& identity);
|
||||
|
||||
bool InitFromFile(ma_engine& engine, const std::string& filePathOnDisk);
|
||||
|
||||
void Destroy();
|
||||
|
||||
bool IsInitialized() const;
|
||||
|
||||
bool IsPlaying() const;
|
||||
|
||||
bool Play();
|
||||
|
||||
bool Resume();
|
||||
|
||||
bool Stop();
|
||||
|
||||
void Loop();
|
||||
|
||||
float GetVolume() const;
|
||||
|
||||
void SetVolume(float volume);
|
||||
|
||||
void SetPitch(float pitch);
|
||||
|
||||
void SetPosition(float x, float y, float z);
|
||||
|
||||
const std::string& GetIdentity() const;
|
||||
|
||||
void Config3D(bool toggle, float minDist = 100.0f/*1m*/, float maxDist = 4000.0f/*40m*/);
|
||||
|
||||
void Fade(float toVolume, float secDurationFromMinMax);
|
||||
|
||||
void StopFading();
|
||||
|
||||
bool IsFading() const;
|
||||
|
||||
void Update();
|
||||
|
||||
private:
|
||||
std::string m_Identity;
|
||||
ma_sound m_Sound{};
|
||||
ma_decoder m_Decoder{};
|
||||
bool m_Initialized{};
|
||||
float m_FadeTargetVolume{};
|
||||
float m_FadeRatePerFrame{};
|
||||
};
|
||||
258
src/AudioLib/SoundEngine.cpp
Normal file
258
src/AudioLib/SoundEngine.cpp
Normal file
@@ -0,0 +1,258 @@
|
||||
#include "stdafx.h"
|
||||
#include "SoundEngine.h"
|
||||
|
||||
#include "EterBase/Random.h"
|
||||
#include "EterBase/Timer.h"
|
||||
#include "PackLib/PackManager.h"
|
||||
|
||||
SoundEngine::SoundEngine()
|
||||
{
|
||||
}
|
||||
|
||||
SoundEngine::~SoundEngine()
|
||||
{
|
||||
for (auto& [name, instance] : m_Sounds2D)
|
||||
instance.Destroy();
|
||||
|
||||
for (auto& instance : m_Sounds3D)
|
||||
instance.Destroy();
|
||||
|
||||
ma_engine_uninit(&m_Engine);
|
||||
m_Files.clear();
|
||||
m_Sounds2D.clear();
|
||||
}
|
||||
|
||||
bool SoundEngine::Initialize()
|
||||
{
|
||||
if (!MD_ASSERT(ma_engine_init(NULL, &m_Engine) == MA_SUCCESS))
|
||||
{
|
||||
TraceError("SoundEngine::Initialize: Failed to initialize engine.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ma_engine_listener_set_position(&m_Engine, 0, 0, 0, 0); // engine
|
||||
SetListenerPosition(0.0f, 0.0f, 0.0f); // character
|
||||
SetListenerOrientation(0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoundEngine::SetSoundVolume(float volume)
|
||||
{
|
||||
m_SoundVolume = std::clamp<float>(volume, 0.0, 1.0);
|
||||
}
|
||||
|
||||
bool SoundEngine::PlaySound2D(const std::string& name)
|
||||
{
|
||||
if (!Internal_LoadSoundFromPack(name))
|
||||
return false;
|
||||
|
||||
auto& instance = m_Sounds2D[name]; // 2d sounds are persistent, no need to destroy
|
||||
instance.InitFromBuffer(m_Engine, m_Files[name], name);
|
||||
instance.Config3D(false);
|
||||
instance.SetVolume(m_SoundVolume);
|
||||
return instance.Play();
|
||||
}
|
||||
|
||||
MaSoundInstance* SoundEngine::PlaySound3D(const std::string& name, float fx, float fy, float fz)
|
||||
{
|
||||
if (auto instance = Internal_GetInstance3D(name))
|
||||
{
|
||||
constexpr float minDist = 100.0f; // 1m
|
||||
constexpr float maxDist = 5000.0f; // 50m
|
||||
|
||||
instance->SetPosition(fx - m_CharacterPosition.x,
|
||||
fy - m_CharacterPosition.y,
|
||||
fz - m_CharacterPosition.z);
|
||||
instance->Config3D(true, minDist, maxDist);
|
||||
instance->SetVolume(m_SoundVolume);
|
||||
instance->Play();
|
||||
return instance;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MaSoundInstance* SoundEngine::PlayAmbienceSound3D(float fx, float fy, float fz, const std::string& name, int loopCount)
|
||||
{
|
||||
auto vec3 = ma_engine_listener_get_position(&m_Engine, 0);
|
||||
float dx = fx - vec3.x;
|
||||
float dy = fy - vec3.y;
|
||||
float dz = fz - vec3.z;
|
||||
float distance = sqrtf(dx * dx + dy * dy + dz * dz);
|
||||
return PlaySound3D(name, fx, fy, fz);
|
||||
}
|
||||
|
||||
void SoundEngine::StopAllSound3D()
|
||||
{
|
||||
for (auto& instance : m_Sounds3D)
|
||||
instance.Stop();
|
||||
}
|
||||
|
||||
void SoundEngine::UpdateSoundInstance(float fx, float fy, float fz, uint32_t dwcurFrame, const NSound::TSoundInstanceVector* c_pSoundInstanceVector, bool checkFrequency)
|
||||
{
|
||||
for (uint32_t i = 0; i < c_pSoundInstanceVector->size(); ++i)
|
||||
{
|
||||
const NSound::TSoundInstance& c_rSoundInstance = c_pSoundInstanceVector->at(i);
|
||||
if (c_rSoundInstance.dwFrame == dwcurFrame)
|
||||
{
|
||||
if (checkFrequency)
|
||||
{
|
||||
float& lastPlay = m_PlaySoundHistoryMap[c_rSoundInstance.strSoundFileName];
|
||||
float diff = CTimer::Instance().GetCurrentSecond() - lastPlay;
|
||||
|
||||
if (CTimer::Instance().GetCurrentSecond() - lastPlay < 0.3f)
|
||||
return;
|
||||
|
||||
lastPlay = CTimer::Instance().GetCurrentSecond();
|
||||
}
|
||||
|
||||
PlaySound3D(c_rSoundInstance.strSoundFileName, fx, fy, fz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundEngine::FadeInMusic(const std::string& path, float targetVolume /* 1.0f by default */, float fadeInDurationSecondsFromMin)
|
||||
{
|
||||
if (path.empty())
|
||||
return false;
|
||||
|
||||
auto& fadeOutMusic = m_Music[m_CurrentMusicIndex];
|
||||
if (fadeOutMusic.IsPlaying() && path == fadeOutMusic.GetIdentity())
|
||||
{
|
||||
fadeOutMusic.Fade(targetVolume, fadeInDurationSecondsFromMin);
|
||||
return fadeOutMusic.Resume();
|
||||
}
|
||||
|
||||
// We're basically just swapping
|
||||
FadeOutMusic(fadeOutMusic.GetIdentity());
|
||||
m_CurrentMusicIndex = int(!m_CurrentMusicIndex);
|
||||
|
||||
auto& music = m_Music[m_CurrentMusicIndex];
|
||||
music.Destroy();
|
||||
music.InitFromFile(m_Engine, path);
|
||||
music.Config3D(false);
|
||||
music.Loop();
|
||||
music.SetVolume(0.0f);
|
||||
music.Fade(targetVolume, fadeInDurationSecondsFromMin);
|
||||
return music.Play();
|
||||
}
|
||||
|
||||
void SoundEngine::FadeOutMusic(const std::string& name, float targetVolume, float fadeOutDurationSecondsFromMax)
|
||||
{
|
||||
for (auto& music : m_Music)
|
||||
{
|
||||
if (music.GetIdentity() == name)
|
||||
music.Fade(targetVolume, fadeOutDurationSecondsFromMax);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundEngine::FadeOutAllMusic()
|
||||
{
|
||||
for (auto& music : m_Music)
|
||||
FadeOutMusic(music.GetIdentity());
|
||||
}
|
||||
|
||||
void SoundEngine::SetMusicVolume(float volume)
|
||||
{
|
||||
m_MusicVolume = std::clamp<float>(volume, 0.0f, 1.0f);
|
||||
m_Music[m_CurrentMusicIndex].StopFading();
|
||||
m_Music[m_CurrentMusicIndex].SetVolume(m_MusicVolume);
|
||||
}
|
||||
|
||||
float SoundEngine::GetMusicVolume() const
|
||||
{
|
||||
return m_MusicVolume;
|
||||
}
|
||||
|
||||
void SoundEngine::SaveVolume(bool isMinimized)
|
||||
{
|
||||
constexpr float ratePerSecond = 1.0f / CS_CLIENT_FPS;
|
||||
// 1.0 to 0 in 1s if minimized, 3s if just out of focus
|
||||
const float durationOnFullVolume = isMinimized ? 1.0f : 3.0f;
|
||||
|
||||
float outOfFocusVolume = 0.35f;
|
||||
if (m_MasterVolume <= outOfFocusVolume)
|
||||
outOfFocusVolume = m_MasterVolume;
|
||||
|
||||
m_MasterVolumeFadeTarget = isMinimized ? 0.0f : outOfFocusVolume;
|
||||
m_MasterVolumeFadeRatePerFrame = -ratePerSecond / durationOnFullVolume;
|
||||
}
|
||||
|
||||
void SoundEngine::RestoreVolume()
|
||||
{
|
||||
constexpr float ratePerSecond = 1.0f / CS_CLIENT_FPS;
|
||||
constexpr float durationToFullVolume = 4.0f; // 0 to 1.0 in 4s
|
||||
m_MasterVolumeFadeTarget = m_MasterVolume;
|
||||
m_MasterVolumeFadeRatePerFrame = ratePerSecond / durationToFullVolume;
|
||||
}
|
||||
|
||||
void SoundEngine::SetMasterVolume(float volume)
|
||||
{
|
||||
m_MasterVolume = volume;
|
||||
ma_engine_set_volume(&m_Engine, volume);
|
||||
}
|
||||
|
||||
void SoundEngine::SetListenerPosition(float x, float y, float z)
|
||||
{
|
||||
m_CharacterPosition.x = x;
|
||||
m_CharacterPosition.y = y;
|
||||
m_CharacterPosition.z = z;
|
||||
}
|
||||
|
||||
void SoundEngine::SetListenerOrientation(float forwardX, float forwardY, float forwardZ,
|
||||
float upX, float upY, float upZ)
|
||||
{
|
||||
ma_engine_listener_set_direction(&m_Engine, 0, forwardX, forwardY, -forwardZ);
|
||||
ma_engine_listener_set_world_up(&m_Engine, 0, upX, -upY, upZ);
|
||||
}
|
||||
|
||||
void SoundEngine::Update()
|
||||
{
|
||||
for (auto& music : m_Music)
|
||||
music.Update();
|
||||
|
||||
if (m_MasterVolumeFadeRatePerFrame)
|
||||
{
|
||||
float volume = ma_engine_get_volume(&m_Engine) + m_MasterVolumeFadeRatePerFrame;
|
||||
if ((m_MasterVolumeFadeRatePerFrame > 0.0f && volume >= m_MasterVolumeFadeTarget) || (m_MasterVolumeFadeRatePerFrame < 0.0f && volume <= m_MasterVolumeFadeTarget))
|
||||
{
|
||||
volume = m_MasterVolumeFadeTarget;
|
||||
m_MasterVolumeFadeRatePerFrame = 0.0f;
|
||||
}
|
||||
ma_engine_set_volume(&m_Engine, volume);
|
||||
}
|
||||
}
|
||||
|
||||
MaSoundInstance* SoundEngine::Internal_GetInstance3D(const std::string& name)
|
||||
{
|
||||
if (Internal_LoadSoundFromPack(name))
|
||||
{
|
||||
for (auto& instance : m_Sounds3D)
|
||||
{
|
||||
if (!instance.IsPlaying())
|
||||
{
|
||||
instance.Destroy();
|
||||
instance.InitFromBuffer(m_Engine, m_Files[name], name);
|
||||
return &instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool SoundEngine::Internal_LoadSoundFromPack(const std::string& name)
|
||||
{
|
||||
if (m_Files.find(name) == m_Files.end())
|
||||
{
|
||||
TPackFile soundFile;
|
||||
if (!CPackManager::Instance().GetFile(name, soundFile))
|
||||
{
|
||||
TraceError("Internal_LoadSoundFromPack: SoundEngine: Failed to register file '%s' - not found.", name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& buffer = m_Files[name];
|
||||
buffer.resize(soundFile.size());
|
||||
memcpy(buffer.data(), soundFile.data(), soundFile.size());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
98
src/AudioLib/SoundEngine.h
Normal file
98
src/AudioLib/SoundEngine.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
#include "EterBase/Singleton.h"
|
||||
#include "Type.h"
|
||||
#include "MaSoundInstance.h"
|
||||
|
||||
//#include <miniaudio.h>
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
|
||||
struct SoundFile
|
||||
{
|
||||
std::string name;
|
||||
std::vector<std::byte> buffer; // raw file data.
|
||||
};
|
||||
|
||||
class SoundEngine : public CSingleton<SoundEngine>
|
||||
{
|
||||
public:
|
||||
enum ESoundConfig
|
||||
{
|
||||
SOUND_INSTANCE_3D_MAX_NUM = 32,
|
||||
};
|
||||
// enum ESoundType
|
||||
// {
|
||||
// SOUND_TYPE_INTERFACE, // Interface sounds. Loaded on game opening, unloaded when the game ends.
|
||||
// SOUND_TYPE_CHARACTER, // Character sounds(hit, damage, etc). Loaded on login, unloaded when the game ends.
|
||||
// SOUND_TYPE_MONSTER, // monster attacks, hits, etc. Loaded and unloaded on warp
|
||||
// SOUND_TYPE_AMBIENCE, // Wind, rain, birds, etc. Loaded and unloaded on warp
|
||||
// SOUND_TYPE_MUSIC, // Bg music played on demand
|
||||
// SOUND_TYPE_MAX_NUM,
|
||||
// };
|
||||
public:
|
||||
SoundEngine();
|
||||
|
||||
~SoundEngine();
|
||||
|
||||
bool Initialize();
|
||||
|
||||
void SetSoundVolume(float volume);
|
||||
|
||||
bool PlaySound2D(const std::string& name);
|
||||
|
||||
MaSoundInstance* PlaySound3D(const std::string& name, float fx, float fy, float fz);
|
||||
|
||||
MaSoundInstance* PlayAmbienceSound3D(float fx, float fy, float fz, const std::string& name, int loopCount = 1);
|
||||
|
||||
void StopAllSound3D();
|
||||
|
||||
void UpdateSoundInstance(float fx, float fy, float fz, uint32_t dwcurFrame, const NSound::TSoundInstanceVector* c_pSoundInstanceVector, bool checkFrequency = false);
|
||||
|
||||
bool FadeInMusic(const std::string& path, float targetVolume = 1.0f, float fadeInDurationSecondsFromMin = 1.5f);
|
||||
|
||||
void FadeOutMusic(const std::string& name, float targetVolume = 0.0f, float fadeOutDurationSecondsFromMax = 1.5f);
|
||||
|
||||
void FadeOutAllMusic();
|
||||
|
||||
void SetMusicVolume(float volume);
|
||||
|
||||
float GetMusicVolume() const;
|
||||
|
||||
void SaveVolume(bool isMinimized);
|
||||
|
||||
void RestoreVolume();
|
||||
|
||||
void SetMasterVolume(float volume);
|
||||
|
||||
void SetListenerPosition(float x, float y, float z);
|
||||
|
||||
void SetListenerOrientation(float forwardX, float forwardY, float forwardZ,
|
||||
float upX, float upY, float upZ);
|
||||
|
||||
void Update();
|
||||
|
||||
|
||||
private:
|
||||
MaSoundInstance* Internal_GetInstance3D(const std::string& name);
|
||||
|
||||
bool Internal_LoadSoundFromPack(const std::string& name);
|
||||
|
||||
private:
|
||||
struct { float x, y, z; } m_CharacterPosition{};
|
||||
|
||||
ma_engine m_Engine{};
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> m_Files;
|
||||
std::unordered_map<std::string, MaSoundInstance> m_Sounds2D;
|
||||
std::array<MaSoundInstance, SOUND_INSTANCE_3D_MAX_NUM> m_Sounds3D;
|
||||
std::unordered_map<std::string, float> m_PlaySoundHistoryMap;
|
||||
|
||||
// One song at a time, but holding both current and previous for graceful fading
|
||||
std::array<MaSoundInstance, 2> m_Music;
|
||||
int m_CurrentMusicIndex{};
|
||||
float m_MusicVolume{ 1.0f };
|
||||
float m_SoundVolume{ 1.0f };
|
||||
|
||||
float m_MasterVolume{ 1.0f };
|
||||
float m_MasterVolumeFadeTarget{};
|
||||
float m_MasterVolumeFadeRatePerFrame{};
|
||||
};
|
||||
9
src/AudioLib/Stdafx.h
Normal file
9
src/AudioLib/Stdafx.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
//#include <windows.h>
|
||||
|
||||
#include "EterBase/CRC32.h"
|
||||
#include "EterBase/Utils.h"
|
||||
#include "EterBase/Debug.h"
|
||||
|
||||
#include <algorithm>
|
||||
141
src/AudioLib/Type.cpp
Normal file
141
src/AudioLib/Type.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "StdAfx.h"
|
||||
#include "Type.h"
|
||||
#include "EterLib/TextFileLoader.h"
|
||||
#include <utf8.h>
|
||||
|
||||
std::string NSound::strResult;
|
||||
|
||||
const char* NSound::GetResultString()
|
||||
{
|
||||
return strResult.c_str();
|
||||
}
|
||||
|
||||
void NSound::SetResultString(const char* c_pszStr)
|
||||
{
|
||||
strResult.assign(c_pszStr);
|
||||
}
|
||||
|
||||
bool NSound::LoadSoundInformationPiece(const char* c_szFileName, NSound::TSoundDataVector& rSoundDataVector, const char* c_szPathHeader)
|
||||
{
|
||||
std::string strResult;
|
||||
strResult = c_szFileName;
|
||||
|
||||
CTextFileLoader* pkTextFileLoader = CTextFileLoader::Cache(c_szFileName);
|
||||
if (!pkTextFileLoader)
|
||||
return false;
|
||||
|
||||
CTextFileLoader& rkTextFileLoader = *pkTextFileLoader;
|
||||
if (rkTextFileLoader.IsEmpty())
|
||||
{
|
||||
SetResultString((strResult + " Can not open file for reading").c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
rkTextFileLoader.SetTop();
|
||||
|
||||
int iCount;
|
||||
if (!rkTextFileLoader.GetTokenInteger("sounddatacount", &iCount))
|
||||
{
|
||||
SetResultString((strResult + " File format error, SoundDataCount Unable to find.").c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
rSoundDataVector.clear();
|
||||
rSoundDataVector.resize(iCount);
|
||||
|
||||
char szSoundDataHeader[32 + 1];
|
||||
for (uint32_t i = 0; i < rSoundDataVector.size(); ++i)
|
||||
{
|
||||
_snprintf_s(szSoundDataHeader, sizeof(szSoundDataHeader), "sounddata%02d", i);
|
||||
CTokenVector* pTokenVector;
|
||||
if (!rkTextFileLoader.GetTokenVector(szSoundDataHeader, &pTokenVector))
|
||||
{
|
||||
SetResultString((strResult + " File format error: " + szSoundDataHeader + " Unable to find").c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (2 != pTokenVector->size())
|
||||
{
|
||||
SetResultString((strResult + " File format error: The size of the vector is not 2").c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
rSoundDataVector[i].fTime = (float)atof(pTokenVector->at(0).c_str());
|
||||
if (c_szPathHeader)
|
||||
{
|
||||
rSoundDataVector[i].strSoundFileName = c_szPathHeader;
|
||||
rSoundDataVector[i].strSoundFileName += pTokenVector->at(1).c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
rSoundDataVector[i].strSoundFileName = pTokenVector->at(1).c_str();
|
||||
}
|
||||
|
||||
//TraceError("LoadSoundInformation %s -- %f -- %s", c_szFileName, rSoundDataVector[i].fTime, rSoundDataVector[i].strSoundFileName.c_str());
|
||||
}
|
||||
|
||||
SetResultString((strResult + " Loaded").c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NSound::SaveSoundInformationPiece(const char* c_szFileName, NSound::TSoundDataVector& rSoundDataVector)
|
||||
{
|
||||
if (rSoundDataVector.empty()) // If no data is considered success
|
||||
{
|
||||
if (IsFile(c_szFileName)) // If the data is empty but there is a file
|
||||
{
|
||||
_unlink(c_szFileName); // erase.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string strResult;
|
||||
strResult = c_szFileName;
|
||||
|
||||
// UTF-8 → UTF-16 conversion for Unicode path support
|
||||
std::wstring wFileName = Utf8ToWide(c_szFileName);
|
||||
FILE* File = _wfopen(wFileName.c_str(), L"wt");
|
||||
|
||||
if (!File)
|
||||
{
|
||||
char szErrorText[256 + 1];
|
||||
_snprintf_s(szErrorText, sizeof(szErrorText), "Failed to save file (%s).\nPlease check if it is read-only or you have no space on the disk.\n", c_szFileName);
|
||||
LogBox(szErrorText, "Error");
|
||||
SetResultString((strResult + " Cannot open file for writing").c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(File, "ScriptType CharacterSoundInformation\n");
|
||||
fprintf(File, "\n");
|
||||
|
||||
fprintf(File, "SoundDataCount %llu\n", rSoundDataVector.size());
|
||||
|
||||
for (uint32_t i = 0; i < rSoundDataVector.size(); ++i)
|
||||
{
|
||||
NSound::TSoundData& rSoundData = rSoundDataVector[i];
|
||||
fprintf(File, "SoundData%02d %f \"%s\"\n", i, rSoundData.fTime, rSoundData.strSoundFileName.c_str());
|
||||
}
|
||||
|
||||
fclose(File);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NSound::DataToInstance(const TSoundDataVector& c_rSoundDataVector, TSoundInstanceVector* pSoundInstanceVector)
|
||||
{
|
||||
if (c_rSoundDataVector.empty())
|
||||
return;
|
||||
|
||||
uint32_t dwFPS = 60;
|
||||
const float c_fFrameTime = 1.0f / float(dwFPS);
|
||||
|
||||
pSoundInstanceVector->clear();
|
||||
pSoundInstanceVector->resize(c_rSoundDataVector.size());
|
||||
for (uint32_t i = 0; i < c_rSoundDataVector.size(); ++i)
|
||||
{
|
||||
const TSoundData& c_rSoundData = c_rSoundDataVector[i];
|
||||
TSoundInstance& rSoundInstance = pSoundInstanceVector->at(i);
|
||||
|
||||
rSoundInstance.dwFrame = (uint32_t)(c_rSoundData.fTime / c_fFrameTime);
|
||||
rSoundInstance.strSoundFileName = c_rSoundData.strSoundFileName;
|
||||
}
|
||||
}
|
||||
29
src/AudioLib/Type.h
Normal file
29
src/AudioLib/Type.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace NSound
|
||||
{
|
||||
extern std::string strResult;
|
||||
|
||||
typedef struct SSoundData
|
||||
{
|
||||
float fTime;
|
||||
std::string strSoundFileName;
|
||||
} TSoundData;
|
||||
typedef struct SSoundInstance
|
||||
{
|
||||
uint32_t dwFrame;
|
||||
std::string strSoundFileName;
|
||||
} TSoundInstance;
|
||||
typedef std::vector<TSoundData> TSoundDataVector;
|
||||
typedef std::vector<TSoundInstance> TSoundInstanceVector;
|
||||
|
||||
bool LoadSoundInformationPiece(const char* c_szFileName, TSoundDataVector& rSoundDataVector, const char* c_szPathHeader = NULL);
|
||||
bool SaveSoundInformationPiece(const char* c_szFileName, TSoundDataVector& rSoundDataVector);
|
||||
void DataToInstance(const TSoundDataVector& c_rSoundDataVector, TSoundInstanceVector* pSoundInstanceVector);
|
||||
|
||||
const char* GetResultString();
|
||||
void SetResultString(const char* c_pszStr);
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
add_subdirectory(AudioLib)
|
||||
add_subdirectory(Discord)
|
||||
add_subdirectory(EffectLib)
|
||||
add_subdirectory(EterBase)
|
||||
@@ -5,12 +6,12 @@ add_subdirectory(EterGrnLib)
|
||||
add_subdirectory(EterImageLib)
|
||||
add_subdirectory(EterLib)
|
||||
add_subdirectory(EterLocale)
|
||||
add_subdirectory(EterPack)
|
||||
add_subdirectory(EterPythonLib)
|
||||
add_subdirectory(GameLib)
|
||||
add_subdirectory(MilesLib)
|
||||
add_subdirectory(PRTerrainLib)
|
||||
add_subdirectory(ScriptLib)
|
||||
add_subdirectory(SpeedTreeLib)
|
||||
add_subdirectory(SphereLib)
|
||||
add_subdirectory(UserInterface)
|
||||
add_subdirectory(PackMaker)
|
||||
add_subdirectory(PackLib)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "connection.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMCX
|
||||
#define NOSERVICE
|
||||
#define NOIME
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "discord_rpc.h"
|
||||
#include "discord_register.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMCX
|
||||
#define NOSERVICE
|
||||
#define NOIME
|
||||
@@ -134,27 +133,36 @@ static void Discord_RegisterW(const wchar_t* applicationId, const wchar_t* comma
|
||||
extern "C" DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command)
|
||||
{
|
||||
wchar_t appId[32];
|
||||
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
|
||||
int app = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, applicationId, -1, appId, 32);
|
||||
if (app <= 0)
|
||||
return;
|
||||
|
||||
wchar_t openCommand[1024];
|
||||
const wchar_t* wcommand = nullptr;
|
||||
if (command && command[0]) {
|
||||
const auto commandBufferLen = sizeof(openCommand) / sizeof(*openCommand);
|
||||
MultiByteToWideChar(CP_UTF8, 0, command, -1, openCommand, commandBufferLen);
|
||||
|
||||
int ok = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, command, -1, openCommand, commandBufferLen);
|
||||
if (ok <= 0)
|
||||
return;
|
||||
|
||||
wcommand = openCommand;
|
||||
}
|
||||
|
||||
Discord_RegisterW(appId, wcommand);
|
||||
}
|
||||
|
||||
extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId,
|
||||
const char* steamId)
|
||||
extern "C" DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId)
|
||||
{
|
||||
wchar_t appId[32];
|
||||
MultiByteToWideChar(CP_UTF8, 0, applicationId, -1, appId, 32);
|
||||
int app = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, applicationId, -1, appId, 32);
|
||||
if (app <= 0)
|
||||
return;
|
||||
|
||||
wchar_t wSteamId[32];
|
||||
MultiByteToWideChar(CP_UTF8, 0, steamId, -1, wSteamId, 32);
|
||||
int steam = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, steamId, -1, wSteamId, 32);
|
||||
if (steam <= 0)
|
||||
return;
|
||||
|
||||
HKEY key;
|
||||
auto status = RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Valve\\Steam", 0, KEY_READ, &key);
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
add_library(EffectLib STATIC ${FILE_SOURCES})
|
||||
|
||||
target_link_libraries(EffectLib
|
||||
lzo2
|
||||
cryptopp-static
|
||||
mio
|
||||
)
|
||||
|
||||
GroupSourcesByFolder(EffectLib)
|
||||
|
||||
@@ -96,7 +96,7 @@ bool CEffectData::LoadScript(const char * c_szFileName)
|
||||
|
||||
bool CEffectData::LoadSoundScriptData(const char * c_szFileName)
|
||||
{
|
||||
TSoundDataVector SoundDataVector;
|
||||
NSound::TSoundDataVector SoundDataVector;
|
||||
|
||||
if (LoadSoundInformationPiece(c_szFileName, SoundDataVector))
|
||||
{
|
||||
@@ -164,7 +164,7 @@ CEffectMeshScript * CEffectData::GetMeshPointer(DWORD dwPosition)
|
||||
return m_MeshVector[dwPosition];
|
||||
}
|
||||
|
||||
TSoundInstanceVector * CEffectData::GetSoundInstanceVector()
|
||||
NSound::TSoundInstanceVector * CEffectData::GetSoundInstanceVector()
|
||||
{
|
||||
return &m_SoundInstanceVector;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../milesLib/Type.h"
|
||||
#include "AudioLib/Type.h"
|
||||
|
||||
#include "ParticleSystemData.h"
|
||||
#include "EffectMesh.h"
|
||||
@@ -30,7 +30,7 @@ class CEffectData
|
||||
DWORD GetLightCount();
|
||||
CLightData * GetLightPointer(DWORD dwPosition);
|
||||
|
||||
TSoundInstanceVector * GetSoundInstanceVector();
|
||||
NSound::TSoundInstanceVector * GetSoundInstanceVector();
|
||||
|
||||
float GetBoundingSphereRadius();
|
||||
D3DXVECTOR3 GetBoundingSpherePosition();
|
||||
@@ -52,7 +52,7 @@ class CEffectData
|
||||
TParticleVector m_ParticleVector;
|
||||
TMeshVector m_MeshVector;
|
||||
TLightVector m_LightVector;
|
||||
TSoundInstanceVector m_SoundInstanceVector;
|
||||
NSound::TSoundInstanceVector m_SoundInstanceVector;
|
||||
|
||||
float m_fBoundingSphereRadius;
|
||||
D3DXVECTOR3 m_v3BoundingSpherePosition;
|
||||
|
||||
@@ -90,6 +90,14 @@ void CEffectElementBaseInstance::Destroy()
|
||||
}
|
||||
|
||||
CEffectElementBaseInstance::CEffectElementBaseInstance()
|
||||
: mc_pmatLocal(nullptr)
|
||||
, m_isActive(false)
|
||||
, m_fLocalTime(0.0f)
|
||||
, m_dwStartTime(0)
|
||||
, m_fElapsedTime(0.0f)
|
||||
, m_fRemainingTime(0.0f)
|
||||
, m_bStart(false)
|
||||
, m_pBase(nullptr)
|
||||
{
|
||||
}
|
||||
CEffectElementBaseInstance::~CEffectElementBaseInstance()
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
#include "ParticleSystemInstance.h"
|
||||
#include "SimpleLightInstance.h"
|
||||
|
||||
#include "../eterBase/Stl.h"
|
||||
#include "../eterLib/StateManager.h"
|
||||
#include "../MilesLib/SoundManager.h"
|
||||
#include "EterBase/Stl.h"
|
||||
#include "EterLib/StateManager.h"
|
||||
#include "AudioLib/SoundEngine.h"
|
||||
|
||||
CDynamicPool<CEffectInstance> CEffectInstance::ms_kPool;
|
||||
int CEffectInstance::ms_iRenderingEffectCount = 0;
|
||||
@@ -50,12 +50,12 @@ void CEffectInstance::UpdateSound()
|
||||
{
|
||||
if (m_pSoundInstanceVector)
|
||||
{
|
||||
UpdateSoundInstance(m_dwFrame,
|
||||
*m_pSoundInstanceVector,
|
||||
m_matGlobal._41,
|
||||
m_matGlobal._42,
|
||||
m_matGlobal._43,
|
||||
false);
|
||||
SoundEngine::Instance().UpdateSoundInstance(m_matGlobal._41,
|
||||
m_matGlobal._42,
|
||||
m_matGlobal._43,
|
||||
m_dwFrame,
|
||||
m_pSoundInstanceVector,
|
||||
false);
|
||||
// NOTE : 매트릭스에서 위치를 직접 얻어온다 - [levites]
|
||||
}
|
||||
++m_dwFrame;
|
||||
@@ -80,11 +80,7 @@ void CEffectInstance::OnUpdate()
|
||||
{
|
||||
Transform();
|
||||
|
||||
#ifdef WORLD_EDITOR
|
||||
FEffectUpdator f(CTimer::Instance().GetElapsedSecond());
|
||||
#else
|
||||
FEffectUpdator f(CTimer::Instance().GetCurrentSecond()-m_fLastTime);
|
||||
#endif
|
||||
f = std::for_each(m_ParticleInstanceVector.begin(), m_ParticleInstanceVector.end(),f);
|
||||
f = std::for_each(m_MeshInstanceVector.begin(), m_MeshInstanceVector.end(),f);
|
||||
f = std::for_each(m_LightInstanceVector.begin(), m_LightInstanceVector.end(),f);
|
||||
@@ -95,7 +91,7 @@ void CEffectInstance::OnUpdate()
|
||||
|
||||
void CEffectInstance::OnRender()
|
||||
{
|
||||
STATEMANAGER.SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
|
||||
STATEMANAGER.SetFVF(D3DFVF_XYZ | D3DFVF_TEX1);
|
||||
|
||||
STATEMANAGER.SaveSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_NONE);
|
||||
STATEMANAGER.SaveSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
|
||||
@@ -106,30 +102,19 @@ void CEffectInstance::OnRender()
|
||||
STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, FALSE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_ZWRITEENABLE, FALSE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_LIGHTING, FALSE);
|
||||
/////
|
||||
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
|
||||
std::for_each(m_ParticleInstanceVector.begin(),m_ParticleInstanceVector.end(),std::mem_fn(&CEffectElementBaseInstance::Render));
|
||||
|
||||
STATEMANAGER.SetFVF(D3DFVF_XYZ | D3DFVF_TEX1);
|
||||
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
|
||||
std::for_each(m_ParticleInstanceVector.begin(),m_ParticleInstanceVector.end(),std::mem_fn(&CEffectElementBaseInstance::Render));
|
||||
std::for_each(m_MeshInstanceVector.begin(),m_MeshInstanceVector.end(),std::mem_fn(&CEffectElementBaseInstance::Render));
|
||||
|
||||
/////
|
||||
STATEMANAGER.RestoreSamplerState(0, D3DSAMP_MINFILTER);
|
||||
STATEMANAGER.RestoreSamplerState(0, D3DSAMP_MAGFILTER);
|
||||
|
||||
@@ -139,7 +124,6 @@ void CEffectInstance::OnRender()
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_ZWRITEENABLE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_LIGHTING);
|
||||
|
||||
++ms_iRenderingEffectCount;
|
||||
}
|
||||
@@ -283,18 +267,10 @@ void CEffectInstance::Clear()
|
||||
__Initialize();
|
||||
}
|
||||
|
||||
void CEffectInstance::BatchParticles()
|
||||
{
|
||||
std::for_each(m_ParticleInstanceVector.begin(), m_ParticleInstanceVector.end(), std::mem_fn(&CParticleSystemInstance::BatchParticles));
|
||||
}
|
||||
|
||||
void CEffectInstance::RenderMeshes()
|
||||
{
|
||||
std::for_each(m_MeshInstanceVector.begin(), m_MeshInstanceVector.end(), std::mem_fn(&CEffectElementBaseInstance::Render));
|
||||
}
|
||||
|
||||
void CEffectInstance::__Initialize()
|
||||
{
|
||||
ReleaseAlwaysHidden();
|
||||
|
||||
m_isAlive = FALSE;
|
||||
m_dwFrame = 0;
|
||||
m_pSoundInstanceVector = NULL;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
#include "UserInterface/Locale_inc.h"
|
||||
|
||||
#include "Eterlib/GrpObjectInstance.h"
|
||||
#include "Eterlib/Pool.h"
|
||||
#include "Mileslib/Type.h"
|
||||
#include "AudioLib/Type.h"
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "EffectElementBaseInstance.h"
|
||||
#include "EffectData.h"
|
||||
#include "EffectMeshInstance.h"
|
||||
@@ -41,6 +42,12 @@ class CEffectInstance : public CGraphicObjectInstance
|
||||
bool LessRenderOrder(CEffectInstance* pkEftInst);
|
||||
|
||||
void SetEffectDataPointer(CEffectData * pEffectData);
|
||||
|
||||
// Returns the pointer to the effect data associated with this instance.
|
||||
CEffectData* GetEffectDataPointer() const
|
||||
{
|
||||
return m_pkEftData;
|
||||
}
|
||||
|
||||
void Clear();
|
||||
BOOL isAlive();
|
||||
@@ -55,9 +62,6 @@ class CEffectInstance : public CGraphicObjectInstance
|
||||
void OnRenderShadow() {} // Not used
|
||||
void OnRenderPCBlocker() {} // Not used
|
||||
|
||||
void BatchParticles();
|
||||
void RenderMeshes();
|
||||
|
||||
protected:
|
||||
void __Initialize();
|
||||
|
||||
@@ -80,7 +84,7 @@ class CEffectInstance : public CGraphicObjectInstance
|
||||
std::vector<CEffectMeshInstance*> m_MeshInstanceVector;
|
||||
std::vector<CLightInstance*> m_LightInstanceVector;
|
||||
|
||||
TSoundInstanceVector * m_pSoundInstanceVector;
|
||||
NSound::TSoundInstanceVector * m_pSoundInstanceVector;
|
||||
|
||||
float m_fBoundingSphereRadius;
|
||||
D3DXVECTOR3 m_v3BoundingSpherePosition;
|
||||
@@ -88,6 +92,7 @@ class CEffectInstance : public CGraphicObjectInstance
|
||||
float m_fLastTime;
|
||||
|
||||
public:
|
||||
|
||||
static CDynamicPool<CEffectInstance> ms_kPool;
|
||||
static int ms_iRenderingEffectCount;
|
||||
};
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
#include "StdAfx.h"
|
||||
#include "../eterBase/Random.h"
|
||||
#include "../eterlib/StateManager.h"
|
||||
#include "EterBase/Random.h"
|
||||
#include "Eterlib/StateManager.h"
|
||||
#include "EffectManager.h"
|
||||
|
||||
extern std::unordered_map<LPDIRECT3DTEXTURE9, std::unordered_map<uint32_t, std::vector<TPDTVertex>>> g_particleVertexBatch;
|
||||
|
||||
void CEffectManager::GetInfo(std::string* pstInfo)
|
||||
{
|
||||
char szInfo[256];
|
||||
|
||||
sprintf(szInfo, "Effect: Inst - ED %d, EI %d Pool - PSI %d, MI %d, LI %d, PI %d, EI %d, ED %d, PSD %d, EM %d, LD %d",
|
||||
sprintf(szInfo, "Effect: Inst - ED %zd, EI %zd Pool - PSI %zd, MI %zd, LI %zd, PI %zd, EI %zd, ED %zd, PSD %zd, EM %zd, LD %zd",
|
||||
m_kEftDataMap.size(),
|
||||
m_kEftInstMap.size(),
|
||||
CParticleSystemInstance::ms_kPool.GetCapacity(),
|
||||
@@ -82,15 +80,50 @@ void CEffectManager::Update()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct CEffectManager_LessEffectInstancePtrRenderOrder
|
||||
{
|
||||
bool operator() (CEffectInstance* pkLeft, CEffectInstance* pkRight)
|
||||
{
|
||||
return pkLeft->LessRenderOrder(pkRight);
|
||||
}
|
||||
};
|
||||
|
||||
struct CEffectManager_FEffectInstanceRender
|
||||
{
|
||||
inline void operator () (CEffectInstance * pkEftInst)
|
||||
{
|
||||
pkEftInst->Render();
|
||||
}
|
||||
};
|
||||
|
||||
void CEffectManager::Render()
|
||||
{
|
||||
STATEMANAGER.SetTexture(0, NULL);
|
||||
STATEMANAGER.SetTexture(1, NULL);
|
||||
|
||||
if (m_isDisableSortRendering)
|
||||
{
|
||||
for (TEffectInstanceMap::iterator itor = m_kEftInstMap.begin(); itor != m_kEftInstMap.end();)
|
||||
{
|
||||
CEffectInstance * pEffectInstance = itor->second;
|
||||
pEffectInstance->Render();
|
||||
++itor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static std::vector<CEffectInstance*> s_kVct_pkEftInstSort;
|
||||
s_kVct_pkEftInstSort.clear();
|
||||
|
||||
g_particleVertexBatch.clear();
|
||||
TEffectInstanceMap& rkMap_pkEftInstSrc=m_kEftInstMap;
|
||||
TEffectInstanceMap::iterator i;
|
||||
for (i=rkMap_pkEftInstSrc.begin(); i!=rkMap_pkEftInstSrc.end(); ++i)
|
||||
s_kVct_pkEftInstSort.push_back(i->second);
|
||||
|
||||
__RenderParticles();
|
||||
__RenderMeshes();
|
||||
std::sort(s_kVct_pkEftInstSort.begin(), s_kVct_pkEftInstSort.end(), CEffectManager_LessEffectInstancePtrRenderOrder());
|
||||
std::for_each(s_kVct_pkEftInstSort.begin(), s_kVct_pkEftInstSort.end(), CEffectManager_FEffectInstanceRender());
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CEffectManager::RegisterEffect(const char * c_szFileName,bool isExistDelete,bool isNeedCache)
|
||||
@@ -318,6 +351,33 @@ void CEffectManager::HideEffect()
|
||||
m_pSelectedEffectInstance->Hide();
|
||||
}
|
||||
|
||||
// MR-5: Fix effect rendering when actor is semi-transparent
|
||||
// Credits to d1str4ught
|
||||
void CEffectManager::RenderEffect()
|
||||
{
|
||||
if (!m_pSelectedEffectInstance)
|
||||
return;
|
||||
|
||||
m_pSelectedEffectInstance->Render();
|
||||
}
|
||||
// MR-5: -- END OF -- Fix effect rendering when actor is semi-transparent
|
||||
|
||||
void CEffectManager::ApplyAlwaysHidden()
|
||||
{
|
||||
if (!m_pSelectedEffectInstance)
|
||||
return;
|
||||
|
||||
m_pSelectedEffectInstance->ApplyAlwaysHidden();
|
||||
}
|
||||
|
||||
void CEffectManager::ReleaseAlwaysHidden()
|
||||
{
|
||||
if (!m_pSelectedEffectInstance)
|
||||
return;
|
||||
|
||||
m_pSelectedEffectInstance->ReleaseAlwaysHidden();
|
||||
}
|
||||
|
||||
bool CEffectManager::GetEffectData(DWORD dwID, CEffectData ** ppEffect)
|
||||
{
|
||||
TEffectDataMap::iterator itor = m_kEftDataMap.find(dwID);
|
||||
@@ -404,122 +464,6 @@ void CEffectManager::__DestroyEffectDataMap()
|
||||
m_kEftDataMap.clear();
|
||||
}
|
||||
|
||||
void CEffectManager::__RenderParticles()
|
||||
{
|
||||
STATEMANAGER.SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
|
||||
|
||||
STATEMANAGER.SaveSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_NONE);
|
||||
STATEMANAGER.SaveSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
|
||||
|
||||
STATEMANAGER.SaveRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, FALSE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_ZWRITEENABLE, FALSE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_LIGHTING, FALSE);
|
||||
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
|
||||
for (auto itor = m_kEftInstMap.begin(); itor != m_kEftInstMap.end(); ++itor) {
|
||||
itor->second->BatchParticles();
|
||||
}
|
||||
|
||||
for (auto& [pTexture, keyMap] : g_particleVertexBatch) {
|
||||
if (keyMap.empty())
|
||||
continue;
|
||||
|
||||
STATEMANAGER.SetTexture(0, pTexture);
|
||||
for (auto& [opKey, vtxBatch] : keyMap) {
|
||||
if (vtxBatch.empty())
|
||||
continue;
|
||||
|
||||
uint8_t* pKeyPart = (uint8_t*)&opKey;
|
||||
STATEMANAGER.SetRenderState(D3DRS_SRCBLEND, pKeyPart[0]);
|
||||
STATEMANAGER.SetRenderState(D3DRS_DESTBLEND, pKeyPart[1]);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, pKeyPart[2]);
|
||||
|
||||
STATEMANAGER.DrawPrimitiveUP(
|
||||
D3DPT_TRIANGLELIST,
|
||||
vtxBatch.size() / 3,
|
||||
vtxBatch.data(),
|
||||
sizeof(TPDTVertex));
|
||||
}
|
||||
}
|
||||
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLORARG1);
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLORARG2);
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLOROP);
|
||||
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ALPHAARG1);
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ALPHAARG2);
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ALPHAOP);
|
||||
|
||||
STATEMANAGER.RestoreSamplerState(0, D3DSAMP_MINFILTER);
|
||||
STATEMANAGER.RestoreSamplerState(0, D3DSAMP_MAGFILTER);
|
||||
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_ALPHABLENDENABLE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_SRCBLEND);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_DESTBLEND);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_ZWRITEENABLE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_LIGHTING);
|
||||
}
|
||||
|
||||
void CEffectManager::__RenderMeshes()
|
||||
{
|
||||
STATEMANAGER.SetFVF(D3DFVF_XYZ | D3DFVF_TEX1);
|
||||
|
||||
STATEMANAGER.SaveSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_NONE);
|
||||
STATEMANAGER.SaveSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
|
||||
|
||||
STATEMANAGER.SaveRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, FALSE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_ZWRITEENABLE, FALSE);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_LIGHTING, FALSE);
|
||||
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR);
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
|
||||
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TFACTOR);
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
|
||||
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
|
||||
|
||||
for (auto itor = m_kEftInstMap.begin(); itor != m_kEftInstMap.end(); ++itor) {
|
||||
itor->second->RenderMeshes();
|
||||
}
|
||||
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLORARG1);
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLORARG2);
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLOROP);
|
||||
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ALPHAARG1);
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ALPHAARG2);
|
||||
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ALPHAOP);
|
||||
|
||||
STATEMANAGER.RestoreSamplerState(0, D3DSAMP_MINFILTER);
|
||||
STATEMANAGER.RestoreSamplerState(0, D3DSAMP_MAGFILTER);
|
||||
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_ALPHABLENDENABLE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_SRCBLEND);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_DESTBLEND);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_ZWRITEENABLE);
|
||||
STATEMANAGER.RestoreRenderState(D3DRS_LIGHTING);
|
||||
}
|
||||
|
||||
void CEffectManager::Destroy()
|
||||
{
|
||||
__DestroyEffectInstanceMap();
|
||||
@@ -545,5 +489,26 @@ CEffectManager::~CEffectManager()
|
||||
Destroy();
|
||||
}
|
||||
|
||||
DWORD CEffectManager::GetSelectedEffectDataCRC() const
|
||||
{
|
||||
if (!m_pSelectedEffectInstance)
|
||||
return 0;
|
||||
|
||||
CEffectData* pData = m_pSelectedEffectInstance->GetEffectDataPointer();
|
||||
|
||||
if (!pData)
|
||||
return 0;
|
||||
|
||||
const char* cszFile = pData->GetFileName();
|
||||
|
||||
if (!cszFile || !cszFile[0])
|
||||
return 0;
|
||||
|
||||
std::string str;
|
||||
StringPath(cszFile, str);
|
||||
|
||||
return GetCaseCRC32(str.c_str(), (int)str.length());
|
||||
}
|
||||
|
||||
// just for map effect
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "StdAfx.h"
|
||||
#include "EffectInstance.h"
|
||||
|
||||
class CEffectManager : public CScreen, public CSingleton<CEffectManager>
|
||||
@@ -55,6 +56,13 @@ class CEffectManager : public CScreen, public CSingleton<CEffectManager>
|
||||
|
||||
void ShowEffect();
|
||||
void HideEffect();
|
||||
// MR-5: Fix effect rendering when actor is semi-transparent
|
||||
// Credits to d1str4ught
|
||||
void RenderEffect();
|
||||
// MR-5: -- END OF -- Fix effect rendering when actor is semi-transparent
|
||||
|
||||
void ApplyAlwaysHidden();
|
||||
void ReleaseAlwaysHidden();
|
||||
|
||||
// Temporary function
|
||||
DWORD GetRandomEffect();
|
||||
@@ -69,6 +77,9 @@ class CEffectManager : public CScreen, public CSingleton<CEffectManager>
|
||||
|
||||
int GetRenderingEffectCount();
|
||||
|
||||
// Return the CRC of the effect-data for the selected effect instance.
|
||||
DWORD GetSelectedEffectDataCRC() const;
|
||||
|
||||
protected:
|
||||
void __Initialize();
|
||||
|
||||
@@ -76,9 +87,6 @@ class CEffectManager : public CScreen, public CSingleton<CEffectManager>
|
||||
void __DestroyEffectCacheMap();
|
||||
void __DestroyEffectDataMap();
|
||||
|
||||
void __RenderParticles();
|
||||
void __RenderMeshes();
|
||||
|
||||
protected:
|
||||
bool m_isDisableSortRendering;
|
||||
TEffectDataMap m_kEftDataMap;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "StdAfx.h"
|
||||
#include "../eterlib/StateManager.h"
|
||||
#include "../eterlib/ResourceManager.h"
|
||||
#include "../eterpack/EterPackManager.h"
|
||||
#include "Eterlib/StateManager.h"
|
||||
#include "Eterlib/ResourceManager.h"
|
||||
#include "PackLib/PackManager.h"
|
||||
#include "EffectMesh.h"
|
||||
|
||||
CDynamicPool<CEffectMesh::SEffectMeshData> CEffectMesh::SEffectMeshData::ms_kPool;
|
||||
@@ -192,15 +192,14 @@ BOOL CEffectMesh::__LoadData_Ver002(int iSize, const BYTE * c_pbBuf)
|
||||
|
||||
if (0 == strExtension.compare("ifl"))
|
||||
{
|
||||
LPCVOID pMotionData;
|
||||
CMappedFile File;
|
||||
TPackFile File;
|
||||
|
||||
if (CEterPackManager::Instance().Get(File, pMeshData->szDiffuseMapFileName, &pMotionData))
|
||||
if (CPackManager::Instance().GetFile(pMeshData->szDiffuseMapFileName, File))
|
||||
{
|
||||
CMemoryTextFileLoader textFileLoader;
|
||||
std::vector<std::string> stTokenVector;
|
||||
|
||||
textFileLoader.Bind(File.Size(), pMotionData);
|
||||
textFileLoader.Bind(File.size(), File.data());
|
||||
|
||||
std::string strPathName;
|
||||
GetOnlyPathName(pMeshData->szDiffuseMapFileName, strPathName);
|
||||
@@ -337,15 +336,14 @@ BOOL CEffectMesh::__LoadData_Ver001(int iSize, const BYTE * c_pbBuf)
|
||||
|
||||
if (0 == strExtension.compare("ifl"))
|
||||
{
|
||||
LPCVOID pMotionData;
|
||||
CMappedFile File;
|
||||
TPackFile File;
|
||||
|
||||
if (CEterPackManager::Instance().Get(File, pMeshData->szDiffuseMapFileName, &pMotionData))
|
||||
if (CPackManager::Instance().GetFile(pMeshData->szDiffuseMapFileName, File))
|
||||
{
|
||||
CMemoryTextFileLoader textFileLoader;
|
||||
std::vector<std::string> stTokenVector;
|
||||
|
||||
textFileLoader.Bind(File.Size(), pMotionData);
|
||||
textFileLoader.Bind(File.size(), File.data());
|
||||
|
||||
std::string strPathName;
|
||||
GetOnlyPathName(pMeshData->szDiffuseMapFileName, strPathName);
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
#include <d3dx9.h>
|
||||
|
||||
#include "../eterlib/GrpScreen.h"
|
||||
#include "../eterlib/Resource.h"
|
||||
#include "../eterlib/GrpImageInstance.h"
|
||||
#include "../eterLib/TextFileLoader.h"
|
||||
#include "Eterlib/GrpScreen.h"
|
||||
#include "Eterlib/Resource.h"
|
||||
#include "Eterlib/GrpImageInstance.h"
|
||||
#include "EterLib/TextFileLoader.h"
|
||||
|
||||
#include "Type.h"
|
||||
#include "EffectElementBase.h"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "StdAfx.h"
|
||||
#include "../eterLib/StateManager.h"
|
||||
#include "../eterLib/ResourceManager.h"
|
||||
#include "EterLib/StateManager.h"
|
||||
#include "EterLib/ResourceManager.h"
|
||||
#include "EffectMeshInstance.h"
|
||||
#include "../eterlib/GrpMath.h"
|
||||
#include "Eterlib/GrpMath.h"
|
||||
|
||||
CDynamicPool<CEffectMeshInstance> CEffectMeshInstance::ms_kPool;
|
||||
|
||||
@@ -167,7 +167,8 @@ void CEffectMeshInstance::OnRender()
|
||||
}
|
||||
|
||||
Color.a = fAlpha * rFrameData.fVisibility;
|
||||
STATEMANAGER.SetRenderState(D3DRS_TEXTUREFACTOR, Color);
|
||||
STATEMANAGER.SetRenderState(D3DRS_TEXTUREFACTOR, DWORD(Color));
|
||||
STATEMANAGER.SetFVF(D3DFVF_XYZ | D3DFVF_TEX1);
|
||||
STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLELIST,
|
||||
rFrameData.dwIndexCount/3,
|
||||
&rFrameData.PDTVertexVector[0],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../eterlib/GrpScreen.h"
|
||||
#include "../eterlib/GrpImageInstance.h"
|
||||
#include "Eterlib/GrpScreen.h"
|
||||
#include "Eterlib/GrpImageInstance.h"
|
||||
#include "EffectElementBaseInstance.h"
|
||||
#include "FrameController.h"
|
||||
#include "EffectMesh.h"
|
||||
|
||||
@@ -156,12 +156,6 @@ void CParticleInstance::UpdateColor(float time, float elapsedTime)
|
||||
return;
|
||||
|
||||
m_Color = GetTimeEventBlendValue(time, m_pParticleProperty->m_TimeEventColor);
|
||||
|
||||
#ifdef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
for (auto& vtx : m_ParticleMesh) {
|
||||
vtx.diffuse = m_Color;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CParticleInstance::UpdateGravity(float time, float elapsedTime)
|
||||
@@ -185,10 +179,7 @@ void CParticleInstance::UpdateAirResistance(float time, float elapsedTime)
|
||||
|
||||
void CParticleInstance::Transform(const D3DXMATRIX * c_matLocal)
|
||||
{
|
||||
#ifndef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
STATEMANAGER.SetRenderState(D3DRS_TEXTUREFACTOR, m_Color);
|
||||
#endif
|
||||
/////
|
||||
|
||||
D3DXVECTOR3 v3Up;
|
||||
D3DXVECTOR3 v3Cross;
|
||||
@@ -322,63 +313,24 @@ void CParticleInstance::Transform(const D3DXMATRIX * c_matLocal)
|
||||
{
|
||||
D3DXVECTOR3 v3Position;
|
||||
D3DXVec3TransformCoord(&v3Position, &m_v3Position, c_matLocal);
|
||||
|
||||
D3DXVECTOR3 p0 = v3Position - v3Up + v3Cross; // bottom-left
|
||||
D3DXVECTOR3 p1 = v3Position - v3Up - v3Cross; // bottom-right
|
||||
D3DXVECTOR3 p2 = v3Position + v3Up + v3Cross; // top-left
|
||||
D3DXVECTOR3 p3 = v3Position + v3Up - v3Cross; // top-right
|
||||
|
||||
#ifdef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
// triangle 1: p0, p1, p2
|
||||
m_ParticleMesh[0].position = p0;
|
||||
m_ParticleMesh[1].position = p1;
|
||||
m_ParticleMesh[2].position = p2;
|
||||
|
||||
// triangle 2: p2, p1, p3
|
||||
m_ParticleMesh[3].position = p2;
|
||||
m_ParticleMesh[4].position = p1;
|
||||
m_ParticleMesh[5].position = p3;
|
||||
#else
|
||||
m_ParticleMesh[0].position = p0;
|
||||
m_ParticleMesh[1].position = p1;
|
||||
m_ParticleMesh[2].position = p2;
|
||||
m_ParticleMesh[3].position = p3;
|
||||
#endif
|
||||
m_ParticleMesh[0].position = v3Position - v3Up + v3Cross;
|
||||
m_ParticleMesh[1].position = v3Position - v3Up - v3Cross;
|
||||
m_ParticleMesh[2].position = v3Position + v3Up + v3Cross;
|
||||
m_ParticleMesh[3].position = v3Position + v3Up - v3Cross;
|
||||
}
|
||||
else
|
||||
{
|
||||
D3DXVECTOR3 p0 = m_v3Position - v3Up + v3Cross; // bottom-left
|
||||
D3DXVECTOR3 p1 = m_v3Position - v3Up - v3Cross; // bottom-right
|
||||
D3DXVECTOR3 p2 = m_v3Position + v3Up + v3Cross; // top-left
|
||||
D3DXVECTOR3 p3 = m_v3Position + v3Up - v3Cross; // top-right
|
||||
|
||||
#ifdef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
// triangle 1: p0, p1, p2
|
||||
m_ParticleMesh[0].position = p0;
|
||||
m_ParticleMesh[1].position = p1;
|
||||
m_ParticleMesh[2].position = p2;
|
||||
|
||||
// triangle 2: p2, p1, p3
|
||||
m_ParticleMesh[3].position = p2;
|
||||
m_ParticleMesh[4].position = p1;
|
||||
m_ParticleMesh[5].position = p3;
|
||||
#else
|
||||
m_ParticleMesh[0].position = p0;
|
||||
m_ParticleMesh[1].position = p1;
|
||||
m_ParticleMesh[2].position = p2;
|
||||
m_ParticleMesh[3].position = p3;
|
||||
#endif
|
||||
m_ParticleMesh[0].position = m_v3Position - v3Up + v3Cross;
|
||||
m_ParticleMesh[1].position = m_v3Position - v3Up - v3Cross;
|
||||
m_ParticleMesh[2].position = m_v3Position + v3Up + v3Cross;
|
||||
m_ParticleMesh[3].position = m_v3Position + v3Up - v3Cross;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CParticleInstance::Transform(const D3DXMATRIX * c_matLocal, const float c_fZRotation)
|
||||
{
|
||||
#ifndef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
STATEMANAGER.SetRenderState(D3DRS_TEXTUREFACTOR, m_Color);
|
||||
#endif
|
||||
|
||||
/////
|
||||
|
||||
D3DXVECTOR3 v3Up;
|
||||
D3DXVECTOR3 v3Cross;
|
||||
@@ -510,52 +462,17 @@ void CParticleInstance::Transform(const D3DXMATRIX * c_matLocal, const float c_f
|
||||
{
|
||||
D3DXVECTOR3 v3Position;
|
||||
D3DXVec3TransformCoord(&v3Position, &m_v3Position, c_matLocal);
|
||||
|
||||
D3DXVECTOR3 p0 = v3Position - v3Up + v3Cross; // bottom-left
|
||||
D3DXVECTOR3 p1 = v3Position - v3Up - v3Cross; // bottom-right
|
||||
D3DXVECTOR3 p2 = v3Position + v3Up + v3Cross; // top-left
|
||||
D3DXVECTOR3 p3 = v3Position + v3Up - v3Cross; // top-right
|
||||
|
||||
#ifdef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
// triangle 1: p0, p1, p2
|
||||
m_ParticleMesh[0].position = p0;
|
||||
m_ParticleMesh[1].position = p1;
|
||||
m_ParticleMesh[2].position = p2;
|
||||
|
||||
// triangle 2: p2, p1, p3
|
||||
m_ParticleMesh[3].position = p2;
|
||||
m_ParticleMesh[4].position = p1;
|
||||
m_ParticleMesh[5].position = p3;
|
||||
#else
|
||||
m_ParticleMesh[0].position = p0;
|
||||
m_ParticleMesh[1].position = p1;
|
||||
m_ParticleMesh[2].position = p2;
|
||||
m_ParticleMesh[3].position = p3;
|
||||
#endif
|
||||
m_ParticleMesh[0].position = v3Position - v3Up + v3Cross;
|
||||
m_ParticleMesh[1].position = v3Position - v3Up - v3Cross;
|
||||
m_ParticleMesh[2].position = v3Position + v3Up + v3Cross;
|
||||
m_ParticleMesh[3].position = v3Position + v3Up - v3Cross;
|
||||
}
|
||||
else
|
||||
{
|
||||
D3DXVECTOR3 p0 = m_v3Position - v3Up + v3Cross; // bottom-left
|
||||
D3DXVECTOR3 p1 = m_v3Position - v3Up - v3Cross; // bottom-right
|
||||
D3DXVECTOR3 p2 = m_v3Position + v3Up + v3Cross; // top-left
|
||||
D3DXVECTOR3 p3 = m_v3Position + v3Up - v3Cross; // top-right
|
||||
|
||||
#ifdef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
// triangle 1: p0, p1, p2
|
||||
m_ParticleMesh[0].position = p0;
|
||||
m_ParticleMesh[1].position = p1;
|
||||
m_ParticleMesh[2].position = p2;
|
||||
|
||||
// triangle 2: p2, p1, p3
|
||||
m_ParticleMesh[3].position = p2;
|
||||
m_ParticleMesh[4].position = p1;
|
||||
m_ParticleMesh[5].position = p3;
|
||||
#else
|
||||
m_ParticleMesh[0].position = p0;
|
||||
m_ParticleMesh[1].position = p1;
|
||||
m_ParticleMesh[2].position = p2;
|
||||
m_ParticleMesh[3].position = p3;
|
||||
#endif
|
||||
m_ParticleMesh[0].position = m_v3Position - v3Up + v3Cross;
|
||||
m_ParticleMesh[1].position = m_v3Position - v3Up - v3Cross;
|
||||
m_ParticleMesh[2].position = m_v3Position + v3Up + v3Cross;
|
||||
m_ParticleMesh[3].position = m_v3Position + v3Up - v3Cross;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,29 +490,14 @@ void CParticleInstance::__Initialize()
|
||||
m_v2Scale = D3DXVECTOR2(1.0f, 1.0f);
|
||||
m_Color = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
#ifdef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
for (auto& vtx : m_ParticleMesh) {
|
||||
vtx.diffuse = m_Color;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_byFrameIndex = 0;
|
||||
m_rotationType = CParticleProperty::ROTATION_TYPE_NONE;
|
||||
m_fFrameTime = 0;
|
||||
|
||||
#ifdef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
m_ParticleMesh[0].texCoord = D3DXVECTOR2(0.0f, 1.0f);
|
||||
m_ParticleMesh[1].texCoord = D3DXVECTOR2(0.0f, 0.0f);
|
||||
m_ParticleMesh[2].texCoord = D3DXVECTOR2(1.0f, 1.0f);
|
||||
m_ParticleMesh[3].texCoord = D3DXVECTOR2(1.0f, 1.0f);
|
||||
m_ParticleMesh[4].texCoord = D3DXVECTOR2(0.0f, 0.0f);
|
||||
m_ParticleMesh[5].texCoord = D3DXVECTOR2(1.0f, 0.0f);
|
||||
#else
|
||||
m_ParticleMesh[0].texCoord = D3DXVECTOR2(0.0f, 1.0f);
|
||||
m_ParticleMesh[1].texCoord = D3DXVECTOR2(0.0f, 0.0f);
|
||||
m_ParticleMesh[2].texCoord = D3DXVECTOR2(1.0f, 1.0f);
|
||||
m_ParticleMesh[3].texCoord = D3DXVECTOR2(1.0f, 0.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
CParticleInstance::CParticleInstance()
|
||||
@@ -608,11 +510,7 @@ CParticleInstance::~CParticleInstance()
|
||||
Destroy();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
const std::array<TPDTVertex, 6>& CParticleInstance::GetParticleMeshPointer()
|
||||
#else
|
||||
const std::array<TPTVertex, 4>& CParticleInstance::GetParticleMeshPointer()
|
||||
#endif
|
||||
TPTVertex * CParticleInstance::GetParticleMeshPointer()
|
||||
{
|
||||
return m_ParticleMesh;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
#pragma once
|
||||
#include "UserInterface/Locale_inc.h"
|
||||
#include "Type.h"
|
||||
#include "Eterlib/GrpBase.h"
|
||||
#include "EterLib/Pool.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
class CParticleProperty;
|
||||
class CEmitterProperty;
|
||||
|
||||
@@ -41,7 +38,6 @@ class CParticleInstance
|
||||
D3DXVECTOR2 m_v2Scale;
|
||||
|
||||
float m_fRotation;
|
||||
|
||||
D3DXCOLOR m_Color;
|
||||
|
||||
BYTE m_byTextureAnimationType;
|
||||
@@ -68,11 +64,7 @@ class CParticleInstance
|
||||
void Transform(const D3DXMATRIX * c_matLocal=NULL);
|
||||
void Transform(const D3DXMATRIX * c_matLocal, const float c_fZRotation);
|
||||
|
||||
#ifdef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
const std::array<TPDTVertex, 6>& GetParticleMeshPointer();
|
||||
#else
|
||||
const std::array<TPTVertex, 4>& GetParticleMeshPointer();
|
||||
#endif
|
||||
TPTVertex * GetParticleMeshPointer();
|
||||
|
||||
void DeleteThis();
|
||||
|
||||
@@ -80,12 +72,7 @@ class CParticleInstance
|
||||
|
||||
protected:
|
||||
void __Initialize();
|
||||
|
||||
#ifdef ENABLE_BATCHED_PARTICLE_RENDERING
|
||||
std::array<TPDTVertex, 6> m_ParticleMesh;
|
||||
#else
|
||||
std::array<TPTVertex, 4> m_ParticleMesh;
|
||||
#endif
|
||||
TPTVertex m_ParticleMesh[4];
|
||||
public:
|
||||
static CDynamicPool<CParticleInstance> ms_kPool;
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
#include "StdAfx.h"
|
||||
#include "ParticleProperty.h"
|
||||
#include "../eterlib/ResourceManager.h"
|
||||
#include "Eterlib/ResourceManager.h"
|
||||
|
||||
void CParticleProperty::InsertTexture(const char * c_szFileName)
|
||||
{
|
||||
CGraphicImage * pImage = (CGraphicImage *)CResourceManager::Instance().GetResourcePointer(c_szFileName);
|
||||
|
||||
m_ImageVector.push_back(pImage);
|
||||
#ifdef WORLD_EDITOR
|
||||
m_TextureNameVector.push_back(c_szFileName);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CParticleProperty::SetTexture(const char * c_szFileName)
|
||||
@@ -20,9 +16,6 @@ bool CParticleProperty::SetTexture(const char * c_szFileName)
|
||||
return false;
|
||||
}
|
||||
m_ImageVector.clear();
|
||||
#ifdef WORLD_EDITOR
|
||||
m_TextureNameVector.clear();
|
||||
#endif
|
||||
InsertTexture(c_szFileName);
|
||||
return true;
|
||||
}
|
||||
@@ -56,15 +49,7 @@ void CParticleProperty::Clear()
|
||||
m_TimeEventScaleX.clear();
|
||||
m_TimeEventScaleY.clear();
|
||||
//m_TimeEventScaleXY.clear();
|
||||
#ifdef WORLD_EDITOR
|
||||
m_TimeEventColorRed.clear();
|
||||
m_TimeEventColorGreen.clear();
|
||||
m_TimeEventColorBlue.clear();
|
||||
m_TimeEventAlpha.clear();
|
||||
m_TextureNameVector.clear();
|
||||
#else
|
||||
m_TimeEventColor.clear();
|
||||
#endif
|
||||
m_TimeEventRotation.clear();
|
||||
|
||||
m_ImageVector.clear();
|
||||
@@ -103,16 +88,7 @@ CParticleProperty & CParticleProperty::operator = ( const CParticleProperty& c_P
|
||||
m_TimeEventScaleX = c_ParticleProperty.m_TimeEventScaleX;
|
||||
m_TimeEventScaleY = c_ParticleProperty.m_TimeEventScaleY;
|
||||
|
||||
#ifdef WORLD_EDITOR
|
||||
m_TimeEventColorRed = c_ParticleProperty.m_TimeEventColorRed;
|
||||
m_TimeEventColorGreen = c_ParticleProperty.m_TimeEventColorGreen;
|
||||
m_TimeEventColorBlue = c_ParticleProperty.m_TimeEventColorBlue;
|
||||
m_TimeEventAlpha = c_ParticleProperty.m_TimeEventAlpha;
|
||||
|
||||
m_TextureNameVector = c_ParticleProperty.m_TextureNameVector;
|
||||
#else
|
||||
m_TimeEventColor = c_ParticleProperty.m_TimeEventColor;
|
||||
#endif
|
||||
m_TimeEventRotation = c_ParticleProperty.m_TimeEventRotation;
|
||||
|
||||
m_ImageVector = c_ParticleProperty.m_ImageVector;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "../eterlib/GrpImageInstance.h"
|
||||
#include "Eterlib/GrpImageInstance.h"
|
||||
|
||||
#include "Type.h"
|
||||
|
||||
@@ -75,16 +75,7 @@ class CParticleProperty
|
||||
|
||||
TTimeEventTableFloat m_TimeEventScaleX;
|
||||
TTimeEventTableFloat m_TimeEventScaleY;
|
||||
#ifdef WORLD_EDITOR
|
||||
TTimeEventTableFloat m_TimeEventColorRed;
|
||||
TTimeEventTableFloat m_TimeEventColorGreen;
|
||||
TTimeEventTableFloat m_TimeEventColorBlue;
|
||||
TTimeEventTableFloat m_TimeEventAlpha;
|
||||
|
||||
std::vector<std::string> m_TextureNameVector;
|
||||
#else
|
||||
TTimeEventTableColor m_TimeEventColor;
|
||||
#endif
|
||||
TTimeEventTableFloat m_TimeEventRotation;
|
||||
|
||||
std::vector<CGraphicImage*> m_ImageVector;
|
||||
|
||||
@@ -210,17 +210,6 @@ BOOL CParticleSystemData::OnLoadScript(CTextFileLoader & rTextFileLoader)
|
||||
if (!GetTokenTimeEventFloat(rTextFileLoader, "timeeventscaley", &m_ParticleProperty.m_TimeEventScaleY))
|
||||
return FALSE;
|
||||
|
||||
#ifdef WORLD_EDITOR
|
||||
if (!GetTokenTimeEventFloat(rTextFileLoader, "timeeventcolorred", &m_ParticleProperty.m_TimeEventColorRed))
|
||||
return FALSE;
|
||||
if (!GetTokenTimeEventFloat(rTextFileLoader, "timeeventcolorgreen", &m_ParticleProperty.m_TimeEventColorGreen))
|
||||
return FALSE;
|
||||
if (!GetTokenTimeEventFloat(rTextFileLoader, "timeeventcolorblue", &m_ParticleProperty.m_TimeEventColorBlue))
|
||||
return FALSE;
|
||||
|
||||
if (!GetTokenTimeEventFloat(rTextFileLoader, "timeeventalpha", &m_ParticleProperty.m_TimeEventAlpha))
|
||||
return FALSE;
|
||||
#else
|
||||
TTimeEventTableFloat TimeEventR;
|
||||
TTimeEventTableFloat TimeEventB;
|
||||
TTimeEventTableFloat TimeEventG;
|
||||
@@ -257,16 +246,13 @@ BOOL CParticleSystemData::OnLoadScript(CTextFileLoader & rTextFileLoader)
|
||||
fA = GetTimeEventBlendValue(fTime, TimeEventA);
|
||||
TTimeEventTypeColor t;
|
||||
t.m_fTime = fTime;
|
||||
D3DXCOLOR c;
|
||||
c.r = fR;
|
||||
c.g = fG;
|
||||
c.b = fB;
|
||||
c.a = fA;
|
||||
t.m_Value = c;
|
||||
t.m_Value.r = fR;
|
||||
t.m_Value.g = fG;
|
||||
t.m_Value.b = fB;
|
||||
t.m_Value.a = fA;
|
||||
m_ParticleProperty.m_TimeEventColor.push_back(t);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!GetTokenTimeEventFloat(rTextFileLoader, "timeeventrotation", &m_ParticleProperty.m_TimeEventRotation))
|
||||
return FALSE;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../eterLib/TextFileLoader.h"
|
||||
#include "EterLib/TextFileLoader.h"
|
||||
|
||||
#include "EffectElementBase.h"
|
||||
#include "EmitterProperty.h"
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#include "StdAfx.h"
|
||||
#include "../eterBase/Random.h"
|
||||
#include "../eterLib/StateManager.h"
|
||||
#include "EterBase/Random.h"
|
||||
#include "EterLib/StateManager.h"
|
||||
#include "ParticleSystemData.h"
|
||||
#include "ParticleSystemInstance.h"
|
||||
#include "ParticleInstance.h"
|
||||
|
||||
std::unordered_map<LPDIRECT3DTEXTURE9, std::unordered_map<uint32_t, std::vector<TPDTVertex>>> g_particleVertexBatch;
|
||||
|
||||
CDynamicPool<CParticleSystemInstance> CParticleSystemInstance::ms_kPool;
|
||||
|
||||
void CParticleSystemInstance::DestroySystem()
|
||||
@@ -250,14 +248,7 @@ void CParticleSystemInstance::CreateParticles(float fElapsedTime)
|
||||
pInstance->m_v2Scale.x = m_pParticleProperty->m_TimeEventScaleX.front().m_Value;
|
||||
pInstance->m_v2Scale.y= m_pParticleProperty->m_TimeEventScaleY.front().m_Value;
|
||||
//pInstance->m_v2Scale = m_pParticleProperty->m_TimeEventScaleXY.front().m_Value;
|
||||
#ifdef WORLD_EDITOR
|
||||
pInstance->m_Color.r = m_pParticleProperty->m_TimeEventColorRed.front().m_Value;
|
||||
pInstance->m_Color.g = m_pParticleProperty->m_TimeEventColorGreen.front().m_Value;
|
||||
pInstance->m_Color.b = m_pParticleProperty->m_TimeEventColorBlue.front().m_Value;
|
||||
pInstance->m_Color.a = m_pParticleProperty->m_TimeEventAlpha.front().m_Value;
|
||||
#else
|
||||
pInstance->m_Color = m_pParticleProperty->m_TimeEventColor.front().m_Value;
|
||||
#endif
|
||||
}
|
||||
|
||||
m_ParticleInstanceListVector[pInstance->m_byFrameIndex].push_back(pInstance);
|
||||
@@ -345,189 +336,80 @@ namespace NParticleRenderer
|
||||
struct TwoSideRenderer
|
||||
{
|
||||
const D3DXMATRIX * pmat;
|
||||
uint32_t opKey;
|
||||
|
||||
TwoSideRenderer(uint32_t key, const D3DXMATRIX * pmat=NULL)
|
||||
: opKey(key), pmat(pmat)
|
||||
TwoSideRenderer(const D3DXMATRIX * pmat=NULL)
|
||||
: pmat(pmat)
|
||||
{
|
||||
}
|
||||
|
||||
inline void operator () (CParticleInstance * pInstance, LPDIRECT3DTEXTURE9 pTexture)
|
||||
inline void operator () (CParticleInstance * pInstance)
|
||||
{
|
||||
auto& vtxBatch = g_particleVertexBatch[pTexture][opKey];
|
||||
const auto& particleMesh = pInstance->GetParticleMeshPointer();
|
||||
|
||||
pInstance->Transform(pmat,D3DXToRadian(-30.0f));
|
||||
vtxBatch.insert(vtxBatch.end(), particleMesh.begin(), particleMesh.end());
|
||||
STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, pInstance->GetParticleMeshPointer(), sizeof(TPTVertex));
|
||||
|
||||
pInstance->Transform(pmat,D3DXToRadian(+30.0f));
|
||||
vtxBatch.insert(vtxBatch.end(), particleMesh.begin(), particleMesh.end());
|
||||
STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, pInstance->GetParticleMeshPointer(), sizeof(TPTVertex));
|
||||
}
|
||||
};
|
||||
|
||||
struct ThreeSideRenderer
|
||||
{
|
||||
const D3DXMATRIX * pmat;
|
||||
uint32_t opKey;
|
||||
|
||||
ThreeSideRenderer(uint32_t key, const D3DXMATRIX * pmat=NULL)
|
||||
: opKey(key), pmat(pmat)
|
||||
ThreeSideRenderer(const D3DXMATRIX * pmat=NULL)
|
||||
: pmat(pmat)
|
||||
{
|
||||
}
|
||||
|
||||
inline void operator () (CParticleInstance* pInstance, LPDIRECT3DTEXTURE9 pTexture)
|
||||
inline void operator () (CParticleInstance * pInstance)
|
||||
{
|
||||
auto& vtxBatch = g_particleVertexBatch[pTexture][opKey];
|
||||
const auto& particleMesh = pInstance->GetParticleMeshPointer();
|
||||
|
||||
pInstance->Transform(pmat);
|
||||
vtxBatch.insert(vtxBatch.end(), particleMesh.begin(), particleMesh.end());
|
||||
|
||||
STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, pInstance->GetParticleMeshPointer(), sizeof(TPTVertex));
|
||||
pInstance->Transform(pmat,D3DXToRadian(-60.0f));
|
||||
vtxBatch.insert(vtxBatch.end(), particleMesh.begin(), particleMesh.end());
|
||||
|
||||
STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, pInstance->GetParticleMeshPointer(), sizeof(TPTVertex));
|
||||
pInstance->Transform(pmat,D3DXToRadian(+60.0f));
|
||||
vtxBatch.insert(vtxBatch.end(), particleMesh.begin(), particleMesh.end());
|
||||
STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, pInstance->GetParticleMeshPointer(), sizeof(TPTVertex));
|
||||
}
|
||||
};
|
||||
|
||||
struct NormalRenderer
|
||||
{
|
||||
uint32_t opKey;
|
||||
|
||||
NormalRenderer(uint32_t key) : opKey(key) {}
|
||||
inline void operator () (CParticleInstance* pInstance, LPDIRECT3DTEXTURE9 pTexture)
|
||||
inline void operator () (CParticleInstance * pInstance)
|
||||
{
|
||||
auto& vtxBatch = g_particleVertexBatch[pTexture][opKey];
|
||||
const auto& particleMesh = pInstance->GetParticleMeshPointer();
|
||||
|
||||
pInstance->Transform();
|
||||
vtxBatch.insert(vtxBatch.end(), particleMesh.begin(), particleMesh.end());
|
||||
STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, pInstance->GetParticleMeshPointer(), sizeof(TPTVertex));
|
||||
}
|
||||
};
|
||||
struct AttachRenderer
|
||||
{
|
||||
const D3DXMATRIX* pmat;
|
||||
uint32_t opKey;
|
||||
|
||||
AttachRenderer(uint32_t key, const D3DXMATRIX * pmat)
|
||||
: opKey(key), pmat(pmat)
|
||||
AttachRenderer(const D3DXMATRIX * pmat)
|
||||
: pmat(pmat)
|
||||
{
|
||||
}
|
||||
|
||||
inline void operator () (CParticleInstance* pInstance, LPDIRECT3DTEXTURE9 pTexture)
|
||||
inline void operator () (CParticleInstance * pInstance)
|
||||
{
|
||||
auto& vtxBatch = g_particleVertexBatch[pTexture][opKey];
|
||||
const auto& particleMesh = pInstance->GetParticleMeshPointer();
|
||||
|
||||
pInstance->Transform(pmat);
|
||||
vtxBatch.insert(vtxBatch.end(), particleMesh.begin(), particleMesh.end());
|
||||
STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, pInstance->GetParticleMeshPointer(), sizeof(TPTVertex));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void CParticleSystemInstance::OnRender()
|
||||
{
|
||||
g_particleVertexBatch.clear();
|
||||
|
||||
CScreen::Identity();
|
||||
|
||||
uint32_t opKey;
|
||||
{
|
||||
uint8_t* pKeyPart = (uint8_t*)&opKey;
|
||||
pKeyPart[0] = m_pParticleProperty->m_bySrcBlendType;
|
||||
pKeyPart[1] = m_pParticleProperty->m_byDestBlendType;
|
||||
pKeyPart[2] = m_pParticleProperty->m_byColorOperationType;
|
||||
}
|
||||
|
||||
if (m_pParticleProperty->m_byBillboardType < BILLBOARD_TYPE_2FACE)
|
||||
{
|
||||
if (!m_pParticleProperty->m_bAttachFlag)
|
||||
{
|
||||
auto obj = NParticleRenderer::NormalRenderer(opKey);
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto obj = NParticleRenderer::AttachRenderer(opKey, mc_pmatLocal);
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
}
|
||||
else if (m_pParticleProperty->m_byBillboardType == BILLBOARD_TYPE_2FACE)
|
||||
{
|
||||
if (!m_pParticleProperty->m_bAttachFlag)
|
||||
{
|
||||
auto obj = NParticleRenderer::TwoSideRenderer(opKey);
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto obj = NParticleRenderer::TwoSideRenderer(opKey, mc_pmatLocal);
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
}
|
||||
else if (m_pParticleProperty->m_byBillboardType == BILLBOARD_TYPE_3FACE)
|
||||
{
|
||||
if (!m_pParticleProperty->m_bAttachFlag)
|
||||
{
|
||||
auto obj = NParticleRenderer::ThreeSideRenderer(opKey);
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto obj = NParticleRenderer::ThreeSideRenderer(opKey, mc_pmatLocal);
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& [pTexture, keyMap] : g_particleVertexBatch) {
|
||||
if (keyMap.empty())
|
||||
continue;
|
||||
|
||||
STATEMANAGER.SetTexture(0, pTexture);
|
||||
for (auto& [opKey, vtxBatch] : keyMap) {
|
||||
if (vtxBatch.empty())
|
||||
continue;
|
||||
|
||||
uint8_t* pKeyPart = (uint8_t*)&opKey;
|
||||
STATEMANAGER.SetRenderState(D3DRS_SRCBLEND, pKeyPart[0]);
|
||||
STATEMANAGER.SetRenderState(D3DRS_DESTBLEND, pKeyPart[1]);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, pKeyPart[2]);
|
||||
|
||||
STATEMANAGER.DrawPrimitiveUP(
|
||||
D3DPT_TRIANGLELIST,
|
||||
vtxBatch.size() / 3,
|
||||
vtxBatch.data(),
|
||||
sizeof(TPDTVertex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CParticleSystemInstance::BatchParticles()
|
||||
{
|
||||
CScreen::Identity();
|
||||
STATEMANAGER.SetRenderState(D3DRS_SRCBLEND, m_pParticleProperty->m_bySrcBlendType);
|
||||
STATEMANAGER.SetRenderState(D3DRS_DESTBLEND, m_pParticleProperty->m_byDestBlendType);
|
||||
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, m_pParticleProperty->m_byColorOperationType);
|
||||
|
||||
uint32_t opKey;
|
||||
{
|
||||
uint8_t* pKeyPart = (uint8_t*)&opKey;
|
||||
pKeyPart[0] = m_pParticleProperty->m_bySrcBlendType;
|
||||
pKeyPart[1] = m_pParticleProperty->m_byDestBlendType;
|
||||
pKeyPart[2] = m_pParticleProperty->m_byColorOperationType;
|
||||
}
|
||||
|
||||
STATEMANAGER.SetTextureStageState(0,D3DTSS_COLOROP,m_pParticleProperty->m_byColorOperationType);
|
||||
if (m_pParticleProperty->m_byBillboardType < BILLBOARD_TYPE_2FACE)
|
||||
{
|
||||
if (!m_pParticleProperty->m_bAttachFlag)
|
||||
{
|
||||
auto obj = NParticleRenderer::NormalRenderer(opKey);
|
||||
auto obj = NParticleRenderer::NormalRenderer();
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto obj = NParticleRenderer::AttachRenderer(opKey, mc_pmatLocal);
|
||||
auto obj = NParticleRenderer::AttachRenderer(mc_pmatLocal);
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
}
|
||||
@@ -535,12 +417,12 @@ void CParticleSystemInstance::BatchParticles()
|
||||
{
|
||||
if (!m_pParticleProperty->m_bAttachFlag)
|
||||
{
|
||||
auto obj = NParticleRenderer::TwoSideRenderer(opKey);
|
||||
auto obj = NParticleRenderer::TwoSideRenderer();
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto obj = NParticleRenderer::TwoSideRenderer(opKey, mc_pmatLocal);
|
||||
auto obj = NParticleRenderer::TwoSideRenderer(mc_pmatLocal);
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
}
|
||||
@@ -548,12 +430,12 @@ void CParticleSystemInstance::BatchParticles()
|
||||
{
|
||||
if (!m_pParticleProperty->m_bAttachFlag)
|
||||
{
|
||||
auto obj = NParticleRenderer::ThreeSideRenderer(opKey);
|
||||
auto obj = NParticleRenderer::ThreeSideRenderer();
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto obj = NParticleRenderer::ThreeSideRenderer(opKey, mc_pmatLocal);
|
||||
auto obj = NParticleRenderer::ThreeSideRenderer(mc_pmatLocal);
|
||||
ForEachParticleRendering(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include "UserInterface/Locale_inc.h"
|
||||
#include "EffectElementBaseInstance.h"
|
||||
#include "ParticleInstance.h"
|
||||
#include "ParticleProperty.h"
|
||||
@@ -26,12 +25,14 @@ class CParticleSystemInstance : public CEffectElementBaseInstance
|
||||
DWORD dwFrameIndex;
|
||||
for(dwFrameIndex=0; dwFrameIndex<m_kVct_pkImgInst.size(); dwFrameIndex++)
|
||||
{
|
||||
STATEMANAGER.SetTexture(0, m_kVct_pkImgInst[dwFrameIndex]->GetTextureReference().GetD3DTexture());
|
||||
TParticleInstanceList::iterator itor = m_ParticleInstanceListVector[dwFrameIndex].begin();
|
||||
for (; itor != m_ParticleInstanceListVector[dwFrameIndex].end(); ++itor)
|
||||
{
|
||||
if (!InFrustum(*itor))
|
||||
return;
|
||||
FunObj(*itor, m_kVct_pkImgInst[dwFrameIndex]->GetTextureReference().GetD3DTexture());
|
||||
continue;
|
||||
|
||||
FunObj(*itor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,8 +58,6 @@ class CParticleSystemInstance : public CEffectElementBaseInstance
|
||||
|
||||
DWORD GetEmissionCount();
|
||||
|
||||
void BatchParticles();
|
||||
|
||||
protected:
|
||||
void OnInitialize();
|
||||
void OnDestroy();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "Stdafx.h"
|
||||
#include "../eterLib/GrpLightManager.h"
|
||||
#include "EterLib/GrpLightManager.h"
|
||||
|
||||
#include "SimpleLightInstance.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../eterLib/GrpScreen.h"
|
||||
#include "EterLib/GrpScreen.h"
|
||||
|
||||
#include "EffectElementBaseInstance.h"
|
||||
#include "SimpleLightData.h"
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
|
||||
//#include <crtdbg.h>
|
||||
|
||||
#include "../eterBase/StdAfx.h"
|
||||
#include "EterBase/StdAfx.h"
|
||||
|
||||
#include "EterBase/Utils.h"
|
||||
#include "EterBase/Timer.h"
|
||||
#include "EterBase/CRC32.h"
|
||||
#include "EterBase/Debug.h"
|
||||
|
||||
#include "EterLib/StdAfx.h"
|
||||
#include "EterLib/TextFileLoader.h"
|
||||
|
||||
#include "../eterBase/Utils.h"
|
||||
#include "../eterBase/Timer.h"
|
||||
#include "../eterBase/CRC32.h"
|
||||
#include "../eterBase/Debug.h"
|
||||
#include "AudioLib/StdAfx.h"
|
||||
|
||||
#include "../eterLib/StdAfx.h"
|
||||
#include "../eterLib/TextFileLoader.h"
|
||||
|
||||
#include "../milesLib/StdAfx.h"
|
||||
#include "UserInterface/Locale_inc.h"
|
||||
|
||||
/*
|
||||
#include "FrameController.h"
|
||||
|
||||
@@ -74,8 +74,8 @@ enum EBillBoardType
|
||||
|
||||
BILLBOARD_TYPE_LIE, // 바닥에 누은 형상
|
||||
|
||||
BILLBOARD_TYPE_2FACE, // / and \
|
||||
BILLBOARD_TYPE_3FACE, // / and \ and -
|
||||
BILLBOARD_TYPE_2FACE, // / and
|
||||
BILLBOARD_TYPE_3FACE, // / and
|
||||
|
||||
//BILLBOARD_TYPE_RAY, // 잔상
|
||||
|
||||
@@ -123,68 +123,6 @@ typedef struct SEffectPosition : public CTimeEvent<D3DXVECTOR3>
|
||||
D3DXVECTOR3 m_vecControlPoint;
|
||||
} TEffectPosition;
|
||||
|
||||
#define AG_MASK 0xff00ff00
|
||||
#define RB_MASK 0x00ff00ff
|
||||
|
||||
struct DWORDCOLOR
|
||||
{
|
||||
DWORD m_dwColor;
|
||||
|
||||
DWORDCOLOR()
|
||||
{
|
||||
}
|
||||
DWORDCOLOR(const DWORDCOLOR& r)
|
||||
: m_dwColor(r.m_dwColor)
|
||||
{
|
||||
}
|
||||
DWORDCOLOR(DWORD dwColor)
|
||||
: m_dwColor(dwColor)
|
||||
{
|
||||
}
|
||||
|
||||
DWORDCOLOR& operator = (const DWORDCOLOR& r)
|
||||
{
|
||||
m_dwColor = r.m_dwColor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DWORDCOLOR& operator *= (float f)
|
||||
{
|
||||
DWORD idx = DWORD(f * 256);
|
||||
m_dwColor =
|
||||
(((DWORD)(((m_dwColor & AG_MASK) >> 8) * idx)) & AG_MASK)
|
||||
+ ((DWORD)(((m_dwColor & RB_MASK) * idx) >> 8) & RB_MASK);
|
||||
return *this;
|
||||
}
|
||||
DWORDCOLOR& operator += (const DWORDCOLOR& r)
|
||||
{
|
||||
m_dwColor += r.m_dwColor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator DWORD()
|
||||
{
|
||||
return m_dwColor;
|
||||
}
|
||||
};
|
||||
|
||||
#undef AG_MASK
|
||||
#undef RB_MASK
|
||||
|
||||
inline DWORDCOLOR operator * (DWORDCOLOR dc, float f)
|
||||
{
|
||||
DWORDCOLOR tmp(dc);
|
||||
tmp *= f;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline DWORDCOLOR operator * (float f, DWORDCOLOR dc)
|
||||
{
|
||||
DWORDCOLOR tmp(dc);
|
||||
tmp *= f;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
typedef CTimeEvent<char> TTimeEventTypeCharacter;
|
||||
typedef CTimeEvent<short> TTimeEventTypeShort;
|
||||
typedef CTimeEvent<float> TTimeEventTypeFloat;
|
||||
@@ -238,14 +176,6 @@ inline D3DXVECTOR3 BlendSingleValue(float time, const TEffectPosition& low, cons
|
||||
return D3DXVECTOR3();
|
||||
}
|
||||
|
||||
inline DWORDCOLOR BlendSingleValue(float time, const TTimeEventTypeColor& low, const TTimeEventTypeColor& high)
|
||||
{
|
||||
const float timeDiff = high.m_fTime - low.m_fTime;
|
||||
const float perc = (time - low.m_fTime) / timeDiff;
|
||||
|
||||
return low.m_Value * (1.0f - perc) + high.m_Value * perc;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto GetTimeEventBlendValue(float time,
|
||||
const std::vector<T>& vec) -> typename T::value_type
|
||||
|
||||
@@ -5,6 +5,7 @@ add_library(EterBase STATIC ${FILE_SOURCES})
|
||||
target_link_libraries(EterBase
|
||||
lzo2
|
||||
cryptopp-static
|
||||
mio
|
||||
)
|
||||
|
||||
GroupSourcesByFolder(EterBase)
|
||||
|
||||
@@ -1,299 +0,0 @@
|
||||
#include "StdAfx.h"
|
||||
#include "CPostIt.h"
|
||||
#include "../eterBase/utils.h"
|
||||
|
||||
class _CPostItMemoryBlock
|
||||
{
|
||||
public:
|
||||
_CPostItMemoryBlock( void );
|
||||
~_CPostItMemoryBlock( void );
|
||||
|
||||
BOOL Assign( HANDLE hBlock );
|
||||
HANDLE CreateHandle( void );
|
||||
BOOL DestroyHandle( void );
|
||||
|
||||
LPSTR Find( LPCSTR lpszKeyName );
|
||||
BOOL Put( LPCSTR lpBuffer );
|
||||
BOOL Put( LPCSTR lpszKeyName, LPCSTR lpBuffer );
|
||||
BOOL Get( LPCSTR lpszKeyName, LPSTR lpBuffer, DWORD nSize );
|
||||
|
||||
protected:
|
||||
typedef std::list<CHAR *> StrList;
|
||||
typedef StrList::iterator StrListItr;
|
||||
|
||||
HANDLE m_hHandle;
|
||||
StrList m_StrList;
|
||||
};
|
||||
|
||||
CPostIt::CPostIt( LPCSTR szAppName ) : m_pMemoryBlock(NULL), m_bModified(FALSE)
|
||||
{
|
||||
Init( szAppName );
|
||||
}
|
||||
|
||||
CPostIt::~CPostIt( void )
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
BOOL CPostIt::Init( LPCSTR szAppName )
|
||||
{
|
||||
if( szAppName == NULL || !*szAppName ) {
|
||||
strcpy( m_szClipFormatName, "YMCF" );
|
||||
} else {
|
||||
strcpy( m_szClipFormatName, "YMCF_" );
|
||||
strcat( m_szClipFormatName, szAppName );
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CPostIt::CopyTo( CPostIt *pPostIt, LPCSTR lpszKeyName )
|
||||
{
|
||||
if( m_pMemoryBlock == NULL )
|
||||
return FALSE;
|
||||
LPSTR szText = m_pMemoryBlock->Find( lpszKeyName );
|
||||
if( szText == NULL )
|
||||
return FALSE;
|
||||
return pPostIt->Set( szText );
|
||||
}
|
||||
|
||||
BOOL CPostIt::Flush( void )
|
||||
{
|
||||
if( m_bModified == FALSE )
|
||||
return FALSE;
|
||||
if( m_pMemoryBlock == NULL )
|
||||
return FALSE;
|
||||
UINT uDGPFormat;
|
||||
|
||||
uDGPFormat = ::RegisterClipboardFormatA( m_szClipFormatName );
|
||||
if( ::OpenClipboard( NULL ) == FALSE )
|
||||
return FALSE;
|
||||
if( ::SetClipboardData( uDGPFormat, m_pMemoryBlock->CreateHandle() ) == NULL ) {
|
||||
// DWORD dwLastError = ::GetLastError();
|
||||
m_pMemoryBlock->DestroyHandle();
|
||||
::CloseClipboard();
|
||||
m_bModified = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
::CloseClipboard();
|
||||
m_bModified = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CPostIt::Empty( void )
|
||||
{
|
||||
SAFE_DELETE( m_pMemoryBlock );
|
||||
|
||||
UINT uDGPFormat;
|
||||
|
||||
uDGPFormat = ::RegisterClipboardFormatA( m_szClipFormatName );
|
||||
if( ::OpenClipboard( NULL ) == FALSE )
|
||||
return;
|
||||
HANDLE hClipboardMemory = ::GetClipboardData( uDGPFormat );
|
||||
if( hClipboardMemory ) {
|
||||
// ::GlobalFree( hClipboardMemory );
|
||||
::SetClipboardData( uDGPFormat, NULL );
|
||||
}
|
||||
::CloseClipboard();
|
||||
|
||||
m_bModified = FALSE;
|
||||
}
|
||||
|
||||
void CPostIt::Destroy( void )
|
||||
{
|
||||
Flush();
|
||||
SAFE_DELETE( m_pMemoryBlock );
|
||||
}
|
||||
|
||||
BOOL CPostIt::Set( LPCSTR lpszKeyName, LPCSTR lpBuffer )
|
||||
{
|
||||
if( m_pMemoryBlock == NULL )
|
||||
m_pMemoryBlock = new _CPostItMemoryBlock;
|
||||
m_pMemoryBlock->Put( lpszKeyName, lpBuffer );
|
||||
m_bModified = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CPostIt::Set( LPCSTR lpszKeyName, DWORD dwValue )
|
||||
{
|
||||
CHAR szValue[12];
|
||||
|
||||
_snprintf( szValue, 12, "%d", dwValue );
|
||||
return Set( lpszKeyName, szValue );
|
||||
}
|
||||
|
||||
BOOL CPostIt::Set( LPCSTR lpBuffer )
|
||||
{
|
||||
if( lpBuffer == NULL )
|
||||
return FALSE;
|
||||
if( m_pMemoryBlock == NULL )
|
||||
m_pMemoryBlock = new _CPostItMemoryBlock;
|
||||
m_pMemoryBlock->Put( lpBuffer );
|
||||
m_bModified = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CPostIt::Get( LPCSTR lpszKeyName, LPSTR lpBuffer, DWORD nSize )
|
||||
{
|
||||
if( m_pMemoryBlock == NULL ) {
|
||||
UINT uDGPFormat;
|
||||
|
||||
uDGPFormat = ::RegisterClipboardFormatA( m_szClipFormatName );
|
||||
if( ::OpenClipboard( NULL ) == FALSE )
|
||||
return FALSE;
|
||||
|
||||
HANDLE hClipboardMemory = ::GetClipboardData( uDGPFormat );
|
||||
|
||||
if( hClipboardMemory == NULL ) {
|
||||
::CloseClipboard();
|
||||
return FALSE;
|
||||
}
|
||||
m_pMemoryBlock = new _CPostItMemoryBlock;
|
||||
m_pMemoryBlock->Assign( hClipboardMemory );
|
||||
|
||||
::CloseClipboard();
|
||||
}
|
||||
|
||||
return m_pMemoryBlock->Get( lpszKeyName, lpBuffer, nSize );
|
||||
}
|
||||
|
||||
_CPostItMemoryBlock::_CPostItMemoryBlock( void ) : m_hHandle( NULL )
|
||||
{
|
||||
}
|
||||
|
||||
_CPostItMemoryBlock::~_CPostItMemoryBlock( void )
|
||||
{
|
||||
for( StrListItr itr = m_StrList.begin(); itr != m_StrList.end(); ) {
|
||||
LPSTR lpszText = *itr;
|
||||
SAFE_DELETE_ARRAY( lpszText );
|
||||
itr = m_StrList.erase( itr );
|
||||
}
|
||||
}
|
||||
|
||||
BOOL _CPostItMemoryBlock::Assign( HANDLE hBlock )
|
||||
{
|
||||
if( hBlock == NULL || hBlock == INVALID_HANDLE_VALUE )
|
||||
return FALSE;
|
||||
LPBYTE lpBuffer = (LPBYTE) ::GlobalLock( hBlock );
|
||||
|
||||
if( lpBuffer == NULL )
|
||||
return FALSE;
|
||||
DWORD dwCount = *((LPDWORD) lpBuffer); lpBuffer += sizeof( DWORD );
|
||||
for( DWORD dwI=0; dwI < dwCount; dwI++ ) {
|
||||
WORD wLen = *((LPWORD) lpBuffer); lpBuffer += sizeof( WORD );
|
||||
|
||||
LPSTR lpszText = new CHAR[ wLen + 1 ];
|
||||
::CopyMemory( lpszText, lpBuffer, wLen );
|
||||
lpszText[ wLen ] = '\0';
|
||||
|
||||
lpBuffer += wLen;
|
||||
|
||||
Put( lpszText );
|
||||
}
|
||||
::GlobalUnlock( hBlock );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HANDLE _CPostItMemoryBlock::CreateHandle( void )
|
||||
{
|
||||
if( m_StrList.size() == 0 )
|
||||
return INVALID_HANDLE_VALUE;
|
||||
DWORD dwBlockSize = sizeof( DWORD );
|
||||
StrListItr itr;
|
||||
|
||||
// Calculation for Memory Block Size
|
||||
for( itr = m_StrList.begin(); itr != m_StrList.end(); ++itr ) {
|
||||
dwBlockSize += sizeof( WORD );
|
||||
dwBlockSize += (DWORD) strlen( *itr );
|
||||
}
|
||||
|
||||
HANDLE hBlock = ::GlobalAlloc( GMEM_ZEROINIT | GMEM_MOVEABLE, dwBlockSize );
|
||||
if( hBlock == NULL )
|
||||
return INVALID_HANDLE_VALUE;
|
||||
LPBYTE lpBuffer = (LPBYTE) ::GlobalLock( hBlock );
|
||||
if( lpBuffer == NULL ) {
|
||||
::GlobalFree( hBlock );
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
*((LPDWORD) lpBuffer) = (DWORD) m_StrList.size(); lpBuffer += sizeof( DWORD );
|
||||
for( itr = m_StrList.begin(); itr != m_StrList.end(); ++itr ) {
|
||||
*((LPWORD) lpBuffer) = (WORD) strlen( *itr ); lpBuffer += sizeof( WORD );
|
||||
::CopyMemory( lpBuffer, *itr, strlen( *itr ) ); lpBuffer += strlen( *itr );
|
||||
}
|
||||
::GlobalUnlock( hBlock );
|
||||
|
||||
m_hHandle = hBlock;
|
||||
return hBlock;
|
||||
}
|
||||
|
||||
BOOL _CPostItMemoryBlock::DestroyHandle( void )
|
||||
{
|
||||
::GlobalFree( m_hHandle );
|
||||
m_hHandle = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
LPSTR _CPostItMemoryBlock::Find( LPCSTR lpszKeyName )
|
||||
{
|
||||
for( StrListItr itr = m_StrList.begin(); itr != m_StrList.end(); ++itr ) {
|
||||
LPSTR lpszText = *itr;
|
||||
|
||||
if( _strnicmp( lpszText, lpszKeyName, strlen( lpszKeyName ) ) != 0 )
|
||||
continue;
|
||||
if( *(lpszText + strlen( lpszKeyName )) != '=' )
|
||||
continue;
|
||||
return lpszText;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL _CPostItMemoryBlock::Put( LPCSTR lpszKeyName, LPCSTR lpBuffer )
|
||||
{
|
||||
LPSTR lpszText;
|
||||
|
||||
if( (lpszText = Find( lpszKeyName )) != NULL ) {
|
||||
for( StrListItr itr = m_StrList.begin(); itr != m_StrList.end(); ++itr ) {
|
||||
if( lpszText == *itr ) {
|
||||
SAFE_DELETE_ARRAY( lpszText );
|
||||
m_StrList.erase( itr );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( lpBuffer == NULL || !*lpBuffer )
|
||||
return TRUE;
|
||||
size_t nStrLen = strlen( lpszKeyName ) + 1 /* '=' */ + strlen( lpBuffer );
|
||||
lpszText = new CHAR[ nStrLen + 1 ];
|
||||
::CopyMemory( lpszText, lpszKeyName, strlen( lpszKeyName ) );
|
||||
*(lpszText + strlen( lpszKeyName )) = '=';
|
||||
::CopyMemory( lpszText + strlen( lpszKeyName ) + 1, lpBuffer, strlen( lpBuffer ) );
|
||||
*(lpszText + nStrLen) = '\0';
|
||||
|
||||
m_StrList.push_back( lpszText );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL _CPostItMemoryBlock::Put( LPCSTR lpBuffer )
|
||||
{
|
||||
LPSTR lpszText;
|
||||
|
||||
if( lpBuffer == NULL || !*lpBuffer )
|
||||
return TRUE;
|
||||
size_t nStrLen = strlen( lpBuffer );
|
||||
lpszText = new CHAR[ nStrLen + 1 ];
|
||||
::CopyMemory( lpszText, lpBuffer, nStrLen );
|
||||
*(lpszText + nStrLen) = '\0';
|
||||
|
||||
m_StrList.push_back( lpszText );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL _CPostItMemoryBlock::Get( LPCSTR lpszKeyName, LPSTR lpBuffer, DWORD nSize )
|
||||
{
|
||||
LPSTR lpszText = Find( lpszKeyName );
|
||||
if( lpszText == NULL )
|
||||
return FALSE;
|
||||
lpszText += (strlen( lpszKeyName ) + 1);
|
||||
::ZeroMemory( lpBuffer, nSize );
|
||||
strncpy( lpBuffer, lpszText, (nSize < strlen( lpszText )) ? nSize : strlen( lpszText ) );
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#ifndef _EL_CPOSTIT_H_
|
||||
#define _EL_CPOSTIT_H_
|
||||
|
||||
// _CPostItMemoryBlock is defined in CPostIt.cpp
|
||||
class _CPostItMemoryBlock;
|
||||
|
||||
/**
|
||||
* @class CPostIt
|
||||
* @brief 게임런처에서 게임 클라이언트로 정보를 전달 및 클라이언트에서 수신하기 위하여 사용되는 클래스
|
||||
*/
|
||||
class CPostIt
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief CPostIt constructor
|
||||
* @param [in] szAppName : 게임의 이름이 들어간다.
|
||||
*/
|
||||
explicit CPostIt( LPCSTR szAppName );
|
||||
|
||||
/**
|
||||
* @brief CPostIt destructor
|
||||
*/
|
||||
~CPostIt( void );
|
||||
|
||||
/**
|
||||
* @brief CPostIt class에서 보유하고 있는 데이타를 클립보드에 저장한다.
|
||||
*/
|
||||
BOOL Flush( void );
|
||||
|
||||
/**
|
||||
* @brief CPostIt class에서 보유하고 있는 데이타 및 클립보드에 있는 내용을 지운다.
|
||||
*/
|
||||
void Empty( void );
|
||||
|
||||
/**
|
||||
* @brief 데이타를 읽어온다.
|
||||
* @param [in] lpszKeyName : 불러올 데이타의 키. "KEY" 식의 내용을 넣는다.
|
||||
* @param [in] lpszData : 불러올 데이타의 버퍼
|
||||
* @param [in] nSize : lpszData 버퍼의 최대사이즈
|
||||
*/
|
||||
BOOL Get( LPCSTR lpszKeyName, LPSTR lpszData, DWORD nSize );
|
||||
|
||||
/**
|
||||
* @brief 저장할 데이타를 넣는다.
|
||||
* @param [in] lpBuffer : 저장할 데이타. "KEY=DATA" 식의 내용을 넣는다.
|
||||
*/
|
||||
BOOL Set( LPCSTR lpszData );
|
||||
|
||||
/**
|
||||
* @brief 저장할 데이타를 넣는다.
|
||||
* @param [in] lpszKeyName : 저장할 데이타의 키. "KEY" 식의 내용을 넣는다.
|
||||
* @param [in] lpszData : 저장할 데이타. "DATA" 식의 내용을 넣는다.
|
||||
*/
|
||||
BOOL Set( LPCSTR lpszKeyName, LPCSTR lpszData );
|
||||
|
||||
/**
|
||||
* @brief 저장할 데이타(DWORD)를 넣는다.
|
||||
* @param [in] lpBuffer : 저장할 데이타. "KEY=DATA" 식의 데이타를 넣는다.
|
||||
* @param [in] dwValue : 저장할 데이타. (DWORD)
|
||||
*/
|
||||
BOOL Set( LPCSTR lpszKeyName, DWORD dwValue );
|
||||
|
||||
/**
|
||||
* @brief CPostIt class를 복사한다. (클래스 constructor에 이름 인자가 있기 때문에, 새 이름을 지정해야함)
|
||||
* @param [in] pPostIt : Destination class
|
||||
* @param [in] lpszKeyName : Destination class's new app-name
|
||||
*/
|
||||
BOOL CopyTo( CPostIt *pPostIt, LPCSTR lpszKeyName );
|
||||
|
||||
protected:
|
||||
BOOL Init( LPCSTR szAppName );
|
||||
void Destroy( void );
|
||||
|
||||
protected:
|
||||
BOOL m_bModified;
|
||||
CHAR m_szClipFormatName[_MAX_PATH];
|
||||
_CPostItMemoryBlock* m_pMemoryBlock;
|
||||
};
|
||||
|
||||
#endif /* _EL_CPOSTIT_H_ */
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "StdAfx.h"
|
||||
#include "CRC32.h"
|
||||
|
||||
#include <utf8.h>
|
||||
|
||||
static unsigned long CRCTable[256] =
|
||||
{
|
||||
0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,
|
||||
@@ -147,65 +149,55 @@ DWORD GetHFILECRC32(HANDLE hFile)
|
||||
dataOffset + mapSize, // low
|
||||
NULL); // name
|
||||
if (hFM)
|
||||
{
|
||||
LPVOID lpMapData = MapViewOfFile(hFM,
|
||||
FILE_MAP_READ,
|
||||
0,
|
||||
dwFileMapStart,
|
||||
dwMapViewSize);
|
||||
|
||||
dwRetCRC32=GetCRC32((const char*)lpMapData, dwFileSize);
|
||||
|
||||
|
||||
{
|
||||
LPVOID lpMapData = MapViewOfFile(hFM, FILE_MAP_READ, 0, dwFileMapStart, dwMapViewSize);
|
||||
if (lpMapData)
|
||||
{
|
||||
|
||||
//tw1x1: If MapViewOfFile returns null, crash risk on mapping failure
|
||||
dwRetCRC32 = GetCRC32((const char*)lpMapData, dwFileSize);
|
||||
UnmapViewOfFile(lpMapData);
|
||||
}
|
||||
|
||||
CloseHandle(hFM);
|
||||
}
|
||||
|
||||
return dwRetCRC32;
|
||||
}
|
||||
|
||||
DWORD GetFileCRC32(const char* c_szFileName)
|
||||
DWORD GetFileCRC32(const wchar_t* c_szFileName)
|
||||
{
|
||||
HANDLE hFile = CreateFile(c_szFileName, // name of the file
|
||||
GENERIC_READ, // desired access
|
||||
FILE_SHARE_READ, // share mode
|
||||
NULL, // security attributes
|
||||
OPEN_EXISTING, // creation disposition
|
||||
FILE_ATTRIBUTE_NORMAL, // flags and attr
|
||||
NULL); // template file
|
||||
|
||||
if (INVALID_HANDLE_VALUE == hFile)
|
||||
if (!c_szFileName || !*c_szFileName)
|
||||
return 0;
|
||||
|
||||
|
||||
DWORD dwRetCRC32=GetHFILECRC32(hFile);
|
||||
|
||||
CloseHandle(hFile);
|
||||
HANDLE hFile = CreateFileW(c_szFileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
|
||||
DWORD dwRetCRC32 = GetHFILECRC32(hFile);
|
||||
CloseHandle(hFile);
|
||||
return dwRetCRC32;
|
||||
}
|
||||
|
||||
DWORD GetFileCRC32(const char* fileUtf8)
|
||||
{
|
||||
if (!fileUtf8 || !*fileUtf8)
|
||||
return 0;
|
||||
|
||||
std::wstring wFile = Utf8ToWide(fileUtf8);
|
||||
return GetFileCRC32(wFile.c_str());
|
||||
}
|
||||
|
||||
DWORD GetFileSize(const char* c_szFileName)
|
||||
{
|
||||
HANDLE hFile = CreateFile(c_szFileName, // name of the file
|
||||
GENERIC_READ, // desired access
|
||||
FILE_SHARE_READ, // share mode
|
||||
NULL, // security attributes
|
||||
OPEN_EXISTING, // creation disposition
|
||||
FILE_ATTRIBUTE_NORMAL, // flags and attr
|
||||
NULL); // template file
|
||||
|
||||
if (INVALID_HANDLE_VALUE == hFile)
|
||||
if (!c_szFileName || !*c_szFileName)
|
||||
return 0;
|
||||
|
||||
DWORD dwSize = GetFileSize(hFile, NULL);
|
||||
std::wstring wFile = Utf8ToWide(std::string(c_szFileName));
|
||||
HANDLE hFile = CreateFileW(wFile.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
|
||||
DWORD dwSize = ::GetFileSize(hFile, nullptr);
|
||||
CloseHandle(hFile);
|
||||
|
||||
return dwSize;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
DWORD GetCRC32(const char* buffer, size_t count);
|
||||
DWORD GetCaseCRC32(const char * buf, size_t len);
|
||||
DWORD GetHFILECRC32(HANDLE hFile);
|
||||
DWORD GetFileCRC32(const char* c_szFileName);
|
||||
DWORD GetFileCRC32(const wchar_t* c_szFileName);
|
||||
DWORD GetFileCRC32(const char* fileUtf8);
|
||||
DWORD GetFileSize(const char* c_szFileName);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,237 +2,266 @@
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
|
||||
#include "Debug.h"
|
||||
#include "Singleton.h"
|
||||
#include "Timer.h"
|
||||
#include <filesystem>
|
||||
#include <utf8.h>
|
||||
|
||||
const DWORD DEBUG_STRING_MAX_LEN = 1024;
|
||||
|
||||
static int isLogFile = false;
|
||||
HWND g_PopupHwnd = NULL;
|
||||
|
||||
// Convert UTF-8 char* -> wide and send to debugger (NO helper function, just a macro)
|
||||
#ifdef _DEBUG
|
||||
#define DBG_OUT_W_UTF8(psz) \
|
||||
do { \
|
||||
const char* __s = (psz) ? (psz) : ""; \
|
||||
std::wstring __w = Utf8ToWide(__s); \
|
||||
OutputDebugStringW(__w.c_str()); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DBG_OUT_W_UTF8(psz) do { (void)(psz); } while (0)
|
||||
#endif
|
||||
|
||||
class CLogFile : public CSingleton<CLogFile>
|
||||
{
|
||||
public:
|
||||
CLogFile() : m_fp(NULL)
|
||||
{
|
||||
}
|
||||
public:
|
||||
CLogFile() : m_fp(NULL) {}
|
||||
|
||||
virtual ~CLogFile()
|
||||
{
|
||||
if (m_fp)
|
||||
fclose(m_fp);
|
||||
virtual ~CLogFile()
|
||||
{
|
||||
if (m_fp)
|
||||
fclose(m_fp);
|
||||
|
||||
m_fp = NULL;
|
||||
}
|
||||
m_fp = NULL;
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
m_fp = fopen("log.txt", "w");
|
||||
}
|
||||
void Initialize()
|
||||
{
|
||||
m_fp = fopen("log/log.txt", "w");
|
||||
}
|
||||
|
||||
void Write(const char * c_pszMsg)
|
||||
{
|
||||
if (!m_fp)
|
||||
return;
|
||||
void Write(const char* c_pszMsg)
|
||||
{
|
||||
if (!m_fp)
|
||||
return;
|
||||
|
||||
time_t ct = time(0);
|
||||
struct tm ctm = *localtime(&ct);
|
||||
time_t ct = time(0);
|
||||
struct tm ctm = *localtime(&ct);
|
||||
|
||||
fprintf(m_fp, "%02d%02d %02d:%02d:%05d :: %s",
|
||||
ctm.tm_mon + 1,
|
||||
ctm.tm_mday,
|
||||
ctm.tm_hour,
|
||||
ctm.tm_min,
|
||||
ELTimer_GetMSec() % 60000,
|
||||
c_pszMsg);
|
||||
fprintf(m_fp, "%02d%02d %02d:%02d:%05d :: %s",
|
||||
ctm.tm_mon + 1,
|
||||
ctm.tm_mday,
|
||||
ctm.tm_hour,
|
||||
ctm.tm_min,
|
||||
ELTimer_GetMSec() % 60000,
|
||||
c_pszMsg);
|
||||
|
||||
fflush(m_fp);
|
||||
}
|
||||
fflush(m_fp);
|
||||
}
|
||||
|
||||
protected:
|
||||
FILE * m_fp;
|
||||
protected:
|
||||
FILE* m_fp;
|
||||
};
|
||||
|
||||
static CLogFile gs_logfile;
|
||||
|
||||
static UINT gs_uLevel=0;
|
||||
static UINT gs_uLevel = 0;
|
||||
|
||||
void SetLogLevel(UINT uLevel)
|
||||
{
|
||||
gs_uLevel=uLevel;
|
||||
gs_uLevel = uLevel;
|
||||
}
|
||||
|
||||
void Log(UINT uLevel, const char* c_szMsg)
|
||||
{
|
||||
if (uLevel>=gs_uLevel)
|
||||
Trace(c_szMsg);
|
||||
if (uLevel >= gs_uLevel)
|
||||
Trace(c_szMsg);
|
||||
}
|
||||
|
||||
void Logn(UINT uLevel, const char* c_szMsg)
|
||||
{
|
||||
if (uLevel>=gs_uLevel)
|
||||
Tracen(c_szMsg);
|
||||
if (uLevel >= gs_uLevel)
|
||||
Tracen(c_szMsg);
|
||||
}
|
||||
|
||||
void Logf(UINT uLevel, const char* c_szFormat, ...)
|
||||
{
|
||||
if (uLevel<gs_uLevel)
|
||||
return;
|
||||
|
||||
char szBuf[DEBUG_STRING_MAX_LEN+1];
|
||||
if (uLevel < gs_uLevel)
|
||||
return;
|
||||
|
||||
char szBuf[DEBUG_STRING_MAX_LEN + 1];
|
||||
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
_vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args);
|
||||
va_end(args);
|
||||
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
_vsnprintf(szBuf, sizeof(szBuf), c_szFormat, args);
|
||||
va_end(args);
|
||||
#ifdef _DEBUG
|
||||
OutputDebugString(szBuf);
|
||||
fputs(szBuf, stdout);
|
||||
DBG_OUT_W_UTF8(szBuf);
|
||||
fputs(szBuf, stdout);
|
||||
#endif
|
||||
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
}
|
||||
|
||||
void Lognf(UINT uLevel, const char* c_szFormat, ...)
|
||||
{
|
||||
if (uLevel<gs_uLevel)
|
||||
return;
|
||||
if (uLevel < gs_uLevel)
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
char szBuf[DEBUG_STRING_MAX_LEN + 2];
|
||||
|
||||
char szBuf[DEBUG_STRING_MAX_LEN+2];
|
||||
int len = _vsnprintf(szBuf, sizeof(szBuf)-1, c_szFormat, args);
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
_vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args);
|
||||
va_end(args);
|
||||
|
||||
size_t cur = strnlen(szBuf, sizeof(szBuf));
|
||||
if (cur + 1 < sizeof(szBuf)) {
|
||||
szBuf[cur] = '\n';
|
||||
szBuf[cur + 1] = '\0';
|
||||
}
|
||||
else {
|
||||
szBuf[sizeof(szBuf) - 2] = '\n';
|
||||
szBuf[sizeof(szBuf) - 1] = '\0';
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
szBuf[len] = '\n';
|
||||
szBuf[len + 1] = '\0';
|
||||
}
|
||||
va_end(args);
|
||||
#ifdef _DEBUG
|
||||
OutputDebugString(szBuf);
|
||||
puts(szBuf);
|
||||
DBG_OUT_W_UTF8(szBuf);
|
||||
fputs(szBuf, stdout);
|
||||
#endif
|
||||
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
}
|
||||
|
||||
|
||||
void Trace(const char * c_szMsg)
|
||||
void Trace(const char* c_szMsg)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
OutputDebugString(c_szMsg);
|
||||
printf("%s", c_szMsg);
|
||||
DBG_OUT_W_UTF8(c_szMsg);
|
||||
printf("%s", c_szMsg ? c_szMsg : "");
|
||||
#endif
|
||||
|
||||
if (isLogFile)
|
||||
LogFile(c_szMsg);
|
||||
if (isLogFile)
|
||||
LogFile(c_szMsg ? c_szMsg : "");
|
||||
}
|
||||
|
||||
void Tracen(const char* c_szMsg)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
char szBuf[DEBUG_STRING_MAX_LEN+1];
|
||||
_snprintf(szBuf, sizeof(szBuf), "%s\n", c_szMsg);
|
||||
OutputDebugString(szBuf);
|
||||
puts(c_szMsg);
|
||||
char szBuf[DEBUG_STRING_MAX_LEN + 2];
|
||||
_snprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, "%s\n", c_szMsg ? c_szMsg : "");
|
||||
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
DBG_OUT_W_UTF8(szBuf);
|
||||
|
||||
puts(c_szMsg);
|
||||
putc('\n', stdout);
|
||||
fputs(szBuf, stdout);
|
||||
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
#else
|
||||
if (isLogFile)
|
||||
{
|
||||
LogFile(c_szMsg);
|
||||
LogFile("\n");
|
||||
}
|
||||
if (isLogFile)
|
||||
{
|
||||
LogFile(c_szMsg ? c_szMsg : "");
|
||||
LogFile("\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Tracenf(const char* c_szFormat, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
char szBuf[DEBUG_STRING_MAX_LEN + 2];
|
||||
|
||||
char szBuf[DEBUG_STRING_MAX_LEN+2];
|
||||
int len = _vsnprintf(szBuf, sizeof(szBuf)-1, c_szFormat, args);
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
_vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args);
|
||||
va_end(args);
|
||||
|
||||
size_t cur = strnlen(szBuf, sizeof(szBuf));
|
||||
if (cur + 1 < sizeof(szBuf)) {
|
||||
szBuf[cur] = '\n';
|
||||
szBuf[cur + 1] = '\0';
|
||||
}
|
||||
else {
|
||||
szBuf[sizeof(szBuf) - 2] = '\n';
|
||||
szBuf[sizeof(szBuf) - 1] = '\0';
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
szBuf[len] = '\n';
|
||||
szBuf[len + 1] = '\0';
|
||||
}
|
||||
va_end(args);
|
||||
#ifdef _DEBUG
|
||||
OutputDebugString(szBuf);
|
||||
printf("%s", szBuf);
|
||||
DBG_OUT_W_UTF8(szBuf);
|
||||
fputs(szBuf, stdout);
|
||||
#endif
|
||||
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
}
|
||||
|
||||
void Tracef(const char* c_szFormat, ...)
|
||||
{
|
||||
char szBuf[DEBUG_STRING_MAX_LEN+1];
|
||||
char szBuf[DEBUG_STRING_MAX_LEN + 1];
|
||||
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
_vsnprintf(szBuf, sizeof(szBuf), c_szFormat, args);
|
||||
va_end(args);
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
_vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args);
|
||||
va_end(args);
|
||||
|
||||
#ifdef _DEBUG
|
||||
OutputDebugString(szBuf);
|
||||
fputs(szBuf, stdout);
|
||||
DBG_OUT_W_UTF8(szBuf);
|
||||
fputs(szBuf, stdout);
|
||||
#endif
|
||||
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
}
|
||||
|
||||
void TraceError(const char* c_szFormat, ...)
|
||||
{
|
||||
#ifndef _DISTRIBUTE
|
||||
char szBuf[DEBUG_STRING_MAX_LEN + 2];
|
||||
|
||||
char szBuf[DEBUG_STRING_MAX_LEN+2];
|
||||
strncpy_s(szBuf, sizeof(szBuf), "SYSERR: ", _TRUNCATE);
|
||||
int prefixLen = (int)strlen(szBuf);
|
||||
|
||||
strncpy(szBuf, "SYSERR: ", DEBUG_STRING_MAX_LEN);
|
||||
int len = strlen(szBuf);
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
_vsnprintf_s(szBuf + prefixLen, sizeof(szBuf) - prefixLen, _TRUNCATE, c_szFormat, args);
|
||||
va_end(args);
|
||||
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
len = _vsnprintf(szBuf + len, sizeof(szBuf) - (len + 1), c_szFormat, args) + len;
|
||||
va_end(args);
|
||||
size_t cur = strnlen(szBuf, sizeof(szBuf));
|
||||
if (cur + 1 < sizeof(szBuf)) {
|
||||
szBuf[cur] = '\n';
|
||||
szBuf[cur + 1] = '\0';
|
||||
}
|
||||
else {
|
||||
szBuf[sizeof(szBuf) - 2] = '\n';
|
||||
szBuf[sizeof(szBuf) - 1] = '\0';
|
||||
}
|
||||
|
||||
szBuf[len] = '\n';
|
||||
szBuf[len + 1] = '\0';
|
||||
time_t ct = time(0);
|
||||
struct tm ctm = *localtime(&ct);
|
||||
|
||||
time_t ct = time(0);
|
||||
struct tm ctm = *localtime(&ct);
|
||||
fprintf(stderr, "%02d%02d %02d:%02d:%05d :: %s",
|
||||
ctm.tm_mon + 1,
|
||||
ctm.tm_mday,
|
||||
ctm.tm_hour,
|
||||
ctm.tm_min,
|
||||
ELTimer_GetMSec() % 60000,
|
||||
szBuf + 8);
|
||||
fflush(stderr);
|
||||
|
||||
fprintf(stderr, "%02d%02d %02d:%02d:%05d :: %s",
|
||||
ctm.tm_mon + 1,
|
||||
ctm.tm_mday,
|
||||
ctm.tm_hour,
|
||||
ctm.tm_min,
|
||||
ELTimer_GetMSec() % 60000,
|
||||
szBuf + 8);
|
||||
fflush(stderr);
|
||||
|
||||
#ifdef _DEBUG
|
||||
OutputDebugString(szBuf);
|
||||
fputs(szBuf, stdout);
|
||||
DBG_OUT_W_UTF8(szBuf);
|
||||
fputs(szBuf, stdout);
|
||||
#endif
|
||||
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -240,87 +269,101 @@ void TraceErrorWithoutEnter(const char* c_szFormat, ...)
|
||||
{
|
||||
#ifndef _DISTRIBUTE
|
||||
|
||||
char szBuf[DEBUG_STRING_MAX_LEN];
|
||||
char szBuf[DEBUG_STRING_MAX_LEN];
|
||||
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
_vsnprintf(szBuf, sizeof(szBuf), c_szFormat, args);
|
||||
va_end(args);
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
_vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args);
|
||||
va_end(args);
|
||||
|
||||
time_t ct = time(0);
|
||||
struct tm ctm = *localtime(&ct);
|
||||
time_t ct = time(0);
|
||||
struct tm ctm = *localtime(&ct);
|
||||
|
||||
fprintf(stderr, "%02d%02d %02d:%02d:%05d :: %s",
|
||||
ctm.tm_mon + 1,
|
||||
ctm.tm_mday,
|
||||
ctm.tm_hour,
|
||||
ctm.tm_min,
|
||||
ELTimer_GetMSec() % 60000,
|
||||
szBuf + 8);
|
||||
fflush(stderr);
|
||||
fprintf(stderr, "%02d%02d %02d:%02d:%05d :: %s",
|
||||
ctm.tm_mon + 1,
|
||||
ctm.tm_mday,
|
||||
ctm.tm_hour,
|
||||
ctm.tm_min,
|
||||
ELTimer_GetMSec() % 60000,
|
||||
szBuf + 8);
|
||||
fflush(stderr);
|
||||
|
||||
#ifdef _DEBUG
|
||||
OutputDebugString(szBuf);
|
||||
fputs(szBuf, stdout);
|
||||
DBG_OUT_W_UTF8(szBuf);
|
||||
fputs(szBuf, stdout);
|
||||
#endif
|
||||
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
if (isLogFile)
|
||||
LogFile(szBuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LogBoxf(const char* c_szFormat, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
va_list args;
|
||||
va_start(args, c_szFormat);
|
||||
|
||||
char szBuf[2048];
|
||||
_vsnprintf(szBuf, sizeof(szBuf), c_szFormat, args);
|
||||
char szBuf[2048];
|
||||
_vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szFormat, args);
|
||||
|
||||
LogBox(szBuf);
|
||||
va_end(args);
|
||||
|
||||
LogBox(szBuf);
|
||||
}
|
||||
|
||||
void LogBox(const char* c_szMsg, const char * c_szCaption, HWND hWnd)
|
||||
void LogBox(const char* c_szMsg, const char* c_szCaption, HWND hWnd)
|
||||
{
|
||||
if (!hWnd)
|
||||
hWnd = g_PopupHwnd;
|
||||
if (!hWnd)
|
||||
hWnd = g_PopupHwnd;
|
||||
|
||||
MessageBox(hWnd, c_szMsg, c_szCaption ? c_szCaption : "LOG", MB_OK);
|
||||
Tracen(c_szMsg);
|
||||
std::wstring wMsg = Utf8ToWide(c_szMsg ? c_szMsg : "");
|
||||
std::wstring wCaption = Utf8ToWide(c_szCaption ? c_szCaption : "LOG");
|
||||
|
||||
MessageBoxW(hWnd, wMsg.c_str(), wCaption.c_str(), MB_OK);
|
||||
|
||||
// Logging stays UTF-8
|
||||
Tracen(c_szMsg ? c_szMsg : "");
|
||||
}
|
||||
|
||||
void LogFile(const char * c_szMsg)
|
||||
void LogFile(const char* c_szMsg)
|
||||
{
|
||||
CLogFile::Instance().Write(c_szMsg);
|
||||
CLogFile::Instance().Write(c_szMsg);
|
||||
}
|
||||
|
||||
void LogFilef(const char * c_szMessage, ...)
|
||||
void LogFilef(const char* c_szMessage, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, c_szMessage);
|
||||
char szBuf[DEBUG_STRING_MAX_LEN+1];
|
||||
_vsnprintf(szBuf, sizeof(szBuf), c_szMessage, args);
|
||||
va_list args;
|
||||
va_start(args, c_szMessage);
|
||||
|
||||
CLogFile::Instance().Write(szBuf);
|
||||
char szBuf[DEBUG_STRING_MAX_LEN + 1];
|
||||
_vsnprintf_s(szBuf, sizeof(szBuf), _TRUNCATE, c_szMessage, args);
|
||||
|
||||
va_end(args);
|
||||
|
||||
CLogFile::Instance().Write(szBuf);
|
||||
}
|
||||
|
||||
void OpenLogFile(bool bUseLogFIle)
|
||||
{
|
||||
#ifndef _DISTRIBUTE
|
||||
freopen("syserr.txt", "w", stderr);
|
||||
if (!std::filesystem::exists("log")) {
|
||||
std::filesystem::create_directory("log");
|
||||
}
|
||||
|
||||
if (bUseLogFIle)
|
||||
{
|
||||
isLogFile = true;
|
||||
CLogFile::Instance().Initialize();
|
||||
}
|
||||
#ifndef _DISTRIBUTE
|
||||
_wfreopen(L"log/syserr.txt", L"w", stderr);
|
||||
|
||||
if (bUseLogFIle)
|
||||
{
|
||||
isLogFile = true;
|
||||
CLogFile::Instance().Initialize();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenConsoleWindow()
|
||||
{
|
||||
AllocConsole();
|
||||
AllocConsole();
|
||||
|
||||
freopen("CONOUT$", "a", stdout);
|
||||
freopen("CONIN$", "r", stdin);
|
||||
_wfreopen(L"CONOUT$", L"a", stdout);
|
||||
_wfreopen(L"CONIN$", L"r", stdin);
|
||||
}
|
||||
|
||||
@@ -38,3 +38,9 @@ extern HWND g_PopupHwnd;
|
||||
} \
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_DEBUG)
|
||||
#define MD_ASSERT(expr) ((expr) ? true : (TraceError("MD_ASSERT('%s') failed at (%s:%d)", #expr, __FILE__, __LINE__), throw "ffs", false))
|
||||
#else
|
||||
#define MD_ASSERT(expr) ((expr) ? true : (TraceError("MD_ASSERT('%s') failed at (%s:%d)", #expr, __FILE__, __LINE__), false))
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "StdAfx.h"
|
||||
#include "FileBase.h"
|
||||
|
||||
#include <utf8.h>
|
||||
|
||||
CFileBase::CFileBase() : m_hFile(NULL), m_dwSize(0)
|
||||
{
|
||||
}
|
||||
@@ -34,9 +36,12 @@ BOOL CFileBase::Create(const char* filename, EFileMode mode)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
// Keep filename internally as UTF-8 (engine side)
|
||||
strncpy(m_filename, filename, MAX_PATH);
|
||||
m_filename[MAX_PATH - 1] = '\0';
|
||||
|
||||
DWORD dwMode, dwShareMode = FILE_SHARE_READ;
|
||||
DWORD dwMode;
|
||||
DWORD dwShareMode = FILE_SHARE_READ;
|
||||
|
||||
if (mode == FILEMODE_WRITE)
|
||||
{
|
||||
@@ -44,19 +49,26 @@ BOOL CFileBase::Create(const char* filename, EFileMode mode)
|
||||
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwMode = GENERIC_READ;
|
||||
}
|
||||
|
||||
m_hFile = CreateFile(filename, // name of the file
|
||||
dwMode, // desired access
|
||||
dwShareMode, // share mode
|
||||
NULL, // security attributes
|
||||
mode == FILEMODE_READ ? OPEN_EXISTING : OPEN_ALWAYS, // creation disposition
|
||||
FILE_ATTRIBUTE_NORMAL, // flags and attr
|
||||
NULL); // template file
|
||||
// UTF-8 -> UTF-16 conversion for WinAPI
|
||||
std::wstring wFilename = Utf8ToWide(filename);
|
||||
|
||||
m_hFile = CreateFileW(
|
||||
wFilename.c_str(), // UTF-16 path
|
||||
dwMode,
|
||||
dwShareMode,
|
||||
nullptr,
|
||||
(mode == FILEMODE_READ) ? OPEN_EXISTING : OPEN_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr
|
||||
);
|
||||
|
||||
if (m_hFile != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
m_dwSize = GetFileSize(m_hFile, NULL);
|
||||
m_dwSize = GetFileSize(m_hFile, nullptr);
|
||||
m_mode = mode;
|
||||
return true;
|
||||
}
|
||||
@@ -64,7 +76,7 @@ BOOL CFileBase::Create(const char* filename, EFileMode mode)
|
||||
/* char buf[256];
|
||||
GetCurrentDirectory(256, buf);
|
||||
DWORD dwErr = GetLastError();*/
|
||||
m_hFile = NULL;
|
||||
m_hFile = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -95,7 +107,7 @@ BOOL CFileBase::Write(const void* src, int bytes)
|
||||
{
|
||||
DWORD dwUseless;
|
||||
BOOL ret = WriteFile(m_hFile, src, bytes, &dwUseless, NULL);
|
||||
|
||||
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "StdAfx.h"
|
||||
#include "FileDir.h"
|
||||
#include <string>
|
||||
#include <utf8.h>
|
||||
|
||||
CDir::CDir()
|
||||
{
|
||||
@@ -20,43 +21,46 @@ void CDir::Destroy()
|
||||
Initialize();
|
||||
}
|
||||
|
||||
bool CDir::Create(const char * c_szFilter, const char* c_szPath, BOOL bCheckedExtension)
|
||||
bool CDir::Create(const char* c_szFilter, const char* c_szPath, BOOL bCheckedExtension)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
std::string stPath = c_szPath;
|
||||
|
||||
if (stPath.length())
|
||||
std::string stPath = c_szPath ? c_szPath : "";
|
||||
|
||||
if (!stPath.empty())
|
||||
{
|
||||
char end = stPath[stPath.length() - 1];
|
||||
|
||||
char end = stPath.back();
|
||||
if (end != '\\')
|
||||
stPath+='\\';
|
||||
stPath += '\\';
|
||||
}
|
||||
|
||||
std::string stQuery;
|
||||
stQuery += stPath;
|
||||
stQuery += "*.*";
|
||||
|
||||
m_wfd.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
m_hFind = FindFirstFile(stQuery.c_str(), &m_wfd);
|
||||
// Query: UTF-8 -> UTF-16 for WinAPI
|
||||
std::string stQueryUtf8 = stPath + "*.*";
|
||||
std::wstring stQueryW = Utf8ToWide(stQueryUtf8);
|
||||
|
||||
m_wfd.dwFileAttributes = 0;
|
||||
m_wfd.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
|
||||
m_hFind = FindFirstFileW(stQueryW.c_str(), &m_wfd);
|
||||
if (m_hFind == INVALID_HANDLE_VALUE)
|
||||
return true;
|
||||
|
||||
do
|
||||
{
|
||||
if (*m_wfd.cFileName == '.')
|
||||
// Convert filename to UTF-8 for existing logic/callbacks
|
||||
std::string fileNameUtf8 = WideToUtf8(m_wfd.cFileName);
|
||||
|
||||
if (!fileNameUtf8.empty() && fileNameUtf8[0] == '.')
|
||||
continue;
|
||||
|
||||
if (IsFolder())
|
||||
if (IsFolder())
|
||||
{
|
||||
if (!OnFolder(c_szFilter, stPath.c_str(), m_wfd.cFileName))
|
||||
if (!OnFolder(c_szFilter, stPath.c_str(), fileNameUtf8.c_str()))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char * c_szExtension = strchr(m_wfd.cFileName, '.');
|
||||
const char* c_szExtension = strchr(fileNameUtf8.c_str(), '.');
|
||||
if (!c_szExtension)
|
||||
continue;
|
||||
|
||||
@@ -65,27 +69,29 @@ bool CDir::Create(const char * c_szFilter, const char* c_szPath, BOOL bCheckedEx
|
||||
// 그전에 전 프로젝트의 CDir을 사용하는 곳에서 Extension을 "wav", "gr2" 이런식으로 넣게끔 한다. - [levites]
|
||||
if (bCheckedExtension)
|
||||
{
|
||||
std::string strFilter = c_szFilter;
|
||||
int iPos = strFilter.find_first_of(';', 0);
|
||||
std::string strFilter = c_szFilter ? c_szFilter : "";
|
||||
int iPos = (int)strFilter.find_first_of(';', 0);
|
||||
|
||||
if (iPos > 0)
|
||||
{
|
||||
std::string strFirstFilter = std::string(c_szFilter).substr(0, iPos);
|
||||
std::string strSecondFilter = std::string(c_szFilter).substr(iPos+1, strlen(c_szFilter));
|
||||
if (0 != strFirstFilter.compare(c_szExtension+1) && 0 != strSecondFilter.compare(c_szExtension+1))
|
||||
std::string first = strFilter.substr(0, iPos);
|
||||
std::string second = strFilter.substr(iPos + 1);
|
||||
|
||||
if (0 != first.compare(c_szExtension + 1) &&
|
||||
0 != second.compare(c_szExtension + 1))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 != stricmp(c_szExtension+1, c_szFilter))
|
||||
if (0 != _stricmp(c_szExtension + 1, c_szFilter))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!OnFile(stPath.c_str(), m_wfd.cFileName))
|
||||
if (!OnFile(stPath.c_str(), fileNameUtf8.c_str()))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (FindNextFile(m_hFind, &m_wfd));
|
||||
} while (FindNextFileW(m_hFind, &m_wfd));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -94,12 +100,12 @@ bool CDir::IsFolder()
|
||||
{
|
||||
if (m_wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return true;
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CDir::Initialize()
|
||||
{
|
||||
memset(&m_wfd, 0, sizeof(m_wfd));
|
||||
m_hFind = NULL;
|
||||
m_hFind = NULL;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "StdAfx.h"
|
||||
#include "FileLoader.h"
|
||||
#include <assert.h>
|
||||
#include <utf8.h>
|
||||
|
||||
CMemoryTextFileLoader::CMemoryTextFileLoader()
|
||||
{
|
||||
@@ -252,7 +253,9 @@ bool CDiskFileLoader::Open(const char* c_szFileName)
|
||||
if (!c_szFileName[0])
|
||||
return false;
|
||||
|
||||
m_fp = fopen(c_szFileName, "rb");
|
||||
// UTF-8 → UTF-16 conversion for Unicode path support
|
||||
std::wstring wFileName = Utf8ToWide(c_szFileName);
|
||||
m_fp = _wfopen(wFileName.c_str(), L"rb");
|
||||
|
||||
if (!m_fp)
|
||||
return false;
|
||||
|
||||
@@ -1,259 +0,0 @@
|
||||
#include "StdAfx.h"
|
||||
#include "MappedFile.h"
|
||||
#include "Debug.h"
|
||||
|
||||
CMappedFile::CMappedFile() :
|
||||
m_hFM(NULL),
|
||||
m_lpMapData(NULL),
|
||||
m_dataOffset(0),
|
||||
m_mapSize(0),
|
||||
m_seekPosition(0),
|
||||
m_pLZObj(NULL),
|
||||
m_pbBufLinkData(NULL),
|
||||
m_dwBufLinkSize(0),
|
||||
m_pbAppendResultDataBlock(NULL),
|
||||
m_dwAppendResultDataSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
CMappedFile::~CMappedFile()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
BOOL CMappedFile::Create(const char * filename)
|
||||
{
|
||||
Destroy();
|
||||
return CFileBase::Create(filename, FILEMODE_READ);
|
||||
}
|
||||
|
||||
BOOL CMappedFile::Create(const char * filename, const void** dest, int offset, int size)
|
||||
{
|
||||
if (!CMappedFile::Create(filename))
|
||||
return NULL;
|
||||
|
||||
int ret = Map(dest, offset, size);
|
||||
return (ret) > 0;
|
||||
}
|
||||
|
||||
LPCVOID CMappedFile::Get()
|
||||
{
|
||||
return m_lpData;
|
||||
}
|
||||
|
||||
void CMappedFile::Link(DWORD dwBufSize, const void* c_pvBufData)
|
||||
{
|
||||
m_dwBufLinkSize=dwBufSize;
|
||||
m_pbBufLinkData=(BYTE*)c_pvBufData;
|
||||
}
|
||||
|
||||
void CMappedFile::BindLZObject(CLZObject * pLZObj)
|
||||
{
|
||||
assert(m_pLZObj == NULL);
|
||||
m_pLZObj = pLZObj;
|
||||
|
||||
Link(m_pLZObj->GetSize(), m_pLZObj->GetBuffer());
|
||||
}
|
||||
|
||||
void CMappedFile::BindLZObjectWithBufferedSize(CLZObject * pLZObj)
|
||||
{
|
||||
assert(m_pLZObj == NULL);
|
||||
m_pLZObj = pLZObj;
|
||||
|
||||
Link(m_pLZObj->GetBufferSize(), m_pLZObj->GetBuffer());
|
||||
}
|
||||
|
||||
BYTE* CMappedFile::AppendDataBlock( const void* pBlock, DWORD dwBlockSize )
|
||||
{
|
||||
if( m_pbAppendResultDataBlock )
|
||||
{
|
||||
delete []m_pbAppendResultDataBlock;
|
||||
}
|
||||
|
||||
//realloc
|
||||
m_dwAppendResultDataSize = m_dwBufLinkSize+dwBlockSize;
|
||||
m_pbAppendResultDataBlock = new BYTE[m_dwAppendResultDataSize];
|
||||
|
||||
memcpy(m_pbAppendResultDataBlock, m_pbBufLinkData, m_dwBufLinkSize );
|
||||
memcpy(m_pbAppendResultDataBlock + m_dwBufLinkSize, pBlock, dwBlockSize );
|
||||
|
||||
//redirect
|
||||
Link(m_dwAppendResultDataSize, m_pbAppendResultDataBlock);
|
||||
|
||||
return m_pbAppendResultDataBlock;
|
||||
}
|
||||
|
||||
void CMappedFile::Destroy()
|
||||
{
|
||||
if (m_pLZObj) // 압축된 데이터가 이 포인터로 연결 된다
|
||||
{
|
||||
delete m_pLZObj;
|
||||
m_pLZObj = NULL;
|
||||
}
|
||||
|
||||
if (NULL != m_lpMapData)
|
||||
{
|
||||
Unmap(m_lpMapData);
|
||||
m_lpMapData = NULL;
|
||||
}
|
||||
|
||||
if (NULL != m_hFM)
|
||||
{
|
||||
CloseHandle(m_hFM);
|
||||
m_hFM = NULL;
|
||||
}
|
||||
|
||||
if( m_pbAppendResultDataBlock )
|
||||
{
|
||||
delete []m_pbAppendResultDataBlock;
|
||||
m_pbAppendResultDataBlock = NULL;
|
||||
}
|
||||
|
||||
m_dwAppendResultDataSize = 0;
|
||||
|
||||
m_pbBufLinkData = NULL;
|
||||
m_dwBufLinkSize = 0;
|
||||
|
||||
m_seekPosition = 0;
|
||||
m_dataOffset = 0;
|
||||
m_mapSize = 0;
|
||||
|
||||
CFileBase::Destroy();
|
||||
}
|
||||
|
||||
int CMappedFile::Seek(DWORD offset, int iSeekType)
|
||||
{
|
||||
switch (iSeekType)
|
||||
{
|
||||
case SEEK_TYPE_BEGIN:
|
||||
if (offset > m_dwSize)
|
||||
offset = m_dwSize;
|
||||
|
||||
m_seekPosition = offset;
|
||||
break;
|
||||
|
||||
case SEEK_TYPE_CURRENT:
|
||||
m_seekPosition = std::min(m_seekPosition + offset, Size());
|
||||
break;
|
||||
|
||||
case SEEK_TYPE_END:
|
||||
m_seekPosition = std::max(0ul, Size() - offset);
|
||||
break;
|
||||
}
|
||||
|
||||
return m_seekPosition;
|
||||
}
|
||||
|
||||
// 2004.09.16.myevan.MemoryMappedFile 98/ME 개수 제한 문제 체크
|
||||
//DWORD g_dwCount=0;
|
||||
|
||||
int CMappedFile::Map(const void **dest, int offset, int size)
|
||||
{
|
||||
m_dataOffset = offset;
|
||||
|
||||
if (size == 0)
|
||||
m_mapSize = m_dwSize;
|
||||
else
|
||||
m_mapSize = size;
|
||||
|
||||
if (m_dataOffset + m_mapSize > m_dwSize)
|
||||
return NULL;
|
||||
|
||||
SYSTEM_INFO SysInfo;
|
||||
GetSystemInfo(&SysInfo);
|
||||
DWORD dwSysGran = SysInfo.dwAllocationGranularity;
|
||||
DWORD dwFileMapStart = (m_dataOffset / dwSysGran) * dwSysGran;
|
||||
DWORD dwMapViewSize = (m_dataOffset % dwSysGran) + m_mapSize;
|
||||
INT iViewDelta = m_dataOffset - dwFileMapStart;
|
||||
|
||||
|
||||
m_hFM = CreateFileMapping(m_hFile, // handle
|
||||
NULL, // security
|
||||
PAGE_READONLY, // flProtect
|
||||
0, // high
|
||||
m_dataOffset + m_mapSize, // low
|
||||
NULL); // name
|
||||
|
||||
if (!m_hFM)
|
||||
{
|
||||
OutputDebugString("CMappedFile::Map !m_hFM\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_lpMapData = MapViewOfFile(m_hFM,
|
||||
FILE_MAP_READ,
|
||||
0,
|
||||
dwFileMapStart,
|
||||
dwMapViewSize);
|
||||
|
||||
if (!m_lpMapData) // Success
|
||||
{
|
||||
TraceError("CMappedFile::Map !m_lpMapData %lu", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 2004.09.16.myevan.MemoryMappedFile 98/ME 개수 제한 문제 체크
|
||||
//g_dwCount++;
|
||||
//Tracenf("MAPFILE %d", g_dwCount);
|
||||
|
||||
m_lpData = (char*) m_lpMapData + iViewDelta;
|
||||
*dest = (char*) m_lpData;
|
||||
m_seekPosition = 0;
|
||||
|
||||
Link(m_mapSize, m_lpData);
|
||||
|
||||
return (m_mapSize);
|
||||
}
|
||||
|
||||
BYTE * CMappedFile::GetCurrentSeekPoint()
|
||||
{
|
||||
return m_pbBufLinkData+m_seekPosition;
|
||||
//return m_pLZObj ? m_pLZObj->GetBuffer() + m_seekPosition : (BYTE *) m_lpData + m_seekPosition;
|
||||
}
|
||||
|
||||
|
||||
DWORD CMappedFile::Size()
|
||||
{
|
||||
return m_dwBufLinkSize;
|
||||
/*
|
||||
if (m_pLZObj)
|
||||
return m_pLZObj->GetSize();
|
||||
|
||||
return (m_mapSize);
|
||||
*/
|
||||
}
|
||||
|
||||
DWORD CMappedFile::GetPosition()
|
||||
{
|
||||
return m_dataOffset;
|
||||
}
|
||||
|
||||
BOOL CMappedFile::Read(void * dest, int bytes)
|
||||
{
|
||||
if (m_seekPosition + bytes > Size())
|
||||
return FALSE;
|
||||
|
||||
memcpy(dest, GetCurrentSeekPoint(), bytes);
|
||||
m_seekPosition += bytes;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DWORD CMappedFile::GetSeekPosition(void)
|
||||
{
|
||||
return m_seekPosition;
|
||||
}
|
||||
|
||||
void CMappedFile::Unmap(LPCVOID data)
|
||||
{
|
||||
if (UnmapViewOfFile(data))
|
||||
{
|
||||
// 2004.09.16.myevan.MemoryMappedFile 98/ME 개수 제한 문제 체크
|
||||
//g_dwCount--;
|
||||
//Tracenf("UNMAPFILE %d", g_dwCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceError("CMappedFile::Unmap - Error");
|
||||
}
|
||||
m_lpData = NULL;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#ifndef __INC_MAPPEDFILE_H__
|
||||
#define __INC_MAPPEDFILE_H__
|
||||
|
||||
#include "lzo.h"
|
||||
#include "FileBase.h"
|
||||
|
||||
class CMappedFile : public CFileBase
|
||||
{
|
||||
public:
|
||||
enum ESeekType
|
||||
{
|
||||
SEEK_TYPE_BEGIN,
|
||||
SEEK_TYPE_CURRENT,
|
||||
SEEK_TYPE_END
|
||||
};
|
||||
|
||||
public:
|
||||
CMappedFile();
|
||||
virtual ~CMappedFile();
|
||||
|
||||
void Link(DWORD dwBufSize, const void* c_pvBufData);
|
||||
|
||||
BOOL Create(const char* filename);
|
||||
BOOL Create(const char* filename, const void** dest, int offset, int size);
|
||||
LPCVOID Get();
|
||||
void Destroy();
|
||||
int Seek(DWORD offset, int iSeekType = SEEK_TYPE_BEGIN);
|
||||
int Map(const void **dest, int offset=0, int size=0);
|
||||
DWORD Size();
|
||||
DWORD GetPosition();
|
||||
BOOL Read(void* dest, int bytes);
|
||||
DWORD GetSeekPosition();
|
||||
void BindLZObject(CLZObject * pLZObj);
|
||||
void BindLZObjectWithBufferedSize(CLZObject * pLZObj);
|
||||
BYTE* AppendDataBlock( const void* pBlock, DWORD dwBlockSize );
|
||||
|
||||
BYTE * GetCurrentSeekPoint();
|
||||
|
||||
private:
|
||||
void Unmap(LPCVOID data);
|
||||
|
||||
private:
|
||||
BYTE* m_pbBufLinkData;
|
||||
DWORD m_dwBufLinkSize;
|
||||
|
||||
BYTE* m_pbAppendResultDataBlock;
|
||||
DWORD m_dwAppendResultDataSize;
|
||||
|
||||
DWORD m_seekPosition;
|
||||
HANDLE m_hFM;
|
||||
DWORD m_dataOffset;
|
||||
DWORD m_mapSize;
|
||||
LPVOID m_lpMapData;
|
||||
LPVOID m_lpData;
|
||||
|
||||
CLZObject * m_pLZObj;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,6 +0,0 @@
|
||||
#ifndef _EL_SERVICEDEFS_H_
|
||||
#define _EL_SERVICEDEFS_H_
|
||||
|
||||
#define _IMPROVED_PACKET_ENCRYPTION_
|
||||
|
||||
#endif //_EL_SERVICEDEFS_H_
|
||||
@@ -1,10 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#pragma warning(disable:4710) // not inlined
|
||||
#pragma warning(disable:4786) // character 255 넘어가는거 끄기
|
||||
#pragma warning(disable:4244) // type conversion possible lose of data
|
||||
@@ -53,4 +48,5 @@
|
||||
|
||||
#include "vk.h"
|
||||
#include "filename.h"
|
||||
#include "ServiceDefs.h"
|
||||
|
||||
#include "../UserInterface/Locale_inc.h"
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
#include "TempFile.h"
|
||||
#include "Utils.h"
|
||||
#include "Debug.h"
|
||||
#include <utf8.h>
|
||||
|
||||
CTempFile::~CTempFile()
|
||||
{
|
||||
Destroy();
|
||||
DeleteFile(m_szFileName);
|
||||
|
||||
if (m_szFileName[0])
|
||||
{
|
||||
std::wstring wPath = Utf8ToWide(m_szFileName);
|
||||
DeleteFileW(wPath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
CTempFile::CTempFile(const char * c_pszPrefix)
|
||||
|
||||
@@ -5,24 +5,43 @@
|
||||
#include <io.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <utf8.h>
|
||||
|
||||
#include "Utils.h"
|
||||
#include "filedir.h"
|
||||
|
||||
char korean_tolower(const char c);
|
||||
|
||||
const char * CreateTempFileName(const char * c_pszPrefix)
|
||||
const char* CreateTempFileName(const char* c_pszPrefix)
|
||||
{
|
||||
char szTempPath[MAX_PATH + 1];
|
||||
static char szTempName[MAX_PATH + 1];
|
||||
static std::string s_utf8TempName; // safe static storage
|
||||
|
||||
GetTempPath(MAX_PATH, szTempPath);
|
||||
wchar_t wTempPath[MAX_PATH + 1]{};
|
||||
wchar_t wTempName[MAX_PATH + 1]{};
|
||||
|
||||
GetTempFileName(szTempPath, // directory for temp files
|
||||
c_pszPrefix ? c_pszPrefix : "etb", // temp file name prefix
|
||||
c_pszPrefix ? true : false, // create unique name
|
||||
szTempName); // buffer for name
|
||||
// Get temp directory
|
||||
if (!GetTempPathW(MAX_PATH, wTempPath))
|
||||
return "";
|
||||
|
||||
return (szTempName);
|
||||
// Prefix must be wide
|
||||
wchar_t wPrefix[4] = L"etb";
|
||||
if (c_pszPrefix && *c_pszPrefix)
|
||||
{
|
||||
std::wstring wp = Utf8ToWide(c_pszPrefix);
|
||||
wcsncpy_s(wPrefix, wp.c_str(), 3);
|
||||
}
|
||||
|
||||
// Create temp file name
|
||||
if (!GetTempFileNameW(
|
||||
wTempPath,
|
||||
wPrefix,
|
||||
0, // unique number generated by system
|
||||
wTempName))
|
||||
return "";
|
||||
|
||||
// Convert result to UTF-8 for engine
|
||||
s_utf8TempName = WideToUtf8(wTempName);
|
||||
return s_utf8TempName.c_str();
|
||||
}
|
||||
|
||||
void GetFilePathNameExtension(const char * c_szFile, int len, std::string * pstPath, std::string * pstName, std::string * pstExt)
|
||||
@@ -41,10 +60,10 @@ void GetFilePathNameExtension(const char * c_szFile, int len, std::string * pstP
|
||||
|
||||
if (ext == len && c == '.')
|
||||
{
|
||||
ext = pos;
|
||||
ext = pos;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (c == '/' || c == '\\')
|
||||
break;
|
||||
}
|
||||
@@ -83,7 +102,7 @@ void GetFileExtension(const char* c_szFile, int len, std::string* pstExt)
|
||||
char c=c_szFile[pos];
|
||||
if (ext==len && c=='.')
|
||||
{
|
||||
ext=pos;
|
||||
ext=pos;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -91,7 +110,7 @@ void GetFileExtension(const char* c_szFile, int len, std::string* pstExt)
|
||||
else if (c=='\\') break;
|
||||
}
|
||||
|
||||
++ext;
|
||||
++ext;
|
||||
if (len>ext)
|
||||
pstExt->append(c_szFile+ext, len-ext);
|
||||
}
|
||||
@@ -110,7 +129,7 @@ void GetFileNameParts(const char* c_szFile, int len, char* pszPath, char* pszNam
|
||||
char c=c_szFile[pos];
|
||||
if (ext==len && c=='.')
|
||||
{
|
||||
ext=pos;
|
||||
ext=pos;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -127,7 +146,7 @@ void GetFileNameParts(const char* c_szFile, int len, char* pszPath, char* pszNam
|
||||
else if (c=='\\') break;
|
||||
}
|
||||
|
||||
if (pos)
|
||||
if (pos)
|
||||
{
|
||||
++pos;
|
||||
for (int i = 0; i < pos; ++i)
|
||||
@@ -147,7 +166,7 @@ void GetFileNameParts(const char* c_szFile, int len, char* pszPath, char* pszNam
|
||||
pszName[count] = '\0';
|
||||
}
|
||||
|
||||
++ext;
|
||||
++ext;
|
||||
if (len > ext)
|
||||
{
|
||||
int count = 0;
|
||||
@@ -163,10 +182,10 @@ void GetOldIndexingName(char * szName, int Index)
|
||||
{
|
||||
int dec, sign;
|
||||
char Temp[512];
|
||||
|
||||
|
||||
strcpy(Temp, _ecvt(Index, 256, &dec, &sign));
|
||||
Temp[dec] = '\0';
|
||||
|
||||
|
||||
strcat(szName, Temp);
|
||||
}
|
||||
|
||||
@@ -423,38 +442,40 @@ bool IsGlobalFileName(const char * c_szFileName)
|
||||
return strchr(c_szFileName, ':') != NULL;
|
||||
}
|
||||
|
||||
void MyCreateDirectory(const char* path)
|
||||
void MyCreateDirectory(const char* pathUtf8)
|
||||
{
|
||||
if (!path || !*path)
|
||||
if (!pathUtf8 || !*pathUtf8)
|
||||
return;
|
||||
|
||||
char * dir;
|
||||
const char * p;
|
||||
// Skip drive letter (C:\)
|
||||
const char* path = pathUtf8;
|
||||
if (strlen(path) >= 3 && path[1] == ':')
|
||||
path += 3;
|
||||
|
||||
if (strlen(path) >= 3)
|
||||
{
|
||||
if (*(path + 1) == ':') // C:, D: 같은 경우를 체크
|
||||
path += 3;
|
||||
}
|
||||
size_t len = strlen(pathUtf8) + 1;
|
||||
char* dirUtf8 = new char[len];
|
||||
|
||||
p = path;
|
||||
|
||||
int len = strlen(path) + 1;
|
||||
dir = new char[len];
|
||||
const char* p = path;
|
||||
|
||||
while (*p)
|
||||
{
|
||||
if (*p == '/' || *p == '\\')
|
||||
{
|
||||
memset(dir, 0, len);
|
||||
strncpy(dir, path, p - path);
|
||||
CreateDirectory(dir, NULL);
|
||||
}
|
||||
memset(dirUtf8, 0, len);
|
||||
strncpy(dirUtf8, pathUtf8, p - path + (path - pathUtf8));
|
||||
|
||||
// UTF-8 → UTF-16 for WinAPI
|
||||
std::wstring wDir = Utf8ToWide(dirUtf8);
|
||||
CreateDirectoryW(wDir.c_str(), nullptr);
|
||||
}
|
||||
++p;
|
||||
}
|
||||
|
||||
delete [] dir;
|
||||
// Create final directory too
|
||||
std::wstring wFinal = Utf8ToWide(pathUtf8);
|
||||
CreateDirectoryW(wFinal.c_str(), nullptr);
|
||||
|
||||
delete[] dirUtf8;
|
||||
}
|
||||
|
||||
class CDirRemover : public CDir
|
||||
@@ -484,22 +505,31 @@ class CDirRemover : public CDir
|
||||
ms_strDirectoryDeque.push_back(strWorkingFolder);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnFile(const char* c_szPathName, const char* c_szFileName)
|
||||
{
|
||||
std::string strFullPathName;
|
||||
strFullPathName = c_szPathName;
|
||||
strFullPathName += c_szFileName;
|
||||
_chmod(strFullPathName.c_str(), _S_IWRITE);
|
||||
DeleteFile(strFullPathName.c_str());
|
||||
strFullPathName += c_szFileName;
|
||||
|
||||
std::wstring wFullPath = Utf8ToWide(strFullPathName);
|
||||
|
||||
// Make writable (use wide version)
|
||||
_wchmod(wFullPath.c_str(), _S_IWRITE);
|
||||
|
||||
// Delete (use wide WinAPI)
|
||||
DeleteFileW(wFullPath.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
static void RemoveAllDirectory()
|
||||
{
|
||||
for (std::deque<std::string>::iterator itor = ms_strDirectoryDeque.begin(); itor != ms_strDirectoryDeque.end(); ++itor)
|
||||
for (std::deque<std::string>::iterator itor = ms_strDirectoryDeque.begin();
|
||||
itor != ms_strDirectoryDeque.end(); ++itor)
|
||||
{
|
||||
const std::string & c_rstrDirectory = *itor;
|
||||
RemoveDirectory(c_rstrDirectory.c_str());
|
||||
const std::string& dirUtf8 = *itor;
|
||||
std::wstring wDir = Utf8ToWide(dirUtf8);
|
||||
RemoveDirectoryW(wDir.c_str());
|
||||
}
|
||||
|
||||
ms_strDirectoryDeque.clear();
|
||||
@@ -511,14 +541,19 @@ class CDirRemover : public CDir
|
||||
|
||||
std::deque<std::string> CDirRemover::ms_strDirectoryDeque;
|
||||
|
||||
void RemoveAllDirectory(const char * c_szDirectoryName)
|
||||
void RemoveAllDirectory(const char* c_szDirectoryName)
|
||||
{
|
||||
{
|
||||
CDirRemover remover;
|
||||
remover.Create("*.*", c_szDirectoryName);
|
||||
CDirRemover::RemoveAllDirectory();
|
||||
}
|
||||
RemoveDirectory(c_szDirectoryName);
|
||||
|
||||
if (c_szDirectoryName && *c_szDirectoryName)
|
||||
{
|
||||
std::wstring wDir = Utf8ToWide(c_szDirectoryName);
|
||||
RemoveDirectoryW(wDir.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void StringExceptCharacter(std::string * pstrString, const char * c_szCharacter)
|
||||
@@ -573,14 +608,15 @@ bool SplitLine(const char * c_szLine, const char * c_szDelimeter, std::vector<st
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetExcutedFileName(std::string & r_str)
|
||||
void GetExcutedFileName(std::string& r_str)
|
||||
{
|
||||
char szPath[MAX_PATH+1];
|
||||
wchar_t wPath[MAX_PATH + 1]{};
|
||||
|
||||
GetModuleFileName(NULL, szPath, MAX_PATH);
|
||||
szPath[MAX_PATH] = '\0';
|
||||
GetModuleFileNameW(nullptr, wPath, MAX_PATH);
|
||||
wPath[MAX_PATH] = L'\0';
|
||||
|
||||
r_str = szPath;
|
||||
// Convert UTF-16 → UTF-8 for engine use
|
||||
r_str = WideToUtf8(wPath);
|
||||
}
|
||||
|
||||
const char * _getf(const char* c_szFormat, ...)
|
||||
|
||||
@@ -27,12 +27,6 @@
|
||||
|
||||
#define AssertLog(str) TraceError(str); assert(!str)
|
||||
|
||||
#ifndef MAKEFOURCC
|
||||
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
|
||||
((DWORD)(BYTE) (ch0 ) | ((DWORD)(BYTE) (ch1) << 8) | \
|
||||
((DWORD)(BYTE) (ch2) << 16) | ((DWORD)(BYTE) (ch3) << 24))
|
||||
#endif // defined(MAKEFOURCC)
|
||||
|
||||
#ifndef IS_SET
|
||||
#define IS_SET(flag,bit) ((flag) & (bit))
|
||||
#endif
|
||||
@@ -215,4 +209,18 @@ void StringExceptCharacter(std::string * pstrString, const char * c_szCharacter)
|
||||
|
||||
extern void GetExcutedFileName(std::string & r_str);
|
||||
|
||||
template<typename T>
|
||||
constexpr T LinearInterpolation(const T& tMin, const T& tMax, float fRatio)
|
||||
{
|
||||
return T(tMin * (1.0f - fRatio) + tMax * fRatio);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T HermiteInterpolation(const T& tMin, const T& tMax, float fRatio)
|
||||
{
|
||||
fRatio = MINMAX(0.0f, fRatio, 1.0f);
|
||||
fRatio = fRatio * fRatio * (3.0f - 2.0f * fRatio);
|
||||
return LinearInterpolation(tMin, tMax, fRatio);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <time.h>
|
||||
#include <winsock.h>
|
||||
#include <imagehlp.h>
|
||||
#include <utf8.h>
|
||||
|
||||
FILE* fException;
|
||||
|
||||
@@ -44,18 +45,21 @@ LONG __stdcall EterExceptionFilter(_EXCEPTION_POINTERS* pExceptionInfo)
|
||||
HANDLE hProcess = GetCurrentProcess();
|
||||
HANDLE hThread = GetCurrentThread();
|
||||
|
||||
fException = fopen("ErrorLog.txt", "wt");
|
||||
fException = fopen("log/ErrorLog.txt", "wt");
|
||||
if (fException)
|
||||
{
|
||||
char module_name[256];
|
||||
wchar_t wModuleName[MAX_PATH]{};
|
||||
time_t module_time;
|
||||
|
||||
HMODULE hModule = GetModuleHandle(NULL);
|
||||
HMODULE hModule = GetModuleHandleW(nullptr);
|
||||
|
||||
GetModuleFileName(hModule, module_name, sizeof(module_name));
|
||||
GetModuleFileNameW(hModule, wModuleName, MAX_PATH);
|
||||
module_time = (time_t)GetTimestampForLoadedLibrary(hModule);
|
||||
|
||||
fprintf(fException, "Module Name: %s\n", module_name);
|
||||
// Convert once for logging
|
||||
std::string moduleNameUtf8 = WideToUtf8(wModuleName);
|
||||
|
||||
fprintf(fException, "Module Name: %s\n", moduleNameUtf8.c_str());
|
||||
fprintf(fException, "Time Stamp: 0x%08x - %s\n", (unsigned int)module_time, ctime(&module_time));
|
||||
fprintf(fException, "\n");
|
||||
fprintf(fException, "Exception Type: 0x%08x\n", pExceptionInfo->ExceptionRecord->ExceptionCode);
|
||||
|
||||
@@ -216,22 +216,14 @@ public:
|
||||
public:
|
||||
DecryptBuffer(unsigned size)
|
||||
{
|
||||
static unsigned count = 0;
|
||||
static unsigned sum = 0;
|
||||
static unsigned maxSize = 0;
|
||||
|
||||
sum += size;
|
||||
count++;
|
||||
|
||||
maxSize = std::max(size, maxSize);
|
||||
if (size >= LOCAL_BUF_SIZE)
|
||||
{
|
||||
m_buf = new char[size];
|
||||
dbg_printf("DecryptBuffer - AllocHeap %d max(%d) ave(%d)\n", size, maxSize/1024, sum/count);
|
||||
dbg_printf("DecryptBuffer - AllocHeap %d\n", size);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_printf("DecryptBuffer - AllocStack %d max(%d) ave(%d)\n", size, maxSize/1024, sum/count);
|
||||
dbg_printf("DecryptBuffer - AllocStack %d\n", size);
|
||||
m_buf = m_local_buf;
|
||||
}
|
||||
}
|
||||
@@ -239,12 +231,12 @@ public:
|
||||
{
|
||||
if (m_local_buf != m_buf)
|
||||
{
|
||||
dbg_printf("DecruptBuffer - FreeHeap\n");
|
||||
dbg_printf("DecryptBuffer - FreeHeap\n");
|
||||
delete [] m_buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_printf("DecruptBuffer - FreeStack\n");
|
||||
dbg_printf("DecryptBuffer - FreeStack\n");
|
||||
}
|
||||
}
|
||||
void* GetBufferPtr()
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
add_library(EterGrnLib STATIC ${FILE_SOURCES})
|
||||
|
||||
target_link_libraries(EterGrnLib
|
||||
lzo2
|
||||
cryptopp-static
|
||||
mio
|
||||
)
|
||||
|
||||
GroupSourcesByFolder(EterGrnLib)
|
||||
|
||||
153
src/EterGrnLib/Deform.cpp
Normal file
153
src/EterGrnLib/Deform.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "Deform.h"
|
||||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
void DeformPWNT3432toGrannyPNGBT33332D(granny_int32x Count, void const* SourceInit, void* DestInit,
|
||||
granny_matrix_4x4 const* Transforms,
|
||||
granny_int32x CopySize, granny_int32x SourceStride, granny_int32x DestStride)
|
||||
{
|
||||
const float inv255 = 1.0f / 255.0f;
|
||||
|
||||
const granny_pwnt3432_vertex* src = (const granny_pwnt3432_vertex*)SourceInit;
|
||||
granny_pnt332_vertex* dst = (granny_pnt332_vertex*)DestInit;
|
||||
|
||||
while (Count--) {
|
||||
const __m128 srcPos = _mm_set_ps(1.0f, src->Position[2], src->Position[1], src->Position[0]);
|
||||
const __m128 srcNrm = _mm_set_ps(0.0f, src->Normal[2], src->Normal[1], src->Normal[0]);
|
||||
|
||||
const __m128 px = _mm_shuffle_ps(srcPos, srcPos, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
const __m128 py = _mm_shuffle_ps(srcPos, srcPos, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
const __m128 pz = _mm_shuffle_ps(srcPos, srcPos, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
const __m128 pw = _mm_shuffle_ps(srcPos, srcPos, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
const __m128 nx = _mm_shuffle_ps(srcNrm, srcNrm, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
const __m128 ny = _mm_shuffle_ps(srcNrm, srcNrm, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
const __m128 nz = _mm_shuffle_ps(srcNrm, srcNrm, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
|
||||
__m128 P = _mm_setzero_ps();
|
||||
__m128 N = _mm_setzero_ps();
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
const int bi = src->BoneIndices[i];
|
||||
const float wS = (float)src->BoneWeights[i] * inv255;
|
||||
if (wS <= 0.0f) continue;
|
||||
|
||||
const float* m = (const float*)(&Transforms[bi]);
|
||||
|
||||
const __m128 r0 = _mm_loadu_ps(m + 0);
|
||||
const __m128 r1 = _mm_loadu_ps(m + 4);
|
||||
const __m128 r2 = _mm_loadu_ps(m + 8);
|
||||
const __m128 r3 = _mm_loadu_ps(m + 12);
|
||||
|
||||
__m128 p = _mm_add_ps(_mm_mul_ps(r0, px), _mm_mul_ps(r1, py));
|
||||
p = _mm_add_ps(p, _mm_mul_ps(r2, pz));
|
||||
p = _mm_add_ps(p, _mm_mul_ps(r3, pw));
|
||||
|
||||
const __m128 w = _mm_set1_ps(wS);
|
||||
P = _mm_add_ps(P, _mm_mul_ps(p, w));
|
||||
|
||||
__m128 n = _mm_add_ps(_mm_mul_ps(r0, nx), _mm_mul_ps(r1, ny));
|
||||
n = _mm_add_ps(n, _mm_mul_ps(r2, nz));
|
||||
N = _mm_add_ps(N, _mm_mul_ps(n, w));
|
||||
}
|
||||
|
||||
float pOut[4], nOut[4];
|
||||
_mm_storeu_ps(pOut, P);
|
||||
_mm_storeu_ps(nOut, N);
|
||||
|
||||
dst->Position[0] = pOut[0];
|
||||
dst->Position[1] = pOut[1];
|
||||
dst->Position[2] = pOut[2];
|
||||
|
||||
dst->Normal[0] = nOut[0];
|
||||
dst->Normal[1] = nOut[1];
|
||||
dst->Normal[2] = nOut[2];
|
||||
|
||||
dst->UV[0] = src->UV[0];
|
||||
dst->UV[1] = src->UV[1];
|
||||
|
||||
src = (const granny_pwnt3432_vertex*)((const granny_uint8*)src + SourceStride);
|
||||
dst = (granny_pnt332_vertex*)((granny_uint8*)dst + DestStride);
|
||||
}
|
||||
}
|
||||
|
||||
void DeformPWNT3432toGrannyPNGBT33332I(granny_int32x Count, void const* SourceInit, void* DestInit,
|
||||
granny_int32x const* TransformTable, granny_matrix_4x4 const* Transforms,
|
||||
granny_int32x CopySize, granny_int32x SourceStride, granny_int32x DestStride)
|
||||
{
|
||||
const float inv255 = 1.0f / 255.0f;
|
||||
|
||||
const granny_pwnt3432_vertex* src = (const granny_pwnt3432_vertex*)SourceInit;
|
||||
granny_pnt332_vertex* dst = (granny_pnt332_vertex*)DestInit;
|
||||
|
||||
while (Count--) {
|
||||
const __m128 srcPos = _mm_set_ps(1.0f, src->Position[2], src->Position[1], src->Position[0]);
|
||||
const __m128 srcNrm = _mm_set_ps(0.0f, src->Normal[2], src->Normal[1], src->Normal[0]);
|
||||
|
||||
const __m128 px = _mm_shuffle_ps(srcPos, srcPos, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
const __m128 py = _mm_shuffle_ps(srcPos, srcPos, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
const __m128 pz = _mm_shuffle_ps(srcPos, srcPos, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
const __m128 pw = _mm_shuffle_ps(srcPos, srcPos, _MM_SHUFFLE(3, 3, 3, 3));
|
||||
|
||||
const __m128 nx = _mm_shuffle_ps(srcNrm, srcNrm, _MM_SHUFFLE(0, 0, 0, 0));
|
||||
const __m128 ny = _mm_shuffle_ps(srcNrm, srcNrm, _MM_SHUFFLE(1, 1, 1, 1));
|
||||
const __m128 nz = _mm_shuffle_ps(srcNrm, srcNrm, _MM_SHUFFLE(2, 2, 2, 2));
|
||||
|
||||
__m128 P = _mm_setzero_ps();
|
||||
__m128 N = _mm_setzero_ps();
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
const int bi = TransformTable[src->BoneIndices[i]];
|
||||
const float wS = (float)src->BoneWeights[i] * inv255;
|
||||
if (wS <= 0.0f) continue;
|
||||
|
||||
const float* m = (const float*)(&Transforms[bi]);
|
||||
|
||||
const __m128 r0 = _mm_loadu_ps(m + 0);
|
||||
const __m128 r1 = _mm_loadu_ps(m + 4);
|
||||
const __m128 r2 = _mm_loadu_ps(m + 8);
|
||||
const __m128 r3 = _mm_loadu_ps(m + 12);
|
||||
|
||||
__m128 p = _mm_add_ps(_mm_mul_ps(r0, px), _mm_mul_ps(r1, py));
|
||||
p = _mm_add_ps(p, _mm_mul_ps(r2, pz));
|
||||
p = _mm_add_ps(p, _mm_mul_ps(r3, pw));
|
||||
|
||||
const __m128 w = _mm_set1_ps(wS);
|
||||
P = _mm_add_ps(P, _mm_mul_ps(p, w));
|
||||
|
||||
__m128 n = _mm_add_ps(_mm_mul_ps(r0, nx), _mm_mul_ps(r1, ny));
|
||||
n = _mm_add_ps(n, _mm_mul_ps(r2, nz));
|
||||
N = _mm_add_ps(N, _mm_mul_ps(n, w));
|
||||
}
|
||||
|
||||
float pOut[4], nOut[4];
|
||||
_mm_storeu_ps(pOut, P);
|
||||
_mm_storeu_ps(nOut, N);
|
||||
|
||||
dst->Position[0] = pOut[0];
|
||||
dst->Position[1] = pOut[1];
|
||||
dst->Position[2] = pOut[2];
|
||||
|
||||
dst->Normal[0] = nOut[0];
|
||||
dst->Normal[1] = nOut[1];
|
||||
dst->Normal[2] = nOut[2];
|
||||
|
||||
dst->UV[0] = src->UV[0];
|
||||
dst->UV[1] = src->UV[1];
|
||||
|
||||
src = (const granny_pwnt3432_vertex*)((const granny_uint8*)src + SourceStride);
|
||||
dst = (granny_pnt332_vertex*)((granny_uint8*)dst + DestStride);
|
||||
}
|
||||
}
|
||||
|
||||
void DeformPWNT3432toGrannyPNGBT33332(granny_int32x Count, void const* SourceInit, void* DestInit,
|
||||
granny_int32x const* TransformTable, granny_matrix_4x4 const* Transforms,
|
||||
granny_int32x CopySize, granny_int32x SourceStride, granny_int32x DestStride)
|
||||
{
|
||||
if (TransformTable) {
|
||||
DeformPWNT3432toGrannyPNGBT33332I(Count, SourceInit, DestInit, TransformTable, Transforms, CopySize, SourceStride, DestStride);
|
||||
}
|
||||
else {
|
||||
DeformPWNT3432toGrannyPNGBT33332D(Count, SourceInit, DestInit, Transforms, CopySize, SourceStride, DestStride);
|
||||
}
|
||||
}
|
||||
6
src/EterGrnLib/Deform.h
Normal file
6
src/EterGrnLib/Deform.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <granny.h>
|
||||
|
||||
void DeformPWNT3432toGrannyPNGBT33332(granny_int32x Count, void const* SourceInit, void* DestInit,
|
||||
granny_int32x const* TransformTable, granny_matrix_4x4 const* Transforms,
|
||||
granny_int32x CopySize, granny_int32x SourceStride, granny_int32x DestStride);
|
||||
@@ -58,10 +58,10 @@ static CGraphicVertexBuffer* __AllocDeformVertexBuffer(unsigned deformableVertex
|
||||
CGraphicVertexBuffer* pkNewVB = new CGraphicVertexBuffer;
|
||||
|
||||
if (!pkNewVB->Create(
|
||||
capacity,
|
||||
D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1,
|
||||
D3DUSAGE_WRITEONLY,
|
||||
D3DPOOL_MANAGED))
|
||||
capacity,
|
||||
D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1,
|
||||
D3DUSAGE_DYNAMIC,
|
||||
D3DPOOL_DEFAULT))
|
||||
{
|
||||
TraceError("NEW_ERROR %8d: %d(%d)", time(NULL) - base, capacity, deformableVertexCount);
|
||||
}
|
||||
@@ -101,10 +101,10 @@ void __ReserveSharedVertexBuffers(unsigned index, unsigned count)
|
||||
{
|
||||
CGraphicVertexBuffer* pkNewVB = new CGraphicVertexBuffer;
|
||||
pkNewVB->Create(
|
||||
capacity,
|
||||
D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1,
|
||||
D3DUSAGE_WRITEONLY,
|
||||
D3DPOOL_MANAGED);
|
||||
capacity,
|
||||
D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1,
|
||||
D3DUSAGE_DYNAMIC,
|
||||
D3DPOOL_DEFAULT);
|
||||
gs_vbs[index].push_back(pkNewVB);
|
||||
}
|
||||
NANOEND
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
#include <windows.h>
|
||||
#include <d3d9.h>
|
||||
|
||||
#include "../eterlib/ReferenceObject.h"
|
||||
#include "../eterlib/Ref.h"
|
||||
#include "../eterlib/GrpImageInstance.h"
|
||||
#include "Eterlib/ReferenceObject.h"
|
||||
#include "Eterlib/Ref.h"
|
||||
#include "Eterlib/GrpImageInstance.h"
|
||||
#include "Util.h"
|
||||
|
||||
class CGrannyMaterial : public CReferenceObject
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Mesh.h"
|
||||
#include "Model.h"
|
||||
#include "Material.h"
|
||||
#include "Deform.h"
|
||||
|
||||
granny_data_type_definition GrannyPNT3322VertexType[5] =
|
||||
{
|
||||
@@ -42,30 +43,45 @@ void CGrannyMesh::NEW_LoadVertices(void * dstBaseVertices)
|
||||
GrannyCopyMeshVertices(pgrnMesh, m_pgrnMeshType, dstVertices);
|
||||
}
|
||||
|
||||
void CGrannyMesh::DeformPNTVertices(void * dstBaseVertices, D3DXMATRIX * boneMatrices, granny_mesh_binding* pgrnMeshBinding) const
|
||||
void CGrannyMesh::DeformPNTVertices(void* dstBaseVertices, D3DXMATRIX* boneMatrices, granny_mesh_binding* pgrnMeshBinding) const
|
||||
{
|
||||
assert(dstBaseVertices != NULL);
|
||||
assert(boneMatrices != NULL);
|
||||
assert(m_pgrnMeshDeformer != NULL);
|
||||
|
||||
const granny_mesh * pgrnMesh = GetGrannyMeshPointer();
|
||||
const granny_mesh* pgrnMesh = GetGrannyMeshPointer();
|
||||
|
||||
TPNTVertex* srcVertices = (TPNTVertex*)GrannyGetMeshVertices(pgrnMesh);
|
||||
TPNTVertex* dstVertices = ((TPNTVertex*)dstBaseVertices) + m_vtxBasePos;
|
||||
|
||||
TPNTVertex * srcVertices = (TPNTVertex *) GrannyGetMeshVertices(pgrnMesh);
|
||||
TPNTVertex * dstVertices = ((TPNTVertex *) dstBaseVertices) + m_vtxBasePos;
|
||||
|
||||
int vtxCount = GrannyGetMeshVertexCount(pgrnMesh);
|
||||
|
||||
// WORK
|
||||
granny_int32x * boneIndices = (granny_int32x*)GrannyGetMeshBindingToBoneIndices(pgrnMeshBinding);
|
||||
granny_int32x* boneIndices = (granny_int32x*)GrannyGetMeshBindingToBoneIndices(pgrnMeshBinding);
|
||||
// END_OF_WORK
|
||||
|
||||
GrannyDeformVertices(
|
||||
m_pgrnMeshDeformer,
|
||||
boneIndices,
|
||||
(float *)boneMatrices,
|
||||
vtxCount,
|
||||
srcVertices,
|
||||
dstVertices);
|
||||
extern bool CPU_HAS_SSE2;
|
||||
if (CPU_HAS_SSE2) {
|
||||
DeformPWNT3432toGrannyPNGBT33332(
|
||||
vtxCount,
|
||||
srcVertices,
|
||||
dstVertices,
|
||||
boneIndices,
|
||||
(granny_matrix_4x4 const*)boneMatrices,
|
||||
sizeof(granny_pwnt3432_vertex),
|
||||
sizeof(granny_pwnt3432_vertex),
|
||||
sizeof(granny_pnt332_vertex)
|
||||
);
|
||||
}
|
||||
else {
|
||||
GrannyDeformVertices(
|
||||
m_pgrnMeshDeformer,
|
||||
boneIndices,
|
||||
(float*)boneMatrices,
|
||||
vtxCount,
|
||||
srcVertices,
|
||||
dstVertices);
|
||||
}
|
||||
}
|
||||
|
||||
bool CGrannyMesh::CanDeformPNTVertices() const
|
||||
|
||||
@@ -109,7 +109,7 @@ bool CGrannyModel::LoadPNTVertices()
|
||||
|
||||
assert(m_meshs != NULL);
|
||||
|
||||
if (!m_pntVtxBuf.Create(m_rigidVtxCount, m_dwFvF, D3DUSAGE_WRITEONLY, D3DPOOL_MANAGED))
|
||||
if (!m_pntVtxBuf.Create(m_rigidVtxCount, m_dwFvF, D3DUSAGE_WRITEONLY, D3DPOOL_DEFAULT))
|
||||
return false;
|
||||
|
||||
void* vertices;
|
||||
@@ -257,7 +257,7 @@ BOOL CGrannyModel::CheckMeshIndex(int iIndex) const
|
||||
{
|
||||
if (iIndex < 0)
|
||||
return FALSE;
|
||||
if (iIndex >= m_meshNodeSize)
|
||||
if (iIndex >= GetMeshCount())
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
@@ -361,7 +361,7 @@ bool CGrannyModel::__LoadVertices()
|
||||
// assert((m_dwFvF & (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1)) == m_dwFvF);
|
||||
|
||||
// if (!m_pntVtxBuf.Create(m_rigidVtxCount, D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1, D3DUSAGE_WRITEONLY, D3DPOOL_MANAGED))
|
||||
if (!m_pntVtxBuf.Create(m_rigidVtxCount, m_dwFvF, D3DUSAGE_WRITEONLY, D3DPOOL_MANAGED))
|
||||
if (!m_pntVtxBuf.Create(m_rigidVtxCount, m_dwFvF, D3DUSAGE_WRITEONLY, D3DPOOL_DEFAULT))
|
||||
return false;
|
||||
|
||||
void* vertices;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../eterlib/GrpVertexBuffer.h"
|
||||
#include "../eterlib/GrpIndexBuffer.h"
|
||||
#include "Eterlib/GrpVertexBuffer.h"
|
||||
#include "Eterlib/GrpIndexBuffer.h"
|
||||
|
||||
#include "Mesh.h"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "StdAfx.h"
|
||||
#include "ModelInstance.h"
|
||||
#include "Model.h"
|
||||
#include "../EterLib/ResourceManager.h"
|
||||
#include "EterLib/ResourceManager.h"
|
||||
|
||||
|
||||
CGrannyModel* CGrannyModelInstance::GetModel()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
//#define CACHE_DEFORMED_VERTEX
|
||||
#include "../eterlib/GrpImage.h"
|
||||
#include "../eterlib/GrpCollisionObject.h"
|
||||
#include "Eterlib/GrpImage.h"
|
||||
#include "Eterlib/GrpCollisionObject.h"
|
||||
|
||||
#include "Model.h"
|
||||
#include "Motion.h"
|
||||
|
||||
@@ -176,7 +176,7 @@ bool CGrannyModelInstance::Intersect(const D3DXMATRIX * c_pMatrix,
|
||||
*/
|
||||
}
|
||||
|
||||
#include "../EterBase/Timer.h"
|
||||
#include "EterBase/Timer.h"
|
||||
|
||||
void CGrannyModelInstance::GetBoundBox(D3DXVECTOR3* vtMin, D3DXVECTOR3* vtMax)
|
||||
{
|
||||
|
||||
@@ -232,9 +232,7 @@ void CGrannyModelInstance::__CreateDynamicVertexBuffer()
|
||||
{
|
||||
if (!m_kLocalDeformableVertexBuffer.Create(vtxCount,
|
||||
D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1,
|
||||
//D3DUSAGE_DYNAMIC, D3DPOOL_SYSTEMMEM
|
||||
|
||||
D3DUSAGE_WRITEONLY, D3DPOOL_MANAGED
|
||||
D3DUSAGE_DYNAMIC, D3DPOOL_DEFAULT
|
||||
))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "StdAfx.h"
|
||||
#include "../eterlib/StateManager.h"
|
||||
#include "Eterlib/StateManager.h"
|
||||
#include "ModelInstance.h"
|
||||
#include "Model.h"
|
||||
|
||||
#ifdef _TEST
|
||||
|
||||
#include "../eterlib/GrpScreen.h"
|
||||
#include "Eterlib/GrpScreen.h"
|
||||
|
||||
void Granny_RenderBoxBones(const granny_skeleton* pkGrnSkeleton, const granny_world_pose* pkGrnWorldPose, const D3DXMATRIX& matBase)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "StdAfx.h"
|
||||
#include "../eterbase/Debug.h"
|
||||
#include "Eterbase/Debug.h"
|
||||
#include "ModelInstance.h"
|
||||
#include "Model.h"
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
//#include <crtdbg.h>
|
||||
#include <granny.h>
|
||||
|
||||
#include "../eterBase/Utils.h"
|
||||
#include "../eterBase/Debug.h"
|
||||
#include "../eterBase/Stl.h"
|
||||
#include "EterBase/Utils.h"
|
||||
#include "EterBase/Debug.h"
|
||||
#include "EterBase/Stl.h"
|
||||
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "StdAfx.h"
|
||||
#include "../eterbase/Debug.h"
|
||||
#include "Eterbase/Debug.h"
|
||||
#include "Thing.h"
|
||||
#include "ThingInstance.h"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "StdAfx.h"
|
||||
#include "../eterbase/Debug.h"
|
||||
#include "../eterlib/Camera.h"
|
||||
#include "../eterBase/Timer.h"
|
||||
#include "Eterbase/Debug.h"
|
||||
#include "Eterlib/Camera.h"
|
||||
#include "EterBase/Timer.h"
|
||||
#include "ThingInstance.h"
|
||||
#include "Thing.h"
|
||||
#include "ModelInstance.h"
|
||||
@@ -534,7 +534,7 @@ void CGraphicThingInstance::RegisterMotionThing(DWORD dwMotionKey, CGraphicThing
|
||||
{
|
||||
CGraphicThing::TRef * pMotionRef = new CGraphicThing::TRef;
|
||||
pMotionRef->SetPointer(pMotionThing);
|
||||
m_roMotionThingMap.insert(std::map<DWORD, CGraphicThing::TRef *>::value_type(dwMotionKey, pMotionRef));
|
||||
m_roMotionThingMap.insert(std::make_pair(dwMotionKey, pMotionRef));
|
||||
}
|
||||
|
||||
void CGraphicThingInstance::ResetLocalTime()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../eterbase/Stl.h"
|
||||
#include "../eterlib/GrpObjectInstance.h"
|
||||
#include "../eterlib/GrpShadowTexture.h"
|
||||
#include "Eterbase/Stl.h"
|
||||
#include "Eterlib/GrpObjectInstance.h"
|
||||
#include "Eterlib/GrpShadowTexture.h"
|
||||
|
||||
#include "LODController.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
add_library(EterImageLib STATIC ${FILE_SOURCES})
|
||||
|
||||
target_link_libraries(EterImageLib
|
||||
lzo2
|
||||
cryptopp-static
|
||||
mio
|
||||
)
|
||||
|
||||
GroupSourcesByFolder(EterImageLib)
|
||||
|
||||
1573
src/EterImageLib/DDSTextureLoader9.cpp
Normal file
1573
src/EterImageLib/DDSTextureLoader9.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user