본문 바로가기

remote-craft

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

 

두 프로젝트 모두 공통적으로 사용하는 환경설정 주입, 오디오, 비디오 출력, 컨트롤 입력, 게임 상태 저장+불러오기 등 게임을 플레이할때 필수적으로 필요한 부분들을 위주로 살펴보며 개발 착수.


게임 구동까지 필요했던 스텝

  1. 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)
    
    
    ...
  2. 코어 환경주입, 코어 불러오기, 컨텐츠 불러오기, 실행(비디오 1프레임 + 오디오 샘플 1프레임 출력(확실치 않음)), 상태 저장, 상태 불러오기, 코어 종료 등 주요 API + Cgo 바인딩 탐구 및 실험
  3. 비디오 포멧 설정: 레트로게임들은 안타깝게도 대부분 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,
    	}
    }
  4. 비디오 및 오디오 데이터 파이프라인을 Go Channel을 이용하여 만들 계획이었기때문에 숨막히는 deadlock으로 고생을 많이 했다...
  5. Go의 채널을 통해 들어오는 이미지를 .png로 저장! 사진이 나온다!

 

감동의 .png

 

일기)

연결만 했을 뿐인데.. 사진이 나온다! 재밌다!

/* ... */ 커멘트로 C코드를 작성하고 import "C"를 쓰면 C.<function> C.<variable>로 C언어 코더를 불러 올 수 있었다. 생각보다 강력한 Go의 기능에 다시 한번 감탄... 글로 쓰니까 뭔가 짧다... 2주 가량 정도 열심히 갈아넣어서 아웃풋 생성!

 

 

 

To be Continued...

Libretro의 다음편은 외부세상과 연결해줄 RetroEmulator 객체에 대해 이야기 해볼게요!

'remote-craft' 카테고리의 다른 글