Version 1.08 with public source code release.

This commit is contained in:
icex2
2020-10-03 20:56:55 +02:00
parent 656148121d
commit 762bf140c0
555 changed files with 55502 additions and 3 deletions
+105
View File
@@ -0,0 +1,105 @@
/**
* Common API to use for any devices implementing piu (pro) button controls
* (e.g. keyboard emulation, custom IOs)
*/
#ifndef PTAPI_IO_PIUBTN_H
#define PTAPI_IO_PIUBTN_H
#include <stdbool.h>
#include <stdint.h>
/**
* Button inputs of a single player
*/
struct ptapi_io_piubtn_inputs {
bool start;
bool back;
bool left;
bool right;
};
/**
* Button light outputs of a single player
*/
struct ptapi_io_piubtn_outputs {
bool start;
bool back;
bool left;
bool right;
};
typedef const char* (*ptapi_io_piubtn_ident_t)(void);
typedef bool (*ptapi_io_piubtn_open_t)(void);
typedef void (*ptapi_io_piubtn_close_t)(void);
typedef bool (*ptapi_io_piubtn_recv_t)(void);
typedef bool (*ptapi_io_piubtn_send_t)(void);
typedef void (*ptapi_io_piubtn_get_input_t)(uint8_t player, struct ptapi_io_piubtn_inputs* inputs);
typedef void (*ptapi_io_piubtn_set_output_t)(uint8_t player, const struct ptapi_io_piubtn_outputs* outputs);
/**
* API functions in a struct to make them easier to handle and have less boilerplate
*/
struct ptapi_io_piubtn_api {
ptapi_io_piubtn_ident_t ident;
ptapi_io_piubtn_open_t open;
ptapi_io_piubtn_close_t close;
ptapi_io_piubtn_recv_t recv;
ptapi_io_piubtn_send_t send;
ptapi_io_piubtn_get_input_t get_input;
ptapi_io_piubtn_set_output_t set_output;
};
/**
* Return an identifier of the implementation. Used for logging and debugging.
*
* @return Identifier string for your implementation (e.g. "my-custom-btnio")
*/
const char* ptapi_io_piubtn_ident(void);
/**
* Open the piubtn device
*
* Open any file handles, spawn threads and/or initialize your device or emulation code to be ready to accept further
* commands.
*
* @return True if opening/initializing your implementation was sucessful, false on failure
*/
bool ptapi_io_piubtn_open(void);
/**
* Close and cleanup your device, shut down threads, free resources etc
*/
void ptapi_io_piubtn_close(void);
/**
* Get _ALL_ readable data from your device in this single recv call.
*
* @return True on success, false on error
*/
bool ptapi_io_piubtn_recv(void);
/**
* Send any output data (e.g. lights) to your device
*
* @return True on success, false on error
*/
bool ptapi_io_piubtn_send(void);
/**
* Get buffered button input data
*
* @param player Player id (0, 1)
* @param inputs Pointer to buffer to copy inputs to. Every time this is invoked, this buffer is nulled. If you do not
* write any data to this, all inputs remain un-triggered.
*/
void ptapi_io_piubtn_get_input(uint8_t player, struct ptapi_io_piubtn_inputs* inputs);
/**
* Set button light outputs (i.e. buffer them)
*
* @param player Player id (0, 1)
* @param outputs Pointer to output data to copy to your private buffers.
*/
void ptapi_io_piubtn_set_output(uint8_t player, const struct ptapi_io_piubtn_outputs* outputs);
#endif
+163
View File
@@ -0,0 +1,163 @@
/**
* Common API to use for any devices implementing (MK6) piuio controls
* (e.g. keyboard emulation, custom IOs)
*/
#ifndef PTAPI_IO_PIUIO_H
#define PTAPI_IO_PIUIO_H
#include <stdbool.h>
#include <stdint.h>
/**
* Enum for pad input sensor groups (four sensors per button/panel)
*/
enum ptapi_io_piuio_sensor_group {
PTAPI_IO_PIUIO_SENSOR_GROUP_RIGHT = 0,
PTAPI_IO_PIUIO_SENSOR_GROUP_LEFT = 1,
PTAPI_IO_PIUIO_SENSOR_GROUP_DOWN = 2,
PTAPI_IO_PIUIO_SENSOR_GROUP_UP = 3,
PTAPI_IO_PIUIO_SENSOR_GROUP_NUM = 4,
};
/**
* Inputs of all arrows of a single pad (per sensor group)
*/
struct ptapi_io_piuio_pad_inputs {
bool lu;
bool ru;
bool cn;
bool ld;
bool rd;
};
/**
* System/operator inputs
*/
struct ptapi_io_piuio_sys_inputs {
bool test;
bool service;
bool clear;
bool coin;
bool coin2;
};
/**
* Pad light outputs
*/
struct ptapi_io_piuio_pad_outputs {
bool lu;
bool ru;
bool cn;
bool ld;
bool rd;
};
/**
* Cabinet light outputs
*/
struct ptapi_io_piuio_cab_outputs {
bool bass;
bool halo_r2;
bool halo_r1;
bool halo_l2;
bool halo_l1;
};
typedef const char* (*ptapi_io_piuio_ident_t)(void);
typedef bool (*ptapi_io_piuio_open_t)(void);
typedef void (*ptapi_io_piuio_close_t)(void);
typedef bool (*ptapi_io_piuio_recv_t)(void);
typedef bool (*ptapi_io_piuio_send_t)(void);
typedef void (*ptapi_io_piuio_get_input_pad_t)(uint8_t player, enum ptapi_io_piuio_sensor_group sensor_group,
struct ptapi_io_piuio_pad_inputs* inputs);
typedef void (*ptapi_io_piuio_get_input_sys_t)(struct ptapi_io_piuio_sys_inputs* inputs);
typedef void (*ptapi_io_piuio_set_output_pad_t)(uint8_t player, struct ptapi_io_piuio_pad_outputs* outputs);
typedef void (*ptapi_io_piuio_set_output_cab_t)(const struct ptapi_io_piuio_cab_outputs* outputs);
/**
* API functions in a struct to make them easier to handle and have less boilerplate
*/
struct ptapi_io_piuio_api {
ptapi_io_piuio_ident_t ident;
ptapi_io_piuio_open_t open;
ptapi_io_piuio_close_t close;
ptapi_io_piuio_recv_t recv;
ptapi_io_piuio_send_t send;
ptapi_io_piuio_get_input_pad_t get_input_pad;
ptapi_io_piuio_get_input_sys_t get_input_sys;
ptapi_io_piuio_set_output_pad_t set_output_pad;
ptapi_io_piuio_set_output_cab_t set_output_cab;
};
/**
* Return an identifier of the implementation. Used for logging and debugging.
*
* @return Identifier string for your implementation (e.g. "my-custom-io")
*/
const char* ptapi_io_piuio_ident(void);
/**
* Open the piuio device
*
* Open any file handles, spawn threads and/or initialize your device or emulation code to be ready to accept further
* commands.
*
* @return True if opening/initializing your implementation was sucessful, false on failure
*/
bool ptapi_io_piuio_open(void);
/**
* Close and cleanup your device, shut down threads, free resources etc
*/
void ptapi_io_piuio_close(void);
/**
* Get _ALL_ readable data from your device in this single recv call. If you have to address single sensores like the
* real piuio, you have to do this on a single call to this function.
*
* @return True on success, false on error
*/
bool ptapi_io_piuio_recv(void);
/**
* Send any output data (e.g. lights) to your device
*
* @return True on success, false on error
*/
bool ptapi_io_piuio_send(void);
/**
* Get buffered pad input data
*
* @param player Player id (0, 1)
* @param sensor_group Sensor group states to get
* @param inputs Pointer to buffer to copy inputs of the selected sensor group to. Every time this is invoked, this
* buffer is nulled. If you do not write any data to this, all inputs remain un-triggered.
*/
void ptapi_io_piuio_get_input_pad(uint8_t player, enum ptapi_io_piuio_sensor_group sensor_group,
struct ptapi_io_piuio_pad_inputs* inputs);
/**
* Get buffered system (test, service, clear, coin) inputs
*
* @param inputs Pointer to buffer to copy inputs to. Every time this is invoked, this buffer is nulled. If you do not
* write any data to this, all inputs remain un-triggered.
*/
void ptapi_io_piuio_get_input_sys(struct ptapi_io_piuio_sys_inputs* inputs);
/**
* Set pad light outputs (i.e. buffer them)
*
* @param player Player id (0, 1)
* @param outputs Pointer to output data to copy to your private buffers.
*/
void ptapi_io_piuio_set_output_pad(uint8_t player, const struct ptapi_io_piuio_pad_outputs* outputs);
/**
* Set cabinet light outputs (i.e. buffer them)
*
* @param outputs Pointer to output data to copy to your private buffers.
*/
void ptapi_io_piuio_set_output_cab(const struct ptapi_io_piuio_cab_outputs* outputs);
#endif
+39
View File
@@ -0,0 +1,39 @@
/**
* Pumptools hooks into the X11 event dispatch loop to grab different events and exposes
* keyboard input events. This allows external modules to react to these inputs.
*/
#ifndef PTAPI_IO_X11_INPUT_HOOK_H
#define PTAPI_IO_X11_INPUT_HOOK_H
#include <X11/Xutil.h>
/**
* Input hooking structure with dispatch functions.
* Set functions you do not use to NULL.
*/
struct ptapi_io_x11_input_hook_handler {
/**
* Dispatch the key press event
*
* @param key Key pressed
*/
void (*dispatch_key_press)(KeySym key);
/**
* Dispatch the key release event
*
* @param key Key released
*/
void (*dispatch_key_release)(KeySym key);
};
typedef const struct ptapi_io_x11_input_hook_handler* (*ptapi_io_x11_input_handler_hook_t)(void);
/**
* Get an input hook handler to receive keyboard inputs from the X11 event loop.
*
* @return Either a valid handler structure with dispatch functions or NULL if unused.
*/
const struct ptapi_io_x11_input_hook_handler* ptapi_io_x11_input_handler_hook(void);
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+163
View File
@@ -0,0 +1,163 @@
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CMOCKA_PRIVATE_H_
#define CMOCKA_PRIVATE_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdint.h>
#ifdef _WIN32
#include <windows.h>
# ifdef _MSC_VER
# include <stdio.h> /* _snprintf */
# undef inline
# define inline __inline
# ifndef va_copy
# define va_copy(dest, src) (dest = src)
# endif
# define strcasecmp _stricmp
# define strncasecmp _strnicmp
# if defined(HAVE__SNPRINTF_S)
# undef snprintf
# define snprintf(d, n, ...) _snprintf_s((d), (n), _TRUNCATE, __VA_ARGS__)
# else /* HAVE__SNPRINTF_S */
# if defined(HAVE__SNPRINTF)
# undef snprintf
# define snprintf _snprintf
# else /* HAVE__SNPRINTF */
# if !defined(HAVE_SNPRINTF)
# error "no snprintf compatible function found"
# endif /* HAVE_SNPRINTF */
# endif /* HAVE__SNPRINTF */
# endif /* HAVE__SNPRINTF_S */
# if defined(HAVE__VSNPRINTF_S)
# undef vsnprintf
# define vsnprintf(s, n, f, v) _vsnprintf_s((s), (n), _TRUNCATE, (f), (v))
# else /* HAVE__VSNPRINTF_S */
# if defined(HAVE__VSNPRINTF)
# undef vsnprintf
# define vsnprintf _vsnprintf
# else
# if !defined(HAVE_VSNPRINTF)
# error "No vsnprintf compatible function found"
# endif /* HAVE_VSNPRINTF */
# endif /* HAVE__VSNPRINTF */
# endif /* HAVE__VSNPRINTF_S */
# endif /* _MSC_VER */
/*
* Backwards compatibility with headers shipped with Visual Studio 2005 and
* earlier.
*/
WINBASEAPI BOOL WINAPI IsDebuggerPresent(VOID);
#ifndef PRIdS
# define PRIdS "Id"
#endif
#ifndef PRIu64
# define PRIu64 "I64u"
#endif
#ifndef PRIuMAX
# define PRIuMAX PRIu64
#endif
#ifndef PRIxMAX
#define PRIxMAX "I64x"
#endif
#ifndef PRIXMAX
#define PRIXMAX "I64X"
#endif
#else /* _WIN32 */
#ifndef __PRI64_PREFIX
# if __WORDSIZE == 64
# define __PRI64_PREFIX "l"
# else
# define __PRI64_PREFIX "ll"
# endif
#endif
#ifndef PRIdS
# define PRIdS "zd"
#endif
#ifndef PRIu64
# define PRIu64 __PRI64_PREFIX "u"
#endif
#ifndef PRIuMAX
# define PRIuMAX __PRI64_PREFIX "u"
#endif
#ifndef PRIxMAX
#define PRIxMAX __PRI64_PREFIX "x"
#endif
#ifndef PRIXMAX
#define PRIXMAX __PRI64_PREFIX "X"
#endif
#endif /* _WIN32 */
/** Free memory space */
#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
/** Zero a structure */
#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
/** Zero a structure given a pointer to the structure */
#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
/** Get the size of an array */
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
/** Overwrite the complete string with 'X' */
#define BURN_STRING(x) do { if ((x) != NULL) memset((x), 'X', strlen((x))); } while(0)
/**
* This is a hack to fix warnings. The idea is to use this everywhere that we
* get the "discarding const" warning by the compiler. That doesn't actually
* fix the real issue, but marks the place and you can search the code for
* discard_const.
*
* Please use this macro only when there is no other way to fix the warning.
* We should use this function in only in a very few places.
*
* Also, please call this via the discard_const_p() macro interface, as that
* makes the return type safe.
*/
#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
/**
* Type-safe version of discard_const
*/
#define discard_const_p(type, ptr) ((type *)discard_const(ptr))
#endif /* CMOCKA_PRIVATE_H_ */
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#define HAVE_MALLOC_H
#define HAVE_INTTYPES_H
#define HAVE_SIGNAL_H
#define HAVE_STRINGS_H
File diff suppressed because it is too large Load Diff
+609
View File
@@ -0,0 +1,609 @@
/* ========================================================================================== */
/* FMOD Ex - C++ header file. Copyright (c), Firelight Technologies Pty, Ltd. 2004-2016. */
/* */
/* Use this header in conjunction with fmod.h (which contains all the constants / callbacks) */
/* to develop using C++ classes. */
/* ========================================================================================== */
#ifndef _FMOD_HPP
#define _FMOD_HPP
#include "fmod.h"
/*
Constant and defines
*/
/*
FMOD Namespace
*/
namespace FMOD
{
class System;
class Sound;
class Channel;
class ChannelGroup;
class SoundGroup;
class Reverb;
class DSP;
class DSPConnection;
class Geometry;
/*
FMOD global system functions (optional).
*/
inline FMOD_RESULT Memory_Initialize(void *poolmem, int poollen, FMOD_MEMORY_ALLOCCALLBACK useralloc, FMOD_MEMORY_REALLOCCALLBACK userrealloc, FMOD_MEMORY_FREECALLBACK userfree, FMOD_MEMORY_TYPE memtypeflags = FMOD_MEMORY_ALL) { return FMOD_Memory_Initialize(poolmem, poollen, useralloc, userrealloc, userfree, memtypeflags); }
inline FMOD_RESULT Memory_GetStats (int *currentalloced, int *maxalloced, bool blocking = true) { return FMOD_Memory_GetStats(currentalloced, maxalloced, blocking); }
inline FMOD_RESULT Debug_SetLevel(FMOD_DEBUGLEVEL level) { return FMOD_Debug_SetLevel(level); }
inline FMOD_RESULT Debug_GetLevel(FMOD_DEBUGLEVEL *level) { return FMOD_Debug_GetLevel(level); }
inline FMOD_RESULT File_SetDiskBusy(int busy) { return FMOD_File_SetDiskBusy(busy); }
inline FMOD_RESULT File_GetDiskBusy(int *busy) { return FMOD_File_GetDiskBusy(busy); }
/*
FMOD System factory functions.
*/
inline FMOD_RESULT System_Create(System **system) { return FMOD_System_Create((FMOD_SYSTEM **)system); }
/*
'System' API
*/
class System
{
private:
System(); /* Constructor made private so user cannot statically instance a System class.
System_Create must be used. */
public:
FMOD_RESULT F_API release ();
// Pre-init functions.
FMOD_RESULT F_API setOutput (FMOD_OUTPUTTYPE output);
FMOD_RESULT F_API getOutput (FMOD_OUTPUTTYPE *output);
FMOD_RESULT F_API getNumDrivers (int *numdrivers);
FMOD_RESULT F_API getDriverInfo (int id, char *name, int namelen, FMOD_GUID *guid);
FMOD_RESULT F_API getDriverInfoW (int id, short *name, int namelen, FMOD_GUID *guid);
FMOD_RESULT F_API getDriverCaps (int id, FMOD_CAPS *caps, int *controlpaneloutputrate, FMOD_SPEAKERMODE *controlpanelspeakermode);
FMOD_RESULT F_API setDriver (int driver);
FMOD_RESULT F_API getDriver (int *driver);
FMOD_RESULT F_API setHardwareChannels (int numhardwarechannels);
FMOD_RESULT F_API setSoftwareChannels (int numsoftwarechannels);
FMOD_RESULT F_API getSoftwareChannels (int *numsoftwarechannels);
FMOD_RESULT F_API setSoftwareFormat (int samplerate, FMOD_SOUND_FORMAT format, int numoutputchannels, int maxinputchannels, FMOD_DSP_RESAMPLER resamplemethod);
FMOD_RESULT F_API getSoftwareFormat (int *samplerate, FMOD_SOUND_FORMAT *format, int *numoutputchannels, int *maxinputchannels, FMOD_DSP_RESAMPLER *resamplemethod, int *bits);
FMOD_RESULT F_API setDSPBufferSize (unsigned int bufferlength, int numbuffers);
FMOD_RESULT F_API getDSPBufferSize (unsigned int *bufferlength, int *numbuffers);
FMOD_RESULT F_API setFileSystem (FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek, FMOD_FILE_ASYNCREADCALLBACK userasyncread, FMOD_FILE_ASYNCCANCELCALLBACK userasynccancel, int blockalign);
FMOD_RESULT F_API attachFileSystem (FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek);
FMOD_RESULT F_API setAdvancedSettings (FMOD_ADVANCEDSETTINGS *settings);
FMOD_RESULT F_API getAdvancedSettings (FMOD_ADVANCEDSETTINGS *settings);
FMOD_RESULT F_API setSpeakerMode (FMOD_SPEAKERMODE speakermode);
FMOD_RESULT F_API getSpeakerMode (FMOD_SPEAKERMODE *speakermode);
FMOD_RESULT F_API setCallback (FMOD_SYSTEM_CALLBACK callback);
// Plug-in support
FMOD_RESULT F_API setPluginPath (const char *path);
FMOD_RESULT F_API loadPlugin (const char *filename, unsigned int *handle, unsigned int priority = 0);
FMOD_RESULT F_API unloadPlugin (unsigned int handle);
FMOD_RESULT F_API getNumPlugins (FMOD_PLUGINTYPE plugintype, int *numplugins);
FMOD_RESULT F_API getPluginHandle (FMOD_PLUGINTYPE plugintype, int index, unsigned int *handle);
FMOD_RESULT F_API getPluginInfo (unsigned int handle, FMOD_PLUGINTYPE *plugintype, char *name, int namelen, unsigned int *version);
FMOD_RESULT F_API setOutputByPlugin (unsigned int handle);
FMOD_RESULT F_API getOutputByPlugin (unsigned int *handle);
FMOD_RESULT F_API createDSPByPlugin (unsigned int handle, DSP **dsp);
FMOD_RESULT F_API registerCodec (FMOD_CODEC_DESCRIPTION *description, unsigned int *handle, unsigned int priority = 0);
FMOD_RESULT F_API registerDSP (FMOD_DSP_DESCRIPTION *description, unsigned int *handle);
// Init/Close
FMOD_RESULT F_API init (int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata);
FMOD_RESULT F_API close ();
// General post-init system functions
FMOD_RESULT F_API update (); /* IMPORTANT! CALL THIS ONCE PER FRAME! */
FMOD_RESULT F_API set3DSettings (float dopplerscale, float distancefactor, float rolloffscale);
FMOD_RESULT F_API get3DSettings (float *dopplerscale, float *distancefactor, float *rolloffscale);
FMOD_RESULT F_API set3DNumListeners (int numlisteners);
FMOD_RESULT F_API get3DNumListeners (int *numlisteners);
FMOD_RESULT F_API set3DListenerAttributes(int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up);
FMOD_RESULT F_API get3DListenerAttributes(int listener, FMOD_VECTOR *pos, FMOD_VECTOR *vel, FMOD_VECTOR *forward, FMOD_VECTOR *up);
FMOD_RESULT F_API set3DRolloffCallback (FMOD_3D_ROLLOFFCALLBACK callback);
FMOD_RESULT F_API set3DSpeakerPosition (FMOD_SPEAKER speaker, float x, float y, bool active);
FMOD_RESULT F_API get3DSpeakerPosition (FMOD_SPEAKER speaker, float *x, float *y, bool *active);
FMOD_RESULT F_API setStreamBufferSize (unsigned int filebuffersize, FMOD_TIMEUNIT filebuffersizetype);
FMOD_RESULT F_API getStreamBufferSize (unsigned int *filebuffersize, FMOD_TIMEUNIT *filebuffersizetype);
// System information functions.
FMOD_RESULT F_API getVersion (unsigned int *version);
FMOD_RESULT F_API getOutputHandle (void **handle);
FMOD_RESULT F_API getChannelsPlaying (int *channels);
FMOD_RESULT F_API getHardwareChannels (int *numhardwarechannels);
FMOD_RESULT F_API getCPUUsage (float *dsp, float *stream, float *geometry, float *update, float *total);
FMOD_RESULT F_API getSoundRAM (int *currentalloced, int *maxalloced, int *total);
FMOD_RESULT F_API getNumCDROMDrives (int *numdrives);
FMOD_RESULT F_API getCDROMDriveName (int drive, char *drivename, int drivenamelen, char *scsiname, int scsinamelen, char *devicename, int devicenamelen);
FMOD_RESULT F_API getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype);
FMOD_RESULT F_API getWaveData (float *wavearray, int numvalues, int channeloffset);
// Sound/DSP/Channel/FX creation and retrieval.
FMOD_RESULT F_API createSound (const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound);
FMOD_RESULT F_API createStream (const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound);
FMOD_RESULT F_API createDSP (FMOD_DSP_DESCRIPTION *description, DSP **dsp);
FMOD_RESULT F_API createDSPByType (FMOD_DSP_TYPE type, DSP **dsp);
FMOD_RESULT F_API createChannelGroup (const char *name, ChannelGroup **channelgroup);
FMOD_RESULT F_API createSoundGroup (const char *name, SoundGroup **soundgroup);
FMOD_RESULT F_API createReverb (Reverb **reverb);
FMOD_RESULT F_API playSound (FMOD_CHANNELINDEX channelid, Sound *sound, bool paused, Channel **channel);
FMOD_RESULT F_API playDSP (FMOD_CHANNELINDEX channelid, DSP *dsp, bool paused, Channel **channel);
FMOD_RESULT F_API getChannel (int channelid, Channel **channel);
FMOD_RESULT F_API getMasterChannelGroup (ChannelGroup **channelgroup);
FMOD_RESULT F_API getMasterSoundGroup (SoundGroup **soundgroup);
// Reverb API
FMOD_RESULT F_API setReverbProperties (const FMOD_REVERB_PROPERTIES *prop);
FMOD_RESULT F_API getReverbProperties (FMOD_REVERB_PROPERTIES *prop);
FMOD_RESULT F_API setReverbAmbientProperties(FMOD_REVERB_PROPERTIES *prop);
FMOD_RESULT F_API getReverbAmbientProperties(FMOD_REVERB_PROPERTIES *prop);
// System level DSP access.
FMOD_RESULT F_API getDSPHead (DSP **dsp);
FMOD_RESULT F_API addDSP (DSP *dsp, DSPConnection **connection);
FMOD_RESULT F_API lockDSP ();
FMOD_RESULT F_API unlockDSP ();
FMOD_RESULT F_API getDSPClock (unsigned int *hi, unsigned int *lo);
// Recording API.
FMOD_RESULT F_API getRecordNumDrivers (int *numdrivers);
FMOD_RESULT F_API getRecordDriverInfo (int id, char *name, int namelen, FMOD_GUID *guid);
FMOD_RESULT F_API getRecordDriverInfoW (int id, short *name, int namelen, FMOD_GUID *guid);
FMOD_RESULT F_API getRecordDriverCaps (int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency);
FMOD_RESULT F_API getRecordPosition (int id, unsigned int *position);
FMOD_RESULT F_API recordStart (int id, Sound *sound, bool loop);
FMOD_RESULT F_API recordStop (int id);
FMOD_RESULT F_API isRecording (int id, bool *recording);
// Geometry API.
FMOD_RESULT F_API createGeometry (int maxpolygons, int maxvertices, Geometry **geometry);
FMOD_RESULT F_API setGeometrySettings (float maxworldsize);
FMOD_RESULT F_API getGeometrySettings (float *maxworldsize);
FMOD_RESULT F_API loadGeometry (const void *data, int datasize, Geometry **geometry);
FMOD_RESULT F_API getGeometryOcclusion (const FMOD_VECTOR *listener, const FMOD_VECTOR *source, float *direct, float *reverb);
// Network functions.
FMOD_RESULT F_API setNetworkProxy (const char *proxy);
FMOD_RESULT F_API getNetworkProxy (char *proxy, int proxylen);
FMOD_RESULT F_API setNetworkTimeout (int timeout);
FMOD_RESULT F_API getNetworkTimeout (int *timeout);
// Userdata set/get.
FMOD_RESULT F_API setUserData (void *userdata);
FMOD_RESULT F_API getUserData (void **userdata);
FMOD_RESULT F_API getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details);
};
/*
'Sound' API
*/
class Sound
{
private:
Sound(); /* Constructor made private so user cannot statically instance a Sound class.
Appropriate Sound creation or retrieval function must be used. */
public:
FMOD_RESULT F_API release ();
FMOD_RESULT F_API getSystemObject (System **system);
// Standard sound manipulation functions.
FMOD_RESULT F_API lock (unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2);
FMOD_RESULT F_API unlock (void *ptr1, void *ptr2, unsigned int len1, unsigned int len2);
FMOD_RESULT F_API setDefaults (float frequency, float volume, float pan, int priority);
FMOD_RESULT F_API getDefaults (float *frequency, float *volume, float *pan, int *priority);
FMOD_RESULT F_API setVariations (float frequencyvar, float volumevar, float panvar);
FMOD_RESULT F_API getVariations (float *frequencyvar, float *volumevar, float *panvar);
FMOD_RESULT F_API set3DMinMaxDistance (float min, float max);
FMOD_RESULT F_API get3DMinMaxDistance (float *min, float *max);
FMOD_RESULT F_API set3DConeSettings (float insideconeangle, float outsideconeangle, float outsidevolume);
FMOD_RESULT F_API get3DConeSettings (float *insideconeangle, float *outsideconeangle, float *outsidevolume);
FMOD_RESULT F_API set3DCustomRolloff (FMOD_VECTOR *points, int numpoints);
FMOD_RESULT F_API get3DCustomRolloff (FMOD_VECTOR **points, int *numpoints);
FMOD_RESULT F_API setSubSound (int index, Sound *subsound);
FMOD_RESULT F_API getSubSound (int index, Sound **subsound);
FMOD_RESULT F_API getSubSoundParent (Sound **parentsound);
FMOD_RESULT F_API setSubSoundSentence (int *subsoundlist, int numsubsounds);
FMOD_RESULT F_API getName (char *name, int namelen);
FMOD_RESULT F_API getLength (unsigned int *length, FMOD_TIMEUNIT lengthtype);
FMOD_RESULT F_API getFormat (FMOD_SOUND_TYPE *type, FMOD_SOUND_FORMAT *format, int *channels, int *bits);
FMOD_RESULT F_API getNumSubSounds (int *numsubsounds);
FMOD_RESULT F_API getNumTags (int *numtags, int *numtagsupdated);
FMOD_RESULT F_API getTag (const char *name, int index, FMOD_TAG *tag);
FMOD_RESULT F_API getOpenState (FMOD_OPENSTATE *openstate, unsigned int *percentbuffered, bool *starving, bool *diskbusy);
FMOD_RESULT F_API readData (void *buffer, unsigned int lenbytes, unsigned int *read);
FMOD_RESULT F_API seekData (unsigned int pcm);
FMOD_RESULT F_API setSoundGroup (SoundGroup *soundgroup);
FMOD_RESULT F_API getSoundGroup (SoundGroup **soundgroup);
// Synchronization point API. These points can come from markers embedded in wav files, and can also generate channel callbacks.
FMOD_RESULT F_API getNumSyncPoints (int *numsyncpoints);
FMOD_RESULT F_API getSyncPoint (int index, FMOD_SYNCPOINT **point);
FMOD_RESULT F_API getSyncPointInfo (FMOD_SYNCPOINT *point, char *name, int namelen, unsigned int *offset, FMOD_TIMEUNIT offsettype);
FMOD_RESULT F_API addSyncPoint (unsigned int offset, FMOD_TIMEUNIT offsettype, const char *name, FMOD_SYNCPOINT **point);
FMOD_RESULT F_API deleteSyncPoint (FMOD_SYNCPOINT *point);
// Functions also in Channel class but here they are the 'default' to save having to change it in Channel all the time.
FMOD_RESULT F_API setMode (FMOD_MODE mode);
FMOD_RESULT F_API getMode (FMOD_MODE *mode);
FMOD_RESULT F_API setLoopCount (int loopcount);
FMOD_RESULT F_API getLoopCount (int *loopcount);
FMOD_RESULT F_API setLoopPoints (unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype);
FMOD_RESULT F_API getLoopPoints (unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype);
// For MOD/S3M/XM/IT/MID sequenced formats only.
FMOD_RESULT F_API getMusicNumChannels (int *numchannels);
FMOD_RESULT F_API setMusicChannelVolume (int channel, float volume);
FMOD_RESULT F_API getMusicChannelVolume (int channel, float *volume);
FMOD_RESULT F_API setMusicSpeed (float speed);
FMOD_RESULT F_API getMusicSpeed (float *speed);
// Userdata set/get.
FMOD_RESULT F_API setUserData (void *userdata);
FMOD_RESULT F_API getUserData (void **userdata);
FMOD_RESULT F_API getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details);
};
/*
'Channel' API.
*/
class Channel
{
private:
Channel(); /* Constructor made private so user cannot statically instance a Channel class.
Appropriate Channel creation or retrieval function must be used. */
public:
FMOD_RESULT F_API getSystemObject (System **system);
FMOD_RESULT F_API stop ();
FMOD_RESULT F_API setPaused (bool paused);
FMOD_RESULT F_API getPaused (bool *paused);
FMOD_RESULT F_API setVolume (float volume);
FMOD_RESULT F_API getVolume (float *volume);
FMOD_RESULT F_API setFrequency (float frequency);
FMOD_RESULT F_API getFrequency (float *frequency);
FMOD_RESULT F_API setPan (float pan);
FMOD_RESULT F_API getPan (float *pan);
FMOD_RESULT F_API setDelay (FMOD_DELAYTYPE delaytype, unsigned int delayhi, unsigned int delaylo);
FMOD_RESULT F_API getDelay (FMOD_DELAYTYPE delaytype, unsigned int *delayhi, unsigned int *delaylo);
FMOD_RESULT F_API setSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright);
FMOD_RESULT F_API getSpeakerMix (float *frontleft, float *frontright, float *center, float *lfe, float *backleft, float *backright, float *sideleft, float *sideright);
FMOD_RESULT F_API setSpeakerLevels (FMOD_SPEAKER speaker, float *levels, int numlevels);
FMOD_RESULT F_API getSpeakerLevels (FMOD_SPEAKER speaker, float *levels, int numlevels);
FMOD_RESULT F_API setInputChannelMix (float *levels, int numlevels);
FMOD_RESULT F_API getInputChannelMix (float *levels, int numlevels);
FMOD_RESULT F_API setMute (bool mute);
FMOD_RESULT F_API getMute (bool *mute);
FMOD_RESULT F_API setPriority (int priority);
FMOD_RESULT F_API getPriority (int *priority);
FMOD_RESULT F_API setPosition (unsigned int position, FMOD_TIMEUNIT postype);
FMOD_RESULT F_API getPosition (unsigned int *position, FMOD_TIMEUNIT postype);
FMOD_RESULT F_API setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop);
FMOD_RESULT F_API getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop);
FMOD_RESULT F_API setLowPassGain (float gain);
FMOD_RESULT F_API getLowPassGain (float *gain);
FMOD_RESULT F_API setChannelGroup (ChannelGroup *channelgroup);
FMOD_RESULT F_API getChannelGroup (ChannelGroup **channelgroup);
FMOD_RESULT F_API setCallback (FMOD_CHANNEL_CALLBACK callback);
// 3D functionality.
FMOD_RESULT F_API set3DAttributes (const FMOD_VECTOR *pos, const FMOD_VECTOR *vel);
FMOD_RESULT F_API get3DAttributes (FMOD_VECTOR *pos, FMOD_VECTOR *vel);
FMOD_RESULT F_API set3DMinMaxDistance (float mindistance, float maxdistance);
FMOD_RESULT F_API get3DMinMaxDistance (float *mindistance, float *maxdistance);
FMOD_RESULT F_API set3DConeSettings (float insideconeangle, float outsideconeangle, float outsidevolume);
FMOD_RESULT F_API get3DConeSettings (float *insideconeangle, float *outsideconeangle, float *outsidevolume);
FMOD_RESULT F_API set3DConeOrientation (FMOD_VECTOR *orientation);
FMOD_RESULT F_API get3DConeOrientation (FMOD_VECTOR *orientation);
FMOD_RESULT F_API set3DCustomRolloff (FMOD_VECTOR *points, int numpoints);
FMOD_RESULT F_API get3DCustomRolloff (FMOD_VECTOR **points, int *numpoints);
FMOD_RESULT F_API set3DOcclusion (float directocclusion, float reverbocclusion);
FMOD_RESULT F_API get3DOcclusion (float *directocclusion, float *reverbocclusion);
FMOD_RESULT F_API set3DSpread (float angle);
FMOD_RESULT F_API get3DSpread (float *angle);
FMOD_RESULT F_API set3DPanLevel (float level);
FMOD_RESULT F_API get3DPanLevel (float *level);
FMOD_RESULT F_API set3DDopplerLevel (float level);
FMOD_RESULT F_API get3DDopplerLevel (float *level);
FMOD_RESULT F_API set3DDistanceFilter (bool custom, float customLevel, float centerFreq);
FMOD_RESULT F_API get3DDistanceFilter (bool *custom, float *customLevel, float *centerFreq);
// DSP functionality only for channels playing sounds created with FMOD_SOFTWARE.
FMOD_RESULT F_API getDSPHead (DSP **dsp);
FMOD_RESULT F_API addDSP (DSP *dsp, DSPConnection **connection);
// Information only functions.
FMOD_RESULT F_API isPlaying (bool *isplaying);
FMOD_RESULT F_API isVirtual (bool *isvirtual);
FMOD_RESULT F_API getAudibility (float *audibility);
FMOD_RESULT F_API getCurrentSound (Sound **sound);
FMOD_RESULT F_API getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype);
FMOD_RESULT F_API getWaveData (float *wavearray, int numvalues, int channeloffset);
FMOD_RESULT F_API getIndex (int *index);
// Functions also found in Sound class but here they can be set per channel.
FMOD_RESULT F_API setMode (FMOD_MODE mode);
FMOD_RESULT F_API getMode (FMOD_MODE *mode);
FMOD_RESULT F_API setLoopCount (int loopcount);
FMOD_RESULT F_API getLoopCount (int *loopcount);
FMOD_RESULT F_API setLoopPoints (unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype);
FMOD_RESULT F_API getLoopPoints (unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype);
// Userdata set/get.
FMOD_RESULT F_API setUserData (void *userdata);
FMOD_RESULT F_API getUserData (void **userdata);
FMOD_RESULT F_API getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details);
};
/*
'ChannelGroup' API
*/
class ChannelGroup
{
private:
ChannelGroup(); /* Constructor made private so user cannot statically instance a ChannelGroup class.
Appropriate ChannelGroup creation or retrieval function must be used. */
public:
FMOD_RESULT F_API release ();
FMOD_RESULT F_API getSystemObject (System **system);
// Channelgroup scale values. (changes attributes relative to the channels, doesn't overwrite them)
FMOD_RESULT F_API setVolume (float volume);
FMOD_RESULT F_API getVolume (float *volume);
FMOD_RESULT F_API setPitch (float pitch);
FMOD_RESULT F_API getPitch (float *pitch);
FMOD_RESULT F_API set3DOcclusion (float directocclusion, float reverbocclusion);
FMOD_RESULT F_API get3DOcclusion (float *directocclusion, float *reverbocclusion);
FMOD_RESULT F_API setPaused (bool paused);
FMOD_RESULT F_API getPaused (bool *paused);
FMOD_RESULT F_API setMute (bool mute);
FMOD_RESULT F_API getMute (bool *mute);
// Channelgroup override values. (recursively overwrites whatever settings the channels had)
FMOD_RESULT F_API stop ();
FMOD_RESULT F_API overrideVolume (float volume);
FMOD_RESULT F_API overrideFrequency (float frequency);
FMOD_RESULT F_API overridePan (float pan);
FMOD_RESULT F_API overrideReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop);
FMOD_RESULT F_API override3DAttributes (const FMOD_VECTOR *pos, const FMOD_VECTOR *vel);
FMOD_RESULT F_API overrideSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright);
// Nested channel groups.
FMOD_RESULT F_API addGroup (ChannelGroup *group);
FMOD_RESULT F_API getNumGroups (int *numgroups);
FMOD_RESULT F_API getGroup (int index, ChannelGroup **group);
FMOD_RESULT F_API getParentGroup (ChannelGroup **group);
// DSP functionality only for channel groups playing sounds created with FMOD_SOFTWARE.
FMOD_RESULT F_API getDSPHead (DSP **dsp);
FMOD_RESULT F_API addDSP (DSP *dsp, DSPConnection **connection);
// Information only functions.
FMOD_RESULT F_API getName (char *name, int namelen);
FMOD_RESULT F_API getNumChannels (int *numchannels);
FMOD_RESULT F_API getChannel (int index, Channel **channel);
FMOD_RESULT F_API getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype);
FMOD_RESULT F_API getWaveData (float *wavearray, int numvalues, int channeloffset);
// Userdata set/get.
FMOD_RESULT F_API setUserData (void *userdata);
FMOD_RESULT F_API getUserData (void **userdata);
FMOD_RESULT F_API getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details);
};
/*
'SoundGroup' API
*/
class SoundGroup
{
private:
SoundGroup(); /* Constructor made private so user cannot statically instance a SoundGroup class.
Appropriate SoundGroup creation or retrieval function must be used. */
public:
FMOD_RESULT F_API release ();
FMOD_RESULT F_API getSystemObject (System **system);
// SoundGroup control functions.
FMOD_RESULT F_API setMaxAudible (int maxaudible);
FMOD_RESULT F_API getMaxAudible (int *maxaudible);
FMOD_RESULT F_API setMaxAudibleBehavior (FMOD_SOUNDGROUP_BEHAVIOR behavior);
FMOD_RESULT F_API getMaxAudibleBehavior (FMOD_SOUNDGROUP_BEHAVIOR *behavior);
FMOD_RESULT F_API setMuteFadeSpeed (float speed);
FMOD_RESULT F_API getMuteFadeSpeed (float *speed);
FMOD_RESULT F_API setVolume (float volume);
FMOD_RESULT F_API getVolume (float *volume);
FMOD_RESULT F_API stop ();
// Information only functions.
FMOD_RESULT F_API getName (char *name, int namelen);
FMOD_RESULT F_API getNumSounds (int *numsounds);
FMOD_RESULT F_API getSound (int index, Sound **sound);
FMOD_RESULT F_API getNumPlaying (int *numplaying);
// Userdata set/get.
FMOD_RESULT F_API setUserData (void *userdata);
FMOD_RESULT F_API getUserData (void **userdata);
FMOD_RESULT F_API getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details);
};
/*
'DSP' API
*/
class DSP
{
private:
DSP(); /* Constructor made private so user cannot statically instance a DSP class.
Appropriate DSP creation or retrieval function must be used. */
public:
FMOD_RESULT F_API release ();
FMOD_RESULT F_API getSystemObject (System **system);
// Connection / disconnection / input and output enumeration.
FMOD_RESULT F_API addInput (DSP *target, DSPConnection **connection);
FMOD_RESULT F_API disconnectFrom (DSP *target);
FMOD_RESULT F_API disconnectAll (bool inputs, bool outputs);
FMOD_RESULT F_API remove ();
FMOD_RESULT F_API getNumInputs (int *numinputs);
FMOD_RESULT F_API getNumOutputs (int *numoutputs);
FMOD_RESULT F_API getInput (int index, DSP **input, DSPConnection **inputconnection);
FMOD_RESULT F_API getOutput (int index, DSP **output, DSPConnection **outputconnection);
// DSP unit control.
FMOD_RESULT F_API setActive (bool active);
FMOD_RESULT F_API getActive (bool *active);
FMOD_RESULT F_API setBypass (bool bypass);
FMOD_RESULT F_API getBypass (bool *bypass);
FMOD_RESULT F_API setSpeakerActive (FMOD_SPEAKER speaker, bool active);
FMOD_RESULT F_API getSpeakerActive (FMOD_SPEAKER speaker, bool *active);
FMOD_RESULT F_API reset ();
// DSP parameter control.
FMOD_RESULT F_API setParameter (int index, float value);
FMOD_RESULT F_API getParameter (int index, float *value, char *valuestr, int valuestrlen);
FMOD_RESULT F_API getNumParameters (int *numparams);
FMOD_RESULT F_API getParameterInfo (int index, char *name, char *label, char *description, int descriptionlen, float *min, float *max);
FMOD_RESULT F_API showConfigDialog (void *hwnd, bool show);
// DSP attributes.
FMOD_RESULT F_API getInfo (char *name, unsigned int *version, int *channels, int *configwidth, int *configheight);
FMOD_RESULT F_API getType (FMOD_DSP_TYPE *type);
FMOD_RESULT F_API setDefaults (float frequency, float volume, float pan, int priority);
FMOD_RESULT F_API getDefaults (float *frequency, float *volume, float *pan, int *priority);
// Userdata set/get.
FMOD_RESULT F_API setUserData (void *userdata);
FMOD_RESULT F_API getUserData (void **userdata);
FMOD_RESULT F_API getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details);
};
/*
'DSPConnection' API
*/
class DSPConnection
{
private:
DSPConnection(); /* Constructor made private so user cannot statically instance a DSPConnection class.
Appropriate DSPConnection creation or retrieval function must be used. */
public:
FMOD_RESULT F_API getInput (DSP **input);
FMOD_RESULT F_API getOutput (DSP **output);
FMOD_RESULT F_API setMix (float volume);
FMOD_RESULT F_API getMix (float *volume);
FMOD_RESULT F_API setLevels (FMOD_SPEAKER speaker, float *levels, int numlevels);
FMOD_RESULT F_API getLevels (FMOD_SPEAKER speaker, float *levels, int numlevels);
// Userdata set/get.
FMOD_RESULT F_API setUserData (void *userdata);
FMOD_RESULT F_API getUserData (void **userdata);
FMOD_RESULT F_API getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details);
};
/*
'Geometry' API
*/
class Geometry
{
private:
Geometry(); /* Constructor made private so user cannot statically instance a Geometry class.
Appropriate Geometry creation or retrieval function must be used. */
public:
FMOD_RESULT F_API release ();
// Polygon manipulation.
FMOD_RESULT F_API addPolygon (float directocclusion, float reverbocclusion, bool doublesided, int numvertices, const FMOD_VECTOR *vertices, int *polygonindex);
FMOD_RESULT F_API getNumPolygons (int *numpolygons);
FMOD_RESULT F_API getMaxPolygons (int *maxpolygons, int *maxvertices);
FMOD_RESULT F_API getPolygonNumVertices (int index, int *numvertices);
FMOD_RESULT F_API setPolygonVertex (int index, int vertexindex, const FMOD_VECTOR *vertex);
FMOD_RESULT F_API getPolygonVertex (int index, int vertexindex, FMOD_VECTOR *vertex);
FMOD_RESULT F_API setPolygonAttributes (int index, float directocclusion, float reverbocclusion, bool doublesided);
FMOD_RESULT F_API getPolygonAttributes (int index, float *directocclusion, float *reverbocclusion, bool *doublesided);
// Object manipulation.
FMOD_RESULT F_API setActive (bool active);
FMOD_RESULT F_API getActive (bool *active);
FMOD_RESULT F_API setRotation (const FMOD_VECTOR *forward, const FMOD_VECTOR *up);
FMOD_RESULT F_API getRotation (FMOD_VECTOR *forward, FMOD_VECTOR *up);
FMOD_RESULT F_API setPosition (const FMOD_VECTOR *position);
FMOD_RESULT F_API getPosition (FMOD_VECTOR *position);
FMOD_RESULT F_API setScale (const FMOD_VECTOR *scale);
FMOD_RESULT F_API getScale (FMOD_VECTOR *scale);
FMOD_RESULT F_API save (void *data, int *datasize);
// Userdata set/get.
FMOD_RESULT F_API setUserData (void *userdata);
FMOD_RESULT F_API getUserData (void **userdata);
FMOD_RESULT F_API getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details);
};
/*
'Reverb' API
*/
class Reverb
{
private:
Reverb(); /* Constructor made private so user cannot statically instance a Reverb class.
Appropriate Reverb creation or retrieval function must be used. */
public:
FMOD_RESULT F_API release ();
// Reverb manipulation.
FMOD_RESULT F_API set3DAttributes (const FMOD_VECTOR *position, float mindistance, float maxdistance);
FMOD_RESULT F_API get3DAttributes (FMOD_VECTOR *position, float *mindistance,float *maxdistance);
FMOD_RESULT F_API setProperties (const FMOD_REVERB_PROPERTIES *properties);
FMOD_RESULT F_API getProperties (FMOD_REVERB_PROPERTIES *properties);
FMOD_RESULT F_API setActive (bool active);
FMOD_RESULT F_API getActive (bool *active);
// Userdata set/get.
FMOD_RESULT F_API setUserData (void *userdata);
FMOD_RESULT F_API getUserData (void **userdata);
FMOD_RESULT F_API getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, FMOD_MEMORY_USAGE_DETAILS *memoryused_details);
};
}
#endif
+159
View File
@@ -0,0 +1,159 @@
/* ==================================================================================================== */
/* FMOD Ex - codec development header file. Copyright (c), Firelight Technologies Pty, Ltd. 2004-2016. */
/* */
/* Use this header if you are wanting to develop your own file format plugin to use with */
/* FMOD's codec system. With this header you can make your own fileformat plugin that FMOD */
/* can register and use. See the documentation and examples on how to make a working plugin. */
/* */
/* ==================================================================================================== */
#ifndef _FMOD_CODEC_H
#define _FMOD_CODEC_H
typedef struct FMOD_CODEC_STATE FMOD_CODEC_STATE;
typedef struct FMOD_CODEC_WAVEFORMAT FMOD_CODEC_WAVEFORMAT;
/*
Codec callbacks
*/
typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_OPENCALLBACK) (FMOD_CODEC_STATE *codec_state, FMOD_MODE usermode, FMOD_CREATESOUNDEXINFO *userexinfo);
typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_CLOSECALLBACK) (FMOD_CODEC_STATE *codec_state);
typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_READCALLBACK) (FMOD_CODEC_STATE *codec_state, void *buffer, unsigned int sizebytes, unsigned int *bytesread);
typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_GETLENGTHCALLBACK) (FMOD_CODEC_STATE *codec_state, unsigned int *length, FMOD_TIMEUNIT lengthtype);
typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_SETPOSITIONCALLBACK) (FMOD_CODEC_STATE *codec_state, int subsound, unsigned int position, FMOD_TIMEUNIT postype);
typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_GETPOSITIONCALLBACK) (FMOD_CODEC_STATE *codec_state, unsigned int *position, FMOD_TIMEUNIT postype);
typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_SOUNDCREATECALLBACK) (FMOD_CODEC_STATE *codec_state, int subsound, FMOD_SOUND *sound);
typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_METADATACALLBACK) (FMOD_CODEC_STATE *codec_state, FMOD_TAGTYPE tagtype, char *name, void *data, unsigned int datalen, FMOD_TAGDATATYPE datatype, int unique);
typedef FMOD_RESULT (F_CALLBACK *FMOD_CODEC_GETWAVEFORMAT) (FMOD_CODEC_STATE *codec_state, int index, FMOD_CODEC_WAVEFORMAT *waveformat);
/*
[STRUCTURE]
[
[DESCRIPTION]
When creating a codec, declare one of these and provide the relevant callbacks and name for FMOD to use when it opens and reads a file.
[REMARKS]
Members marked with [in] mean the variable can be written to. The user can set the value.<br>
Members marked with [out] mean the variable is modified by FMOD and is for reading purposes only. Do not change this value.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
FMOD_CODEC_STATE
]
*/
typedef struct FMOD_CODEC_DESCRIPTION
{
const char *name; /* [in] Name of the codec. */
unsigned int version; /* [in] Plugin writer's version number. */
int defaultasstream; /* [in] Tells FMOD to open the file as a stream when calling System::createSound, and not a static sample. Should normally be 0 (FALSE), because generally the user wants to decode the file into memory when using System::createSound. Mainly used for formats that decode for a very long time, or could use large amounts of memory when decoded. Usually sequenced formats such as mod/s3m/xm/it/midi fall into this category. It is mainly to stop users that don't know what they're doing from getting FMOD_ERR_MEMORY returned from createSound when they should have in fact called System::createStream or used FMOD_CREATESTREAM in System::createSound. */
FMOD_TIMEUNIT timeunits; /* [in] When setposition codec is called, only these time formats will be passed to the codec. Use bitwise OR to accumulate different types. */
FMOD_CODEC_OPENCALLBACK open; /* [in] Open callback for the codec for when FMOD tries to open a sound using this codec. */
FMOD_CODEC_CLOSECALLBACK close; /* [in] Close callback for the codec for when FMOD tries to close a sound using this codec. */
FMOD_CODEC_READCALLBACK read; /* [in] Read callback for the codec for when FMOD tries to read some data from the file to the destination format (specified in the open callback). */
FMOD_CODEC_GETLENGTHCALLBACK getlength; /* [in] Callback to return the length of the song in whatever format required when Sound::getLength is called. */
FMOD_CODEC_SETPOSITIONCALLBACK setposition; /* [in] Seek callback for the codec for when FMOD tries to seek within the file with Channel::setPosition. */
FMOD_CODEC_GETPOSITIONCALLBACK getposition; /* [in] Tell callback for the codec for when FMOD tries to get the current position within the with Channel::getPosition. */
FMOD_CODEC_SOUNDCREATECALLBACK soundcreate; /* [in] Sound creation callback for the codec when FMOD finishes creating the sound. (So the codec can set more parameters for the related created sound, ie loop points/mode or 3D attributes etc). */
FMOD_CODEC_GETWAVEFORMAT getwaveformat; /* [in] Callback to tell FMOD about the waveformat of a particular subsound. This is to save memory, rather than saving 1000 FMOD_CODEC_WAVEFORMAT structures in the codec, the codec might have a more optimal way of storing this information. */
} FMOD_CODEC_DESCRIPTION;
/*
[STRUCTURE]
[
[DESCRIPTION]
Set these values marked 'in' to tell fmod what sort of sound to create.<br>
The format, channels and frequency tell FMOD what sort of hardware buffer to create when you initialize your code. So if you wrote an MP3 codec that decoded to stereo 16bit integer PCM, you would specify FMOD_SOUND_FORMAT_PCM16, and channels would be equal to 2.<br>
Members marked as 'out' are set by fmod. Do not modify these. Simply specify 0 for these values when declaring the structure, FMOD will fill in the values for you after creation with the correct function pointers.<br>
[REMARKS]
Members marked with [in] mean the variable can be written to. The user can set the value.<br>
Members marked with [out] mean the variable is modified by FMOD and is for reading purposes only. Do not change this value.<br>
<br>
An FMOD file might be from disk, memory or network, however the file may be opened by the user.<br>
<br>
'numsubsounds' should be 0 if the file is a normal single sound stream or sound. Examples of this would be .WAV, .WMA, .MP3, .AIFF.<br>
'numsubsounds' should be 1+ if the file is a container format, and does not contain wav data itself. Examples of these types would be CDDA (multiple CD tracks), FSB (contains multiple sounds), MIDI/MOD/S3M/XM/IT (contain instruments).<br>
The arrays of format, channel, frequency, length and blockalign should point to arrays of information based on how many subsounds are in the format. If the number of subsounds is 0 then it should point to 1 of each attribute, the same as if the number of subsounds was 1. If subsounds was 100 for example, each pointer should point to an array of 100 of each attribute.<br>
When a sound has 1 or more subsounds, you must play the individual sounds specified by first obtaining the subsound with Sound::getSubSound.
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
FMOD_SOUND_FORMAT
FMOD_FILE_READCALLBACK
FMOD_FILE_SEEKCALLBACK
FMOD_CODEC_METADATACALLBACK
Sound::getSubSound
Sound::getNumSubSounds
]
*/
struct FMOD_CODEC_WAVEFORMAT
{
char name[256]; /* [in] Name of sound.*/
FMOD_SOUND_FORMAT format; /* [in] Format for (decompressed) codec output, ie FMOD_SOUND_FORMAT_PCM8, FMOD_SOUND_FORMAT_PCM16.*/
int channels; /* [in] Number of channels used by codec, ie mono = 1, stereo = 2. */
int frequency; /* [in] Default frequency in hz of the codec, ie 44100. */
unsigned int lengthbytes; /* [in] Length in bytes of the source data. */
unsigned int lengthpcm; /* [in] Length in decompressed, PCM samples of the file, ie length in seconds * frequency. Used for Sound::getLength and for memory allocation of static decompressed sample data. */
int blockalign; /* [in] Blockalign in decompressed, PCM samples of the optimal decode chunk size for this format. The codec read callback will be called in multiples of this value. */
int loopstart; /* [in] Loopstart in decompressed, PCM samples of file. */
int loopend; /* [in] Loopend in decompressed, PCM samples of file. */
FMOD_MODE mode; /* [in] Mode to determine whether the sound should by default load as looping, non looping, 2d or 3d. */
unsigned int channelmask; /* [in] Microsoft speaker channel mask, as defined for WAVEFORMATEXTENSIBLE and is found in ksmedia.h. Leave at 0 to play in natural speaker order. */
};
/*
[STRUCTURE]
[
[DESCRIPTION]
Codec plugin structure that is passed into each callback.<br>
<br>
Set these numsubsounds and waveformat members when called in FMOD_CODEC_OPENCALLBACK to tell fmod what sort of sound to create.<br>
<br>
The format, channels and frequency tell FMOD what sort of hardware buffer to create when you initialize your code. So if you wrote an MP3 codec that decoded to stereo 16bit integer PCM, you would specify FMOD_SOUND_FORMAT_PCM16, and channels would be equal to 2.<br>
[REMARKS]
Members marked with [in] mean the variable can be written to. The user can set the value.<br>
Members marked with [out] mean the variable is modified by FMOD and is for reading purposes only. Do not change this value.<br>
<br>
An FMOD file might be from disk, memory or internet, however the file may be opened by the user.<br>
<br>
'numsubsounds' should be 0 if the file is a normal single sound stream or sound. Examples of this would be .WAV, .WMA, .MP3, .AIFF.<br>
'numsubsounds' should be 1+ if the file is a container format, and does not contain wav data itself. Examples of these types would be CDDA (multiple CD tracks), FSB (contains multiple sounds), DLS (contain instruments).<br>
The arrays of format, channel, frequency, length and blockalign should point to arrays of information based on how many subsounds are in the format. If the number of subsounds is 0 then it should point to 1 of each attribute, the same as if the number of subsounds was 1. If subsounds was 100 for example, each pointer should point to an array of 100 of each attribute.<br>
When a sound has 1 or more subsounds, you must play the individual sounds specified by first obtaining the subsound with Sound::getSubSound.
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
FMOD_SOUND_FORMAT
FMOD_FILE_READCALLBACK
FMOD_FILE_SEEKCALLBACK
FMOD_CODEC_METADATACALLBACK
Sound::getSubSound
Sound::getNumSubSounds
]
*/
struct FMOD_CODEC_STATE
{
int numsubsounds; /* [in] Number of 'subsounds' in this sound. Anything other than 0 makes it a 'container' format (ie CDDA/DLS/FSB etc which contain 1 or more su bsounds). For most normal, single sound codec such as WAV/AIFF/MP3, this should be 0 as they are not a container for subsounds, they are the sound by itself. */
FMOD_CODEC_WAVEFORMAT *waveformat; /* [in] Pointer to an array of format structures containing information about each sample. Can be 0 or NULL if FMOD_CODEC_GETWAVEFORMAT callback is preferred. The number of entries here must equal the number of subsounds defined in the subsound parameter. If numsubsounds = 0 then there should be 1 instance of this structure. */
void *plugindata; /* [in] Plugin writer created data the codec author wants to attach to this object. */
void *filehandle; /* [out] This will return an internal FMOD file handle to use with the callbacks provided. */
unsigned int filesize; /* [out] This will contain the size of the file in bytes. */
FMOD_FILE_READCALLBACK fileread; /* [out] This will return a callable FMOD file function to use from codec. */
FMOD_FILE_SEEKCALLBACK fileseek; /* [out] This will return a callable FMOD file function to use from codec. */
FMOD_CODEC_METADATACALLBACK metadata; /* [out] This will return a callable FMOD metadata function to use from codec. */
};
#endif
+746
View File
@@ -0,0 +1,746 @@
/* ========================================================================================== */
/* FMOD Ex - DSP header file. Copyright (c), Firelight Technologies Pty, Ltd. 2004-2016. */
/* */
/* Use this header if you are interested in delving deeper into the FMOD software mixing / */
/* DSP engine. In this header you can find parameter structures for FMOD system reigstered */
/* DSP effects and generators. */
/* Also use this header if you are wanting to develop your own DSP plugin to use with FMOD's */
/* dsp system. With this header you can make your own DSP plugin that FMOD can */
/* register and use. See the documentation and examples on how to make a working plugin. */
/* */
/* ========================================================================================== */
#ifndef _FMOD_DSP_H
#define _FMOD_DSP_H
typedef struct FMOD_DSP_STATE FMOD_DSP_STATE;
/*
DSP callbacks
*/
typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_CREATECALLBACK) (FMOD_DSP_STATE *dsp_state);
typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_RELEASECALLBACK) (FMOD_DSP_STATE *dsp_state);
typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_RESETCALLBACK) (FMOD_DSP_STATE *dsp_state);
typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_READCALLBACK) (FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels);
typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_SETPOSITIONCALLBACK)(FMOD_DSP_STATE *dsp_state, unsigned int pos);
typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_SETPARAMCALLBACK) (FMOD_DSP_STATE *dsp_state, int index, float value);
typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_GETPARAMCALLBACK) (FMOD_DSP_STATE *dsp_state, int index, float *value, char *valuestr);
typedef FMOD_RESULT (F_CALLBACK *FMOD_DSP_DIALOGCALLBACK) (FMOD_DSP_STATE *dsp_state, void *hwnd, int show);
/*
[ENUM]
[
[DESCRIPTION]
These definitions can be used for creating FMOD defined special effects or DSP units.
[REMARKS]
To get them to be active, first create the unit, then add it somewhere into the DSP network, either at the front of the network near the soundcard unit to affect the global output (by using System::getDSPHead), or on a single channel (using Channel::getDSPHead).
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
System::createDSPByType
]
*/
typedef enum
{
FMOD_DSP_TYPE_UNKNOWN, /* This unit was created via a non FMOD plugin so has an unknown purpose. */
FMOD_DSP_TYPE_MIXER, /* This unit does nothing but take inputs and mix them together then feed the result to the soundcard unit. */
FMOD_DSP_TYPE_OSCILLATOR, /* This unit generates sine/square/saw/triangle or noise tones. */
FMOD_DSP_TYPE_LOWPASS, /* This unit filters sound using a high quality, resonant lowpass filter algorithm but consumes more CPU time. */
FMOD_DSP_TYPE_ITLOWPASS, /* This unit filters sound using a resonant lowpass filter algorithm that is used in Impulse Tracker, but with limited cutoff range (0 to 8060hz). */
FMOD_DSP_TYPE_HIGHPASS, /* This unit filters sound using a resonant highpass filter algorithm. */
FMOD_DSP_TYPE_ECHO, /* This unit produces an echo on the sound and fades out at the desired rate. */
FMOD_DSP_TYPE_FLANGE, /* This unit produces a flange effect on the sound. */
FMOD_DSP_TYPE_DISTORTION, /* This unit distorts the sound. */
FMOD_DSP_TYPE_NORMALIZE, /* This unit normalizes or amplifies the sound to a certain level. */
FMOD_DSP_TYPE_PARAMEQ, /* This unit attenuates or amplifies a selected frequency range. */
FMOD_DSP_TYPE_PITCHSHIFT, /* This unit bends the pitch of a sound without changing the speed of playback. */
FMOD_DSP_TYPE_CHORUS, /* This unit produces a chorus effect on the sound. */
FMOD_DSP_TYPE_VSTPLUGIN, /* This unit allows the use of Steinberg VST plugins */
FMOD_DSP_TYPE_WINAMPPLUGIN, /* This unit allows the use of Nullsoft Winamp plugins */
FMOD_DSP_TYPE_ITECHO, /* This unit produces an echo on the sound and fades out at the desired rate as is used in Impulse Tracker. */
FMOD_DSP_TYPE_COMPRESSOR, /* This unit implements dynamic compression (linked multichannel, wideband) */
FMOD_DSP_TYPE_SFXREVERB, /* This unit implements SFX reverb */
FMOD_DSP_TYPE_LOWPASS_SIMPLE, /* This unit filters sound using a simple lowpass with no resonance, but has flexible cutoff and is fast. */
FMOD_DSP_TYPE_DELAY, /* This unit produces different delays on individual channels of the sound. */
FMOD_DSP_TYPE_TREMOLO, /* This unit produces a tremolo / chopper effect on the sound. */
FMOD_DSP_TYPE_LADSPAPLUGIN, /* This unit allows the use of LADSPA standard plugins. */
FMOD_DSP_TYPE_HIGHPASS_SIMPLE, /* This unit filters sound using a simple highpass with no resonance, but has flexible cutoff and is fast. */
FMOD_DSP_TYPE_HARDWARE = 1000, /* Offset that platform specific FMOD_HARDWARE DSPs will start at. */
FMOD_DSP_TYPE_FORCEINT = 65536 /* Makes sure this enum is signed 32bit. */
} FMOD_DSP_TYPE;
/*
[STRUCTURE]
[
[DESCRIPTION]
Structure to define a parameter for a DSP unit.
[REMARKS]
Members marked with [r] mean the variable is modified by FMOD and is for reading purposes only. Do not change this value.<br>
Members marked with [w] mean the variable can be written to. The user can set the value.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
System::createDSP
DSP::setParameter
]
*/
typedef struct FMOD_DSP_PARAMETERDESC
{
float min; /* [w] Minimum value of the parameter (ie 100.0). */
float max; /* [w] Maximum value of the parameter (ie 22050.0). */
float defaultval; /* [w] Default value of parameter. */
char name[16]; /* [w] Name of the parameter to be displayed (ie "Cutoff frequency"). */
char label[16]; /* [w] Short string to be put next to value to denote the unit type (ie "hz"). */
const char *description; /* [w] Description of the parameter to be displayed as a help item / tooltip for this parameter. */
} FMOD_DSP_PARAMETERDESC;
/*
[STRUCTURE]
[
[DESCRIPTION]
When creating a DSP unit, declare one of these and provide the relevant callbacks and name for FMOD to use when it creates and uses a DSP unit of this type.
[REMARKS]
<br>
Members marked with [r] mean the variable is modified by FMOD and is for reading purposes only. Do not change this value.<br>
Members marked with [w] mean the variable can be written to. The user can set the value.<br>
<br>
IMPORTANT: The 'paramdesc' member should point to static memory, as FMOD references the data internally using the pointer provided. Do not store these parameter description structures on the stack, or in heap memory that is freed while FMOD is using it.<br>
<br>
There are 2 different ways to change a parameter in this architecture.<br>
One is to use DSP::setParameter / DSP::getParameter. This is platform independant and is dynamic, so new unknown plugins can have their parameters enumerated and used.<br>
The other is to use DSP::showConfigDialog. This is platform specific and requires a GUI, and will display a dialog box to configure the plugin.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
System::createDSP
FMOD_DSP_STATE
]
*/
typedef struct FMOD_DSP_DESCRIPTION
{
char name[32]; /* [w] Name of the unit to be displayed in the network. */
unsigned int version; /* [w] Plugin writer's version number. */
int channels; /* [w] Number of channels. Use 0 to process whatever number of channels is currently in the network. >0 would be mostly used if the unit is a unit that only generates sound. */
FMOD_DSP_CREATECALLBACK create; /* [w] Create callback. This is called when DSP unit is created. Can be null. */
FMOD_DSP_RELEASECALLBACK release; /* [w] Release callback. This is called just before the unit is freed so the user can do any cleanup needed for the unit. Can be null. */
FMOD_DSP_RESETCALLBACK reset; /* [w] Reset callback. This is called by the user to reset any history buffers that may need resetting for a filter, when it is to be used or re-used for the first time to its initial clean state. Use to avoid clicks or artifacts. */
FMOD_DSP_READCALLBACK read; /* [w] Read callback. Processing is done here. Can be null. */
FMOD_DSP_SETPOSITIONCALLBACK setposition; /* [w] Set position callback. This is called if the unit wants to update its position info but not process data, or reset a cursor position internally if it is reading data from a certain source. Can be null. */
int numparameters; /* [w] Number of parameters used in this filter. The user finds this with DSP::getNumParameters */
FMOD_DSP_PARAMETERDESC *paramdesc; /* [w] Variable number of parameter structures. */
FMOD_DSP_SETPARAMCALLBACK setparameter; /* [w] This is called when the user calls DSP::setParameter. Can be null. */
FMOD_DSP_GETPARAMCALLBACK getparameter; /* [w] This is called when the user calls DSP::getParameter. Can be null. */
FMOD_DSP_DIALOGCALLBACK config; /* [w] This is called when the user calls DSP::showConfigDialog. Can be used to display a dialog to configure the filter. Can be null. */
int configwidth; /* [w] Width of config dialog graphic if there is one. 0 otherwise.*/
int configheight; /* [w] Height of config dialog graphic if there is one. 0 otherwise.*/
void *userdata; /* [w] Optional. Specify 0 to ignore. This is user data to be attached to the DSP unit during creation. Access via DSP::getUserData. */
} FMOD_DSP_DESCRIPTION;
/*
[STRUCTURE]
[
[DESCRIPTION]
DSP plugin structure that is passed into each callback.
[REMARKS]
Members marked with [r] mean the variable is modified by FMOD and is for reading purposes only. Do not change this value.<br>
Members marked with [w] mean the variable can be written to. The user can set the value.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
FMOD_DSP_DESCRIPTION
]
*/
struct FMOD_DSP_STATE
{
FMOD_DSP *instance; /* [r] Handle to the DSP hand the user created. Not to be modified. C++ users cast to FMOD::DSP to use. */
void *plugindata; /* [w] Plugin writer created data the output author wants to attach to this object. */
unsigned short speakermask; /* [w] Specifies which speakers the DSP effect is active on */
};
/*
===================================================================================================
FMOD built in effect parameters.
Use DSP::setParameter with these enums for the 'index' parameter.
===================================================================================================
*/
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_OSCILLATOR filter.
[REMARKS]
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_OSCILLATOR_TYPE, /* Waveform type. 0 = sine. 1 = square. 2 = sawup. 3 = sawdown. 4 = triangle. 5 = noise. */
FMOD_DSP_OSCILLATOR_RATE /* Frequency of the sinewave in hz. 1.0 to 22000.0. Default = 220.0. */
} FMOD_DSP_OSCILLATOR;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_LOWPASS filter.
[REMARKS]
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_LOWPASS_CUTOFF, /* Lowpass cutoff frequency in hz. 10.0 to 22000.0. Default = 5000.0. */
FMOD_DSP_LOWPASS_RESONANCE /* Lowpass resonance Q value. 1.0 to 10.0. Default = 1.0. */
} FMOD_DSP_LOWPASS;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_ITLOWPASS filter.<br>
This is different to the default FMOD_DSP_TYPE_ITLOWPASS filter in that it uses a different quality algorithm and is
the filter used to produce the correct sounding playback in .IT files.<br>
FMOD Ex's .IT playback uses this filter.<br>
[REMARKS]
Note! This filter actually has a limited cutoff frequency below the specified maximum, due to its limited design,
so for a more open range filter use FMOD_DSP_LOWPASS or if you don't mind not having resonance,
FMOD_DSP_LOWPASS_SIMPLE.<br>
The effective maximum cutoff is about 8060hz.
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_ITLOWPASS_CUTOFF, /* Lowpass cutoff frequency in hz. 1.0 to 22000.0. Default = 5000.0/ */
FMOD_DSP_ITLOWPASS_RESONANCE /* Lowpass resonance Q value. 0.0 to 127.0. Default = 1.0. */
} FMOD_DSP_ITLOWPASS;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_HIGHPASS filter.
[REMARKS]
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_HIGHPASS_CUTOFF, /* Highpass cutoff frequency in hz. 1.0 to output 22000.0. Default = 5000.0. */
FMOD_DSP_HIGHPASS_RESONANCE /* Highpass resonance Q value. 1.0 to 10.0. Default = 1.0. */
} FMOD_DSP_HIGHPASS;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_ECHO filter.
[REMARKS]
Note. Every time the delay is changed, the plugin re-allocates the echo buffer. This means the echo will dissapear at that time while it refills its new buffer.<br>
Larger echo delays result in larger amounts of memory allocated.<br>
<br>
'<i>maxchannels</i>' also dictates the amount of memory allocated. By default, the maxchannels value is 0. If FMOD is set to stereo, the echo unit will allocate enough memory for 2 channels. If it is 5.1, it will allocate enough memory for a 6 channel echo, etc.<br>
If the echo effect is only ever applied to the global mix (ie it was added with System::addDSP), then 0 is the value to set as it will be enough to handle all speaker modes.<br>
When the echo is added to a channel (ie Channel::addDSP) then the channel count that comes in could be anything from 1 to 8 possibly. It is only in this case where you might want to increase the channel count above the output's channel count.<br>
If a channel echo is set to a lower number than the sound's channel count that is coming in, it will not echo the sound.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_ECHO_DELAY, /* Echo delay in ms. 10 to 5000. Default = 500. */
FMOD_DSP_ECHO_DECAYRATIO, /* Echo decay per delay. 0 to 1. 1.0 = No decay, 0.0 = total decay (ie simple 1 line delay). Default = 0.5. */
FMOD_DSP_ECHO_MAXCHANNELS, /* Maximum channels supported. 0 to 16. 0 = same as fmod's default output polyphony, 1 = mono, 2 = stereo etc. See remarks for more. Default = 0. It is suggested to leave at 0! */
FMOD_DSP_ECHO_DRYMIX, /* Volume of original signal to pass to output. 0.0 to 1.0. Default = 1.0. */
FMOD_DSP_ECHO_WETMIX /* Volume of echo signal to pass to output. 0.0 to 1.0. Default = 1.0. */
} FMOD_DSP_ECHO;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_DELAY filter.
[REMARKS]
Note. Every time MaxDelay is changed, the plugin re-allocates the delay buffer. This means the delay will dissapear at that time while it refills its new buffer.<br>
A larger MaxDelay results in larger amounts of memory allocated.<br>
Channel delays above MaxDelay will be clipped to MaxDelay and the delay buffer will not be resized.<br>
<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_DELAY_CH0, /* Channel #0 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH1, /* Channel #1 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH2, /* Channel #2 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH3, /* Channel #3 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH4, /* Channel #4 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH5, /* Channel #5 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH6, /* Channel #6 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH7, /* Channel #7 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH8, /* Channel #8 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH9, /* Channel #9 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH10, /* Channel #10 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH11, /* Channel #11 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH12, /* Channel #12 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH13, /* Channel #13 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH14, /* Channel #14 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_CH15, /* Channel #15 Delay in ms. 0 to 10000. Default = 0. */
FMOD_DSP_DELAY_MAXDELAY /* Maximum delay in ms. 0 to 10000. Default = 10. */
} FMOD_DSP_DELAY;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_FLANGE filter.
[REMARKS]
Flange is an effect where the signal is played twice at the same time, and one copy slides back and forth creating a whooshing or flanging effect.<br>
As there are 2 copies of the same signal, by default each signal is given 50% mix, so that the total is not louder than the original unaffected signal.<br>
<br>
Flange depth is a percentage of a 10ms shift from the original signal. Anything above 10ms is not considered flange because to the ear it begins to 'echo' so 10ms is the highest value possible.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_FLANGE_DRYMIX, /* Volume of original signal to pass to output. 0.0 to 1.0. Default = 0.45. */
FMOD_DSP_FLANGE_WETMIX, /* Volume of flange signal to pass to output. 0.0 to 1.0. Default = 0.55. */
FMOD_DSP_FLANGE_DEPTH, /* Flange depth (percentage of 40ms delay). 0.01 to 1.0. Default = 1.0. */
FMOD_DSP_FLANGE_RATE /* Flange speed in hz. 0.0 to 20.0. Default = 0.1. */
} FMOD_DSP_FLANGE;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_TREMOLO filter.
[REMARKS]
The tremolo effect varies the amplitude of a sound. Depending on the settings, this unit can produce a tremolo, chopper or auto-pan effect.<br>
<br>
The shape of the LFO (low freq. oscillator) can morphed between sine, triangle and sawtooth waves using the FMOD_DSP_TREMOLO_SHAPE and FMOD_DSP_TREMOLO_SKEW parameters.<br>
FMOD_DSP_TREMOLO_DUTY and FMOD_DSP_TREMOLO_SQUARE are useful for a chopper-type effect where the first controls the on-time duration and second controls the flatness of the envelope.<br>
FMOD_DSP_TREMOLO_SPREAD varies the LFO phase between channels to get an auto-pan effect. This works best with a sine shape LFO.<br>
The LFO can be synchronized using the FMOD_DSP_TREMOLO_PHASE parameter which sets its instantaneous phase.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_TREMOLO_FREQUENCY, /* LFO frequency in Hz. 0.1 to 20. Default = 4. */
FMOD_DSP_TREMOLO_DEPTH, /* Tremolo depth. 0 to 1. Default = 0. */
FMOD_DSP_TREMOLO_SHAPE, /* LFO shape morph between triangle and sine. 0 to 1. Default = 0. */
FMOD_DSP_TREMOLO_SKEW, /* Time-skewing of LFO cycle. -1 to 1. Default = 0. */
FMOD_DSP_TREMOLO_DUTY, /* LFO on-time. 0 to 1. Default = 0.5. */
FMOD_DSP_TREMOLO_SQUARE, /* Flatness of the LFO shape. 0 to 1. Default = 0. */
FMOD_DSP_TREMOLO_PHASE, /* Instantaneous LFO phase. 0 to 1. Default = 0. */
FMOD_DSP_TREMOLO_SPREAD /* Rotation / auto-pan effect. -1 to 1. Default = 0. */
} FMOD_DSP_TREMOLO;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_DISTORTION filter.
[REMARKS]
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_DISTORTION_LEVEL /* Distortion value. 0.0 to 1.0. Default = 0.5. */
} FMOD_DSP_DISTORTION;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_NORMALIZE filter.
[REMARKS]
Normalize amplifies the sound based on the maximum peaks within the signal.<br>
For example if the maximum peaks in the signal were 50% of the bandwidth, it would scale the whole sound by 2.<br>
The lower threshold value makes the normalizer ignores peaks below a certain point, to avoid over-amplification if a loud signal suddenly came in, and also to avoid amplifying to maximum things like background hiss.<br>
<br>
Because FMOD is a realtime audio processor, it doesn't have the luxury of knowing the peak for the whole sound (ie it can't see into the future), so it has to process data as it comes in.<br>
To avoid very sudden changes in volume level based on small samples of new data, fmod fades towards the desired amplification which makes for smooth gain control. The fadetime parameter can control this.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_NORMALIZE_FADETIME, /* Time to ramp the silence to full in ms. 0.0 to 20000.0. Default = 5000.0. */
FMOD_DSP_NORMALIZE_THRESHHOLD, /* Lower volume range threshold to ignore. 0.0 to 1.0. Default = 0.1. Raise higher to stop amplification of very quiet signals. */
FMOD_DSP_NORMALIZE_MAXAMP /* Maximum amplification allowed. 1.0 to 100000.0. Default = 20.0. 1.0 = no amplifaction, higher values allow more boost. */
} FMOD_DSP_NORMALIZE;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_PARAMEQ filter.
[REMARKS]
Parametric EQ is a bandpass filter that attenuates or amplifies a selected frequency and its neighbouring frequencies.<br>
<br>
To create a multi-band EQ create multiple FMOD_DSP_TYPE_PARAMEQ units and set each unit to different frequencies, for example 1000hz, 2000hz, 4000hz, 8000hz, 16000hz with a range of 1 octave each.<br>
<br>
When a frequency has its gain set to 1.0, the sound will be unaffected and represents the original signal exactly.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_PARAMEQ_CENTER, /* Frequency center. 20.0 to 22000.0. Default = 8000.0. */
FMOD_DSP_PARAMEQ_BANDWIDTH, /* Octave range around the center frequency to filter. 0.2 to 5.0. Default = 1.0. */
FMOD_DSP_PARAMEQ_GAIN /* Frequency Gain. 0.05 to 3.0. Default = 1.0. */
} FMOD_DSP_PARAMEQ;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_PITCHSHIFT filter.
[REMARKS]
This pitch shifting unit can be used to change the pitch of a sound without speeding it up or slowing it down.<br>
It can also be used for time stretching or scaling, for example if the pitch was doubled, and the frequency of the sound was halved, the pitch of the sound would sound correct but it would be twice as slow.<br>
<br>
<b>Warning!</b> This filter is very computationally expensive! Similar to a vocoder, it requires several overlapping FFT and IFFT's to produce smooth output, and can require around 440mhz for 1 stereo 48khz signal using the default settings.<br>
Reducing the signal to mono will half the cpu usage.<br>
Reducing this will lower audio quality, but what settings to use are largely dependant on the sound being played. A noisy polyphonic signal will need higher fft size compared to a speaking voice for example.<br>
<br>
This pitch shifter is based on the pitch shifter code at http://www.dspdimension.com, written by Stephan M. Bernsee.<br>
The original code is COPYRIGHT 1999-2003 Stephan M. Bernsee <smb@dspdimension.com>.<br>
<br>
'<i>maxchannels</i>' dictates the amount of memory allocated. By default, the maxchannels value is 0. If FMOD is set to stereo, the pitch shift unit will allocate enough memory for 2 channels. If it is 5.1, it will allocate enough memory for a 6 channel pitch shift, etc.<br>
If the pitch shift effect is only ever applied to the global mix (ie it was added with System::addDSP), then 0 is the value to set as it will be enough to handle all speaker modes.<br>
When the pitch shift is added to a channel (ie Channel::addDSP) then the channel count that comes in could be anything from 1 to 8 possibly. It is only in this case where you might want to increase the channel count above the output's channel count.<br>
If a channel pitch shift is set to a lower number than the sound's channel count that is coming in, it will not pitch shift the sound.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_PITCHSHIFT_PITCH, /* Pitch value. 0.5 to 2.0. Default = 1.0. 0.5 = one octave down, 2.0 = one octave up. 1.0 does not change the pitch. */
FMOD_DSP_PITCHSHIFT_FFTSIZE, /* FFT window size. 256, 512, 1024, 2048, 4096. Default = 1024. Increase this to reduce 'smearing'. This effect is a warbling sound similar to when an mp3 is encoded at very low bitrates. */
FMOD_DSP_PITCHSHIFT_OVERLAP, /* Removed. Do not use. FMOD now uses 4 overlaps and cannot be changed. */
FMOD_DSP_PITCHSHIFT_MAXCHANNELS /* Maximum channels supported. 0 to 16. 0 = same as fmod's default output polyphony, 1 = mono, 2 = stereo etc. See remarks for more. Default = 0. It is suggested to leave at 0! */
} FMOD_DSP_PITCHSHIFT;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_CHORUS filter.
[REMARKS]
Chrous is an effect where the sound is more 'spacious' due to 1 to 3 versions of the sound being played along side the original signal but with the pitch of each copy modulating on a sine wave.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_CHORUS_DRYMIX, /* Volume of original signal to pass to output. 0.0 to 1.0. Default = 0.5. */
FMOD_DSP_CHORUS_WETMIX1, /* Volume of 1st chorus tap. 0.0 to 1.0. Default = 0.5. */
FMOD_DSP_CHORUS_WETMIX2, /* Volume of 2nd chorus tap. This tap is 90 degrees out of phase of the first tap. 0.0 to 1.0. Default = 0.5. */
FMOD_DSP_CHORUS_WETMIX3, /* Volume of 3rd chorus tap. This tap is 90 degrees out of phase of the second tap. 0.0 to 1.0. Default = 0.5. */
FMOD_DSP_CHORUS_DELAY, /* Chorus delay in ms. 0.1 to 100.0. Default = 40.0 ms. */
FMOD_DSP_CHORUS_RATE, /* Chorus modulation rate in hz. 0.0 to 20.0. Default = 0.8 hz. */
FMOD_DSP_CHORUS_DEPTH /* Chorus modulation depth. 0.0 to 1.0. Default = 0.03. */
} FMOD_DSP_CHORUS;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_ITECHO filter.<br>
This is effectively a software based echo filter that emulates the DirectX DMO echo effect. Impulse tracker files can support this, and FMOD will produce the effect on ANY platform, not just those that support DirectX effects!<br>
[REMARKS]
Note. Every time the delay is changed, the plugin re-allocates the echo buffer. This means the echo will dissapear at that time while it refills its new buffer.<br>
Larger echo delays result in larger amounts of memory allocated.<br>
<br>
As this is a stereo filter made mainly for IT playback, it is targeted for stereo signals.<br>
With mono signals only the FMOD_DSP_ITECHO_LEFTDELAY is used.<br>
For multichannel signals (>2) there will be no echo on those channels.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::SetParameter
DSP::GetParameter
FMOD_DSP_TYPE
System::addDSP
]
*/
typedef enum
{
FMOD_DSP_ITECHO_WETDRYMIX, /* Ratio of wet (processed) signal to dry (unprocessed) signal. Must be in the range from 0.0 through 100.0 (all wet). The default value is 50. */
FMOD_DSP_ITECHO_FEEDBACK, /* Percentage of output fed back into input, in the range from 0.0 through 100.0. The default value is 50. */
FMOD_DSP_ITECHO_LEFTDELAY, /* Delay for left channel, in milliseconds, in the range from 1.0 through 2000.0. The default value is 500 ms. */
FMOD_DSP_ITECHO_RIGHTDELAY, /* Delay for right channel, in milliseconds, in the range from 1.0 through 2000.0. The default value is 500 ms. */
FMOD_DSP_ITECHO_PANDELAY /* Value that specifies whether to swap left and right delays with each successive echo. The default value is zero, meaning no swap. Possible values are defined as 0.0 (equivalent to FALSE) and 1.0 (equivalent to TRUE). CURRENTLY NOT SUPPORTED. */
} FMOD_DSP_ITECHO;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_COMPRESSOR unit.
This is a simple linked multichannel software limiter that is uniform across the whole spectrum.<br>
[REMARKS]
The limiter is not guaranteed to catch every peak above the threshold level,
because it cannot apply gain reduction instantaneously - the time delay is
determined by the attack time. However setting the attack time too short will
distort the sound, so it is a compromise. High level peaks can be avoided by
using a short attack time - but not too short, and setting the threshold a few
decibels below the critical level.
<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::SetParameter
DSP::GetParameter
FMOD_DSP_TYPE
System::addDSP
]
*/
typedef enum
{
FMOD_DSP_COMPRESSOR_THRESHOLD, /* Threshold level (dB) in the range from -60 through 0. The default value is 0. */
FMOD_DSP_COMPRESSOR_ATTACK, /* Gain reduction attack time (milliseconds), in the range from 10 through 200. The default value is 50. */
FMOD_DSP_COMPRESSOR_RELEASE, /* Gain reduction release time (milliseconds), in the range from 20 through 1000. The default value is 50. */
FMOD_DSP_COMPRESSOR_GAINMAKEUP /* Make-up gain (dB) applied after limiting, in the range from 0 through 30. The default value is 0. */
} FMOD_DSP_COMPRESSOR;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_SFXREVERB unit.<br>
[REMARKS]
This is a high quality I3DL2 based reverb.<br>
On top of the I3DL2 property set, "Dry Level" is also included to allow the dry mix to be changed.<br>
<br>
These properties can be set with presets in FMOD_REVERB_PRESETS.
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::SetParameter
DSP::GetParameter
FMOD_DSP_TYPE
System::addDSP
FMOD_REVERB_PRESETS
]
*/
typedef enum
{
FMOD_DSP_SFXREVERB_DRYLEVEL, /* Dry Level : Mix level of dry signal in output in mB. Ranges from -10000.0 to 0.0. Default is 0. */
FMOD_DSP_SFXREVERB_ROOM, /* Room : Room effect level at low frequencies in mB. Ranges from -10000.0 to 0.0. Default is -10000.0. */
FMOD_DSP_SFXREVERB_ROOMHF, /* Room HF : Room effect high-frequency level re. low frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0. */
FMOD_DSP_SFXREVERB_DECAYTIME, /* Decay Time : Reverberation decay time at low-frequencies in seconds. Ranges from 0.1 to 20.0. Default is 1.0. */
FMOD_DSP_SFXREVERB_DECAYHFRATIO, /* Decay HF Ratio : High-frequency to low-frequency decay time ratio. Ranges from 0.1 to 2.0. Default is 0.5. */
FMOD_DSP_SFXREVERB_REFLECTIONSLEVEL, /* Reflections : Early reflections level relative to room effect in mB. Ranges from -10000.0 to 1000.0. Default is -10000.0. */
FMOD_DSP_SFXREVERB_REFLECTIONSDELAY, /* Reflect Delay : Delay time of first reflection in seconds. Ranges from 0.0 to 0.3. Default is 0.02. */
FMOD_DSP_SFXREVERB_REVERBLEVEL, /* Reverb : Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0. */
FMOD_DSP_SFXREVERB_REVERBDELAY, /* Reverb Delay : Late reverberation delay time relative to first reflection in seconds. Ranges from 0.0 to 0.1. Default is 0.04. */
FMOD_DSP_SFXREVERB_DIFFUSION, /* Diffusion : Reverberation diffusion (echo density) in percent. Ranges from 0.0 to 100.0. Default is 100.0. */
FMOD_DSP_SFXREVERB_DENSITY, /* Density : Reverberation density (modal density) in percent. Ranges from 0.0 to 100.0. Default is 100.0. */
FMOD_DSP_SFXREVERB_HFREFERENCE, /* HF Reference : Reference high frequency in Hz. Ranges from 20.0 to 20000.0. Default is 5000.0. */
FMOD_DSP_SFXREVERB_ROOMLF, /* Room LF : Room effect low-frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0. */
FMOD_DSP_SFXREVERB_LFREFERENCE /* LF Reference : Reference low-frequency in Hz. Ranges from 20.0 to 1000.0. Default is 250.0. */
} FMOD_DSP_SFXREVERB;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_LOWPASS_SIMPLE filter.<br>
This is a very simple low pass filter, based on two single-pole RC time-constant modules.
The emphasis is on speed rather than accuracy, so this should not be used for task requiring critical filtering.<br>
[REMARKS]
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_LOWPASS_SIMPLE_CUTOFF /* Lowpass cutoff frequency in hz. 10.0 to 22000.0. Default = 5000.0 */
} FMOD_DSP_LOWPASS_SIMPLE;
/*
[ENUM]
[
[DESCRIPTION]
Parameter types for the FMOD_DSP_TYPE_HIGHPASS_SIMPLE filter.<br>
This is a very simple single-order high pass filter.
The emphasis is on speed rather than accuracy, so this should not be used for task requiring critical filtering.<br>
[REMARKS]
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
DSP::setParameter
DSP::getParameter
FMOD_DSP_TYPE
]
*/
typedef enum
{
FMOD_DSP_HIGHPASS_SIMPLE_CUTOFF /* Highpass cutoff frequency in hz. 10.0 to 22000.0. Default = 1000.0 */
} FMOD_DSP_HIGHPASS_SIMPLE;
#endif
+127
View File
@@ -0,0 +1,127 @@
/*$ preserve start $*/
/* ============================================================================================== */
/* FMOD Ex - Error string header file. Copyright (c), Firelight Technologies Pty, Ltd. 2004-2016. */
/* */
/* Use this header if you want to store or display a string version / english explanation of */
/* the FMOD error codes. */
/* */
/* ============================================================================================== */
#ifndef _FMOD_ERRORS_H
#define _FMOD_ERRORS_H
#include "fmod.h"
#ifdef __GNUC__
static const char *FMOD_ErrorString(FMOD_RESULT errcode) __attribute__((unused));
#endif
static const char *FMOD_ErrorString(FMOD_RESULT errcode)
{
switch (errcode)
{
/*$ preserve end $*/
case FMOD_ERR_ALREADYLOCKED: return "Tried to call lock a second time before unlock was called. ";
case FMOD_ERR_BADCOMMAND: return "Tried to call a function on a data type that does not allow this type of functionality (ie calling Sound::lock on a streaming sound). ";
case FMOD_ERR_CDDA_DRIVERS: return "Neither NTSCSI nor ASPI could be initialised. ";
case FMOD_ERR_CDDA_INIT: return "An error occurred while initialising the CDDA subsystem. ";
case FMOD_ERR_CDDA_INVALID_DEVICE: return "Couldn't find the specified device. ";
case FMOD_ERR_CDDA_NOAUDIO: return "No audio tracks on the specified disc. ";
case FMOD_ERR_CDDA_NODEVICES: return "No CD/DVD devices were found. ";
case FMOD_ERR_CDDA_NODISC: return "No disc present in the specified drive. ";
case FMOD_ERR_CDDA_READ: return "A CDDA read error occurred. ";
case FMOD_ERR_CHANNEL_ALLOC: return "Error trying to allocate a channel. ";
case FMOD_ERR_CHANNEL_STOLEN: return "The specified channel has been reused to play another sound. ";
case FMOD_ERR_COM: return "A Win32 COM related error occured. COM failed to initialize or a QueryInterface failed meaning a Windows codec or driver was not installed properly. ";
case FMOD_ERR_DMA: return "DMA Failure. See debug output for more information. ";
case FMOD_ERR_DSP_CONNECTION: return "DSP connection error. Connection possibly caused a cyclic dependancy. Or tried to connect a tree too many units deep (more than 128). ";
case FMOD_ERR_DSP_FORMAT: return "DSP Format error. A DSP unit may have attempted to connect to this network with the wrong format. ";
case FMOD_ERR_DSP_NOTFOUND: return "DSP connection error. Couldn't find the DSP unit specified. ";
case FMOD_ERR_DSP_RUNNING: return "DSP error. Cannot perform this operation while the network is in the middle of running. This will most likely happen if a connection or disconnection is attempted in a DSP callback. ";
case FMOD_ERR_DSP_TOOMANYCONNECTIONS: return "DSP connection error. The unit being connected to or disconnected should only have 1 input or output. ";
case FMOD_ERR_EVENT_ALREADY_LOADED: return "The specified project or bank has already been loaded. Having multiple copies of the same project loaded simultaneously is forbidden. ";
case FMOD_ERR_EVENT_FAILED: return "An Event failed to be retrieved, most likely due to 'just fail' being specified as the max playbacks behavior. ";
case FMOD_ERR_EVENT_GUIDCONFLICT: return "An event with the same GUID already exists. ";
case FMOD_ERR_EVENT_INFOONLY: return "Can't execute this command on an EVENT_INFOONLY event. ";
case FMOD_ERR_EVENT_INTERNAL: return "An error occured that wasn't supposed to. See debug log for reason. ";
case FMOD_ERR_EVENT_MAXSTREAMS: return "Event failed because 'Max streams' was hit when FMOD_EVENT_INIT_FAIL_ON_MAXSTREAMS was specified. ";
case FMOD_ERR_EVENT_MISMATCH: return "FSB mismatches the FEV it was compiled with, the stream/sample mode it was meant to be created with was different, or the FEV was built for a different platform. ";
case FMOD_ERR_EVENT_NAMECONFLICT: return "A category with the same name already exists. ";
case FMOD_ERR_EVENT_NEEDSSIMPLE: return "Tried to call a function on a complex event that's only supported by simple events. ";
case FMOD_ERR_EVENT_NOTFOUND: return "The requested event, event group, event category or event property could not be found. ";
case FMOD_ERR_FILE_BAD: return "Error loading file. ";
case FMOD_ERR_FILE_COULDNOTSEEK: return "Couldn't perform seek operation. This is a limitation of the medium (ie netstreams) or the file format. ";
case FMOD_ERR_FILE_DISKEJECTED: return "Media was ejected while reading. ";
case FMOD_ERR_FILE_EOF: return "End of file unexpectedly reached while trying to read essential data (truncated data?). ";
case FMOD_ERR_FILE_NOTFOUND: return "File not found. ";
case FMOD_ERR_FILE_UNWANTED: return "Unwanted file access occured. ";
case FMOD_ERR_FORMAT: return "Unsupported file or audio format. ";
case FMOD_ERR_HTTP: return "A HTTP error occurred. This is a catch-all for HTTP errors not listed elsewhere. ";
case FMOD_ERR_HTTP_ACCESS: return "The specified resource requires authentication or is forbidden. ";
case FMOD_ERR_HTTP_PROXY_AUTH: return "Proxy authentication is required to access the specified resource. ";
case FMOD_ERR_HTTP_SERVER_ERROR: return "A HTTP server error occurred. ";
case FMOD_ERR_HTTP_TIMEOUT: return "The HTTP request timed out. ";
case FMOD_ERR_INITIALIZATION: return "FMOD was not initialized correctly to support this function. ";
case FMOD_ERR_INITIALIZED: return "Cannot call this command after System::init. ";
case FMOD_ERR_INTERNAL: return "An error occured that wasn't supposed to. Contact support. ";
case FMOD_ERR_INVALID_ADDRESS: return "On Xbox 360, this memory address passed to FMOD must be physical, (ie allocated with XPhysicalAlloc.) ";
case FMOD_ERR_INVALID_FLOAT: return "Value passed in was a NaN, Inf or denormalized float. ";
case FMOD_ERR_INVALID_HANDLE: return "An invalid object handle was used. ";
case FMOD_ERR_INVALID_PARAM: return "An invalid parameter was passed to this function. ";
case FMOD_ERR_INVALID_POSITION: return "An invalid seek position was passed to this function. ";
case FMOD_ERR_INVALID_SPEAKER: return "An invalid speaker was passed to this function based on the current speaker mode. ";
case FMOD_ERR_INVALID_SYNCPOINT: return "The syncpoint did not come from this sound handle. ";
case FMOD_ERR_INVALID_VECTOR: return "The vectors passed in are not unit length, or perpendicular. ";
case FMOD_ERR_MAXAUDIBLE: return "Reached maximum audible playback count for this sound's soundgroup. ";
case FMOD_ERR_MEMORY: return "Not enough memory or resources. ";
case FMOD_ERR_MEMORY_CANTPOINT: return "Can't use FMOD_OPENMEMORY_POINT on non PCM source data, or non mp3/xma/adpcm data if FMOD_CREATECOMPRESSEDSAMPLE was used. ";
case FMOD_ERR_MEMORY_SRAM: return "Not enough memory or resources on console sound ram. ";
case FMOD_ERR_MUSIC_NOCALLBACK: return "The music callback is required, but it has not been set. ";
case FMOD_ERR_MUSIC_NOTFOUND: return "The requested music entity could not be found. ";
case FMOD_ERR_MUSIC_UNINITIALIZED: return "Music system is not initialized probably because no music data is loaded. ";
case FMOD_ERR_NEEDS2D: return "Tried to call a command on a 3d sound when the command was meant for 2d sound. ";
case FMOD_ERR_NEEDS3D: return "Tried to call a command on a 2d sound when the command was meant for 3d sound. ";
case FMOD_ERR_NEEDSHARDWARE: return "Tried to use a feature that requires hardware support. (ie trying to play a GCADPCM compressed sound in software on Wii). ";
case FMOD_ERR_NEEDSSOFTWARE: return "Tried to use a feature that requires the software engine. Software engine has either been turned off, or command was executed on a hardware channel which does not support this feature. ";
case FMOD_ERR_NET_CONNECT: return "Couldn't connect to the specified host. ";
case FMOD_ERR_NET_SOCKET_ERROR: return "A socket error occurred. This is a catch-all for socket-related errors not listed elsewhere. ";
case FMOD_ERR_NET_URL: return "The specified URL couldn't be resolved. ";
case FMOD_ERR_NET_WOULD_BLOCK: return "Operation on a non-blocking socket could not complete immediately. ";
case FMOD_ERR_NOTREADY: return "Operation could not be performed because specified sound/DSP connection is not ready. ";
case FMOD_ERR_OUTPUT_ALLOCATED: return "Error initializing output device, but more specifically, the output device is already in use and cannot be reused. ";
case FMOD_ERR_OUTPUT_CREATEBUFFER: return "Error creating hardware sound buffer. ";
case FMOD_ERR_OUTPUT_DRIVERCALL: return "A call to a standard soundcard driver failed, which could possibly mean a bug in the driver or resources were missing or exhausted. ";
case FMOD_ERR_OUTPUT_ENUMERATION: return "Error enumerating the available driver list. List may be inconsistent due to a recent device addition or removal. ";
case FMOD_ERR_OUTPUT_FORMAT: return "Soundcard does not support the minimum features needed for this soundsystem (16bit stereo output). ";
case FMOD_ERR_OUTPUT_INIT: return "Error initializing output device. ";
case FMOD_ERR_OUTPUT_NOHARDWARE: return "FMOD_HARDWARE was specified but the sound card does not have the resources necessary to play it. ";
case FMOD_ERR_OUTPUT_NOSOFTWARE: return "Attempted to create a software sound but no software channels were specified in System::init. ";
case FMOD_ERR_PAN: return "Panning only works with mono or stereo sound sources. ";
case FMOD_ERR_PLUGIN: return "An unspecified error has been returned from a 3rd party plugin. ";
case FMOD_ERR_PLUGIN_INSTANCES: return "The number of allowed instances of a plugin has been exceeded. ";
case FMOD_ERR_PLUGIN_MISSING: return "A requested output, dsp unit type or codec was not available. ";
case FMOD_ERR_PLUGIN_RESOURCE: return "A resource that the plugin requires cannot be found. (ie the DLS file for MIDI playback or other DLLs that it needs to load) ";
case FMOD_ERR_PRELOADED: return "The specified sound is still in use by the event system, call EventSystem::unloadFSB before trying to release it. ";
case FMOD_ERR_PROGRAMMERSOUND: return "The specified sound is still in use by the event system, wait for the event which is using it finish with it. ";
case FMOD_ERR_RECORD: return "An error occured trying to initialize the recording device. ";
case FMOD_ERR_REVERB_INSTANCE: return "Specified instance in FMOD_REVERB_PROPERTIES couldn't be set. Most likely because it is an invalid instance number or the reverb doesnt exist. ";
case FMOD_ERR_SUBSOUNDS: return "The error occured because the sound referenced contains subsounds when it shouldn't have, or it doesn't contain subsounds when it should have. The operation may also not be able to be performed on a parent sound, or a parent sound was played without setting up a sentence first. ";
case FMOD_ERR_SUBSOUND_ALLOCATED: return "This subsound is already being used by another sound, you cannot have more than one parent to a sound. Null out the other parent's entry first. ";
case FMOD_ERR_SUBSOUND_CANTMOVE: return "Shared subsounds cannot be replaced or moved from their parent stream, such as when the parent stream is an FSB file. ";
case FMOD_ERR_SUBSOUND_MODE: return "The subsound's mode bits do not match with the parent sound's mode bits. See documentation for function that it was called with. ";
case FMOD_ERR_TAGNOTFOUND: return "The specified tag could not be found or there are no tags. ";
case FMOD_ERR_TOOMANYCHANNELS: return "The sound created exceeds the allowable input channel count. This can be increased using the maxinputchannels parameter in System::setSoftwareFormat. ";
case FMOD_ERR_UNIMPLEMENTED: return "Something in FMOD hasn't been implemented when it should be! contact support! ";
case FMOD_ERR_UNINITIALIZED: return "This command failed because System::init or System::setDriver was not called. ";
case FMOD_ERR_UNSUPPORTED: return "A command issued was not supported by this object. Possibly a plugin without certain callbacks specified. ";
case FMOD_ERR_UPDATE: return "An error caused by System::update occured. ";
case FMOD_ERR_VERSION: return "The version number of this file format is not supported. ";
case FMOD_OK: return "No errors.";
default : return "Unknown error.";
/*$ preserve start $*/
};
}
#endif
/*$ preserve end $*/
+201
View File
@@ -0,0 +1,201 @@
/* ============================================================================================= */
/* FMOD Ex - Memory info header file. Copyright (c), Firelight Technologies Pty, Ltd. 2008-2016. */
/* */
/* Use this header if you are interested in getting detailed information on FMOD's memory */
/* usage. See the documentation for more details. */
/* */
/* ============================================================================================= */
#ifndef _FMOD_MEMORYINFO_H
#define _FMOD_MEMORYINFO_H
/*
[STRUCTURE]
[
[DESCRIPTION]
Structure to be filled with detailed memory usage information of an FMOD object
[REMARKS]
Every public FMOD class has a getMemoryInfo function which can be used to get detailed information on what memory resources are associated with the object in question.
On return from getMemoryInfo, each member of this structure will hold the amount of memory used for its type in bytes.<br>
<br>
Members marked with [in] mean the user sets the value before passing it to the function.<br>
Members marked with [out] mean FMOD sets the value to be used after the function exits.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
System::getMemoryInfo
EventSystem::getMemoryInfo
FMOD_MEMBITS
FMOD_EVENT_MEMBITS
]
*/
typedef struct FMOD_MEMORY_USAGE_DETAILS
{
unsigned int other; /* [out] Memory not accounted for by other types */
unsigned int string; /* [out] String data */
unsigned int system; /* [out] System object and various internals */
unsigned int plugins; /* [out] Plugin objects and internals */
unsigned int output; /* [out] Output module object and internals */
unsigned int channel; /* [out] Channel related memory */
unsigned int channelgroup; /* [out] ChannelGroup objects and internals */
unsigned int codec; /* [out] Codecs allocated for streaming */
unsigned int file; /* [out] File buffers and structures */
unsigned int sound; /* [out] Sound objects and internals */
unsigned int secondaryram; /* [out] Sound data stored in secondary RAM */
unsigned int soundgroup; /* [out] SoundGroup objects and internals */
unsigned int streambuffer; /* [out] Stream buffer memory */
unsigned int dspconnection; /* [out] DSPConnection objects and internals */
unsigned int dsp; /* [out] DSP implementation objects */
unsigned int dspcodec; /* [out] Realtime file format decoding DSP objects */
unsigned int profile; /* [out] Profiler memory footprint. */
unsigned int recordbuffer; /* [out] Buffer used to store recorded data from microphone */
unsigned int reverb; /* [out] Reverb implementation objects */
unsigned int reverbchannelprops; /* [out] Reverb channel properties structs */
unsigned int geometry; /* [out] Geometry objects and internals */
unsigned int syncpoint; /* [out] Sync point memory. */
unsigned int eventsystem; /* [out] EventSystem and various internals */
unsigned int musicsystem; /* [out] MusicSystem and various internals */
unsigned int fev; /* [out] Definition of objects contained in all loaded projects e.g. events, groups, categories */
unsigned int memoryfsb; /* [out] Data loaded with preloadFSB */
unsigned int eventproject; /* [out] EventProject objects and internals */
unsigned int eventgroupi; /* [out] EventGroup objects and internals */
unsigned int soundbankclass; /* [out] Objects used to manage wave banks */
unsigned int soundbanklist; /* [out] Data used to manage lists of wave bank usage */
unsigned int streaminstance; /* [out] Stream objects and internals */
unsigned int sounddefclass; /* [out] Sound definition objects */
unsigned int sounddefdefclass; /* [out] Sound definition static data objects */
unsigned int sounddefpool; /* [out] Sound definition pool data */
unsigned int reverbdef; /* [out] Reverb definition objects */
unsigned int eventreverb; /* [out] Reverb objects */
unsigned int userproperty; /* [out] User property objects */
unsigned int eventinstance; /* [out] Event instance base objects */
unsigned int eventinstance_complex; /* [out] Complex event instance objects */
unsigned int eventinstance_simple; /* [out] Simple event instance objects */
unsigned int eventinstance_layer; /* [out] Event layer instance objects */
unsigned int eventinstance_sound; /* [out] Event sound instance objects */
unsigned int eventenvelope; /* [out] Event envelope objects */
unsigned int eventenvelopedef; /* [out] Event envelope definition objects */
unsigned int eventparameter; /* [out] Event parameter objects */
unsigned int eventcategory; /* [out] Event category objects */
unsigned int eventenvelopepoint; /* [out] Event envelope point objects */
unsigned int eventinstancepool; /* [out] Event instance pool memory */
} FMOD_MEMORY_USAGE_DETAILS;
/*
[DEFINE]
[
[NAME]
FMOD_MEMBITS
[DESCRIPTION]
Bitfield used to request specific memory usage information from the getMemoryInfo function of every public FMOD Ex class.
Use with the "memorybits" parameter of getMemoryInfo to get information on FMOD Ex memory usage.
[REMARKS]
Every public FMOD class has a getMemoryInfo function which can be used to get detailed information on what memory resources are associated with the object in question.
The FMOD_MEMBITS defines can be OR'd together to specify precisely what memory usage you'd like to get information on. See System::getMemoryInfo for an example.
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
FMOD_EVENT_MEMBITS
System::getMemoryInfo
]
*/
#define FMOD_MEMBITS_OTHER 0x00000001 /* Memory not accounted for by other types */
#define FMOD_MEMBITS_STRING 0x00000002 /* String data */
#define FMOD_MEMBITS_SYSTEM 0x00000004 /* System object and various internals */
#define FMOD_MEMBITS_PLUGINS 0x00000008 /* Plugin objects and internals */
#define FMOD_MEMBITS_OUTPUT 0x00000010 /* Output module object and internals */
#define FMOD_MEMBITS_CHANNEL 0x00000020 /* Channel related memory */
#define FMOD_MEMBITS_CHANNELGROUP 0x00000040 /* ChannelGroup objects and internals */
#define FMOD_MEMBITS_CODEC 0x00000080 /* Codecs allocated for streaming */
#define FMOD_MEMBITS_FILE 0x00000100 /* Codecs allocated for streaming */
#define FMOD_MEMBITS_SOUND 0x00000200 /* Sound objects and internals */
#define FMOD_MEMBITS_SOUND_SECONDARYRAM 0x00000400 /* Sound data stored in secondary RAM */
#define FMOD_MEMBITS_SOUNDGROUP 0x00000800 /* SoundGroup objects and internals */
#define FMOD_MEMBITS_STREAMBUFFER 0x00001000 /* Stream buffer memory */
#define FMOD_MEMBITS_DSPCONNECTION 0x00002000 /* DSPConnection objects and internals */
#define FMOD_MEMBITS_DSP 0x00004000 /* DSP implementation objects */
#define FMOD_MEMBITS_DSPCODEC 0x00008000 /* Realtime file format decoding DSP objects */
#define FMOD_MEMBITS_PROFILE 0x00010000 /* Profiler memory footprint. */
#define FMOD_MEMBITS_RECORDBUFFER 0x00020000 /* Buffer used to store recorded data from microphone */
#define FMOD_MEMBITS_REVERB 0x00040000 /* Reverb implementation objects */
#define FMOD_MEMBITS_REVERBCHANNELPROPS 0x00080000 /* Reverb channel properties structs */
#define FMOD_MEMBITS_GEOMETRY 0x00100000 /* Geometry objects and internals */
#define FMOD_MEMBITS_SYNCPOINT 0x00200000 /* Sync point memory. */
#define FMOD_MEMBITS_ALL 0xffffffff /* All memory used by FMOD Ex */
/* [DEFINE_END] */
/*
[DEFINE]
[
[NAME]
FMOD_EVENT_MEMBITS
[DESCRIPTION]
Bitfield used to request specific memory usage information from the getMemoryInfo function of every public FMOD Event System class.
Use with the "event_memorybits" parameter of getMemoryInfo to get information on FMOD Event System memory usage.
[REMARKS]
Every public FMOD Event System class has a getMemoryInfo function which can be used to get detailed information on what memory resources are associated with the object in question.
The FMOD_EVENT_MEMBITS defines can be OR'd together to specify precisely what memory usage you'd like to get information on. See EventSystem::getMemoryInfo for an example.
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
FMOD_MEMBITS
System::getMemoryInfo
]
*/
#define FMOD_EVENT_MEMBITS_EVENTSYSTEM 0x00000001 /* EventSystem and various internals */
#define FMOD_EVENT_MEMBITS_MUSICSYSTEM 0x00000002 /* MusicSystem and various internals */
#define FMOD_EVENT_MEMBITS_FEV 0x00000004 /* Definition of objects contained in all loaded projects e.g. events, groups, categories */
#define FMOD_EVENT_MEMBITS_MEMORYFSB 0x00000008 /* Data loaded with preloadFSB */
#define FMOD_EVENT_MEMBITS_EVENTPROJECT 0x00000010 /* EventProject objects and internals */
#define FMOD_EVENT_MEMBITS_EVENTGROUPI 0x00000020 /* EventGroup objects and internals */
#define FMOD_EVENT_MEMBITS_SOUNDBANKCLASS 0x00000040 /* Objects used to manage wave banks */
#define FMOD_EVENT_MEMBITS_SOUNDBANKLIST 0x00000080 /* Data used to manage lists of wave bank usage */
#define FMOD_EVENT_MEMBITS_STREAMINSTANCE 0x00000100 /* Stream objects and internals */
#define FMOD_EVENT_MEMBITS_SOUNDDEFCLASS 0x00000200 /* Sound definition objects */
#define FMOD_EVENT_MEMBITS_SOUNDDEFDEFCLASS 0x00000400 /* Sound definition static data objects */
#define FMOD_EVENT_MEMBITS_SOUNDDEFPOOL 0x00000800 /* Sound definition pool data */
#define FMOD_EVENT_MEMBITS_REVERBDEF 0x00001000 /* Reverb definition objects */
#define FMOD_EVENT_MEMBITS_EVENTREVERB 0x00002000 /* Reverb objects */
#define FMOD_EVENT_MEMBITS_USERPROPERTY 0x00004000 /* User property objects */
#define FMOD_EVENT_MEMBITS_EVENTINSTANCE 0x00008000 /* Event instance base objects */
#define FMOD_EVENT_MEMBITS_EVENTINSTANCE_COMPLEX 0x00010000 /* Complex event instance objects */
#define FMOD_EVENT_MEMBITS_EVENTINSTANCE_SIMPLE 0x00020000 /* Simple event instance objects */
#define FMOD_EVENT_MEMBITS_EVENTINSTANCE_LAYER 0x00040000 /* Event layer instance objects */
#define FMOD_EVENT_MEMBITS_EVENTINSTANCE_SOUND 0x00080000 /* Event sound instance objects */
#define FMOD_EVENT_MEMBITS_EVENTENVELOPE 0x00100000 /* Event envelope objects */
#define FMOD_EVENT_MEMBITS_EVENTENVELOPEDEF 0x00200000 /* Event envelope definition objects */
#define FMOD_EVENT_MEMBITS_EVENTPARAMETER 0x00400000 /* Event parameter objects */
#define FMOD_EVENT_MEMBITS_EVENTCATEGORY 0x00800000 /* Event category objects */
#define FMOD_EVENT_MEMBITS_EVENTENVELOPEPOINT 0x01000000 /* Event envelope point object+s */
#define FMOD_EVENT_MEMBITS_EVENTINSTANCEPOOL 0x02000000 /* Event instance pool data */
#define FMOD_EVENT_MEMBITS_ALL 0xffffffff /* All memory used by FMOD Event System */
/* All event instance memory */
#define FMOD_EVENT_MEMBITS_EVENTINSTANCE_GROUP (FMOD_EVENT_MEMBITS_EVENTINSTANCE | \
FMOD_EVENT_MEMBITS_EVENTINSTANCE_COMPLEX | \
FMOD_EVENT_MEMBITS_EVENTINSTANCE_SIMPLE | \
FMOD_EVENT_MEMBITS_EVENTINSTANCE_LAYER | \
FMOD_EVENT_MEMBITS_EVENTINSTANCE_SOUND)
/* All sound definition memory */
#define FMOD_EVENT_MEMBITS_SOUNDDEF_GROUP (FMOD_EVENT_MEMBITS_SOUNDDEFCLASS | \
FMOD_EVENT_MEMBITS_SOUNDDEFDEFCLASS | \
FMOD_EVENT_MEMBITS_SOUNDDEFPOOL)
/* [DEFINE_END] */
#endif
+93
View File
@@ -0,0 +1,93 @@
/* ==================================================================================================== */
/* FMOD Ex - output development header file. Copyright (c), Firelight Technologies Pty, Ltd. 2004-2016. */
/* */
/* Use this header if you are wanting to develop your own output plugin to use with */
/* FMOD's output system. With this header you can make your own output plugin that FMOD */
/* can register and use. See the documentation and examples on how to make a working plugin. */
/* */
/* ==================================================================================================== */
#ifndef _FMOD_OUTPUT_H
#define _FMOD_OUTPUT_H
#include "fmod.h"
typedef struct FMOD_OUTPUT_STATE FMOD_OUTPUT_STATE;
/*
Output callbacks
*/
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETNUMDRIVERSCALLBACK)(FMOD_OUTPUT_STATE *output_state, int *numdrivers);
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETDRIVERNAMECALLBACK)(FMOD_OUTPUT_STATE *output_state, int id, char *name, int namelen);
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETDRIVERCAPSCALLBACK)(FMOD_OUTPUT_STATE *output_state, int id, FMOD_CAPS *caps);
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_INITCALLBACK) (FMOD_OUTPUT_STATE *output_state, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata);
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_CLOSECALLBACK) (FMOD_OUTPUT_STATE *output_state);
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_UPDATECALLBACK) (FMOD_OUTPUT_STATE *output_state);
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETHANDLECALLBACK) (FMOD_OUTPUT_STATE *output_state, void **handle);
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_GETPOSITIONCALLBACK) (FMOD_OUTPUT_STATE *output_state, unsigned int *pcm);
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_LOCKCALLBACK) (FMOD_OUTPUT_STATE *output_state, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2);
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_UNLOCKCALLBACK) (FMOD_OUTPUT_STATE *output_state, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2);
typedef FMOD_RESULT (F_CALLBACK *FMOD_OUTPUT_READFROMMIXER) (FMOD_OUTPUT_STATE *output_state, void *buffer, unsigned int length);
/*
[STRUCTURE]
[
[DESCRIPTION]
When creating an output, declare one of these and provide the relevant callbacks and name for FMOD to use when it opens and reads a file of this type.
[REMARKS]
Members marked with [in] mean the variable can be written to. The user can set the value.<br>
Members marked with [out] mean the variable is modified by FMOD and is for reading purposes only. Do not change this value.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
FMOD_OUTPUT_STATE
]
*/
typedef struct FMOD_OUTPUT_DESCRIPTION
{
const char *name; /* [in] Name of the output. */
unsigned int version; /* [in] Plugin writer's version number. */
int polling; /* [in] If TRUE (non zero), this tells FMOD to start a thread and call getposition / lock / unlock for feeding data. If 0, the output is probably callback based, so all the plugin needs to do is call readfrommixer to the appropriate pointer. */
FMOD_OUTPUT_GETNUMDRIVERSCALLBACK getnumdrivers; /* [in] For sound device enumeration. This callback is to give System::getNumDrivers somthing to return. */
FMOD_OUTPUT_GETDRIVERNAMECALLBACK getdrivername; /* [in] For sound device enumeration. This callback is to give System::getDriverName somthing to return. */
FMOD_OUTPUT_GETDRIVERCAPSCALLBACK getdrivercaps; /* [in] For sound device enumeration. This callback is to give System::getDriverCaps somthing to return. */
FMOD_OUTPUT_INITCALLBACK init; /* [in] Initialization function for the output device. This is called from System::init. */
FMOD_OUTPUT_CLOSECALLBACK close; /* [in] Cleanup / close down function for the output device. This is called from System::close. */
FMOD_OUTPUT_UPDATECALLBACK update; /* [in] Update function that is called once a frame by the user. This is called from System::update. */
FMOD_OUTPUT_GETHANDLECALLBACK gethandle; /* [in] This is called from System::getOutputHandle. This is just to return a pointer to the internal system device object that the system may be using.*/
FMOD_OUTPUT_GETPOSITIONCALLBACK getposition; /* [in] This is called from the FMOD software mixer thread if 'polling' = true. This returns a position value in samples so that FMOD knows where and when to fill its buffer. */
FMOD_OUTPUT_LOCKCALLBACK lock; /* [in] This is called from the FMOD software mixer thread if 'polling' = true. This function provides a pointer to data that FMOD can write to when software mixing. */
FMOD_OUTPUT_UNLOCKCALLBACK unlock; /* [in] This is called from the FMOD software mixer thread if 'polling' = true. This optional function accepts the data that has been mixed and copies it or does whatever it needs to before sending it to the hardware. */
} FMOD_OUTPUT_DESCRIPTION;
/*
[STRUCTURE]
[
[DESCRIPTION]
Output plugin structure that is passed into each callback.
[REMARKS]
Members marked with [in] mean the variable can be written to. The user can set the value.<br>
Members marked with [out] mean the variable is modified by FMOD and is for reading purposes only. Do not change this value.<br>
[PLATFORMS]
Win32, Win64, Linux, Linux64, Macintosh, Xbox360, PlayStation Portable, PlayStation 3, Wii, iPhone, 3GS, NGP, Android
[SEE_ALSO]
FMOD_OUTPUT_DESCRIPTION
]
*/
struct FMOD_OUTPUT_STATE
{
void *plugindata; /* [in] Plugin writer created data the output author wants to attach to this object. */
FMOD_OUTPUT_READFROMMIXER readfrommixer; /* [out] Function to update mixer and write the result to the provided pointer. Used from callback based output only. Polling based output uses lock/unlock/getposition. */
};
#endif
+36
View File
@@ -0,0 +1,36 @@
/* =========================================================================================== */
/* FMOD Linux Specific header file. Copyright (c), Firelight Technologies Pty, Ltd. 2005-2016. */
/* =========================================================================================== */
#ifndef _FMODLINUX_H
#define _FMODLINUX_H
#include "fmod.h"
/*
[STRUCTURE]
[
[DESCRIPTION]
Use this structure with System::init to set the information required for linux
initialisation.
Pass this structure in as the "extradriverdata" parameter in System::init.
[REMARKS]
[PLATFORMS]
Linux, Linux64
[SEE_ALSO]
System::init
]
*/
typedef struct FMOD_LINUX_EXTRADRIVERDATA
{
const char *output_driver_arguments; /* ALSA Only - Arguments to apply to the selected output driver */
const char *record_driver_arguments; /* ALSA Only - Arguments to apply to the selected input (record) driver */
} FMOD_LINUX_EXTRADRIVERDATA;
#endif
+49
View File
@@ -0,0 +1,49 @@
#include "util/adler32.h"
#include "usb-rank.h"
#define ENC_DEC_INIT_SEED 0xEBADA1
struct asset_f2_usb_rank* asset_f2_usb_rank_new(void)
{
// TODO
return malloc(sizeof(struct asset_f2_usb_rank));
}
void asset_f2_usb_rank_finalize(struct asset_f2_usb_rank* rank)
{
/* don't include the checksum field */
/*
rank->header.adler32 = util_adler32_calc(rank->header.adler_seed,
&rank->header.adler_seed,
sizeof(struct f2_profile_rank) - sizeof(uint32_t));
*/
}
char* asset_f2_usb_rank_to_string(const struct asset_f2_usb_rank* rank)
{
// TODO
return "";
}
void asset_f2_usb_rank_decrypt(uint8_t* buf, size_t len)
{
uint32_t seed = ENC_DEC_INIT_SEED;
for (int i = 0; i < len; i++){
uint8_t smbuff = buf[i];
buf[i] ^= (seed >> 8) & 0xff ;
seed = 0x68993 * (smbuff + seed) + 0x4FDCF;
}
}
void asset_f2_usb_rank_encrypt(uint8_t* buf, size_t len)
{
uint32_t seed = ENC_DEC_INIT_SEED;
for (size_t i = 0; i < len; i++){
uint8_t smbuff = buf[i] ^ ((seed >> 8) & 0xff);
buf[i] = smbuff ;
seed = 0x68993 * (smbuff + seed) + 0x4FDCF;
}
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef ASSET_F2_USB_RANK_H
#define ASSET_F2_USB_RANK_H
#include <stdint.h>
struct asset_f2_usb_rank {
// TODO
} __attribute__((__packed__));
struct asset_f2_usb_rank* asset_f2_usb_rank_new(void);
// update checksum and prepare profile to get encrypted
void asset_f2_usb_rank_finalize(struct asset_f2_usb_rank* rank);
char* asset_f2_usb_rank_to_string(const struct asset_f2_usb_rank* rank);
void asset_f2_usb_rank_decrypt(uint8_t* buf, size_t len);
void asset_f2_usb_rank_encrypt(uint8_t* buf, size_t len);
#endif
+47
View File
@@ -0,0 +1,47 @@
#include "util/adler32.h"
#include "usb-save.h"
#define ENC_DEC_INIT_SEED 0xEBADA1
struct asset_f2_usb_save* asset_f2_usb_save_new(void)
{
// TODO
return malloc(sizeof(struct asset_f2_usb_save));
}
void asset_f2_usb_save_finalize(struct asset_f2_usb_save* save)
{
/* don't include the checksum field */
save->header.adler32 = util_adler32_calc(save->header.adler_seed,
&save->header.adler_seed,
sizeof(struct asset_f2_usb_save) - sizeof(uint32_t));
}
char* asset_f2_usb_save_to_string(const struct asset_f2_usb_save* save)
{
// TODO
return "";
}
void asset_f2_usb_save_decrypt(uint8_t* buf, size_t len)
{
uint32_t seed = ENC_DEC_INIT_SEED;
for (int i = 0; i < len; i++){
uint8_t smbuff = buf[i];
buf[i] ^= (seed >> 8) & 0xff ;
seed = 0x68993 * (smbuff + seed) + 0x4FDCF;
}
}
void asset_f2_usb_save_encrypt(uint8_t* buf, size_t len)
{
uint32_t seed = ENC_DEC_INIT_SEED;
for (size_t i = 0; i < len; i++){
uint8_t smbuff = buf[i] ^ (seed >> 8) & 0xff;
buf[i] = smbuff ;
seed = 0x68993 * (smbuff + seed) + 0x4FDCF;
}
}
+105
View File
@@ -0,0 +1,105 @@
#ifndef ASSET_F2_USB_SAVE_H
#define ASSET_F2_USB_SAVE_H
#include <stdint.h>
#include <stdlib.h>
struct asset_f2_usb_save_machine_stats {
uint32_t beginner_mode_play_count;
uint32_t maniac_mode_play_count;
uint32_t unkn;
uint32_t unkn2;
uint32_t battle_mode_play_count;
uint32_t mission_zone_play_count;
uint32_t skill_zone_play_count;
uint32_t coop_mission_play_count;
} __attribute__((__packed__));
struct asset_f2_usb_save_machine_info {
/* NULL terminated */
char version[8];
/* NULL terminated */
char cpu[128];
/* NULL terminated */
char motherboard[128];
/* NULL terminated */
char gfxcard[128];
/* NULL terminated */
char hdd[32];
uint32_t total_ram_bytes;
uint32_t haspkey_id;
} __attribute__((__packed__));
struct asset_f2_usb_save_header {
uint32_t adler32;
uint32_t adler_seed;
/* NULL terminated */
char player_id[8];
uint32_t region;
uint32_t avatar_id;
uint32_t level;
float total_calories;
float total_v02;
uint64_t num_running_steps;
uint64_t num_games_played;
uint64_t exp;
uint64_t arcade_score;
uint32_t num_missions_completed;
uint32_t num_coop_missions_completed;
uint32_t battle_count_wins;
uint32_t battle_count_loses;
uint32_t battle_count_draws;
/* TODO: modifier stuff? */
uint64_t unlock;
uint32_t unlock_seed;
} __attribute__((__packed__));
struct asset_f2_usb_save_machine_highscore_entry {
uint32_t song_id;
uint8_t difficulty;
uint8_t mode;
uint8_t grade;
uint8_t clear_state;
uint32_t score_total;
uint32_t play_count;
uint32_t unkn;
/* NULL terminated */
char player_id[8];
uint32_t unkn2;
uint32_t null_padding;
} __attribute__((__packed__));
struct asset_f2_usb_save_highscore_entry {
uint32_t song_id;
uint8_t difficulty;
uint8_t mode;
uint8_t grade;
uint8_t clear_and_unlock_state;
uint32_t score_total;
uint32_t play_count;
uint32_t null_padding;
} __attribute__((__packed__));
struct asset_f2_usb_save {
/* start offset: 0x00 */
struct asset_f2_usb_save_header header;
/* start offset: 0x120 */
struct asset_f2_usb_save_machine_info machine_info;
struct asset_f2_usb_save_machine_stats machine_stats;
/* start offset: 0x2F0 */
// TODO continue
} __attribute__((__packed__));
struct asset_f2_usb_save* asset_f2_usb_save_new(void);
// update checksum and prepare profile to get encrypted
void asset_f2_usb_save_finalize(struct asset_f2_usb_save* save);
char* asset_f2_usb_save_to_string(const struct asset_f2_usb_save* save);
void asset_f2_usb_save_decrypt(uint8_t* buf, size_t len);
void asset_f2_usb_save_encrypt(uint8_t* buf, size_t len);
#endif
+96
View File
@@ -0,0 +1,96 @@
#define LOG_MODULE "asset-f2-util"
#include <string.h>
#include "util/fs.h"
#include "util/log.h"
#include "util.h"
struct asset_f2_usb_rank* asset_f2_util_rank_usb_load_from_file(const char* path, bool encrypted)
{
struct asset_f2_usb_rank* rank;
size_t size;
if (!util_file_load(path, (void**) &rank, &size, false)) {
log_error("Loading f2 rank file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_f2_usb_rank)) {
log_error("Invalid size of f2 rank file %s: %d != %d", path, size,
sizeof(struct asset_f2_usb_rank));
free(rank);
return NULL;
}
if (encrypted) {
asset_f2_usb_rank_decrypt((uint8_t*) rank, size);
}
return rank;
}
bool asset_f2_util_rank_usb_save_to_file(const char* path, const struct asset_f2_usb_rank* rank, bool encrypt)
{
struct asset_f2_usb_rank rank_enc;
memcpy(&rank_enc, rank, sizeof(struct asset_f2_usb_rank));
if (encrypt) {
asset_f2_usb_rank_encrypt((uint8_t*) &rank_enc,
sizeof(struct asset_f2_usb_rank));
}
if (!util_file_save(path, (void**) &rank_enc,
sizeof(struct asset_f2_usb_rank))) {
log_error("Storing f2 rank data to %s failed", path);
return false;
}
return true;
}
struct asset_f2_usb_save* asset_f2_util_usb_save_load_from_file(const char* path, bool encrypted)
{
struct asset_f2_usb_save* save;
size_t size;
if (!util_file_load(path, (void**) &save, &size, false)) {
log_error("Loading f2 save file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_f2_usb_save)) {
log_error("Invalid size of f2 save file %s: %d != %d", path, size,
sizeof(struct asset_f2_usb_save));
free(save);
return NULL;
}
if (encrypted) {
asset_f2_usb_save_decrypt((uint8_t*) save, size);
}
return save;
}
bool asset_f2_util_usb_save_save_to_file(const char* path, const struct asset_f2_usb_save* save, bool encrypt)
{
struct asset_f2_usb_save save_enc;
memcpy(&save_enc, save, sizeof(struct asset_f2_usb_save));
if (encrypt) {
asset_f2_usb_save_encrypt((uint8_t*) &save_enc,
sizeof(struct asset_f2_usb_save));
}
if (!util_file_save(path, (const void*) &save_enc,
sizeof(struct asset_f2_usb_save))) {
log_error("Storing f2 save data to %s failed", path);
return false;
}
return true;
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef ASSET_F2_UTIL_H
#define ASSET_F2_UTIL_H
#include <stdbool.h>
#include "usb-rank.h"
#include "usb-save.h"
// load from file and decrypt, returns decrypted struct
struct asset_f2_usb_rank* asset_f2_util_usb_rank_load_from_file(const char* path, bool encrypted);
// takes decrypted rank data, encrypts it and writes it to file
bool asset_f2_util_usb_rank_save_to_file(const char* path, const struct asset_f2_usb_rank* rank, bool encrypt);
// load from file and decrypt, returns decrypted struct
struct asset_f2_usb_save* asset_f2_util_usb_save_load_from_file(const char* path, bool encrypted);
// takes decrypted save data, encrypts it and writes it to file
bool asset_f2_util_usb_save_save_to_file(const char* path, const struct asset_f2_usb_save* save, bool encrypt);
#endif
+32
View File
@@ -0,0 +1,32 @@
#define LOG_MODULE "fex-profile-rank"
#include "usb-rank.h"
struct asset_fex_usb_rank* asset_fex_usb_rank_new(void)
{
return malloc(sizeof(struct asset_fex_usb_rank));
}
void asset_fex_usb_rank_finalize(struct asset_fex_usb_rank* rank)
{
}
char* asset_fex_usb_rank_to_string(const struct asset_fex_usb_rank* rank)
{
return malloc(0);
}
void asset_fex_usb_rank_decrypt(uint8_t* buf, size_t len)
{
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_fex_usb_rank_encrypt(uint8_t* buf, size_t len)
{
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef ASSET_FEX_USB_RANK_H
#define ASSET_FEX_USB_RANK_H
#include <stdint.h>
#include <stdlib.h>
struct asset_fex_usb_rank_header {
uint32_t adler32;
/* NULL terminated */
char player_id[8];
} __attribute__((__packed__));
struct asset_fex_usb_rank {
struct asset_fex_usb_rank_header header;
} __attribute__((__packed__));
struct asset_fex_usb_rank* asset_fex_usb_rank_new(void);
// update checksum and prepare profile to get encrypted
void asset_fex_usb_rank_finalize(struct asset_fex_usb_rank* rank);
char* asset_fex_usb_rank_to_string(const struct asset_fex_usb_rank* rank);
void asset_fex_usb_rank_decrypt(uint8_t* buf, size_t len);
void asset_fex_usb_rank_encrypt(uint8_t* buf, size_t len);
#endif
+339
View File
@@ -0,0 +1,339 @@
#include <stdio.h>
#include <string.h>
#include "util/adler32.h"
#include "util/mem.h"
#include "util/str.h"
#include "usb-save.h"
static char* fex_profile_save_header_to_string(
const struct asset_fex_usb_save_header* header)
{
char* buffer;
buffer = (char*) util_xmalloc(256);
memset(buffer, 0, 256);
util_str_format(buffer, 256,
"-------------- header --------------\n"
"adler32: %X\n"
"usb_serial: %s\n"
"dongle_serial: %X\n",
header->adler32,
header->usb_serial,
header->dongle_serial);
return buffer;
}
static char* fex_profile_save_player_to_string(
const struct asset_fex_usb_save_player* player)
{
char* buffer;
buffer = (char*) util_xmalloc(1024);
memset(buffer, 0, 1024);
util_str_format(buffer, 1024,
"-------------- player --------------\n"
"unkn: %X\n"
"unkn2: %X\n"
"avatar_id: %d\n"
"player_lvl: %d\n"
"unkn3: %X\n"
"player_id: %s\n"
"unkn4: %X\n"
"unkn5: %X\n"
"unkn6: %X\n"
"unkn7: %X\n"
"unkn8: %X\n"
"unkn9: %X\n"
"unkn10: %X\n"
"unkn11: %X\n"
"unkn12: %X\n"
"unkn13: %X\n"
"unkn14: %X\n"
"unkn15: %X\n"
"total_step: %d\n"
"play_count: %d\n"
"exp: %d\n"
"num_quest_world_passed: %d\n"
"unkn16: %X\n"
"arcade_score: %d\n"
"num_battle_wins: %d\n"
"num_battle_loss: %d\n"
"num_battle_draw: %d\n",
player->unkn,
player->unkn2,
player->avatar_id,
player->player_lvl,
player->unkn3,
player->player_id,
player->unkn4,
player->unkn5,
player->unkn6,
player->unkn7,
player->unkn8,
player->unkn9,
player->unkn10,
player->unkn11,
player->unkn12,
player->unkn13,
player->unkn14,
player->unkn15,
player->total_step,
player->play_count,
player->exp,
player->num_quest_world_passed,
player->unkn16,
player->arcade_score,
player->num_battle_wins,
player->num_battle_loss,
player->num_battle_draw);
return buffer;
}
static char* fex_profile_save_mods_to_string(
const struct asset_fex_usb_save_player_mods* mods)
{
char* buffer;
buffer = (char*) util_xmalloc(128);
memset(buffer, 0, 128);
util_str_format(buffer, 128,
"-------------- modifiers --------------\n"
"modifiers: %X\n"
"half_speed_mod: %X\n"
"noteskin: %X\n",
mods->modifiers,
mods->half_speed_mod,
mods->noteskin);
return buffer;
}
static char* fex_profile_save_mod_unlocks_to_string(
const struct asset_fex_usb_save_mod_unlocks* unlocks)
{
char* merged;
char* buffer;
char* tmp;
const size_t buf_size = 512;
merged = (char*) util_xmalloc(buf_size);
memset(merged, 0, buf_size);
buffer = (char*) util_xmalloc(buf_size);
memset(buffer, 0, buf_size);
util_str_format(buffer, buf_size,
"-------------- mod unlocks --------------\n" );
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
for (uint32_t i = 0; i < 0x12; i++) {
util_str_format(buffer, buf_size,
"unkn0[%d]: %d\n",
i,
unlocks->unkn0[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
util_str_format(buffer, buf_size,
"display_va: %d\n",
unlocks->display_va);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
for (uint32_t i = 0; i < 0x0D; i++) {
util_str_format(buffer, buf_size,
"unkn1[%d]: %d\n",
i,
unlocks->unkn1[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x10; i++) {
util_str_format(buffer, buf_size,
"noteskins[%d]: %d\n",
i,
unlocks->noteskins[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x04; i++) {
util_str_format(buffer, buf_size,
"unkn2[%d]: %d\n",
i,
unlocks->unkn2[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x03; i++) {
util_str_format(buffer, buf_size,
"path[%d]: %d\n",
i,
unlocks->path[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x06; i++) {
util_str_format(buffer, buf_size,
"unkn3[%d]: %d\n",
i,
unlocks->unkn3[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
util_str_format(buffer, buf_size,
"xj: %d\n",
unlocks->xj);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
for (uint32_t i = 0; i < 0x02; i++) {
util_str_format(buffer, buf_size,
"system[%d]: %d\n",
i,
unlocks->system[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x08; i++) {
util_str_format(buffer, buf_size,
"rush[%d]: %d\n",
i,
unlocks->rush[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < 0x1A; i++) {
util_str_format(buffer, buf_size,
"unkn4[%d]: %d\n",
i,
unlocks->unkn4[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
return merged;
}
static char* fex_profile_save_mission_state_to_string(
const struct asset_fex_usb_save_mission_state* state)
{
char* buffer;
buffer = (char*) util_xmalloc(128);
memset(buffer, 0, 128);
util_str_format(buffer, 128,
"-------------- mission state --------------\n"
"quest_world_location: %X\n"
"skill_up_zone_location: %X\n",
state->quest_world_location,
state->skill_up_zone_location);
return buffer;
}
struct asset_fex_usb_save* asset_fex_usb_save_new(void)
{
return malloc(sizeof(struct asset_fex_usb_save));
}
void asset_fex_usb_save_finalize(struct asset_fex_usb_save* save)
{
}
char* asset_fex_usb_save_to_string(const struct asset_fex_usb_save* save)
{
char* header;
char* player;
char* mods;
char* unlocks;
char* mission_state;
char* merged;
char* merged2;
header = fex_profile_save_header_to_string(&save->header);
player = fex_profile_save_player_to_string(&save->player);
mods = fex_profile_save_mods_to_string(&save->mods);
unlocks = fex_profile_save_mod_unlocks_to_string(&save->mod_unlocks);
mission_state = fex_profile_save_mission_state_to_string(&save->mission_state);
merged = util_str_merge(header, player);
merged2 = util_str_merge(merged, mods);
free(merged);
merged = util_str_merge(merged2, unlocks);
free(merged2);
merged2 = util_str_merge(merged, mission_state);
free(merged);
free(header);
free(player);
free(mods);
free(unlocks);
free(mission_state);
return merged2;
}
void asset_fex_usb_save_decrypt(uint8_t* buf, size_t len)
{
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_fex_usb_save_encrypt(uint8_t* buf, size_t len)
{
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+191
View File
@@ -0,0 +1,191 @@
#ifndef ASSET_FEX_USB_SAVE_H
#define ASSET_FEX_USB_SAVE_H
#include <stdint.h>
#include <stdlib.h>
enum asset_fex_usb_save_noteskins {
ASSET_FEX_USB_SAVE_SKIN_DEFAULT = 0x00,
ASSET_FEX_USB_SAVE_SKIN_HANAFUDA = 0x01,
ASSET_FEX_USB_SAVE_SKIN_CLASSIC = 0x02,
ASSET_FEX_USB_SAVE_SKIN_CANDY = 0x03,
ASSET_FEX_USB_SAVE_SKIN_PORING = 0x04,
ASSET_FEX_USB_SAVE_SKIN_MUSICAL_NOTES = 0x05,
ASSET_FEX_USB_SAVE_SKIN_CANON_D = 0x06,
ASSET_FEX_USB_SAVE_SKIN_PLAYING_CARDS = 0x07,
ASSET_FEX_USB_SAVE_SKIN_NX = 0x08,
ASSET_FEX_USB_SAVE_SKIN_DIGNITY = 0x09,
ASSET_FEX_USB_SAVE_SKIN_HORSE_HEAD = 0x0A,
ASSET_FEX_USB_SAVE_SKIN_DOG = 0x0B,
ASSET_FEX_USB_SAVE_SKIN_GIRL = 0x0C,
ASSET_FEX_USB_SAVE_SKIN_FIRE = 0x0D,
ASSET_FEX_USB_SAVE_SKIN_ICE = 0x0E,
ASSET_FEX_USB_SAVE_SKIN_WIND = 0x0F,
ASSET_FEX_USB_SAVE_SKIN_RED_DP = 0x10,
ASSET_FEX_USB_SAVE_SKIN_BLUE_DP = 0x11,
ASSET_FEX_USB_SAVE_SKIN_YELLOW_DP = 0x12,
ASSET_FEX_USB_SAVE_SKIN_NXA = 0x13,
ASSET_FEX_USB_SAVE_SKIN_NX2 = 0x14,
ASSET_FEX_USB_SAVE_SKIN_ELECTRIC_FIESTA = 0x15,
ASSET_FEX_USB_SAVE_SKIN_DRUM = 0x16,
ASSET_FEX_USB_SAVE_SKIN_BULLET = 0x17,
ASSET_FEX_USB_SAVE_SKIN_BLUE_DRUM = 0x18,
ASSET_FEX_USB_SAVE_SKIN_RED_DRUM = 0x19,
ASSET_FEX_USB_SAVE_SKIN_YELLOW_DRUM = 0x1A,
ASSET_FEX_USB_SAVE_SKIN_WORLD_CUP = 0x1B,
ASSET_FEX_USB_SAVE_SKIN_PREX3 = 0x1C,
ASSET_FEX_USB_SAVE_SKIN_FIESTA_BASIC = 0x1D,
};
struct asset_fex_usb_save_header {
/* 0x00 */
uint32_t adler32;
/* 0x04: NULL terminated */
char usb_serial[0x40];
/* 0x44: Deprecated from old games */
uint32_t dongle_serial;
} __attribute__((__packed__));
struct asset_fex_usb_save_player {
/* 0x48 */
uint32_t unkn;
/* 0x4C */
uint32_t unkn2;
/* 0x50 */
uint8_t avatar_id;
/* 0x51: Range: 0x00 - 0x21 */
uint8_t player_lvl;
/* 0x52: PumBi sets it to 0xC4 */
uint16_t unkn3;
/* 0x54: NULL terminated */
char player_id[8];
/* 0x5C */
uint32_t unkn4;
/* 0x60 */
uint32_t unkn5;
/* 0x64: Each time you save, it inc. by 6... */
uint32_t unkn6;
/* 0x68 */
uint32_t unkn7;
/* 0x6C */
uint32_t unkn8;
/* 0x70 */
uint32_t unkn9;
/* 0x74 */
uint32_t unkn10;
/* 0x78 */
uint32_t unkn11;
/* 0x7C */
uint32_t unkn12;
/* 0x80 */
uint32_t unkn13;
/* 0x84 */
uint32_t unkn14;
/* 0x88 */
uint32_t unkn15;
/* 0x8C */
uint32_t total_step;
/* 0x90 */
uint32_t play_count;
/* 0x94 */
uint32_t exp;
/* 0x98 */
uint32_t num_quest_world_passed;
/* 0x9C */
uint32_t unkn16;
/* 0xA0 */
uint32_t arcade_score;
/* 0xA8 */
uint32_t num_battle_wins;
/* 0xAC */
uint32_t num_battle_loss;
/* 0xB0 */
uint32_t num_battle_draw;
} __attribute__((__packed__));
struct asset_fex_usb_save_player_mods {
/* 0xB4: The first set of saved modifiers (see MOD_1 below) */
uint32_t modifiers;
/* 0xB8: If the +0.5x mod is enabled (boolean) */
uint32_t half_speed_mod;
/* 0xBC: Which noteskin is currently selected (see NOTE_S below) */
uint32_t noteskin;
} __attribute__((__packed__));
struct asset_fex_usb_save_mod_unlocks {
/* 0xC0: Unknown Unlocks Area */
uint8_t unkn0[0x12];
/* 0xD2: Boolean for unlocking VA Mod. */
uint8_t display_va;
/* 0xD3: Unknown Unlocks Area */
uint8_t unkn1[0x0D];
/* 0xE0: Array of bools - Which Noteskins are Unlocked. */
uint8_t noteskins[0x10];
/* 0xF0: Unknown Unlocks Area */
uint8_t unkn2[0x04];
/* 0xF4: Array of bools - Which Path Mods are Unlocked. */
uint8_t path[0x03];
/* 0xF7: Unknown Unlocks Area */
uint8_t unkn3[0x06];
/* 0xFE: Boolean for unlocking XJ */
uint8_t xj;
/* 0x100: Array of bools - Which System Mods are Unlocked. */
uint8_t system[0x02];
/* 0x102: Array of bools - Which Rush/NOT Modes are Unlocked. */
uint8_t rush[0x08];
/* 0x10A: Unknown Unlocks Area */
uint8_t unkn4[0x1A];
} __attribute__((__packed__));
struct asset_fex_usb_save_mission_state {
/* 0x124 */
uint32_t quest_world_location;
/* 0x128 */
uint32_t skill_up_zone_location;
} __attribute__((__packed__));
struct asset_fex_usb_save {
struct asset_fex_usb_save_header header;
struct asset_fex_usb_save_player player;
struct asset_fex_usb_save_player_mods mods;
struct asset_fex_usb_save_mod_unlocks mod_unlocks;
struct asset_fex_usb_save_mission_state mission_state;
/* Not verified, yet. just notes from old tools */
/* 0x17AD (0x0A): Quest world inventory */
/* 0x17B7: ??? */
/* 0x17B8 (0x1C): Quest world warp unlocks */
/* 0x17AC (0x11): QW inventory */
/* 0x17D4 or 0x17D5 ? (0x14): favorites channel */
/* 0x2E48 - 0x1A5EF: step chart blacklist */
/* 0x2E48 - 0x1A5EF: step chart unlocks = blacklist */
/* pumbi stepcharts */
/* 0x6047: pumbi stepchart 1009 Level S15 */
/* 0x6083 */
/* 0x60AB */
/* 0x647F */
/* 0x64BB */
/* 0x64CF */
/* 0x650B */
/* 0x6533 */
/* 0x655B */
/* 0x65AB */
/* 0x660F */
} __attribute__((__packed__));
struct asset_fex_usb_save* asset_fex_usb_save_new(void);
// update checksum and prepare profile to get encrypted
void asset_fex_usb_save_finalize(struct asset_fex_usb_save* save);
char* asset_fex_usb_save_to_string(const struct asset_fex_usb_save* save);
void asset_fex_usb_save_decrypt(uint8_t* buf, size_t len);
void asset_fex_usb_save_encrypt(uint8_t* buf, size_t len);
#endif
+100
View File
@@ -0,0 +1,100 @@
#define LOG_MODULE "asset-fex-util"
#include <string.h>
#include "util/fs.h"
#include "util/log.h"
#include "util.h"
struct asset_fex_usb_rank* asset_fex_usb_rank_load_from_file(const char* path,
bool encrypted)
{
struct asset_fex_usb_rank* rank;
size_t size;
if (!util_file_load(path, (void**) &rank, &size, false)) {
log_error("Loading fex rank file %s failed", path);
return NULL;
}
// if (size != sizeof(struct fex_profile_rank)) {
// log_error("Invalid size of fex rank file %s: %d != %d", path, size,
// sizeof(struct fex_profile_rank));
// free(rank);
// return NULL;
// }
if (encrypted) {
asset_fex_usb_rank_decrypt((uint8_t*) rank, size);
}
return rank;
}
bool asset_fex_usb_rank_save_to_file(const char* path,
const struct asset_fex_usb_rank* rank, bool encrypt)
{
struct asset_fex_usb_rank rank_enc;
memcpy(&rank_enc, rank, sizeof(struct asset_fex_usb_rank));
if (encrypt) {
asset_fex_usb_rank_encrypt((uint8_t*) &rank_enc,
sizeof(struct asset_fex_usb_rank));
}
if (!util_file_save(path, (void**) &rank_enc,
sizeof(struct asset_fex_usb_rank))) {
log_error("Storing fex rank data to %s failed", path);
return false;
}
return true;
}
struct asset_fex_usb_save* asset_fex_usb_save_load_from_file(const char* path,
bool encrypted)
{
struct asset_fex_usb_save* save;
size_t size;
if (!util_file_load(path, (void**) &save, &size, false)) {
log_error("Loading fex save file %s failed", path);
return NULL;
}
// if (size != sizeof(struct fex_profile_save)) {
// log_error("Invalid size of fex save file %s: %d != %d", path, size,
// sizeof(struct fex_profile_save));
// free(save);
// return NULL;
// }
if (encrypted) {
asset_fex_usb_save_decrypt((uint8_t*) save, size);
}
return save;
}
bool asset_fex_usb_save_save_to_file(const char* path,
const struct asset_fex_usb_save* save, bool encrypt)
{
struct asset_fex_usb_save save_enc;
memcpy(&save_enc, save, sizeof(struct asset_fex_usb_save));
if (encrypt) {
asset_fex_usb_save_encrypt((uint8_t*) &save_enc,
sizeof(struct asset_fex_usb_save));
}
if (!util_file_save(path, (const void*) &save_enc,
sizeof(struct asset_fex_usb_save))) {
log_error("Storing fex save data to %s failed", path);
return false;
}
return true;
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef ASSET_FEX_UTIL_H
#define ASSET_FEX_UTIL_H
#include <stdbool.h>
#include "usb-rank.h"
#include "usb-save.h"
// load from file and decrypt, returns decrypted struct
struct asset_fex_usb_rank* asset_fex_usb_rank_load_from_file(const char* path, bool encrypted);
// takes decrypted rank data, encrypts it and writes it to file
bool asset_fex_usb_rank_save_to_file(const char* path, const struct asset_fex_usb_rank* rank, bool encrypt);
// load from file and decrypt, returns decrypted struct
struct asset_fex_usb_save* asset_fex_usb_save_load_from_file(const char* path, bool encrypted);
// takes decrypted save data, encrypts it and writes it to file
bool asset_fex_usb_save_save_to_file(const char* path, const struct asset_fex_usb_save* save, bool encrypt);
#endif
+154
View File
@@ -0,0 +1,154 @@
/**
* Tool for FiestaEX profiles: decrypt, encrypt, print profile data
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asset/fex/lib/util.h"
#include "util/str.h"
int main(int argc, char** argv)
{
int ret;
char* rank_path;
char* save_path;
if (argc < 3) {
printf("Usage: %s [cmd: new, dec, enc, dump] "
"[path containing fiestaex_rank.bin fiestaex_save.bin]\n", argv[0]);
return -1;
}
ret = 0;
rank_path = util_str_merge(argv[2], "fiestaex_rank.bin");
save_path = util_str_merge(argv[2], "fiestaex_save.bin");
if (!strcmp(argv[1], "new")) {
struct asset_fex_usb_rank* rank = asset_fex_usb_rank_new();
struct asset_fex_usb_save* save = asset_fex_usb_save_new();
asset_fex_usb_rank_finalize(rank);
asset_fex_usb_save_finalize(save);
if (!asset_fex_usb_rank_save_to_file(rank_path, rank, true)) {
fprintf(stderr, "Creating rank file %s failed\n", rank_path);
ret = -2;
}
if (!asset_fex_usb_save_save_to_file(save_path, save, true)) {
fprintf(stderr, "Creating save file %s failed\n", save_path);
ret = -3;
}
free(rank);
free(save);
} else {
struct asset_fex_usb_rank* rank;
struct asset_fex_usb_save* save;
bool encrypted;
if (!strcmp(argv[1], "enc")) {
char* tmp;
encrypted = false;
tmp = util_str_merge(rank_path, ".dec");
free(rank_path);
rank_path = tmp;
tmp = util_str_merge(save_path, ".dec");
free(save_path);
save_path = tmp;
} else {
encrypted = true;
}
rank = asset_fex_usb_rank_load_from_file(rank_path, encrypted);
save = asset_fex_usb_save_load_from_file(save_path, encrypted);
if (!rank) {
fprintf(stderr, "Loading %s failed", rank_path);
ret = -4;
goto cleanup;
}
if (!save) {
fprintf(stderr, "Loading %s failed", save_path);
ret = -5;
free(rank);
goto cleanup;
}
if (!strcmp(argv[1], "dec")) {
char* rank_path_dec;
char* save_path_dec;
rank_path_dec = util_str_merge(rank_path, ".dec");
save_path_dec = util_str_merge(save_path, ".dec");
if (!asset_fex_usb_rank_save_to_file(rank_path_dec, rank, false)) {
fprintf(stderr, "Saving decrypted rank file to %s failed\n",
rank_path_dec);
ret = -6;
}
if (!asset_fex_usb_save_save_to_file(save_path_dec, save, false)) {
fprintf(stderr, "Saving decrypted save file to %s failed\n",
save_path_dec);
ret = -7;
}
free(rank_path_dec);
free(save_path_dec);
} else if (!strcmp(argv[1], "enc")) {
char* rank_path_enc;
char* save_path_enc;
rank_path_enc = util_str_merge(rank_path, ".enc");
save_path_enc = util_str_merge(save_path, ".enc");
if (!asset_fex_usb_rank_save_to_file(rank_path_enc, rank, true)) {
fprintf(stderr, "Saving encrypted rank file to %s failed\n",
rank_path_enc);
ret = -6;
}
if (!asset_fex_usb_save_save_to_file(save_path_enc, save, true)) {
fprintf(stderr, "Saving encrypted save file to %s failed\n",
save_path_enc);
ret = -7;
}
free(rank_path_enc);
free(save_path_enc);
} else if (!strcmp(argv[1], "dump")) {
char* rank_str;
char* save_str;
rank_str = asset_fex_usb_rank_to_string(rank);
save_str = asset_fex_usb_save_to_string(save);
printf("----------------- rank -----------------\n"
"%s----------------- save -----------------\n%s",
rank_str, save_str);
free(rank_str);
free(save_str);
} else {
fprintf(stderr, "Unknown command %s\n", argv[1]);
ret = -4;
}
free(rank);
free(save);
}
cleanup:
free(rank_path);
free(save_path);
return ret;
}
+38
View File
@@ -0,0 +1,38 @@
#ifndef ASSET_GAME_VERSION_H
#define ASSET_GAME_VERSION_H
/* Note: Version IDs must be sync with server code */
enum asset_game_version {
ASSET_GAME_VERSION_INVALID = 0,
ASSET_GAME_VERSION_FIRST = 1,
ASSET_GAME_VERSION_SECOND = 2,
ASSET_GAME_VERSION_OBG = 3,
ASSET_GAME_VERSION_OBG_SE = 4,
ASSET_GAME_VERSION_THE_COLLECTION = 5,
ASSET_GAME_VERSION_PERFECT_COLLECTION = 6,
ASSET_GAME_VERSION_EXTRA = 7,
ASSET_GAME_VERSION_PREMIERE_1 = 8,
ASSET_GAME_VERSION_PREX_1 = 9,
ASSET_GAME_VERSION_REBIRTH = 10,
ASSET_GAME_VERSION_PREMIERE_2 = 11,
ASSET_GAME_VERSION_PREX_2 = 12,
ASSET_GAME_VERSION_PREMIERE_3 = 13,
ASSET_GAME_VERSION_PREX_3 = 14,
ASSET_GAME_VERSION_EXCEED = 15,
ASSET_GAME_VERSION_EXCEED_2 = 16,
ASSET_GAME_VERSION_ZERO = 17,
ASSET_GAME_VERSION_NX = 18,
ASSET_GAME_VERSION_PRO = 19,
ASSET_GAME_VERSION_NX2 = 20,
ASSET_GAME_VERSION_NXA = 21,
ASSET_GAME_VERSION_FIESTA = 22,
ASSET_GAME_VERSION_PRO2 = 23,
ASSET_GAME_VERSION_FIESTA_EX = 24,
ASSET_GAME_VERSION_FIESTA_2 = 25,
ASSET_GAME_VERSION_INFINITY = 26,
ASSET_GAME_VERSION_PRIME = 27,
ASSET_GAME_VERSION_PRIME_2 = 28,
ASSET_GAME_VERSION_XX = 29
};
#endif
+68
View File
@@ -0,0 +1,68 @@
#include <string.h>
#include "asset/nx/lib/settings.h"
#include "util/adler32.h"
#include "util/mem.h"
struct asset_nx_settings* asset_nx_settings_new(void)
{
struct asset_nx_settings* settings;
settings = util_xmalloc(sizeof(struct asset_nx_settings));
memset(settings->ff_pad, 0xFF, sizeof(settings->ff_pad));
memcpy(settings->ident, "NX10", 4);
/* Updated using finalize */
settings->adler32 = 0;
settings->total_uptime = 0;
settings->language = ASSET_NX_SETTINGS_LANG_ENG;
memset(settings->song_play_count, 0, sizeof(uint32_t) * ASSET_NX_SETTINGS_MAX_SONGS);
settings->game_option_default_station = ASSET_NX_SETTINGS_STATION_TRAIN;
settings->game_mode = ASSET_NX_SETTINGS_GAME_MODE_NORMAL;
settings->game_level = ASSET_NX_SETTINGS_LEVEL_NORMAL;
settings->game_stage_break = ASSET_NX_SETTINGS_STAGE_BREAK_2ND;
settings->game_demo_sound = 1;
settings->game_show_help = 1;
settings->game_mercy_ticket = 0;
settings->game_score_per_ticket = 0;
settings->coin_limit = 0;
settings->coin1_setting = 1;
settings->coin2_setting = 1;
memset(settings->unknown_unlock_flags, 0, ASSET_NX_SETTINGS_NUM_UNLOCK_FLAGS);
settings->unkn2 = 0x2A;
settings->unkn3 = 0;
settings->bookkeeping_coin1_total = 0;
settings->bookkeeping_coin2_total = 0;
settings->bookkeeping_service_total = 0;
settings->bookkeeping_play_total = 0;
settings->bookkeeping_faep21_play_count = 0;
settings->bookkeeping_ticket_1 = 0;
settings->bookkeeping_ticket_2 = 0;
settings->region = 0xFF;
memset(settings->censor_table, 0, ASSET_NX_SETTINGS_MAX_SONGS);
return settings;
}
void asset_nx_settings_finalize(struct asset_nx_settings* settings)
{
/* Seems odd, but they just checksum the game settings part...*/
settings->adler32 = util_adler32_calc(1, (const uint8_t*) &settings->game_mode, 8);
}
void asset_nx_settings_unlock_all(struct asset_nx_settings* settings)
{
/* FAEP 2-1 must be played at least 100 times to unlock FAEP 2-2 */
settings->bookkeeping_faep21_play_count = 100;
/* To unlock all world tour missions and special zone songs, set this to 20k.
The first 10k unlock only the first half of the missions while another 10k unlocks the second half (wtf) */
settings->bookkeeping_play_total = 20000;
}
char* asset_nx_settings_to_string(const struct asset_nx_settings* settings)
{
// TODO
return "";
}
+101
View File
@@ -0,0 +1,101 @@
#ifndef ASSET_NX_SETTINGS_H
#define ASSET_NX_SETTINGS_H
#include <stdint.h>
#define ASSET_NX_SETTINGS_FILE_NAME "PIUNX.INI"
#define ASSET_NX_SETTINGS_MAX_SONGS 200
#define ASSET_NX_SETTINGS_NUM_UNLOCK_FLAGS 59
enum asset_nx_settings_language {
ASSET_NX_SETTINGS_LANG_KOR = 0,
ASSET_NX_SETTINGS_LANG_ENG = 1,
ASSET_NX_SETTINGS_LANG_SP = 2,
ASSET_NX_SETTINGS_LANG_CH = 3,
};
enum asset_nx_settings_station {
ASSET_NX_SETTINGS_STATION_TRAIN = 0,
ASSET_NX_SETTINGS_STATION_ARCADE = 1,
ASSET_NX_SETTINGS_STATION_WORLD = 2,
ASSET_NX_SETTINGS_STATION_SPECIAL = 3,
};
enum asset_nx_settings_game_mode {
ASSET_NX_SETTINGS_GAME_MODE_NORMAL = 0,
ASSET_NX_SETTINGS_GAME_MODE_EVENT = 1,
};
enum asset_nx_settings_level {
ASSET_NX_SETTINGS_LEVEL_EASY = 0,
ASSET_NX_SETTINGS_LEVEL_NORMAL = 1,
ASSET_NX_SETTINGS_LEVEL_HARD = 2,
};
enum asset_nx_settings_stage_break {
ASSET_NX_SETTINGS_STAGE_BREAK_OFF = 0,
ASSET_NX_SETTINGS_STAGE_BREAK_1ST = 1,
ASSET_NX_SETTINGS_STAGE_BREAK_2ND = 2,
ASSET_NX_SETTINGS_STAGE_BREAK_3RD = 3,
ASSET_NX_SETTINGS_STAGE_BREAK_4TH = 4,
};
struct asset_nx_settings {
/* Padding to fill up to 4k EEPROM legacy size */
uint8_t ff_pad[0xB9D];
/* NX10 */
char ident[4];
uint32_t adler32;
uint32_t total_uptime;
uint8_t language;
uint32_t song_play_count[ASSET_NX_SETTINGS_MAX_SONGS];
uint32_t game_option_default_station;
uint8_t game_mode;
uint8_t game_level;
uint8_t game_stage_break;
/* 0 off, 1 on */
uint8_t game_demo_sound;
/* 0 off, 1 on */
uint8_t game_show_help;
/* 0 off, 1 on */
uint8_t game_mercy_ticket;
/* 0 off, 1 - 6 for score */
uint8_t game_score_per_ticket;
/* 0 none */
uint8_t coin_limit; // 0 none
/* 0 freeplay */
uint8_t coin1_setting;
/* 0 freeplay */
uint8_t coin2_setting;
/* Unlock flags for world tour missions? setting them to 1 doesn't unlock anything.
64 missions total, 55 unlock flags, 0 = locked, 1 = unlocked */
uint8_t unknown_unlock_flags[ASSET_NX_SETTINGS_NUM_UNLOCK_FLAGS];
/* Default value 0x2A */
uint32_t unkn2;
uint32_t unkn3;
uint32_t bookkeeping_coin1_total;
uint32_t bookkeeping_coin2_total;
uint32_t bookkeeping_service_total;
uint32_t bookkeeping_play_total;
/* Separate counter for FAEP 2-1 play counts. Once this hits 100, FAEP 2-2 is unlocked in arcade station */
uint32_t bookkeeping_faep21_play_count;
uint32_t bookkeeping_ticket_1;
uint32_t bookkeeping_ticket_2;
/* FF -> not used, censoring globally disabled. unused feature though */
uint8_t region;
/* 0 = censoring of song off */
uint8_t censor_table[ASSET_NX_SETTINGS_MAX_SONGS];
} __attribute__((__packed__));
struct asset_nx_settings* asset_nx_settings_new(void);
// update checksum
void asset_nx_settings_finalize(struct asset_nx_settings* settings);
// unlocks FAEP 2-2 by setting the play count to 100
// and sets all missions cleared to unlock special stage songs and other missions
void asset_nx_settings_unlock_all(struct asset_nx_settings* settings);
char* asset_nx_settings_to_string(const struct asset_nx_settings* settings);
#endif
+35
View File
@@ -0,0 +1,35 @@
#include <string.h>
#include "asset/nx/lib/util.h"
#include "util/fs.h"
#include "util/log.h"
struct asset_nx_settings* asset_nx_util_load_settings_from_file(const char* path)
{
struct asset_nx_settings* settings;
size_t size;
if (!util_file_load(path, (void**) &settings, &size, false)) {
log_error("Loading nx setting file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_nx_settings)) {
log_error("Invalid size of nx setting file %s: %d != %d", path, size, sizeof(struct asset_nx_settings));
free(settings);
return NULL;
}
return settings;
}
bool asset_nx_util_save_settings_to_file(const char* path, const struct asset_nx_settings* settings)
{
if (!util_file_save(path, (void**) settings, sizeof(struct asset_nx_settings))) {
log_error("Storing nx settings data to %s failed", path);
return false;
}
return true;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef ASSET_NX_UTIL_H
#define ASSET_NX_UTIL_H
#include <stdbool.h>
#include "asset/nx/lib/settings.h"
struct asset_nx_settings* asset_nx_util_load_settings_from_file(const char* path);
bool asset_nx_util_save_settings_to_file(const char* path, const struct asset_nx_settings* settings);
#endif
+88
View File
@@ -0,0 +1,88 @@
/**
* Tool for NX settings files
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asset/nx/lib/settings.h"
#include "asset/nx/lib/util.h"
static int main_settings(char* cmd, char* file);
int main(int argc, char** argv)
{
int ret;
char* cmd;
char* type;
char* file;
if (argc < 4) {
printf("Usage: %s [cmd: new, dump, unlock] [type: ini, mrank, rank] [file]\n", argv[0]);
return -1;
}
cmd = argv[1];
type = argv[2];
file = argv[3];
if (!strcmp(type, "ini")) {
ret = main_settings(cmd, file);
} else {
fprintf(stderr, "Unknown file type %s\n", type);
ret = -1;
}
return ret;
}
static int main_settings(char* cmd, char* file)
{
int ret;
ret = 0;
if (!strcmp(cmd, "new")) {
struct asset_nx_settings* settings = asset_nx_settings_new();
asset_nx_settings_finalize(settings);
if (!asset_nx_util_save_settings_to_file(file, settings)) {
fprintf(stderr, "Creating nx settings file %s failed\n", file);
ret = -1;
}
free(settings);
} else {
struct asset_nx_settings* settings = asset_nx_util_load_settings_from_file(file);
if (!settings) {
fprintf(stderr, "Loading %s failed", file);
ret = -1;
}
if (!strcmp(cmd, "dump")) {
char* str = asset_nx_settings_to_string(settings);
printf("----------------- settings -----------------\n%s\n", str);
free(str);
} else if (!strcmp(cmd, "unlock")) {
asset_nx_settings_unlock_all(settings);
asset_nx_settings_finalize(settings);
if (!asset_nx_util_save_settings_to_file(file, settings)) {
fprintf(stderr, "Saving nx settings file %s failed\n", file);
ret = -1;
}
} else {
fprintf(stderr, "Unknown command %s\n", cmd);
ret = -1;
}
free(settings);
}
return ret;
}
+93
View File
@@ -0,0 +1,93 @@
#include <string.h>
#include "util/adler32.h"
#include "util/mem.h"
#include "util/str.h"
#include "usb-rank.h"
struct asset_nx2_usb_rank* asset_nx2_usb_rank_new(void)
{
struct asset_nx2_usb_rank* rank;
rank = (struct asset_nx2_usb_rank*)
util_xmalloc(sizeof(struct asset_nx2_usb_rank));
memset(rank, 0, sizeof(struct asset_nx2_usb_rank));
return rank;
}
void asset_nx2_usb_rank_finalize(struct asset_nx2_usb_rank* rank)
{
rank->adler32 = util_adler32_calc(1, ((const uint8_t*) rank) + 4,
sizeof(struct asset_nx2_usb_rank) - 4);
}
char* asset_nx2_usb_rank_to_string(const struct asset_nx2_usb_rank* rank)
{
char* buffer;
char* buffer2;
char* merged;
buffer = (char*) util_xmalloc(128);
memset(buffer, 0, 128);
util_str_format(buffer, 128,
"adler32: 0x%X\n"
"num_rankings: %d\n"
"ranking entries:\n",
rank->adler32,
rank->num_rankings);
for (uint32_t i = 0; i < ASSET_NX2_USB_RANK_MAX_RANK_ENTRIES; i++) {
for (uint32_t j = 0; j < ASSET_NX2_USB_RANK_MAX_STAGES; j++) {
buffer2 = (char*) util_xmalloc(512);
memset(buffer2, 0, 512);
util_str_format(buffer2, 512,
"%d/%d:\n"
"game_mode: %d\n"
"play_order: %d\n"
"play_score: %d\n"
"grade: %d\n"
"mileage: %d\n"
"play_time: %f\n"
"kcal: %f\n"
"v02: %f\n",
i, j,
rank->entries[i][j].game_mode,
rank->entries[i][j].play_order,
rank->entries[i][j].play_score,
rank->entries[i][j].grade,
rank->entries[i][j].mileage,
rank->entries[i][j].play_time,
rank->entries[i][j].kcal,
rank->entries[i][j].v02);
merged = util_str_merge(buffer, buffer2);
free(buffer);
free(buffer2);
buffer = merged;
}
}
return buffer;
}
void asset_nx2_usb_rank_decrypt(uint8_t* buf, size_t len)
{
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_nx2_usb_rank_encrypt(uint8_t* buf, size_t len)
{
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+42
View File
@@ -0,0 +1,42 @@
#ifndef ASSET_NX2_USB_RANK_H
#define ASSET_NX2_USB_RANK_H
#include <stdint.h>
#include <stdlib.h>
#define ASSET_NX2_USB_RANK_SIZE 12296
enum asset_nx2_usb_rank_sizes {
ASSET_NX2_USB_RANK_MAX_STAGES = 3,
ASSET_NX2_USB_RANK_MAX_RANK_ENTRIES = 128,
};
struct asset_nx2_usb_rank_entry {
int32_t game_mode;
int32_t play_order;
int32_t play_score;
int32_t grade;
int32_t mileage;
float play_time;
float kcal;
float v02;
} __attribute__((__packed__));
struct asset_nx2_usb_rank {
uint32_t adler32;
uint32_t num_rankings;
struct asset_nx2_usb_rank_entry entries[ASSET_NX2_USB_RANK_MAX_RANK_ENTRIES][ASSET_NX2_USB_RANK_MAX_STAGES];
} __attribute__((__packed__));
struct asset_nx2_usb_rank* asset_nx2_usb_rank_new(void);
// update checksum and prepare profile to get encrypted
void asset_nx2_usb_rank_finalize(struct asset_nx2_usb_rank* rank);
char* asset_nx2_usb_rank_to_string(const struct asset_nx2_usb_rank* rank);
void asset_nx2_usb_rank_decrypt(uint8_t* buf, size_t len);
void asset_nx2_usb_rank_encrypt(uint8_t* buf, size_t len);
#endif
+370
View File
@@ -0,0 +1,370 @@
#include <stdio.h>
#include <string.h>
#include "util/adler32.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/str.h"
#include "usb-save.h"
static char* asset_nx2_usb_save_time_to_string(
const struct asset_nx2_usb_save_time* time)
{
char* buffer;
buffer = (char*) util_xmalloc(128);
memset(buffer, 0, 128);
util_str_format(buffer, 128,
"year: %d\n"
"month: %d\n"
"day: %d\n"
"hour: %d\n"
"min: %d\n"
"ms: %d\n",
time->year,
time->month,
time->day,
time->hour,
time->min,
time->ms);
return buffer;
}
static char* asset_nx2_usb_save_review_to_string(
const struct asset_nx2_usb_save_review* review)
{
char* buffer;
char* land;
char* mission;
buffer = (char*) util_xmalloc(1024);
memset(buffer, 0, 1024);
land = util_str_buffer((const uint8_t*) review->worldmax_current_land,
ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX);
mission = util_str_buffer((const uint8_t*) review->worldmax_current_mission,
ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX);
util_str_format(buffer, 1024,
">>> review:\n"
"player_id: %s\n"
"mileage: %d\n"
"reward_count: %d\n"
"worldmax_count: %d\n"
"play_count: %d\n"
"worldmax_current_land: %s\n"
"worldmax_current_mission: %s\n"
"kcal: %d\n"
"v02: %d\n",
review->player_id,
review->mileage,
review->reward_count,
review->worldmax_count,
review->play_count,
land,
mission,
review->kcal,
review->v02);
free(land);
free(mission);
return buffer;
}
static char* asset_nx2_usb_save_stats_to_string(
const struct asset_nx2_usb_save_stats* stats)
{
char* merged;
char* buffer;
char* timestamp;
char* tmp;
const size_t buf_size = 512;
merged = (char*) util_xmalloc(buf_size);
memset(merged, 0, buf_size);
buffer = (char*) util_xmalloc(buf_size);
memset(buffer, 0, buf_size);
timestamp = asset_nx2_usb_save_time_to_string(&stats->timestamp);
util_str_format(merged, buf_size,
">>> stats:\n"
"adler32: 0x%X\n"
"usb_serial: %s\n"
"timestamp: %s"
"avatar_id: %d\n"
"rank: %d\n"
"country_id: %d\n"
"player_id: %s\n"
"mileage: %d\n"
"play_count: %d\n"
"kcal: %d\n"
"v02: %d\n"
"worldmax_map_position: 0x%X\n"
"reward_count: %d\n"
"worldmax_count: %d\n",
stats->adler32,
stats->usb_serial,
timestamp,
stats->avatar_id,
stats->rank,
stats->country_id,
stats->player_id,
stats->mileage,
stats->play_count,
stats->kcal,
stats->v02,
stats->worldmax_map_position,
stats->reward_count,
stats->worldmax_count);
free(timestamp);
tmp = util_str_merge(merged, "song_unlocks:\n");
free(merged);
merged = tmp;
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_SONG_MAX; i++) {
util_str_format(buffer, buf_size,
"song slot: %d\n"
"mode: 0x%X\n"
"unused: 0x%X\n"
"song: %d\n"
"reveal: %d\n",
i,
stats->song_unlocks[i].mode,
stats->song_unlocks[i].unused,
stats->song_unlocks[i].song,
stats->song_unlocks[i].reveal);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"mission slot: %d\n"
"clear_flags_directions: 0x%X\n"
"clear_flag_mission: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_mission_unlocks[i].clear_flags_directions,
stats->worldmax_mission_unlocks[i].clear_flag_mission,
stats->worldmax_mission_unlocks[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_high_score %d: %d\n",
i,
stats->worldmax_high_score[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_challenge %d: %d\n",
i,
stats->worldmax_challenge[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_BARRICADE_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_barricade %d:\n"
"flag_open: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_barricade[i].flag_open,
stats->worldmax_barricade[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_EVENT_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_event %d:\n"
"flag_cleared: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_event[i].flag_cleared,
stats->worldmax_event[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_WARP_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_warp %d:\n"
"direction_clear: 0x%X\n"
"clear: %d\n",
"unused: 0x%X\n",
i,
stats->worldmax_warp[i].direction_clear,
stats->worldmax_warp[i].clear,
stats->worldmax_warp[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_current_land %d: %d\n",
i,
stats->worldmax_current_land[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_current_mission %d: %d\n",
i,
stats->worldmax_current_mission[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NX2_USB_SAVE_SONG_MAX; i++) {
for (uint32_t j = 0; j < ASSET_NX2_USB_SAVE_NUM_MODES; j++) {
util_str_format(buffer, buf_size,
"song_scores %d/%d:\n"
"score: %d\n"
"player_id: %s\n",
i,
j,
stats->song_scores[i][j].score,
stats->song_scores[i][j].player_id);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
}
free(buffer);
return merged;
}
struct asset_nx2_usb_save* asset_nx2_usb_save_new(void)
{
struct asset_nx2_usb_save* save;
char name[10];
save = (struct asset_nx2_usb_save*) util_xmalloc(
sizeof(struct asset_nx2_usb_save));
memset(save, 0, sizeof(struct asset_nx2_usb_save));
sprintf(name, "NX%06d", rand() % 1000000);
strcpy(save->review.player_id, name);
strcpy(save->stats.player_id, name);
/* default world map position */
save->stats.worldmax_map_position = 0xEF001;
/* nx2 release date as default timestamp */
save->stats.timestamp.year = 2008;
save->stats.timestamp.month = 1;
save->stats.timestamp.day = 21;
save->stats.timestamp.hour = 0;
save->stats.timestamp.min = 0;
save->stats.timestamp.ms = 0;
return save;
}
void asset_nx2_usb_save_finalize(struct asset_nx2_usb_save* save)
{
/* checksum update */
save->stats.adler32 = util_adler32_calc(1,
((const uint8_t*) &save->stats) + 4,
sizeof(struct asset_nx2_usb_save_stats) - 4);
}
char* asset_nx2_usb_save_to_string(const struct asset_nx2_usb_save* save)
{
char* review;
char* stats;
char* merged;
review = asset_nx2_usb_save_review_to_string(&save->review);
stats = asset_nx2_usb_save_stats_to_string(&save->stats);
merged = util_str_merge(review, stats);
free(review);
free(stats);
return merged;
}
void asset_nx2_usb_save_decrypt(uint8_t* buf, size_t len)
{
/* skip non encrypted review area */
buf += sizeof(struct asset_nx2_usb_save_review);
len -= sizeof(struct asset_nx2_usb_save_review);
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_nx2_usb_save_encrypt(uint8_t* buf, size_t len)
{
/* skip non encrypted review area */
buf += sizeof(struct asset_nx2_usb_save_review);
len -= sizeof(struct asset_nx2_usb_save_review);
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+122
View File
@@ -0,0 +1,122 @@
#ifndef ASSET_NX2_USB_SAVE_H
#define ASSET_NX2_USB_SAVE_H
#include <stdint.h>
#include <stdlib.h>
#define ASSET_NX2_USB_SAVE_SIZE 30780
enum asset_nx2_usb_save_limits {
ASSET_NX2_USB_SAVE_SERIAL_ID_MAX = 64,
ASSET_NX2_USB_SAVE_PLAYER_ID_MAX = 12,
ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX = 128,
ASSET_NX2_USB_SAVE_SONG_MAX = 256,
ASSET_NX2_USB_SAVE_MISSION_MAX = 1024,
ASSET_NX2_USB_SAVE_BARRICADE_MAX = 16,
ASSET_NX2_USB_SAVE_EVENT_MAX = 128,
ASSET_NX2_USB_SAVE_WARP_MAX = 8,
ASSET_NX2_USB_SAVE_NUM_MODES = 5,
};
struct asset_nx2_usb_save_time {
int16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint16_t ms;
} __attribute__((__packed__));
struct asset_nx2_usb_save_review {
char player_id[ASSET_NX2_USB_SAVE_PLAYER_ID_MAX];
int32_t mileage;
int32_t reward_count;
int32_t worldmax_count;
int32_t play_count;
char worldmax_current_land[ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX];
char worldmax_current_mission[ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX];
int32_t kcal;
int32_t v02;
} __attribute__((__packed__));
struct asset_nx2_usb_save_stats {
uint32_t adler32;
char usb_serial[ASSET_NX2_USB_SAVE_SERIAL_ID_MAX];
struct asset_nx2_usb_save_time timestamp;
int32_t avatar_id;
int32_t rank;
int32_t country_id;
char player_id[ASSET_NX2_USB_SAVE_PLAYER_ID_MAX];
int32_t mileage;
int32_t play_count;
int32_t kcal;
int32_t v02;
int32_t worldmax_map_position;
int32_t reward_count;
int32_t worldmax_count;
struct {
/* bits 0-4 for modes/stepcharts */
uint8_t mode : 5;
/* unused bit */
uint8_t unused : 1;
/* bit 6 song itself */
uint8_t song : 1;
/* bit 7 reveal flag */
uint8_t reveal : 1;
} song_unlocks[ASSET_NX2_USB_SAVE_SONG_MAX] __attribute__((__packed__));
struct {
uint8_t clear_flags_directions : 4;
uint8_t clear_flag_mission : 1;
uint8_t unused : 3;
} worldmax_mission_unlocks[ASSET_NX2_USB_SAVE_MISSION_MAX]
__attribute__((__packed__));
int32_t worldmax_high_score[ASSET_NX2_USB_SAVE_MISSION_MAX];
int32_t worldmax_challenge[ASSET_NX2_USB_SAVE_MISSION_MAX];
struct {
uint8_t flag_open : 1;
uint8_t unused : 7;
} worldmax_barricade[ASSET_NX2_USB_SAVE_BARRICADE_MAX]
__attribute__((__packed__));
struct {
uint8_t flag_cleared : 1;
uint8_t unused : 7;
} worldmax_event[ASSET_NX2_USB_SAVE_EVENT_MAX] __attribute__((__packed__));
struct {
uint8_t direction_clear : 4;
uint8_t clear : 1;
uint8_t unused : 3;
} worldmax_warp[ASSET_NX2_USB_SAVE_WARP_MAX] __attribute__((__packed__));
char worldmax_current_land[ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX];
char worldmax_current_mission[ASSET_NX2_USB_SAVE_WORLDMAX_LOC_MAX];
struct {
int32_t score;
char player_id[ASSET_NX2_USB_SAVE_PLAYER_ID_MAX];
} song_scores[ASSET_NX2_USB_SAVE_SONG_MAX][ASSET_NX2_USB_SAVE_NUM_MODES]
__attribute__((__packed__));
} __attribute__((__packed__));
struct asset_nx2_usb_save {
struct asset_nx2_usb_save_review review;
struct asset_nx2_usb_save_stats stats;
} __attribute__((__packed__));
struct asset_nx2_usb_save* asset_nx2_usb_save_new(void);
// update checksum and prepare profile to get encrypted
void asset_nx2_usb_save_finalize(struct asset_nx2_usb_save* save);
char* asset_nx2_usb_save_to_string(const struct asset_nx2_usb_save* save);
void asset_nx2_usb_save_decrypt(uint8_t* buf, size_t len);
void asset_nx2_usb_save_encrypt(uint8_t* buf, size_t len);
#endif
+100
View File
@@ -0,0 +1,100 @@
#define LOG_MODULE "asset-nx2-util"
#include <string.h>
#include "util/fs.h"
#include "util/log.h"
#include "util.h"
struct asset_nx2_usb_rank* asset_nx2_usb_rank_load_from_file(const char* path,
bool encrypted)
{
struct asset_nx2_usb_rank* rank;
size_t size;
if (!util_file_load(path, (void**) &rank, &size, false)) {
log_error("Loading nx2 rank file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_nx2_usb_rank)) {
log_error("Invalid size of nx2 rank file %s: %d != %d", path, size,
sizeof(struct asset_nx2_usb_rank));
free(rank);
return NULL;
}
if (encrypted) {
asset_nx2_usb_rank_decrypt((uint8_t*) rank, size);
}
return rank;
}
bool asset_nx2_usb_rank_save_to_file(const char* path,
const struct asset_nx2_usb_rank* rank, bool encrypt)
{
struct asset_nx2_usb_rank rank_enc;
memcpy(&rank_enc, rank, sizeof(struct asset_nx2_usb_rank));
if (encrypt) {
asset_nx2_usb_rank_encrypt((uint8_t*) &rank_enc,
sizeof(struct asset_nx2_usb_rank));
}
if (!util_file_save(path, (void**) &rank_enc,
sizeof(struct asset_nx2_usb_rank))) {
log_error("Storing nx2 rank data to %s failed", path);
return false;
}
return true;
}
struct asset_nx2_usb_save* asset_nx2_usb_save_load_from_file(const char* path,
bool encrypted)
{
struct asset_nx2_usb_save* save;
size_t size;
if (!util_file_load(path, (void**) &save, &size, false)) {
log_error("Loading nx2 save file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_nx2_usb_save)) {
log_error("Invalid size of nx2 save file %s: %d != %d", path, size,
sizeof(struct asset_nx2_usb_save));
free(save);
return NULL;
}
if (encrypted) {
asset_nx2_usb_save_decrypt((uint8_t*) save, size);
}
return save;
}
bool asset_nx2_usb_save_save_to_file(const char* path,
const struct asset_nx2_usb_save* save, bool encrypt)
{
struct asset_nx2_usb_save save_enc;
memcpy(&save_enc, save, sizeof(struct asset_nx2_usb_save));
if (encrypt) {
asset_nx2_usb_save_encrypt((uint8_t*) &save_enc,
sizeof(struct asset_nx2_usb_save));
}
if (!util_file_save(path, (const void*) &save_enc,
sizeof(struct asset_nx2_usb_save))) {
log_error("Storing nx2 save data to %s failed", path);
return false;
}
return true;
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef ASSET_NX2_UTIL_H
#define ASSET_NX2_UTIL_H
#include <stdbool.h>
#include "usb-rank.h"
#include "usb-save.h"
// load from file and decrypt, returns decrypted struct
struct asset_nx2_usb_rank* asset_nx2_usb_rank_load_from_file(const char* path, bool encrypted);
// takes decrypted rank data, encrypts it and writes it to file
bool asset_nx2_usb_rank_save_to_file(const char* path, const struct asset_nx2_usb_rank* rank, bool encrypt);
// load from file and decrypt, returns decrypted struct
struct asset_nx2_usb_save* asset_nx2_usb_save_load_from_file(const char* path, bool encrypted);
// takes decrypted save data, encrypts it and writes it to file
bool asset_nx2_usb_save_save_to_file(const char* path, const struct asset_nx2_usb_save* save, bool encrypt);
#endif
+154
View File
@@ -0,0 +1,154 @@
/**
* Tool for NX2 profiles: decrypt, encrypt, print profile data
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asset/nx2/lib/util.h"
#include "util/str.h"
int main(int argc, char** argv)
{
int ret;
char* rank_path;
char* save_path;
if (argc < 3) {
printf("Usage: %s [cmd: new, dec, enc, dump] "
"[path containing nx2rank.bin nx2save.bin]\n", argv[0]);
return -1;
}
ret = 0;
rank_path = util_str_merge(argv[2], "nx2rank.bin");
save_path = util_str_merge(argv[2], "nx2save.bin");
if (!strcmp(argv[1], "new")) {
struct asset_nx2_usb_rank* rank = asset_nx2_usb_rank_new();
struct asset_nx2_usb_save* save = asset_nx2_usb_save_new();
asset_nx2_usb_rank_finalize(rank);
asset_nx2_usb_save_finalize(save);
if (!asset_nx2_usb_rank_save_to_file(rank_path, rank, true)) {
fprintf(stderr, "Creating rank file %s failed\n", rank_path);
ret = -2;
}
if (!asset_nx2_usb_save_save_to_file(save_path, save, true)) {
fprintf(stderr, "Creating save file %s failed\n", save_path);
ret = -3;
}
free(rank);
free(save);
} else {
struct asset_nx2_usb_rank* rank;
struct asset_nx2_usb_save* save;
bool encrypted;
if (!strcmp(argv[1], "enc")) {
char* tmp;
encrypted = false;
tmp = util_str_merge(rank_path, ".dec");
free(rank_path);
rank_path = tmp;
tmp = util_str_merge(save_path, ".dec");
free(save_path);
save_path = tmp;
} else {
encrypted = true;
}
rank = asset_nx2_usb_rank_load_from_file(rank_path, encrypted);
save = asset_nx2_usb_save_load_from_file(save_path, encrypted);
if (!rank) {
fprintf(stderr, "Loading %s failed", rank_path);
ret = -4;
goto cleanup;
}
if (!save) {
fprintf(stderr, "Loading %s failed", save_path);
ret = -5;
free(rank);
goto cleanup;
}
if (!strcmp(argv[1], "dec")) {
char* rank_path_dec;
char* save_path_dec;
rank_path_dec = util_str_merge(rank_path, ".dec");
save_path_dec = util_str_merge(save_path, ".dec");
if (!asset_nx2_usb_rank_save_to_file(rank_path_dec, rank, false)) {
fprintf(stderr, "Saving decrypted rank file to %s failed\n",
rank_path_dec);
ret = -6;
}
if (!asset_nx2_usb_save_save_to_file(save_path_dec, save, false)) {
fprintf(stderr, "Saving decrypted save file to %s failed\n",
save_path_dec);
ret = -7;
}
free(rank_path_dec);
free(save_path_dec);
} else if (!strcmp(argv[1], "enc")) {
char* rank_path_enc;
char* save_path_enc;
rank_path_enc = util_str_merge(rank_path, ".enc");
save_path_enc = util_str_merge(save_path, ".enc");
if (!asset_nx2_usb_rank_save_to_file(rank_path_enc, rank, true)) {
fprintf(stderr, "Saving encrypted rank file to %s failed\n",
rank_path_enc);
ret = -6;
}
if (!asset_nx2_usb_save_save_to_file(save_path_enc, save, true)) {
fprintf(stderr, "Saving encrypted save file to %s failed\n",
save_path_enc);
ret = -7;
}
free(rank_path_enc);
free(save_path_enc);
} else if (!strcmp(argv[1], "dump")) {
char* rank_str;
char* save_str;
rank_str = asset_nx2_usb_rank_to_string(rank);
save_str = asset_nx2_usb_save_to_string(save);
printf("----------------- rank -----------------\n"
"%s----------------- save -----------------\n%s",
rank_str, save_str);
free(rank_str);
free(save_str);
} else {
fprintf(stderr, "Unknown command %s\n", argv[1]);
ret = -4;
}
free(rank);
free(save);
}
cleanup:
free(rank_path);
free(save_path);
return ret;
}
+103
View File
@@ -0,0 +1,103 @@
#include <stdio.h>
#include <string.h>
#include "util/adler32.h"
#include "util/mem.h"
#include "util/str.h"
#include "usb-rank.h"
struct asset_nxa_usb_rank* asset_nxa_usb_rank_new(void)
{
struct asset_nxa_usb_rank* rank;
rank = (struct asset_nxa_usb_rank*)
util_xmalloc(sizeof(struct asset_nxa_usb_rank));
memset(rank, 0, sizeof(struct asset_nxa_usb_rank));
return rank;
}
void asset_nxa_usb_rank_finalize(struct asset_nxa_usb_rank* rank)
{
rank->adler32 = util_adler32_calc(1, ((const uint8_t*) rank) + 4,
sizeof(struct asset_nxa_usb_rank) - 4);
}
char* asset_nxa_usb_rank_to_string(const struct asset_nxa_usb_rank* rank)
{
char* buffer;
char* buffer2;
char* merged;
char* usb_ser;
buffer = (char*) util_xmalloc(512);
memset(buffer, 0, 512);
usb_ser = util_str_buffer((const uint8_t*) rank->usb_serial,
ASSET_NXA_USB_RANK_USB_SERIAL_LEN);
util_str_format(buffer, 512,
"adler32: 0x%X\n"
// "usb_serial: %s\n",
"num_rankings: %d\n"
"ranking entries:\n",
rank->adler32,
// usb_ser,
rank->num_rankings);
for (uint32_t i = 0; i < ASSET_NXA_USB_RANK_MAX_RANK_ENTRIES; i++) {
for (uint32_t j = 0; j < ASSET_NXA_USB_RANK_MAX_STAGES; j++) {
buffer2 = (char*) util_xmalloc(512);
memset(buffer2, 0, 512);
util_str_format(buffer2, 512,
"%d/%d:\n"
"game_mode: %d\n"
"play_order: %d\n"
"play_score: %d\n"
"grade: %d\n"
"mileage: %d\n"
"play_time: %f\n"
"kcal: %f\n"
"v02: %f\n",
i, j,
rank->entries[i][j].game_mode,
rank->entries[i][j].play_order,
rank->entries[i][j].play_score,
rank->entries[i][j].grade,
rank->entries[i][j].mileage,
rank->entries[i][j].play_time,
rank->entries[i][j].kcal,
rank->entries[i][j].v02);
merged = util_str_merge(buffer, buffer2);
free(buffer);
free(buffer2);
buffer = merged;
}
}
free(usb_ser);
return buffer;
}
void asset_nxa_usb_rank_decrypt(uint8_t* buf, size_t len)
{
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_nxa_usb_rank_encrypt(uint8_t* buf, size_t len)
{
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef ASSET_NXA_USB_RANK_H
#define ASSET_NXA_USB_RANK_H
#include <stdint.h>
#include <stdlib.h>
enum asset_nxa_usb_rank_sizes {
ASSET_NXA_USB_RANK_MAX_STAGES = 3,
ASSET_NXA_USB_RANK_MAX_RANK_ENTRIES = 256,
ASSET_NXA_USB_RANK_USB_SERIAL_LEN = 64,
};
struct asset_nxa_usb_rank_entry {
int32_t game_mode;
int32_t play_order;
int32_t play_score;
int32_t grade;
int32_t mileage;
float play_time;
float kcal;
float v02;
} __attribute__((__packed__));
struct asset_nxa_usb_rank {
uint32_t adler32;
char usb_serial[ASSET_NXA_USB_RANK_USB_SERIAL_LEN];
uint32_t num_rankings;
struct asset_nxa_usb_rank_entry
entries[ASSET_NXA_USB_RANK_MAX_RANK_ENTRIES][ASSET_NXA_USB_RANK_MAX_STAGES];
} __attribute__((__packed__));
struct asset_nxa_usb_rank* asset_nxa_usb_rank_new(void);
// update checksum and prepare profile to get encrypted
void asset_nxa_usb_rank_finalize(struct asset_nxa_usb_rank* rank);
char* asset_nxa_usb_rank_to_string(const struct asset_nxa_usb_rank* rank);
void asset_nxa_usb_rank_decrypt(uint8_t* buf, size_t len);
void asset_nxa_usb_rank_encrypt(uint8_t* buf, size_t len);
#endif
+457
View File
@@ -0,0 +1,457 @@
#include <stdio.h>
#include <string.h>
#include "util/adler32.h"
#include "util/mem.h"
#include "util/str.h"
#include "usb-save.h"
static char* asset_nxa_usb_save_time_to_string(
const struct asset_nxa_usb_save_time* time)
{
char* buffer;
buffer = (char*) util_xmalloc(128);
memset(buffer, 0, 128);
util_str_format(buffer, 128,
"year: %d\n"
"month: %d\n"
"day: %d\n"
"hour: %d\n"
"min: %d\n"
"ms: %d\n",
time->year,
time->month,
time->day,
time->hour,
time->min,
time->ms);
return buffer;
}
static char* asset_nxa_usb_save_review_to_string(
const struct asset_nxa_usb_save_review* review)
{
char* buffer;
char* land;
char* mission;
char* timestamp;
buffer = (char*) util_xmalloc(1024);
memset(buffer, 0, 1024);
land = util_str_buffer((const uint8_t*) review->worldmax_current_land,
ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX);
mission = util_str_buffer((const uint8_t*) review->worldmax_current_mission,
ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX);
timestamp = asset_nxa_usb_save_time_to_string(&review->timestamp);
util_str_format(buffer, 1024,
">>> review:\n"
"player_id: %s\n"
"mileage: %d\n"
"reward_count: %d\n"
"worldmax_count: %d\n"
"play_count: %d\n"
"worldmax_current_land: %s\n"
"worldmax_current_mission: %s\n"
"kcal: %d\n"
"v02: %d\n",
"timestamp: \n%s",
"running_step: %d\n",
"play_time_minutes: %f\n",
"total_completion_percentage: %f\n",
"arcade_percentage: %f\n",
"brain_shower_percentage: %f\n",
"special_percentage: %f\n",
review->player_id,
review->mileage,
review->reward_count,
review->worldmax_count,
review->play_count,
land,
mission,
review->kcal,
review->v02,
timestamp,
review->running_step,
review->play_time_minutes,
review->total_completion_percentage,
review->arcade_percentage,
review->brain_shower_percentage,
review->special_percentage);
free(land);
free(mission);
free(timestamp);
return buffer;
}
static char* asset_nxa_usb_save_stats_to_string(
const struct asset_nxa_usb_save_stats* stats)
{
char* merged;
char* buffer;
char* timestamp;
char* tmp;
char* tmp2;
const size_t buf_size = 16384;
merged = (char*) util_xmalloc(buf_size);
memset(merged, 0, buf_size);
buffer = (char*) util_xmalloc(buf_size);
memset(buffer, 0, buf_size);
timestamp = asset_nxa_usb_save_time_to_string(&stats->timestamp);
util_str_format(merged, buf_size,
">>> stats:\n"
"adler32: 0x%X\n"
"usb_serial: %s\n"
"timestamp: %s"
"avatar_id: %d\n"
"rank: %d\n"
"country_id: %d\n"
"player_id: %s\n"
"mileage: %d\n"
"play_count: %d\n"
"kcal: %d\n"
"v02: %d\n"
"worldmax_map_position: 0x%X\n"
"reward_count: %d\n"
"worldmax_count: %d\n",
stats->adler32,
stats->usb_serial,
timestamp,
stats->avatar_id,
stats->rank,
stats->country_id,
stats->player_id,
stats->mileage,
stats->play_count,
stats->kcal,
stats->v02,
stats->worldmax_map_position,
stats->reward_count,
stats->worldmax_count);
free(timestamp);
tmp = util_str_merge(merged, "song_unlocks:\n");
free(merged);
merged = tmp;
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_SONG_MAX; i++) {
util_str_format(buffer, buf_size,
"song slot: %d\n"
"mode: 0x%X\n"
"unused: 0x%X\n"
"song: %d\n"
"reveal: %d\n",
i,
stats->song_unlocks[i].mode,
stats->song_unlocks[i].unused,
stats->song_unlocks[i].song,
stats->song_unlocks[i].reveal);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"mission slot: %d\n"
"clear_flags_directions: 0x%X\n"
"clear_flag_mission: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_mission_unlocks[i].clear_flags_directions,
stats->worldmax_mission_unlocks[i].clear_flag_mission,
stats->worldmax_mission_unlocks[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_high_score %d: %d\n",
i,
stats->worldmax_high_score[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_MISSION_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_challenge %d: %d\n",
i,
stats->worldmax_challenge[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_BARRICADE_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_barricade %d:\n"
"flag_open: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_barricade[i].flag_open,
stats->worldmax_barricade[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_EVENT_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_event %d:\n"
"flag_cleared: %d\n"
"unused: 0x%X\n",
i,
stats->worldmax_event[i].flag_cleared,
stats->worldmax_event[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_WARP_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_warp %d:\n"
"direction_clear: 0x%X\n"
"clear: %d\n",
"unused: 0x%X\n",
i,
stats->worldmax_warp[i].direction_clear,
stats->worldmax_warp[i].clear,
stats->worldmax_warp[i].unused);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_current_land %d: %d\n",
i,
stats->worldmax_current_land[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX; i++) {
util_str_format(buffer, buf_size,
"worldmax_current_mission %d: %d\n",
i,
stats->worldmax_current_mission[i]);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
for (uint32_t i = 0; i < ASSET_NXA_USB_SAVE_SONG_MAX; i++) {
for (uint32_t j = 0; j < ASSET_NXA_USB_SAVE_NUM_MODES; j++) {
util_str_format(buffer, buf_size,
"song_scores %d/%d:\n"
"score: %d\n"
"player_id: %s\n",
i,
j,
stats->song_scores[i][j].score,
stats->song_scores[i][j].player_id);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
}
}
/* ------------- */
util_str_format(buffer, buf_size,
"security_dongle_mfgid 0x%X\n"
"unlock_signal: 0x%X\n"
"running_steps: %d\n"
"total_play_time_min: %f\n"
"total_completion_percentage: %f\n"
"arcade_completion_percentage: %f\n"
"brain_shower_completion_percentage: %f\n"
"special_stage_completion_percentage: %f\n",
stats->security_dongle_mfgid,
stats->unlock_signal,
stats->running_steps,
stats->total_play_time_min,
stats->total_completion_percentage,
stats->arcade_completion_percentage,
stats->brain_shower_completion_percentage,
stats->special_stage_completion_percentage);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
/* ------------- */
tmp2 = util_str_buffer(stats->unkn_some_map_scores_to_song_ids,
4096);
util_str_format(buffer, buf_size,
"unkn_some_map_scores_to_song_ids: %s\n",
tmp2);
tmp = util_str_merge(merged, buffer);
free(tmp2);
free(merged);
merged = tmp;
/* ------------- */
tmp2 = util_str_buffer(stats->unkn2, sizeof(stats->unkn2));
util_str_format(buffer, buf_size,
"unkn2: %s\n",
tmp2);
tmp = util_str_merge(merged, buffer);
free(tmp2);
free(merged);
merged = tmp;
/* ------------- */
util_str_format(buffer, buf_size,
"break_item_slot 0x%X\n"
"func_item_slot: 0x%X\n"
"bga_item_slot: 0x%X\n"
"time_item_slot: 0x%X\n"
"unknown: 0x%X\n",
stats->break_item_slot,
stats->func_item_slot,
stats->bga_item_slot,
stats->time_item_slot,
stats->unknown);
tmp = util_str_merge(merged, buffer);
free(merged);
merged = tmp;
free(buffer);
return merged;
}
struct asset_nxa_usb_save* asset_nxa_usb_save_new(void)
{
struct asset_nxa_usb_save* save;
char name[10];
save = (struct asset_nxa_usb_save*) util_xmalloc(
sizeof(struct asset_nxa_usb_save));
memset(save, 0, sizeof(struct asset_nxa_usb_save));
sprintf(name, "NX%06d", rand() % 1000000);
strcpy(save->review.player_id, name);
strcpy(save->stats.player_id, name);
/* default world map position */
save->stats.worldmax_map_position = 0xEF001;
/* nxa release date as default timestamp */
save->stats.timestamp.year = 2008;
save->stats.timestamp.month = 12;
save->stats.timestamp.day = 26;
save->stats.timestamp.hour = 0;
save->stats.timestamp.min = 0;
save->stats.timestamp.ms = 0;
return save;
}
void asset_nxa_usb_save_finalize(struct asset_nxa_usb_save* save)
{
/* checksum update, expected size for area to checksum: 90540 */
save->stats.adler32 = util_adler32_calc(1,
((const uint8_t*) &save->stats) + 4,
sizeof(struct asset_nxa_usb_save_stats) - 4);
}
char* asset_nxa_usb_save_to_string(const struct asset_nxa_usb_save* save)
{
char* review;
char* stats;
char* merged;
review = asset_nxa_usb_save_review_to_string(&save->review);
stats = asset_nxa_usb_save_stats_to_string(&save->stats);
merged = util_str_merge(review, stats);
free(review);
free(stats);
return merged;
}
void asset_nxa_usb_save_decrypt(uint8_t* buf, size_t len)
{
/* skip non encrypted review area */
buf += sizeof(struct asset_nxa_usb_save_review);
len -= sizeof(struct asset_nxa_usb_save_review);
for (size_t a = len - 1; a > 0; --a) {
buf[a] = (buf[a] ^ buf[a - 1]) + ((a * 1234567) >> 8);
}
}
void asset_nxa_usb_save_encrypt(uint8_t* buf, size_t len)
{
/* skip non encrypted review area */
buf += sizeof(struct asset_nxa_usb_save_review);
len -= sizeof(struct asset_nxa_usb_save_review);
for (size_t a = 1; a < len; ++a) {
buf[a] = (buf[a] - ((a * 1234567) >> 8)) ^ buf[a - 1];
}
}
+275
View File
@@ -0,0 +1,275 @@
#ifndef ASSET_NXA_USB_SAVE_H
#define ASSET_NXA_USB_SAVE_H
#include <stdint.h>
#include <stdlib.h>
enum asset_nxa_usb_save_limits {
ASSET_NXA_USB_SAVE_SERIAL_ID_MAX = 64,
ASSET_NXA_USB_SAVE_PLAYER_ID_MAX = 12,
ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX = 128,
ASSET_NXA_USB_SAVE_SONG_MAX = 256,
ASSET_NXA_USB_SAVE_MISSION_MAX = 1024,
ASSET_NXA_USB_SAVE_BARRICADE_MAX = 16,
ASSET_NXA_USB_SAVE_EVENT_MAX = 128,
ASSET_NXA_USB_SAVE_WARP_MAX = 8,
ASSET_NXA_USB_SAVE_NUM_MODES = 5,
};
struct asset_nxa_usb_save_time {
int16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint16_t ms;
} __attribute__((__packed__));
/* Not encrypted and read only, offset 0x00 to 0x144 (excl) */
struct asset_nxa_usb_save_review {
/* 0x00, 12 byte char array: max char length is 8 which gets displayed
ingame */
char player_id[ASSET_NXA_USB_SAVE_PLAYER_ID_MAX];
/* 0x0C, 4 byte int */
uint32_t mileage;
/* 0x10, 4 byte int: achieved reward count, max 65 */
uint32_t reward_count;
/* 0x14, 4 byte int: successful mission count, max 634 */
uint32_t worldmax_count;
/* 0x18, 4 byte int */
uint32_t play_count;
/* 0x1C, 128 byte char array */
char worldmax_current_land[ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX];
/* 0x9C, 128 byte char array */
char worldmax_current_mission[ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX];
/* 0x11C, 4 byte int */
uint32_t kcal;
/* 0x120, 4 byte int */
uint32_t v02;
/* 0x124, 8 byte */
struct asset_nxa_usb_save_time timestamp;
/* 0x12C, 4 byte int */
uint32_t running_step;
/* 0x130, 4 byte float */
float play_time_minutes;
/* 0x134, 4 byte float */
float total_completion_percentage;
/* 0x138, 4 byte float */
float arcade_percentage;
/* 0x13C, 4 byte float */
float brain_shower_percentage;
/* 0x140, 4 byte float */
float special_percentage;
} __attribute__((__packed__));
struct asset_nxa_usb_save_stats {
/* 0x144, 4 byte int */
uint32_t adler32;
/* 0x148, 64 byte */
char usb_serial[ASSET_NXA_USB_SAVE_SERIAL_ID_MAX];
/* 0x188, 8 byte */
struct asset_nxa_usb_save_time timestamp;
/* 0x190, 4 byte int */
int32_t avatar_id;
/* 0x194, 4 byte int: setting this non null will give you a trophy */
int32_t rank;
/* 0x198, 4 byte int: seems unused */
int32_t country_id;
/* 0x19C, 12 byte char array */
char player_id[ASSET_NXA_USB_SAVE_PLAYER_ID_MAX];
/* 0x1A8, 4 byte int */
int32_t mileage;
/* 0x1AD, 4 byte int */
int32_t play_count;
/* 0x1B0, 4 byte int */
int32_t kcal;
/* 0x1B4, 4 byte int */
int32_t v02;
/* 0x1B8, 4 byte int */
int32_t worldmax_map_position;
/* 0x1BC, 4 byte int: achieved reward count, max 65 */
int32_t reward_count;
/* 0x1C0, 4 byte int: successful mission count, max 634 */
int32_t worldmax_count;
/* 0x1C4, 256 bytes */
struct {
/* bits 0-4 for modes/stepcharts */
uint8_t mode : 5;
/* unused bit */
uint8_t unused : 1;
/* bit 6 song itself */
uint8_t song : 1;
/* bit 7 reveal flag */
uint8_t reveal : 1;
} song_unlocks[ASSET_NXA_USB_SAVE_SONG_MAX] __attribute__((__packed__));
/* 0x2C4, 1024 bytes */
struct {
uint8_t clear_flags_directions : 4;
uint8_t clear_flag_mission : 1;
uint8_t unused : 3;
} worldmax_mission_unlocks[ASSET_NXA_USB_SAVE_MISSION_MAX]
__attribute__((__packed__));
/* 0x6C4, 4 byte int * 1024: high score for each mission */
int32_t worldmax_high_score[ASSET_NXA_USB_SAVE_MISSION_MAX];
/* 0x16C4, 4 byte int * 1024 */
int32_t worldmax_challenge[ASSET_NXA_USB_SAVE_MISSION_MAX];
/* 0x26C4, 16 bytes
0x00 for barricade clear and 0x01 for block */
struct {
uint8_t flag_open : 1;
uint8_t unused : 7;
} worldmax_barricade[ASSET_NXA_USB_SAVE_BARRICADE_MAX]
__attribute__((__packed__));
/* 0x26D4, 128 bytes
0x00 for event clear and 0x01 for not cleared */
struct {
uint8_t flag_cleared : 1;
uint8_t unused : 7;
} worldmax_event[ASSET_NXA_USB_SAVE_EVENT_MAX] __attribute__((__packed__));
/* 0x2754, 8 bytes
bit 0~3: clear bit for each direction bit:4 clear */
struct {
uint8_t direction_clear : 4;
uint8_t clear : 1;
uint8_t unused : 3;
} worldmax_warp[ASSET_NXA_USB_SAVE_WARP_MAX] __attribute__((__packed__));
/* 0x275C, 128 byte char array */
char worldmax_current_land[ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX];
/* 0x27DC, 128 byte char array */
char worldmax_current_mission[ASSET_NXA_USB_SAVE_WORLDMAX_LOC_MAX];
/* 0x285C */
struct {
int32_t score;
char player_id[ASSET_NXA_USB_SAVE_PLAYER_ID_MAX];
} song_scores[ASSET_NXA_USB_SAVE_SONG_MAX][ASSET_NXA_USB_SAVE_NUM_MODES]
__attribute__((__packed__));
/* 0x785C, 4 byte int */
uint32_t security_dongle_mfgid;
/* 0x7860, 4 byte int: <=3 Tells game to unlock new song */
uint32_t unlock_signal;
/* 0x7864, 4 byte int */
uint32_t running_steps;
/* 0x7868, 4 byte float */
float total_play_time_min;
/* 0x786C, 4 byte float */
float total_completion_percentage;
/* 0x7870, 4 byte float */
float arcade_completion_percentage;
/* 0x7874, 4 byte float */
float brain_shower_completion_percentage;
/* 0x7878, 4 byte float */
float special_stage_completion_percentage;
/* 0x787C TODO unknown
0x787C, 1 byte char * 4096 - Used by GetSongIndex -
Assuming Maps Scores to Song IDs */
uint8_t unkn_some_map_scores_to_song_ids[4096];
/* 0x887C TODO unknown
0x887C, 1 byte char * 576 - Song Locks/Unlocks - 0x40 Unlocks (Special Zone, etc.)
0x8A70 , 1 byte char * 36 - WorldMax Keys and various other events for completion
0x8A9C, 8 byte char* - A Worldmax ID (Perhaps one to mark progress in Space)
0x8AA4, 1 byte char * 24 - More WorldMax Event Switches */
uint8_t unkn2[576];
/* 0x8ABC, 4 byte int: possible values
0x1 - grey
0x2 - red
0x3 - yellow
0x4 - green
0x5 - Dark Green
0x6 - aqua
0x7 - blue
0x8 - purple
0x9 - pink
0xA - light grey
0xB - Black
0xC - Orange
*/
uint32_t break_item_slot;
/* 0x8AC0, 4 byte int
0x1 - Shield
0x2 - PermaShield
0x3 - Line Searcher
0x4 - Perma Line Searcher
0x5 - Life Up
0x6 - Perma Life Up
0x7 - Lucky
0x8 - Perma Lucky
0x9 - Portal Pass
*/
uint32_t func_item_slot;
/* 0x8AC4, 4 byte int
0x1 - BGA OFF
0x2 - Perma BGA OFF
*/
uint32_t bga_item_slot;
/* 0x8AC4, 4 byte int
0x1 - 10+
0x2 - 20+
0x3 - 30+
*/
uint32_t time_item_slot;
/* 0x8AC8, 4 byte int */
uint32_t unknown;
/* 0x8ACC, unknown stuff: More WorldMax Event Switches (Unused)? */
uint8_t unknown2[12];
/* 0x8AD8, 4 byte float - Unknown What this Does (Unused) */
float unknown3;
/* 0x8ADC, totally unknown stuff, just nulls */
uint8_t unknown4[0x7F8];
/* 0x92D4 */
uint8_t unknown5[0xD01C];
/* TODO missing switches for dark/light world stuff
0x8ACC, 1 byte char - More WorldMax Event Switches (Unused)
0x8AD8 , 4 byte float - Unknown What this Does (Unused)
0x92D4, High Score Table [See Below]
0x132D0, 5216 bytes - Unknown Table (Unused)
0x162D0, 24 bytes - Unknown Footer (Unused by Game - Most Likely PumBI)
*/
} __attribute__((__packed__));
struct asset_nxa_usb_save {
struct asset_nxa_usb_save_review review;
struct asset_nxa_usb_save_stats stats;
} __attribute__((__packed__));
struct asset_nxa_usb_save* asset_nxa_usb_save_new(void);
/* update checksum and prepare profile to get encrypted */
void asset_nxa_usb_save_finalize(struct asset_nxa_usb_save* save);
char* asset_nxa_usb_save_to_string(const struct asset_nxa_usb_save* save);
void asset_nxa_usb_save_decrypt(uint8_t* buf, size_t len);
void asset_nxa_usb_save_encrypt(uint8_t* buf, size_t len);
#endif
+100
View File
@@ -0,0 +1,100 @@
#define LOG_MODULE "asset-nxa-util"
#include <string.h>
#include "util/fs.h"
#include "util/log.h"
#include "util.h"
struct asset_nxa_usb_rank* asset_nxa_usb_rank_load_from_file(const char* path,
bool encrypted)
{
struct asset_nxa_usb_rank* rank;
size_t size;
if (!util_file_load(path, (void**) &rank, &size, false)) {
log_error("Loading nxa rank file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_nxa_usb_rank)) {
log_error("Invalid size of nxa rank file %s: %d != %d", path, size,
sizeof(struct asset_nxa_usb_rank));
free(rank);
return NULL;
}
if (encrypted) {
asset_nxa_usb_rank_decrypt((uint8_t*) rank, size);
}
return rank;
}
bool asset_nxa_usb_rank_save_to_file(const char* path,
const struct asset_nxa_usb_rank* rank, bool encrypt)
{
struct asset_nxa_usb_rank rank_enc;
memcpy(&rank_enc, rank, sizeof(struct asset_nxa_usb_rank));
if (encrypt) {
asset_nxa_usb_rank_encrypt((uint8_t*) &rank_enc,
sizeof(struct asset_nxa_usb_rank));
}
if (!util_file_save(path, (void**) &rank_enc,
sizeof(struct asset_nxa_usb_rank))) {
log_error("Storing nxa rank data to %s failed", path);
return false;
}
return true;
}
struct asset_nxa_usb_save* asset_nxa_usb_save_load_from_file(const char* path,
bool encrypted)
{
struct asset_nxa_usb_save* save;
size_t size;
if (!util_file_load(path, (void**) &save, &size, false)) {
log_error("Loading nxa save file %s failed", path);
return NULL;
}
if (size != sizeof(struct asset_nxa_usb_save)) {
log_error("Invalid size of nxa save file %s: %d != %d", path, size,
sizeof(struct asset_nxa_usb_save));
free(save);
return NULL;
}
if (encrypted) {
asset_nxa_usb_save_decrypt((uint8_t*) save, size);
}
return save;
}
bool asset_nxa_usb_save_save_to_file(const char* path,
const struct asset_nxa_usb_save* save, bool encrypt)
{
struct asset_nxa_usb_save save_enc;
memcpy(&save_enc, save, sizeof(struct asset_nxa_usb_save));
if (encrypt) {
asset_nxa_usb_save_encrypt((uint8_t*) &save_enc,
sizeof(struct asset_nxa_usb_save));
}
if (!util_file_save(path, (const void*) &save_enc,
sizeof(struct asset_nxa_usb_save))) {
log_error("Storing nxa save data to %s failed", path);
return false;
}
return true;
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef ASSET_NXA_UTIL_H
#define ASSET_NXA_UTIL_H
#include <stdbool.h>
#include "usb-rank.h"
#include "usb-save.h"
// load from file and decrypt, returns decrypted struct
struct asset_nxa_usb_rank* asset_nxa_usb_rank_load_from_file(const char* path, bool encrypted);
// takes decrypted rank data, encrypts it and writes it to file
bool asset_nxa_usb_rank_save_to_file(const char* path, const struct asset_nxa_usb_rank* rank, bool encrypt);
// load from file and decrypt, returns decrypted struct
struct asset_nxa_usb_save* asset_nxa_usb_save_load_from_file(const char* path, bool encrypted);
// takes decrypted save data, encrypts it and writes it to file
bool asset_nxa_usb_save_save_to_file(const char* path, const struct asset_nxa_usb_save* save, bool encrypt);
#endif
+154
View File
@@ -0,0 +1,154 @@
/**
* Tool for NXA profiles: decrypt, encrypt, print profile data
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asset/nxa/lib/util.h"
#include "util/str.h"
int main(int argc, char** argv)
{
int ret;
char* rank_path;
char* save_path;
if (argc < 3) {
printf("Usage: %s [cmd: new, dec, enc, dump] "
"[path containing nxarank.bin nxasave.bin]\n", argv[0]);
return -1;
}
ret = 0;
rank_path = util_str_merge(argv[2], "nxarank.bin");
save_path = util_str_merge(argv[2], "nxasave.bin");
if (!strcmp(argv[1], "new")) {
struct asset_nxa_usb_rank* rank = asset_nxa_usb_rank_new();
struct asset_nxa_usb_save* save = asset_nxa_usb_save_new();
asset_nxa_usb_rank_finalize(rank);
asset_nxa_usb_save_finalize(save);
if (!asset_nxa_usb_rank_save_to_file(rank_path, rank, true)) {
fprintf(stderr, "Creating rank file %s failed\n", rank_path);
ret = -2;
}
if (!asset_nxa_usb_save_save_to_file(save_path, save, true)) {
fprintf(stderr, "Creating save file %s failed\n", save_path);
ret = -3;
}
free(rank);
free(save);
} else {
struct asset_nxa_usb_rank* rank;
struct asset_nxa_usb_save* save;
bool encrypted;
if (!strcmp(argv[1], "enc")) {
char* tmp;
encrypted = false;
tmp = util_str_merge(rank_path, ".dec");
free(rank_path);
rank_path = tmp;
tmp = util_str_merge(save_path, ".dec");
free(save_path);
save_path = tmp;
} else {
encrypted = true;
}
rank = asset_nxa_usb_rank_load_from_file(rank_path, encrypted);
save = asset_nxa_usb_save_load_from_file(save_path, encrypted);
if (!rank) {
fprintf(stderr, "Loading %s failed", rank_path);
ret = -4;
goto cleanup;
}
if (!save) {
fprintf(stderr, "Loading %s failed", save_path);
ret = -5;
free(rank);
goto cleanup;
}
if (!strcmp(argv[1], "dec")) {
char* rank_path_dec;
char* save_path_dec;
rank_path_dec = util_str_merge(rank_path, ".dec");
save_path_dec = util_str_merge(save_path, ".dec");
if (!asset_nxa_usb_rank_save_to_file(rank_path_dec, rank, false)) {
fprintf(stderr, "Saving decrypted rank file to %s failed\n",
rank_path_dec);
ret = -6;
}
if (!asset_nxa_usb_save_save_to_file(save_path_dec, save, false)) {
fprintf(stderr, "Saving decrypted save file to %s failed\n",
save_path_dec);
ret = -7;
}
free(rank_path_dec);
free(save_path_dec);
} else if (!strcmp(argv[1], "enc")) {
char* rank_path_enc;
char* save_path_enc;
rank_path_enc = util_str_merge(rank_path, ".enc");
save_path_enc = util_str_merge(save_path, ".enc");
if (!asset_nxa_usb_rank_save_to_file(rank_path_enc, rank, true)) {
fprintf(stderr, "Saving encrypted rank file to %s failed\n",
rank_path_enc);
ret = -6;
}
if (!asset_nxa_usb_save_save_to_file(save_path_enc, save, true)) {
fprintf(stderr, "Saving encrypted save file to %s failed\n",
save_path_enc);
ret = -7;
}
free(rank_path_enc);
free(save_path_enc);
} else if (!strcmp(argv[1], "dump")) {
char* rank_str;
char* save_str;
rank_str = asset_nxa_usb_rank_to_string(rank);
save_str = asset_nxa_usb_save_to_string(save);
printf("----------------- rank -----------------\n"
"%s----------------- save -----------------\n%s",
rank_str, save_str);
free(rank_str);
free(save_str);
} else {
fprintf(stderr, "Unknown command %s\n", argv[1]);
ret = -4;
}
free(rank);
free(save);
}
cleanup:
free(rank_path);
free(save_path);
return ret;
}
+87
View File
@@ -0,0 +1,87 @@
#include <string.h>
#include "asset/x2/lib/settings.h"
#include "util/adler32.h"
#include "util/mem.h"
static const char* _asset_x2_settings_defaut_high_score_names[] = {
"MKII",
"YAHP",
"KANN",
"ZINY",
"PIAH",
"AHN ",
"PIYO",
"HIKO",
"HALO",
"BAJI",
"YUTE",
"SS ",
"SIHO",
"AZKI",
"JUNE",
"GOON",
"ARI*",
"TAMA",
"YSMA",
"PIU "
};
struct asset_x2_settings* asset_x2_settings_new(void)
{
struct asset_x2_settings* settings;
settings = util_xmalloc(sizeof(struct asset_x2_settings));
memset(settings->ff_pad, 0xFF, sizeof(settings->ff_pad));
memset(settings->unkn, 0, sizeof(settings->unkn));
settings->unlock_canond_full = 0;
settings->total_uptime = 0;
settings->unkn69 = 0x69;
memset(settings->song_blacklist, 0, ASSET_X2_SETTINGS_MAX_SONGS);
memset(settings->unkn3, 0, sizeof(settings->unkn3));
memcpy(settings->ident, "EX02", 4);
/* Updated using finalize */
settings->adler32 = 0;
memset(settings->song_play_count, 0, sizeof(uint16_t) * ASSET_X2_SETTINGS_MAX_SONGS);
settings->padding = 0;
for (uint8_t i = 0; i < ASSET_X2_SETTINGS_HIGH_SCORE_COUNT; i++) {
settings->high_score_scores[i] = 10000u - (500u * i);
memcpy(&settings->high_score_names[i], &_asset_x2_settings_defaut_high_score_names[i],
ASSET_X2_SETTINGS_HIGH_SCORE_NAME_LEN);
}
settings->coin_limit = 0;
settings->game_mode = ASSET_X2_SETTINGS_GAME_MODE_NORMAL;
settings->game_level = ASSET_X2_SETTINGS_LEVEL_NORMAL;
settings->game_stage_break = ASSET_X2_SETTINGS_STAGE_BREAK_2ND;
settings->language = ASSET_X2_SETTINGS_LANG_ENG;
settings->game_demo_sound = 1;
settings->game_show_help = 1;
settings->coin1_setting = 1;
settings->coin2_setting = 1;
settings->bookkeeping_coin1_total = 0;
settings->bookkeeping_coin2_total = 0;
settings->bookkeeping_service_total = 0;
return settings;
}
void asset_x2_settings_finalize(struct asset_x2_settings* settings)
{
/* Seems odd, but they just checksum the game settings part...*/
settings->adler32 = util_adler32_calc(1, (const uint8_t*) &settings->game_mode, 8);
}
void asset_x2_settings_unlock_all(struct asset_x2_settings* settings)
{
settings->unlock_canond_full = 1;
}
char* asset_x2_settings_to_string(const struct asset_x2_settings* settings)
{
// TODO
return "";
}
+82
View File
@@ -0,0 +1,82 @@
#ifndef ASSET_X2_SETTINGS_H
#define ASSET_X2_SETTINGS_H
#include <stdint.h>
#define ASSET_X2_SETTINGS_FILE_NAME "PIUEXCEED.INI"
#define ASSET_X2_SETTINGS_MAX_SONGS 139
#define ASSET_X2_SETTINGS_HIGH_SCORE_COUNT 20
#define ASSET_X2_SETTINGS_HIGH_SCORE_NAME_LEN 4
enum asset_x2_settings_language {
ASSET_X2_SETTINGS_LANG_KOR = 0,
ASSET_X2_SETTINGS_LANG_ENG = 1,
ASSET_X2_SETTINGS_LANG_SP = 2,
ASSET_X2_SETTINGS_LANG_CH = 3,
};
enum asset_x2_settings_game_mode {
ASSET_X2_SETTINGS_GAME_MODE_NORMAL = 0,
ASSET_X2_SETTINGS_GAME_MODE_EVENT = 1,
};
enum asset_x2_settings_level {
ASSET_X2_SETTINGS_LEVEL_EASY = 0,
ASSET_X2_SETTINGS_LEVEL_NORMAL = 1,
ASSET_X2_SETTINGS_LEVEL_HARD = 2,
};
enum asset_x2_settings_stage_break {
ASSET_X2_SETTINGS_STAGE_BREAK_OFF = 0,
ASSET_X2_SETTINGS_STAGE_BREAK_1ST = 1,
ASSET_X2_SETTINGS_STAGE_BREAK_2ND = 2,
ASSET_X2_SETTINGS_STAGE_BREAK_3RD = 3,
ASSET_X2_SETTINGS_STAGE_BREAK_4TH = 4,
};
struct asset_x2_settings {
/* Padding to fill up to 4k EEPROM legacy size */
uint8_t ff_pad[0x51B];
uint8_t unkn[10];
uint8_t unlock_canond_full;
uint32_t total_uptime;
uint8_t unkn69;
uint8_t song_blacklist[ASSET_X2_SETTINGS_MAX_SONGS];
uint8_t unkn3[0x75];
/* EX02 */
char ident[4];
uint32_t adler32;
uint16_t song_play_count[ASSET_X2_SETTINGS_MAX_SONGS];
uint16_t padding;
uint32_t high_score_scores[ASSET_X2_SETTINGS_HIGH_SCORE_COUNT];
/* 20 names a 4 chars for the default high score list */
char high_score_names[ASSET_X2_SETTINGS_HIGH_SCORE_COUNT][ASSET_X2_SETTINGS_HIGH_SCORE_NAME_LEN];
/* 0 none */
uint8_t coin_limit;
uint8_t game_mode;
uint8_t game_level;
uint8_t game_stage_break;
uint8_t language;
/* 0 off, 1 on */
uint8_t game_demo_sound;
/* 0 off, 1 on */
uint8_t game_show_help;
/* 0 freeplay */
uint8_t coin1_setting;
/* 0 freeplay */
uint8_t coin2_setting;
uint32_t bookkeeping_coin1_total;
uint32_t bookkeeping_coin2_total;
uint32_t bookkeeping_service_total;
} __attribute__((__packed__));
struct asset_x2_settings* asset_x2_settings_new(void);
// update checksum
void asset_x2_settings_finalize(struct asset_x2_settings* settings);
void asset_x2_settings_unlock_all(struct asset_x2_settings* settings);
char* asset_x2_settings_to_string(const struct asset_x2_settings* settings);
#endif
+62
View File
@@ -0,0 +1,62 @@
#include <string.h>
#include "asset/zero/lib/settings.h"
#include "util/adler32.h"
#include "util/mem.h"
struct asset_zero_settings* asset_zero_settings_new(void)
{
struct asset_zero_settings* settings;
settings = util_xmalloc(sizeof(struct asset_zero_settings));
memset(settings->ff_pad, 0xFF, sizeof(settings->ff_pad));
memcpy(settings->ident, "ZERO", 4);
/* Updated using finalize */
settings->adler32 = 0;
settings->total_uptime = 0;
settings->language = ASSET_ZERO_SETTINGS_LANG_ENG;
memset(settings->song_play_count, 0, sizeof(uint32_t) * ASSET_ZERO_SETTINGS_MAX_SONGS);
settings->game_option_default_station = ASSET_ZERO_SETTINGS_STATION_EASY;
settings->game_mode = ASSET_ZERO_SETTINGS_GAME_MODE_NORMAL;
settings->game_level = ASSET_ZERO_SETTINGS_LEVEL_NORMAL;
settings->game_stage_break = ASSET_ZERO_SETTINGS_STAGE_BREAK_2ND;
settings->game_demo_sound = 1;
settings->game_show_help = 1;
settings->game_mercy_ticket = 0;
settings->game_score_per_ticket = 0;
settings->coin_limit = 0;
settings->coin1_setting = 1;
settings->coin2_setting = 1;
memset(settings->mission_unlock_flags, 0, ASSET_ZERO_SETTINGS_NUM_UNLOCK_FLAGS);
settings->unkn2 = 0x2A;
settings->unkn3 = 0;
settings->bookkeeping_coin1_total = 0;
settings->bookkeeping_coin2_total = 0;
settings->bookkeeping_service_total = 0;
settings->bookkeeping_ticket_1 = 0;
settings->bookkeeping_ticket_2 = 0;
settings->region = 0xFF;
memset(settings->censor_table, 0, ASSET_ZERO_SETTINGS_MAX_SONGS);
return settings;
}
void asset_zero_settings_finalize(struct asset_zero_settings* settings)
{
/* Seems odd, but they just checksum the game settings part...*/
settings->adler32 = util_adler32_calc(1, (const uint8_t*) &settings->game_mode, 8);
}
void asset_zero_settings_unlock_all(struct asset_zero_settings* settings)
{
/* Mark all missions cleared to unlock all missions and content locked by some missions */
memset(settings->mission_unlock_flags, 1, ASSET_ZERO_SETTINGS_NUM_UNLOCK_FLAGS);
}
char* asset_zero_settings_to_string(const struct asset_zero_settings* settings)
{
// TODO
return "";
}
+95
View File
@@ -0,0 +1,95 @@
#ifndef ASSET_ZERO_SETTINGS_H
#define ASSET_ZERO_SETTINGS_H
#include <stdint.h>
#define ASSET_ZERO_SETTINGS_FILE_NAME "PIUZERO.INI"
#define ASSET_ZERO_SETTINGS_MAX_SONGS 200
#define ASSET_ZERO_SETTINGS_NUM_UNLOCK_FLAGS 59
enum asset_zero_settings_language {
ASSET_ZERO_SETTINGS_LANG_KOR = 0,
ASSET_ZERO_SETTINGS_LANG_ENG = 1,
ASSET_ZERO_SETTINGS_LANG_SP = 2,
ASSET_ZERO_SETTINGS_LANG_CH = 3,
};
enum asset_zero_settings_station {
ASSET_ZERO_SETTINGS_STATION_EASY = 0,
ASSET_ZERO_SETTINGS_STATION_ARCADE = 1,
ASSET_ZERO_SETTINGS_STATION_REMIX = 2,
};
enum asset_zero_settings_game_mode {
ASSET_ZERO_SETTINGS_GAME_MODE_NORMAL = 0,
ASSET_ZERO_SETTINGS_GAME_MODE_EVENT = 1,
};
enum asset_zero_settings_level {
ASSET_ZERO_SETTINGS_LEVEL_EASY = 0,
ASSET_ZERO_SETTINGS_LEVEL_NORMAL = 1,
ASSET_ZERO_SETTINGS_LEVEL_HARD = 2,
};
enum asset_zero_settings_stage_break {
ASSET_ZERO_SETTINGS_STAGE_BREAK_OFF = 0,
ASSET_ZERO_SETTINGS_STAGE_BREAK_1ST = 1,
ASSET_ZERO_SETTINGS_STAGE_BREAK_2ND = 2,
ASSET_ZERO_SETTINGS_STAGE_BREAK_3RD = 3,
ASSET_ZERO_SETTINGS_STAGE_BREAK_4TH = 4,
};
struct asset_zero_settings {
/* Padding to fill up to 4k EEPROM legacy size */
uint8_t ff_pad[0xBA4];
/* ZERO */
char ident[4];
uint32_t adler32;
uint32_t total_uptime;
uint8_t language;
uint32_t song_play_count[ASSET_ZERO_SETTINGS_MAX_SONGS];
uint32_t game_option_default_station;
uint8_t game_mode;
uint8_t game_level;
uint8_t game_stage_break;
/* 0 off, 1 on */
uint8_t game_demo_sound;
/* 0 off, 1 on */
uint8_t game_show_help;
/* 0 off, 1 on */
uint8_t game_mercy_ticket;
/* 0 off, 1 - 6 for score */
uint8_t game_score_per_ticket;
/* 0 none */
uint8_t coin_limit; // 0 none
/* 0 freeplay */
uint8_t coin1_setting;
/* 0 freeplay */
uint8_t coin2_setting;
/* Unlock flags for missions (total of 30) */
uint8_t mission_unlock_flags[ASSET_ZERO_SETTINGS_NUM_UNLOCK_FLAGS];
/* Default value 0x2A */
uint32_t unkn2;
uint32_t unkn3;
uint32_t bookkeeping_coin1_total;
uint32_t bookkeeping_coin2_total;
uint32_t bookkeeping_service_total;
uint32_t bookkeeping_ticket_1;
uint32_t bookkeeping_ticket_2;
/* FF -> not used, censoring globally disabled. unused feature though */
uint8_t region;
/* 0 = censoring of song off */
uint8_t censor_table[ASSET_ZERO_SETTINGS_MAX_SONGS];
} __attribute__((__packed__));
struct asset_zero_settings* asset_zero_settings_new(void);
// update checksum
void asset_zero_settings_finalize(struct asset_zero_settings* settings);
// sets all missions cleared to unlock special stage songs and other missions
void asset_zero_settings_unlock_all(struct asset_zero_settings* settings);
char* asset_zero_settings_to_string(const struct asset_zero_settings* settings);
#endif
+12
View File
@@ -0,0 +1,12 @@
/**
* Common stuff for capnhook/hook module
*
* Based on original capnhook code for windows by decafcode:
* https://github.com/decafcode/capnhook
*/
#ifndef CAPNHOOK_COMMON_H
#define CAPNHOOK_COMMON_H
#define _countof(array) (sizeof(array) / sizeof((array)[0]))
#endif
+662
View File
@@ -0,0 +1,662 @@
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "capnhook/hook/common.h"
#include "capnhook/hook/filehook.h"
#include "capnhook/hook/lib.h"
#include "util/time.h"
/* ------------------------------------------------------------------------------------------------------------------ */
/* Real funcs typedefs */
/* ------------------------------------------------------------------------------------------------------------------ */
typedef FILE* (*cnh_filehook_fopen_t)(const char* filename, const char* mode);
typedef int (*cnh_filehook_fclose_t)(FILE* stream);
typedef size_t (*cnh_filehook_fread_t)(void* ptr, size_t size, size_t nmemb, FILE* stream);
typedef size_t (*cnh_filehook_fwrite_t)(const void* ptr, size_t size, size_t nmemb, FILE* stream);
typedef char* (*cnh_filehook_fgets_t)(char* s, int size, FILE* stream);
typedef int (*cnh_filehook_fseek_t)(FILE* stream, long offset, int whence);
typedef long (*cnh_filehook_ftell_t)(FILE* stream);
typedef int (*cnh_filehook_feof_t)(FILE* stream);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private helpers */
/* ------------------------------------------------------------------------------------------------------------------ */
static void _cnh_filehook_init(void);
static enum cnh_result _cnh_filehook_invoke_real(struct cnh_filehook_irp *irp);
static enum cnh_result _cnh_filehook_invoke_real_fopen(struct cnh_filehook_irp *irp);
static enum cnh_result _cnh_filehook_invoke_real_fclose(struct cnh_filehook_irp *irp);
static enum cnh_result _cnh_filehook_invoke_real_fread(struct cnh_filehook_irp *irp);
static enum cnh_result _cnh_filehook_invoke_real_fwrite(struct cnh_filehook_irp *irp);
static enum cnh_result _cnh_filehook_invoke_real_fgets(struct cnh_filehook_irp *irp);
static enum cnh_result _cnh_filehook_invoke_real_fseek(struct cnh_filehook_irp *irp);
static enum cnh_result _cnh_filehook_invoke_real_ftell(struct cnh_filehook_irp *irp);
static enum cnh_result _cnh_filehook_invoke_real_feof(struct cnh_filehook_irp *irp);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private state */
/* ------------------------------------------------------------------------------------------------------------------ */
static cnh_filehook_fopen_t _cnh_filehook_real_fopen;
static cnh_filehook_fclose_t _cnh_filehook_real_fclose;
static cnh_filehook_fread_t _cnh_filehook_real_fread;
static cnh_filehook_fwrite_t _cnh_filehook_real_fwrite;
static cnh_filehook_fgets_t _cnh_filehook_real_fgets;
static cnh_filehook_fseek_t _cnh_filehook_real_fseek;
static cnh_filehook_ftell_t _cnh_filehook_real_ftell;
static cnh_filehook_feof_t _cnh_filehook_real_feof;
static const cnh_filehook_fn_t _cnh_filehook_real_handlers[8] = {
[CNH_FILEHOOK_IRP_OP_OPEN] = _cnh_filehook_invoke_real_fopen,
[CNH_FILEHOOK_IRP_OP_CLOSE] = _cnh_filehook_invoke_real_fclose,
[CNH_FILEHOOK_IRP_OP_READ] = _cnh_filehook_invoke_real_fread,
[CNH_FILEHOOK_IRP_OP_WRITE] = _cnh_filehook_invoke_real_fwrite,
[CNH_FILEHOOK_IRP_OP_FGETS] = _cnh_filehook_invoke_real_fgets,
[CNH_FILEHOOK_IRP_OP_SEEK] = _cnh_filehook_invoke_real_fseek,
[CNH_FILEHOOK_IRP_OP_TELL] = _cnh_filehook_invoke_real_ftell,
[CNH_FILEHOOK_IRP_OP_EOF] = _cnh_filehook_invoke_real_feof,
};
static atomic_int _cnh_filehook_initted = ATOMIC_VAR_INIT(0);
static atomic_int _cnh_filehook_init_in_progress = ATOMIC_VAR_INIT(0);
static pthread_mutex_t _cnh_filehook_lock;
static cnh_filehook_fn_t *_cnh_filehook_handlers;
static size_t _cnh_filehook_nhandlers;
/* ------------------------------------------------------------------------------------------------------------------ */
/* Public module functions */
/* ------------------------------------------------------------------------------------------------------------------ */
enum cnh_result cnh_filehook_push_handler(cnh_filehook_fn_t fn)
{
cnh_filehook_fn_t *new_array;
size_t new_size;
enum cnh_result result;
assert(fn != NULL);
_cnh_filehook_init();
pthread_mutex_lock(&_cnh_filehook_lock);
new_size = _cnh_filehook_nhandlers + 1;
new_array = realloc(_cnh_filehook_handlers, new_size * sizeof(cnh_filehook_fn_t));
if (new_array != NULL) {
_cnh_filehook_handlers = new_array;
_cnh_filehook_handlers[_cnh_filehook_nhandlers++] = fn;
result = CNH_RESULT_SUCCESS;
} else {
result = CNH_RESULT_OUT_OF_MEMORY;
}
pthread_mutex_unlock(&_cnh_filehook_lock);
return result;
}
enum cnh_result cnh_filehook_invoke_next(struct cnh_filehook_irp *irp)
{
cnh_filehook_fn_t handler;
enum cnh_result result;
assert(irp != NULL);
assert(_cnh_filehook_initted > 0);
pthread_mutex_lock(&_cnh_filehook_lock);
assert(irp->next_handler <= _cnh_filehook_nhandlers);
if (irp->next_handler < _cnh_filehook_nhandlers) {
handler = _cnh_filehook_handlers[irp->next_handler];
irp->next_handler++;
} else {
handler = _cnh_filehook_invoke_real;
irp->next_handler = (size_t) -1;
}
pthread_mutex_unlock(&_cnh_filehook_lock);
result = handler(irp);
if (result != CNH_RESULT_SUCCESS) {
irp->next_handler = (size_t) -1;
}
return result;
}
FILE* cnh_filehook_open_dummy_file_handle()
{
_cnh_filehook_init();
/* We need to return some sort of valid FD to the
caller, since we're simulating a device that
doesn't exist. Open an FD on /dev/null. */
return _cnh_filehook_real_fopen("/dev/null", "r");
}
void cnh_filehook_close_dummy_file_handle(FILE* file)
{
_cnh_filehook_init();
/* Avoid hooking pipeline when using dummy handles */
_cnh_filehook_real_fclose(file);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooked functions */
/* ------------------------------------------------------------------------------------------------------------------ */
FILE* fopen(const char* filename, const char* mode)
{
struct cnh_filehook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_filehook_init();
if (filename == NULL || mode == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return NULL;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FILEHOOK_IRP_OP_OPEN;
irp.file = NULL;
irp.open_filename = filename;
irp.open_mode = mode;
result = cnh_filehook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return NULL;
}
errno = 0;
return irp.file;
}
int fclose(FILE* stream)
{
struct cnh_filehook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_filehook_init();
if (stream == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return EOF;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FILEHOOK_IRP_OP_CLOSE;
irp.file = stream;
result = cnh_filehook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return EOF;
}
errno = 0;
return 0;
}
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream)
{
struct cnh_filehook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_filehook_init();
if (ptr == NULL || stream == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return 0;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FILEHOOK_IRP_OP_READ;
irp.file = stream;
irp.read.bytes = ptr;
irp.read.nbytes = size * nmemb;
irp.read.pos = 0;
/* To ensure we don't mess up any calls that are just passed through, we have to drag along the original size
and nmemb parameters and apply them to the real call at the end of the chain if the iobuffer was not
modified */
irp.orig_read_write_size = size;
irp.orig_read_write_nmemb = nmemb;
result = cnh_filehook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return 0;
}
errno = 0;
return irp.read.pos / size;
}
size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream)
{
struct cnh_filehook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_filehook_init();
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FILEHOOK_IRP_OP_WRITE;
irp.file = stream;
irp.write.bytes = ptr;
irp.write.nbytes = size * nmemb;
irp.write.pos = 0;
/* To ensure we don't mess up any calls that are just passed through, we have to drag along the original size
and nmemb parameters and apply them to the real call at the end of the chain if the iobuffer was not
modified */
irp.orig_read_write_size = size;
irp.orig_read_write_nmemb = nmemb;
result = cnh_filehook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return 0;
}
errno = 0;
return irp.write.pos / size;
}
char* fgets(char* s, int size, FILE* stream)
{
struct cnh_filehook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_filehook_init();
if (s == NULL || stream == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return NULL;
}
/* Redirect this to fread */
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FILEHOOK_IRP_OP_FGETS;
irp.file = stream;
irp.read.bytes = (uint8_t*) s;
irp.read.nbytes = (size_t) size;
irp.read.pos = 0;
result = cnh_filehook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return NULL;
}
errno = 0;
return (char*) irp.read.bytes;
}
int fseek(FILE* stream, long offset, int whence)
{
struct cnh_filehook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_filehook_init();
if (stream == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FILEHOOK_IRP_OP_SEEK;
irp.file = stream;
irp.seek_origin = whence;
irp.seek_offset = offset;
result = cnh_filehook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
errno = 0;
return 0;
}
void rewind(FILE* stream)
{
struct cnh_filehook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_filehook_init();
if (stream == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return;
}
/* Seek to beginning */
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FILEHOOK_IRP_OP_SEEK;
irp.file = stream;
irp.seek_origin = SEEK_SET;
irp.seek_offset = 0;
result = cnh_filehook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return;
}
}
long ftell(FILE* stream)
{
struct cnh_filehook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_filehook_init();
if (stream == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FILEHOOK_IRP_OP_TELL;
irp.file = stream;
/* Return value */
irp.tell_offset = 0;
result = cnh_filehook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
errno = 0;
return (long) irp.tell_offset;
}
int feof(FILE* stream)
{
struct cnh_filehook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_filehook_init();
if (stream == NULL) {
/* Must be EBADF according to man pages */
errno = EBADF;
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FILEHOOK_IRP_OP_EOF;
irp.file = stream;
/* Return value */
irp.eof = false;
result = cnh_filehook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
/* Function is never supposed to fail and EBADF is the only allowed error code */
errno = EBADF;
return -1;
}
errno = 0;
return irp.eof ? 1 : 0;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Helper functions */
/* ------------------------------------------------------------------------------------------------------------------ */
static void _cnh_filehook_init(void)
{
int expected;
if (atomic_load(&_cnh_filehook_initted) > 0) {
return;
}
expected = 0;
if (!atomic_compare_exchange_strong(&_cnh_filehook_init_in_progress, &expected, 1)) {
while (atomic_load(&_cnh_filehook_init_in_progress) > 1) {
util_time_sleep_us(100);
}
return;
}
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
if (atomic_load(&_cnh_filehook_initted) > 0) {
/* Revert init in progress, because nothing to do anymore */
atomic_store(&_cnh_filehook_init_in_progress, 0);
return;
}
_cnh_filehook_real_fopen = (cnh_filehook_fopen_t) cnh_lib_get_func_addr("fopen");
_cnh_filehook_real_fclose = (cnh_filehook_fclose_t) cnh_lib_get_func_addr("fclose");
_cnh_filehook_real_fread = (cnh_filehook_fread_t) cnh_lib_get_func_addr("fread");
_cnh_filehook_real_fwrite = (cnh_filehook_fwrite_t) cnh_lib_get_func_addr("fwrite");
_cnh_filehook_real_fgets = (cnh_filehook_fgets_t) cnh_lib_get_func_addr("fgets");
_cnh_filehook_real_fseek = (cnh_filehook_fseek_t) cnh_lib_get_func_addr("fseek");
_cnh_filehook_real_ftell = (cnh_filehook_ftell_t) cnh_lib_get_func_addr("ftell");
_cnh_filehook_real_feof = (cnh_filehook_feof_t) cnh_lib_get_func_addr("feof");
pthread_mutex_init(&_cnh_filehook_lock, NULL);
atomic_store(&_cnh_filehook_initted, 1);
atomic_store(&_cnh_filehook_init_in_progress, 0);
}
static enum cnh_result _cnh_filehook_invoke_real(struct cnh_filehook_irp *irp)
{
cnh_filehook_fn_t handler;
assert(irp != NULL);
assert(irp->op < _countof(_cnh_filehook_real_handlers));
handler = _cnh_filehook_real_handlers[irp->op];
assert(handler != NULL);
return handler(irp);
}
static enum cnh_result _cnh_filehook_invoke_real_fopen(struct cnh_filehook_irp *irp)
{
FILE* file;
assert(irp != NULL);
file = _cnh_filehook_real_fopen(irp->open_filename, irp->open_mode);
if (!file) {
return cnh_errno_to_result(errno);
}
irp->file = file;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_filehook_invoke_real_fclose(struct cnh_filehook_irp *irp)
{
int res;
assert(irp != NULL);
res = _cnh_filehook_real_fclose(irp->file);
if (res == EOF) {
return cnh_errno_to_result(errno);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_filehook_invoke_real_fread(struct cnh_filehook_irp *irp)
{
size_t read;
assert(irp != NULL);
/* Check if any previous hook modified the buffer and use original parameters otherwise to ensure the original
call is correctly applied to the real function */
if (irp->read.pos != 0 || irp->read.nbytes != irp->orig_read_write_nmemb * irp->orig_read_write_size) {
read = _cnh_filehook_real_fread(&irp->read.bytes[irp->read.pos], irp->read.nbytes - irp->read.pos, 1,
irp->file);
if (read < 1 && ferror(irp->file) != 0) {
return cnh_errno_to_result(errno);
}
irp->read.pos += irp->read.nbytes - irp->read.pos;
} else {
read = _cnh_filehook_real_fread(irp->read.bytes, irp->orig_read_write_size, irp->orig_read_write_nmemb,
irp->file);
if (read < 1 && ferror(irp->file) != 0) {
return cnh_errno_to_result(errno);
}
irp->read.pos = read * irp->orig_read_write_size;
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_filehook_invoke_real_fwrite(struct cnh_filehook_irp *irp)
{
size_t written;
assert(irp != NULL);
/* Check if any previous hook modified the buffer and use original parameters otherwise to ensure the original
call is correctly applied to the real function */
if (irp->write.pos != 0 || irp->write.nbytes != irp->orig_read_write_nmemb * irp->orig_read_write_size) {
written = _cnh_filehook_real_fwrite(&irp->write.bytes[irp->write.pos], irp->write.nbytes - irp->write.pos, 1,
irp->file);
if (written < 1 && ferror(irp->file) != 0) {
return cnh_errno_to_result(errno);
}
irp->write.pos += irp->write.nbytes - irp->write.pos;
} else {
written = _cnh_filehook_real_fwrite(irp->write.bytes, irp->orig_read_write_size, irp->orig_read_write_nmemb,
irp->file);
if (written < 1 && ferror(irp->file) != 0) {
return cnh_errno_to_result(errno);
}
irp->write.pos += written * irp->orig_read_write_size;
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_filehook_invoke_real_fgets(struct cnh_filehook_irp *irp)
{
char* res;
res = _cnh_filehook_real_fgets((char*) irp->read.bytes, irp->read.nbytes, irp->file);
if (res == NULL) {
/* eof */
if (errno == 0) {
irp->read.bytes = NULL;
return CNH_RESULT_SUCCESS;
} else {
return cnh_errno_to_result(errno);
}
} else {
return CNH_RESULT_SUCCESS;
}
}
static enum cnh_result _cnh_filehook_invoke_real_fseek(struct cnh_filehook_irp *irp)
{
int res;
assert(irp != NULL);
res = _cnh_filehook_real_fseek(irp->file, (long) irp->seek_offset, irp->seek_origin);
if (res != 0) {
return cnh_errno_to_result(errno);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_filehook_invoke_real_ftell(struct cnh_filehook_irp *irp)
{
long res;
assert(irp != NULL);
res = _cnh_filehook_real_ftell(irp->file);
if (res < 0) {
return cnh_errno_to_result(errno);
}
irp->tell_offset = (uint64_t) res;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_filehook_invoke_real_feof(struct cnh_filehook_irp *irp)
{
int res;
assert(irp != NULL);
res = _cnh_filehook_real_feof(irp->file);
if (res < 0) {
return cnh_errno_to_result(errno);
}
irp->eof = res > 0;
return CNH_RESULT_SUCCESS;
}
+90
View File
@@ -0,0 +1,90 @@
/**
* Hook module for hooking file related calls, e.g. fopen, fread, fwrite, etc.
*
* Based on original capnhook code for windows by decafcode:
* https://github.com/decafcode/capnhook
*/
#ifndef CAPNHOOK_FILEHOOK_H
#define CAPNHOOK_FILEHOOK_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "capnhook/hook/iobuf.h"
/**
* Available operations to hook
*/
enum cnh_filehook_irp_op {
CNH_FILEHOOK_IRP_OP_OPEN = 0,
CNH_FILEHOOK_IRP_OP_CLOSE = 1,
CNH_FILEHOOK_IRP_OP_READ = 2,
CNH_FILEHOOK_IRP_OP_WRITE = 3,
CNH_FILEHOOK_IRP_OP_FGETS = 4,
CNH_FILEHOOK_IRP_OP_SEEK = 5,
CNH_FILEHOOK_IRP_OP_TELL = 6,
CNH_FILEHOOK_IRP_OP_EOF = 7,
};
/**
* I/O request packet
*/
struct cnh_filehook_irp {
enum cnh_filehook_irp_op op;
size_t next_handler;
FILE* file;
const char *open_filename;
const char* open_mode;
struct cnh_iobuf read;
struct cnh_const_iobuf write;
size_t orig_read_write_size;
size_t orig_read_write_nmemb;
int seek_origin;
int64_t seek_offset;
uint64_t tell_offset;
bool eof;
};
/**
* Hook function type
*/
typedef enum cnh_result (*cnh_filehook_fn_t)(struct cnh_filehook_irp *irp);
/**
* Add a new hook handler to the hook module.
* The handler added is getting called on every hooked operation.
*
* @param fn Pointer to hook function to add
* @return Result/error code if hooking was successful
*/
enum cnh_result cnh_filehook_push_handler(cnh_filehook_fn_t fn);
/**
* Invoke the next hooked function registered in the hook module.
* Call this from your hook handler if you want to pass on execution to further
* hooked calls reaching the original function at the end of the hook chain.
*
* @param irp I/O request package to dispatch
* @return Result of the following function dispatching the request package
*/
enum cnh_result cnh_filehook_invoke_next(struct cnh_filehook_irp *irp);
/**
* Create a dummy file handle. Use this function for file access virtualization.
* Do not execute real operations on the handle returned.
*
* @return Actual file handle instance pointing to a dummy endpoint.
*/
FILE* cnh_filehook_open_dummy_file_handle();
/**
* Close a dummy file handle. Make sure to free your previously allocated handles
* to avoid resource leaks. Do not free them using the real close function.
*
* @param file Dummy file handle to free
*/
void cnh_filehook_close_dummy_file_handle(FILE* file);
#endif
+447
View File
@@ -0,0 +1,447 @@
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include "capnhook/hook/common.h"
#include "capnhook/hook/fshook.h"
#include "capnhook/hook/lib.h"
#include "util/time.h"
/* ------------------------------------------------------------------------------------------------------------------ */
/* Real funcs typedefs */
/* ------------------------------------------------------------------------------------------------------------------ */
typedef DIR* (*cnh_fshook_opendir_t)(const char* name);
typedef int (*cnh_fshook_lxstat_t)(int version, const char* file, struct stat* buf);
typedef int (*cnh_fshook_xstat_t)(int version, const char* file, struct stat* buf);
typedef int (*cnh_fshook_rename_t)(const char* old, const char* new);
typedef int (*cnh_fshook_remove_t)(const char* pathname);
typedef int (*cnh_fshook_access_t)(const char* path, int amode);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private helpers */
/* ------------------------------------------------------------------------------------------------------------------ */
static void _cnh_fshook_init(void);
static enum cnh_result _cnh_fshook_invoke_real(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_fshook_invoke_real_opendir(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_fshook_invoke_real_lxstat(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_fshook_invoke_real_xstat(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_fshook_invoke_real_rename(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_fshook_invoke_real_remove(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_fshook_invoke_real_access(struct cnh_fshook_irp *irp);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private state */
/* ------------------------------------------------------------------------------------------------------------------ */
static cnh_fshook_opendir_t _cnh_fshook_real_opendir;
static cnh_fshook_lxstat_t _cnh_fshook_real_lxstat;
static cnh_fshook_xstat_t _cnh_fshook_real_xstat;
static cnh_fshook_rename_t _cnh_fshook_real_rename;
static cnh_fshook_remove_t _cnh_fshook_real_remove;
static cnh_fshook_access_t _cnh_fshook_real_access;
static const cnh_fshook_fn_t _cnh_fshook_real_handlers[6] = {
[CNH_FSHOOK_IRP_OP_DIR_OPEN] = _cnh_fshook_invoke_real_opendir,
[CNH_FSHOOK_IRP_OP_LXSTAT] = _cnh_fshook_invoke_real_lxstat,
[CNH_FSHOOK_IRP_OP_XSTAT] = _cnh_fshook_invoke_real_xstat,
[CNH_FSHOOK_IRP_OP_RENAME] = _cnh_fshook_invoke_real_rename,
[CNH_FSHOOK_IRP_OP_REMOVE] = _cnh_fshook_invoke_real_remove,
[CNH_FSHOOK_IRP_OP_ACCESS] = _cnh_fshook_invoke_real_access,
};
static atomic_int _cnh_fshook_initted = ATOMIC_VAR_INIT(0);
static atomic_int _cnh_fshook_init_in_progress = ATOMIC_VAR_INIT(0);
static pthread_mutex_t _cnh_fshook_lock;
static cnh_fshook_fn_t *_cnh_fshook_handlers;
static size_t _cnh_fshook_nhandlers;
/* ------------------------------------------------------------------------------------------------------------------ */
/* Public module functions */
/* ------------------------------------------------------------------------------------------------------------------ */
enum cnh_result cnh_fshook_push_handler(cnh_fshook_fn_t fn)
{
cnh_fshook_fn_t *new_array;
size_t new_size;
enum cnh_result result;
assert(fn != NULL);
_cnh_fshook_init();
pthread_mutex_lock(&_cnh_fshook_lock);
new_size = _cnh_fshook_nhandlers + 1;
new_array = realloc(_cnh_fshook_handlers, new_size * sizeof(cnh_fshook_fn_t));
if (new_array != NULL) {
_cnh_fshook_handlers = new_array;
_cnh_fshook_handlers[_cnh_fshook_nhandlers++] = fn;
result = CNH_RESULT_SUCCESS;
} else {
result = CNH_RESULT_OUT_OF_MEMORY;
}
pthread_mutex_unlock(&_cnh_fshook_lock);
return result;
}
enum cnh_result cnh_fshook_invoke_next(struct cnh_fshook_irp *irp)
{
cnh_fshook_fn_t handler;
enum cnh_result result;
assert(irp != NULL);
assert(_cnh_fshook_initted > 0);
pthread_mutex_lock(&_cnh_fshook_lock);
assert(irp->next_handler <= _cnh_fshook_nhandlers);
if (irp->next_handler < _cnh_fshook_nhandlers) {
handler = _cnh_fshook_handlers[irp->next_handler];
irp->next_handler++;
} else {
handler = _cnh_fshook_invoke_real;
irp->next_handler = (size_t) -1;
}
pthread_mutex_unlock(&_cnh_fshook_lock);
result = handler(irp);
if (result != CNH_RESULT_SUCCESS) {
irp->next_handler = (size_t) -1;
}
return result;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooked functions */
/* ------------------------------------------------------------------------------------------------------------------ */
DIR* opendir(const char* name)
{
struct cnh_fshook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_fshook_init();
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FSHOOK_IRP_OP_DIR_OPEN;
irp.opendir_name = name;
irp.opendir_ret = NULL;
result = cnh_fshook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return NULL;
}
errno = 0;
return irp.opendir_ret;
}
int __lxstat(int version, const char* file, struct stat* buf)
{
struct cnh_fshook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_fshook_init();
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FSHOOK_IRP_OP_LXSTAT;
irp.xstat_version = version;
irp.xstat_file = file;
irp.xstat_buf = buf;
result = cnh_fshook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
errno = 0;
return 0;
}
int __xstat(int version, const char* file, struct stat* buf)
{
struct cnh_fshook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_fshook_init();
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FSHOOK_IRP_OP_XSTAT;
irp.xstat_version = version;
irp.xstat_file = file;
irp.xstat_buf = buf;
result = cnh_fshook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
errno = 0;
return 0;
}
int rename(const char* old, const char* new)
{
struct cnh_fshook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_fshook_init();
if (old == NULL || new == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FSHOOK_IRP_OP_RENAME;
irp.rename_old = old;
irp.rename_new = new;
result = cnh_fshook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
errno = 0;
return 0;
}
int remove(const char* pathname)
{
struct cnh_fshook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_fshook_init();
if (pathname == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FSHOOK_IRP_OP_REMOVE;
irp.remove_pathname = pathname;
result = cnh_fshook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
errno = 0;
return 0;
}
int access(const char* path, int amode)
{
struct cnh_fshook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_fshook_init();
if (path == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_FSHOOK_IRP_OP_ACCESS;
irp.access_path = path;
irp.access_amode = amode;
result = cnh_fshook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
errno = 0;
return 0;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Helper functions */
/* ------------------------------------------------------------------------------------------------------------------ */
static void _cnh_fshook_init(void)
{
int expected;
if (atomic_load(&_cnh_fshook_initted) > 0) {
return;
}
expected = 0;
if (!atomic_compare_exchange_strong(&_cnh_fshook_init_in_progress, &expected, 1)) {
while (atomic_load(&_cnh_fshook_init_in_progress) > 1) {
util_time_sleep_us(100);
}
return;
}
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
if (atomic_load(&_cnh_fshook_initted) > 0) {
/* Revert init in progress, because nothing to do anymore */
atomic_store(&_cnh_fshook_init_in_progress, 0);
return;
}
_cnh_fshook_real_opendir = (cnh_fshook_opendir_t) cnh_lib_get_func_addr("opendir");
_cnh_fshook_real_lxstat = (cnh_fshook_lxstat_t) cnh_lib_get_func_addr("__lxstat");
_cnh_fshook_real_xstat = (cnh_fshook_xstat_t) cnh_lib_get_func_addr("__xstat");
_cnh_fshook_real_rename = (cnh_fshook_rename_t) cnh_lib_get_func_addr("rename");
_cnh_fshook_real_remove = (cnh_fshook_remove_t) cnh_lib_get_func_addr("remove");
_cnh_fshook_real_access = (cnh_fshook_access_t) cnh_lib_get_func_addr("access");
pthread_mutex_init(&_cnh_fshook_lock, NULL);
atomic_store(&_cnh_fshook_initted, 1);
atomic_store(&_cnh_fshook_init_in_progress, 0);
}
static enum cnh_result _cnh_fshook_invoke_real(struct cnh_fshook_irp *irp)
{
cnh_fshook_fn_t handler;
assert(irp != NULL);
assert(irp->op < _countof(_cnh_fshook_real_handlers));
handler = _cnh_fshook_real_handlers[irp->op];
assert(handler != NULL);
return handler(irp);
}
static enum cnh_result _cnh_fshook_invoke_real_opendir(struct cnh_fshook_irp *irp)
{
DIR* dir;
assert(irp != NULL);
dir = _cnh_fshook_real_opendir(irp->opendir_name);
if (dir == NULL) {
return cnh_errno_to_result(errno);
}
irp->opendir_ret = dir;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_fshook_invoke_real_lxstat(struct cnh_fshook_irp *irp)
{
int res;
assert(irp != NULL);
res = _cnh_fshook_real_lxstat(irp->xstat_version, irp->xstat_file, irp->xstat_buf);
if (res != 0) {
return cnh_errno_to_result(errno);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_fshook_invoke_real_xstat(struct cnh_fshook_irp *irp)
{
int res;
assert(irp != NULL);
res = _cnh_fshook_real_xstat(irp->xstat_version, irp->xstat_file, irp->xstat_buf);
if (res != 0) {
return cnh_errno_to_result(errno);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_fshook_invoke_real_rename(struct cnh_fshook_irp *irp)
{
int res;
assert(irp != NULL);
res = _cnh_fshook_real_rename(irp->rename_old, irp->rename_new);
if (res != 0) {
return cnh_errno_to_result(errno);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_fshook_invoke_real_remove(struct cnh_fshook_irp *irp)
{
int res;
assert(irp != NULL);
res = _cnh_fshook_real_remove(irp->remove_pathname);
if (res != 0) {
return cnh_errno_to_result(errno);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_fshook_invoke_real_access(struct cnh_fshook_irp *irp)
{
int res;
assert(irp != NULL);
res = _cnh_fshook_real_access(irp->access_path, irp->access_amode);
if (res != 0) {
return cnh_errno_to_result(errno);
}
return CNH_RESULT_SUCCESS;
}
+72
View File
@@ -0,0 +1,72 @@
/**
* Hook module for hooking file system related calls,
* not counting calls operating on actual opened files, e.g.
* fopen, fread, fwrite etc.
*
* Based on original capnhook code for windows by decafcode:
* https://github.com/decafcode/capnhook
*/
#ifndef CAPNHOOK_FSHOOK_H
#define CAPNHOOK_FSHOOK_H
#include <dirent.h>
#include <stddef.h>
#include <stdint.h>
#include "capnhook/hook/result.h"
/**
* Available operations to hook
*/
enum cnh_fshook_irp_op {
CNH_FSHOOK_IRP_OP_DIR_OPEN = 0,
CNH_FSHOOK_IRP_OP_LXSTAT = 1,
CNH_FSHOOK_IRP_OP_XSTAT = 2,
CNH_FSHOOK_IRP_OP_RENAME = 3,
CNH_FSHOOK_IRP_OP_REMOVE = 4,
CNH_FSHOOK_IRP_OP_ACCESS = 5,
};
/**
* I/O request packet
*/
struct cnh_fshook_irp {
enum cnh_fshook_irp_op op;
size_t next_handler;
const char* opendir_name;
DIR* opendir_ret;
int xstat_version;
const char* xstat_file;
struct stat* xstat_buf;
const char* rename_old;
const char* rename_new;
const char* remove_pathname;
const char* access_path;
int access_amode;
};
/**
* Hook function type
*/
typedef enum cnh_result (*cnh_fshook_fn_t)(struct cnh_fshook_irp *irp);
/**
* Add a new hook handler to the hook module.
* The handler added is getting called on every hooked operation.
*
* @param fn Pointer to hook function to add
* @return Result/error code if hooking was successful
*/
enum cnh_result cnh_fshook_push_handler(cnh_fshook_fn_t fn);
/**
* Invoke the next hooked function registered in the hook module.
* Call this from your hook handler if you want to pass on execution to further
* hooked calls reaching the original function at the end of the hook chain.
*
* @param irp I/O request package to dispatch
* @return Result of the following function dispatching the request package
*/
enum cnh_result cnh_fshook_invoke_next(struct cnh_fshook_irp *irp);
#endif
+253
View File
@@ -0,0 +1,253 @@
#include <assert.h>
#include <string.h>
#include "capnhook/hook/iobuf.h"
void cnh_iobuf_flip(struct cnh_const_iobuf *child, struct cnh_iobuf *parent)
{
assert(child != NULL);
assert(parent != NULL);
child->bytes = parent->bytes;
child->pos = 0;
child->nbytes = parent->pos;
}
size_t cnh_iobuf_move(struct cnh_iobuf *dest, struct cnh_const_iobuf *src)
{
size_t dest_avail;
size_t src_avail;
size_t chunksz;
assert(dest != NULL);
assert(dest->bytes != NULL || dest->nbytes == 0);
assert(dest->pos <= dest->nbytes);
assert(src != NULL);
assert(src->bytes != NULL || src->nbytes == 0);
assert(src->pos <= src->nbytes);
dest_avail = dest->nbytes - dest->pos;
src_avail = src->nbytes - src->pos;
chunksz = dest_avail < src_avail ? dest_avail : src_avail;
memcpy(&dest->bytes[dest->pos], &src->bytes[src->pos], chunksz);
dest->pos += chunksz;
src->pos += chunksz;
return chunksz;
}
size_t cnh_iobuf_shift(struct cnh_iobuf *dest, struct cnh_iobuf *src)
{
struct cnh_const_iobuf span;
assert(dest != NULL);
assert(src != NULL);
cnh_iobuf_flip(&span, src);
cnh_iobuf_move(dest, &span);
memmove(src->bytes, &src->bytes[span.pos], span.nbytes - span.pos);
src->pos -= span.pos;
return span.pos;
}
enum cnh_result cnh_iobuf_read(struct cnh_const_iobuf *src, void *bytes, size_t nbytes)
{
assert(src != NULL);
assert(bytes != NULL || nbytes == 0);
if (src->pos + nbytes > src->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
memcpy(bytes, &src->bytes[src->pos], nbytes);
src->pos += nbytes;
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_read_8(struct cnh_const_iobuf *src, uint8_t *out)
{
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint8_t) > src->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
*out = src->bytes[src->pos++];
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_read_be16(struct cnh_const_iobuf *src, uint16_t *out)
{
uint16_t value;
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint16_t) > src->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
value = src->bytes[src->pos++] << 8;
value |= src->bytes[src->pos++];
*out = value;
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_read_be32(struct cnh_const_iobuf *src, uint32_t *out)
{
uint32_t value;
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint32_t) > src->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
value = src->bytes[src->pos++] << 24;
value |= src->bytes[src->pos++] << 16;
value |= src->bytes[src->pos++] << 8;
value |= src->bytes[src->pos++];
*out = value;
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_read_le16(struct cnh_const_iobuf *src, uint16_t *out)
{
uint16_t value;
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint16_t) > src->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
value = src->bytes[src->pos++];
value |= src->bytes[src->pos++] << 8;
*out = value;
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_read_le32(struct cnh_const_iobuf *src, uint32_t *out)
{
uint32_t value;
assert(src != NULL);
assert(out != NULL);
if (src->pos + sizeof(uint32_t) > src->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
value = src->bytes[src->pos++];
value |= src->bytes[src->pos++] << 8;
value |= src->bytes[src->pos++] << 16;
value |= src->bytes[src->pos++] << 24;
*out = value;
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_write(struct cnh_iobuf *dest, const void *bytes, size_t nbytes)
{
assert(dest != NULL);
assert(bytes != NULL || nbytes == 0);
if (dest->pos + nbytes > dest->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
memcpy(&dest->bytes[dest->pos], bytes, nbytes);
dest->pos += nbytes;
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_write_8(struct cnh_iobuf *dest, uint8_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint8_t) > dest->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
dest->bytes[dest->pos++] = value;
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_write_be16(struct cnh_iobuf *dest, uint16_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint16_t) > dest->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
dest->bytes[dest->pos++] = (uint8_t) (value >> 8);
dest->bytes[dest->pos++] = (uint8_t) value;
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_write_be32(struct cnh_iobuf *dest, uint32_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint32_t) > dest->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
dest->bytes[dest->pos++] = (uint8_t) (value >> 24);
dest->bytes[dest->pos++] = (uint8_t) (value >> 16);
dest->bytes[dest->pos++] = (uint8_t) (value >> 8);
dest->bytes[dest->pos++] = (uint8_t) value;
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_write_le16(struct cnh_iobuf *dest, uint16_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint16_t) > dest->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
dest->bytes[dest->pos++] = (uint8_t) value;
dest->bytes[dest->pos++] = (uint8_t) (value >> 8);
return CNH_RESULT_SUCCESS;
}
enum cnh_result cnh_iobuf_write_le32(struct cnh_iobuf *dest, uint32_t value)
{
assert(dest != NULL);
if (dest->pos + sizeof(uint32_t) > dest->nbytes) {
return CNH_RESULT_ERROR_INSUFFICIENT_BUFFER;
}
dest->bytes[dest->pos++] = (uint8_t) value;
dest->bytes[dest->pos++] = (uint8_t) (value >> 8);
dest->bytes[dest->pos++] = (uint8_t) (value >> 16);
dest->bytes[dest->pos++] = (uint8_t) (value >> 24);
return CNH_RESULT_SUCCESS;
}
+162
View File
@@ -0,0 +1,162 @@
/**
* Implementation of an I/O buffer used by hooking modules
*
* Based on original capnhook code for windows by decafcode:
* https://github.com/decafcode/capnhook
*/
#ifndef CAPNHOOK_IOBUF_H
#define CAPNHOOK_IOBUF_H
#include <stddef.h>
#include <stdint.h>
#include "capnhook/hook/result.h"
/**
* I/O buffer
*/
struct cnh_iobuf {
uint8_t *bytes;
size_t nbytes;
size_t pos;
};
/**
* I/O buffer
*/
struct cnh_const_iobuf {
const uint8_t *bytes;
size_t nbytes;
size_t pos;
};
/**
* Flip the I/O buffer. Set the current pos as size and reset the position.
*
* @param child Flipped I/O buffer
* @param parent I/O buffer to flip
*/
void cnh_iobuf_flip(struct cnh_const_iobuf *child, struct cnh_iobuf *parent);
/**
* Move the contents currently enclosed by pos and end of an I/O buffer to a destination.
*
* @param dest Destination buffer to move the source to, starting at current pos
* @param src Source to move to the destination, starting at current pos up to end of the buffer
* @return Number of bytes copied to destination
*/
size_t cnh_iobuf_move(struct cnh_iobuf *dest, struct cnh_const_iobuf *src);
size_t cnh_iobuf_shift(struct cnh_iobuf *dest, struct cnh_iobuf *src);
/**
* Read data from an I/O buffer
*
* @param src Source to read from (starting at current position)
* @param bytes Target to read to
* @param nbytes Number of bytes to read
* @return Result of operation
*/
enum cnh_result cnh_iobuf_read(struct cnh_const_iobuf *src, void *bytes, size_t nbytes);
/**
* Read a byte from an I/O buffer
*
* @param src Source to read from (starting at current position)
* @param value Pointer to variable to read the data into
* @return Result of operation
*/
enum cnh_result cnh_iobuf_read_8(struct cnh_const_iobuf *src, uint8_t *value);
/**
* Read a 2 byte value, big endian byte order, from an I/O buffer
*
* @param src Source to read from (starting at current position)
* @param value Pointer to variable to read the data into
* @return Result of operation
*/
enum cnh_result cnh_iobuf_read_be16(struct cnh_const_iobuf *src, uint16_t *value);
/**
* Read a 4 byte value, big endian byte order, from an I/O buffer
*
* @param src Source to read from (starting at current position)
* @param value Pointer to variable to read the data into
* @return Result of operation
*/
enum cnh_result cnh_iobuf_read_be32(struct cnh_const_iobuf *src, uint32_t *value);
/**
* Read a 2 byte value, little endian byte order, from an I/O buffer
*
* @param src Source to read from (starting at current position)
* @param value Pointer to variable to read the data into
* @return Result of operation
*/
enum cnh_result cnh_iobuf_read_le16(struct cnh_const_iobuf *src, uint16_t *value);
/**
* Read a 4 byte value, little endian byte order, from an I/O buffer
*
* @param src Source to read from (starting at current position)
* @param value Pointer to variable to read the data into
* @return Result of operation
*/
enum cnh_result cnh_iobuf_read_le32(struct cnh_const_iobuf *src, uint32_t *value);
/**
* Write data to an I/O buffer
*
* @param dest I/O buffer to write to (starting at current position)
* @param bytes Pointer to data to write
* @param nbytes Number of bytes to write
* @return Result of operation
*/
enum cnh_result cnh_iobuf_write(struct cnh_iobuf *dest, const void *bytes, size_t nbytes);
/**
* Write a byte to an I/O buffer
*
* @param dest Destination I/O buffer to write to (starting at current position)
* @param value Value to write
* @return Result of operation
*/
enum cnh_result cnh_iobuf_write_8(struct cnh_iobuf *dest, uint8_t value);
/**
* Write a 2 byte value, big endian byte order, to an I/O buffer
*
* @param dest Destination I/O buffer to write to (starting at current position)
* @param value Value to write
* @return Result of operation
*/
enum cnh_result cnh_iobuf_write_be16(struct cnh_iobuf *dest, uint16_t value);
/**
* Write a 4 byte value, big endian byte order, to an I/O buffer
*
* @param dest Destination I/O buffer to write to (starting at current position)
* @param value Value to write
* @return Result of operation
*/
enum cnh_result cnh_iobuf_write_be32(struct cnh_iobuf *dest, uint32_t value);
/**
* Write a 2 byte value, little endian byte order, to an I/O buffer
*
* @param dest Destination I/O buffer to write to (starting at current position)
* @param value Value to write
* @return Result of operation
*/
enum cnh_result cnh_iobuf_write_le16(struct cnh_iobuf *dest, uint16_t value);
/**
* Write a 4 byte value, little endian byte order, to an I/O buffer
*
* @param dest Destination I/O buffer to write to (starting at current position)
* @param value Value to write
* @return Result of operation
*/
enum cnh_result cnh_iobuf_write_le32(struct cnh_iobuf *dest, uint32_t value);
#endif
+552
View File
@@ -0,0 +1,552 @@
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "capnhook/hook/common.h"
#include "capnhook/hook/iohook.h"
#include "capnhook/hook/lib.h"
#include "util/time.h"
#define INVALID_FILE_DEVICE (-1)
/* ------------------------------------------------------------------------------------------------------------------ */
/* Real funcs typedefs */
/* ------------------------------------------------------------------------------------------------------------------ */
typedef int (*cnh_iohook_open_t)(const char* path, int oflag);
typedef FILE* (*cnh_iohook_fdopen_t)(int fd, const char* mode);
typedef ssize_t (*cnh_iohook_write_t)(int fd, const void* buf, size_t count);
typedef ssize_t (*cnh_iohook_read_t)(int fd, void* buf, size_t count);
typedef off_t (*cnh_iohook_lseek_t)(int fildes, off_t offset, int whence);
typedef int (*cnh_iohook_ioctl_t)(int fd, int request, void* data);
typedef int (*cnh_iohook_close_t)(int fd);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private helpers */
/* ------------------------------------------------------------------------------------------------------------------ */
static void _cnh_iohook_init(void);
static enum cnh_result _cnh_iohook_invoke_real(struct cnh_iohook_irp *irp);
static enum cnh_result _cnh_iohook_invoke_real_open(struct cnh_iohook_irp *irp);
static enum cnh_result _cnh_iohook_invoke_real_fdopen(struct cnh_iohook_irp *irp);
static enum cnh_result _cnh_iohook_invoke_real_close(struct cnh_iohook_irp *irp);
static enum cnh_result _cnh_iohook_invoke_real_read(struct cnh_iohook_irp *irp);
static enum cnh_result _cnh_iohook_invoke_real_write(struct cnh_iohook_irp *irp);
static enum cnh_result _cnh_iohook_invoke_real_seek(struct cnh_iohook_irp *irp);
static enum cnh_result _cnh_iohook_invoke_real_ioctl(struct cnh_iohook_irp *irp);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private state */
/* ------------------------------------------------------------------------------------------------------------------ */
static cnh_iohook_open_t _cnh_iohook_real_open;
static cnh_iohook_fdopen_t _cnh_iohook_real_fdopen;
static cnh_iohook_close_t _cnh_iohook_real_close;
static cnh_iohook_read_t _cnh_iohook_real_read;
static cnh_iohook_write_t _cnh_iohook_real_write;
static cnh_iohook_lseek_t _cnh_iohook_real_lseek;
static cnh_iohook_ioctl_t _cnh_iohook_real_ioctl;
static const cnh_iohook_fn_t _cnh_iohook_real_handlers[7] = {
[CNH_IOHOOK_IRP_OP_OPEN] = _cnh_iohook_invoke_real_open,
[CNH_IOHOOK_IRP_OP_FDOPEN] = _cnh_iohook_invoke_real_fdopen,
[CNH_IOHOOK_IRP_OP_CLOSE] = _cnh_iohook_invoke_real_close,
[CNH_IOHOOK_IRP_OP_READ] = _cnh_iohook_invoke_real_read,
[CNH_IOHOOK_IRP_OP_WRITE] = _cnh_iohook_invoke_real_write,
[CNH_IOHOOK_IRP_OP_SEEK] = _cnh_iohook_invoke_real_seek,
[CNH_IOHOOK_IRP_OP_IOCTL] = _cnh_iohook_invoke_real_ioctl,
};
static atomic_int _cnh_iohook_initted = ATOMIC_VAR_INIT(0);
static atomic_int _cnh_iohook_init_in_progress = ATOMIC_VAR_INIT(0);
static pthread_mutex_t _cnh_iohook_lock;
static cnh_iohook_fn_t *_cnh_iohook_handlers;
static size_t _cnh_iohook_nhandlers;
/* ------------------------------------------------------------------------------------------------------------------ */
/* Public module functions */
/* ------------------------------------------------------------------------------------------------------------------ */
enum cnh_result cnh_iohook_push_handler(cnh_iohook_fn_t fn)
{
cnh_iohook_fn_t *new_array;
size_t new_size;
enum cnh_result result;
assert(fn != NULL);
_cnh_iohook_init();
pthread_mutex_lock(&_cnh_iohook_lock);
new_size = _cnh_iohook_nhandlers + 1;
new_array = realloc(_cnh_iohook_handlers, new_size * sizeof(cnh_iohook_fn_t));
if (new_array != NULL) {
_cnh_iohook_handlers = new_array;
_cnh_iohook_handlers[_cnh_iohook_nhandlers++] = fn;
result = CNH_RESULT_SUCCESS;
} else {
result = CNH_RESULT_OUT_OF_MEMORY;
}
pthread_mutex_unlock(&_cnh_iohook_lock);
return result;
}
enum cnh_result cnh_iohook_invoke_next(struct cnh_iohook_irp *irp)
{
cnh_iohook_fn_t handler;
enum cnh_result result;
assert(irp != NULL);
assert(_cnh_iohook_initted > 0);
pthread_mutex_lock(&_cnh_iohook_lock);
assert(irp->next_handler <= _cnh_iohook_nhandlers);
if (irp->next_handler < _cnh_iohook_nhandlers) {
handler = _cnh_iohook_handlers[irp->next_handler];
irp->next_handler++;
} else {
handler = _cnh_iohook_invoke_real;
irp->next_handler = (size_t) -1;
}
pthread_mutex_unlock(&_cnh_iohook_lock);
result = handler(irp);
if (result != CNH_RESULT_SUCCESS) {
irp->next_handler = (size_t) -1;
}
return result;
}
int cnh_iohook_open_dummy_fd()
{
_cnh_iohook_init();
/* We need to return some sort of valid FD to the
caller, since we're simulating a device that
doesn't exist. Open an FD on /dev/null. */
/* Open read only */
return _cnh_iohook_real_open("/dev/null", 0);
}
void cnh_iohook_close_dummy_fd(int fd)
{
_cnh_iohook_init();
/* Avoid hooking pipeline when using dummy handles */
_cnh_iohook_real_close(fd);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooked functions */
/* ------------------------------------------------------------------------------------------------------------------ */
int open(const char* path, int oflag)
{
struct cnh_iohook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_iohook_init();
if (path == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return INVALID_FILE_DEVICE;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_IOHOOK_IRP_OP_OPEN;
irp.fd = INVALID_FILE_DEVICE;
irp.open_filename = path;
irp.open_flags = oflag;
result = cnh_iohook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return INVALID_FILE_DEVICE;
}
errno = 0;
return irp.fd;
}
FILE* fdopen(int fd, const char* mode)
{
struct cnh_iohook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_iohook_init();
if (fd == -1 || mode == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return NULL;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_IOHOOK_IRP_OP_FDOPEN;
irp.fdopen_fd = fd;
irp.fdopen_mode = mode;
irp.fdopen_res = NULL;
result = cnh_iohook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return NULL;
}
errno = 0;
return irp.fdopen_res;
}
int close(int fd)
{
struct cnh_iohook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_iohook_init();
if (fd == INVALID_FILE_DEVICE) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_IOHOOK_IRP_OP_CLOSE;
irp.fd = fd;
result = cnh_iohook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
errno = 0;
return 0;
}
ssize_t read(int fd, void* buf, size_t count)
{
struct cnh_iohook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_iohook_init();
if (fd == INVALID_FILE_DEVICE || buf == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_IOHOOK_IRP_OP_READ;
irp.fd = fd;
irp.read.bytes = buf;
irp.read.nbytes = count;
irp.read.pos = 0;
result = cnh_iohook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
assert(irp.read.pos <= irp.read.nbytes);
errno = 0;
return irp.read.pos;
}
ssize_t write(int fd, const void* buf, size_t count)
{
struct cnh_iohook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_iohook_init();
if (fd == INVALID_FILE_DEVICE || buf == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_IOHOOK_IRP_OP_WRITE;
irp.fd = fd;
irp.write.bytes = buf;
irp.write.nbytes = count;
irp.write.pos = 0;
result = cnh_iohook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
assert(irp.write.pos <= irp.write.nbytes);
errno = 0;
return irp.write.pos;
}
off_t lseek(int fd, off_t offset, int whence)
{
struct cnh_iohook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_iohook_init();
if (fd == INVALID_FILE_DEVICE) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_IOHOOK_IRP_OP_SEEK;
irp.fd = fd;
irp.seek_origin = whence;
irp.seek_offset = offset;
result = cnh_iohook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
errno = 0;
return (off_t) irp.seek_pos;
}
int ioctl(int fd, int request, void* data)
{
struct cnh_iohook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_iohook_init();
if (fd == INVALID_FILE_DEVICE) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
return -1;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_IOHOOK_IRP_OP_IOCTL;
irp.fd = fd;
irp.ioctl_req = request;
irp.ioctl.bytes = data;
result = cnh_iohook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return -1;
}
errno = 0;
return irp.ioctl.pos;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Helper functions */
/* ------------------------------------------------------------------------------------------------------------------ */
static void _cnh_iohook_init(void)
{
int expected;
if (atomic_load(&_cnh_iohook_initted) > 0) {
return;
}
expected = 0;
if (!atomic_compare_exchange_strong(&_cnh_iohook_init_in_progress, &expected, 1)) {
while (atomic_load(&_cnh_iohook_init_in_progress) > 1) {
util_time_sleep_us(100);
}
return;
}
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
if (atomic_load(&_cnh_iohook_initted) > 0) {
/* Revert init in progress, because nothing to do anymore */
atomic_store(&_cnh_iohook_init_in_progress, 0);
return;
}
_cnh_iohook_real_open = (cnh_iohook_open_t) cnh_lib_get_func_addr("open");
_cnh_iohook_real_fdopen = (cnh_iohook_fdopen_t) cnh_lib_get_func_addr("fdopen");
_cnh_iohook_real_close = (cnh_iohook_close_t) cnh_lib_get_func_addr("close");
_cnh_iohook_real_read = (cnh_iohook_read_t) cnh_lib_get_func_addr("read");
_cnh_iohook_real_write = (cnh_iohook_write_t) cnh_lib_get_func_addr("write");
_cnh_iohook_real_lseek = (cnh_iohook_lseek_t) cnh_lib_get_func_addr("lseek");
_cnh_iohook_real_ioctl = (cnh_iohook_ioctl_t) cnh_lib_get_func_addr("ioctl");
pthread_mutex_init(&_cnh_iohook_lock, NULL);
atomic_store(&_cnh_iohook_initted, 1);
atomic_store(&_cnh_iohook_init_in_progress, 0);
}
static enum cnh_result _cnh_iohook_invoke_real(struct cnh_iohook_irp *irp)
{
cnh_iohook_fn_t handler;
assert(irp != NULL);
assert(irp->op < _countof(_cnh_iohook_real_handlers));
handler = _cnh_iohook_real_handlers[irp->op];
assert(handler != NULL);
return handler(irp);
}
static enum cnh_result _cnh_iohook_invoke_real_open(struct cnh_iohook_irp *irp)
{
int fd;
assert(irp != NULL);
fd = _cnh_iohook_real_open(irp->open_filename, irp->open_flags);
if (fd == INVALID_FILE_DEVICE) {
return cnh_errno_to_result(errno);
}
irp->fd = fd;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_iohook_invoke_real_fdopen(struct cnh_iohook_irp *irp)
{
FILE* file;
assert(irp != NULL);
file = _cnh_iohook_real_fdopen(irp->fdopen_fd, irp->fdopen_mode);
if (file == NULL) {
return cnh_errno_to_result(errno);
}
irp->fdopen_res = file;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_iohook_invoke_real_close(struct cnh_iohook_irp *irp)
{
int ok;
assert(irp != NULL);
ok = _cnh_iohook_real_close(irp->fd);
if (ok < 0) {
return cnh_errno_to_result(errno);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_iohook_invoke_real_read(struct cnh_iohook_irp *irp)
{
ssize_t read;
assert(irp != NULL);
read = _cnh_iohook_real_read(irp->fd, &irp->read.bytes[irp->read.pos], irp->read.nbytes - irp->read.pos);
if (read < 0) {
return cnh_errno_to_result(errno);
}
irp->read.pos += read;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_iohook_invoke_real_write(struct cnh_iohook_irp *irp)
{
ssize_t written;
assert(irp != NULL);
written = _cnh_iohook_real_write(irp->fd, &irp->write.bytes[irp->write.pos], irp->write.nbytes - irp->write.pos);
if (written < 0) {
return cnh_errno_to_result(errno);
}
irp->write.pos += written;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_iohook_invoke_real_seek(struct cnh_iohook_irp *irp)
{
off_t result;
assert(irp != NULL);
result = _cnh_iohook_real_lseek(irp->fd, (off_t) irp->seek_offset, irp->seek_origin);
if (result < 0) {
return cnh_errno_to_result(errno);
}
irp->seek_pos = result;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_iohook_invoke_real_ioctl(struct cnh_iohook_irp *irp)
{
int result;
assert(irp != NULL);
result = _cnh_iohook_real_ioctl(irp->fd, irp->ioctl_req, irp->ioctl.bytes);
if (result < 0) {
return cnh_errno_to_result(errno);
}
irp->ioctl.pos = (size_t) result;
return CNH_RESULT_SUCCESS;
}
+90
View File
@@ -0,0 +1,90 @@
/**
* Hook module for hooking I/O related calls, e.g. open, read, write etc.
*
* Based on original capnhook code for windows by decafcode:
* https://github.com/decafcode/capnhook
*/
#ifndef CAPNHOOK_IOHOOK_H
#define CAPNHOOK_IOHOOK_H
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "capnhook/hook/iobuf.h"
/**
* Available operations to hook
*/
enum cnh_iohook_irp_op {
CNH_IOHOOK_IRP_OP_OPEN = 0,
CNH_IOHOOK_IRP_OP_FDOPEN = 1,
CNH_IOHOOK_IRP_OP_CLOSE = 2,
CNH_IOHOOK_IRP_OP_READ = 3,
CNH_IOHOOK_IRP_OP_WRITE = 4,
CNH_IOHOOK_IRP_OP_SEEK = 5,
CNH_IOHOOK_IRP_OP_IOCTL = 6,
};
/**
* I/O request packet
*/
struct cnh_iohook_irp {
enum cnh_iohook_irp_op op;
size_t next_handler;
int fd;
const char *open_filename;
int open_flags;
int fdopen_fd;
const char* fdopen_mode;
FILE* fdopen_res;
struct cnh_iobuf read;
struct cnh_const_iobuf write;
int seek_origin;
int64_t seek_offset;
uint64_t seek_pos;
int ioctl_req;
struct cnh_iobuf ioctl;
};
/**
* Hook function type
*/
typedef enum cnh_result (*cnh_iohook_fn_t)(struct cnh_iohook_irp *irp);
/**
* Add a new hook handler to the hook module.
* The handler added is getting called on every hooked operation.
*
* @param fn Pointer to hook function to add
* @return Result/error code if hooking was successful
*/
enum cnh_result cnh_iohook_push_handler(cnh_iohook_fn_t fn);
/**
* Invoke the next hooked function registered in the hook module.
* Call this from your hook handler if you want to pass on execution to further
* hooked calls reaching the original function at the end of the hook chain.
*
* @param irp I/O request package to dispatch
* @return Result of the following function dispatching the request package
*/
enum cnh_result cnh_iohook_invoke_next(struct cnh_iohook_irp *irp);
/**
* Create a dummy file handle. Use this function for file access virtualization.
* Do not execute real operations on the handle returned.
*
* @return Actual file handle instance pointing to a dummy endpoint.
*/
int cnh_iohook_open_dummy_fd();
/**
* Close a dummy file handle. Make sure to free your previously allocated handles
* to avoid resource leaks. Do not free them using the real close function.
*
* @param file Dummy file handle to free
*/
void cnh_iohook_close_dummy_fd(int fd);
#endif
+57
View File
@@ -0,0 +1,57 @@
/**
* Provides a set of macros to hook into an application right before its
* main function is called.
*/
#ifndef CNH_LIB_MAIN_H
#define CNH_LIB_MAIN_H
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include "util/log.h"
#define LIB_MAIN_CONSTRUCTOR(func) __attribute__((constructor)) void lib_constructor() { func(); }
#define LIB_MAIN_DESTRUCTOR(func) __attribute__((destructor)) void lib_destructor() { func(); }
#define LIB_MAIN_TRAP_MAIN(func_before_main, func_after_main) \
typedef int (*func_libc_start_main_t)(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end)); \
typedef int* (*func_main_t)(int, char**, char**); \
typedef void (*func_init_t)(void); \
static func_main_t orig_main; \
static func_init_t orig_init; \
static bool trapped; \
\
int* trap_main(int argc, char** argv, char** envp) \
{ \
log_debug("Trap before main (%s): argc %d", argv[0], argc); \
func_before_main(argc, argv); \
log_debug("Calling orig_init"); \
orig_init(); \
log_debug("Calling real main (%s): argc %d", argv[0], argc); \
int* ret = orig_main(argc, argv, envp); \
log_debug("Calling cleanup after main (%s): ret %d", argv[0], ret); \
func_after_main(); \
return ret; \
} \
\
void dummy_init(void) {} \
\
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end)) \
{ \
func_libc_start_main_t start = (func_libc_start_main_t) dlsym(RTLD_NEXT, "__libc_start_main"); \
if (start == NULL) { \
printf("ERROR finding orig func __libc_start_main\n"); \
} \
orig_main = main; \
main = trap_main; \
orig_init = init; \
init = dummy_init; \
trapped = true; \
return (*start)(main, argc, ubp_av, init, fini, rtld_fini, stack_end); \
}
#endif
+179
View File
@@ -0,0 +1,179 @@
#define LOG_MODULE "hook-lib"
#include <dlfcn.h>
#include "capnhook/hook/lib.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/rand.h"
typedef void* (*cnh_lib_load_t)(const char* lib);
typedef void* (*cnh_lib_get_func_addr_handle_t)(void* lib_handle, const char* func_name);
typedef void (*cnh_lib_unload_t)(void* handle);
typedef void* (*cnh_lib_get_func_addr_t)(const char* func_name);
static void* _cnh_lib_load(const char* lib);
static void* _cnh_lib_get_func_addr_handle(void* lib_handle, const char* func_name);
static void _cnh_lib_unload(void* handle);
static void* _cnh_lib_get_func_addr(const char* func_name);
static void* _cnh_lib_load_test(const char* lib);
static void* _cnh_lib_get_func_addr_handle_test(void* lib_handle, const char* func_name);
static void _cnh_lib_unload_test(void* handle);
static void* _cnh_lib_get_func_addr_test(const char* func_name);
struct cnh_lib_interface {
cnh_lib_load_t load;
cnh_lib_get_func_addr_handle_t get_func_addr_handle;
cnh_lib_unload_t unload;
cnh_lib_get_func_addr_t get_func_addr;
};
static struct cnh_lib_interface _cnh_lib_interface = {
.load = _cnh_lib_load,
.get_func_addr_handle = _cnh_lib_get_func_addr_handle,
.unload = _cnh_lib_unload,
.get_func_addr = _cnh_lib_get_func_addr,
};
static struct cnh_lib_unit_test_func_mocks* _cnh_lib_func_mocks;
size_t _cnh_lib_func_mocks_cnt;
static void* _cnh_lib_load(const char* lib)
{
void* so = dlopen(lib, RTLD_NOW);
if (so == NULL) {
log_warn("Failed to open library %s: %s", lib, dlerror());
} else {
log_debug("Opened lib %s, handle %p", lib, so);
}
return so;
}
static void* _cnh_lib_get_func_addr_handle(void* lib_handle, const char* func_name)
{
void* ret = dlsym(lib_handle, func_name);
if (ret == NULL) {
log_warn("Could not find func %s in library %p", func_name, lib_handle);
}
return ret;
}
static void _cnh_lib_unload(void* handle)
{
dlclose(handle);
log_debug("Closed lib handle %p", handle);
}
static void* _cnh_lib_get_func_addr(const char* func_name)
{
void* ret = dlsym(RTLD_NEXT, func_name);
if (ret == NULL) {
log_warn("Could not find func %s", func_name);
}
return ret;
}
static void* _cnh_lib_load_test(const char* lib)
{
log_warn("Loading lib %s not supported in test", lib);
if (sizeof(void*) == 4) {
return (void*) util_rand_gen_32();
} else {
log_die_illegal_state();
}
}
static void* _cnh_lib_get_func_addr_handle_test(void* lib_handle, const char* func_name)
{
return _cnh_lib_get_func_addr_test(func_name);
}
static void _cnh_lib_unload_test(void* handle)
{
log_warn("Unloading lib %p not supported in test", handle);
}
static void* _cnh_lib_get_func_addr_test(const char* func_name)
{
for (size_t i = 0; i < _cnh_lib_func_mocks_cnt; i++) {
if (!strcmp(_cnh_lib_func_mocks[i].name, func_name)) {
return _cnh_lib_func_mocks->func;
}
}
log_warn("Could not find func %s", func_name);
return NULL;
}
struct cnh_lib_unit_test_func_mocks* cnh_lib_allocate_func_mocks(size_t count)
{
log_assert(count >= 0);
struct cnh_lib_unit_test_func_mocks* func_mocks;
func_mocks = util_xmalloc(sizeof(struct cnh_lib_unit_test_func_mocks) * count);
memset(func_mocks, 0, sizeof(struct cnh_lib_unit_test_func_mocks) * count);
return func_mocks;
}
void cnh_lib_init_unit_test(struct cnh_lib_unit_test_func_mocks* func_mocks, size_t func_mocks_cnt)
{
log_warn("Setting up %d unit test func mocks", func_mocks_cnt);
_cnh_lib_func_mocks = func_mocks;
_cnh_lib_func_mocks_cnt = func_mocks_cnt;
_cnh_lib_interface.load = _cnh_lib_load_test;
_cnh_lib_interface.get_func_addr_handle = _cnh_lib_get_func_addr_handle_test;
_cnh_lib_interface.unload = _cnh_lib_unload_test;
_cnh_lib_interface.get_func_addr = _cnh_lib_get_func_addr_test;
}
void cnh_lib_init()
{
log_warn("Setting up real interface");
_cnh_lib_interface.load = _cnh_lib_load;
_cnh_lib_interface.get_func_addr_handle = _cnh_lib_get_func_addr_handle;
_cnh_lib_interface.unload = _cnh_lib_unload;
_cnh_lib_interface.get_func_addr = _cnh_lib_get_func_addr;
}
void cnh_lib_shutdown_unit_test()
{
log_assert(_cnh_lib_func_mocks);
util_xfree((void**) &_cnh_lib_func_mocks);
}
void* cnh_lib_load(const char* lib)
{
return _cnh_lib_interface.load(lib);
}
void* cnh_lib_get_func_addr_handle(void* lib_handle, const char* func_name)
{
return _cnh_lib_interface.get_func_addr_handle(lib_handle, func_name);
}
void cnh_lib_unload(void* handle)
{
_cnh_lib_interface.unload(handle);
}
void* cnh_lib_get_func_addr(const char* func_name)
{
return _cnh_lib_interface.get_func_addr(func_name);
}
+84
View File
@@ -0,0 +1,84 @@
/**
* Various functions related to library hooking
*/
#ifndef CAPNHOOK_LIB_H
#define CAPNHOOK_LIB_H
#include <stdlib.h>
/**
* Provide a mock function key'd by it's name and pointing to your actual mock implementation.
*/
struct cnh_lib_unit_test_func_mocks {
const char* name;
void* func;
};
/**
* Allocate memory for a list of mock functions to pass to cnh_lib_init_unit_test.
*
* @param count The size of the list.
* @return Allocated memory with the specified number of entries.
*/
struct cnh_lib_unit_test_func_mocks* cnh_lib_allocate_func_mocks(size_t count);
/**
* Initialize this module when using it in a unit-test. This allows you to add function mocks that are returned on the
* various calls to get function pointers from real library functions. Use this to mock them to test various patching
* modules.
*
* Use the cnh_lib_allocate_func_mocks function to allocate a list that you can fill in.
*
* @param func_mocks A list of functions mocks with function names and pointers to the test mocks you set up. Ensure
* that the function signatures match, otherwise things might crash when getting called. The module
* will cleanup the memory allocated of this for you.
* @param func_mocks_cnt The length of the list of func_mocks.
*/
void cnh_lib_init_unit_test(struct cnh_lib_unit_test_func_mocks* func_mocks, size_t func_mocks_cnt);
/**
* Optional, used for unit testing. Switch to the default backend that utilizes loading real libraries and getting real
* function pointers from them. This is enabled by default.
*/
void cnh_lib_init();
/**
* Cleanup any memory allocated/used on cnh_lib_init_unit_test.
*/
void cnh_lib_shutdown_unit_test();
/**
* Load a dynamic library
*
* @param lib Path to library file to load
* @return Pointer to a handle on success, NULL on failure
*/
void* cnh_lib_load(const char* lib);
/**
* Get the address of a function from a library
*
* @param lib_handle Handle of an opened library
* @param func_name Name of the function to get
* @return Function ptr on success, false on error (function not found)
*/
void*cnh_lib_get_func_addr_handle(void* lib_handle, const char* func_name);
/**
* Unload a loaded dynamic library
*
* @param handle Handle of the loaded lib to unload
*/
void cnh_lib_unload(void* handle);
/**
* Get the adress of a function (from a dynamic library)
*
* Note: This will return the first function found matching the specified name
*
* @param func_name Name of the function
* @return Valid ptr to function on success, false on error (not found)
*/
void* cnh_lib_get_func_addr(const char* func_name);
#endif
+99
View File
@@ -0,0 +1,99 @@
#define LOG_MODULE "capnhook"
#include <errno.h>
#include "capnhook/hook/result.h"
#include "util/log.h"
int cnh_result_to_errno(enum cnh_result result)
{
switch (result) {
case CNH_RESULT_SUCCESS:
return 0;
case CNH_RESULT_ERROR_INSUFFICIENT_BUFFER:
return EIO;
case CNH_RESULT_INVALID_PARAMETER:
return EINVAL;
case CNH_RESULT_OUT_OF_MEMORY:
return ENOMEM;
case CNH_RESULT_REPEAT_OPERATION:
return EAGAIN;
case CNH_RESULT_NO_SUCH_FILE_OR_DIR:
return ENOENT;
case CNH_RESULT_NOT_TTY:
return ENOTTY;
case CNH_RESULT_BUSY:
return EBUSY;
case CNH_RESULT_IS_A_DIRECTORY:
return EISDIR;
case CNH_RESULT_PERMISSION_DENIED:
return EACCES;
case CNH_RESULT_DIRECTORY_NOT_EMPTY:
return ENOTEMPTY;
case CNH_RESULT_OPERATION_NOT_PERMITTED:
return EPERM;
case CNH_RESULT_NO_SUCH_DEVICE:
return ENODEV;
case CNH_RESULT_BAD_FILE_NUMBER:
return EBADF;
case CNH_RESULT_FILE_DESCRIPTOR_IN_BAD_STATE:
return EBADFD;
case CNH_RESULT_VALUE_TOO_LARGE_FOR_DEFINED_DATATYPE:
return EOVERFLOW;
case CNH_RESULT_NO_SUCH_DEVICE_OR_ADDRESS:
return ENXIO;
case CNH_RESULT_BROKEN_PIPE:
return EPIPE;
case CNH_RESULT_TIMER_EXPIRED:
return ETIME;
default:
log_warn("Unhandled other error %d to errno convert might cause bugs", result);
return EIO;
}
}
enum cnh_result cnh_errno_to_result(int errn)
{
switch (errn) {
case 0:
return CNH_RESULT_SUCCESS;
case EINVAL:
return CNH_RESULT_INVALID_PARAMETER;
case ENOMEM:
return CNH_RESULT_OUT_OF_MEMORY;
case EAGAIN:
return CNH_RESULT_REPEAT_OPERATION;
case ENOENT:
return CNH_RESULT_NO_SUCH_FILE_OR_DIR;
case ENOTTY:
return CNH_RESULT_NOT_TTY;
case EBUSY:
return CNH_RESULT_BUSY;
case EISDIR:
return CNH_RESULT_IS_A_DIRECTORY;
case EACCES:
return CNH_RESULT_PERMISSION_DENIED;
case ENOTEMPTY:
return CNH_RESULT_DIRECTORY_NOT_EMPTY;
case EPERM:
return CNH_RESULT_OPERATION_NOT_PERMITTED;
case ENODEV:
return CNH_RESULT_NO_SUCH_DEVICE;
case EBADF:
return CNH_RESULT_BAD_FILE_NUMBER;
case EBADFD:
return CNH_RESULT_FILE_DESCRIPTOR_IN_BAD_STATE;
case EOVERFLOW:
return CNH_RESULT_VALUE_TOO_LARGE_FOR_DEFINED_DATATYPE;
case ENXIO:
return CNH_RESULT_NO_SUCH_DEVICE_OR_ADDRESS;
case EPIPE:
return CNH_RESULT_BROKEN_PIPE;
case ETIME:
return CNH_RESULT_TIMER_EXPIRED;
default:
log_warn("Unhandled errno to general error convert might cause bugs, errno: %d", errn);
return CNH_RESULT_OTHER_ERROR;
}
}
+52
View File
@@ -0,0 +1,52 @@
/**
* Results for returning errors on hook modules, converting them to errno and vice versa
*
* Based on original capnhook code for windows by decafcode:
* https://github.com/decafcode/capnhook
*/
#ifndef CAPNHOOK_RESULT_H
#define CAPNHOOK_RESULT_H
/**
* Result values for hook functions
*/
enum cnh_result {
CNH_RESULT_SUCCESS = 0,
CNH_RESULT_ERROR_INSUFFICIENT_BUFFER = 1,
CNH_RESULT_INVALID_PARAMETER = 2,
CNH_RESULT_OUT_OF_MEMORY = 3,
CNH_RESULT_REPEAT_OPERATION = 4,
CNH_RESULT_NO_SUCH_FILE_OR_DIR = 5,
CNH_RESULT_OTHER_ERROR = 6,
CNH_RESULT_NOT_TTY = 7,
CNH_RESULT_BUSY = 8,
CNH_RESULT_IS_A_DIRECTORY = 9,
CNH_RESULT_PERMISSION_DENIED = 10,
CNH_RESULT_DIRECTORY_NOT_EMPTY = 11,
CNH_RESULT_OPERATION_NOT_PERMITTED = 12,
CNH_RESULT_NO_SUCH_DEVICE = 13,
CNH_RESULT_BAD_FILE_NUMBER = 14,
CNH_RESULT_FILE_DESCRIPTOR_IN_BAD_STATE = 15,
CNH_RESULT_VALUE_TOO_LARGE_FOR_DEFINED_DATATYPE = 16,
CNH_RESULT_NO_SUCH_DEVICE_OR_ADDRESS = 17,
CNH_RESULT_BROKEN_PIPE = 18,
CNH_RESULT_TIMER_EXPIRED = 19,
};
/**
* Convert a hook function result to errno
*
* @param result Result to convert
* @return errno value
*/
int cnh_result_to_errno(enum cnh_result result);
/**
* Convert an errno value to a hook function result
*
* @param errn errno value to convert
* @return Hook function result
*/
enum cnh_result cnh_errno_to_result(int errn);
#endif
+58
View File
@@ -0,0 +1,58 @@
#define LOG_MODULE "cnh-sig"
#include "capnhook/hook/lib.h"
#include "capnhook/hook/sig.h"
#include "util/log.h"
typedef int (*cnh_sig_handler_sigaction_t) (int sig, const struct sigaction* act, struct sigaction* oact);
static void _cnh_signal_handler(int sig);
static cnh_sig_handler_sigaction_t _cnh_sig_handler_real_sigaction;
static cnh_sig_handler_t _cnh_signal_handlers[32];
static __sighandler_t _cnh_signal_orig_handlers[32];
void cnh_sig_install(int sig, cnh_sig_handler_t handler)
{
if (sig < 0 || sig >= 32) {
log_die("Can't register handler for invalid signal number %d", sig);
}
/* Ensure that the application does not install it's own handler */
_cnh_signal_handlers[sig] = handler;
/* Install a little detour as our signal handler */
signal(sig, _cnh_signal_handler);
log_info("Installed signal handler %p for %d", handler, sig);
}
int sigaction(int sig, const struct sigaction* act, struct sigaction* oact)
{
if (_cnh_signal_handlers[sig]) {
log_info("Enforce own signal handler on signal: %d", sig);
/* save the original handler, we might need it */
_cnh_signal_orig_handlers[sig] = ((struct sigaction*) act)->sa_handler;
/* swap */
((struct sigaction*) act)->sa_handler = (__sighandler_t) _cnh_signal_handler;
log_debug("Original handler %p for signal %d", _cnh_signal_orig_handlers[sig], sig);
}
if (!_cnh_sig_handler_real_sigaction) {
_cnh_sig_handler_real_sigaction = (cnh_sig_handler_sigaction_t) cnh_lib_get_func_addr("sigaction");
}
return _cnh_sig_handler_real_sigaction(sig, act, oact);
}
static void _cnh_signal_handler(int sig)
{
if (_cnh_signal_handlers[sig]) {
_cnh_signal_handlers[sig](sig, _cnh_signal_orig_handlers[sig]);
}
}
+26
View File
@@ -0,0 +1,26 @@
/**
* Hook and intercept signal handlers
*/
#ifndef CAPNHOOK_SIG_H
#define CAPNHOOK_SIG_H
#include <signal.h>
/**
* Special callback for signal allowing access to a signal handler installed
* by the application
*
* @param signal Signal to handle
* @param orig_handler Original handler of application or NULL if non exists
*/
typedef void (*cnh_sig_handler_t)(int signal, __sighandler_t orig_handler);
/**
* Install our own signal handler (and possibly override existing handlers)
*
* @param sig Signal to handle
* @param handler Handler to install for specified signal
*/
void cnh_sig_install(int sig, cnh_sig_handler_t handler);
#endif
+624
View File
@@ -0,0 +1,624 @@
#define LOG_MODULE "cnh-usbhook"
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdatomic.h>
#include <string.h>
#include <usb.h>
#include "capnhook/hook/common.h"
#include "capnhook/hook/lib.h"
#include "capnhook/hook/usbhook.h"
#include "util/time.h"
/* ------------------------------------------------------------------------------------------------------------------ */
/* Real funcs typedefs */
/* ------------------------------------------------------------------------------------------------------------------ */
typedef int (*cnh_usbhook_usb_init_t)(void);
typedef int (*cnh_usbhook_usb_find_devices_t)(void);
typedef int (*cnh_usbhook_usb_find_busses_t)(void);
typedef usb_dev_handle* (*cnh_usbhook_usb_open_t)(struct usb_device* dev);
typedef int (*cnh_usbhook_usb_close_t)(usb_dev_handle* dev);
typedef int (*cnh_usbhook_usb_reset_t)(usb_dev_handle* dev);
typedef int (*cnh_usbhook_usb_set_altinterface_t)(usb_dev_handle* dev, int interface);
typedef int (*cnh_usbhook_usb_set_configuration_t)(usb_dev_handle* dev, int configuration);
typedef int (*cnh_usbhook_usb_claim_interface_t)(usb_dev_handle* dev, int interface);
typedef int (*cnh_usbhook_usb_control_msg_t)(usb_dev_handle* dev, int requesttype, int request, int value, int index,
char* bytes, int nbytes, int timeout);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private helpers */
/* ------------------------------------------------------------------------------------------------------------------ */
static void _cnh_usbhook_init(void);
static enum cnh_result _cnh_usbhook_invoke_real(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbhook_invoke_real_init(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbhook_invoke_real_find_busses(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbhook_invoke_real_find_devices(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbhook_invoke_real_open(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbhook_invoke_real_close(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbhook_invoke_real_reset(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbhook_invoke_real_set_altinterface(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbhook_invoke_real_set_configuration(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbhook_invoke_real_claim_interface(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbhook_invoke_real_ctrl_msg(struct cnh_usbhook_irp* irp);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private state */
/* ------------------------------------------------------------------------------------------------------------------ */
static cnh_usbhook_usb_init_t _cnh_usbhook_real_init;
static cnh_usbhook_usb_find_devices_t _cnh_usbhook_real_find_devices;
static cnh_usbhook_usb_find_busses_t _cnh_usbhook_real_find_busses;
static cnh_usbhook_usb_open_t _cnh_usbhook_real_open;
static cnh_usbhook_usb_close_t _cnh_usbhook_real_close;
static cnh_usbhook_usb_reset_t _cnh_usbhook_real_reset;
static cnh_usbhook_usb_set_altinterface_t _cnh_usbhook_real_set_altinterface;
static cnh_usbhook_usb_set_configuration_t _cnh_usbhook_real_set_configuration;
static cnh_usbhook_usb_claim_interface_t _cnh_usbhook_real_claim_interface;
static cnh_usbhook_usb_control_msg_t _cnh_usbhook_real_control_msg;
static const cnh_usbhook_fn_t _cnh_usbhook_real_handlers[10] = {
[CNH_USBHOOK_IRP_OP_INIT] = _cnh_usbhook_invoke_real_init,
[CNH_USBHOOK_IRP_OP_FIND_BUSSES ]= _cnh_usbhook_invoke_real_find_busses,
[CNH_USBHOOK_IRP_OP_FIND_DEVICES] = _cnh_usbhook_invoke_real_find_devices,
[CNH_USBHOOK_IRP_OP_OPEN] = _cnh_usbhook_invoke_real_open,
[CNH_USBHOOK_IRP_OP_CLOSE] = _cnh_usbhook_invoke_real_close,
[CNH_USBHOOK_IRP_OP_RESET] = _cnh_usbhook_invoke_real_reset,
[CNH_USBHOOK_IRP_OP_SET_ALTINTERFACE] = _cnh_usbhook_invoke_real_set_altinterface,
[CNH_USBHOOK_IRP_OP_SET_CONFIGURATION] = _cnh_usbhook_invoke_real_set_configuration,
[CNH_USBHOOK_IRP_OP_CLAIM_INTERFACE] = _cnh_usbhook_invoke_real_claim_interface,
[CNH_USBHOOK_IRP_OP_CTRL_MSG] = _cnh_usbhook_invoke_real_ctrl_msg,
};
static atomic_int _cnh_usbhook_initted = ATOMIC_VAR_INIT(0);
static atomic_int _cnh_usbhook_init_in_progress = ATOMIC_VAR_INIT(0);
static pthread_mutex_t _cnh_usbhook_lock;
static cnh_usbhook_fn_t *_cnh_usbhook_handlers;
static size_t _cnh_usbhook_nhandlers;
/* ------------------------------------------------------------------------------------------------------------------ */
/* Public module functions */
/* ------------------------------------------------------------------------------------------------------------------ */
enum cnh_result cnh_usbhook_push_handler(cnh_usbhook_fn_t fn)
{
cnh_usbhook_fn_t *new_array;
size_t new_size;
enum cnh_result result;
assert(fn != NULL);
_cnh_usbhook_init();
pthread_mutex_lock(&_cnh_usbhook_lock);
new_size = _cnh_usbhook_nhandlers + 1;
new_array = realloc(_cnh_usbhook_handlers, new_size * sizeof(cnh_usbhook_fn_t));
if (new_array != NULL) {
_cnh_usbhook_handlers = new_array;
_cnh_usbhook_handlers[_cnh_usbhook_nhandlers++] = fn;
result = CNH_RESULT_SUCCESS;
} else {
result = CNH_RESULT_OUT_OF_MEMORY;
}
pthread_mutex_unlock(&_cnh_usbhook_lock);
return result;
}
enum cnh_result cnh_usbhook_invoke_next(struct cnh_usbhook_irp *irp)
{
cnh_usbhook_fn_t handler;
enum cnh_result result;
assert(irp != NULL);
assert(_cnh_usbhook_initted > 0);
pthread_mutex_lock(&_cnh_usbhook_lock);
assert(irp->next_handler <= _cnh_usbhook_nhandlers);
if (irp->next_handler < _cnh_usbhook_nhandlers) {
handler = _cnh_usbhook_handlers[irp->next_handler];
irp->next_handler++;
} else {
handler = _cnh_usbhook_invoke_real;
irp->next_handler = (size_t) -1;
}
pthread_mutex_unlock(&_cnh_usbhook_lock);
result = handler(irp);
if (result != CNH_RESULT_SUCCESS) {
irp->next_handler = (size_t) -1;
}
return result;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooked functions */
/* ------------------------------------------------------------------------------------------------------------------ */
void usb_init(void)
{
struct cnh_usbhook_irp irp;
/* Ensure module is initialized */
_cnh_usbhook_init();
memset(&irp, 0, sizeof(irp));
irp.op = CNH_USBHOOK_IRP_OP_INIT;
cnh_usbhook_invoke_next(&irp);
/* No return value, results ignored */
}
int usb_find_busses(void)
{
struct cnh_usbhook_irp irp;
/* Ensure module is initialized */
_cnh_usbhook_init();
memset(&irp, 0, sizeof(irp));
irp.op = CNH_USBHOOK_IRP_OP_FIND_BUSSES;
irp.find_busses_res_num_busses = 0;
cnh_usbhook_invoke_next(&irp);
/* No errors processed */
return irp.find_busses_res_num_busses;
}
int usb_find_devices(void)
{
struct cnh_usbhook_irp irp;
/* Ensure module is initialized */
_cnh_usbhook_init();
memset(&irp, 0, sizeof(irp));
irp.op = CNH_USBHOOK_IRP_OP_FIND_DEVICES;
irp.find_devices_res_num_devices = 0;
cnh_usbhook_invoke_next(&irp);
/* No errors processed */
return irp.find_devices_res_num_devices;
}
usb_dev_handle* usb_open(struct usb_device* dev)
{
struct cnh_usbhook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_usbhook_init();
if (dev == NULL) {
/* No errno is set here (from libusbcompat impl) */
return NULL;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_USBHOOK_IRP_OP_OPEN;
irp.open_usb_dev = dev;
irp.handle = NULL;
result = cnh_usbhook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
return NULL;
}
return irp.handle;
}
int usb_close(usb_dev_handle* dev)
{
struct cnh_usbhook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_usbhook_init();
if (dev == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
/* Always return negative errno on errors */
return -errno;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_USBHOOK_IRP_OP_CLOSE;
irp.handle = dev;
result = cnh_usbhook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
/* Always return negative errno on errors */
return -errno;
}
return 0;
}
int usb_reset(usb_dev_handle* dev)
{
struct cnh_usbhook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_usbhook_init();
if (dev == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
/* Always return negative errno on errors */
return -errno;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_USBHOOK_IRP_OP_RESET;
irp.handle = dev;
result = cnh_usbhook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
/* Always return negative errno on errors */
return -errno;
}
return 0;
}
int usb_set_altinterface(usb_dev_handle* dev, int interface)
{
struct cnh_usbhook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_usbhook_init();
if (dev == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
/* Always return negative errno on errors */
return -errno;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_USBHOOK_IRP_OP_SET_ALTINTERFACE;
irp.handle = dev;
irp.set_altinterface = interface;
result = cnh_usbhook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
/* Always return negative errno on errors */
return -errno;
}
return 0;
}
int usb_set_configuration(usb_dev_handle* dev, int configuration)
{
struct cnh_usbhook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_usbhook_init();
if (dev == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
/* Always return negative errno on errors */
return -errno;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_USBHOOK_IRP_OP_SET_CONFIGURATION;
irp.handle = dev;
irp.set_configuration = configuration;
result = cnh_usbhook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
/* Always return negative errno on errors */
return -errno;
}
return 0;
}
int usb_claim_interface(usb_dev_handle* dev, int interface)
{
struct cnh_usbhook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_usbhook_init();
if (dev == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
/* Always return negative errno on errors */
return -errno;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_USBHOOK_IRP_OP_CLAIM_INTERFACE;
irp.handle = dev;
irp.claim_interface = interface;
result = cnh_usbhook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
/* Always return negative errno on errors */
return -errno;
}
return 0;
}
int usb_control_msg(usb_dev_handle* dev, int requesttype, int request, int value, int index, char* bytes, int nbytes,
int timeout)
{
struct cnh_usbhook_irp irp;
enum cnh_result result;
/* Ensure module is initialized */
_cnh_usbhook_init();
if (dev == NULL || bytes == NULL) {
errno = cnh_result_to_errno(CNH_RESULT_INVALID_PARAMETER);
/* Always return negative errno on errors */
return -errno;
}
memset(&irp, 0, sizeof(irp));
irp.op = CNH_USBHOOK_IRP_OP_CTRL_MSG;
irp.handle = dev;
irp.ctrl_req_type = requesttype;
irp.ctrl_req = request;
irp.ctrl_value = value;
irp.ctrl_index = index;
irp.ctrl_buffer.bytes = (uint8_t*) bytes;
irp.ctrl_buffer.nbytes = (size_t) nbytes;
irp.ctrl_buffer.pos = 0;
irp.ctrl_timeout = timeout;
result = cnh_usbhook_invoke_next(&irp);
if (result != CNH_RESULT_SUCCESS) {
errno = cnh_result_to_errno(result);
/* Always return negative errno on errors */
return -errno;
}
return irp.ctrl_buffer.pos;
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Helper functions */
/* ------------------------------------------------------------------------------------------------------------------ */
static void _cnh_usbhook_init(void)
{
int expected;
if (atomic_load(&_cnh_usbhook_initted) > 0) {
return;
}
expected = 0;
if (!atomic_compare_exchange_strong(&_cnh_usbhook_init_in_progress, &expected, 1)) {
while (atomic_load(&_cnh_usbhook_init_in_progress) > 1) {
util_time_sleep_us(100);
}
return;
}
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
if (atomic_load(&_cnh_usbhook_initted) > 0) {
/* Revert init in progress, because nothing to do anymore */
atomic_store(&_cnh_usbhook_init_in_progress, 0);
return;
}
_cnh_usbhook_real_init = (cnh_usbhook_usb_init_t) cnh_lib_get_func_addr("usb_init");
_cnh_usbhook_real_find_devices = (cnh_usbhook_usb_find_devices_t) cnh_lib_get_func_addr("usb_find_devices");
_cnh_usbhook_real_find_busses = (cnh_usbhook_usb_find_busses_t) cnh_lib_get_func_addr("usb_find_busses");
_cnh_usbhook_real_open = (cnh_usbhook_usb_open_t) cnh_lib_get_func_addr("usb_open");
_cnh_usbhook_real_close = (cnh_usbhook_usb_close_t) cnh_lib_get_func_addr("usb_close");
_cnh_usbhook_real_reset = (cnh_usbhook_usb_reset_t) cnh_lib_get_func_addr("usb_reset");
_cnh_usbhook_real_set_altinterface =
(cnh_usbhook_usb_set_altinterface_t) cnh_lib_get_func_addr("usb_set_altinterface");
_cnh_usbhook_real_set_configuration =
(cnh_usbhook_usb_set_configuration_t) cnh_lib_get_func_addr("usb_set_configuration");
_cnh_usbhook_real_claim_interface =
(cnh_usbhook_usb_claim_interface_t) cnh_lib_get_func_addr("usb_claim_interface");
_cnh_usbhook_real_control_msg = (cnh_usbhook_usb_control_msg_t) cnh_lib_get_func_addr("usb_control_msg");
pthread_mutex_init(&_cnh_usbhook_lock, NULL);
atomic_store(&_cnh_usbhook_initted, 1);
atomic_store(&_cnh_usbhook_init_in_progress, 0);
}
static enum cnh_result _cnh_usbhook_invoke_real(struct cnh_usbhook_irp* irp)
{
cnh_usbhook_fn_t handler;
assert(irp != NULL);
assert(irp->op < _countof(_cnh_usbhook_real_handlers));
handler = _cnh_usbhook_real_handlers[irp->op];
assert(handler != NULL);
return handler(irp);
}
static enum cnh_result _cnh_usbhook_invoke_real_init(struct cnh_usbhook_irp* irp)
{
assert(irp != NULL);
_cnh_usbhook_real_init();
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usbhook_invoke_real_find_busses(struct cnh_usbhook_irp* irp)
{
int res;
assert(irp != NULL);
res = _cnh_usbhook_real_find_busses();
irp->find_busses_res_num_busses = res;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usbhook_invoke_real_find_devices(struct cnh_usbhook_irp* irp)
{
int res;
assert(irp != NULL);
res = _cnh_usbhook_real_find_devices();
irp->find_devices_res_num_devices = res;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usbhook_invoke_real_open(struct cnh_usbhook_irp* irp)
{
usb_dev_handle* res;
assert(irp != NULL);
res = _cnh_usbhook_real_open(irp->open_usb_dev);
if (res == NULL) {
irp->handle = NULL;
return cnh_errno_to_result(errno);
}
irp->handle = res;
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usbhook_invoke_real_close(struct cnh_usbhook_irp* irp)
{
int res;
assert(irp != NULL);
res = _cnh_usbhook_real_close(irp->handle);
if (res < 0) {
return cnh_errno_to_result(res);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usbhook_invoke_real_reset(struct cnh_usbhook_irp* irp)
{
int res;
assert(irp != NULL);
res = _cnh_usbhook_real_reset(irp->handle);
if (res < 0) {
return cnh_errno_to_result(res);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usbhook_invoke_real_set_altinterface(struct cnh_usbhook_irp* irp)
{
int res;
assert(irp != NULL);
res = _cnh_usbhook_real_set_altinterface(irp->handle, irp->set_altinterface);
if (res < 0) {
return cnh_errno_to_result(res);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usbhook_invoke_real_set_configuration(struct cnh_usbhook_irp* irp)
{
int res;
assert(irp != NULL);
res = _cnh_usbhook_real_set_configuration(irp->handle, irp->set_configuration);
if (res < 0) {
return cnh_errno_to_result(res);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usbhook_invoke_real_claim_interface(struct cnh_usbhook_irp* irp)
{
int res;
assert(irp != NULL);
res = _cnh_usbhook_real_claim_interface(irp->handle, irp->claim_interface);
if (res < 0) {
return cnh_errno_to_result(res);
}
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usbhook_invoke_real_ctrl_msg(struct cnh_usbhook_irp* irp)
{
int res;
assert(irp != NULL);
res = _cnh_usbhook_real_control_msg(irp->handle, irp->ctrl_req_type, irp->ctrl_req, irp->ctrl_value,
irp->ctrl_index, (char*) irp->ctrl_buffer.bytes, irp->ctrl_buffer.nbytes, irp->ctrl_timeout);
irp->ctrl_buffer.pos = 0;
if (res < 0) {
return cnh_errno_to_result(res);
}
irp->ctrl_buffer.pos = (size_t) res;
return CNH_RESULT_SUCCESS;
}
+75
View File
@@ -0,0 +1,75 @@
/**
* Hook module for hooking libusb 0.1
*
* Based on original capnhook code for windows by decafcode:
* https://github.com/decafcode/capnhook
*/
#ifndef CAPNHOOK_USBHOOK_H
#define CAPNHOOK_USBHOOK_H
#include <usb.h>
#include "capnhook/hook/iobuf.h"
/**
* Available operations to hook
*/
enum cnh_usbhook_irp_op {
CNH_USBHOOK_IRP_OP_INIT = 0,
CNH_USBHOOK_IRP_OP_FIND_BUSSES = 1,
CNH_USBHOOK_IRP_OP_FIND_DEVICES = 2,
CNH_USBHOOK_IRP_OP_OPEN = 3,
CNH_USBHOOK_IRP_OP_CLOSE = 4,
CNH_USBHOOK_IRP_OP_RESET = 5,
CNH_USBHOOK_IRP_OP_SET_ALTINTERFACE = 6,
CNH_USBHOOK_IRP_OP_SET_CONFIGURATION = 7,
CNH_USBHOOK_IRP_OP_CLAIM_INTERFACE = 8,
CNH_USBHOOK_IRP_OP_CTRL_MSG = 9
};
/**
* I/O request packet
*/
struct cnh_usbhook_irp {
enum cnh_usbhook_irp_op op;
size_t next_handler;
int find_busses_res_num_busses;
int find_devices_res_num_devices;
struct usb_device* open_usb_dev;
usb_dev_handle* handle;
int set_altinterface;
int set_configuration;
int claim_interface;
int ctrl_req_type;
int ctrl_req;
int ctrl_value;
int ctrl_index;
struct cnh_iobuf ctrl_buffer;
int ctrl_timeout;
};
/**
* Hook function type
*/
typedef enum cnh_result (*cnh_usbhook_fn_t)(struct cnh_usbhook_irp *irp);
/**
* Add a new hook handler to the hook module.
* The handler added is getting called on every hooked operation.
*
* @param fn Pointer to hook function to add
* @return Result/error code if hooking was successful
*/
enum cnh_result cnh_usbhook_push_handler(cnh_usbhook_fn_t fn);
/**
* Invoke the next hooked function registered in the hook module.
* Call this from your hook handler if you want to pass on execution to further
* hooked calls reaching the original function at the end of the hook chain.
*
* @param irp I/O request package to dispatch
* @return Result of the following function dispatching the request package
*/
enum cnh_result cnh_usbhook_invoke_next(struct cnh_usbhook_irp *irp);
#endif
+57
View File
@@ -0,0 +1,57 @@
#define LOG_MODULE "cnh-filehook-mon"
#include "capnhook/hooklib/filehook-mon.h"
#include "util/log.h"
static const char* _cnh_filehook_mon_str_empty = "";
static const char* _cnh_filehook_mon_irp_op_str[] = {
"fopen",
"fclose",
"fread",
"fwrite",
"fgets",
"fseek",
"ftell",
"feof"
};
enum cnh_result cnh_filehook_mon(struct cnh_filehook_irp *irp)
{
enum cnh_result result;
/* Skip writes to log file because this results in infinite recursion */
if (irp->file == util_log_get_file_handle()) {
return cnh_filehook_invoke_next(irp);
}
log_debug("[before][%s] next_handler %d, file %p, open_filename %s, open_mode %s, read(bytes %p, nbytes %d, pos %d)"
", write(bytes %p, nbytes %d, pos %d), orig_read_write_size %d, orig_read_write_nmemb %d, seek_origin %d, "
"seek_offset %d, tell_offset %llu, eof %d", _cnh_filehook_mon_irp_op_str[irp->op], irp->next_handler,
irp->file, irp->open_filename ? irp->open_filename : _cnh_filehook_mon_str_empty,
irp->open_mode ? irp->open_mode : _cnh_filehook_mon_str_empty, irp->read.bytes, irp->read.nbytes,
irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->orig_read_write_size,
irp->orig_read_write_nmemb, irp->seek_origin, irp->seek_offset, irp->tell_offset, irp->eof);
result = cnh_filehook_invoke_next(irp);
if (result != CNH_RESULT_SUCCESS) {
log_error("[after][%s] result %d, next_handler %d, file %p, open_filename %s, open_mode %s, read(bytes %p, "
"nbytes %d, pos %d), write(bytes %p, nbytes %d, pos %d), orig_read_write_size %d, orig_read_write_nmemb %d,"
" seek_origin %d, seek_offset %d, tell_offset %llu, eof %d", _cnh_filehook_mon_irp_op_str[irp->op],
result, irp->next_handler, irp->file, irp->open_filename ? irp->open_filename : _cnh_filehook_mon_str_empty,
irp->open_mode ? irp->open_mode : _cnh_filehook_mon_str_empty, irp->read.bytes, irp->read.nbytes,
irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->orig_read_write_size,
irp->orig_read_write_nmemb, irp->seek_origin, irp->seek_offset, irp->tell_offset, irp->eof);
} else {
log_debug("[after][%s] result %d, next_handler %d, file %p, open_filename %s, open_mode %s, read(bytes %p, "
"nbytes %d, pos %d), write(bytes %p, nbytes %d, pos %d), orig_read_write_size %d, orig_read_write_nmemb %d,"
" seek_origin %d, seek_offset %d, tell_offset %llu, eof %d", _cnh_filehook_mon_irp_op_str[irp->op],
result, irp->next_handler, irp->file, irp->open_filename ? irp->open_filename : _cnh_filehook_mon_str_empty,
irp->open_mode ? irp->open_mode : _cnh_filehook_mon_str_empty, irp->read.bytes, irp->read.nbytes,
irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->orig_read_write_size,
irp->orig_read_write_nmemb, irp->seek_origin, irp->seek_offset, irp->tell_offset, irp->eof);
}
return result;
}
+17
View File
@@ -0,0 +1,17 @@
/**
* Hook implementation to monitor file related calls, e.g. fopen, fread, fwrite etc.
*/
#ifndef CAPNHOOK_FILEHOOK_MON_H
#define CAPNHOOK_FILEHOOK_MON_H
#include "capnhook/hook/filehook.h"
/**
* Hook function to add to the hook module for monitoring file related calls
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_filehook_mon(struct cnh_filehook_irp *irp);
#endif
+96
View File
@@ -0,0 +1,96 @@
#include "capnhook/hook/filehook.h"
#include "capnhook/hook/fshook.h"
#include "capnhook/hook/iohook.h"
#include "util/log.h"
enum cnh_result cnh_fileopen_mon_filehook(struct cnh_filehook_irp *irp)
{
enum cnh_result result;
if (irp->op == CNH_FILEHOOK_IRP_OP_OPEN) {
log_debug("[before][fopen]: %s %s", irp->open_filename, irp->open_mode);
result = cnh_filehook_invoke_next(irp);
log_debug("[after][fopen]: %s %s -> %d (%d)", irp->open_filename, irp->open_mode, irp->file, result);
} else {
result = cnh_filehook_invoke_next(irp);
}
return result;
}
enum cnh_result cnh_fileopen_mon_fshook(struct cnh_fshook_irp *irp)
{
enum cnh_result result;
switch (irp->op) {
case CNH_FSHOOK_IRP_OP_DIR_OPEN:
log_debug("[before][diropen]: %s", irp->opendir_name);
break;
case CNH_FSHOOK_IRP_OP_LXSTAT:
log_debug("[before][lxstat]: %s", irp->xstat_file);
break;
case CNH_FSHOOK_IRP_OP_XSTAT:
log_debug("[before][xstat]: %s", irp->xstat_file);
break;
case CNH_FSHOOK_IRP_OP_RENAME:
log_debug("[before][rename]: %s %s", irp->rename_old, irp->rename_new);
break;
case CNH_FSHOOK_IRP_OP_REMOVE:
log_debug("[before][remove]: %s", irp->remove_pathname);
break;
case CNH_FSHOOK_IRP_OP_ACCESS:
log_debug("[before][access]: %s", irp->access_path);
break;
default:
log_die("Unhandled case: %d", irp->op);
break;
}
result = cnh_fshook_invoke_next(irp);
switch (irp->op) {
case CNH_FSHOOK_IRP_OP_DIR_OPEN:
log_debug("[after][diropen]: %s -> %d", irp->opendir_name, result);
break;
case CNH_FSHOOK_IRP_OP_LXSTAT:
log_debug("[after][lxstat]: %s -> %d", irp->xstat_file, result);
break;
case CNH_FSHOOK_IRP_OP_XSTAT:
log_debug("[after][xstat]: %s -> %d", irp->xstat_file, result);
break;
case CNH_FSHOOK_IRP_OP_RENAME:
log_debug("[after][rename]: %s %s -> %d", irp->rename_old, irp->rename_new, result);
break;
case CNH_FSHOOK_IRP_OP_REMOVE:
log_debug("[after][remove]: %s -> %d", irp->remove_pathname, result);
break;
case CNH_FSHOOK_IRP_OP_ACCESS:
log_debug("[after][access]: %s -> %d", irp->access_path, result);
break;
default:
log_die("Unhandled case: %d", irp->op);
break;
}
return result;
}
enum cnh_result cnh_fileopen_mon_iohook(struct cnh_iohook_irp *irp)
{
enum cnh_result result;
if (irp->op == CNH_IOHOOK_IRP_OP_OPEN) {
log_debug("[before][open]: %s %d", irp->open_filename, irp->open_flags);
result = cnh_iohook_invoke_next(irp);
log_debug("[after][open]: %s %d -> %d (%d)", irp->open_filename, irp->open_flags, irp->fd, result);
} else {
result = cnh_iohook_invoke_next(irp);
}
return result;
}
+35
View File
@@ -0,0 +1,35 @@
/**
* Hook implementation to monitor different kinds of operations opening a file
*/
#ifndef CAPNHOOK_FILEOPEN_MON_H
#define CAPNHOOK_FILEOPEN_MON_H
#include "capnhook/hook/filehook.h"
#include "capnhook/hook/fshook.h"
#include "capnhook/hook/iohook.h"
/**
* Hook function to add to the hook module for monitoring open file calls on file operations
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_fileopen_mon_filehook(struct cnh_filehook_irp *irp);
/**
* Hook function to add to the hook module for monitoring open file calls on file system related operations
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_fileopen_mon_fshook(struct cnh_fshook_irp *irp);
/**
* Hook function to add to the hook module for monitoring open file/device calls on I/O operations
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_fileopen_mon_iohook(struct cnh_iohook_irp *irp);
#endif
+43
View File
@@ -0,0 +1,43 @@
#define LOG_MODULE "cnh-fshook-mon"
#include "capnhook/hooklib/fshook-mon.h"
#include "util/log.h"
static const char* _cnh_fshook_mon_irp_op_str[] = {
"diropen",
"lxstat",
"xstat",
"rename",
"remove",
"access"
};
enum cnh_result cnh_fshook_mon(struct cnh_fshook_irp *irp)
{
enum cnh_result result;
log_debug("[before][%s] next_handler %d, opendir_name %s, opendir_ret %p, xstat_version %d, xstat_file %s, "
"xstat_buf %p, rename_old %s, rename_new %s, remove_pathname %s, access_path %s, access_amode %d",
_cnh_fshook_mon_irp_op_str[irp->op], irp->next_handler, irp->opendir_name, irp->opendir_ret,
irp->xstat_version, irp->xstat_file, irp->xstat_buf, irp->rename_old, irp->rename_new, irp->remove_pathname,
irp->access_path, irp->access_amode);
result = cnh_fshook_invoke_next(irp);
if (result != CNH_RESULT_SUCCESS) {
log_error("[before][%s] result %d, next_handler %d, opendir_name %s, opendir_ret %p, xstat_version %d, "
"xstat_file %s, xstat_buf %p, rename_old %s, rename_new %s, remove_pathname %s, access_path %s, "
"access_amode %d", _cnh_fshook_mon_irp_op_str[irp->op], result, irp->next_handler, irp->opendir_name,
irp->opendir_ret, irp->xstat_version, irp->xstat_file, irp->xstat_buf, irp->rename_old, irp->rename_new,
irp->remove_pathname, irp->access_path, irp->access_amode);
} else {
log_debug("[before][%s] result %d, next_handler %d, opendir_name %s, opendir_ret %p, xstat_version %d, "
"xstat_file %s, xstat_buf %p, rename_old %s, rename_new %s, remove_pathname %s, access_path %s, "
"access_amode %d", _cnh_fshook_mon_irp_op_str[irp->op], result, irp->next_handler, irp->opendir_name,
irp->opendir_ret, irp->xstat_version, irp->xstat_file, irp->xstat_buf, irp->rename_old, irp->rename_new,
irp->remove_pathname, irp->access_path, irp->access_amode);
}
return result;
}
+17
View File
@@ -0,0 +1,17 @@
/**
* Hook implementation to monitor file system related calls
*/
#ifndef CAPNHOOK_FSHOOK_MON_H
#define CAPNHOOK_FSHOOK_MON_H
#include "capnhook/hook/fshook.h"
/**
* Hook function to add to the hook module for monitoring file system related calls
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_fshook_mon(struct cnh_fshook_irp *irp);
#endif
+54
View File
@@ -0,0 +1,54 @@
#define LOG_MODULE "cnh-iohook-mon"
#include "capnhook/hooklib/iohook-mon.h"
#include "util/log.h"
static const char* _cnh_iohook_mon_str_empty = "";
static const char* _cnh_iohook_mon_irp_op_str[] = {
"open",
"fdopen",
"close",
"read",
"write",
"seek",
"ioctl"
};
enum cnh_result cnh_iohook_mon(struct cnh_iohook_irp *irp)
{
enum cnh_result result;
log_debug("[before][%s] next_handler %d, fd %d, open_filename %s, open_flags %d, fdopen_fd %d, fdopen_mode %s, "
"fdopen_res %p, read(bytes %p, nbytes %d, pos %d), write(bytes %p, nbytes %d, pos %d), seek_origin %d, "
"seek_offset %d, seek_pos %llu, ioctl_req %d, ioctl(bytes %p, nbytes %d, pos %d)",
_cnh_iohook_mon_irp_op_str[irp->op], irp->next_handler, irp->fd,
irp->open_filename ? irp->open_filename : _cnh_iohook_mon_str_empty, irp->open_flags, irp->fdopen_fd,
irp->fdopen_mode ? irp->fdopen_mode : _cnh_iohook_mon_str_empty, irp->fdopen_res, irp->read.bytes,
irp->read.nbytes, irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->seek_origin,
irp->seek_offset, irp->seek_pos, irp->ioctl_req, irp->ioctl.bytes, irp->ioctl.nbytes, irp->ioctl.pos);
result = cnh_iohook_invoke_next(irp);
if (result != CNH_RESULT_SUCCESS) {
log_error("[after][%s] next_handler %d, res: %d, fd %d, open_filename %s, open_flags %d, fdopen_fd %d, "
"fdopen_mode %s, fdopen_res %p, read(bytes %p, nbytes %d, pos %d), write(bytes %p, nbytes %d, pos %d), "
"seek_origin %d, seek_offset %d, seek_pos %llu, ioctl_req %d, ioctl(bytes %p, nbytes %d, pos %d)",
_cnh_iohook_mon_irp_op_str[irp->op], irp->next_handler, result, irp->fd,
irp->open_filename ? irp->open_filename : _cnh_iohook_mon_str_empty, irp->open_flags, irp->fdopen_fd,
irp->fdopen_mode ? irp->fdopen_mode : _cnh_iohook_mon_str_empty, irp->fdopen_res, irp->read.bytes,
irp->read.nbytes, irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->seek_origin,
irp->seek_offset, irp->seek_pos, irp->ioctl_req, irp->ioctl.bytes, irp->ioctl.nbytes, irp->ioctl.pos);
} else {
log_debug("[after][%s] next_handler %d, res %d, fd %d, open_filename %s, open_flags %d, fdopen_fd %d, "
"fdopen_mode %s, fdopen_res %p, read(bytes %p, nbytes %d, pos %d), write(bytes %p, nbytes %d, pos %d), "
"seek_origin %d, seek_offset %d, seek_pos %llu, ioctl_req %d, ioctl(bytes %p, nbytes %d, pos %d)",
_cnh_iohook_mon_irp_op_str[irp->op], irp->next_handler, result, irp->fd,
irp->open_filename ? irp->open_filename : _cnh_iohook_mon_str_empty, irp->open_flags, irp->fdopen_fd,
irp->fdopen_mode ? irp->fdopen_mode : _cnh_iohook_mon_str_empty, irp->fdopen_res, irp->read.bytes,
irp->read.nbytes, irp->read.pos, irp->write.bytes, irp->write.nbytes, irp->write.pos, irp->seek_origin,
irp->seek_offset, irp->seek_pos, irp->ioctl_req, irp->ioctl.bytes, irp->ioctl.nbytes, irp->ioctl.pos);
}
return result;
}
+17
View File
@@ -0,0 +1,17 @@
/**
* Hook implementation to monitor I/O calls, e.g. open, read, write etc.
*/
#ifndef CAPNHOOK_IOHOOK_MON_H
#define CAPNHOOK_IOHOOK_MON_H
#include "capnhook/hook/iohook.h"
/**
* Hook function to add to the hook module for monitoring I/O calls
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_iohook_mon(struct cnh_iohook_irp *irp);
#endif
+371
View File
@@ -0,0 +1,371 @@
#include <assert.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
#include "capnhook/hook/fshook.h"
#include "capnhook/hook/filehook.h"
#include "capnhook/hook/iohook.h"
#include "util/log.h"
#include "util/str.h"
#include "util/time.h"
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooks to fshook module */
/* ------------------------------------------------------------------------------------------------------------------ */
static enum cnh_result _cnh_redir_fshook_diropen(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_redir_fshook_lxstat(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_redir_fshook_xstat(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_redir_fshook_rename(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_redir_fshook_remove(struct cnh_fshook_irp *irp);
static enum cnh_result _cnh_redir_fshook_access(struct cnh_fshook_irp *irp);
static const cnh_fshook_fn_t _cnh_redir_fshook_handlers[6] = {
[CNH_FSHOOK_IRP_OP_DIR_OPEN] = _cnh_redir_fshook_diropen,
[CNH_FSHOOK_IRP_OP_LXSTAT] = _cnh_redir_fshook_lxstat,
[CNH_FSHOOK_IRP_OP_XSTAT] = _cnh_redir_fshook_xstat,
[CNH_FSHOOK_IRP_OP_RENAME] = _cnh_redir_fshook_rename,
[CNH_FSHOOK_IRP_OP_REMOVE] = _cnh_redir_fshook_remove,
[CNH_FSHOOK_IRP_OP_ACCESS] = _cnh_redir_fshook_access,
};
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooks to filehook module */
/* ------------------------------------------------------------------------------------------------------------------ */
static enum cnh_result _cnh_redir_filehook_default(struct cnh_filehook_irp *irp);
static enum cnh_result _cnh_redir_filehook_fopen(struct cnh_filehook_irp *irp);
static const cnh_filehook_fn_t _cnh_redir_filehook_handlers[8] = {
[CNH_FILEHOOK_IRP_OP_OPEN] = _cnh_redir_filehook_fopen,
[CNH_FILEHOOK_IRP_OP_CLOSE] = _cnh_redir_filehook_default,
[CNH_FILEHOOK_IRP_OP_READ] = _cnh_redir_filehook_default,
[CNH_FILEHOOK_IRP_OP_WRITE] = _cnh_redir_filehook_default,
[CNH_FILEHOOK_IRP_OP_FGETS] = _cnh_redir_filehook_default,
[CNH_FILEHOOK_IRP_OP_SEEK] = _cnh_redir_filehook_default,
[CNH_FILEHOOK_IRP_OP_TELL] = _cnh_redir_filehook_default,
[CNH_FILEHOOK_IRP_OP_EOF] = _cnh_redir_filehook_default,
};
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooks to iohook module */
/* ------------------------------------------------------------------------------------------------------------------ */
static enum cnh_result _cnh_redir_iohook_default(struct cnh_iohook_irp *irp);
static enum cnh_result _cnh_redir_iohook_open(struct cnh_iohook_irp *irp);
static const cnh_iohook_fn_t _cnh_redir_iohook_handlers[7] = {
[CNH_IOHOOK_IRP_OP_OPEN] = _cnh_redir_iohook_open,
[CNH_IOHOOK_IRP_OP_FDOPEN] = _cnh_redir_iohook_default,
[CNH_IOHOOK_IRP_OP_CLOSE] = _cnh_redir_iohook_default,
[CNH_IOHOOK_IRP_OP_READ] = _cnh_redir_iohook_default,
[CNH_IOHOOK_IRP_OP_WRITE] = _cnh_redir_iohook_default,
[CNH_IOHOOK_IRP_OP_SEEK] = _cnh_redir_iohook_default,
[CNH_IOHOOK_IRP_OP_IOCTL] = _cnh_redir_iohook_default,
};
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private state */
/* ------------------------------------------------------------------------------------------------------------------ */
struct cnh_redir_redir {
char* src;
char* dest;
};
static atomic_int _cnh_redir_initted = ATOMIC_VAR_INIT(0);
static atomic_int _cnh_redir_init_in_progress = ATOMIC_VAR_INIT(0);
static pthread_mutex_t _cnh_redir_lock;
static struct cnh_redir_redir* _cnh_redir_redirs;
static size_t _cnh_redir_nredirs;
static void _cnh_redir_init();
static char* _cnh_redir_check(const char* src);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Public module functions */
/* ------------------------------------------------------------------------------------------------------------------ */
void cnh_redir_add(const char* src, const char* dest)
{
struct cnh_redir_redir *new_array;
size_t new_size;
assert(src != NULL);
assert(dest != NULL);
/* Ensure module is initialized */
_cnh_redir_init();
pthread_mutex_lock(&_cnh_redir_lock);
new_size = _cnh_redir_nredirs + 1;
new_array = realloc(_cnh_redir_redirs, new_size * sizeof(struct cnh_redir_redir));
if (new_array != NULL) {
_cnh_redir_redirs = new_array;
}
_cnh_redir_redirs[_cnh_redir_nredirs].src = util_str_dup(src);
_cnh_redir_redirs[_cnh_redir_nredirs].dest = util_str_dup(dest);
_cnh_redir_nredirs++;
pthread_mutex_unlock(&_cnh_redir_lock);
log_info("Added path redirect %s -> %s", src, dest);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooks to fshook module */
/* ------------------------------------------------------------------------------------------------------------------ */
enum cnh_result cnh_redir_fshook(struct cnh_fshook_irp* irp)
{
cnh_fshook_fn_t handler;
handler = _cnh_redir_fshook_handlers[irp->op];
assert(handler != NULL);
return handler(irp);
}
static enum cnh_result _cnh_redir_fshook_diropen(struct cnh_fshook_irp *irp)
{
char* res;
/* Ensure module is initialized */
_cnh_redir_init();
res = _cnh_redir_check(irp->opendir_name);
if (res != NULL) {
irp->opendir_name = res;
}
return cnh_fshook_invoke_next(irp);
}
static enum cnh_result _cnh_redir_fshook_lxstat(struct cnh_fshook_irp *irp)
{
char* res;
/* Ensure module is initialized */
_cnh_redir_init();
res = _cnh_redir_check(irp->xstat_file);
if (res != NULL) {
irp->xstat_file = res;
}
return cnh_fshook_invoke_next(irp);
}
static enum cnh_result _cnh_redir_fshook_xstat(struct cnh_fshook_irp *irp)
{
char* res;
/* Ensure module is initialized */
_cnh_redir_init();
res = _cnh_redir_check(irp->xstat_file);
if (res != NULL) {
irp->xstat_file = res;
}
return cnh_fshook_invoke_next(irp);
}
static enum cnh_result _cnh_redir_fshook_rename(struct cnh_fshook_irp *irp)
{
char* res_old;
char* res_new;
/* Ensure module is initialized */
_cnh_redir_init();
res_old = _cnh_redir_check(irp->rename_old);
res_new = _cnh_redir_check(irp->rename_old);
if (res_old != NULL) {
irp->rename_old = res_old;
}
if (res_new != NULL) {
irp->rename_new = res_new;
}
return cnh_fshook_invoke_next(irp);
}
static enum cnh_result _cnh_redir_fshook_remove(struct cnh_fshook_irp *irp)
{
char* res;
/* Ensure module is initialized */
_cnh_redir_init();
res = _cnh_redir_check(irp->remove_pathname);
if (res != NULL) {
irp->remove_pathname = res;
}
return cnh_fshook_invoke_next(irp);
}
static enum cnh_result _cnh_redir_fshook_access(struct cnh_fshook_irp *irp)
{
char* res;
/* Ensure module is initialized */
_cnh_redir_init();
res = _cnh_redir_check(irp->access_path);
if (res != NULL) {
irp->access_path = res;
}
return cnh_fshook_invoke_next(irp);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooks to iohook module */
/* ------------------------------------------------------------------------------------------------------------------ */
enum cnh_result cnh_redir_iohook(struct cnh_iohook_irp* irp)
{
cnh_iohook_fn_t handler;
handler = _cnh_redir_iohook_handlers[irp->op];
assert(handler != NULL);
return handler(irp);
}
static enum cnh_result _cnh_redir_iohook_default(struct cnh_iohook_irp *irp)
{
return cnh_iohook_invoke_next(irp);
}
static enum cnh_result _cnh_redir_iohook_open(struct cnh_iohook_irp *irp)
{
char* res;
/* Ensure module is initialized */
_cnh_redir_init();
res = _cnh_redir_check(irp->open_filename);
if (res != NULL) {
irp->open_filename = res;
}
return cnh_iohook_invoke_next(irp);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooks to filehook module */
/* ------------------------------------------------------------------------------------------------------------------ */
enum cnh_result cnh_redir_filehook(struct cnh_filehook_irp* irp)
{
cnh_filehook_fn_t handler;
handler = _cnh_redir_filehook_handlers[irp->op];
assert(handler != NULL);
return handler(irp);
}
static enum cnh_result _cnh_redir_filehook_default(struct cnh_filehook_irp *irp)
{
return cnh_filehook_invoke_next(irp);
}
static enum cnh_result _cnh_redir_filehook_fopen(struct cnh_filehook_irp *irp)
{
char* res;
/* Ensure module is initialized */
_cnh_redir_init();
res = _cnh_redir_check(irp->open_filename);
if (res != NULL) {
irp->open_filename = res;
}
return cnh_filehook_invoke_next(irp);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Helper functions */
/* ------------------------------------------------------------------------------------------------------------------ */
static void _cnh_redir_init()
{
int expected;
if (atomic_load(&_cnh_redir_initted) > 0) {
return;
}
expected = 0;
if (!atomic_compare_exchange_strong(&_cnh_redir_init_in_progress, &expected, 1)) {
while (atomic_load(&_cnh_redir_init_in_progress) > 1) {
util_time_sleep_us(100);
}
return;
}
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
if (atomic_load(&_cnh_redir_initted) > 0) {
/* Revert init in progress, because nothing to do anymore */
atomic_store(&_cnh_redir_init_in_progress, 0);
return;
}
pthread_mutex_init(&_cnh_redir_lock, NULL);
atomic_store(&_cnh_redir_initted, 1);
atomic_store(&_cnh_redir_init_in_progress, 0);
}
static char* _cnh_redir_check(const char* src)
{
const char* redir_src;
const char* redir_dest;
char* res;
res = NULL;
pthread_mutex_lock(&_cnh_redir_lock);
for (size_t i = 0; i < _cnh_redir_nredirs; i++) {
redir_src = _cnh_redir_redirs[i].src;
redir_dest = _cnh_redir_redirs[i].dest;
if (strncmp(src, redir_src, strlen(redir_src)) == 0) {
// FIXME memory leak
res = util_str_merge(redir_dest, &src[strlen(redir_src)]);
log_debug("Redirect %s -> %s", src, res);
break;
}
}
pthread_mutex_unlock(&_cnh_redir_lock);
return res;
}
+44
View File
@@ -0,0 +1,44 @@
/**
* Hook implementation to redirect files/paths passed as parameters on various file, file system and I/O calls
*/
#ifndef CAPNHOOK_REDIR_H
#define CAPNHOOK_REDIR_H
#include "capnhook/hook/fshook.h"
#include "capnhook/hook/filehook.h"
#include "capnhook/hook/iohook.h"
/**
* Add a new path/file redirect to the module
*
* @param src Source path/file to redirect. This does not have to match the full path.
* Postfix, e.g. just the filename is sufficient as well
* @param dest Destination to redirect the original path to. Recommended to use absolute paths here.
*/
void cnh_redir_add(const char* src, const char* dest);
/**
* Hook function to add to the hook module for redirecting file system related calls
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_redir_fshook(struct cnh_fshook_irp* irp);
/**
* Hook function to add to the hook module for redirecting I/O calls
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_redir_iohook(struct cnh_iohook_irp* irp);
/**
* Hook function to add to the hook module for redirecting file related calls
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_redir_filehook(struct cnh_filehook_irp* irp);
#endif
+494
View File
@@ -0,0 +1,494 @@
#include <assert.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <string.h>
#include <util/mem.h>
#include "capnhook/hooklib/usb-emu.h"
#include "util/log.h"
#include "util/time.h"
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private helpers */
/* ------------------------------------------------------------------------------------------------------------------ */
static void _cnh_usb_emu_init(void);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooks to usbhook module */
/* ------------------------------------------------------------------------------------------------------------------ */
static enum cnh_result _cnh_usb_emu_usbhook_default(struct cnh_usbhook_irp *irp);
static enum cnh_result _cnh_usb_emu_usbhook_noop(struct cnh_usbhook_irp *irp);
static enum cnh_result _cnh_usb_emu_usbhook_find_busses(struct cnh_usbhook_irp *irp);
static enum cnh_result _cnh_usb_emu_usbhook_find_devices(struct cnh_usbhook_irp *irp);
static enum cnh_result _cnh_usb_emu_usbhook_open(struct cnh_usbhook_irp *irp);
static enum cnh_result _cnh_usb_emu_usbhook_close(struct cnh_usbhook_irp *irp);
static enum cnh_result _cnh_usb_emu_usbhook_reset(struct cnh_usbhook_irp *irp);
static enum cnh_result _cnh_usb_emu_usbhook_ctrl_msg(struct cnh_usbhook_irp *irp);
static const cnh_usbhook_fn_t _cnh_usb_emu_usb_handlers[10] = {
[CNH_USBHOOK_IRP_OP_INIT] = _cnh_usb_emu_usbhook_default,
[CNH_USBHOOK_IRP_OP_FIND_BUSSES] = _cnh_usb_emu_usbhook_find_busses,
[CNH_USBHOOK_IRP_OP_FIND_DEVICES] = _cnh_usb_emu_usbhook_find_devices,
[CNH_USBHOOK_IRP_OP_OPEN] = _cnh_usb_emu_usbhook_open,
[CNH_USBHOOK_IRP_OP_CLOSE] = _cnh_usb_emu_usbhook_close,
[CNH_USBHOOK_IRP_OP_RESET] = _cnh_usb_emu_usbhook_reset,
[CNH_USBHOOK_IRP_OP_SET_ALTINTERFACE] = _cnh_usb_emu_usbhook_noop,
[CNH_USBHOOK_IRP_OP_SET_CONFIGURATION] = _cnh_usb_emu_usbhook_noop,
[CNH_USBHOOK_IRP_OP_CLAIM_INTERFACE] = _cnh_usb_emu_usbhook_noop,
[CNH_USBHOOK_IRP_OP_CTRL_MSG] = _cnh_usb_emu_usbhook_ctrl_msg,
};
/* ------------------------------------------------------------------------------------------------------------------ */
/* Private state */
/* ------------------------------------------------------------------------------------------------------------------ */
struct usb_dev_handle {
struct cnh_usb_emu_virtdev* virtdev;
};
struct cnh_usb_emu_virtdev {
const struct cnh_usb_emu_virtdev_ep* ep;
bool real_dev_avail;
bool enumerated;
struct usb_dev_handle usb_dev_handle;
};
static atomic_int _cnh_usb_emu_initted = ATOMIC_VAR_INIT(0);
static atomic_int _cnh_usb_emu_init_in_progress = ATOMIC_VAR_INIT(0);
static pthread_mutex_t _cnh_usb_emu_virtdevs_lock;
static struct cnh_usb_emu_virtdev *_cnh_usb_emu_virtdevs;
static size_t _cnh_usb_emu_nvirtdevs;
static struct cnh_usb_emu_virtdev* _cnh_usb_emu_get_virtdev(uint16_t vid, uint16_t pid);
static struct cnh_usb_emu_virtdev* _cnh_usb_emu_get_virtdev_by_handle(usb_dev_handle* handle);
static struct usb_device* _cnh_usb_emu_create_fakedev(uint16_t vid, uint16_t pid);
/* ------------------------------------------------------------------------------------------------------------------ */
/* Public module functions */
/* ------------------------------------------------------------------------------------------------------------------ */
void cnh_usb_emu_add_virtdevep(const struct cnh_usb_emu_virtdev_ep* virtdevep)
{
struct cnh_usb_emu_virtdev *new_array;
size_t new_size;
assert(virtdevep->enumerate);
assert(virtdevep->open);
assert(virtdevep->reset);
assert(virtdevep->control_msg);
assert(virtdevep->close);
/* Ensure initialized */
_cnh_usb_emu_init();
pthread_mutex_lock(&_cnh_usb_emu_virtdevs_lock);
new_size = _cnh_usb_emu_nvirtdevs + 1;
new_array = realloc(_cnh_usb_emu_virtdevs, new_size * sizeof(struct cnh_usb_emu_virtdev));
if (new_array != NULL) {
_cnh_usb_emu_virtdevs = new_array;
_cnh_usb_emu_virtdevs[_cnh_usb_emu_nvirtdevs].enumerated = false;
_cnh_usb_emu_virtdevs[_cnh_usb_emu_nvirtdevs].real_dev_avail = false;
_cnh_usb_emu_virtdevs[_cnh_usb_emu_nvirtdevs].ep = virtdevep;
_cnh_usb_emu_virtdevs[_cnh_usb_emu_nvirtdevs].usb_dev_handle.virtdev = &_cnh_usb_emu_virtdevs[_cnh_usb_emu_nvirtdevs];
_cnh_usb_emu_nvirtdevs++;
} else {
log_die("Out of memory");
}
pthread_mutex_unlock(&_cnh_usb_emu_virtdevs_lock);
}
enum cnh_result cnh_usb_emu(struct cnh_usbhook_irp *irp)
{
cnh_usbhook_fn_t handler;
/* Ensure initialized */
_cnh_usb_emu_init();
handler = _cnh_usb_emu_usb_handlers[irp->op];
assert(handler != NULL);
return handler(irp);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Hooked functions to usbhook module */
/* ------------------------------------------------------------------------------------------------------------------ */
static enum cnh_result _cnh_usb_emu_usbhook_default(struct cnh_usbhook_irp *irp)
{
return cnh_usbhook_invoke_next(irp);
}
static enum cnh_result _cnh_usb_emu_usbhook_noop(struct cnh_usbhook_irp *irp)
{
struct cnh_usb_emu_virtdev* virtdev;
assert(irp->handle);
virtdev = _cnh_usb_emu_get_virtdev_by_handle(irp->handle);
/* Not a virtual device handle, probably real handle from libusb */
if (virtdev == NULL) {
return cnh_usbhook_invoke_next(irp);
}
/* No-op */
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usb_emu_usbhook_find_busses(struct cnh_usbhook_irp *irp)
{
enum cnh_result result;
result = cnh_usbhook_invoke_next(irp);
// inject another custom bus for virtual devices
if (result == CNH_RESULT_SUCCESS) {
log_debug("Injecting usb bus for virtual devices");
irp->find_busses_res_num_busses++;
}
return result;
}
static enum cnh_result _cnh_usb_emu_usbhook_find_devices(struct cnh_usbhook_irp *irp)
{
enum cnh_result result;
struct usb_bus* it;
int fakedev_count;
fakedev_count = 0;
/* first, populate the list with real devices */
result = cnh_usbhook_invoke_next(irp);
/* Iterate to the end of the list and add our stuff keeping real devices */
it = usb_get_busses();
/* Iterate all busses */
do {
/* First, iterate all devices on each bus and check for all real
devices connected which are also part of our hook list */
if (it != NULL) {
/* Iterate devices on the current bus */
struct usb_device* dev = it->devices;
while (dev != NULL) {
struct cnh_usb_emu_virtdev* virt_dev = _cnh_usb_emu_get_virtdev((uint16_t) dev->descriptor.idVendor,
(uint16_t) dev->descriptor.idProduct);
/* Real device connected for a hook */
if (virt_dev) {
log_debug("Detected real %04X:%04X device for virtual one", dev->descriptor.idVendor,
dev->descriptor.idProduct);
virt_dev->real_dev_avail = true;
}
dev = dev->next;
}
}
/* it == NULL, nothing found on current bus which is our last bus
we added on usb_find_busses. We put our fakedevs on that bus */
if (it == NULL || it->next == NULL) {
struct usb_bus* fakebus;
struct usb_device* fakebus_dev_tail;
/* Create an additional bus for our fakedevs */
fakebus = (struct usb_bus*) util_xmalloc(sizeof(struct usb_bus));
memset(fakebus, 0, sizeof(struct usb_bus));
/* Make sure to modify the linked list correctly. If we fuck up,
this works fine until Fiesta where they started to iterate
everything in a different manner (-> crash) */
if (it == NULL) {
usb_busses = fakebus;
/* Bus empty, no prev bus */
fakebus->prev = NULL;
} else {
/* Append at end of bus chain */
it->next = fakebus;
/* Keep doubly linked list intact */
fakebus->prev = it->next;
}
fakebus_dev_tail = NULL;
pthread_mutex_lock(&_cnh_usb_emu_virtdevs_lock);
for (size_t i = 0; i < _cnh_usb_emu_nvirtdevs; i++) {
struct cnh_usb_emu_virtdev* virtdev = &_cnh_usb_emu_virtdevs[i];
virtdev->enumerated = virtdev->ep->enumerate(virtdev->real_dev_avail);
if (virtdev->enumerated) {
if (virtdev->real_dev_avail) {
/* Suppress real device and detour everything to
our hook */
log_debug("Suppressing real device %04X:%04X", virtdev->ep->vid, virtdev->ep->pid);
} else {
/* Create a fakedev and add it to the bus */
struct usb_device* fakedev;
{
/* Print this once. Pump Pro calls this
function every second */
static int counter = 0;
if (counter++ == 0) {
log_info("Injecting fake usb device %04X:%04X into bus", virtdev->ep->vid,
virtdev->ep->pid);
} else {
counter = 1;
}
}
fakedev = _cnh_usb_emu_create_fakedev(virtdev->ep->vid, virtdev->ep->pid);
/* Append to linked list */
if (!fakebus->devices) {
/* Set root on bus */
fakebus->devices = fakedev;
}
/* Cur element */
fakedev->prev = fakebus_dev_tail;
fakedev->next = NULL;
/* Update prev element's next */
if (fakebus_dev_tail) {
fakebus_dev_tail->next = fakedev;
}
/* tail update */
fakebus_dev_tail = fakedev;
fakedev_count++;
}
} else {
log_debug("Reject enumerate hook for %04X:%04X", virtdev->ep->vid, virtdev->ep->pid);
}
}
pthread_mutex_unlock(&_cnh_usb_emu_virtdevs_lock);
break;
}
/* End of if block which handles fakedevs and hooking */
it = it->next;
}
while (it != NULL);
{
/* Print this once. Pump Pro calls this
function every second */
static int counter = 0;
if (counter++ == 0) {
log_info("Added %d fakedevs to usb bus", fakedev_count);
} else {
counter = 1;
}
}
/* include our fakedev count as well */
return result + fakedev_count;
}
static enum cnh_result _cnh_usb_emu_usbhook_open(struct cnh_usbhook_irp *irp)
{
struct cnh_usb_emu_virtdev* virtdev;
enum cnh_result result;
virtdev = _cnh_usb_emu_get_virtdev((uint16_t) irp->open_usb_dev->descriptor.idVendor,
(uint16_t) irp->open_usb_dev->descriptor.idProduct);
if (virtdev == NULL) {
return cnh_usbhook_invoke_next(irp);
}
if (!virtdev->enumerated) {
log_debug("Virtdev %04X:%04X not enumerated, skipping open", virtdev->ep->vid, virtdev->ep->pid);
return cnh_usbhook_invoke_next(irp);
}
result = virtdev->ep->open();
if (result == CNH_RESULT_SUCCESS) {
/* Return handle, use virtual device as fake handle */
irp->handle = &virtdev->usb_dev_handle;
}
return result;
}
static enum cnh_result _cnh_usb_emu_usbhook_close(struct cnh_usbhook_irp *irp)
{
struct cnh_usb_emu_virtdev* virtdev;
assert(irp->handle);
virtdev = _cnh_usb_emu_get_virtdev_by_handle(irp->handle);
/* Not a virtual device handle, probably real handle from libusb */
if (virtdev == NULL) {
return cnh_usbhook_invoke_next(irp);
}
virtdev->ep->close();
return CNH_RESULT_SUCCESS;
}
static enum cnh_result _cnh_usb_emu_usbhook_reset(struct cnh_usbhook_irp *irp)
{
struct cnh_usb_emu_virtdev* virtdev;
assert(irp->handle);
virtdev = _cnh_usb_emu_get_virtdev_by_handle(irp->handle);
/* Not a virtual device handle, probably real handle from libusb */
if (virtdev == NULL) {
return cnh_usbhook_invoke_next(irp);
}
return virtdev->ep->reset();
}
static enum cnh_result _cnh_usb_emu_usbhook_ctrl_msg(struct cnh_usbhook_irp *irp)
{
struct cnh_usb_emu_virtdev* virtdev;
assert(irp->handle);
virtdev = _cnh_usb_emu_get_virtdev_by_handle(irp->handle);
/* Not a virtual device handle, probably real handle from libusb */
if (virtdev == NULL) {
return cnh_usbhook_invoke_next(irp);
}
return virtdev->ep->control_msg(irp->ctrl_req_type, irp->ctrl_req, irp->ctrl_value, irp->ctrl_index,
&irp->ctrl_buffer, irp->ctrl_timeout);
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* Helper functions */
/* ------------------------------------------------------------------------------------------------------------------ */
void _cnh_usb_emu_init(void)
{
int expected;
if (atomic_load(&_cnh_usb_emu_initted) > 0) {
return;
}
expected = 0;
if (!atomic_compare_exchange_strong(&_cnh_usb_emu_init_in_progress, &expected, 1)) {
while (atomic_load(&_cnh_usb_emu_init_in_progress) > 1) {
util_time_sleep_us(100);
}
return;
}
/* Check again to ensure the current thread yielded between the init'd check and setting init in progress */
if (atomic_load(&_cnh_usb_emu_initted) > 0) {
/* Revert init in progress, because nothing to do anymore */
atomic_store(&_cnh_usb_emu_init_in_progress, 0);
return;
}
pthread_mutex_init(&_cnh_usb_emu_virtdevs_lock, NULL);
atomic_store(&_cnh_usb_emu_initted, 1);
atomic_store(&_cnh_usb_emu_init_in_progress, 0);
}
static struct cnh_usb_emu_virtdev* _cnh_usb_emu_get_virtdev(uint16_t vid, uint16_t pid)
{
struct cnh_usb_emu_virtdev* virtdev;
struct cnh_usb_emu_virtdev* tmp;
virtdev = NULL;
pthread_mutex_lock(&_cnh_usb_emu_virtdevs_lock);
for (size_t i = 0; i < _cnh_usb_emu_nvirtdevs; i++) {
tmp = &_cnh_usb_emu_virtdevs[i];
if (tmp->ep->vid == vid && tmp->ep->pid == pid) {
virtdev = tmp;
break;
}
}
pthread_mutex_unlock(&_cnh_usb_emu_virtdevs_lock);
return virtdev;
}
static struct cnh_usb_emu_virtdev* _cnh_usb_emu_get_virtdev_by_handle(usb_dev_handle* handle)
{
struct cnh_usb_emu_virtdev* virtdev;
struct cnh_usb_emu_virtdev* tmp;
virtdev = NULL;
pthread_mutex_lock(&_cnh_usb_emu_virtdevs_lock);
for (size_t i = 0; i < _cnh_usb_emu_nvirtdevs; i++) {
tmp = &_cnh_usb_emu_virtdevs[i];
if (tmp == handle->virtdev) {
virtdev = tmp;
break;
}
}
pthread_mutex_unlock(&_cnh_usb_emu_virtdevs_lock);
return virtdev;
}
static struct usb_device* _cnh_usb_emu_create_fakedev(uint16_t vid, uint16_t pid)
{
struct usb_device* fakedev = (struct usb_device*) util_xmalloc(sizeof(struct usb_device));
memset(fakedev, 0, sizeof(struct usb_device));
/* Populate fakedev */
fakedev->descriptor.idVendor = vid;
fakedev->descriptor.idProduct = pid;
/* Mark as fakedev */
fakedev->descriptor.bcdDevice = 0xBEEF;
fakedev->config = (struct usb_config_descriptor*) util_xmalloc(
sizeof(struct usb_config_descriptor));
memset(fakedev->config, 0, sizeof(struct usb_config_descriptor));
fakedev->config->interface = (struct usb_interface*) util_xmalloc(
sizeof(struct usb_interface));
memset(fakedev->config->interface, 0, sizeof(struct usb_interface));
fakedev->config->interface->altsetting = (struct usb_interface_descriptor*)
util_xmalloc(sizeof(struct usb_interface_descriptor));
memset(fakedev->config->interface->altsetting, 0,
sizeof(struct usb_interface_descriptor));
return fakedev;
}
+42
View File
@@ -0,0 +1,42 @@
/**
* Hook implementation for emulating usb devices on libusb 0.1
*/
#ifndef CNH_USB_EMU_H
#define CNH_USB_EMU_H
#include <stdbool.h>
#include <stdint.h>
#include "capnhook/hook/iobuf.h"
#include "capnhook/hook/usbhook.h"
/**
* Virtual device endpoint
*/
struct cnh_usb_emu_virtdev_ep {
uint16_t pid;
uint16_t vid;
bool (*enumerate)(bool real_exists);
enum cnh_result (*open)(void);
enum cnh_result (*reset)(void);
enum cnh_result (*control_msg)(int request_type, int request, int value, int index, struct cnh_iobuf* buffer,
int timeout);
void (*close)(void);
};
/**
* Add a pointer to a virtual device endpoint implementing handler functions
*
* @param virtdevep Virtual device endpoint to add
*/
void cnh_usb_emu_add_virtdevep(const struct cnh_usb_emu_virtdev_ep* virtdevep);
/**
* Hook function to add to the hook module for emulating usb devices
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_usb_emu(struct cnh_usbhook_irp *irp);
#endif
+80
View File
@@ -0,0 +1,80 @@
#include <assert.h>
#include "capnhook/hooklib/usb-init-fix.h"
#include "util/log.h"
static enum cnh_result _cnh_usb_init_fix_default(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usb_init_fix_init(struct cnh_usbhook_irp* irp);
static enum cnh_result _cnh_usbio_emu_find_busses(struct cnh_usbhook_irp* irp);
static const cnh_usbhook_fn_t _hook_usb_init_fix_handlers[10] = {
[CNH_USBHOOK_IRP_OP_INIT] = _cnh_usb_init_fix_init,
[CNH_USBHOOK_IRP_OP_FIND_BUSSES] = _cnh_usbio_emu_find_busses,
[CNH_USBHOOK_IRP_OP_FIND_DEVICES] = _cnh_usb_init_fix_default,
[CNH_USBHOOK_IRP_OP_OPEN] = _cnh_usb_init_fix_default,
[CNH_USBHOOK_IRP_OP_CLOSE] = _cnh_usb_init_fix_default,
[CNH_USBHOOK_IRP_OP_RESET] = _cnh_usb_init_fix_default,
[CNH_USBHOOK_IRP_OP_SET_ALTINTERFACE] = _cnh_usb_init_fix_default,
[CNH_USBHOOK_IRP_OP_SET_CONFIGURATION] = _cnh_usb_init_fix_default,
[CNH_USBHOOK_IRP_OP_CLAIM_INTERFACE] = _cnh_usb_init_fix_default,
[CNH_USBHOOK_IRP_OP_CTRL_MSG] = _cnh_usb_init_fix_default,
};
static int _cnh_usb_init_fix_init_count = 0;
enum cnh_result cnh_usb_init_fix(struct cnh_usbhook_irp *irp)
{
cnh_usbhook_fn_t handler;
handler = _hook_usb_init_fix_handlers[irp->op];
assert(handler != NULL);
return handler(irp);
}
static enum cnh_result _cnh_usb_init_fix_default(struct cnh_usbhook_irp* irp)
{
return cnh_usbhook_invoke_next(irp);
}
static enum cnh_result _cnh_usb_init_fix_init(struct cnh_usbhook_irp* irp)
{
/* On NXA, one programmer/multiple programmers had the bright idea to call
usb_init twice for reasons...
This breaks stuff in the usb-0.1 backend resulting in usb_find_busses
not even finding native devices anymore.
Make sure we call the real usb_init ONCE ONLY to fix that fuck up */
_cnh_usb_init_fix_init_count++;
if (_cnh_usb_init_fix_init_count <= 1) {
return cnh_usbhook_invoke_next(irp);
} else {
/* Pump Pro is even worse and calls usb_init approx. every second...
avoid overflow of counter if we really leave this running forever.
Furthermore, this avoids printing the log message below with ever
init call */
if (_cnh_usb_init_fix_init_count > 2) {
_cnh_usb_init_fix_init_count = 2;
} else {
log_info("usb_init called multiple times, blocking further calls...");
}
return CNH_RESULT_SUCCESS;
}
}
static enum cnh_result _cnh_usbio_emu_find_busses(struct cnh_usbhook_irp* irp)
{
enum cnh_result result;
/* Fixes quirks with usb_init called multiple times on NXA */
if (_cnh_usb_init_fix_init_count <= 1) {
result = cnh_usbhook_invoke_next(irp);
} else {
result = CNH_RESULT_SUCCESS;
}
return result;
}
+17
View File
@@ -0,0 +1,17 @@
/**
* Hook implementation to fix buggy applications that call usb_init repeatetly
*/
#ifndef CNH_USB_INIT_FIX_H
#define CNH_USB_INIT_FIX_H
#include "capnhook/hook/usbhook.h"
/**
* Hook function to add to the hook module to fix the usb_init bug
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_usb_init_fix(struct cnh_usbhook_irp *irp);
#endif
+55
View File
@@ -0,0 +1,55 @@
#define LOG_MODULE "cnh-usbhook-mon"
#include "capnhook/hooklib/usbhook-mon.h"
#include "util/log.h"
static const char* _cnh_usbhook_mon_irp_op_str[] = {
"init",
"find_busses",
"find_devices",
"open",
"close",
"reset",
"set_altinterface",
"set_configuration",
"claim_interface",
"ctrl_msg"
};
enum cnh_result cnh_usbhook_mon(struct cnh_usbhook_irp *irp)
{
enum cnh_result result;
log_debug("[before][%s] next_handler %d, find_busses_res_num_busses %d, find_devices_res_num_devices %d, "
"open_usb_dev %p, handle %p, set_altinterface %d, set_configuration %d, claim_interface %d, ctrl_req_type %d, "
"ctrl_req %d, ctrl_value %d, ctrl_index %d, ctrl_buffer(bytes %p, nbytes %d, pos %d), ctrl_timeout %d",
_cnh_usbhook_mon_irp_op_str[irp->op], irp->next_handler, irp->find_busses_res_num_busses,
irp->find_devices_res_num_devices, irp->open_usb_dev, irp->handle, irp->set_altinterface,
irp->set_configuration, irp->claim_interface, irp->ctrl_req_type, irp->ctrl_req, irp->ctrl_value,
irp->ctrl_index, irp->ctrl_buffer.bytes, irp->ctrl_buffer.nbytes, irp->ctrl_buffer.pos, irp->ctrl_timeout);
result = cnh_usbhook_invoke_next(irp);
if (result != CNH_RESULT_SUCCESS) {
log_error("[after][%s] result %d, next_handler %d, find_busses_res_num_busses %d, "
"find_devices_res_num_devices %d, open_usb_dev %p, handle %p, set_altinterface %d, set_configuration %d, "
"claim_interface %d, ctrl_req_type %d, ctrl_req %d, ctrl_value %d, ctrl_index %d, ctrl_buffer(bytes %p, "
"nbytes %d, pos %d), ctrl_timeout %d", _cnh_usbhook_mon_irp_op_str[irp->op], result, irp->next_handler,
irp->find_busses_res_num_busses, irp->find_devices_res_num_devices, irp->open_usb_dev, irp->handle,
irp->set_altinterface, irp->set_configuration, irp->claim_interface, irp->ctrl_req_type, irp->ctrl_req,
irp->ctrl_value, irp->ctrl_index, irp->ctrl_buffer.bytes, irp->ctrl_buffer.nbytes, irp->ctrl_buffer.pos,
irp->ctrl_timeout);
} else {
log_debug("[after][%s] result %d, next_handler %d, find_busses_res_num_busses %d, "
"find_devices_res_num_devices %d, open_usb_dev %p, handle %p, set_altinterface %d, set_configuration %d, "
"claim_interface %d, ctrl_req_type %d, ctrl_req %d, ctrl_value %d, ctrl_index %d, ctrl_buffer(bytes %p, "
"nbytes %d, pos %d), ctrl_timeout %d", _cnh_usbhook_mon_irp_op_str[irp->op], result, irp->next_handler,
irp->find_busses_res_num_busses, irp->find_devices_res_num_devices, irp->open_usb_dev, irp->handle,
irp->set_altinterface, irp->set_configuration, irp->claim_interface, irp->ctrl_req_type, irp->ctrl_req,
irp->ctrl_value, irp->ctrl_index, irp->ctrl_buffer.bytes, irp->ctrl_buffer.nbytes, irp->ctrl_buffer.pos,
irp->ctrl_timeout);
}
return result;
}
+17
View File
@@ -0,0 +1,17 @@
/**
* Hook implementation to monitor libusb 0.1 calls
*/
#ifndef CAPNHOOK_USBHOOK_MON_H
#define CAPNHOOK_USBHOOK_MON_H
#include "capnhook/hook/usbhook.h"
/**
* Hook function to add to the hook module for monitoring libusb calls
*
* @param irp I/O request package of hook module received
* @return Result of operation
*/
enum cnh_result cnh_usbhook_mon(struct cnh_usbhook_irp *irp);
#endif
+29
View File
@@ -0,0 +1,29 @@
#define LOG_MODULE "crypt-aes"
#include "lib/rijndael.h"
#include "aes.h"
void crypt_aes_enc(uint8_t* key, enum crypt_aes_key_length key_len,
uint8_t* out, const uint8_t* in, size_t nbytes)
{
unsigned long rk[RKLENGTH(128)];
int nrounds;
nrounds = rijndaelSetupEncrypt(rk, key, key_len * 8);
for (size_t i = 0; i < nbytes / key_len; i++) {
rijndaelEncrypt(rk, nrounds, &in[i * key_len], &out[i * key_len]);
}
}
void crypt_aes_dec(uint8_t* key, enum crypt_aes_key_length key_len,
uint8_t* out, const uint8_t* in, size_t nbytes)
{
unsigned long rk[RKLENGTH(128)];
int nrounds;
nrounds = rijndaelSetupDecrypt(rk, key, key_len * 8);
for (size_t i = 0; i < nbytes / key_len; i++) {
rijndaelDecrypt(rk, nrounds, &in[i * key_len], &out[i * key_len]);
}
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef CRYPT_AES_H
#define CRYPT_AES_H
#include <stdint.h>
#include <stdlib.h>
/**
* AES enc/dec key lengths
*/
enum crypt_aes_key_length {
/* 128 bit */
CRYPT_AES_KEY_LENGTH_16_BYTES = 16,
/* 192 bit */
CRYPT_AES_KEY_LENGTH_24_BYTES = 24,
/* 256 bit */
CRYPT_AES_KEY_LENGTH_32_BYTES = 32,
};
/**
* Encrypt data
*
* @param key AES key to use
* @param key_len Length of the key
* @param out Output buffer encrypted data
* @param in Input buffer to encrypt
* @param nbytes Number of bytes of input buffer to encrypt
*/
void crypt_aes_enc(uint8_t* key, enum crypt_aes_key_length key_len,
uint8_t* out, const uint8_t* in, size_t nbytes);
/**
* Decrypt data
*
* @param key AES key to use
* @param key_len Length of the key
* @param out Output buffer for decrypted data
* @param in Input buffer to decrypt
* @param nbytes Number of bytes of input buffer to decrypt
*/
void crypt_aes_dec(uint8_t* key, enum crypt_aes_key_length key_len,
uint8_t* out, const uint8_t* in, size_t nbytes);
#endif
+333
View File
@@ -0,0 +1,333 @@
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.
License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this
documentation and/or software.
*/
#include "md5.h"
/* Constants for MD5Transform routine.
*/
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21
static void MD5Transform (uint32_t [4], unsigned char [64]);
static void Encode(unsigned char *, uint32_t *, unsigned int);
static void Decode(uint32_t *, unsigned char *, unsigned int);
static void MD5_memcpy(uint8_t *, uint8_t *, unsigned int);
static void MD5_memset(uint8_t *, int, unsigned int);
static unsigned char PADDING[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* F, G, H and I are basic MD5 functions.
*/
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
/* ROTATE_LEFT rotates x left n bits.
*/
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation.
*/
#define FF(a, b, c, d, x, s, ac) { \
(a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) { \
(a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) { \
(a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) { \
(a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
/* MD5 initialization. Begins an MD5 operation, writing a new context.
*/
void MD5Init (context)
MD5_CTX *context; /* context */
{
context->count[0] = context->count[1] = 0;
/* Load magic initialization constants.
*/
context->state[0] = 0x67452301;
context->state[1] = 0xefcdab89;
context->state[2] = 0x98badcfe;
context->state[3] = 0x10325476;
}
/* MD5 block update operation. Continues an MD5 message-digest
operation, processing another message block, and updating the
context.
*/
void MD5Update (context, input, inputLen)
MD5_CTX *context; /* context */
unsigned char *input; /* input block */
unsigned int inputLen; /* length of input block */
{
unsigned int i, index, partLen;
/* Compute number of bytes mod 64 */
index = (unsigned int)((context->count[0] >> 3) & 0x3F);
/* Update number of bits */
if ((context->count[0] += ((uint32_t)inputLen << 3))
< ((uint32_t)inputLen << 3))
context->count[1]++;
context->count[1] += ((uint32_t)inputLen >> 29);
partLen = 64 - index;
/* Transform as many times as possible.
*/
if (inputLen >= partLen) {
MD5_memcpy
((uint8_t *)&context->buffer[index], (uint8_t *)input, partLen);
MD5Transform (context->state, context->buffer);
for (i = partLen; i + 63 < inputLen; i += 64)
MD5Transform (context->state, &input[i]);
index = 0;
}
else
i = 0;
/* Buffer remaining input */
MD5_memcpy
((uint8_t *)&context->buffer[index], (uint8_t *)&input[i],
inputLen-i);
}
/* MD5 finalization. Ends an MD5 message-digest operation, writing the
the message digest and zeroizing the context.
*/
void MD5Final (digest, context)
unsigned char digest[16]; /* message digest */
MD5_CTX *context; /* context */
{
unsigned char bits[8];
unsigned int index, padLen;
/* Save number of bits */
Encode (bits, context->count, 8);
/* Pad out to 56 mod 64.
*/
index = (unsigned int)((context->count[0] >> 3) & 0x3f);
padLen = (index < 56) ? (56 - index) : (120 - index);
MD5Update (context, PADDING, padLen);
/* Append length (before padding) */
MD5Update (context, bits, 8);
/* Store state in digest */
Encode (digest, context->state, 16);
/* Zeroize sensitive information.
*/
MD5_memset ((uint8_t *)context, 0, sizeof (*context));
}
/* MD5 basic transformation. Transforms state based on block.
*/
static void MD5Transform (state, block)
uint32_t state[4];
unsigned char block[64];
{
uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
Decode (x, block, 64);
/* Round 1 */
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
/* Round 2 */
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
/* Round 3 */
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
/* Round 4 */
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
/* Zeroize sensitive information.
*/
MD5_memset ((uint8_t *)x, 0, sizeof (x));
}
/* Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
static void Encode (output, input, len)
unsigned char *output;
uint32_t *input;
unsigned int len;
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4) {
output[j] = (unsigned char)(input[i] & 0xff);
output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
}
}
/* Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
static void Decode (output, input, len)
uint32_t *output;
unsigned char *input;
unsigned int len;
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) |
(((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24);
}
/* Note: Replace "for loop" with standard memcpy if possible.
*/
static void MD5_memcpy (output, input, len)
uint8_t * output;
uint8_t * input;
unsigned int len;
{
unsigned int i;
for (i = 0; i < len; i++)
output[i] = input[i];
}
/* Note: Replace "for loop" with standard memset if possible.
*/
static void MD5_memset (output, value, len)
uint8_t * output;
int value;
unsigned int len;
{
unsigned int i;
for (i = 0; i < len; i++)
((char *)output)[i] = (char)value;
}
+35
View File
@@ -0,0 +1,35 @@
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.
License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this
documentation and/or software.
*/
#include <stdint.h>
/* MD5 context. */
typedef struct {
uint32_t state[4]; /* state (ABCD) */
uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64]; /* input buffer */
} MD5_CTX;
void MD5Init(MD5_CTX *);
void MD5Update(MD5_CTX *, unsigned char *, unsigned int);
void MD5Final(unsigned char [16], MD5_CTX *);
File diff suppressed because it is too large Load Diff
+17
View File
@@ -0,0 +1,17 @@
#ifndef H__RIJNDAEL
#define H__RIJNDAEL
int rijndaelSetupEncrypt(unsigned long *rk, const unsigned char *key,
int keybits);
int rijndaelSetupDecrypt(unsigned long *rk, const unsigned char *key,
int keybits);
void rijndaelEncrypt(const unsigned long *rk, int nrounds,
const unsigned char plaintext[16], unsigned char ciphertext[16]);
void rijndaelDecrypt(const unsigned long *rk, int nrounds,
const unsigned char ciphertext[16], unsigned char plaintext[16]);
#define KEYLENGTH(keybits) ((keybits)/8)
#define RKLENGTH(keybits) ((keybits)/8+28)
#define NROUNDS(keybits) ((keybits)/32+6)
#endif
+14
View File
@@ -0,0 +1,14 @@
#define LOG_MODULE "crypt-md5"
#include "lib/md5.h"
#include "md5.h"
void crypt_md5_hash(uint8_t* out, const uint8_t* in, size_t nbytes)
{
MD5_CTX ctx;
MD5Init(&ctx);
MD5Update(&ctx, (unsigned char*) in, (unsigned int) nbytes);
MD5Final(out, &ctx);
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef CRYPT_MD5_H
#define CRYPT_MD5_H
#include <stdint.h>
#include <stdlib.h>
/**
* Create an MD5 hash
*
* @param out Output buffer
* @param in Input buffer to hash
* @param nbytes Number of bytes of input buffer to process
*/
void crypt_md5_hash(uint8_t* out, const uint8_t* in, size_t nbytes);
#endif
+50
View File
@@ -0,0 +1,50 @@
#include "hook/core/options.h"
#include "util/options.h"
#define HOOK_CORE_OPTIONS_STR_PATCH_UTIL_LOG_FILE "util.log.file"
#define HOOK_CORE_OPTIONS_STR_PATCH_UTIL_LOG_LEVEL "util.log.level"
const struct util_options_def hook_core_options_def[] = {
{
.name = HOOK_CORE_OPTIONS_STR_PATCH_UTIL_LOG_FILE,
.description = "Print the log output to the specified file",
.param = 'o',
.type = UTIL_OPTIONS_TYPE_STR,
.default_value.str = "pumptools.log",
},
{
.name = HOOK_CORE_OPTIONS_STR_PATCH_UTIL_LOG_LEVEL,
.description = "Set the log level (0-4)",
.param = 'l',
.type = UTIL_OPTIONS_TYPE_INT,
.default_value.i = LOG_LEVEL_DEBUG,
},
};
const struct util_options_defs hook_core_options_defs = {
.usage_header = "",
// Just use something because this is not relevant/required here
.usage_param = '\r',
.defs = hook_core_options_def,
.ndefs = lengthof(hook_core_options_def)
};
void hook_core_options_init(int argc, char** argv, struct hook_core_options* options)
{
log_assert(argv);
log_assert(options);
struct util_options_opts* options_opt;
util_options_init(argc, argv);
options_opt = util_options_get(&hook_core_options_defs);
if (!options_opt) {
return;
}
options->log.file = util_options_get_str(options_opt, HOOK_CORE_OPTIONS_STR_PATCH_UTIL_LOG_FILE);
options->log.level =
(enum util_log_level) util_options_get_int(options_opt, HOOK_CORE_OPTIONS_STR_PATCH_UTIL_LOG_LEVEL);
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef HOOK_CORE_OPTIONS_H
#define HOOK_CORE_OPTIONS_H
#include "util/log.h"
struct hook_core_options {
struct log {
const char* file;
enum util_log_level level;
} log;
};
void hook_core_options_init(int argc, char** argv, struct hook_core_options* options);
#endif
+133
View File
@@ -0,0 +1,133 @@
#define LOG_MODULE "hook-core-piu-utils"
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include "hook/core/options.h"
#include "util/fs.h"
#include "util/glibc.h"
#include "util/log.h"
#include "util/proc.h"
#include "util/str.h"
#include "util/sys-info.h"
static void hook_core_piu_log_application_args(int argc, char** argv)
{
const char* sep = "\n";
char* str = util_str_dup("");
char* tmp;
for (int i = 0; i < argc; i++) {
if (strlen(str) > 0) {
tmp = util_str_merge(str, sep);
free(str);
str = tmp;
}
tmp = util_str_merge(str, argv[i]);
free(str);
str = tmp;
}
log_info("Application arguments (%d):\n%s", argc, str);
free(str);
}
void hook_core_piu_log_init(int argc, char** argv)
{
struct hook_core_options options;
hook_core_options_init(argc, argv, &options);
util_log_set_file(options.log.file, false);
util_log_set_level(options.log.level);
}
void hook_core_piu_log_info(int argc, char** argv)
{
log_info("\n"
" _ _ \n"
" _ __ _ _ _ __ ___ _ __ | |_ ___ ___ | |___ \n"
" | '_ \\| | | | '_ ` _ \\| '_ \\| __/ _ \\ / _ \\| / __|\n"
" | |_) | |_| | | | | | | |_) | || (_) | (_) | \\__ \\\n"
" | .__/ \\__,_|_| |_| |_| .__/ \\__\\___/ \\___/|_|___/\n"
" |_| |_| ");
log_info("Pumptools build " __DATE__ " " __TIME__ " gitrev %s", STRINGIFY(GITREV));
util_sys_info_log();
util_proc_log_info();
util_glibc_info_log();
hook_core_piu_log_application_args(argc, argv);
}
char* hook_core_piu_utils_get_and_verify_game_data_path(int argc, char** argv)
{
if (argc < 2) {
log_error("Missing game data path, usage for piu (stock args): piu <game folder> (e.g. piu ./game)");
exit(0);
}
char* game_data_path = util_str_dup(argv[1]);
log_info("Game data path: %s", game_data_path);
/* remove trailing / for the hook. however, the game crashes without it */
if (util_str_ends_with(game_data_path, "/")) {
game_data_path[strlen(game_data_path) - 1] = '\0';
} else {
log_error("Game data path '%s' does not end with a trailing /, exiting because the game is going to "
"crash anyway!", game_data_path);
exit(0);
}
/* depending on the version, the game also just crashes if the game folder or files within it were not found */
if (!util_fs_path_exists(game_data_path)) {
log_error("Game data path '%s' does not exist, exiting", game_data_path);
exit(0);
}
return game_data_path;
}
void hook_core_piu_utils_verify_root_user()
{
log_info("Verifying process is running as root user.");
if (!util_proc_is_running_as_root()) {
log_error("Process not running as root which is required for the game to work correctly.");
//exit(0);
}
}
void hook_core_piu_utils_fix_locale()
{
log_info("Fixing locale settings.");
if (setenv("LC_ALL", "C", 1) != 0) {
log_warn("Setting locale LC_ALL failed.");
}
if (unsetenv("LANGUAGE") != 0) {
log_warn("Unsetting LANGUAGE env var failed.");
}
}
bool hook_core_piu_block_non_piu_process_recursion(int argc, char** argv)
{
/* Sanity check for arguments. If further child processes are started
from the current process, and using LD_PRELOAD on the current process,
all children are also getting the preloaded library. This causes
multiple calls to the trap main function and just crashes our
application at some point.
The executable file is required to contain the "piu" substring to make
this work properly */
if (!strstr(argv[0], "piu")) {
log_warn("Not attaching to non piu process %s", argv[0]);
return true;
} else {
return false;
}
}
+53
View File
@@ -0,0 +1,53 @@
#ifndef HOOK_CORE_PIU_UTILS_H
#define HOOK_CORE_PIU_UTILS_H
/**
* Initialize the logging system as early as possible to allow recording everything to a log file as well.
*/
void hook_core_piu_log_init(int argc, char** argv);
/**
* Log process and system relevant information.
*/
void hook_core_piu_log_info(int argc, char** argv);
/**
* Check and verify the game data path argument passed to the game via argv
*
* Executes various checks and catches errors that might result in the game
* simply crashing without any error indication.
*
* @param argc Argc from piu main
* @param argv Argv from piu main
* @return New string with game data path extracted from argv (caller has to free)
*/
char* hook_core_piu_utils_get_and_verify_game_data_path(int argc, char** argv);
/**
* Verify that the current process is running as root user because the game
* needs access to various devices that require higher privileges.
*
* Does not return and errors if not running as root user.
*/
void hook_core_piu_utils_verify_root_user();
/**
* Fix the locale configuration for Fiesta 2 and newer.
*/
void hook_core_piu_utils_fix_locale();
/**
* If further child processes are started from the current process, and using
* LD_PRELOAD on the current process, all children are also getting the preloaded
* library. This causes multiple calls to the trap main function and just crashes our
* application at some point. The executable file is required to contain the "piu"
* substring to make this work properly.
*
* @param argc Argc from piu main
* @param argv Argv from piu main
* @return True if we have to block this call by another process,
* false if the process calling this is the piu process.
*/
bool hook_core_piu_block_non_piu_process_recursion(int argc, char** argv);
#endif
+98
View File
@@ -0,0 +1,98 @@
#define LOG_MODULE "exchook-eeprom"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "hook/exc/mempatch.h"
#include "util/fs.h"
#include "util/log.h"
#include "util/patch.h"
static int32_t exchook_eeprom_read(uint8_t* buffer, int32_t len);
static int32_t exchook_eeprom_write(const uint8_t* buffer, int32_t len);
static char* exchook_eeprom_file;
static uint8_t exchook_eeprom_buffer[8192];
void exchook_eeprom_init(const struct exchook_mempatch_table* patch_table, const char* eeprom_file)
{
void* buffer;
size_t len;
exchook_eeprom_file = util_fs_get_abs_path(eeprom_file);
/* EEPROM file does not exist, create new */
if (!exchook_eeprom_file) {
memset(exchook_eeprom_buffer, 0xFF, sizeof(exchook_eeprom_buffer));
FILE* file = fopen(eeprom_file, "wb+");
if (file == NULL) {
log_error("Creating new eeprom file %s failed: %s", eeprom_file, strerror(errno));
}
fwrite(exchook_eeprom_buffer, sizeof(exchook_eeprom_buffer), 1, file);
fclose(file);
exchook_eeprom_file = util_fs_get_abs_path(eeprom_file);
len = (size_t) -1;
} else {
if (util_file_load(exchook_eeprom_file, &buffer, &len, false)) {
if (len > sizeof(exchook_eeprom_buffer)) {
log_error("Contents of %s eeprom file exceed the max eeprom buffer "
" size of %d bytes", eeprom_file,
sizeof(exchook_eeprom_buffer));
free(buffer);
return;
}
/* copy read contents */
memcpy(exchook_eeprom_buffer, buffer, len);
free(buffer);
} else {
log_error("Loading eeprom file %s failed", exchook_eeprom_file);
}
}
log_assert(exchook_eeprom_file);
/* Hook eeprom */
util_patch_function(patch_table->addr_eeprom_read, exchook_eeprom_read);
util_patch_function(patch_table->addr_eeprom_write, exchook_eeprom_write);
log_info("Initizlied (%s), eeprom file %s", len == -1 ? "new" : "loaded", eeprom_file);
}
static int32_t exchook_eeprom_read(uint8_t* buffer, int32_t len)
{
if (len > sizeof(exchook_eeprom_buffer)) {
log_error("Eeprom read %d exceeds buffer size %d", len,
sizeof(exchook_eeprom_buffer));
return 0;
}
memcpy(buffer, exchook_eeprom_buffer, len);
return len;
}
static int32_t exchook_eeprom_write(const uint8_t* buffer, int32_t len)
{
if (len > sizeof(exchook_eeprom_buffer)) {
log_error("Eeprom write %d exceeds buffer size %d", len,
sizeof(exchook_eeprom_buffer));
return 0;
}
memcpy(exchook_eeprom_buffer, buffer, len);
/* Write back to file */
if (!util_file_save(exchook_eeprom_file, exchook_eeprom_buffer, len)) {
log_error("Storing eeprom contents to file %s failed",
exchook_eeprom_file);
}
return len;
}
+19
View File
@@ -0,0 +1,19 @@
/**
* Patch module emulating the MK5 (hardware) eeprom chip used to store
* game settings to on Exceed
*/
#ifndef EXCHOOK_EEPROM_H
#define EXCHOOK_EEPROM_H
#include "hook/exc/mempatch.h"
/**
* Init eeprom emulation for exceed
*
* @param patch_table Patch table with memory addresses
* @param eeprom_file Path to existing eeprom file to read from and new eeprom
* file to store to
*/
void exchook_eeprom_init(const struct exchook_mempatch_table* patch_table, const char* eeprom_file);
#endif

Some files were not shown because too many files have changed in this diff Show More