c - 如何触发保存文件的创建
问题描述
我正在尝试创建一个 libretro 核心。这将是一个独立的游戏,所以我设置RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME
为true
. 该文档建议retro_get_memory_*
可用于保存数据而无需显式查询RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY
:
如果 libretro 内核无法使用常规内存接口 (retro_get_memory_data()),则保存目录应用于存储 SRAM、内存卡、高分等。
核心应该如何使用这个接口触发数据的保存?还是我误解了文档?
我希望前端调用retro_get_memory_{data,size}
,在停止核心时从暴露的缓冲区中读取数据,将数据保存到磁盘,并在下次核心启动时将其写回暴露的缓冲区。相反,我观察到:
- 如果我不提供内容文件,前端永远不会调用
retro_get_memory_{data,size}
. - 如果我提供一个内容文件(未使用),前端会
retro_get_memory_{data,size}
在之后调用retro_load_game
但不会写入磁盘。
请注意,这个问题是关于保存文件(自动持久化的数据,通常捕获玩家的进度),而不是由方法实现的保存状态(用户触发的游戏状态的快照)*serialize*
。
这是一个重现问题的简单示例(基于此示例):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libretro.h"
static unsigned char c = 0;
void* retro_get_memory_data(unsigned id) {
fprintf(stderr, "retro_get_memory_data(%d)\n", id);
return (id == RETRO_MEMORY_SAVE_RAM) ? &c : NULL;
}
size_t retro_get_memory_size(unsigned id) {
fprintf(stderr, "retro_get_memory_size(%d)\n", id);
return (id == RETRO_MEMORY_SAVE_RAM) ? 1 : 0;
}
#define WIDTH 320
#define HEIGHT 240
static uint32_t* frame_buf;
void retro_init(void) { frame_buf = calloc(WIDTH * HEIGHT, sizeof(uint32_t)); }
void retro_deinit(void) {
free(frame_buf);
frame_buf = NULL;
}
unsigned retro_api_version(void) { return RETRO_API_VERSION; }
void retro_get_system_info(struct retro_system_info* info) {
memset(info, 0, sizeof(*info));
info->library_name = "SaveTest";
info->library_version = "v1";
info->need_fullpath = false;
info->valid_extensions = NULL; // Anything is fine, we don't care.
}
static retro_video_refresh_t video_cb;
static retro_environment_t environ_cb;
static retro_input_poll_t input_poll_cb;
static retro_input_state_t input_state_cb;
void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
void retro_set_environment(retro_environment_t cb) {
environ_cb = cb;
bool no_content = true;
cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_content);
}
void retro_get_system_av_info(struct retro_system_av_info* info) {
float aspect = (float)WIDTH / HEIGHT;
info->timing = (struct retro_system_timing){
.fps = 60.0,
.sample_rate = 0.0,
};
info->geometry = (struct retro_game_geometry){
.base_width = WIDTH,
.base_height = HEIGHT,
.max_width = WIDTH,
.max_height = HEIGHT,
.aspect_ratio = aspect,
};
}
unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }
bool retro_load_game(const struct retro_game_info* info) {
enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
fprintf(stderr, "XRGB8888 is not supported.\n");
return false;
}
(void)info;
return true;
}
bool button(unsigned id) {
return input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, id);
}
void retro_run(void) {
input_poll_cb();
if (button(RETRO_DEVICE_ID_JOYPAD_LEFT) && c > 0) --c;
if (button(RETRO_DEVICE_ID_JOYPAD_RIGHT) && c < 255) ++c;
uint32_t color = (255 - c) | (c << 8);
uint32_t* buf = frame_buf;
for (unsigned i = WIDTH * HEIGHT; i > 0; --i) {
*buf = color;
++buf;
}
video_cb(frame_buf, WIDTH, HEIGHT, WIDTH * sizeof(uint32_t));
}
void retro_unload_game(void) {}
size_t retro_serialize_size(void) { return 1; }
bool retro_serialize(void* data, size_t size) {
fprintf(stderr, "serialize(%p, %lu) <= %u\n", data, size, c);
*(char*)data = c;
return true;
}
bool retro_unserialize(const void* data, size_t size) {
c = *(char*)data;
fprintf(stderr, "unserialize(%p, %lu) => %u\n", data, size, c);
return true;
}
void retro_set_controller_port_device(unsigned port, unsigned device) {}
void retro_set_audio_sample(retro_audio_sample_t cb) {}
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) {}
void retro_reset(void) {}
bool retro_load_game_special(unsigned type, const struct retro_game_info* info,
size_t num) {
return false;
}
void retro_cheat_reset(void) {}
void retro_cheat_set(unsigned index, bool enabled, const char* code) {}
解决方案
它没有记录,但没有内容的核心不会触发自动保存。
此外,对于有内容但不支持任何内容的内核,不会触发自动保存。这是无意的,最近已修复。
推荐阅读
- javascript - 使用 jQuery,如何在触发表单提交事件之前等待 onChange 事件完成
- amazon-web-services - 如何配置触发 AWS lambda 的自定义 s3 putevent?
- qt - 虚拟键盘仅在我触摸屏幕的可编辑部分而不是鼠标单击时可见
- python - 就地从列表中删除重复项
- java - 将未来与未来的集合(全部同时运行)结合起来以提供类似结构的 lambda
- php - 用户身份验证的其他声明无效
- asterisk - 没有用于扩展的应用程序“实时”(默认,1234、2)
- json - 如何在ajax中将字符串保持为原始格式?
- json - JSON 断言 - 断言多个响应值
- node.js - 如何使用数据库(mongodb)使我的代码更简单?