Commit c7099812 authored by mercury233's avatar mercury233

Merge branch 'fh' into patch-win64

parents daf3becf c3e25993
...@@ -333,9 +333,7 @@ jobs: ...@@ -333,9 +333,7 @@ jobs:
- name: Use premake to generate make files - name: Use premake to generate make files
run: | run: |
./premake5 gmake \ ./premake5 gmake
--freetype-include-dir="/usr/include/freetype2" \
--opus-include-dir="/usr/include/opus"
- name: Make - name: Make
run: | run: |
...@@ -426,31 +424,9 @@ jobs: ...@@ -426,31 +424,9 @@ jobs:
cp -r premake/* . cp -r premake/* .
cp -r resource/* . cp -r resource/* .
- name: Use premake to generate make files (Intel) - name: Use premake to generate make files
if: runner.arch == 'X64' run: |
run: | DYLD_LIBRARY_PATH=$(brew --prefix)/lib ./premake5 gmake --cc=clang
./premake5 gmake \
--cc=clang \
--freetype-include-dir="/usr/local/include/freetype2" \
--opus-include-dir="/usr/local/include/opus"
- name: Use premake to generate make files (ARM64)
if: runner.arch == 'ARM64'
run: |
./premake5 gmake \
--cc=clang \
--event-include-dir="/opt/homebrew/include" \
--event-lib-dir="/opt/homebrew/lib" \
--freetype-include-dir="/opt/homebrew/include/freetype2" \
--freetype-lib-dir="/opt/homebrew/lib" \
--sqlite-include-dir="/opt/homebrew/opt/sqlite/include" \
--sqlite-lib-dir="/opt/homebrew/opt/sqlite/lib" \
--miniaudio-include-dir="/opt/homebrew/include" \
--miniaudio-lib-dir="/opt/homebrew/lib" \
--opus-include-dir="/opt/homebrew/include/opus" \
--opus-lib-dir="/opt/homebrew/lib" \
--vorbis-include-dir="/opt/homebrew/include" \
--vorbis-lib-dir="/opt/homebrew/lib"
- name: Make - name: Make
run: | run: |
......
...@@ -32,7 +32,7 @@ public: ...@@ -32,7 +32,7 @@ public:
* @brief Copy a C-style string to another C-style string. * @brief Copy a C-style string to another C-style string.
* @param src The source wide string * @param src The source wide string
* @param pstr The destination char string * @param pstr The destination char string
* @param bufsize The size of the destination buffer * @param bufsize The length of the destination buffer
* @return The length of the copied string * @return The length of the copied string
*/ */
template<typename T1, typename T2> template<typename T1, typename T2>
...@@ -62,13 +62,13 @@ public: ...@@ -62,13 +62,13 @@ public:
} }
template<size_t N> template<size_t N>
static void CopyString(const char* src, char(&dst)[N]) { static void CopyString(const char* src, char(&dst)[N]) {
dst[0] = 0; std::strncpy(dst, src, N - 1);
std::strncat(dst, src, N - 1); dst[N - 1] = 0;
} }
template<size_t N> template<size_t N>
static void CopyWideString(const wchar_t* src, wchar_t(&dst)[N]) { static void CopyWideString(const wchar_t* src, wchar_t(&dst)[N]) {
dst[0] = 0; std::wcsncpy(dst, src, N - 1);
std::wcsncat(dst, src, N - 1); dst[N - 1] = 0;
} }
template<typename T> template<typename T>
static bool CheckUTF8Byte(const T* str, int len) { static bool CheckUTF8Byte(const T* str, int len) {
......
...@@ -359,34 +359,21 @@ bool DeckManager::DeleteCategory(const wchar_t* name) { ...@@ -359,34 +359,21 @@ bool DeckManager::DeleteCategory(const wchar_t* name) {
return false; return false;
return FileSystem::DeleteDir(localname); return FileSystem::DeleteDir(localname);
} }
bool DeckManager::SaveDeckBuffer(const int deckbuf[], const wchar_t* name) { bool DeckManager::SaveDeckArray(const DeckArray& deck, const wchar_t* name) {
if (!FileSystem::IsDirExists(L"./deck") && !FileSystem::MakeDir(L"./deck")) if (!FileSystem::IsDirExists(L"./deck") && !FileSystem::MakeDir(L"./deck"))
return false; return false;
FILE* fp = OpenDeckFile(name, "w"); FILE* fp = OpenDeckFile(name, "w");
if (!fp) if (!fp)
return false; return false;
int it = 0;
const int mainc = deckbuf[it];
++it;
std::fprintf(fp, "#created by ...\n#main\n"); std::fprintf(fp, "#created by ...\n#main\n");
for (int i = 0; i < mainc; ++i) { for (const auto& code : deck.main)
std::fprintf(fp, "%d\n", deckbuf[it]); std::fprintf(fp, "%u\n", code);
++it;
}
const int extrac = deckbuf[it];
++it;
std::fprintf(fp, "#extra\n"); std::fprintf(fp, "#extra\n");
for (int i = 0; i < extrac; ++i) { for (const auto& code : deck.extra)
std::fprintf(fp, "%d\n", deckbuf[it]); std::fprintf(fp, "%u\n", code);
++it;
}
const int sidec = deckbuf[it];
++it;
std::fprintf(fp, "!side\n"); std::fprintf(fp, "!side\n");
for (int i = 0; i < sidec; ++i) { for (const auto& code : deck.side)
std::fprintf(fp, "%d\n", deckbuf[it]); std::fprintf(fp, "%u\n", code);
++it;
}
std::fclose(fp); std::fclose(fp);
return true; return true;
} }
......
...@@ -35,6 +35,12 @@ struct Deck { ...@@ -35,6 +35,12 @@ struct Deck {
} }
}; };
struct DeckArray {
std::vector<uint32_t> main;
std::vector<uint32_t> extra;
std::vector<uint32_t> side;
};
class DeckManager { class DeckManager {
public: public:
Deck current_deck; Deck current_deck;
...@@ -49,7 +55,6 @@ public: ...@@ -49,7 +55,6 @@ public:
unsigned int CheckDeck(const Deck& deck, unsigned int lfhash, int rule); unsigned int CheckDeck(const Deck& deck, unsigned int lfhash, int rule);
bool LoadCurrentDeck(const wchar_t* file, bool is_packlist = false); bool LoadCurrentDeck(const wchar_t* file, bool is_packlist = false);
bool LoadCurrentDeck(int category_index, const wchar_t* category_name, const wchar_t* deckname); bool LoadCurrentDeck(int category_index, const wchar_t* category_name, const wchar_t* deckname);
bool SaveDeckBuffer(const int deckbuf[], const wchar_t* name);
static uint32_t LoadDeck(Deck& deck, uint32_t dbuf[], int mainc, int sidec, bool is_packlist = false); static uint32_t LoadDeck(Deck& deck, uint32_t dbuf[], int mainc, int sidec, bool is_packlist = false);
static uint32_t LoadDeckFromStream(Deck& deck, std::istringstream& deckStream, bool is_packlist = false); static uint32_t LoadDeckFromStream(Deck& deck, std::istringstream& deckStream, bool is_packlist = false);
...@@ -63,6 +68,7 @@ public: ...@@ -63,6 +68,7 @@ public:
static bool CreateCategory(const wchar_t* name); static bool CreateCategory(const wchar_t* name);
static bool RenameCategory(const wchar_t* oldname, const wchar_t* newname); static bool RenameCategory(const wchar_t* oldname, const wchar_t* newname);
static bool DeleteCategory(const wchar_t* name); static bool DeleteCategory(const wchar_t* name);
static bool SaveDeckArray(const DeckArray& deck, const wchar_t* name);
}; };
extern DeckManager deckManager; extern DeckManager deckManager;
......
...@@ -1262,7 +1262,12 @@ void Game::DrawDeckBd() { ...@@ -1262,7 +1262,12 @@ void Game::DrawDeckBd() {
driver->draw2DRectangle(Resize(805, 160, 1020, 630), 0x400000ff, 0x400000ff, 0x40000000, 0x40000000); driver->draw2DRectangle(Resize(805, 160, 1020, 630), 0x400000ff, 0x400000ff, 0x40000000, 0x40000000);
driver->draw2DRectangleOutline(Resize(804, 159, 1020, 630)); driver->draw2DRectangleOutline(Resize(804, 159, 1020, 630));
} }
for(int i = 0; i < 9 && i + scrFilter->getPos() < (int)deckBuilder.results.size(); ++i) { #ifdef YGOPRO_USE_THUMB_LOAD_THREAD
constexpr int MAX_RESULT = 9;
#else
constexpr int MAX_RESULT = 7;
#endif
for(int i = 0; i < MAX_RESULT && i + scrFilter->getPos() < (int)deckBuilder.results.size(); ++i) {
code_pointer ptr = deckBuilder.results[i + scrFilter->getPos()]; code_pointer ptr = deckBuilder.results[i + scrFilter->getPos()];
if(i >= 7) if(i >= 7)
{ {
......
...@@ -1557,6 +1557,10 @@ void Game::ShowCardInfo(int code, bool resize) { ...@@ -1557,6 +1557,10 @@ void Game::ShowCardInfo(int code, bool resize) {
myswprintf(formatBuffer, L"%ls[%08d]", dataManager.GetName(code), code); myswprintf(formatBuffer, L"%ls[%08d]", dataManager.GetName(code), code);
} }
stName->setText(formatBuffer); stName->setText(formatBuffer);
if((int)guiFont->getDimension(formatBuffer).Width > stName->getRelativePosition().getWidth() - gameConf.textfontsize)
stName->setToolTipText(formatBuffer);
else
stName->setToolTipText(nullptr);
int offset = 0; int offset = 0;
if (is_valid && !gameConf.hide_setname) { if (is_valid && !gameConf.hide_setname) {
auto& cd = cit->second; auto& cd = cit->second;
...@@ -2033,6 +2037,9 @@ void Game::OnResize() { ...@@ -2033,6 +2037,9 @@ void Game::OnResize() {
btnBigCardZoomIn->setRelativePosition(Resize(205, 140, 295, 175)); btnBigCardZoomIn->setRelativePosition(Resize(205, 140, 295, 175));
btnBigCardZoomOut->setRelativePosition(Resize(205, 180, 295, 215)); btnBigCardZoomOut->setRelativePosition(Resize(205, 180, 295, 215));
btnBigCardClose->setRelativePosition(Resize(205, 230, 295, 265)); btnBigCardClose->setRelativePosition(Resize(205, 230, 295, 265));
irr::s32 barWidth = (xScale > 1) ? gameConf.textfontsize * xScale : gameConf.textfontsize;
env->getSkin()->setSize(irr::gui::EGDS_SCROLLBAR_SIZE, barWidth);
} }
void Game::ResizeChatInputWindow() { void Game::ResizeChatInputWindow() {
irr::s32 x = wInfos->getRelativePosition().LowerRightCorner.X + 6; irr::s32 x = wInfos->getRelativePosition().LowerRightCorner.X + 6;
......
...@@ -33,11 +33,17 @@ int main(int argc, char* argv[]) { ...@@ -33,11 +33,17 @@ int main(int argc, char* argv[]) {
#ifdef __APPLE__ #ifdef __APPLE__
CFURLRef bundle_url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); CFURLRef bundle_url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
CFURLRef bundle_base_url = CFURLCreateCopyDeletingLastPathComponent(nullptr, bundle_url); CFURLRef bundle_base_url = CFURLCreateCopyDeletingLastPathComponent(nullptr, bundle_url);
CFStringRef bundle_ext = CFURLCopyPathExtension(bundle_url);
if (bundle_ext) {
char path[PATH_MAX];
if (CFStringCompare(bundle_ext, CFSTR("app"), kCFCompareCaseInsensitive) == kCFCompareEqualTo
&& CFURLGetFileSystemRepresentation(bundle_base_url, true, (UInt8*)path, PATH_MAX)) {
chdir(path);
}
CFRelease(bundle_ext);
}
CFRelease(bundle_url); CFRelease(bundle_url);
CFStringRef path = CFURLCopyFileSystemPath(bundle_base_url, kCFURLPOSIXPathStyle);
CFRelease(bundle_base_url); CFRelease(bundle_base_url);
chdir(CFStringGetCStringPtr(path, kCFStringEncodingUTF8));
CFRelease(path);
#endif //__APPLE__ #endif //__APPLE__
#ifdef _WIN32 #ifdef _WIN32
if (argc == 2 && (ygo::IsExtension(argv[1], ".ydk") || ygo::IsExtension(argv[1], ".yrp"))) { // open file from explorer if (argc == 2 && (ygo::IsExtension(argv[1], ".ydk") || ygo::IsExtension(argv[1], ".yrp"))) { // open file from explorer
......
#include "image_manager.h" #include "image_manager.h"
#include "game.h" #include "game.h"
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
#include <thread> #include <thread>
#endif
#ifdef _OPENMP
#include <omp.h>
#endif
namespace ygo { namespace ygo {
...@@ -17,8 +22,10 @@ bool ImageManager::Initial() { ...@@ -17,8 +22,10 @@ bool ImageManager::Initial() {
tUnknownFit = nullptr; tUnknownFit = nullptr;
tUnknownThumb = nullptr; tUnknownThumb = nullptr;
tBigPicture = nullptr; tBigPicture = nullptr;
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
tLoading = nullptr; tLoading = nullptr;
tThumbLoadingThreadRunning = false; tThumbLoadingThreadRunning = false;
#endif
tAct = driver->getTexture("textures/act.png"); tAct = driver->getTexture("textures/act.png");
tAttack = driver->getTexture("textures/attack.png"); tAttack = driver->getTexture("textures/attack.png");
tChain = driver->getTexture("textures/chain.png"); tChain = driver->getTexture("textures/chain.png");
...@@ -59,7 +66,11 @@ void ImageManager::ClearTexture() { ...@@ -59,7 +66,11 @@ void ImageManager::ClearTexture() {
driver->removeTexture(tit->second); driver->removeTexture(tit->second);
} }
for(auto tit = tThumb.begin(); tit != tThumb.end(); ++tit) { for(auto tit = tThumb.begin(); tit != tThumb.end(); ++tit) {
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
if(tit->second && tit->second != tLoading) if(tit->second && tit->second != tLoading)
#else
if(tit->second)
#endif
driver->removeTexture(tit->second); driver->removeTexture(tit->second);
} }
if(tBigPicture != nullptr) { if(tBigPicture != nullptr) {
...@@ -69,12 +80,14 @@ void ImageManager::ClearTexture() { ...@@ -69,12 +80,14 @@ void ImageManager::ClearTexture() {
tMap[0].clear(); tMap[0].clear();
tMap[1].clear(); tMap[1].clear();
tThumb.clear(); tThumb.clear();
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
tThumbLoadingMutex.lock(); tThumbLoadingMutex.lock();
tThumbLoading.clear(); tThumbLoading.clear();
while(!tThumbLoadingCodes.empty()) while(!tThumbLoadingCodes.empty())
tThumbLoadingCodes.pop(); tThumbLoadingCodes.pop();
tThumbLoadingThreadRunning = false; tThumbLoadingThreadRunning = false;
tThumbLoadingMutex.unlock(); tThumbLoadingMutex.unlock();
#endif
tFields.clear(); tFields.clear();
} }
void ImageManager::RemoveTexture(int code) { void ImageManager::RemoveTexture(int code) {
...@@ -110,11 +123,13 @@ void ImageManager::ResizeTexture() { ...@@ -110,11 +123,13 @@ void ImageManager::ResizeTexture() {
driver->removeTexture(tUnknown); driver->removeTexture(tUnknown);
driver->removeTexture(tUnknownFit); driver->removeTexture(tUnknownFit);
driver->removeTexture(tUnknownThumb); driver->removeTexture(tUnknownThumb);
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
driver->removeTexture(tLoading); driver->removeTexture(tLoading);
tLoading = GetTextureFromFile("textures/cover.jpg", imgWidthThumb, imgHeightThumb);
#endif
tUnknown = GetTextureFromFile("textures/unknown.jpg", CARD_IMG_WIDTH, CARD_IMG_HEIGHT); tUnknown = GetTextureFromFile("textures/unknown.jpg", CARD_IMG_WIDTH, CARD_IMG_HEIGHT);
tUnknownFit = GetTextureFromFile("textures/unknown.jpg", imgWidthFit, imgHeightFit); tUnknownFit = GetTextureFromFile("textures/unknown.jpg", imgWidthFit, imgHeightFit);
tUnknownThumb = GetTextureFromFile("textures/unknown.jpg", imgWidthThumb, imgHeightThumb); tUnknownThumb = GetTextureFromFile("textures/unknown.jpg", imgWidthThumb, imgHeightThumb);
tLoading = GetTextureFromFile("textures/cover.jpg", imgWidthThumb, imgHeightThumb);
driver->removeTexture(tBackGround); driver->removeTexture(tBackGround);
tBackGround = GetTextureFromFile("textures/bg.jpg", bgWidth, bgHeight); tBackGround = GetTextureFromFile("textures/bg.jpg", bgWidth, bgHeight);
driver->removeTexture(tBackGround_menu); driver->removeTexture(tBackGround_menu);
...@@ -128,25 +143,27 @@ void ImageManager::ResizeTexture() { ...@@ -128,25 +143,27 @@ void ImageManager::ResizeTexture() {
} }
// function by Warr1024, from https://github.com/minetest/minetest/issues/2419 , modified // function by Warr1024, from https://github.com/minetest/minetest/issues/2419 , modified
void imageScaleNNAA(irr::video::IImage *src, irr::video::IImage *dest) { void imageScaleNNAA(irr::video::IImage *src, irr::video::IImage *dest) {
double sx, sy, minsx, maxsx, minsy, maxsy, area, ra, ga, ba, aa, pw, ph, pa; const irr::core::dimension2d<irr::u32> srcDim = src->getDimension();
irr::u32 dy, dx; const irr::core::dimension2d<irr::u32> destDim = dest->getDimension();
irr::video::SColor pxl;
// Cache rectsngle boundaries. // Cache scale ratios.
double sw = src->getDimension().Width * 1.0; const double rx = (double)srcDim.Width / destDim.Width;
double sh = src->getDimension().Height * 1.0; const double ry = (double)srcDim.Height / destDim.Height;
// Walk each destination image pixel. #pragma omp parallel
// Note: loop y around x for better cache locality. {
irr::core::dimension2d<irr::u32> dim = dest->getDimension(); double sx, sy, minsx, maxsx, minsy, maxsy, area, ra, ga, ba, aa, pw, ph, pa;
for(dy = 0; dy < dim.Height; dy++) irr::video::SColor pxl, npxl;
for(dx = 0; dx < dim.Width; dx++) {
// Walk each destination image pixel.
#pragma omp for schedule(dynamic)
for(irr::s32 dy = 0; dy < destDim.Height; dy++) {
for(irr::s32 dx = 0; dx < destDim.Width; dx++) {
// Calculate floating-point source rectangle bounds. // Calculate floating-point source rectangle bounds.
minsx = dx * sw / dim.Width; minsx = dx * rx;
maxsx = minsx + sw / dim.Width; maxsx = minsx + rx;
minsy = dy * sh / dim.Height; minsy = dy * ry;
maxsy = minsy + sh / dim.Height; maxsy = minsy + ry;
// Total area, and integral of r, g, b values over that area, // Total area, and integral of r, g, b values over that area,
// initialized to zero, to be summed up in next loops. // initialized to zero, to be summed up in next loops.
...@@ -157,9 +174,8 @@ void imageScaleNNAA(irr::video::IImage *src, irr::video::IImage *dest) { ...@@ -157,9 +174,8 @@ void imageScaleNNAA(irr::video::IImage *src, irr::video::IImage *dest) {
aa = 0; aa = 0;
// Loop over the integral pixel positions described by those bounds. // Loop over the integral pixel positions described by those bounds.
for(sy = floor(minsy); sy < maxsy; sy++) for(sy = floor(minsy); sy < maxsy; sy++) {
for(sx = floor(minsx); sx < maxsx; sx++) { for(sx = floor(minsx); sx < maxsx; sx++) {
// Calculate width, height, then area of dest pixel // Calculate width, height, then area of dest pixel
// that's covered by this source pixel. // that's covered by this source pixel.
pw = 1; pw = 1;
...@@ -183,21 +199,20 @@ void imageScaleNNAA(irr::video::IImage *src, irr::video::IImage *dest) { ...@@ -183,21 +199,20 @@ void imageScaleNNAA(irr::video::IImage *src, irr::video::IImage *dest) {
ba += pa * pxl.getBlue(); ba += pa * pxl.getBlue();
aa += pa * pxl.getAlpha(); aa += pa * pxl.getAlpha();
} }
}
// Set the destination image pixel to the average color. // Set the destination image pixel to the average color.
if(area > 0) { if(area > 0) {
pxl.setRed(ra / area + 0.5); npxl.set((irr::u32)(aa / area + 0.5),
pxl.setGreen(ga / area + 0.5); (irr::u32)(ra / area + 0.5),
pxl.setBlue(ba / area + 0.5); (irr::u32)(ga / area + 0.5),
pxl.setAlpha(aa / area + 0.5); (irr::u32)(ba / area + 0.5));
} else { } else {
pxl.setRed(0); npxl.set(0);
pxl.setGreen(0);
pxl.setBlue(0);
pxl.setAlpha(0);
} }
dest->setPixel(dx, dy, pxl); dest->setPixel(dx, dy, npxl);
} }
}
} // end of parallel region
} }
irr::video::ITexture* ImageManager::GetTextureFromFile(const char* file, irr::s32 width, irr::s32 height) { irr::video::ITexture* ImageManager::GetTextureFromFile(const char* file, irr::s32 width, irr::s32 height) {
if(mainGame->gameConf.use_image_scale) { if(mainGame->gameConf.use_image_scale) {
...@@ -283,6 +298,7 @@ irr::video::ITexture* ImageManager::GetBigPicture(int code, float zoom) { ...@@ -283,6 +298,7 @@ irr::video::ITexture* ImageManager::GetBigPicture(int code, float zoom) {
tBigPicture = texture; tBigPicture = texture;
return texture; return texture;
} }
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
int ImageManager::LoadThumbThread() { int ImageManager::LoadThumbThread() {
while(true) { while(true) {
imageManager.tThumbLoadingMutex.lock(); imageManager.tThumbLoadingMutex.lock();
...@@ -338,9 +354,34 @@ int ImageManager::LoadThumbThread() { ...@@ -338,9 +354,34 @@ int ImageManager::LoadThumbThread() {
imageManager.tThumbLoadingMutex.unlock(); imageManager.tThumbLoadingMutex.unlock();
return 0; return 0;
} }
#endif // YGOPRO_USE_THUMB_LOAD_THREAD
irr::video::ITexture* ImageManager::GetTextureThumb(int code) { irr::video::ITexture* ImageManager::GetTextureThumb(int code) {
if(code == 0) if(code == 0)
return tUnknownThumb; return tUnknownThumb;
#ifndef YGOPRO_USE_THUMB_LOAD_THREAD
auto tit = tThumb.find(code);
if(tit == tThumb.end()) {
char file[256];
std::snprintf(file, sizeof file, "expansions/pics/thumbnail/%d.jpg", code);
int width = CARD_THUMB_WIDTH * mainGame->xScale;
int height = CARD_THUMB_HEIGHT * mainGame->yScale;
irr::video::ITexture* img = GetTextureFromFile(file, width, height);
if(img == NULL) {
std::snprintf(file, sizeof file, "pics/thumbnail/%d.jpg", code);
img = GetTextureFromFile(file, width, height);
}
if(img == NULL && mainGame->gameConf.use_image_scale) {
std::snprintf(file, sizeof file, "expansions/pics/%d.jpg", code);
img = GetTextureFromFile(file, width, height);
if(img == NULL) {
std::snprintf(file, sizeof file, "pics/%d.jpg", code);
img = GetTextureFromFile(file, width, height);
}
}
tThumb[code] = img;
return (img == NULL) ? tUnknownThumb : img;
}
#else // YGOPRO_USE_THUMB_LOAD_THREAD
imageManager.tThumbLoadingMutex.lock(); imageManager.tThumbLoadingMutex.lock();
auto lit = tThumbLoading.find(code); auto lit = tThumbLoading.find(code);
if(lit != tThumbLoading.end()) { if(lit != tThumbLoading.end()) {
...@@ -368,6 +409,7 @@ irr::video::ITexture* ImageManager::GetTextureThumb(int code) { ...@@ -368,6 +409,7 @@ irr::video::ITexture* ImageManager::GetTextureThumb(int code) {
imageManager.tThumbLoadingMutex.unlock(); imageManager.tThumbLoadingMutex.unlock();
return tLoading; return tLoading;
} }
#endif // YGOPRO_USE_THUMB_LOAD_THREAD
if(tit->second) if(tit->second)
return tit->second; return tit->second;
else else
......
#ifndef IMAGEMANAGER_H #ifndef IMAGEMANAGER_H
#define IMAGEMANAGER_H #define IMAGEMANAGER_H
#ifndef _OPENMP
#define YGOPRO_USE_THUMB_LOAD_THREAD
#endif
#include "config.h" #include "config.h"
#include "data_manager.h" #include "data_manager.h"
#include <unordered_map> #include <unordered_map>
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
#include <queue> #include <queue>
#include <mutex> #include <mutex>
#endif
namespace ygo { namespace ygo {
...@@ -21,15 +27,19 @@ public: ...@@ -21,15 +27,19 @@ public:
irr::video::ITexture* GetBigPicture(int code, float zoom); irr::video::ITexture* GetBigPicture(int code, float zoom);
irr::video::ITexture* GetTextureThumb(int code); irr::video::ITexture* GetTextureThumb(int code);
irr::video::ITexture* GetTextureField(int code); irr::video::ITexture* GetTextureField(int code);
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
static int LoadThumbThread(); static int LoadThumbThread();
#endif
std::unordered_map<int, irr::video::ITexture*> tMap[2]; std::unordered_map<int, irr::video::ITexture*> tMap[2];
std::unordered_map<int, irr::video::ITexture*> tThumb; std::unordered_map<int, irr::video::ITexture*> tThumb;
std::unordered_map<int, irr::video::ITexture*> tFields; std::unordered_map<int, irr::video::ITexture*> tFields;
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
std::unordered_map<int, irr::video::IImage*> tThumbLoading; std::unordered_map<int, irr::video::IImage*> tThumbLoading;
std::queue<int> tThumbLoadingCodes; std::queue<int> tThumbLoadingCodes;
std::mutex tThumbLoadingMutex; std::mutex tThumbLoadingMutex;
bool tThumbLoadingThreadRunning; bool tThumbLoadingThreadRunning;
#endif
irr::IrrlichtDevice* device; irr::IrrlichtDevice* device;
irr::video::IVideoDriver* driver; irr::video::IVideoDriver* driver;
irr::video::ITexture* tCover[4]; irr::video::ITexture* tCover[4];
...@@ -37,7 +47,9 @@ public: ...@@ -37,7 +47,9 @@ public:
irr::video::ITexture* tUnknownFit; irr::video::ITexture* tUnknownFit;
irr::video::ITexture* tUnknownThumb; irr::video::ITexture* tUnknownThumb;
irr::video::ITexture* tBigPicture; irr::video::ITexture* tBigPicture;
#ifdef YGOPRO_USE_THUMB_LOAD_THREAD
irr::video::ITexture* tLoading; irr::video::ITexture* tLoading;
#endif
irr::video::ITexture* tAct; irr::video::ITexture* tAct;
irr::video::ITexture* tAttack; irr::video::ITexture* tAttack;
irr::video::ITexture* tNegated; irr::video::ITexture* tNegated;
......
...@@ -232,6 +232,7 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) { ...@@ -232,6 +232,7 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
break; break;
} }
case BUTTON_LOAD_REPLAY: { case BUTTON_LOAD_REPLAY: {
int start_turn = 1;
if(open_file) { if(open_file) {
ReplayMode::cur_replay.OpenReplay(open_file_name); ReplayMode::cur_replay.OpenReplay(open_file_name);
open_file = false; open_file = false;
...@@ -243,6 +244,7 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) { ...@@ -243,6 +244,7 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
myswprintf(replay_path, L"./replay/%ls", mainGame->lstReplayList->getListItem(selected)); myswprintf(replay_path, L"./replay/%ls", mainGame->lstReplayList->getListItem(selected));
if (!ReplayMode::cur_replay.OpenReplay(replay_path)) if (!ReplayMode::cur_replay.OpenReplay(replay_path))
break; break;
start_turn = std::wcstol(mainGame->ebRepStartTurn->getText(), nullptr, 10);
} }
mainGame->ClearCardInfo(); mainGame->ClearCardInfo();
mainGame->wCardImg->setVisible(true); mainGame->wCardImg->setVisible(true);
...@@ -257,7 +259,6 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) { ...@@ -257,7 +259,6 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
mainGame->dField.Clear(); mainGame->dField.Clear();
mainGame->HideElement(mainGame->wReplay); mainGame->HideElement(mainGame->wReplay);
mainGame->device->setEventReceiver(&mainGame->dField); mainGame->device->setEventReceiver(&mainGame->dField);
unsigned int start_turn = std::wcstol(mainGame->ebRepStartTurn->getText(), nullptr, 10);
if(start_turn == 1) if(start_turn == 1)
start_turn = 0; start_turn = 0;
ReplayMode::StartReplay(start_turn); ReplayMode::StartReplay(start_turn);
...@@ -300,42 +301,23 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) { ...@@ -300,42 +301,23 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
if(selected == -1) if(selected == -1)
break; break;
Replay replay; Replay replay;
wchar_t ex_filename[256]{}; wchar_t replay_filename[256]{};
wchar_t namebuf[4][20]{}; wchar_t namebuf[4][20]{};
wchar_t filename[256]{}; wchar_t filename[256]{};
wchar_t replay_path[256]{}; wchar_t replay_path[256]{};
BufferIO::CopyWideString(mainGame->lstReplayList->getListItem(selected), ex_filename); BufferIO::CopyWideString(mainGame->lstReplayList->getListItem(selected), replay_filename);
myswprintf(replay_path, L"./replay/%ls", ex_filename); myswprintf(replay_path, L"./replay/%ls", replay_filename);
if (!replay.OpenReplay(replay_path)) if (!replay.OpenReplay(replay_path))
break; break;
const ReplayHeader& rh = replay.pheader; if (replay.pheader.flag & REPLAY_SINGLE_MODE)
if(rh.flag & REPLAY_SINGLE_MODE)
break; break;
int player_count = (rh.flag & REPLAY_TAG) ? 4 : 2; for (size_t i = 0; i < replay.decks.size(); ++i) {
//player name BufferIO::CopyWideString(replay.players[Replay::GetDeckPlayer(i)].c_str(), namebuf[i]);
for(int i = 0; i < player_count; ++i)
replay.ReadName(namebuf[i]);
//skip pre infos
for(int i = 0; i < 4; ++i)
replay.ReadInt32();
//deck
std::vector<int> deckbuf;
for(int i = 0; i < player_count; ++i) {
deckbuf.clear();
int main = replay.ReadInt32();
deckbuf.push_back(main);
for (int j = 0; j < main; ++j) {
deckbuf.push_back(replay.ReadInt32());
}
int extra = replay.ReadInt32();
deckbuf.push_back(extra);
for (int j = 0; j < extra; ++j) {
deckbuf.push_back(replay.ReadInt32());
}
deckbuf.push_back(0);
FileSystem::SafeFileName(namebuf[i]); FileSystem::SafeFileName(namebuf[i]);
myswprintf(filename, L"deck/%ls-%d %ls.ydk", ex_filename, i + 1, namebuf[i]); }
deckManager.SaveDeckBuffer(deckbuf.data(), filename); for (size_t i = 0; i < replay.decks.size(); ++i) {
myswprintf(filename, L"./deck/%ls-%d %ls.ydk", replay_filename, i + 1, namebuf[i]);
DeckManager::SaveDeckArray(replay.decks[i], filename);
} }
mainGame->stACMessage->setText(dataManager.GetSysString(1335)); mainGame->stACMessage->setText(dataManager.GetSysString(1335));
mainGame->PopupElement(mainGame->wACMessage, 20); mainGame->PopupElement(mainGame->wACMessage, 20);
...@@ -545,30 +527,30 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) { ...@@ -545,30 +527,30 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
break; break;
wchar_t replay_path[256]{}; wchar_t replay_path[256]{};
myswprintf(replay_path, L"./replay/%ls", mainGame->lstReplayList->getListItem(sel)); myswprintf(replay_path, L"./replay/%ls", mainGame->lstReplayList->getListItem(sel));
if (!ReplayMode::cur_replay.OpenReplay(replay_path)) { if (!temp_replay.OpenReplay(replay_path)) {
mainGame->stReplayInfo->setText(L""); mainGame->stReplayInfo->setText(L"Error");
break; break;
} }
wchar_t infobuf[256]{}; wchar_t infobuf[256]{};
std::wstring repinfo; std::wstring repinfo;
time_t curtime; time_t curtime;
if(ReplayMode::cur_replay.pheader.flag & REPLAY_UNIFORM) if(temp_replay.pheader.flag & REPLAY_UNIFORM)
curtime = ReplayMode::cur_replay.pheader.start_time; curtime = temp_replay.pheader.start_time;
else else
curtime = ReplayMode::cur_replay.pheader.seed; curtime = temp_replay.pheader.seed;
std::wcsftime(infobuf, sizeof infobuf / sizeof infobuf[0], L"%Y/%m/%d %H:%M:%S\n", std::localtime(&curtime)); std::wcsftime(infobuf, sizeof infobuf / sizeof infobuf[0], L"%Y/%m/%d %H:%M:%S\n", std::localtime(&curtime));
repinfo.append(infobuf); repinfo.append(infobuf);
wchar_t namebuf[4][20]{}; if (temp_replay.pheader.flag & REPLAY_SINGLE_MODE) {
ReplayMode::cur_replay.ReadName(namebuf[0]); wchar_t path[256]{};
ReplayMode::cur_replay.ReadName(namebuf[1]); BufferIO::DecodeUTF8(temp_replay.script_name.c_str(), path);
if(ReplayMode::cur_replay.pheader.flag & REPLAY_TAG) { repinfo.append(path);
ReplayMode::cur_replay.ReadName(namebuf[2]); repinfo.append(L"\n");
ReplayMode::cur_replay.ReadName(namebuf[3]);
} }
if(ReplayMode::cur_replay.pheader.flag & REPLAY_TAG) const auto& player_names = temp_replay.players;
myswprintf(infobuf, L"%ls\n%ls\n===VS===\n%ls\n%ls\n", namebuf[0], namebuf[1], namebuf[2], namebuf[3]); if(temp_replay.pheader.flag & REPLAY_TAG)
myswprintf(infobuf, L"%ls\n%ls\n===VS===\n%ls\n%ls\n", player_names[0].c_str(), player_names[1].c_str(), player_names[2].c_str(), player_names[3].c_str());
else else
myswprintf(infobuf, L"%ls\n===VS===\n%ls\n", namebuf[0], namebuf[1]); myswprintf(infobuf, L"%ls\n===VS===\n%ls\n", player_names[0].c_str(), player_names[1].c_str());
repinfo.append(infobuf); repinfo.append(infobuf);
mainGame->ebRepStartTurn->setText(L"1"); mainGame->ebRepStartTurn->setText(L"1");
mainGame->SetStaticText(mainGame->stReplayInfo, 180, mainGame->guiFont, repinfo.c_str()); mainGame->SetStaticText(mainGame->stReplayInfo, 180, mainGame->guiFont, repinfo.c_str());
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define MENU_HANDLER_H #define MENU_HANDLER_H
#include <irrlicht.h> #include <irrlicht.h>
#include "replay.h"
namespace ygo { namespace ygo {
...@@ -10,7 +11,7 @@ public: ...@@ -10,7 +11,7 @@ public:
bool OnEvent(const irr::SEvent& event) override; bool OnEvent(const irr::SEvent& event) override;
irr::s32 prev_operation{ 0 }; irr::s32 prev_operation{ 0 };
int prev_sel{ -1 }; int prev_sel{ -1 };
Replay temp_replay;
}; };
} }
......
...@@ -5,6 +5,7 @@ project "YGOPro" ...@@ -5,6 +5,7 @@ project "YGOPro"
kind "WindowedApp" kind "WindowedApp"
cppdialect "C++14" cppdialect "C++14"
rtti "Off" rtti "Off"
openmp "On"
files { "*.cpp", "*.h" } files { "*.cpp", "*.h" }
includedirs { "../ocgcore" } includedirs { "../ocgcore" }
...@@ -49,7 +50,7 @@ project "YGOPro" ...@@ -49,7 +50,7 @@ project "YGOPro"
includedirs { "../miniaudio/extras/decoders/libopus", "../miniaudio/extras/decoders/libvorbis" } includedirs { "../miniaudio/extras/decoders/libopus", "../miniaudio/extras/decoders/libvorbis" }
if not MINIAUDIO_BUILD_OPUS_VORBIS then if not MINIAUDIO_BUILD_OPUS_VORBIS then
links { "opusfile", "vorbisfile", "opus", "vorbis", "ogg" } links { "opusfile", "vorbisfile", "opus", "vorbis", "ogg" }
libdirs { OPUS_LIB_DIR, VORBIS_LIB_DIR, OGG_LIBDIR } libdirs { OPUS_LIB_DIR, OPUSFILE_LIB_DIR, VORBIS_LIB_DIR, OGG_LIB_DIR }
end end
end end
end end
...@@ -83,6 +84,7 @@ project "YGOPro" ...@@ -83,6 +84,7 @@ project "YGOPro"
filter "not system:windows" filter "not system:windows"
links { "event_pthreads", "dl", "pthread" } links { "event_pthreads", "dl", "pthread" }
filter "system:macosx" filter "system:macosx"
openmp "Off"
links { "z" } links { "z" }
defines { "GL_SILENCE_DEPRECATION" } defines { "GL_SILENCE_DEPRECATION" }
if MAC_ARM then if MAC_ARM then
...@@ -94,6 +96,7 @@ project "YGOPro" ...@@ -94,6 +96,7 @@ project "YGOPro"
end end
filter "system:linux" filter "system:linux"
links { "GL", "X11", "Xxf86vm" } links { "GL", "X11", "Xxf86vm" }
linkoptions { "-fopenmp" }
if USE_AUDIO and AUDIO_LIB == "irrklang" then if USE_AUDIO and AUDIO_LIB == "irrklang" then
links { "IrrKlang" } links { "IrrKlang" }
linkoptions{ IRRKLANG_LINK_RPATH } linkoptions{ IRRKLANG_LINK_RPATH }
......
#include "replay.h" #include "replay.h"
#include "myfilesystem.h" #include "myfilesystem.h"
#include "network.h"
#include "lzma/LzmaLib.h" #include "lzma/LzmaLib.h"
namespace ygo { namespace ygo {
...@@ -28,9 +29,7 @@ void Replay::BeginRecord() { ...@@ -28,9 +29,7 @@ void Replay::BeginRecord() {
if(!fp) if(!fp)
return; return;
#endif #endif
replay_size = 0; Reset();
comp_size = 0;
is_replaying = false;
is_recording = true; is_recording = true;
} }
void Replay::WriteHeader(ReplayHeader& header) { void Replay::WriteHeader(ReplayHeader& header) {
...@@ -111,11 +110,7 @@ bool Replay::OpenReplay(const wchar_t* name) { ...@@ -111,11 +110,7 @@ bool Replay::OpenReplay(const wchar_t* name) {
if(!rfp) if(!rfp)
return false; return false;
data_position = 0; Reset();
is_recording = false;
is_replaying = false;
replay_size = 0;
comp_size = 0;
if(std::fread(&pheader, sizeof pheader, 1, rfp) < 1) { if(std::fread(&pheader, sizeof pheader, 1, rfp) < 1) {
std::fclose(rfp); std::fclose(rfp);
return false; return false;
...@@ -138,6 +133,13 @@ bool Replay::OpenReplay(const wchar_t* name) { ...@@ -138,6 +133,13 @@ bool Replay::OpenReplay(const wchar_t* name) {
comp_size = 0; comp_size = 0;
} }
is_replaying = true; is_replaying = true;
can_read = true;
if (!ReadInfo()) {
Reset();
return false;
}
info_offset = data_position;
data_position = 0;
return true; return true;
} }
bool Replay::CheckReplay(const wchar_t* name) { bool Replay::CheckReplay(const wchar_t* name) {
...@@ -177,10 +179,6 @@ bool Replay::ReadNextResponse(unsigned char resp[]) { ...@@ -177,10 +179,6 @@ bool Replay::ReadNextResponse(unsigned char resp[]) {
unsigned char len{}; unsigned char len{};
if (!ReadData(&len, sizeof len)) if (!ReadData(&len, sizeof len))
return false; return false;
if (len > SIZE_RETURN_VALUE) {
is_replaying = false;
return false;
}
if (!ReadData(resp, len)) if (!ReadData(resp, len))
return false; return false;
return true; return true;
...@@ -197,10 +195,10 @@ void Replay::ReadHeader(ReplayHeader& header) { ...@@ -197,10 +195,10 @@ void Replay::ReadHeader(ReplayHeader& header) {
header = pheader; header = pheader;
} }
bool Replay::ReadData(void* data, size_t length) { bool Replay::ReadData(void* data, size_t length) {
if(!is_replaying) if (!is_replaying || !can_read)
return false; return false;
if (data_position + length > replay_size) { if (data_position + length > replay_size) {
is_replaying = false; can_read = false;
return false; return false;
} }
if (length) if (length)
...@@ -213,6 +211,77 @@ int32_t Replay::ReadInt32() { ...@@ -213,6 +211,77 @@ int32_t Replay::ReadInt32() {
} }
void Replay::Rewind() { void Replay::Rewind() {
data_position = 0; data_position = 0;
can_read = true;
}
void Replay::Reset() {
is_recording = false;
is_replaying = false;
can_read = false;
replay_size = 0;
comp_size = 0;
data_position = 0;
info_offset = 0;
players.clear();
params = { 0 };
decks.clear();
script_name.clear();
}
void Replay::SkipInfo(){
if (data_position == 0)
data_position += info_offset;
}
bool Replay::IsReplaying() const {
return is_replaying;
}
bool Replay::ReadInfo() {
int player_count = (pheader.flag & REPLAY_TAG) ? 4 : 2;
for (int i = 0; i < player_count; ++i) {
wchar_t name[20]{};
if (!ReadName(name))
return false;
players.push_back(name);
}
if (!ReadData(&params, sizeof params))
return false;
bool is_tag1 = pheader.flag & REPLAY_TAG;
bool is_tag2 = params.duel_flag & DUEL_TAG_MODE;
if (is_tag1 != is_tag2)
return false;
if (pheader.flag & REPLAY_SINGLE_MODE) {
uint16_t slen = Read<uint16_t>();
char filename[256]{};
if (slen == 0 || slen > sizeof(filename) - 1)
return false;
if (!ReadData(filename, slen))
return false;
filename[slen] = 0;
if (std::strncmp(filename, "./single/", 9))
return false;
script_name = filename + 9;
if (script_name.find_first_of(R"(/\)") != std::string::npos)
return false;
}
else {
for (int p = 0; p < player_count; ++p) {
DeckArray deck;
uint32_t main = Read<uint32_t>();
if (main > MAINC_MAX)
return false;
if (main)
deck.main.resize(main);
if (!ReadData(deck.main.data(), main * sizeof(uint32_t)))
return false;
uint32_t extra = Read<uint32_t>();
if (extra > MAINC_MAX)
return false;
if (extra)
deck.extra.resize(extra);
if (!ReadData(deck.extra.data(), extra * sizeof(uint32_t)))
return false;
decks.push_back(deck);
}
}
return true;
} }
} }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define REPLAY_H #define REPLAY_H
#include "config.h" #include "config.h"
#include "deck_manager.h"
namespace ygo { namespace ygo {
...@@ -13,7 +14,7 @@ namespace ygo { ...@@ -13,7 +14,7 @@ namespace ygo {
#define REPLAY_UNIFORM 0x10 #define REPLAY_UNIFORM 0x10
// max size // max size
constexpr int MAX_REPLAY_SIZE = 0x20000; constexpr int MAX_REPLAY_SIZE = 0x80000;
constexpr int MAX_COMP_SIZE = UINT16_MAX + 1; constexpr int MAX_COMP_SIZE = UINT16_MAX + 1;
struct ReplayHeader { struct ReplayHeader {
...@@ -26,6 +27,13 @@ struct ReplayHeader { ...@@ -26,6 +27,13 @@ struct ReplayHeader {
uint8_t props[8]{}; uint8_t props[8]{};
}; };
struct DuelParameters {
int32_t start_lp{};
int32_t start_hand{};
int32_t draw_count{};
uint32_t duel_flag{};
};
class Replay { class Replay {
public: public:
Replay(); Replay();
...@@ -45,10 +53,20 @@ public: ...@@ -45,10 +53,20 @@ public:
void SaveReplay(const wchar_t* name); void SaveReplay(const wchar_t* name);
// play // play
bool OpenReplay(const wchar_t* name);
static bool CheckReplay(const wchar_t* name); static bool CheckReplay(const wchar_t* name);
static bool DeleteReplay(const wchar_t* name); static bool DeleteReplay(const wchar_t* name);
static bool RenameReplay(const wchar_t* oldname, const wchar_t* newname); static bool RenameReplay(const wchar_t* oldname, const wchar_t* newname);
static size_t GetDeckPlayer(size_t deck_index) {
switch (deck_index) {
case 2:
return 3;
case 3:
return 2;
default:
return deck_index;
}
}
bool OpenReplay(const wchar_t* name);
bool ReadNextResponse(unsigned char resp[]); bool ReadNextResponse(unsigned char resp[]);
bool ReadName(wchar_t* data); bool ReadName(wchar_t* data);
void ReadHeader(ReplayHeader& header); void ReadHeader(ReplayHeader& header);
...@@ -61,6 +79,9 @@ public: ...@@ -61,6 +79,9 @@ public:
} }
int32_t ReadInt32(); int32_t ReadInt32();
void Rewind(); void Rewind();
void Reset();
void SkipInfo();
bool IsReplaying() const;
FILE* fp{ nullptr }; FILE* fp{ nullptr };
#ifdef _WIN32 #ifdef _WIN32
...@@ -71,12 +92,22 @@ public: ...@@ -71,12 +92,22 @@ public:
unsigned char* comp_data; unsigned char* comp_data;
size_t comp_size{}; size_t comp_size{};
std::vector<std::wstring> players; // 80 or 160 bytes
DuelParameters params; // 16 bytes
std::vector<DeckArray> decks; // 4 bytes, main deck, 4 bytes, extra deck
std::string script_name; // 2 bytes, script name (max: 256 bytes)
private: private:
bool ReadInfo();
unsigned char* replay_data; unsigned char* replay_data;
size_t replay_size{}; size_t replay_size{};
size_t data_position{}; size_t data_position{};
size_t info_offset{};
bool is_recording{}; bool is_recording{};
bool is_replaying{}; bool is_replaying{};
bool can_read{};
}; };
} }
......
...@@ -158,89 +158,65 @@ bool ReplayMode::StartDuel() { ...@@ -158,89 +158,65 @@ bool ReplayMode::StartDuel() {
const ReplayHeader& rh = cur_replay.pheader; const ReplayHeader& rh = cur_replay.pheader;
unsigned int seed = rh.seed; unsigned int seed = rh.seed;
std::mt19937 rnd(seed); std::mt19937 rnd(seed);
if(mainGame->dInfo.isTag) { cur_replay.SkipInfo();
cur_replay.ReadName(mainGame->dInfo.hostname); if(rh.flag & REPLAY_TAG) {
cur_replay.ReadName(mainGame->dInfo.hostname_tag); BufferIO::CopyWideString(cur_replay.players[0].c_str(), mainGame->dInfo.hostname);
cur_replay.ReadName(mainGame->dInfo.clientname_tag); BufferIO::CopyWideString(cur_replay.players[1].c_str(), mainGame->dInfo.hostname_tag);
cur_replay.ReadName(mainGame->dInfo.clientname); BufferIO::CopyWideString(cur_replay.players[2].c_str(), mainGame->dInfo.clientname_tag);
BufferIO::CopyWideString(cur_replay.players[3].c_str(), mainGame->dInfo.clientname);
} else { } else {
cur_replay.ReadName(mainGame->dInfo.hostname); BufferIO::CopyWideString(cur_replay.players[0].c_str(), mainGame->dInfo.hostname);
cur_replay.ReadName(mainGame->dInfo.clientname); BufferIO::CopyWideString(cur_replay.players[1].c_str(), mainGame->dInfo.clientname);
} }
pduel = create_duel(rnd()); pduel = create_duel(rnd());
int start_lp = cur_replay.ReadInt32(); mainGame->dInfo.duel_rule = cur_replay.params.duel_flag >> 16;
int start_hand = cur_replay.ReadInt32(); set_player_info(pduel, 0, cur_replay.params.start_lp, cur_replay.params.start_hand, cur_replay.params.draw_count);
int draw_count = cur_replay.ReadInt32(); set_player_info(pduel, 1, cur_replay.params.start_lp, cur_replay.params.start_hand, cur_replay.params.draw_count);
int opt = cur_replay.ReadInt32(); mainGame->dInfo.lp[0] = cur_replay.params.start_lp;
int duel_rule = opt >> 16; mainGame->dInfo.lp[1] = cur_replay.params.start_lp;
mainGame->dInfo.duel_rule = duel_rule; mainGame->dInfo.start_lp = cur_replay.params.start_lp;
set_player_info(pduel, 0, start_lp, start_hand, draw_count);
set_player_info(pduel, 1, start_lp, start_hand, draw_count);
mainGame->dInfo.lp[0] = start_lp;
mainGame->dInfo.lp[1] = start_lp;
mainGame->dInfo.start_lp = start_lp;
myswprintf(mainGame->dInfo.strLP[0], L"%d", mainGame->dInfo.lp[0]); myswprintf(mainGame->dInfo.strLP[0], L"%d", mainGame->dInfo.lp[0]);
myswprintf(mainGame->dInfo.strLP[1], L"%d", mainGame->dInfo.lp[1]); myswprintf(mainGame->dInfo.strLP[1], L"%d", mainGame->dInfo.lp[1]);
mainGame->dInfo.turn = 0; mainGame->dInfo.turn = 0;
if(!mainGame->dInfo.isSingleMode) { if(!(rh.flag & REPLAY_SINGLE_MODE)) {
if(!(opt & DUEL_TAG_MODE)) { if(!(rh.flag & REPLAY_TAG)) {
int main = cur_replay.ReadInt32(); for (int i = 0; i < 2; ++i) {
for(int i = 0; i < main; ++i) for (const auto& code : cur_replay.decks[i].main)
new_card(pduel, cur_replay.ReadInt32(), 0, 0, LOCATION_DECK, 0, POS_FACEDOWN_DEFENSE); new_card(pduel, code, i, i, LOCATION_DECK, 0, POS_FACEDOWN_DEFENSE);
int extra = cur_replay.ReadInt32(); for (const auto& code : cur_replay.decks[i].extra)
for(int i = 0; i < extra; ++i) new_card(pduel, code, i, i, LOCATION_EXTRA, 0, POS_FACEDOWN_DEFENSE);
new_card(pduel, cur_replay.ReadInt32(), 0, 0, LOCATION_EXTRA, 0, POS_FACEDOWN_DEFENSE); mainGame->dField.Initial(mainGame->LocalPlayer(i), cur_replay.decks[i].main.size(), cur_replay.decks[i].extra.size());
mainGame->dField.Initial(mainGame->LocalPlayer(0), main, extra); }
main = cur_replay.ReadInt32();
for(int i = 0; i < main; ++i)
new_card(pduel, cur_replay.ReadInt32(), 1, 1, LOCATION_DECK, 0, POS_FACEDOWN_DEFENSE);
extra = cur_replay.ReadInt32();
for(int i = 0; i < extra; ++i)
new_card(pduel, cur_replay.ReadInt32(), 1, 1, LOCATION_EXTRA, 0, POS_FACEDOWN_DEFENSE);
mainGame->dField.Initial(mainGame->LocalPlayer(1), main, extra);
} else { } else {
int main = cur_replay.ReadInt32(); for (const auto& code : cur_replay.decks[0].main)
for(int i = 0; i < main; ++i) new_card(pduel, code, 0, 0, LOCATION_DECK, 0, POS_FACEDOWN_DEFENSE);
new_card(pduel, cur_replay.ReadInt32(), 0, 0, LOCATION_DECK, 0, POS_FACEDOWN_DEFENSE); for (const auto& code : cur_replay.decks[0].extra)
int extra = cur_replay.ReadInt32(); new_card(pduel, code, 0, 0, LOCATION_EXTRA, 0, POS_FACEDOWN_DEFENSE);
for(int i = 0; i < extra; ++i) mainGame->dField.Initial(mainGame->LocalPlayer(0), cur_replay.decks[0].main.size(), cur_replay.decks[0].extra.size());
new_card(pduel, cur_replay.ReadInt32(), 0, 0, LOCATION_EXTRA, 0, POS_FACEDOWN_DEFENSE); for (const auto& code : cur_replay.decks[1].main)
mainGame->dField.Initial(mainGame->LocalPlayer(0), main, extra); new_tag_card(pduel, code, 0, LOCATION_DECK);
main = cur_replay.ReadInt32(); for (const auto& code : cur_replay.decks[1].extra)
for(int i = 0; i < main; ++i) new_tag_card(pduel, code, 0, LOCATION_EXTRA);
new_tag_card(pduel, cur_replay.ReadInt32(), 0, LOCATION_DECK); for (const auto& code : cur_replay.decks[2].main)
extra = cur_replay.ReadInt32(); new_card(pduel, code, 1, 1, LOCATION_DECK, 0, POS_FACEDOWN_DEFENSE);
for(int i = 0; i < extra; ++i) for (const auto& code : cur_replay.decks[2].extra)
new_tag_card(pduel, cur_replay.ReadInt32(), 0, LOCATION_EXTRA); new_card(pduel, code, 1, 1, LOCATION_EXTRA, 0, POS_FACEDOWN_DEFENSE);
main = cur_replay.ReadInt32(); mainGame->dField.Initial(mainGame->LocalPlayer(1), cur_replay.decks[2].main.size(), cur_replay.decks[2].extra.size());
for(int i = 0; i < main; ++i) for (const auto& code : cur_replay.decks[3].main)
new_card(pduel, cur_replay.ReadInt32(), 1, 1, LOCATION_DECK, 0, POS_FACEDOWN_DEFENSE); new_tag_card(pduel, code, 1, LOCATION_DECK);
extra = cur_replay.ReadInt32(); for (const auto& code : cur_replay.decks[3].extra)
for(int i = 0; i < extra; ++i) new_tag_card(pduel, code, 1, LOCATION_EXTRA);
new_card(pduel, cur_replay.ReadInt32(), 1, 1, LOCATION_EXTRA, 0, POS_FACEDOWN_DEFENSE);
mainGame->dField.Initial(mainGame->LocalPlayer(1), main, extra);
main = cur_replay.ReadInt32();
for(int i = 0; i < main; ++i)
new_tag_card(pduel, cur_replay.ReadInt32(), 1, LOCATION_DECK);
extra = cur_replay.ReadInt32();
for(int i = 0; i < extra; ++i)
new_tag_card(pduel, cur_replay.ReadInt32(), 1, LOCATION_EXTRA);
} }
} else { } else {
char filename[256]; char filename[256]{};
auto slen = cur_replay.Read<uint16_t>(); std::snprintf(filename, sizeof filename, "./single/%s", cur_replay.script_name.c_str());
if (slen > sizeof(filename) - 1) {
return false;
}
cur_replay.ReadData(filename, slen);
filename[slen] = 0;
if(!preload_script(pduel, filename)) { if(!preload_script(pduel, filename)) {
return false; return false;
} }
} }
if (!(rh.flag & REPLAY_UNIFORM)) if (!(rh.flag & REPLAY_UNIFORM))
opt |= DUEL_OLD_REPLAY; cur_replay.params.duel_flag |= DUEL_OLD_REPLAY;
start_duel(pduel, opt); start_duel(pduel, cur_replay.params.duel_flag);
return true; return true;
} }
void ReplayMode::EndDuel() { void ReplayMode::EndDuel() {
...@@ -848,12 +824,12 @@ bool ReplayMode::ReplayAnalyze(unsigned char* msg, unsigned int len) { ...@@ -848,12 +824,12 @@ bool ReplayMode::ReplayAnalyze(unsigned char* msg, unsigned int len) {
break; break;
} }
case MSG_AI_NAME: { case MSG_AI_NAME: {
int len = BufferIO::ReadInt16(pbuf); int len = buffer_read<uint16_t>(pbuf);
pbuf += len + 1; pbuf += len + 1;
break; break;
} }
case MSG_SHOW_HINT: { case MSG_SHOW_HINT: {
int len = BufferIO::ReadInt16(pbuf); int len = buffer_read<uint16_t>(pbuf);
pbuf += len + 1; pbuf += len + 1;
break; break;
} }
......
...@@ -25,7 +25,6 @@ private: ...@@ -25,7 +25,6 @@ private:
public: public:
static Replay cur_replay; static Replay cur_replay;
public:
static bool StartReplay(int skipturn); static bool StartReplay(int skipturn);
static void StopReplay(bool is_exiting = false); static void StopReplay(bool is_exiting = false);
static void SwapField(); static void SwapField();
......
...@@ -749,21 +749,25 @@ bool SingleMode::SinglePlayAnalyze(unsigned char* msg, unsigned int len) { ...@@ -749,21 +749,25 @@ bool SingleMode::SinglePlayAnalyze(unsigned char* msg, unsigned int len) {
case MSG_AI_NAME: { case MSG_AI_NAME: {
char namebuf[128]{}; char namebuf[128]{};
wchar_t wname[20]{}; wchar_t wname[20]{};
int len = BufferIO::ReadInt16(pbuf); int name_len = buffer_read<uint16_t>(pbuf);
auto begin = pbuf; if (name_len + 1 <= (int)sizeof namebuf) {
pbuf += len + 1; std::memcpy(namebuf, pbuf, name_len);
std::memcpy(namebuf, begin, len + 1); namebuf[name_len] = 0;
}
pbuf += name_len + 1;
BufferIO::DecodeUTF8(namebuf, wname); BufferIO::DecodeUTF8(namebuf, wname);
BufferIO::CopyCharArray(wname, mainGame->dInfo.clientname); BufferIO::CopyCharArray(wname, mainGame->dInfo.clientname);
break; break;
} }
case MSG_SHOW_HINT: { case MSG_SHOW_HINT: {
char msgbuf[1024]; char msgbuf[1024]{};
wchar_t msg[1024]; wchar_t msg[1024]{};
int len = BufferIO::ReadInt16(pbuf); int msg_len = buffer_read<uint16_t>(pbuf);
auto begin = pbuf; if (msg_len + 1 <= (int)sizeof msgbuf) {
pbuf += len + 1; std::memcpy(msgbuf, pbuf, msg_len);
std::memcpy(msgbuf, begin, len + 1); msgbuf[msg_len] = 0;
}
pbuf += msg_len + 1;
BufferIO::DecodeUTF8(msgbuf, msg); BufferIO::DecodeUTF8(msgbuf, msg);
mainGame->gMutex.lock(); mainGame->gMutex.lock();
mainGame->SetStaticText(mainGame->stMessage, 310, mainGame->guiFont, msg); mainGame->SetStaticText(mainGame->stMessage, 310, mainGame->guiFont, msg);
......
...@@ -16,6 +16,7 @@ project "irrlicht" ...@@ -16,6 +16,7 @@ project "irrlicht"
"_IRR_STATIC_LIB_", "_IRR_STATIC_LIB_",
"NO_IRR_USE_NON_SYSTEM_BZLIB_", "NO_IRR_USE_NON_SYSTEM_BZLIB_",
"NO_IRR_COMPILE_WITH_BZIP2_", "NO_IRR_COMPILE_WITH_BZIP2_",
"NO_IRR_COMPILE_WITH_LZMA_",
"NO_IRR_COMPILE_WITH_CONSOLE_DEVICE_", "NO_IRR_COMPILE_WITH_CONSOLE_DEVICE_",
"NO_IRR_COMPILE_WITH_DIRECT3D_8_", "NO_IRR_COMPILE_WITH_DIRECT3D_8_",
"NO_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_", "NO_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_",
......
...@@ -123,7 +123,7 @@ project "miniaudio" ...@@ -123,7 +123,7 @@ project "miniaudio"
"OPUS_HAVE_RTCD", "OPUS_X86_MAY_HAVE_SSE", "OPUS_X86_MAY_HAVE_SSE4_1", "OPUS_X86_MAY_HAVE_AVX2", "OPUS_HAVE_RTCD", "OPUS_X86_MAY_HAVE_SSE", "OPUS_X86_MAY_HAVE_SSE4_1", "OPUS_X86_MAY_HAVE_AVX2",
} }
else else
includedirs { OPUS_INCLUDE_DIR, VORBIS_INCLUDE_DIR, OGG_INCLUDE_DIR } includedirs { OPUS_INCLUDE_DIR, OPUSFILE_INCLUDE_DIR, VORBIS_INCLUDE_DIR, OGG_INCLUDE_DIR }
end end
end end
......
...@@ -48,14 +48,14 @@ newoption { trigger = "no-dxsdk", category = "YGOPro - irrlicht", description = ...@@ -48,14 +48,14 @@ newoption { trigger = "no-dxsdk", category = "YGOPro - irrlicht", description =
newoption { trigger = "no-audio", category = "YGOPro", description = "" } newoption { trigger = "no-audio", category = "YGOPro", description = "" }
newoption { trigger = "audio-lib", category = "YGOPro", description = "", value = "miniaudio, irrklang", default = AUDIO_LIB } newoption { trigger = "audio-lib", category = "YGOPro", description = "", value = "miniaudio, irrklang", default = AUDIO_LIB }
newoption { trigger = "miniaudio-include-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" }
newoption { trigger = "miniaudio-lib-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" }
newoption { trigger = "miniaudio-support-opus-vorbis", category = "YGOPro - miniaudio", description = "" } newoption { trigger = "miniaudio-support-opus-vorbis", category = "YGOPro - miniaudio", description = "" }
newoption { trigger = "no-miniaudio-support-opus-vorbis", category = "YGOPro - miniaudio", description = "" } newoption { trigger = "no-miniaudio-support-opus-vorbis", category = "YGOPro - miniaudio", description = "" }
newoption { trigger = "build-opus-vorbis", category = "YGOPro - miniaudio", description = "" } newoption { trigger = "build-opus-vorbis", category = "YGOPro - miniaudio", description = "" }
newoption { trigger = "no-build-opus-vorbis", category = "YGOPro - miniaudio", description = "" } newoption { trigger = "no-build-opus-vorbis", category = "YGOPro - miniaudio", description = "" }
newoption { trigger = "opus-include-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" } newoption { trigger = "opus-include-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" }
newoption { trigger = "opus-lib-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" } newoption { trigger = "opus-lib-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" }
newoption { trigger = "opusfile-include-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" }
newoption { trigger = "opusfile-lib-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" }
newoption { trigger = "vorbis-include-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" } newoption { trigger = "vorbis-include-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" }
newoption { trigger = "vorbis-lib-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" } newoption { trigger = "vorbis-lib-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" }
newoption { trigger = "ogg-include-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" } newoption { trigger = "ogg-include-dir", category = "YGOPro - miniaudio", description = "", value = "PATH" }
...@@ -79,6 +79,14 @@ function GetParam(param) ...@@ -79,6 +79,14 @@ function GetParam(param)
return _OPTIONS[param] or os.getenv(string.upper(string.gsub(param,"-","_"))) return _OPTIONS[param] or os.getenv(string.upper(string.gsub(param,"-","_")))
end end
function FindHeaderWithSubDir(header, subdir)
local result = os.findheader(header)
if result and subdir then
result = path.join(result, subdir)
end
return result
end
if GetParam("build-lua") then if GetParam("build-lua") then
BUILD_LUA = true BUILD_LUA = true
elseif GetParam("no-build-lua") then elseif GetParam("no-build-lua") then
...@@ -88,7 +96,7 @@ if not BUILD_LUA then ...@@ -88,7 +96,7 @@ if not BUILD_LUA then
-- at most times you need to change this if you change BUILD_LUA to false -- at most times you need to change this if you change BUILD_LUA to false
-- make sure your lua lib is built with C++ and version >= 5.3 -- make sure your lua lib is built with C++ and version >= 5.3
LUA_LIB_NAME = GetParam("lua-lib-name") LUA_LIB_NAME = GetParam("lua-lib-name")
LUA_INCLUDE_DIR = GetParam("lua-include-dir") or os.findheader(LUA_LIB_NAME) LUA_INCLUDE_DIR = GetParam("lua-include-dir") or os.findheader("lua.h")
LUA_LIB_DIR = GetParam("lua-lib-dir") or os.findlib(LUA_LIB_NAME) LUA_LIB_DIR = GetParam("lua-lib-dir") or os.findlib(LUA_LIB_NAME)
end end
...@@ -98,7 +106,7 @@ elseif GetParam("no-build-event") then ...@@ -98,7 +106,7 @@ elseif GetParam("no-build-event") then
BUILD_EVENT = false BUILD_EVENT = false
end end
if not BUILD_EVENT then if not BUILD_EVENT then
EVENT_INCLUDE_DIR = GetParam("event-include-dir") or os.findheader("event") EVENT_INCLUDE_DIR = GetParam("event-include-dir") or os.findheader("event2/event.h")
EVENT_LIB_DIR = GetParam("event-lib-dir") or os.findlib("event") EVENT_LIB_DIR = GetParam("event-lib-dir") or os.findlib("event")
end end
...@@ -108,7 +116,7 @@ elseif GetParam("no-build-freetype") then ...@@ -108,7 +116,7 @@ elseif GetParam("no-build-freetype") then
BUILD_FREETYPE = false BUILD_FREETYPE = false
end end
if not BUILD_FREETYPE then if not BUILD_FREETYPE then
FREETYPE_INCLUDE_DIR = GetParam("freetype-include-dir") or os.findheader("freetype") FREETYPE_INCLUDE_DIR = GetParam("freetype-include-dir") or FindHeaderWithSubDir("freetype2/ft2build.h", "freetype2")
FREETYPE_LIB_DIR = GetParam("freetype-lib-dir") or os.findlib("freetype") FREETYPE_LIB_DIR = GetParam("freetype-lib-dir") or os.findlib("freetype")
end end
...@@ -118,7 +126,7 @@ elseif GetParam("no-build-sqlite") then ...@@ -118,7 +126,7 @@ elseif GetParam("no-build-sqlite") then
BUILD_SQLITE = false BUILD_SQLITE = false
end end
if not BUILD_SQLITE then if not BUILD_SQLITE then
SQLITE_INCLUDE_DIR = GetParam("sqlite-include-dir") or os.findheader("sqlite3") SQLITE_INCLUDE_DIR = GetParam("sqlite-include-dir") or os.findheader("sqlite3.h")
SQLITE_LIB_DIR = GetParam("sqlite-lib-dir") or os.findlib("sqlite3") SQLITE_LIB_DIR = GetParam("sqlite-lib-dir") or os.findlib("sqlite3")
end end
...@@ -128,7 +136,7 @@ elseif GetParam("no-build-irrlicht") then ...@@ -128,7 +136,7 @@ elseif GetParam("no-build-irrlicht") then
BUILD_IRRLICHT = false BUILD_IRRLICHT = false
end end
if not BUILD_IRRLICHT then if not BUILD_IRRLICHT then
IRRLICHT_INCLUDE_DIR = GetParam("irrlicht-include-dir") or os.findheader("irrlicht") IRRLICHT_INCLUDE_DIR = GetParam("irrlicht-include-dir") or os.findheader("irrlicht.h")
IRRLICHT_LIB_DIR = GetParam("irrlicht-lib-dir") or os.findlib("irrlicht") IRRLICHT_LIB_DIR = GetParam("irrlicht-lib-dir") or os.findlib("irrlicht")
end end
...@@ -175,12 +183,14 @@ if USE_AUDIO then ...@@ -175,12 +183,14 @@ if USE_AUDIO then
MINIAUDIO_BUILD_OPUS_VORBIS = true MINIAUDIO_BUILD_OPUS_VORBIS = true
end end
if not MINIAUDIO_BUILD_OPUS_VORBIS then if not MINIAUDIO_BUILD_OPUS_VORBIS then
OPUS_INCLUDE_DIR = GetParam("opus-include-dir") or os.findheader("opus") OPUS_INCLUDE_DIR = GetParam("opus-include-dir") or FindHeaderWithSubDir("opus/opus.h", "opus")
OPUS_LIB_DIR = GetParam("opus-lib-dir") or os.findlib("opusfile") OPUS_LIB_DIR = GetParam("opus-lib-dir") or os.findlib("opus")
VORBIS_INCLUDE_DIR = GetParam("vorbis-include-dir") or os.findheader("vorbis") OPUSFILE_INCLUDE_DIR = GetParam("opusfile-include-dir") or FindHeaderWithSubDir("opus/opusfile.h", "opus")
OPUSFILE_LIB_DIR = GetParam("opusfile-lib-dir") or os.findlib("opusfile")
VORBIS_INCLUDE_DIR = GetParam("vorbis-include-dir") or os.findheader("vorbis/vorbisfile.h")
VORBIS_LIB_DIR = GetParam("vorbis-lib-dir") or os.findlib("vorbis") VORBIS_LIB_DIR = GetParam("vorbis-lib-dir") or os.findlib("vorbis")
OGG_INCLUDE_DIR = GetParam("ogg-include-dir") or os.findheader("ogg") OGG_INCLUDE_DIR = GetParam("ogg-include-dir") or os.findheader("ogg/ogg.h")
OCG_LIB_DIR = GetParam("ogg-lib-dir") or os.findlib("ogg") OGG_LIB_DIR = GetParam("ogg-lib-dir") or os.findlib("ogg")
end end
end end
elseif AUDIO_LIB == "irrklang" then elseif AUDIO_LIB == "irrklang" then
......
...@@ -663,6 +663,7 @@ ...@@ -663,6 +663,7 @@
!counter 0x106b 狂爱指示物 !counter 0x106b 狂爱指示物
!counter 0x6c 访问指示物 !counter 0x6c 访问指示物
!counter 0x6d 祝台指示物 !counter 0x6d 祝台指示物
!counter 0x6e 四季指示物
#setnames, using tab for comment #setnames, using tab for comment
!setname 0x1 正义盟军 AOJ !setname 0x1 正义盟军 AOJ
!setname 0x2 次世代 ジェネクス !setname 0x2 次世代 ジェネクス
...@@ -1245,7 +1246,7 @@ ...@@ -1245,7 +1246,7 @@
!setname 0x1c0 龙华 竜華 !setname 0x1c0 龙华 竜華
!setname 0x1c1 阿尔戈☆群星 ARGS !setname 0x1c1 阿尔戈☆群星 ARGS
!setname 0x1c2 喷水引擎 アクア・ジェット !setname 0x1c2 喷水引擎 アクア・ジェット
!setname 0x1c3 Mitsurugi !setname 0x1c3 Mitsurugi
!setname 0x1c4 征龙 征竜 !setname 0x1c4 征龙 征竜
!setname 0x1c5 再世 再世 !setname 0x1c5 再世 再世
!setname 0x1c6 统王 ドミナス !setname 0x1c6 统王 ドミナス
...@@ -1254,3 +1255,6 @@ ...@@ -1254,3 +1255,6 @@
!setname 0x1c9 星辰 ドラゴンテイル !setname 0x1c9 星辰 ドラゴンテイル
!setname 0x1ca 味美喵 ヤミー !setname 0x1ca 味美喵 ヤミー
!setname 0x1cb K9 !setname 0x1cb K9
!setname 0x1cc 瞬间移动 テレポート
!setname 0x1cd 神艺 アルトメギア
!setname 0x1ce 狱神 獄神
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment