Files
Mike Nelson e01a5359ac Scpui API for Control Config (#5305)
* read access to controls

* Early draft of checking keypresses in scpui

* detangle some code and prepare it for API usage

* API access to the core methods required

* move these checks into the UI methods

* comment

* fix bugs

* be smarter

* Need this stuff, too

* some more presets work

* unify func naming scheme

* cleanup and clarifications

* get detect keypress working

* allow searching and creating new presets based on current controls

* address feedback

* return nil if no preset is matched

* use the builtin methods which do the same thing, but slightly cleaner

* typo
2023-04-07 12:02:13 -04:00

243 lines
5.2 KiB
C++

#pragma once
#include "cfile/cfile.h"
#include "controlconfig/controlsconfig.h"
#include "globalincs/pstypes.h"
#include "globalincs/vmallocator.h"
#include <jansson.h>
/** API for saving and loading player custom bindings, known as Presets
Init. If the player/preset dir doesn't exist. Create it, and print a help.txt with all enums (as strings) to label
the button names and action names.
Example output:
{
"actions": {
"TARGET_NEXT": {
"first": {
"cid": 0,
"flags": 0,
"btn": 0
},
"second": {
"cid": 0,
"flags": 0,
"btn": 0
}
},
"TARGET_PREV" : {
"first": {
"cid": 0,
"flags": 0,
"btn": 0
},
"second": {
"cid": 0,
"flags": 0,
"btn": 0
}
},
},
"version": 0
}
*/
namespace io {
namespace presets {
static const unsigned int PST_VERSION = 0;
static const unsigned int PST_FILE_ID = 0x5f545350; // "PST_" in file
enum class Section {
Invalid = -1,
Unnamed = 0,
// arrays
Actions,
// subsections
Primary,
Secondary
};
class PresetFileHandler {
public:
PresetFileHandler(CFILE* cfp, bool reading);
~PresetFileHandler();
/**
* begins writing to a section using the presets::Section as its name
* @param[in] s id of the section
*/
void beginSectionWrite(presets::Section s);
/**
* begins reading a section, if it exists
* @param[in] s id of the section
*/
bool beginSectionRead(presets::Section s);
/**
* Begins writing to an array using the presets::Section as its name
*
* @param[in] s id of the array
*/
void beginArrayWrite(presets::Section s);
/**
* Begins reading from an array
* @param[int] s id of the array
* @param[out] size the size of the array
*/
bool beginArrayRead(presets::Section s, size_t &size);
/**
* ends writing to a section
*/
void endSectionWrite();
/**
* ends reading a section
*/
void endSectionRead();
/**
* ends writing to an array.
*/
void endArrayWrite();
/**
* ends reading from an array
*/
void endArrayRead();
/**
* Writes a string with the given key name
*/
void writeString(const char* key, SCP_string s);
/**
* Reads a string with the given key name
*/
SCP_string readString(const char* key);
/**
* Writes an int with the given key name
*/
void writeInt(const char* key, int val);
/**
* Reads an int with the given key name
*/
int readInt(const char* key);
/**
* For writing, Flushes the buffers and dumps to file
*/
void flush();
/**
* Advances the array parser to the next element
*/
void nextArrayElement();
private:
CFILE* _cfp = nullptr;
json_t* _rootObj = nullptr;
json_t* _currentEl = nullptr;
/**
* BufferStack of all json elements being read/written
*/
SCP_vector<json_t*> _elementStack;
/**
* Stack to keep track of the heirachy/nesting. Back is immediate parent, Front is root
*/
SCP_vector<void*> _parentStack;
size_t _arrayIndex = INVALID_SIZE;
//void* _sectionIterator = nullptr; //!< Pointer to the current section/array we're in
private:
/**
* Pushes an element onto the elementStack
*/
void pushElement(json_t* el);
/**
* Pops an element off the elementStack
*/
json_t * popElement();
/**
* Used for optionals, checks if an element exists with the given name
* @return True if the element exists
* @return False otherwise
*/
bool exists(const char* name);
/**
* Throws an error if an element with the given name does not exist
*/
void ensureExists(const char* name);
/**
* Throws an error if an element with the given name exists
*/
void ensureNotExists(const char* name);
};
} // namespace presets
} // namespace io
/**
* @brief Saves the given preset to file.
* @param[in] preset The preset to save
* @param[in] overwrite If true, overwrite existing preset file which have the same name as the given preset
*
* @returns True if successful, or
* @returns False Preset does not have a name, or
* @returns False Preset file exists and overwrite == false, or
* @returns False Preset file .JSON could not be written
*/
bool save_preset_file(CC_preset preset, bool overwrite);
/**
* @brief Deletes the given preset to file.
* @param[in] preset The preset to delete
*
* @returns True if successful, or
* @returns False Preset is default, or
* @returns False Preset file is the currently selected preset
*/
bool delete_preset_file(CC_preset preset);
/**
* @brief Loads in all preset .json files from 'players/presets'
* @details If a preset file is unique, it is loaded into the game and available for use by the player. However, if
* a preset is a duplicate of another preset, it is ignored, and the player is warned of it
*
* @param[in] clone Used to check if we just cloned a particular preset and allows just that clone in without warning
*/
void load_preset_files(SCP_string clone = "");
/**
* @brief Checks if the given preset is a duplicate within Control_config_presets vector
* @returns iterator to the duplicate if found, or
* @returns iterator to Control_config_presets.end() otherwise
*/
SCP_vector<CC_preset>::iterator preset_find_duplicate(const CC_preset& new_preset);
/**
* @brief Returns true if a preset file with the given name exists.
*/
bool preset_file_exists(SCP_string name);