Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support games in LZH archives #3033

Merged
merged 5 commits into from
Nov 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ add_library(${PROJECT_NAME} OBJECT
src/fileext_guesser.h
src/filesystem.cpp
src/filesystem.h
src/filesystem_lzh.cpp
src/filesystem_lzh.h
src/filesystem_native.cpp
src/filesystem_native.h
src/filesystem_root.cpp
Expand Down Expand Up @@ -827,7 +829,7 @@ endif()
player_find_package(NAME PNG TARGET PNG::PNG REQUIRED)
player_find_package(NAME fmt TARGET fmt::fmt REQUIRED)

# Do not use player_find_package. enable_language used by pixman on Android does work properly inside function calls
# Do not use player_find_package. enable_language used by pixman on Android does not work properly inside function calls
find_package(Pixman REQUIRED)
target_link_libraries(${PROJECT_NAME} PIXMAN::PIXMAN)

Expand Down Expand Up @@ -857,6 +859,15 @@ if(TARGET freetype)
CONFIG_BROKEN)
endif()

# lzh archive support
option(PLAYER_WITH_LHASA "Support running games in lzh archives" ON)

player_find_package(NAME lhasa
CONDITION PLAYER_WITH_LHASA
DEFINITION HAVE_LHASA
TARGET LHASA::liblhasa
)

