RemoteCraft 02 - Libretro 00
Libretro? Libretro!
/*
Libretro is a simple but powerful development interface that allows for the easy creation of
emulators, games and multimedia applications that can plug straight into any libretro-compatible
frontend. This development interface is open to others so that they can run these pluggable emulator
and game cores also in their own programs or devices.
*/
Libretro는 에뮬레이터, 게임, 멀티미디어 어플리케이션의 개발을 도와 줄 수 있는 간단하지만 강력한 개발 인터페이스이다. 여러 종류의 게임플랫폼들의 설정부터 구동, Audio & Video 출력, 컨트롤 입력까지 하나의 통일된 API 통해 제어할 수 있는 환경을 제공한다. Libretro API에 호환되는 프론트엔드만 작성한다면 모든 Libretro에서 제공하는 pluggable 에뮬레이터를 우리가 원하는 환경, 기기, 어플리케이션에서 구동 할 수 있다고 한다.
사실 Libretro가 없었다면 애초에 시작조차 하지 못할 프로젝트입니다... 많은 게임플랫폼의 복잡복잡한 under-the-hood 기능들을 직접 구현하지 않아도 되게끔 해준 요번 프로젝트의 코어가 되준 친구! 이 뿐만이 아니라 통일된 인터페이스이기 때문에 하나의 프론트엔드 코드가 특정 게임플랫폼에 종속되지 않는 장점도 있다.
Libretro는 https://buildbot.libretro.com/nightly/ 에서 Libretro에 연결 할 수 있는 에뮬레이터를 환경 별로 제공하기때문에 본인의 취향(?)에 맞게 본인의 환경에 맞게 에뮬레이터를 골라 개발 할 수 있다. (개발 중에는 OSX 환경의 코어를 이용... 배포할때는 Linux 환경의 코어를 이용 할 예정...)
*TODO 빌드봇을 이용하여 에뮬레이터의 버전을 자동으로 업데이트 할 수 있는 컴포넌트 개발! (언제하지...)
참고 자료
공유할수록 아름답다고... 여러분들도 도전해보아요!
Libretro Docs - Libretro Overview
This is RetroArch's document page, modification or development information outside of this repo may be incorrect.
docs.libretro.com
libretro/go-nanoarch
Minimal libretro frontend written in golang. Contribute to libretro/go-nanoarch development by creating an account on GitHub.
github.com
libretro/ludo
A libretro frontend written in golang. Contribute to libretro/ludo development by creating an account on GitHub.
github.com
두 프로젝트 모두 공통적으로 사용하는 환경설정 주입, 오디오, 비디오 출력, 컨트롤 입력, 게임 상태 저장+불러오기 등 게임을 플레이할때 필수적으로 필요한 부분들을 위주로 살펴보며 개발 착수.
게임 구동까지 필요했던 스텝
- Libretro API와 Go코드를 위한 C코드 & GO코드 바인딩:
/* #include "libretro.h" #include <stdlib.h> #include <stdio.h> #include <string.h> */ import "C" type LibretroCore struct { retroHandle unsafe.Pointer retroAPIVersion unsafe.Pointer retroSetEnvironment unsafe.Pointer retroInit unsafe.Pointer ... } // 에뮬레이터 코어 불러 코어핸들에 등록 core.retroHandle, err = DlOpen(filePath) // Libretro API 바인딩 core.retroInit = DlSym(core.retroHandle, "retro_init") core.retroDeinit = DlSym(core.retroHandle, "retro_deinit") ... // 바인딩 된 API call C.bridge_retro_set_environment(core.retroSetEnvironment, C.coreEnvironment_cgo) C.bridge_retro_set_video_refresh(core.retroSetVideoRefresh, C.coreVideoRefresh_cgo) ... C.bridge_retro_init(core.retroInit) ...
- 코어 환경주입, 코어 불러오기, 컨텐츠 불러오기, 실행(비디오 1프레임 + 오디오 샘플 1프레임 출력(확실치 않음)), 상태 저장, 상태 불러오기, 코어 종료 등 주요 API + Cgo 바인딩 탐구 및 실험
- 비디오 포멧 설정: 레트로게임들은 안타깝게도 대부분 16-bit의 픽셀포멧을 제공한다. 32-bit라면 그저 버퍼에 복붙하면 되겠지만...컨텐츠 로드 후 Libretro API로 뽑아낼 수 있는 SystemAVInfo에서 비디오 포멧이 short_5551 or short565포멧인지 int8888 포멧인지 꼭 확인하고 컨버팅하여 Go에서 지원하는 image 라이브러리의 32-bit rgba 버퍼에 넣어주면 된다!
// short_565 포멧 컨버터 func Rgb565(data []byte, index int) color.RGBA { pixel := ((uint32)(data[index+1]) << 8) + (uint32)(data[index]) return color.RGBA{ R: byte(((pixel>>11) * 255 + 15) / 31), G: byte((((pixel>>5)&0x3F) * 255 + 31) / 63), B: byte(((pixel&0x1F) * 255 + 15) / 31), A: 255, } }
- 비디오 및 오디오 데이터 파이프라인을 Go Channel을 이용하여 만들 계획이었기때문에 숨막히는 deadlock으로 고생을 많이 했다...
- Go의 채널을 통해 들어오는 이미지를 .png로 저장! 사진이 나온다!
일기)
연결만 했을 뿐인데.. 사진이 나온다! 재밌다!
/* ... */ 커멘트로 C코드를 작성하고 import "C"를 쓰면 C.<function> C.<variable>로 C언어 코더를 불러 올 수 있었다. 생각보다 강력한 Go의 기능에 다시 한번 감탄... 글로 쓰니까 뭔가 짧다... 2주 가량 정도 열심히 갈아넣어서 아웃풋 생성!
To be Continued...
Libretro의 다음편은 외부세상과 연결해줄 RetroEmulator 객체에 대해 이야기 해볼게요!