From 93b81484a61cf3b3a9247a07e5022b09ecc3bffd Mon Sep 17 00:00:00 2001 From: Syping Date: Wed, 25 Aug 2021 00:30:10 +0200 Subject: [PATCH] initial commit --- CMakeLists.txt | 51 +++++++ src/RagePhoto-Extract.cpp | 69 +++++++++ src/RagePhoto.cpp | 308 ++++++++++++++++++++++++++++++++++++++ src/RagePhoto.h | 69 +++++++++ src/libragephoto_global.h | 32 ++++ tests/IconvTest.cpp | 37 +++++ 6 files changed, 566 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 src/RagePhoto-Extract.cpp create mode 100644 src/RagePhoto.cpp create mode 100644 src/RagePhoto.h create mode 100644 src/libragephoto_global.h create mode 100644 tests/IconvTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ab1fb75 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.7) +enable_language(CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if (WIN32) + set(LIBRAGEPHOTO_NAME libragephoto) + set(LIBRAGEPHOTO_DEFINES USE_WINAPI) +else() + set(LIBRAGEPHOTO_NAME ragephoto) + try_run(ICONV_RUN ICONV_COMPILE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/tests/IconvTest.cpp) + if (ICONV_COMPILE) + set(LIBRAGEPHOTO_DEFINES USE_ICONV) + endif() +endif() + +option(WITH_BENCHMARK "Benchmark RagePhoto Parsing Engine" OFF) +if (WITH_BENCHMARK) + list(APPEND LIBRAGEPHOTO_DEFINES + RAGEPHOTO_BENCHMARK + ) +endif() + +project(${LIBRAGEPHOTO_NAME} LANGUAGES CXX) +add_library(${LIBRAGEPHOTO_NAME} SHARED + src/libragephoto_global.h + src/RagePhoto.cpp + src/RagePhoto.h +) +target_compile_definitions(${LIBRAGEPHOTO_NAME} PRIVATE + LIBRAGEPHOTO_LIBRARY + ${LIBRAGEPHOTO_DEFINES} +) +install(TARGETS ${LIBRAGEPHOTO_NAME} DESTINATION lib) +install(FILES + src/RagePhoto.h + src/libragephoto_global.h + DESTINATION include +) +set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib) + +project(ragephoto-extract LANGUAGES CXX) +add_executable(ragephoto-extract + src/libragephoto_global.h + src/RagePhoto-Extract.cpp + src/RagePhoto.h +) +target_link_libraries(ragephoto-extract ${LIBRAGEPHOTO_NAME}) +install(TARGETS ragephoto-extract DESTINATION bin) diff --git a/src/RagePhoto-Extract.cpp b/src/RagePhoto-Extract.cpp new file mode 100644 index 0000000..79b9f00 --- /dev/null +++ b/src/RagePhoto-Extract.cpp @@ -0,0 +1,69 @@ +/***************************************************************************** +* libragephoto RAGE Photo Parser +* Copyright (C) 2021 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include "RagePhoto.h" +#include + +int main(int argc, char *argv[]) +{ + if (argc != 3) { + std::cout << "Usage: " << argv[0] << " [snapmatic] [jpegout]" << std::endl; + return 0; + } + + // Initialise RagePhoto + RagePhoto ragePhoto; + + // Read file + FILE *file = fopen(argv[1], "rb"); + if (!file) + return -1; + const int fseek_end_value = fseek(file, 0, SEEK_END); + if (fseek_end_value == -1) { + fclose(file); + return -1; + } + size_t file_size = ftell(file); + if (file_size == -1) { + fclose(file); + return -1; + } + const int fseek_set_value = fseek(file, 0, SEEK_SET); + if (fseek_set_value == -1) { + fclose(file); + return -1; + } + char *data = static_cast(malloc(file_size)); + const size_t file_rsize = fread(data, 1, file_size, file); + if (file_size != file_rsize) { + fclose(file); + return -1; + } + + ragePhoto.load(data, file_size); + fclose(file); + + // Write jpeg + file = fopen(argv[2], "wb"); + if (!file) + return -1; + fwrite(ragePhoto.photoData(), sizeof(char), ragePhoto.photoSize(), file); + fclose(file); + + return 0; +} diff --git a/src/RagePhoto.cpp b/src/RagePhoto.cpp new file mode 100644 index 0000000..3fba132 --- /dev/null +++ b/src/RagePhoto.cpp @@ -0,0 +1,308 @@ +/***************************************************************************** +* libragephoto RAGE Photo Parser +* Copyright (C) 2021 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include "RagePhoto.h" +#include +#include +#include + +#ifdef RAGEPHOTO_BENCHMARK +#include +#endif + +#ifdef USE_ICONV +#include "iconv.h" +#endif + +RagePhoto::RagePhoto() +{ + p_photoData = nullptr; +} + +RagePhoto::~RagePhoto() +{ + free(p_photoData); +} + +bool RagePhoto::load(const char *data, size_t length) +{ +#ifdef RAGEPHOTO_BENCHMARK + auto benchmark_parse_start = std::chrono::high_resolution_clock::now(); +#endif + + size_t pos = 0; + char uInt32Buffer[4]; + size_t size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + + uint32_t format = charToUInt32LE(uInt32Buffer); + if (format == static_cast(PhotoFormat::GTA5)) { + char photoHeader[256]; + size = bRead(data, photoHeader, &pos, 256, length); + if (size != 256) + return false; + +#ifdef USE_ICONV + iconv_t iconv_in = iconv_open("UTF-8", "UTF-16LE"); + if (iconv_in == (iconv_t)-1) + return false; + char photoString[256]; + size_t src_s = sizeof(photoHeader); + size_t dst_s = sizeof(photoString); + char *src = photoHeader; + char *dst = photoString; + size_t ret = iconv(iconv_in, &src, &src_s, &dst, &dst_s); + iconv_close(iconv_in); + if (ret == static_cast(-1)) + return false; + p_photoString = std::string(photoString); +#endif + + size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + p_headerSum = charToUInt32LE(uInt32Buffer); + + size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + p_endOfFile = charToUInt32LE(uInt32Buffer); + + size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + p_jsonOffset = charToUInt32LE(uInt32Buffer); + + size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + p_titlOffset = charToUInt32LE(uInt32Buffer); + + size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + p_descOffset = charToUInt32LE(uInt32Buffer); + + char markerBuffer[4]; + size = bRead(data, markerBuffer, &pos, 4, length); + if (size != 4) + return false; + if (strncmp(markerBuffer, "JPEG", 4) != 0) + return false; + + size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + p_photoBuffer = charToUInt32LE(uInt32Buffer); + + size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + p_photoSize = charToUInt32LE(uInt32Buffer); + + p_photoData = static_cast(malloc(p_photoSize)); + if (!p_photoData) { + return false; + } + size = bRead(data, p_photoData, &pos, p_photoSize, length); + if (size != p_photoSize) { + free(p_photoData); + return false; + } + + pos = p_jsonOffset + 264; + size = bRead(data, markerBuffer, &pos, 4, length); + if (size != 4) + return false; + if (strncmp(markerBuffer, "JSON", 4) != 0) + return false; + + size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + p_jsonBuffer = charToUInt32LE(uInt32Buffer); + + char *t_jsonData = static_cast(malloc(p_jsonBuffer)); + if (!t_jsonData) + return false; + size = bRead(data, t_jsonData, &pos, p_jsonBuffer, length); + if (size != p_jsonBuffer) { + free(t_jsonData); + return false; + } + p_jsonString = std::string(t_jsonData); + free(t_jsonData); + + pos = p_titlOffset + 264; + size = bRead(data, markerBuffer, &pos, 4, length); + if (size != 4) + return false; + if (strncmp(markerBuffer, "TITL", 4) != 0) + return false; + + size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + p_titlBuffer = charToUInt32LE(uInt32Buffer); + + char *t_titlData = static_cast(malloc(p_titlBuffer)); + if (!t_titlData) + return false; + size = bRead(data, t_titlData, &pos, p_titlBuffer, length); + if (size != p_titlBuffer) { + free(t_titlData); + return false; + } + p_titleString = std::string(t_titlData); + free(t_titlData); + + pos = p_descOffset + 264; + size = bRead(data, markerBuffer, &pos, 4, length); + if (size != 4) + return false; + if (strncmp(markerBuffer, "DESC", 4) != 0) + return false; + + size = bRead(data, uInt32Buffer, &pos, 4, length); + if (size != 4) + return false; + p_descBuffer = charToUInt32LE(uInt32Buffer); + + char *t_descData = static_cast(malloc(p_descBuffer)); + if (!t_descData) + return false; + size = bRead(data, t_descData, &pos, p_descBuffer, length); + if (size != p_descBuffer) { + free(t_descData); + return false; + } + p_descriptionString = std::string(t_descData); + free(t_descData); + + pos = p_endOfFile + 260; + size = bRead(data, markerBuffer, &pos, 4, length); + if (size != 4) + return false; + if (strncmp(markerBuffer, "JEND", 4) != 0) + return false; + +#ifdef RAGEPHOTO_BENCHMARK + auto benchmark_parse_end = std::chrono::high_resolution_clock::now(); + auto benchmark_ns = std::chrono::duration_cast(benchmark_parse_end - benchmark_parse_start); + std::cout << "Benchmark: " << benchmark_ns.count() << "ns" << std::endl; +#endif + + return true; + } + return false; +} + +const char* RagePhoto::photoData() +{ + return p_photoData; +} + +const uint32_t RagePhoto::photoSize() +{ + return p_photoSize; +} + +const std::string RagePhoto::description() +{ + return p_descriptionString; +} + +const std::string RagePhoto::json() +{ + return p_jsonString; +} + +const std::string RagePhoto::header() +{ + return p_photoString; +} + +const std::string RagePhoto::title() +{ + return p_titleString; +} + +size_t RagePhoto::bRead(const char *input, char *output, size_t *pos, size_t len) +{ +#ifdef READ_USE_FOR + for (size_t i = 0; i < len; i++) { + output[i] = input[*pos+i]; + } +#else + memcpy(output, &input[*pos], sizeof(char) * len); +#endif + *pos = *pos + len; + return len; +} + +size_t RagePhoto::bRead(const char *input, char *output, size_t *pos, size_t len, size_t inputLen) +{ + size_t readLen = 0; + if (*pos >= inputLen) + return readLen; + readLen = inputLen - *pos; + if (readLen > len) + readLen = len; +#ifdef READ_USE_FOR + for (size_t i = 0; i < readLen; i++) { + output[i] = input[*pos+i]; + } +#else + memcpy(output, &input[*pos], sizeof(char) * readLen); +#endif + *pos = *pos + readLen; + return readLen; +} + +uint32_t RagePhoto::charToUInt32BE(char *x) +{ + return (static_cast(x[0]) << 24 | + static_cast(x[1]) << 16 | + static_cast(x[2]) << 8 | + static_cast(x[3])); +} + +uint32_t RagePhoto::charToUInt32LE(char *x) +{ + return (static_cast(x[3]) << 24 | + static_cast(x[2]) << 16 | + static_cast(x[1]) << 8 | + static_cast(x[0])); +} + +void RagePhoto::uInt32ToCharBE(uint32_t x, char *y) +{ + y[0] = x >> 24; + y[1] = x >> 16; + y[2] = x >> 8; + y[3] = x; +} + +void RagePhoto::uInt32ToCharLE(uint32_t x, char *y) +{ + y[0] = x; + y[1] = x >> 8; + y[2] = x >> 16; + y[3] = x >> 24; +} diff --git a/src/RagePhoto.h b/src/RagePhoto.h new file mode 100644 index 0000000..8f4a899 --- /dev/null +++ b/src/RagePhoto.h @@ -0,0 +1,69 @@ +/***************************************************************************** +* libragephoto RAGE Photo Parser +* Copyright (C) 2021 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#ifndef RAGEPHOTO_H +#define RAGEPHOTO_H + +#include "libragephoto_global.h" +#include +#include +#include + +class LIBRAGEPHOTO_EXPORT RagePhoto +{ +public: + enum class PhotoFormat : uint32_t { + GTA5 = 0x01000000U, + RDR2 = 0x04000000U, + Undefined = 0, + }; + RagePhoto(); + ~RagePhoto(); + bool load(const char *data, size_t length); + const char *photoData(); + const uint32_t photoSize(); + const std::string description(); + const std::string json(); + const std::string header(); + const std::string title(); + +private: + inline size_t bRead(const char *input, char *output, size_t *pos, size_t len); + inline size_t bRead(const char *input, char *output, size_t *pos, size_t len, size_t inputLen); + inline uint32_t charToUInt32BE(char *x); + inline uint32_t charToUInt32LE(char *x); + inline void uInt32ToCharBE(uint32_t x, char *y); + inline void uInt32ToCharLE(uint32_t x, char *y); + char* p_photoData; + std::string p_descriptionString; + std::string p_jsonString; + std::string p_photoString; + std::string p_titleString; + uint32_t p_descBuffer; + uint32_t p_descOffset; + uint32_t p_endOfFile; + uint32_t p_headerSum; + uint32_t p_jsonBuffer; + uint32_t p_jsonOffset; + uint32_t p_photoBuffer; + uint32_t p_photoSize; + uint32_t p_titlBuffer; + uint32_t p_titlOffset; +}; + +#endif // RAGEPHOTO_H diff --git a/src/libragephoto_global.h b/src/libragephoto_global.h new file mode 100644 index 0000000..b8f63b2 --- /dev/null +++ b/src/libragephoto_global.h @@ -0,0 +1,32 @@ +/***************************************************************************** +* libragephoto RAGE Photo Parser +* Copyright (C) 2021 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#ifndef LIBRAGEPHOTO_GLOBAL_H +#define LIBRAGEPHOTO_GLOBAL_H + +#ifdef _WIN32 +#ifdef LIBRAGEPHOTO_LIBRARY +#define LIBRAGEPHOTO_EXPORT __declspec(dllexport) +#else +#define LIBRAGEPHOTO_EXPORT __declspec(dllimport) +#endif +#else +#define LIBRAGEPHOTO_EXPORT __attribute__((visibility("default"))) +#endif + +#endif // LIBRAGEPHOTO_GLOBAL_H diff --git a/tests/IconvTest.cpp b/tests/IconvTest.cpp new file mode 100644 index 0000000..0357d8a --- /dev/null +++ b/tests/IconvTest.cpp @@ -0,0 +1,37 @@ +/***************************************************************************** +* libragephoto RAGE Photo Parser +* Copyright (C) 2021 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include "iconv.h" +#include "stdio.h" + +int main(int argc, char *argv[]) +{ + iconv_t instance = iconv_open("UTF-16LE", "UTF-8"); + if (instance == (iconv_t)-1) + return 1; + char src[] = "Test"; + char dst[256]; + size_t src_s = sizeof(src); + size_t dst_s = sizeof(dst); + char *isrc = src; + char *idst = dst; + iconv(instance, &isrc, &src_s, &idst, &dst_s); + iconv_close(instance); + printf("%s\n", dst); + return 0; +}