# Sound system to use
if(${PLAYER_TARGET_PLATFORM} STREQUAL "SDL2")
set(PLAYER_AUDIO_BACKEND "SDL2" CACHE STRING "Audio system to use. Options: SDL2 OFF")
Expand Down Expand Up @@ -1452,7 +1463,7 @@ if(${PLAYER_AUDIO_BACKEND} MATCHES "^(SDL2|SDL1|libretro|psvita|3ds|switch|wii)$
if(WAV_LIBS)
message(STATUS "WAV playback: ${WAV_LIBS}")
else()
message(STATUS "WAV playback: None")
message(STATUS "WAV playback: No")
endif()

set(MIDI_LIBS)
Expand All @@ -1475,41 +1486,41 @@ if(${PLAYER_AUDIO_BACKEND} MATCHES "^(SDL2|SDL1|libretro|psvita|3ds|switch|wii)$
if(MIDI_LIBS)
message(STATUS "MIDI playback: ${MIDI_LIBS}")
else()
message(STATUS "MIDI playback: None")
message(STATUS "MIDI playback: No")
endif()

if(TARGET MPG123::libmpg123)
message(STATUS "MP3 playback: mpg123")
else()
message(STATUS "MP3 playback: None")
message(STATUS "MP3 playback: No")
endif()

if(TARGET Vorbis::vorbisfile)
message(STATUS "Ogg Vorbis playback: libvorbis")
elseif(TARGET Tremor::Tremor)
message(STATUS "Ogg Vorbis playback: tremor")
else()
message(STATUS "Ogg Vorbis playback: None")
message(STATUS "Ogg Vorbis playback: No")
endif()

if(TARGET XMP::XMP)
message(STATUS "MOD playback: libxmp")
else()
message(STATUS "MOD playback: None")
message(STATUS "MOD playback: No")
endif()

if(TARGET OpusFile::opusfile)
message(STATUS "Opus playback: opusfile")
else()
message(STATUS "Opus playback: None")
message(STATUS "Opus playback: No")
endif()

if(TARGET speexdsp::speexdsp)
message(STATUS "Resampler: speexdsp")
elseif(TARGET Samplerate::Samplerate)
message(STATUS "Resampler: libsamplerate")
else()
message(STATUS "Resampler: None")
message(STATUS "Resampler: No")
endif()
endif()

Expand All @@ -1523,6 +1534,12 @@ else()
message(STATUS "Font rendering: built-in")
endif()

if(TARGET LHASA::liblhasa)
message(STATUS "LZH archive support: lhasa")
else()
message(STATUS "LZH archive support: No")
endif()

message(STATUS "")

message(STATUS "Manual page: ${MANUAL_STATUS}")
Expand Down
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ libeasyrpg_player_a_SOURCES = \
src/fileext_guesser.h \
src/filesystem.cpp \
src/filesystem.h \
src/filesystem_lzh.cpp \
src/filesystem_lzh.h \
src/filesystem_native.cpp \
src/filesystem_native.h \
src/filesystem_root.cpp \
Expand Down Expand Up @@ -575,6 +577,7 @@ libeasyrpg_player_a_CXXFLAGS = \
$(PIXMAN_CFLAGS) \
$(FREETYPE_CFLAGS) \
$(HARFBUZZ_CFLAGS) \
$(LHASA_CFLAGS) \
$(SDL_CFLAGS) \
$(PNG_CFLAGS) \
$(ZLIB_CFLAGS) \
Expand Down Expand Up @@ -627,6 +630,7 @@ easyrpg_player_LDADD = libeasyrpg-player.a libplayer-version.a \
$(PIXMAN_LIBS) \
$(FREETYPE_LIBS) \
$(HARFBUZZ_LIBS) \
$(LHASA_LIBS) \
$(SDL_LIBS) \
$(PNG_LIBS) \
$(ZLIB_LIBS) \
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ Documentation is available at the documentation wiki: https://wiki.easyrpg.org
- SDL2 for screen backend support.
- Pixman for low level pixel manipulation.
- libpng for PNG image support.
- zlib for XYZ image support.
- fmtlib for interal logging.
- zlib for XYZ image and ZIP archive support.
- fmtlib for text formatting and interal logging.

### extended / recommended

Expand All @@ -34,6 +34,7 @@ Documentation is available at the documentation wiki: https://wiki.easyrpg.org
- libsndfile for better WAVE audio support.
- libxmp for tracker music support.
- SpeexDSP or libsamplerate for proper audio resampling.
- lhasa for LHA (.lzh) archive support.

SDL 1.2 is still supported, but deprecated.

Expand Down
64 changes: 64 additions & 0 deletions builds/cmake/Modules/Findlhasa.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#.rst:
# Findlhasa
# -----------
#
# Find the lhasa Library
#
# Imported Targets
# ^^^^^^^^^^^^^^^^
#
# This module defines the following :prop_tgt:`IMPORTED` targets:
#
# ``LHASA::liblhasa``
# The ``lhasa`` library, if found.
#
# Result Variables
# ^^^^^^^^^^^^^^^^
#
# This module will set the following variables in your project:
#
# ``LHASA_INCLUDE_DIRS``
# where to find lhasa headers.
# ``LHASA_LIBRARIES``
# the libraries to link against to use lhasa.
# ``LHASA_FOUND``
# true if the lhasa headers and libraries were found.

find_package(PkgConfig QUIET)

pkg_check_modules(PC_LHASA QUIET liblhasa)

# Look for the header file.
find_path(LHASA_INCLUDE_DIR
NAMES lhasa.h
PATH_SUFFIXES liblhasa-1.0 liblhasa
HINTS ${PC_LHASA_INCLUDE_DIRS})

# Look for the library.
# Allow LHASA_LIBRARY to be set manually, as the location of the lhasa library
if(NOT LHASA_LIBRARY)
find_library(LHASA_LIBRARY
NAMES liblhasa lhasa
HINTS ${PC_LHASA_LIBRARY_DIRS})
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(lhasa
REQUIRED_VARS LHASA_LIBRARY LHASA_INCLUDE_DIR)

if(LHASA_FOUND)
set(LHASA_INCLUDE_DIRS ${LHASA_INCLUDE_DIR})

if(NOT LHASA_LIBRARIES)
set(LHASA_LIBRARIES ${LHASA_LIBRARIES})
endif()

if(NOT TARGET LHASA::liblhasa)
add_library(LHASA::liblhasa UNKNOWN IMPORTED)
set_target_properties(LHASA::liblhasa PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LHASA_INCLUDE_DIRS}"
IMPORTED_LOCATION "${LHASA_LIBRARY}")
endif()
endif()

mark_as_advanced(LHASA_INCLUDE_DIR LHASA_LIBRARY)
2 changes: 2 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ EP_PKG_CHECK([FREETYPE],[freetype2],[Custom Font rendering.])
AS_IF([test "$with_freetype" = "yes"],[
EP_PKG_CHECK([HARFBUZZ],[harfbuzz],[Custom Font text shaping.])
])
EP_PKG_CHECK([LHASA],[liblhasa],[Support running games in lzh archives.])

AC_ARG_WITH([audio],[AS_HELP_STRING([--without-audio], [Disable audio support. @<:@default=on@:>@])])
AS_IF([test "x$with_audio" != "xno"],[
Expand Down Expand Up @@ -226,6 +227,7 @@ if test "yes" != "$silent"; then
echo " -custom Font rendering (freetype2): $with_freetype"
test "$with_freetype" = "yes" && \
echo " -custom Font text shaping (harfbuzz): $with_harfbuzz"
echo " -run games in lzh archives (lhasa): $with_lhasa"

if test "$with_audio" = "no"; then
echo "Audio support: no"
Expand Down
15 changes: 14 additions & 1 deletion src/filefinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ FilesystemView FileFinder::Save() {

if (!game_fs) {
// Filesystem not initialized yet (happens on startup)
return FilesystemView();
return {};
}

// Not overwritten, check if game fs is writable. If not redirect the write operation.
Expand Down Expand Up @@ -285,6 +285,19 @@ std::string FileFinder::GetPathInsideGamePath(StringView path_in) {
return FileFinder::GetPathInsidePath(Game().GetFullPath(), path_in);
}

bool FileFinder::IsSupportedArchiveExtension(std::string path) {
Utils::LowerCaseInPlace(path);
StringView pv = path;

#ifdef HAVE_LHASA
if (pv.ends_with(".lzh")) {
return true;
}
#endif

return pv.ends_with(".zip") || pv.ends_with(".easyrpg");
}

void FileFinder::Quit() {
root_fs.reset();
}
Expand Down
8 changes: 8 additions & 0 deletions src/filefinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,14 @@ namespace FileFinder {
*/
std::string GetPathInsideGamePath(StringView path_in);

/**
* Checks whether a passed path ends with a supported extension for an archive, e.g. ".zip"
*
* @param path path to check
* @return true when the path ends on an archive extension
*/
bool IsSupportedArchiveExtension(std::string path);

/**
* @param p tree Tree to check
* @return Whether the tree contains a valid RPG2k(3) or EasyRPG project
Expand Down
19 changes: 12 additions & 7 deletions src/filesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "filesystem.h"
#include "filesystem_native.h"
#include "filesystem_lzh.h"
#include "filesystem_zip.h"
#include "filesystem_stream.h"
#include "filefinder.h"
Expand Down Expand Up @@ -121,8 +122,7 @@ FilesystemView Filesystem::Create(StringView path) const {
internal_path += comp + "/";
} else {
path_prefix += comp + "/";
auto sv = StringView(comp);
if (sv.ends_with(".zip") || sv.ends_with(".easyrpg")) {
if (FileFinder::IsSupportedArchiveExtension(comp)) {
path_prefix.pop_back();
handle_internal = true;
}
Expand All @@ -133,14 +133,19 @@ FilesystemView Filesystem::Create(StringView path) const {
internal_path.pop_back();
}

auto filesystem = std::make_shared<ZipFilesystem>(path_prefix, Subtree(dir_of_file));
std::shared_ptr<Filesystem> filesystem = std::make_shared<ZipFilesystem>(path_prefix, Subtree(dir_of_file));
if (!filesystem->IsValid()) {
return FilesystemView();
#if HAVE_LHASA
filesystem = std::make_shared<LzhFilesystem>(path_prefix, Subtree(dir_of_file));
#endif
if (!filesystem->IsValid()) {
return {};
}
}
if (!internal_path.empty()) {
auto fs_view = filesystem->Create(internal_path);
if (!fs_view) {
return FilesystemView();
return {};
}
return fs_view;
}
Expand All @@ -149,7 +154,7 @@ FilesystemView Filesystem::Create(StringView path) const {
// This way archives with structure "archive/game_folder" launch the game directly
auto fs_view = filesystem->Subtree("");
if (!fs_view) {
return FilesystemView();
return {};
}
auto entries = fs_view.ListDirectory("");
if (entries->size() == 1 && entries->begin()->second.type == DirectoryTree::FileType::Directory) {
Expand All @@ -158,7 +163,7 @@ FilesystemView Filesystem::Create(StringView path) const {
return fs_view;
} else {
if (!(Exists(path) || !IsDirectory(path, true))) {
return FilesystemView();
return {};
}

// Handle as a normal path in the current filesystem
Expand Down
Loading