mirror of
https://gitea.tendokyu.moe/TeamTofuShop/bananatools.git
synced 2026-10-11 16:58:23 +03:00
move code from segatools
This commit is contained in:
+200
-21
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "util/crc.h"
|
||||
#include "util/dprintf.h"
|
||||
#include "util/env.h"
|
||||
|
||||
struct aime_io_config {
|
||||
wchar_t aime_path[MAX_PATH];
|
||||
@@ -26,6 +27,8 @@ static uint8_t aime_io_aime_id[10];
|
||||
static uint8_t aime_io_felica_id[8];
|
||||
static bool aime_io_aime_id_present;
|
||||
static bool aime_io_felica_id_present;
|
||||
static bool aime_io_radio_on = true;
|
||||
static bool aime_io_update_mode;
|
||||
|
||||
static void aime_io_config_read(
|
||||
struct aime_io_config *cfg,
|
||||
@@ -217,12 +220,12 @@ static HRESULT aime_io_generate_aime(
|
||||
|
||||
uint16_t aime_io_get_api_version(void)
|
||||
{
|
||||
return 0x0100;
|
||||
return 0x0101;
|
||||
}
|
||||
|
||||
HRESULT aime_io_init(void)
|
||||
{
|
||||
aime_io_config_read(&aime_io_cfg, L".\\bananatools.ini");
|
||||
aime_io_config_read(&aime_io_cfg, get_config_path());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -232,10 +235,6 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
||||
bool sense;
|
||||
HRESULT hr;
|
||||
|
||||
if (unit_no != 0) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Reset presence flags */
|
||||
|
||||
aime_io_aime_id_present = false;
|
||||
@@ -243,6 +242,10 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
||||
|
||||
/* Don't do anything more if the scan key is not held */
|
||||
|
||||
if (!aime_io_radio_on) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
sense = GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000;
|
||||
|
||||
if (!sense) {
|
||||
@@ -262,6 +265,20 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Try FeliCa IC */
|
||||
|
||||
hr = aime_io_read_id_file(
|
||||
aime_io_cfg.felica_path,
|
||||
aime_io_felica_id,
|
||||
sizeof(aime_io_felica_id));
|
||||
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE) {
|
||||
aime_io_felica_id_present = true;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// 2026-08-24 Hay1tsme: Skipping Generating aime before checking for felica was incredibly annoying.
|
||||
/* Try generating AiMe IC (if enabled) */
|
||||
|
||||
if (aime_io_cfg.aime_gen) {
|
||||
@@ -278,19 +295,6 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Try FeliCa IC */
|
||||
|
||||
hr = aime_io_read_id_file(
|
||||
aime_io_cfg.felica_path,
|
||||
aime_io_felica_id,
|
||||
sizeof(aime_io_felica_id));
|
||||
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE) {
|
||||
aime_io_felica_id_present = true;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Try generating FeliCa IC (if enabled) */
|
||||
|
||||
if (aime_io_cfg.felica_gen) {
|
||||
@@ -317,7 +321,7 @@ HRESULT aime_io_nfc_get_aime_id(
|
||||
assert(luid != NULL);
|
||||
assert(luid_size == sizeof(aime_io_aime_id));
|
||||
|
||||
if (unit_no != 0 || !aime_io_aime_id_present) {
|
||||
if (!aime_io_aime_id_present) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
@@ -333,7 +337,7 @@ HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm)
|
||||
|
||||
assert(IDm != NULL);
|
||||
|
||||
if (unit_no != 0 || !aime_io_felica_id_present) {
|
||||
if (!aime_io_felica_id_present) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
@@ -348,5 +352,180 @@ HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_get_mifare_uid(
|
||||
uint8_t unit_no,
|
||||
uint8_t *uid,
|
||||
size_t uid_size)
|
||||
{
|
||||
(void) uid;
|
||||
(void) uid_size;
|
||||
|
||||
if (unit_no != 0 || !aime_io_aime_id_present) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_mifare_select(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size)
|
||||
{
|
||||
(void) uid;
|
||||
(void) uid_size;
|
||||
|
||||
if (unit_no != 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_mifare_set_key(
|
||||
uint8_t unit_no,
|
||||
uint8_t key_type,
|
||||
const uint8_t *key,
|
||||
size_t key_size)
|
||||
{
|
||||
(void) key_type;
|
||||
(void) key;
|
||||
(void) key_size;
|
||||
|
||||
if (unit_no != 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_mifare_authenticate(
|
||||
uint8_t unit_no,
|
||||
uint8_t key_type,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size)
|
||||
{
|
||||
(void) key_type;
|
||||
(void) payload;
|
||||
(void) payload_size;
|
||||
|
||||
if (unit_no != 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_mifare_read_block(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size,
|
||||
uint8_t block_no,
|
||||
uint8_t *block,
|
||||
size_t block_size)
|
||||
{
|
||||
(void) uid;
|
||||
(void) uid_size;
|
||||
(void) block_no;
|
||||
(void) block;
|
||||
(void) block_size;
|
||||
|
||||
if (unit_no != 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_felica_transact(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *req,
|
||||
size_t req_size,
|
||||
uint8_t *res,
|
||||
size_t res_size,
|
||||
size_t *res_size_written)
|
||||
{
|
||||
(void) req;
|
||||
(void) req_size;
|
||||
(void) res;
|
||||
(void) res_size;
|
||||
(void) res_size_written;
|
||||
|
||||
if (unit_no != 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_radio_on(uint8_t unit_no)
|
||||
{
|
||||
if (unit_no != 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
aime_io_radio_on = true;
|
||||
aime_io_update_mode = false;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_radio_off(uint8_t unit_no)
|
||||
{
|
||||
if (unit_no != 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
aime_io_radio_on = false;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_to_update_mode(uint8_t unit_no)
|
||||
{
|
||||
if (unit_no != 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
aime_io_update_mode = true;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT aime_io_nfc_send_hex_data(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size,
|
||||
uint8_t *status_out)
|
||||
{
|
||||
(void) payload;
|
||||
(void) payload_size;
|
||||
|
||||
if (unit_no != 0) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
if (status_out != NULL) {
|
||||
*status_out = (payload_size == 0x2b) ? 0x20 : 0x00;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b)
|
||||
{}
|
||||
|
||||
void aime_io_vfd_set_text(
|
||||
const uint8_t *text,
|
||||
size_t text_len,
|
||||
const struct aime_io_vfd_state *state)
|
||||
{
|
||||
(void) text;
|
||||
(void) text_len;
|
||||
(void) state;
|
||||
}
|
||||
|
||||
void aime_io_vfd_set_state(const struct aime_io_vfd_state *state)
|
||||
{
|
||||
(void) state;
|
||||
}
|
||||
|
||||
+183
-5
@@ -11,7 +11,7 @@
|
||||
version and the low byte is the minor version (as defined by the Semantic
|
||||
Versioning standard).
|
||||
|
||||
The latest API version as of this writing is 0x0100.
|
||||
The latest API version as of this writing is 0x0101.
|
||||
*/
|
||||
uint16_t aime_io_get_api_version(void);
|
||||
|
||||
@@ -27,7 +27,7 @@ HRESULT aime_io_init(void);
|
||||
/*
|
||||
Poll for IC cards in the vicinity.
|
||||
|
||||
- unit_no: Always 0 as of the current API version
|
||||
- unit_no: 0 on the primary Aime reader (most games), may be 1 on Sangokushi Taisen for the queue reader
|
||||
|
||||
Minimum API version: 0x0100
|
||||
*/
|
||||
@@ -36,7 +36,7 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no);
|
||||
/*
|
||||
Attempt to read out a classic Aime card ID
|
||||
|
||||
- unit_no: Always 0 as of the current API version
|
||||
- unit_no: 0 on the primary Aime reader (most games), may be 1 on Sangokushi Taisen for the queue reader
|
||||
- luid: Pointer to a ten-byte buffer that will receive the ID
|
||||
- luid_size: Size of the buffer at *luid. Always 10.
|
||||
|
||||
@@ -63,7 +63,7 @@ HRESULT aime_io_nfc_get_aime_id(
|
||||
|
||||
Parameters:
|
||||
|
||||
- unit_no: Always 0 as of the current API version
|
||||
- unit_no: 0 on the primary Aime reader (most games), may be 1 on Sangokushi Taisen for the queue reader
|
||||
- IDm: Output parameter that will receive the card ID
|
||||
|
||||
Returns:
|
||||
@@ -77,11 +77,189 @@ HRESULT aime_io_nfc_get_aime_id(
|
||||
HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm);
|
||||
|
||||
/*
|
||||
Change the color and brightness of the card reader's RGB lighting
|
||||
MIFARE key selector values used by the set key/authenticate functions.
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
enum {
|
||||
AIME_IO_MIFARE_KEY_AIME = 0,
|
||||
AIME_IO_MIFARE_KEY_BANA = 1,
|
||||
};
|
||||
|
||||
/*
|
||||
Attempt to read the 4-byte MIFARE UID of the currently present card.
|
||||
|
||||
Parameters:
|
||||
|
||||
- unit_no: Always 0 as of the current API version
|
||||
- uid: Pointer to a four-byte buffer that will receive the UID
|
||||
- uid_size: Size of the buffer at *uid. Always 4.
|
||||
|
||||
Returns:
|
||||
|
||||
- S_OK if a MIFARE card is present and the UID was read successfully
|
||||
- S_FALSE if no MIFARE card is present (*uid will be ignored)
|
||||
- Any HRESULT error if an error occured.
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
HRESULT aime_io_nfc_get_mifare_uid(
|
||||
uint8_t unit_no,
|
||||
uint8_t *uid,
|
||||
size_t uid_size);
|
||||
|
||||
/*
|
||||
Select a MIFARE card by UID (optional for real readers).
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
HRESULT aime_io_nfc_mifare_select(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size);
|
||||
|
||||
/*
|
||||
Supply a MIFARE authentication key to the reader.
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
HRESULT aime_io_nfc_mifare_set_key(
|
||||
uint8_t unit_no,
|
||||
uint8_t key_type,
|
||||
const uint8_t *key,
|
||||
size_t key_size);
|
||||
|
||||
/*
|
||||
Perform a MIFARE authentication sequence.
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
HRESULT aime_io_nfc_mifare_authenticate(
|
||||
uint8_t unit_no,
|
||||
uint8_t key_type,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size);
|
||||
|
||||
/*
|
||||
Read a 16-byte MIFARE block from the card.
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
HRESULT aime_io_nfc_mifare_read_block(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size,
|
||||
uint8_t block_no,
|
||||
uint8_t *block,
|
||||
size_t block_size);
|
||||
|
||||
/*
|
||||
Forward a raw FeliCa request to a real reader.
|
||||
|
||||
Parameters:
|
||||
|
||||
- req: FeliCa request buffer, including the length byte
|
||||
- res: FeliCa response buffer, including the length byte
|
||||
- res_size_written: Output size of the response (bytes written to *res)
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
HRESULT aime_io_nfc_felica_transact(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *req,
|
||||
size_t req_size,
|
||||
uint8_t *res,
|
||||
size_t res_size,
|
||||
size_t *res_size_written);
|
||||
|
||||
/*
|
||||
Enable the reader's RF field.
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
HRESULT aime_io_nfc_radio_on(uint8_t unit_no);
|
||||
|
||||
/*
|
||||
Disable the reader's RF field.
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
HRESULT aime_io_nfc_radio_off(uint8_t unit_no);
|
||||
|
||||
/*
|
||||
Put the reader into firmware update mode.
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
HRESULT aime_io_nfc_to_update_mode(uint8_t unit_no);
|
||||
|
||||
/*
|
||||
Forward a raw hex-data command to the reader.
|
||||
|
||||
Parameters:
|
||||
|
||||
- payload: Command payload bytes
|
||||
- payload_size: Size of the payload
|
||||
- status_out: Optional pointer to receive the SG status byte
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
HRESULT aime_io_nfc_send_hex_data(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size,
|
||||
uint8_t *status_out);
|
||||
|
||||
/*
|
||||
Change the color and brightness of the card reader's RGB lighting
|
||||
|
||||
- unit_no: 0 on the primary Aime reader (most games), may be 1 on Sangokushi Taisen for the queue reader
|
||||
- r, g, b: Primary color intensity, from 0 to 255 inclusive.
|
||||
|
||||
Minimum API version: 0x0100
|
||||
*/
|
||||
void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
/*
|
||||
VFD text forwarding. This is intended to pass through the text payload
|
||||
plus the most recent VFD state so external handlers can emulate scrolling
|
||||
or layout if desired.
|
||||
|
||||
- text: Pointer to raw text bytes (not null-terminated)
|
||||
- text_len: Length of the text buffer
|
||||
- state: Current VFD state at the time of rendering
|
||||
|
||||
The encoding field uses VFD encoding values (0=GB2312, 1=Big5,
|
||||
2=Shift-JIS, 3=KSC5601).
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
struct aime_io_vfd_state {
|
||||
uint8_t encoding;
|
||||
uint8_t text_speed;
|
||||
uint8_t scroll_enabled;
|
||||
uint16_t h_scroll;
|
||||
uint16_t cursor_x;
|
||||
uint8_t cursor_y;
|
||||
uint16_t wnd_x0;
|
||||
uint8_t wnd_y0;
|
||||
uint16_t wnd_x1;
|
||||
uint8_t wnd_y1;
|
||||
uint8_t rotate;
|
||||
uint8_t brightness;
|
||||
uint8_t screen_on;
|
||||
uint32_t clear_seq;
|
||||
};
|
||||
|
||||
void aime_io_vfd_set_text(
|
||||
const uint8_t *text,
|
||||
size_t text_len,
|
||||
const struct aime_io_vfd_state *state);
|
||||
|
||||
/*
|
||||
VFD state change notification. Called when the VFD state changes even
|
||||
when no text is written.
|
||||
|
||||
Minimum API version: 0x0101
|
||||
*/
|
||||
void aime_io_vfd_set_state(const struct aime_io_vfd_state *state);
|
||||
|
||||
+47
-2
@@ -8,6 +8,11 @@
|
||||
#include "util/dll-bind.h"
|
||||
#include "util/dprintf.h"
|
||||
|
||||
enum {
|
||||
AIME_DLL_SYM_COUNT_V100 = 5,
|
||||
AIME_DLL_SYM_COUNT_V101 = 17,
|
||||
};
|
||||
|
||||
const struct dll_bind_sym aime_dll_syms[] = {
|
||||
{
|
||||
.sym = "aime_io_init",
|
||||
@@ -24,7 +29,43 @@ const struct dll_bind_sym aime_dll_syms[] = {
|
||||
}, {
|
||||
.sym = "aime_io_led_set_color",
|
||||
.off = offsetof(struct aime_dll, led_set_color),
|
||||
}
|
||||
}, {
|
||||
.sym = "aime_io_vfd_set_text",
|
||||
.off = offsetof(struct aime_dll, vfd_set_text),
|
||||
}, {
|
||||
.sym = "aime_io_vfd_set_state",
|
||||
.off = offsetof(struct aime_dll, vfd_set_state),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_get_mifare_uid",
|
||||
.off = offsetof(struct aime_dll, nfc_get_mifare_uid),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_mifare_select",
|
||||
.off = offsetof(struct aime_dll, nfc_mifare_select),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_mifare_set_key",
|
||||
.off = offsetof(struct aime_dll, nfc_mifare_set_key),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_mifare_authenticate",
|
||||
.off = offsetof(struct aime_dll, nfc_mifare_authenticate),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_mifare_read_block",
|
||||
.off = offsetof(struct aime_dll, nfc_mifare_read_block),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_felica_transact",
|
||||
.off = offsetof(struct aime_dll, nfc_felica_transact),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_radio_on",
|
||||
.off = offsetof(struct aime_dll, nfc_radio_on),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_radio_off",
|
||||
.off = offsetof(struct aime_dll, nfc_radio_off),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_to_update_mode",
|
||||
.off = offsetof(struct aime_dll, nfc_to_update_mode),
|
||||
}, {
|
||||
.sym = "aime_io_nfc_send_hex_data",
|
||||
.off = offsetof(struct aime_dll, nfc_send_hex_data),
|
||||
},
|
||||
};
|
||||
|
||||
struct aime_dll aime_dll;
|
||||
@@ -42,6 +83,7 @@ HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self)
|
||||
HINSTANCE owned;
|
||||
HINSTANCE src;
|
||||
HRESULT hr;
|
||||
size_t sym_count;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(self != NULL);
|
||||
@@ -86,7 +128,10 @@ HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self)
|
||||
}
|
||||
|
||||
sym = aime_dll_syms;
|
||||
hr = dll_bind(&aime_dll, src, &sym, _countof(aime_dll_syms));
|
||||
sym_count = (aime_dll.api_version < 0x0101)
|
||||
? AIME_DLL_SYM_COUNT_V100
|
||||
: AIME_DLL_SYM_COUNT_V101;
|
||||
hr = dll_bind(&aime_dll, src, &sym, sym_count);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
if (src != self) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "aimeio/aimeio.h"
|
||||
|
||||
@@ -13,11 +14,57 @@ struct aime_dll {
|
||||
uint8_t *luid,
|
||||
size_t luid_size);
|
||||
HRESULT (*nfc_get_felica_id)(uint8_t unit_no, uint64_t *IDm);
|
||||
HRESULT (*nfc_get_mifare_uid)(
|
||||
uint8_t unit_no,
|
||||
uint8_t *uid,
|
||||
size_t uid_size);
|
||||
HRESULT (*nfc_mifare_select)(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size);
|
||||
HRESULT (*nfc_mifare_set_key)(
|
||||
uint8_t unit_no,
|
||||
uint8_t key_type,
|
||||
const uint8_t *key,
|
||||
size_t key_size);
|
||||
HRESULT (*nfc_mifare_authenticate)(
|
||||
uint8_t unit_no,
|
||||
uint8_t key_type,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size);
|
||||
HRESULT (*nfc_mifare_read_block)(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size,
|
||||
uint8_t block_no,
|
||||
uint8_t *block,
|
||||
size_t block_size);
|
||||
HRESULT (*nfc_felica_transact)(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *req,
|
||||
size_t req_size,
|
||||
uint8_t *res,
|
||||
size_t res_size,
|
||||
size_t *res_size_written);
|
||||
HRESULT (*nfc_radio_on)(uint8_t unit_no);
|
||||
HRESULT (*nfc_radio_off)(uint8_t unit_no);
|
||||
HRESULT (*nfc_to_update_mode)(uint8_t unit_no);
|
||||
HRESULT (*nfc_send_hex_data)(
|
||||
uint8_t unit_no,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size,
|
||||
uint8_t *status_out);
|
||||
void (*led_set_color)(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b);
|
||||
void (*vfd_set_text)(
|
||||
const uint8_t *text,
|
||||
size_t text_len,
|
||||
const struct aime_io_vfd_state *state);
|
||||
void (*vfd_set_state)(const struct aime_io_vfd_state *state);
|
||||
};
|
||||
|
||||
struct aime_dll_config {
|
||||
wchar_t path[MAX_PATH];
|
||||
bool path64;
|
||||
};
|
||||
|
||||
extern struct aime_dll aime_dll;
|
||||
|
||||
+15
-15
@@ -5,21 +5,21 @@
|
||||
#pragma pack(push, 1)
|
||||
|
||||
enum {
|
||||
SG_NFC_CMD_GET_FW_VERSION = 0x30,
|
||||
SG_NFC_CMD_GET_HW_VERSION = 0x32,
|
||||
SG_NFC_CMD_RADIO_ON = 0x40,
|
||||
SG_NFC_CMD_RADIO_OFF = 0x41,
|
||||
SG_NFC_CMD_POLL = 0x42,
|
||||
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x50,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_A = 0x51,
|
||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x54,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_B = 0x55,
|
||||
SG_NFC_CMD_TO_UPDATE_MODE = 0x60,
|
||||
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
|
||||
SG_NFC_CMD_RESET = 0x62,
|
||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||
SG_NFC_CMD_GET_FW_VERSION = 0x30,
|
||||
SG_NFC_CMD_GET_HW_VERSION = 0x32,
|
||||
SG_NFC_CMD_RADIO_ON = 0x40,
|
||||
SG_NFC_CMD_RADIO_OFF = 0x41,
|
||||
SG_NFC_CMD_POLL = 0x42,
|
||||
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x50,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_AIME = 0x51,
|
||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x54,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE_BANA = 0x55,
|
||||
SG_NFC_CMD_TO_UPDATE_MODE = 0x60,
|
||||
SG_NFC_CMD_SEND_HEX_DATA = 0x61,
|
||||
SG_NFC_CMD_RESET = 0x62,
|
||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||
};
|
||||
|
||||
struct sg_nfc_res_get_fw_version {
|
||||
|
||||
+417
-28
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -11,11 +12,14 @@
|
||||
#include "board/sg-nfc.h"
|
||||
#include "board/sg-nfc-cmd.h"
|
||||
|
||||
#include "aimeio/aimeio.h"
|
||||
|
||||
#include "iccard/aime.h"
|
||||
#include "iccard/felica.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
#include "util/slurp.h"
|
||||
|
||||
static HRESULT sg_nfc_dispatch(
|
||||
void *ctx,
|
||||
@@ -55,6 +59,38 @@ static HRESULT sg_nfc_cmd_mifare_read_block(
|
||||
const struct sg_nfc_req_mifare_read_block *req,
|
||||
struct sg_nfc_res_mifare_read_block *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_select(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_set_key(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res,
|
||||
uint8_t key_type);
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_authenticate(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res,
|
||||
uint8_t key_type);
|
||||
|
||||
static HRESULT sg_nfc_cmd_radio_on(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_radio_off(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_to_update_mode(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_felica_encap(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_nfc_req_felica_encap *req,
|
||||
@@ -87,6 +123,9 @@ void sg_nfc_init(
|
||||
uint8_t addr,
|
||||
const struct sg_nfc_ops *ops,
|
||||
unsigned int gen,
|
||||
unsigned int proxy_flag,
|
||||
const wchar_t* authdata_path,
|
||||
bool mobile_felica,
|
||||
void *ops_ctx)
|
||||
{
|
||||
assert(nfc != NULL);
|
||||
@@ -96,6 +135,9 @@ void sg_nfc_init(
|
||||
nfc->ops_ctx = ops_ctx;
|
||||
nfc->addr = addr;
|
||||
nfc->gen = gen;
|
||||
nfc->proxy_flag = proxy_flag;
|
||||
nfc->authdata_path = authdata_path;
|
||||
nfc->felica.is_mobile = mobile_felica;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
@@ -189,19 +231,49 @@ static HRESULT sg_nfc_dispatch(
|
||||
&req->felica_encap,
|
||||
&res->felica_encap);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_A:
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_B:
|
||||
case SG_NFC_CMD_MIFARE_SELECT_TAG:
|
||||
return sg_nfc_cmd_mifare_select(nfc, &req->simple, &res->simple);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_AIME:
|
||||
return sg_nfc_cmd_mifare_set_key(
|
||||
nfc,
|
||||
&req->simple,
|
||||
&res->simple,
|
||||
AIME_IO_MIFARE_KEY_AIME);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_BANA:
|
||||
return sg_nfc_cmd_mifare_set_key(
|
||||
nfc,
|
||||
&req->simple,
|
||||
&res->simple,
|
||||
AIME_IO_MIFARE_KEY_BANA);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_AIME:
|
||||
return sg_nfc_cmd_mifare_authenticate(
|
||||
nfc,
|
||||
&req->simple,
|
||||
&res->simple,
|
||||
AIME_IO_MIFARE_KEY_AIME);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE_BANA:
|
||||
return sg_nfc_cmd_mifare_authenticate(
|
||||
nfc,
|
||||
&req->simple,
|
||||
&res->simple,
|
||||
AIME_IO_MIFARE_KEY_BANA);
|
||||
|
||||
case SG_NFC_CMD_RADIO_ON:
|
||||
return sg_nfc_cmd_radio_on(nfc, &req->simple, &res->simple);
|
||||
|
||||
case SG_NFC_CMD_RADIO_OFF:
|
||||
return sg_nfc_cmd_radio_off(nfc, &req->simple, &res->simple);
|
||||
|
||||
case SG_NFC_CMD_TO_UPDATE_MODE:
|
||||
return sg_nfc_cmd_to_update_mode(nfc, &req->simple, &res->simple);
|
||||
|
||||
case SG_NFC_CMD_SEND_HEX_DATA:
|
||||
return sg_nfc_cmd_send_hex_data(nfc, &req->simple, &res->simple);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_SELECT_TAG:
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_AIME:
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_BANA:
|
||||
case SG_NFC_CMD_RADIO_ON:
|
||||
case SG_NFC_CMD_RADIO_OFF:
|
||||
case SG_NFC_CMD_TO_UPDATE_MODE:
|
||||
return sg_nfc_cmd_dummy(nfc, &req->simple, &res->simple);
|
||||
|
||||
default:
|
||||
sg_nfc_dprintf(nfc, "Unimpl command %02x\n", req->simple.hdr.cmd);
|
||||
|
||||
@@ -294,7 +366,9 @@ static HRESULT sg_nfc_poll_aime(
|
||||
struct sg_nfc *nfc,
|
||||
struct sg_nfc_poll_mifare *mifare)
|
||||
{
|
||||
bool has_uid;
|
||||
uint8_t luid[10];
|
||||
uint8_t uid[4];
|
||||
HRESULT hr;
|
||||
|
||||
/* Call backend */
|
||||
@@ -311,11 +385,30 @@ static HRESULT sg_nfc_poll_aime(
|
||||
|
||||
sg_nfc_dprintf(nfc, "AiMe card is present\n");
|
||||
|
||||
/* Construct response (use an arbitrary UID) */
|
||||
has_uid = false;
|
||||
|
||||
if (nfc->ops->get_mifare_uid != NULL) {
|
||||
hr = nfc->ops->get_mifare_uid(nfc->ops_ctx, uid, sizeof(uid));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (hr == S_OK) {
|
||||
has_uid = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Construct response */
|
||||
|
||||
mifare->type = 0x10;
|
||||
mifare->id_len = sizeof(mifare->uid);
|
||||
mifare->uid = _byteswap_ulong(0x01020304);
|
||||
if (has_uid) {
|
||||
memcpy(&mifare->uid, uid, sizeof(uid));
|
||||
} else {
|
||||
// mifare->uid = _byteswap_ulong(0x8FBECBFF);
|
||||
mifare->uid = _byteswap_ulong(0x01020304);
|
||||
}
|
||||
|
||||
/* Initialize MIFARE IC emulator */
|
||||
|
||||
@@ -354,22 +447,193 @@ static HRESULT sg_nfc_poll_felica(
|
||||
felica->type = 0x20;
|
||||
felica->id_len = sizeof(felica->IDm) + sizeof(felica->PMm);
|
||||
felica->IDm = _byteswap_uint64(IDm);
|
||||
felica->PMm = _byteswap_uint64(felica_get_amusement_ic_PMm());
|
||||
felica->PMm = _byteswap_uint64(felica_get_amusement_ic_PMm(nfc->felica.is_mobile));
|
||||
|
||||
/* Initialize FeliCa IC emulator */
|
||||
|
||||
nfc->felica.IDm = IDm;
|
||||
nfc->felica.PMm = felica_get_amusement_ic_PMm();
|
||||
nfc->felica.PMm = felica_get_amusement_ic_PMm(nfc->felica.is_mobile);
|
||||
nfc->felica.system_code = 0x88b4;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_select(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
const uint8_t *payload;
|
||||
HRESULT hr;
|
||||
|
||||
if (req->payload_len != sizeof(uint32_t)) {
|
||||
sg_nfc_dprintf(nfc, "%s: Payload size is incorrect\n", __func__);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
payload = (const uint8_t *) req + sizeof(*req);
|
||||
|
||||
if (nfc->ops->mifare_select != NULL) {
|
||||
hr = nfc->ops->mifare_select(nfc->ops_ctx, payload, req->payload_len);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_set_key(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res,
|
||||
uint8_t key_type)
|
||||
{
|
||||
const uint8_t *payload;
|
||||
HRESULT hr;
|
||||
|
||||
payload = (const uint8_t *) req + sizeof(*req);
|
||||
|
||||
if (nfc->ops->mifare_set_key != NULL) {
|
||||
hr = nfc->ops->mifare_set_key(
|
||||
nfc->ops_ctx,
|
||||
key_type,
|
||||
payload,
|
||||
req->payload_len);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_authenticate(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res,
|
||||
uint8_t key_type)
|
||||
{
|
||||
const uint8_t *payload;
|
||||
HRESULT hr;
|
||||
|
||||
payload = (const uint8_t *) req + sizeof(*req);
|
||||
|
||||
if (nfc->ops->mifare_authenticate != NULL) {
|
||||
hr = nfc->ops->mifare_authenticate(
|
||||
nfc->ops_ctx,
|
||||
key_type,
|
||||
payload,
|
||||
req->payload_len);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_radio_on(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (nfc->ops->radio_on == NULL) {
|
||||
sg_res_init(res, req, 0);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
hr = nfc->ops->radio_on(nfc->ops_ctx);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (hr == S_FALSE) {
|
||||
sg_res_init(res, req, 0);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_radio_off(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (nfc->ops->radio_off == NULL) {
|
||||
sg_res_init(res, req, 0);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
hr = nfc->ops->radio_off(nfc->ops_ctx);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (hr == S_FALSE) {
|
||||
sg_res_init(res, req, 0);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_to_update_mode(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (nfc->ops->to_update_mode == NULL) {
|
||||
sg_res_init(res, req, 0);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
hr = nfc->ops->to_update_mode(nfc->ops_ctx);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (hr == S_FALSE) {
|
||||
sg_res_init(res, req, 0);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_read_block(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_nfc_req_mifare_read_block *req,
|
||||
struct sg_nfc_res_mifare_read_block *res)
|
||||
{
|
||||
const uint8_t *uid_bytes;
|
||||
HRESULT hr;
|
||||
uint32_t uid;
|
||||
|
||||
if (req->req.payload_len != sizeof(req->payload)) {
|
||||
@@ -378,22 +642,86 @@ static HRESULT sg_nfc_cmd_mifare_read_block(
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
uid_bytes = (const uint8_t *) &req->payload.uid;
|
||||
uid = _byteswap_ulong(req->payload.uid);
|
||||
|
||||
sg_nfc_dprintf(nfc, "Read uid %08x block %i\n", uid, req->payload.block_no);
|
||||
|
||||
if (req->payload.block_no > 3) {
|
||||
if (nfc->ops->mifare_read_block != NULL) {
|
||||
hr = nfc->ops->mifare_read_block(
|
||||
nfc->ops_ctx,
|
||||
uid_bytes,
|
||||
sizeof(req->payload.uid),
|
||||
req->payload.block_no,
|
||||
res->block,
|
||||
sizeof(res->block));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (hr == S_OK) {
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (req->payload.block_no > 14) {
|
||||
sg_nfc_dprintf(nfc, "MIFARE block number out of range\n");
|
||||
|
||||
return E_FAIL;
|
||||
} else if (req->payload.block_no >= 5){ // emoney auth encrypted
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
char* auth;
|
||||
long size = wslurp(nfc->authdata_path, &auth, false);
|
||||
if (size < 0){
|
||||
sg_nfc_dprintf(nfc, "Failed to read %ls: %lx!\n", nfc->authdata_path, GetLastError());
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
int offset = 0;
|
||||
if (req->payload.block_no == 6){
|
||||
offset = 16;
|
||||
} else if (req->payload.block_no == 8){
|
||||
offset = 32;
|
||||
} else if (req->payload.block_no == 9){
|
||||
offset = 48;
|
||||
} else if (req->payload.block_no == 10){
|
||||
offset = 64;
|
||||
} else if (req->payload.block_no == 12){
|
||||
offset = 82;
|
||||
} else if (req->payload.block_no == 13){
|
||||
offset = 98;
|
||||
} else if (req->payload.block_no == 14){
|
||||
offset = 114;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16 && offset + i < size; i++){
|
||||
res->block[i] = auth[offset + i];
|
||||
}
|
||||
|
||||
free(auth);
|
||||
|
||||
} else if (req->payload.block_no == 4){ // emoney auth plain
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
res->block[0] = 0x54; // header
|
||||
res->block[1] = 0x43;
|
||||
res->block[2] = nfc->proxy_flag; // 2 or 3 depending on game (useProxy in env.json)
|
||||
res->block[3] = 0x01; // unknown flag
|
||||
|
||||
} else { // read all other blocks normally
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
memcpy( res->block,
|
||||
nfc->mifare.sectors[0].blocks[req->payload.block_no].bytes,
|
||||
sizeof(res->block));
|
||||
}
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
memcpy( res->block,
|
||||
nfc->mifare.sectors[0].blocks[req->payload.block_no].bytes,
|
||||
sizeof(res->block));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -404,6 +732,7 @@ static HRESULT sg_nfc_cmd_felica_encap(
|
||||
{
|
||||
struct const_iobuf f_req;
|
||||
struct iobuf f_res;
|
||||
size_t res_size_written;
|
||||
HRESULT hr;
|
||||
|
||||
/* First byte of encapsulated request and response is a length byte
|
||||
@@ -421,6 +750,32 @@ static HRESULT sg_nfc_cmd_felica_encap(
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (nfc->ops->felica_transact != NULL) {
|
||||
res_size_written = 0;
|
||||
hr = nfc->ops->felica_transact(
|
||||
nfc->ops_ctx,
|
||||
req->payload,
|
||||
req->payload[0],
|
||||
res->payload,
|
||||
sizeof(res->payload),
|
||||
&res_size_written);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (hr == S_OK) {
|
||||
if (res_size_written == 0 ||
|
||||
res_size_written > sizeof(res->payload)) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
sg_res_init(&res->res, &req->req, res_size_written);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
f_req.bytes = req->payload;
|
||||
f_req.nbytes = req->payload[0];
|
||||
f_req.pos = 1;
|
||||
@@ -429,7 +784,7 @@ static HRESULT sg_nfc_cmd_felica_encap(
|
||||
f_res.nbytes = sizeof(res->payload);
|
||||
f_res.pos = 1;
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_NFC)
|
||||
dprintf("FELICA OUTBOUND:\n");
|
||||
dump_const_iobuf(&f_req);
|
||||
#endif
|
||||
@@ -443,7 +798,7 @@ static HRESULT sg_nfc_cmd_felica_encap(
|
||||
sg_res_init(&res->res, &req->req, f_res.pos);
|
||||
res->payload[0] = f_res.pos;
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_NFC)
|
||||
dprintf("FELICA INBOUND:\n");
|
||||
dump_iobuf(&f_res);
|
||||
#endif
|
||||
@@ -456,14 +811,48 @@ static HRESULT sg_nfc_cmd_send_hex_data(
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
sg_res_init(res, req, 0);
|
||||
const uint8_t *payload;
|
||||
HRESULT hr;
|
||||
uint8_t status;
|
||||
|
||||
/* Firmware checksum length? */
|
||||
if (req->payload_len == 0x2b) {
|
||||
/* The firmware is identical flag? */
|
||||
res->status = 0x20;
|
||||
if (nfc->ops->send_hex_data == NULL) {
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
/* Firmware checksum length? */
|
||||
if (req->payload_len == 0x2b) {
|
||||
/* The firmware is identical flag? */
|
||||
res->status = 0x20;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
payload = (const uint8_t *) req + sizeof(*req);
|
||||
status = 0;
|
||||
|
||||
hr = nfc->ops->send_hex_data(
|
||||
nfc->ops_ctx,
|
||||
payload,
|
||||
req->payload_len,
|
||||
&status);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (hr == S_FALSE) {
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
if (req->payload_len == 0x2b) {
|
||||
res->status = 0x20;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
sg_res_init(res, req, 0);
|
||||
res->status = status;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,40 @@ struct sg_nfc_ops {
|
||||
HRESULT (*poll)(void *ctx);
|
||||
HRESULT (*get_aime_id)(void *ctx, uint8_t *luid, size_t nbytes);
|
||||
HRESULT (*get_felica_id)(void *ctx, uint64_t *IDm);
|
||||
HRESULT (*get_mifare_uid)(void *ctx, uint8_t *uid, size_t nbytes);
|
||||
HRESULT (*mifare_select)(void *ctx, const uint8_t *uid, size_t nbytes);
|
||||
HRESULT (*mifare_set_key)(
|
||||
void *ctx,
|
||||
uint8_t key_type,
|
||||
const uint8_t *key,
|
||||
size_t nbytes);
|
||||
HRESULT (*mifare_authenticate)(
|
||||
void *ctx,
|
||||
uint8_t key_type,
|
||||
const uint8_t *payload,
|
||||
size_t nbytes);
|
||||
HRESULT (*mifare_read_block)(
|
||||
void *ctx,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size,
|
||||
uint8_t block_no,
|
||||
uint8_t *block,
|
||||
size_t block_size);
|
||||
HRESULT (*felica_transact)(
|
||||
void *ctx,
|
||||
const uint8_t *req,
|
||||
size_t req_size,
|
||||
uint8_t *res,
|
||||
size_t res_size,
|
||||
size_t *res_size_written);
|
||||
HRESULT (*radio_on)(void *ctx);
|
||||
HRESULT (*radio_off)(void *ctx);
|
||||
HRESULT (*to_update_mode)(void *ctx);
|
||||
HRESULT (*send_hex_data)(
|
||||
void *ctx,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size,
|
||||
uint8_t *status_out);
|
||||
|
||||
// TODO Banapass, AmuseIC
|
||||
};
|
||||
@@ -23,8 +57,10 @@ struct sg_nfc {
|
||||
void *ops_ctx;
|
||||
uint8_t addr;
|
||||
unsigned int gen;
|
||||
unsigned int proxy_flag;
|
||||
struct felica felica;
|
||||
struct mifare mifare;
|
||||
const wchar_t* authdata_path;
|
||||
};
|
||||
|
||||
void sg_nfc_init(
|
||||
@@ -32,6 +68,9 @@ void sg_nfc_init(
|
||||
uint8_t addr,
|
||||
const struct sg_nfc_ops *ops,
|
||||
unsigned int gen,
|
||||
unsigned int proxy_flag,
|
||||
const wchar_t* authdata_path,
|
||||
bool mobile_felica,
|
||||
void *ops_ctx);
|
||||
|
||||
void sg_nfc_transact(
|
||||
|
||||
+196
-4
@@ -24,12 +24,62 @@ static HRESULT sg_reader_nfc_get_aime_id(
|
||||
uint8_t *luid,
|
||||
size_t luid_size);
|
||||
static HRESULT sg_reader_nfc_get_felica_id(void *ctx, uint64_t *IDm);
|
||||
static HRESULT sg_reader_nfc_get_mifare_uid(
|
||||
void *ctx,
|
||||
uint8_t *uid,
|
||||
size_t uid_size);
|
||||
static HRESULT sg_reader_nfc_mifare_select(
|
||||
void *ctx,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size);
|
||||
static HRESULT sg_reader_nfc_mifare_set_key(
|
||||
void *ctx,
|
||||
uint8_t key_type,
|
||||
const uint8_t *key,
|
||||
size_t key_size);
|
||||
static HRESULT sg_reader_nfc_mifare_authenticate(
|
||||
void *ctx,
|
||||
uint8_t key_type,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size);
|
||||
static HRESULT sg_reader_nfc_mifare_read_block(
|
||||
void *ctx,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size,
|
||||
uint8_t block_no,
|
||||
uint8_t *block,
|
||||
size_t block_size);
|
||||
static HRESULT sg_reader_nfc_felica_transact(
|
||||
void *ctx,
|
||||
const uint8_t *req,
|
||||
size_t req_size,
|
||||
uint8_t *res,
|
||||
size_t res_size,
|
||||
size_t *res_size_written);
|
||||
static HRESULT sg_reader_nfc_radio_on(void *ctx);
|
||||
static HRESULT sg_reader_nfc_radio_off(void *ctx);
|
||||
static HRESULT sg_reader_nfc_to_update_mode(void *ctx);
|
||||
static HRESULT sg_reader_nfc_send_hex_data(
|
||||
void *ctx,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size,
|
||||
uint8_t *status_out);
|
||||
static void sg_reader_led_set_color(void *ctx, uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
static const struct sg_nfc_ops sg_reader_nfc_ops = {
|
||||
.poll = sg_reader_nfc_poll,
|
||||
.get_aime_id = sg_reader_nfc_get_aime_id,
|
||||
.get_felica_id = sg_reader_nfc_get_felica_id,
|
||||
.get_mifare_uid = sg_reader_nfc_get_mifare_uid,
|
||||
.mifare_select = sg_reader_nfc_mifare_select,
|
||||
.mifare_set_key = sg_reader_nfc_mifare_set_key,
|
||||
.mifare_authenticate = sg_reader_nfc_mifare_authenticate,
|
||||
.mifare_read_block = sg_reader_nfc_mifare_read_block,
|
||||
.felica_transact = sg_reader_nfc_felica_transact,
|
||||
.radio_on = sg_reader_nfc_radio_on,
|
||||
.radio_off = sg_reader_nfc_radio_off,
|
||||
.to_update_mode = sg_reader_nfc_to_update_mode,
|
||||
.send_hex_data = sg_reader_nfc_send_hex_data,
|
||||
};
|
||||
|
||||
static const struct sg_led_ops sg_reader_led_ops = {
|
||||
@@ -47,7 +97,7 @@ static struct sg_led sg_reader_led;
|
||||
|
||||
HRESULT sg_reader_hook_init(
|
||||
const struct aime_config *cfg,
|
||||
unsigned int port_no,
|
||||
unsigned int default_port_no,
|
||||
unsigned int gen,
|
||||
HINSTANCE self)
|
||||
{
|
||||
@@ -66,6 +116,11 @@ HRESULT sg_reader_hook_init(
|
||||
return hr;
|
||||
}
|
||||
|
||||
unsigned int port_no = cfg->port_no;
|
||||
if (port_no == 0){
|
||||
port_no = default_port_no;
|
||||
}
|
||||
|
||||
if (cfg->gen != 0) {
|
||||
gen = cfg->gen;
|
||||
}
|
||||
@@ -76,7 +131,7 @@ HRESULT sg_reader_hook_init(
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, gen, NULL);
|
||||
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, gen, cfg->proxy_flag, cfg->authdata_path, cfg->mobile_felica, NULL);
|
||||
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, gen, NULL);
|
||||
|
||||
InitializeCriticalSection(&sg_reader_lock);
|
||||
@@ -85,6 +140,7 @@ HRESULT sg_reader_hook_init(
|
||||
sg_reader_uart.baud.BaudRate = 38400;
|
||||
}
|
||||
|
||||
dprintf("NFC Assembly: enabling (port=%d)\n", port_no);
|
||||
uart_init(&sg_reader_uart, port_no);
|
||||
sg_reader_uart.written.bytes = sg_reader_written_bytes;
|
||||
sg_reader_uart.written.nbytes = sizeof(sg_reader_written_bytes);
|
||||
@@ -115,14 +171,14 @@ static HRESULT sg_reader_handle_irp_locked(struct irp *irp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_NFC)
|
||||
if (irp->op == IRP_OP_WRITE) {
|
||||
dprintf("WRITE:\n");
|
||||
dump_const_iobuf(&irp->write);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#if defined(LOG_NFC)
|
||||
if (irp->op == IRP_OP_READ) {
|
||||
dprintf("READ:\n");
|
||||
dump_iobuf(&sg_reader_uart.readable);
|
||||
@@ -195,6 +251,142 @@ static HRESULT sg_reader_nfc_get_felica_id(void *ctx, uint64_t *IDm)
|
||||
return aime_dll.nfc_get_felica_id(0, IDm);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_get_mifare_uid(
|
||||
void *ctx,
|
||||
uint8_t *uid,
|
||||
size_t uid_size)
|
||||
{
|
||||
if (aime_dll.nfc_get_mifare_uid == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return aime_dll.nfc_get_mifare_uid(0, uid, uid_size);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_mifare_select(
|
||||
void *ctx,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size)
|
||||
{
|
||||
if (aime_dll.nfc_mifare_select == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return aime_dll.nfc_mifare_select(0, uid, uid_size);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_mifare_set_key(
|
||||
void *ctx,
|
||||
uint8_t key_type,
|
||||
const uint8_t *key,
|
||||
size_t key_size)
|
||||
{
|
||||
if (aime_dll.nfc_mifare_set_key == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return aime_dll.nfc_mifare_set_key(0, key_type, key, key_size);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_mifare_authenticate(
|
||||
void *ctx,
|
||||
uint8_t key_type,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size)
|
||||
{
|
||||
if (aime_dll.nfc_mifare_authenticate == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return aime_dll.nfc_mifare_authenticate(
|
||||
0,
|
||||
key_type,
|
||||
payload,
|
||||
payload_size);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_mifare_read_block(
|
||||
void *ctx,
|
||||
const uint8_t *uid,
|
||||
size_t uid_size,
|
||||
uint8_t block_no,
|
||||
uint8_t *block,
|
||||
size_t block_size)
|
||||
{
|
||||
if (aime_dll.nfc_mifare_read_block == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return aime_dll.nfc_mifare_read_block(
|
||||
0,
|
||||
uid,
|
||||
uid_size,
|
||||
block_no,
|
||||
block,
|
||||
block_size);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_felica_transact(
|
||||
void *ctx,
|
||||
const uint8_t *req,
|
||||
size_t req_size,
|
||||
uint8_t *res,
|
||||
size_t res_size,
|
||||
size_t *res_size_written)
|
||||
{
|
||||
if (aime_dll.nfc_felica_transact == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return aime_dll.nfc_felica_transact(
|
||||
0,
|
||||
req,
|
||||
req_size,
|
||||
res,
|
||||
res_size,
|
||||
res_size_written);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_radio_on(void *ctx)
|
||||
{
|
||||
if (aime_dll.nfc_radio_on == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return aime_dll.nfc_radio_on(0);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_radio_off(void *ctx)
|
||||
{
|
||||
if (aime_dll.nfc_radio_off == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return aime_dll.nfc_radio_off(0);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_to_update_mode(void *ctx)
|
||||
{
|
||||
if (aime_dll.nfc_to_update_mode == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return aime_dll.nfc_to_update_mode(0);
|
||||
}
|
||||
|
||||
static HRESULT sg_reader_nfc_send_hex_data(
|
||||
void *ctx,
|
||||
const uint8_t *payload,
|
||||
size_t payload_size,
|
||||
uint8_t *status_out)
|
||||
{
|
||||
if (aime_dll.nfc_send_hex_data == NULL) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return aime_dll.nfc_send_hex_data(0, payload, payload_size, status_out);
|
||||
}
|
||||
|
||||
static void sg_reader_led_set_color(void *ctx, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
aime_dll.led_set_color(0, r, g, b);
|
||||
|
||||
+5
-1
@@ -9,12 +9,16 @@
|
||||
struct aime_config {
|
||||
struct aime_dll_config dll;
|
||||
bool enable;
|
||||
unsigned int port_no;
|
||||
bool high_baudrate;
|
||||
unsigned int gen;
|
||||
unsigned int proxy_flag;
|
||||
wchar_t authdata_path[MAX_PATH];
|
||||
bool mobile_felica;
|
||||
};
|
||||
|
||||
HRESULT sg_reader_hook_init(
|
||||
const struct aime_config *cfg,
|
||||
unsigned int port_no,
|
||||
unsigned int default_port_no,
|
||||
unsigned int gen,
|
||||
HINSTANCE self);
|
||||
|
||||
+14
-28
@@ -1,9 +1,9 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
@@ -37,28 +37,14 @@ static HRESULT felica_cmd_write_without_encryption(
|
||||
struct const_iobuf* req,
|
||||
struct iobuf* res);
|
||||
|
||||
uint64_t felica_get_generic_PMm(void)
|
||||
{
|
||||
/* A FeliCa PMm contains low-level protocol timing information for
|
||||
communicating with a particular IC card. The exact values are not
|
||||
particularly important for our purposes, so we'll just return a hard-
|
||||
coded PMm. This current value has been taken from an iPhone, emulating
|
||||
a Suica pass via Apple Wallet, which seems to be one of the few
|
||||
universally accepted FeliCa types for these games. Certain older
|
||||
Suica passes and other payment and transportation cards
|
||||
do not seem to be supported anymore. */
|
||||
|
||||
return 0x01168B868FBECBFFULL;
|
||||
}
|
||||
|
||||
uint64_t felica_get_amusement_ic_PMm(void)
|
||||
uint64_t felica_get_amusement_ic_PMm(bool is_mobile)
|
||||
{
|
||||
/*
|
||||
* AIC Card PMm, if this is returned from the card,
|
||||
* the aimelib will access the actual blocks for authentication.
|
||||
*/
|
||||
|
||||
return 0x00F1000000014300;
|
||||
return is_mobile ? 0x0018000000014300 : 0x00F1000000014300;
|
||||
}
|
||||
|
||||
HRESULT felica_transact(
|
||||
@@ -211,17 +197,6 @@ static HRESULT felica_cmd_get_system_code(
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT felica_cmd_active(
|
||||
struct felica *f,
|
||||
struct const_iobuf *req,
|
||||
struct iobuf *res)
|
||||
{
|
||||
/* The specification for this command is probably only available under NDA.
|
||||
Returning what the driver seems to want. */
|
||||
|
||||
return iobuf_write_8(res, 0);
|
||||
}
|
||||
|
||||
static HRESULT felica_cmd_read_without_encryption(
|
||||
struct felica *f,
|
||||
struct const_iobuf *req,
|
||||
@@ -345,3 +320,14 @@ static HRESULT felica_cmd_write_without_encryption(
|
||||
{
|
||||
return iobuf_write_be16(res, 0);
|
||||
}
|
||||
|
||||
static HRESULT felica_cmd_active(
|
||||
struct felica *f,
|
||||
struct const_iobuf *req,
|
||||
struct iobuf *res)
|
||||
{
|
||||
/* The specification for this command is probably only available under NDA.
|
||||
Returning what the driver seems to want. */
|
||||
|
||||
return iobuf_write_8(res, 0);
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -19,6 +20,7 @@ struct felica {
|
||||
uint64_t IDm;
|
||||
uint64_t PMm;
|
||||
uint16_t system_code;
|
||||
bool is_mobile;
|
||||
};
|
||||
|
||||
HRESULT felica_transact(
|
||||
@@ -26,5 +28,4 @@ HRESULT felica_transact(
|
||||
struct const_iobuf *req,
|
||||
struct iobuf *res);
|
||||
|
||||
uint64_t felica_get_generic_PMm(void);
|
||||
uint64_t felica_get_amusement_ic_PMm(void);
|
||||
uint64_t felica_get_amusement_ic_PMm(bool is_mobile);
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#include "env.h"
|
||||
|
||||
const wchar_t* get_config_path() {
|
||||
static wchar_t path[MAX_PATH];
|
||||
if (!GetEnvironmentVariableW(L"BANANATOOLS_CONFIG_PATH", path, MAX_PATH)) {
|
||||
return L".\\bananatools.ini";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
const wchar_t* get_config_path();
|
||||
@@ -10,6 +10,8 @@ util_lib = static_library(
|
||||
'async.h',
|
||||
'crc.c',
|
||||
'crc.h',
|
||||
'env.c',
|
||||
'env.h',
|
||||
'dll-bind.c',
|
||||
'dll-bind.h',
|
||||
'dprintf.c',
|
||||
@@ -20,6 +22,8 @@ util_lib = static_library(
|
||||
'lib.h',
|
||||
'str.c',
|
||||
'str.h',
|
||||
'slurp.c',
|
||||
'slurp.h',
|
||||
'hexstr.c',
|
||||
'hexstr.h',
|
||||
],
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
/*
|
||||
* 'slurp' reads the file identified by 'path' into a character buffer
|
||||
* pointed at by 'buf', optionally adding a terminating NUL if
|
||||
* 'add_nul' is true. On success, the size of the file is returned; on
|
||||
* failure, -1 is returned and ERRNO is set by the underlying system
|
||||
* or library call that failed.
|
||||
*
|
||||
* WARNING: 'slurp' malloc()s memory to '*buf' which must be freed by
|
||||
* the caller.
|
||||
*/
|
||||
long wslurp(const wchar_t* path, char **buf, bool add_nul)
|
||||
{
|
||||
FILE *fp;
|
||||
size_t fsz;
|
||||
long off_end;
|
||||
int rc;
|
||||
|
||||
/* Open the file */
|
||||
fp = _wfopen(path, L"rb");
|
||||
if( NULL == fp ) {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
/* Seek to the end of the file */
|
||||
rc = fseek(fp, 0L, SEEK_END);
|
||||
if( 0 != rc ) {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
/* Byte offset to the end of the file (size) */
|
||||
if( 0 > (off_end = ftell(fp)) ) {
|
||||
return -1L;
|
||||
}
|
||||
fsz = (size_t)off_end;
|
||||
|
||||
/* Allocate a buffer to hold the whole file */
|
||||
*buf = malloc( fsz+(int)add_nul );
|
||||
if( NULL == *buf ) {
|
||||
return -1L;
|
||||
}
|
||||
|
||||
/* Rewind file pointer to start of file */
|
||||
rewind(fp);
|
||||
|
||||
/* Slurp file into buffer */
|
||||
if( fsz != fread(*buf, 1, fsz, fp) ) {
|
||||
free(*buf);
|
||||
return -1L;
|
||||
}
|
||||
|
||||
/* Close the file */
|
||||
if( EOF == fclose(fp) ) {
|
||||
free(*buf);
|
||||
return -1L;
|
||||
}
|
||||
|
||||
if( add_nul ) {
|
||||
/* Make sure the buffer is NUL-terminated, just in case */
|
||||
buf[fsz] = '\0';
|
||||
}
|
||||
|
||||
/* Return the file size */
|
||||
return (long)fsz;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include <stdbool.h>
|
||||
/*
|
||||
* 'slurp' reads the file identified by 'path' into a character buffer
|
||||
* pointed at by 'buf', optionally adding a terminating NUL if
|
||||
* 'add_nul' is true. On success, the size of the file is returned; on
|
||||
* failure, -1 is returned and ERRNO is set by the underlying system
|
||||
* or library call that failed.
|
||||
*
|
||||
* WARNING: 'slurp' malloc()s memory to '*buf' which must be freed by
|
||||
* the caller.
|
||||
*/
|
||||
long wslurp(const wchar_t* path, char **buf, bool add_nul);
|
||||
Reference in New Issue
Block a user