diff --git a/CMakeLists.txt b/CMakeLists.txt index 44d6a02ce..6a2230721 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,6 +364,8 @@ endif() #### expose depends #### +include(ExternalProject) + if(UNIX) # Rescan for pthread and zlib if the build arch changed if(NOT "${DFHACK_BUILD_ARCH}" STREQUAL "${DFHACK_BUILD_ARCH_PREV}") @@ -415,6 +417,23 @@ endif() # build the lib itself if(BUILD_LIBRARY) + ExternalProject_Get_Property(xlsxio_project INSTALL_DIR) + set(XLSXIO_INSTALL_DIR ${INSTALL_DIR}) + include_directories(${XLSXIO_INSTALL_DIR}/include) + add_library(xlsxio_read STATIC IMPORTED) + set_target_properties(xlsxio_read PROPERTIES + IMPORTED_LOCATION ${XLSXIO_INSTALL_DIR}/lib/libxlsxio_read.a) + add_dependencies(xlsxio_read xlsxio_project) + + # just pull from the system until I figure out how to integrate these deps + # into the build + add_library(zip SHARED IMPORTED) + set_target_properties(zip PROPERTIES + IMPORTED_LOCATION /usr/lib64/libzip.so) + add_library(expat SHARED IMPORTED) + set_target_properties(expat PROPERTIES + IMPORTED_LOCATION /usr/lib64/libexpat.so) + add_subdirectory(library) install(FILES LICENSE.rst docs/changelog.txt DESTINATION ${DFHACK_USERDOC_DESTINATION}) endif() diff --git a/depends/CMakeLists.txt b/depends/CMakeLists.txt index aac250c39..e43c80266 100644 --- a/depends/CMakeLists.txt +++ b/depends/CMakeLists.txt @@ -17,3 +17,14 @@ option(CLSOCKET_SHARED "Build clsocket lib as shared." OFF) option(CLSOCKET_DEP_ONLY "Build for use inside other CMake projects as dependency." ON) add_subdirectory(clsocket) ide_folder(clsocket "Depends") + +set(XLSXIO_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/xlsxio) +ExternalProject_Add(xlsxio_project + PREFIX xlsxio + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/xlsxio + INSTALL_DIR ${XLSXIO_INSTALL_DIR} + BUILD_BYPRODUCTS ${XLSXIO_INSTALL_DIR}/lib/libxlsxio_read.a + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${XLSXIO_INSTALL_DIR} -DCMAKE_BUILD_TYPE=Release -DBUILD_STATIC=ON -DBUILD_SHARED=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_TOOLS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_DOCUMENTATION=OFF -DWITH_LIBZIP=ON -DZLIB_DIR=${ZLIB_DIR} +) + #-DLIBZIP_DIR= + #-DEXPAT_DIR= diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 2de293e15..583c8768f 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -144,6 +144,7 @@ set(MODULE_HEADERS include/modules/Translation.h include/modules/Units.h include/modules/World.h + include/modules/XlsxReader.h ) set(MODULE_SOURCES @@ -172,6 +173,7 @@ set(MODULE_SOURCES modules/Units.cpp modules/Windows.cpp modules/World.cpp + modules/XlsxReader.cpp ) set(STATIC_FIELDS_FILES) @@ -409,7 +411,7 @@ if(APPLE) set_target_properties(dfhack PROPERTIES SOVERSION 1.0.0) endif() -target_link_libraries(dfhack protobuf-lite clsocket lua jsoncpp_lib_static dfhack-version ${PROJECT_LIBS}) +target_link_libraries(dfhack protobuf-lite clsocket lua jsoncpp_lib_static xlsxio_read zip expat dfhack-version ${PROJECT_LIBS}) set_target_properties(dfhack PROPERTIES INTERFACE_LINK_LIBRARIES "") target_link_libraries(dfhack-client protobuf-lite clsocket jsoncpp_lib_static) diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 25843875d..49fdf4c73 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -60,6 +60,7 @@ distribution. #include "modules/Translation.h" #include "modules/Units.h" #include "modules/World.h" +#include "modules/XlsxReader.h" #include "LuaWrapper.h" #include "LuaTools.h" @@ -2474,6 +2475,47 @@ static const LuaWrapper::FunctionReg dfhack_kitchen_module[] = { {NULL, NULL} }; +/***** XlsxReader module *****/ + +static const LuaWrapper::FunctionReg dfhack_xlsxreader_module[] = { + WRAPM(XlsxReader, open_xlsx_file), + WRAPM(XlsxReader, close_xlsx_file), + WRAPM(XlsxReader, list_sheets), + WRAPM(XlsxReader, open_sheet), + WRAPM(XlsxReader, close_sheet), + WRAPM(XlsxReader, get_next_row), + {NULL, NULL} +}; + +// takes the sheet handle and returns a string, or nil if there is no next cell +// in the current row. +static int xlsxreader_get_next_cell(lua_State *L) +{ + if (lua_gettop(L) != 1 || lua_isnil(L, 1)) + { + luaL_error(L, "invalid sheet handle"); + } + luaL_checktype(L, 1, LUA_TUSERDATA); + XlsxReader::xlsx_sheet_handle sheet_handle = lua_touserdata(L, 1); + + std::string value; + bool ok = XlsxReader::get_next_cell(sheet_handle, value); + if (!ok) + { + lua_pushnil(L); + } + else + { + lua_pushstring(L, value.c_str()); + } + return 1; +} + +static const luaL_Reg dfhack_xlsxreader_funcs[] = { + {"get_next_cell", xlsxreader_get_next_cell}, + {NULL, NULL} +}; + /***** Console module *****/ namespace console { @@ -3027,6 +3069,7 @@ void OpenDFHackApi(lua_State *state) OpenModule(state, "filesystem", dfhack_filesystem_module, dfhack_filesystem_funcs); OpenModule(state, "designations", dfhack_designations_module, dfhack_designations_funcs); OpenModule(state, "kitchen", dfhack_kitchen_module); + OpenModule(state, "xlsxreader", dfhack_xlsxreader_module, dfhack_xlsxreader_funcs); OpenModule(state, "console", dfhack_console_module); OpenModule(state, "internal", dfhack_internal_module, dfhack_internal_funcs); } diff --git a/library/include/modules/XlsxReader.h b/library/include/modules/XlsxReader.h new file mode 100644 index 000000000..93ea1cf20 --- /dev/null +++ b/library/include/modules/XlsxReader.h @@ -0,0 +1,45 @@ +/* + * Wrapper for xlsxio_read library functions. + */ + +#pragma once + +#include +#include + +#include "Export.h" + +/** + * \defgroup grp_xlsx_reader Xlsx file reader + * @ingroup grp_modules + */ +namespace DFHack +{ +namespace XlsxReader +{ + +typedef void* xlsx_file_handle; +typedef void* xlsx_sheet_handle; + +// returns NULL on error +DFHACK_EXPORT xlsx_file_handle open_xlsx_file(std::string filename); +DFHACK_EXPORT void close_xlsx_file(xlsx_file_handle file_handle); +DFHACK_EXPORT std::vector list_sheets(xlsx_file_handle file_handle); + +// returns XLSXIOReaderSheet object or NULL on error +DFHACK_EXPORT xlsx_sheet_handle open_sheet( + xlsx_file_handle file_handle, std::string sheet_name); +DFHACK_EXPORT void close_sheet(xlsx_sheet_handle sheet_handle); + +// start reading the next row of data; must be called before GetNextCell . +// returns false if there is no next row to get. +DFHACK_EXPORT bool get_next_row(xlsx_sheet_handle sheet_handle); + +// fills the value param with the contents of the cell in the next column cell +// in the current row. +// returns false if there are no more cells in this row. +DFHACK_EXPORT bool get_next_cell( + xlsx_sheet_handle sheet_handle, std::string& value); + +} +} diff --git a/library/modules/XlsxReader.cpp b/library/modules/XlsxReader.cpp new file mode 100644 index 000000000..d13582450 --- /dev/null +++ b/library/modules/XlsxReader.cpp @@ -0,0 +1,115 @@ +/* + * Wrapper for xlsxio_read library functions. + * + * Sample usage: + * + * std::string filename = "sample_file.xlsx"; + * xlsxioreader xlsxfile = XlsxReader::open_xlsx_file(filename); + * if (xlsxfile == NULL) { + * printf("cannot open file: '%s'", filename.c_str()); + * return false; + * } + * auto sheetNames = XlsxReader::list_sheets(xlsxfile); + * for (auto sheetName = sheetNames.begin(); + * sheetName != sheetNames.end(); + * ++sheetName) { + * printf("reading sheet: %s\n", sheetName->c_str()); + * xlsxioreadersheet xlsxsheet = + * XlsxReader::open_sheet(xlsxfile, *sheetName); + * if (xlsxsheet == NULL) { + * printf("cannot open sheet: '%s'", sheetName->c_str()); + * continue; + * } + * std::string value; + * int row_num = 1; + * while (XlsxReader::GetNextRow(xlsxsheet)) { + * std::string s; + * printf("%d:\t", row_num); + * while (XlsxReader::GetNextCell(xlsxsheet, s)) { + * printf("%s\t", s.c_str()); + * } + * printf("\n"); + * ++row_num; + * } + * XlsxReader::close_sheet(xlsxsheet); + * } + * XlsxReader::close_xlsx_file(xlsxfile); + * return true; + */ + +#include + +#include "modules/XlsxReader.h" + +using namespace DFHack; + + +// returns NULL on error +DFHACK_EXPORT XlsxReader::xlsx_file_handle XlsxReader::open_xlsx_file( + std::string filename) +{ + return xlsxioread_open(filename.c_str()); +} + +DFHACK_EXPORT void XlsxReader::close_xlsx_file( + XlsxReader::xlsx_file_handle file_handle) +{ + xlsxioread_close((xlsxioreader)file_handle); +} + +static int list_callback(const XLSXIOCHAR* name, void* cbdata) +{ + auto sheetNames = (std::vector *)cbdata; + sheetNames->push_back(name); + return 0; +} + +DFHACK_EXPORT std::vector XlsxReader::list_sheets( + XlsxReader::xlsx_file_handle file_handle) +{ + auto sheetNames = std::vector(); + xlsxioread_list_sheets( + (xlsxioreader)file_handle, list_callback, &sheetNames); + return sheetNames; +} + +// returns XLSXIOReaderSheet object or NULL on error +DFHACK_EXPORT XlsxReader::xlsx_sheet_handle XlsxReader::open_sheet( + XlsxReader::xlsx_file_handle file_handle, std::string sheet_name) +{ + if (file_handle == NULL) + return NULL; + return xlsxioread_sheet_open( + (xlsxioreader)file_handle, sheet_name.c_str(), XLSXIOREAD_SKIP_NONE); +} + +DFHACK_EXPORT void XlsxReader::close_sheet( + XlsxReader::xlsx_sheet_handle sheet_handle) +{ + xlsxioread_sheet_close((xlsxioreadersheet)sheet_handle); +} + +// start reading the next row of data; must be called before GetNextCell . +// returns false if there is no next row to get. +DFHACK_EXPORT bool XlsxReader::get_next_row( + XlsxReader::xlsx_sheet_handle sheet_handle) +{ + return xlsxioread_sheet_next_row((xlsxioreadersheet)sheet_handle) != 0; +} + +// fills the value param with the contents of the cell in the next column cell +// in the current row. +// returns false if there are no more cells in this row. +DFHACK_EXPORT bool XlsxReader::get_next_cell( + XlsxReader::xlsx_sheet_handle sheet_handle, std::string& value) +{ + char* result; + if (!xlsxioread_sheet_next_cell_string((xlsxioreadersheet)sheet_handle, + &result)) { + value.clear(); + return false; + } + value.assign(result); + free(result); + return true; +}