[LINUX] Analysons la machine de jeu rétro chinoise

Aperçu

J'ai acheté une machine de jeu rétro chinoise, qui est devenue un sujet brûlant dans certains domaines, alors je l'ai analysée. C'est comme un mémo pour moi qui oublie beaucoup.

Je me demande quel devrait être le but de l'analyse, mais je pense que ce serait bien si je pouvais montrer comment créer un environnement de développement d'applications pour les machines de jeux rétro chinoises.

Ensuite, je pense que beaucoup de gens avec beaucoup d'idées feront quelque chose de bien.

Fichier d'image

obtenir

Lors de la mise sous tension, l'écran RetroFW s'affiche. Si vous recherchez ce mot-clé sur Google, vous trouverez immédiatement RetroFW.

Téléchargez le fichier de version RetroFW_v1.2.zip et jetez un œil au contenu.

Contenu du document

Lorsque vous décompressez le fichier zip, RetroFW.img, le fichier de commandes et dd.exe, ainsi que divers uBoot.bin et uImage.bin apparaissent dans les sous-dossiers.

Ce que vous pouvez voir dans le fichier de commandes

Si vous regardez à l'intérieur du fichier de commandes, vous pouvez voir que le processus d'écriture de uBoot.bin et uImage.bin à un emplacement spécifique dans RetroFW.img à l'aide de dd.exe est écrit. Par exemple, RetroGame_v1.0_S_B.bat se présente comme suit.

bat:RetroGame_v1.0_S_B.bat


dd if=kernel/RetroGame_v1.0_S_B.uBoot.bin  of=RetroFW.img conv=notrunc bs=512 seek=1 && dd if=kernel/RetroGame_v1.0_S_B.uImage.bin of=RetroFW.img conv=notrunc bs=1024 seek=4096

On peut voir que uBoot.bin est écrit à partir du décalage de position de 512 octets depuis le début du fichier, et uImage.bin est écrit à partir du décalage de position de 4 Mo depuis le début du fichier.

U-Boot démarre l'opération au stade initial immédiatement après la mise sous tension et démarre le système d'exploitation après l'initialisation du matériel. C'est un soi-disant chargeur de démarrage qui fonctionne.

Comme cela est écrit à une position de 512 octets décalée par rapport au début du fichier, la puce utilisée dans cette machine de jeu rétro chinoise lit à partir de la position 512 octets de décalage depuis le début de la microSD (temporairement définie comme LBA 1) vers la RAM et saute. Vous pouvez imaginer qu'il a un mécanisme.

De plus, on peut imaginer que ce U-Boot est écrit pour lire uImage.bin situé à une position décalée de 4 Mo du début de la microSD dans la RAM et sauter.

Ce que vous pouvez voir à partir du fichier image

En regardant RetroFW.img, il semble que les 512 premiers octets sont des MBR.

Extraire uniquement la partie facile à comprendre de la table de partition et en faire une table est la suivante.

No de partition. Premier secteur(LBA) Nombre de secteurs Type de partition
#1 0x0000_4000 0x0004_0000 Linux
#2 0x0004_4000 0x0008_0000 Linux swap
#3 0x000c_4000 0x0013_c000 FAT32
#4 - - Gratuit

Si vous regardez RetroFW.img avec un éditeur binaire conformément à ce qui précède, vous pouvez voir que la première partition (à partir de l'offset 0x0080_0000) est rootfs et la deuxième partition (à partir de l'offset 0x0800_0000) est swap.

Cependant, la troisième partition (à partir du décalage 0x1800_0000) est remplie de 0 pendant un certain temps. Dans ce cas, si vous essayez de monter depuis Linux, il échouera probablement, et on pense que l'opération en cas d'échec est préchargée.

Plus précisément, si le montage échoue avec FAT32, mkfs est exécuté. Si vous avez un design un peu plus astucieux, il est possible d'étendre la zone FAT32 jusqu'à la fin de la microSD puis mkfs (raspbian. ) Parce que c'est le cas). Vous pouvez en fait vérifier le mouvement de ceci.

Le code d'exécution est censé être placé dans la partie chargeur à partir de l'offset 0 du MBR, et quelque chose est réellement écrit, mais il est difficile de juger s'il en est de même avec cette puce.

Pour le moment, essayez assemblage inversé pour les 4 premiers octets, FA B8 00 10 ...

00000000 1000b8fa    b loc_fffee000

Il s'agit donc d'une commande de saut relative et la destination du saut semble être dans la direction négative. Ne pensez pas que ce n'est pas fait. Au lieu de creuser davantage, vous devriez essayer d'écrire une valeur aléatoire et voir réellement ce qui se passe.

En savoir plus sur Linux

J'ai cherché jusqu'à présent avec un éditeur binaire, mais j'ai trouvé qu'il était plus facile de vérifier avec une machine Linux.

$ fdisk -l ./RetroFW.img
Disk ./RetroFW.img: 1 GiB, 1073741824 bytes, 2097152 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xbb005712

Device         Boot  Start     End Sectors  Size Id Type
./RetroFW.img1       16384  278527  262144  128M 83 Linux
./RetroFW.img2      278528  802815  524288  256M 82 Linux swap / Solaris
./RetroFW.img3      802816 2097151 1294336  632M  c W95 FAT32 (LBA)

Comme je l'ai vérifié. C'est un gros problème, alors j'irai un peu plus loin.

$ sudo mount -t ext4 -o ro,loop,offset=8388608 RetroFW.img /mnt

$ file /mnt/bin/busybox 
/mnt/bin/busybox: setuid ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped

$ readelf -a /mnt/bin/busybox
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           MIPS R3000
  Version:                           0x1
  Entry point address:               0x402fb0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          800132 (bytes into file)
  Flags:                             0x50001007, noreorder, pic, cpic, o32, mips32
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         26
  Section header string table index: 25
      :
Attribute Section: gnu
File Attributes
  Tag_GNU_MIPS_ABI_FP: Hard float (double precision)

C'est un binaire de MIPS32 et semble utiliser une puce équipée de FPU.

Il existe des informations selon lesquelles une puce appelée JZ4760 est utilisée lorsqu'elle est recherchée sur Google avec le nom du produit de la machine de jeu rétro chinoise réellement achetée Ingenic Semiconductor --Wikipedia Si vous regardez, vous pouvez obtenir des informations avec FPU avec MIPS32 rev1.

Vous pouvez également voir que le binaire est construit pour utiliser uClibc.

Version U-Boot

Si vous regardez les binaires U-Boot avec un éditeur binaire, vous pouvez voir que les versions utilisées sont les suivantes.

text:RetroGame_v1.0_S_B.uBoot.bin


U-Boot 1.1.6 (Jul 26 2018 - 14:28:08)

Pour ce qui est de la version 1.1.6, c'est probablement vers 2006, et j'en utilise une très ancienne. Eh bien, puisqu'il s'agit d'un chargeur de démarrage, peut-il être ancien?

La version actuelle semble être v2019.07, mais je ne trouve pas d'implémentation pour jz4760 [Implémentation de jz4780](https://gitlab.denx.de/u-boot/u-boot/tree/master/arch/ mips / mach-jz47xx) Il semble qu'il n'y en ait qu'un.

text:RetroGame_v1.0_S_B.uBoot.bin


Board: Ingenic LEPUS (CPU Speed %d MHz)

J'ai également trouvé la chaîne de caractères. LEPUS semble être le nom d'une carte de conception de référence, donc si vous recherchez ceci comme mot-clé, vous pouvez trouver une implémentation pour jz4760.

Pour le moment, j'ai trouvé quelque chose comme u-boot-1.1.6-jz-20120904-r1819.patch.gz. Il comprend une implémentation solide pour le jz4760. L'implémentation de la partie qui rend la RAM disponible peut inclure des paramètres dérivés des constantes de la carte. La clé est de savoir si la carte est conçue selon la conception de référence.

Version Linux

De même, si vous regardez le binaire uImage avec un éditeur binaire, vous pouvez voir que la version utilisée est la suivante.

text:RetroGame_v1.0_S_B.uImage.bin


Linux-2.6.31.3

Peut-être que c'est vers 2009, j'utilise un ancien.

La version stable actuelle semble être la 5.2.14, mais je ne trouve aucune implémentation pour jz4760 non plus [Implémentation de jz4740, jz4770, jz4780](https://git.kernel.org/pub/scm/linux/kernel/ Il semble y avoir git / stable / linux.git / tree / arch / mips / jz4740? H = v5.2.14). Il a été écrit que la différence par rapport à jz4740 est la fréquence d'horloge, la présence ou l'absence de FPU et la présence ou l'absence de GPU, donc cela peut fonctionner avec la version pour jz4740 (vraiment?).

C'est aussi une GPL, donc si vous la cherchez, vous pouvez trouver une implémentation pour jz4760.

Pour le moment, j'ai trouvé quelque chose comme linux-2.6.31.3-jz-20120904-r1448.patch.gz. Il comprend une implémentation solide pour le jz4760.

Environnement de développement standard

Lorsque j'ai recherché un environnement de développement standard pour RetroFW, je l'ai trouvé rapidement.

[Tutoriel](https: // redirect.) Écrit dans Rubrique: RetroFW - Thread de support pour les développeurs viglink.com/?format=go&jsonp=vglnk_156881591905714&key=5150cb74f98acee214af0b53b6829d41&libId=k0pcfxgc0102ljt2000DAwfv3cbhjannx&loc=https%3A%2F% % 2Fdocs.google.com% 2Fdocument% 2Fd% 2F19kJXO3EZ8XCoeporuUUgV_S93AaPbSagza3sAgBILu8% 2Fedit% 3Fusp% 3Dsharing & title = RetroFW% 20-% 20Developer% 20Support% 20Thread% 20Thread% 20Thread% 20Thread% 20Thread% 20Thread% 20Thread% 20Thread% 20Thread% 20Thread% 20Support% 20-% 20Developer% 20 % 20Configuring% 20a% 20Toolchain% 20for% 20RetroFW% 20Development), il était facile de créer un échantillon d'IPK.

Points clés de la construction d'un environnement de développement

Les principaux points écrits dans le didacticiel sont les suivants.

Dans le tutoriel, j'ai utilisé VirtualBox pour préparer l'environnement d'exploitation d'Ubuntu, mais j'ai essayé Windows Subsystem for Linux. Quand j'ai essayé d'utiliser Ubuntu (/ wiki / Windows_Subsystem_for_Linux), j'ai pu le construire sans aucun problème, sauf pour certaines parties. L'explication de l'introduction de WSL est omise.

Ensuite, installez le package pour créer BuildRoot sur Ubuntu de WSL. Comme suit.

$ sudo apt install build-essential libncurses5 libncurses5-dev git python unzip bc

Ensuite, récupérez BuildRoot et compilez.

La version de BuildRoot est spécifiée pour utiliser 2018.02.9, et il est indiqué de ne pas l'utiliser même si vous remarquez qu'une nouvelle version a été publiée (car elle est sensible à la version de la chaîne d'outils).

$ wget https://buildroot.org/downloads/buildroot-2018.02.9.tar.gz
$ tar -xzvf buildroot-2018.02.9.tar.gz
$ cd buildroot-2018.02.9
$ make menuconfig

La configuration de buildroot est décrite comme suit.

Target Options
    Target Architecture - MIPS (little endian)
    Disable soft-float

Toolchain
    C library (uClibc-ng)
    Enable WCHAR support
    Enable C++ support

Target Packages
    Graphic libraries and applications (graphic/text)
        SDL
        SDL_gfx
        SDL_image
        SDL_mixer
        SDL_net
        SDL_sound
        SDL_TTF

Tout ce que vous avez à faire est de construire. Il faut beaucoup de temps pour construire toute la chaîne d'outils (compilateur croisé).

$ export FORCE_UNSAFE_CONFIGURE=1
$ make

Je crée des rootfs pendant make, mais dans WSL cela semble être une erreur (ce qu'est fakeroot, qu'est-ce que truqué). Cette fois, il suffit de créer un environnement de développement et rootfs n'est pas utilisé, il est donc ignoré. Lorsque j'ai essayé la même procédure avec VirtualBox + Ubuntu 18.04.3 LTS, la construction s'est terminée sans aucune erreur.

Après cela, selon vos préférences, il est écrit pour copier la chaîne d'outils dans le répertoire spécifié et la placer dans le PATH.

$ mkdir /opt/rs97tools
$ cp -R output/host/* /opt/rs97tools/
$ export PATH=/opt/rs97tools/mipsel-buildroot-linux-uclibc/sysroot/usr/bin:$PATH
$ export PATH=/opt/rs97tools/bin:$PATH

Ce serait bien si mipsel-linux-gcc et sdl-config --libs pouvaient être exécutés avec ça. Vous pouvez également essayer de créer l'exemple de projet si vous le souhaitez.

$ mkdir /opt/rs97apps
$ cd /opt/rs97apps
$ git clone https://github.com/jbanes/rs97-commander
$ cd rs97-commander
$ make

Résumé

Il a été constaté que Linux fonctionne en tant que système d'exploitation sur la machine de jeu rétro chinoise achetée et que l'émulateur fonctionne en tant qu'application Linux.

U-Boot et Linux sont assez anciens, mais comme des tutoriels sont disponibles, il est facile de créer un environnement de développement.

De plus, en écrivant une application conforme à SDL (1.2), il a été constaté qu'elle pouvait être utilisée sur une machine de jeu rétro chinoise.

SDL2 pris en charge RetroFW v2.0 est en cours de développement J'ai hâte d'y être. J'ai essayé de porter un noyau Linux à long terme pour améliorer l'environnement de développement d'applications, mais cela a été interrompu.

prime

Essayez de construire

Maintenant que la chaîne d'outils est prête, essayez de compiler U-Boot.

U-Boot

$ wget ftp://ftp.denx.de/pub/u-boot/u-boot-1.1.6.tar.bz2
$ tar jxvf u-boot-1.1.6.tar.bz2
$ cd u-boot-1.1.6
$ gzip -cd ../u-boot-1.1.6-jz-20120904-r1819.patch.gz | patch -p1

Reportez-vous à cet exemple et modifiez le contenu du fichier d'en-tête sous ʻinclude / asm-mips. En gros, même si ʻinline et __inline__ sont ajoutés, ʻextern est corrigé en statique`.

$ make lepus_msc_config
$ vi Makefile
(Suppression d'exemples de SUBDIRS)
$ make

Cela créera ʻu-boot-msc.bin`. Je n'ai pas essayé de fonctionner.

Il s'avère que la chaîne d'outils créée par BuildRoot ne semble pas correspondre à la version pour construire U-Boot. En effet, la description de Page, qui a été utilisée comme référence pour modifier le fichier d'en-tête, indique que cette erreur se produira si la chaîne d'outils est nouvelle.

En fait, gcc-mipsel-linux-gnu (gcc-7.4.0), qui peut être installé sur Ubuntu de WSL, fonctionne également.

$ apt install gcc-mipsel-linux-gnu

Après ça

Makefile


ifeq ($(ARCH),mips)
CROSS_COMPILE = mipsel-linux-gnu-
endif

Vous pouvez le modifier pour le faire.

Quand j'ai demandé à voir .config, on m'a [dit] que je n'avais pas le code source (https://boards.dingoonity.org/ingenic-jz4760-devices/retrofw-developer-support-thread/msg190970/ # msg190970).

De plus, il vaut mieux le faire à partir de zéro. Eh bien, celui-ci a été compilé et lié, donc je pense que ce n'est pas grave si vous vérifiez et déplacez. J'en suis accro si je ne le vérifie pas après avoir étudié la méthode de débogage quand cela ne fonctionne pas (je me demande si c'est un débogage LED).

Linux

$ wget https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-2.6.31.3.tar.gz
$ tar zxvf linux-2.6.31.3.tar.gz
$ cd linux-2.6.31.3
$ gzip -cd ../linux-2.6.31.3-jz-20120904-r1448.patch.gz | patch -p1
$ make lepus_defconfig
$ make
  CHK     include/linux/version.h
  CHK     include/linux/utsrelease.h
  SYMLINK include/asm -> include/asm-mips
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/basic/docproc
  HOSTCC  scripts/basic/hash
  CC      kernel/bounds.s
In file included from include/linux/compiler.h:40:0,
                 from include/linux/stddef.h:4,
                 from include/linux/posix_types.h:4,
                 from include/linux/types.h:14,
                 from include/linux/page-flags.h:8,
                 from kernel/bounds.c:9:
include/linux/compiler-gcc.h:86:30: fatal error: linux/compiler-gcc6.h: No such file or directory
 #include gcc_header(__GNUC__)
                              ^
compilation terminated.
/home/yochy/linux-2.6.31.3/./Kbuild:35: recipe for target 'kernel/bounds.s' failed
make[1]: *** [kernel/bounds.s] Error 1
Makefile:977: recipe for target 'prepare0' failed
make: *** [prepare0] Error 2

Par conséquent, il ne se compilera pas tel quel. Le compilateur que j'utilise est gcc-6.4.0, mais la source du noyau Linux dit qu'il ne connaît pas gcc6. En regardant le document (Ingenic Linux Development Guide) publié par Ingenic, le gcc utilisé est 4.1.2.

Pour continuer à travailler avec WSL, vous devez créer un binaire 64 bits pour cela. Pour VirtualBox Ubuntu, tout ce que vous avez à faire est d'installer la bibliothèque de temps de ligne 32 bits et le gcc-4.1.2 pré-construit.

Une fois que vous avez changé le travail en Ubuntu sur VIrtualBox et procédez comme suit ...

$ cd /opt
$ sudo tar jxvf ~/mipseltools-gcc412-glibc261.tar.bz2
$ export PATH=/opt/mipseltools-gcc412-glibc261/bin:$PATH
$ sudo dpkg --add-architecture i386
$ sudo apt install libc6:i386

Vous pouvez maintenant l'utiliser.

$ make lepus_defconfig
$ make
    :
  TIMEC   kernel/timeconst.h
Can't use 'defined(@array)' (Maybe you should just omit the defined()?) at kernel/timeconst.pl line 373.

Cette fois, je me plains de perl. Vous pouvez réécrire kernel / timeconst.pl en vous référant à here.

Ensuite, c'est comme ça.

$ make
    :
drivers/video/jz4760_lcd.c:141: error: 'LCD_CTRL_BST_64' undeclared here (not in a function)

Ceci est lié à la déclaration LCD définie dans .config.

.config


# CONFIG_JZ4760_LCD_TOPPOLY_TD025THEA7_RGB_DELTA is not set
CONFIG_JZ4760_LCD_TOPPOLY_TD043MGEB1=y
# CONFIG_JZ4760_LCD_TRULY_TFTG320240DTSW_18BIT is not set

Je ne sais pas lequel devrait être réglé sur y, alors c'est tout pour le moment. Le LCD_CTRL_BST_64 lui-même est situé dans ʻarch / mips / include / asm / mach-jz4760b / jz4760blcdc.h, donc vous pouvez le passer par # include`.

Quand j'ai demandé à voir .config, j'ai obtenu la source du noyau publiée. À ce stade, le portage vers la version actuelle du noyau n'est pas impossible, mais je ne sais pas si l'application fonctionnera correctement, donc je pense que c'est en attente.

Comparez v1.2.1 et v1.2

J'ai remarqué que RetroFW v1.2.1 est apparu, donc la différence avec la v1.2 utilisée au moment de l'analyse n'est pas bonne. J'ai enquêté dans différentes directions.

Pour conclure, le contenu est le même. En regardant les commentaires de la version et le message de validation, cela ne dit pas que le contenu a changé.

Le nom du fichier batch qui réécrit le contenu de RetroFW.bin pour chaque machine de jeu est devenu kind.

Pour être honnête, je voulais écrire que si vous coupez 0x0000_0000 à 0x087f_ffff (fin de la zone Linux) de RetroFW.bin et que vous l'écrivez sur microSD, vous pouvez mettre à jour la zone FAT32 telle quelle sans être initialisé, vous n'avez donc pas besoin de réinstaller le logiciel. C'était. Pardon.

U-Boot/Linux kernel

U-Boot et le noyau Linux étaient les mêmes (la valeur sha256 est la même). Il ne sert à rien de mettre à jour à cet effet.

RetroFW.img

Le contenu de RegroFW.img était différent (la valeur de sha256 était différente).

Puisque nous savons que U-Boot et le noyau Linux sont identiques, s'il y a une différence dans la zone Linux obtenue à partir de MBR, il peut y avoir un avantage.

C'est pourquoi j'ai comparé RetroFW.img après le décalage 0x80_0000 (secteur 0x4000 dans LBA), mais malheureusement, le contenu était le même.

Comparez v2.0 et v1.2

J'ai remarqué que RetroFW v2.0 était sorti, j'ai donc vérifié la différence par rapport à la v1.2 utilisée au moment de l'analyse. ..

De la conclusion, cela semble valoir la peine d'essayer car le contenu de BuildRoot a changé. Pour plus de détails, reportez-vous à CHANGELOG.md.

Les noyaux U-Boot et Linux sont (probablement) les mêmes.

Configuration de la partition

En regardant le MBR, il y avait une légère différence dans la configuration de la partition (taille), donc je vais le montrer ici.

No de partition. Premier secteur(LBA) Nombre de secteurs Type de partition
#1 0x0000_4000 0x0004_f800 Linux
#2 0x0005_3800 0x0007_0800 Linux swap
#3 0x000c_4000 0x0013_c000 FAT32
#4 - - Gratuit

La zone Linux a été légèrement étendue et la zone d'échange a été réduite en conséquence. Le premier secteur de la zone FAT32 n'a pas changé.

Par conséquent, il semble que la zone FAT32 puisse être mise à jour sans l'initialiser en remplaçant uniquement les zones MBR et Linux.

Version BuildRoot

En vérifiant la version de BuildRoot publiée ensemble, il semble que 2018.02.11 soit utilisé (à partir de CHANGES). Le tutoriel montré ci-dessus indique d'utiliser 2018.02.9, mais il semble que la version a été mise à jour.

Comparez v2.1 et v2.0

RetroFW v2.1 a été publié.

Depuis la version 2.0 et les versions ultérieures, les informations de mise à jour sont rédigées avec gentillesse, vous devez lire la note de publication liée et décider si cela est nécessaire ou non.

À propos, lorsque j'ai essayé de remplacer et de mettre à jour la zone FAT32 et les versions antérieures, la réinitialisation s'est déroulée. N'oubliez pas de le sauvegarder car il sera perdu.

Comparez V2.2 et v2.1

RetroFW v2.2 a été publié.

Pour une raison quelconque, les informations de mise à jour ne sont pas écrites correctement cette fois. Il y a quelque chose comme ça dans Readme.md, mais quel est le changement Q.O.L. de la récupération du système (Q.O.L. dans ce cas, la Quarity of Life?).

Le fichier image cette fois est un peu étrange, il ne contient qu'une seule partition.

No de partition. Premier secteur(LBA) Nombre de secteurs Type de partition
#1 0x0000_4000 0x0004_f800 Linux
#2 - - Gratuit
#3 - - Gratuit
#4 - - Gratuit

Lorsque je l'ai écrit sur la microSD et que je l'ai mis sous tension, le processus d'initialisation s'est déroulé comme une évidence. On a l'impression qu'il s'est terminé plus tôt que le traitement précédent. La structure de partition après le processus d'initialisation était la suivante.

No de partition. Premier secteur(LBA) Nombre de secteurs Type de partition
#1 0x0000_4000 0x0004_f800 Linux
#2 0x0005_3800 0x0007_0800 Linux swap
#3 0x000c_4000 0x01c6_a000 FAT32
#4 - - Gratuit

La zone FAT32 est devenue plus large qu'auparavant.

À propos, la taille du fichier image est de 175 112 704 octets, mais il s'agit du secteur 342 017 (0x53801) et il coupe simplement la partition du fichier d'échange. Y a-t-il une raison pour laquelle je ne l'ai pas réduit d'un secteur?

En passant, quand j'ai lu un peu plus Readme.md, il est dit que libopk et opkrun ont été importés et PyMenu et SimpleMenu sont devenus disponibles. Euh ... PyMenu, SimpleMenu.

Voyons un peu plus tard.

Les références

Recommended Posts

Analysons la machine de jeu rétro chinoise
Analyse inverse du modèle d'apprentissage automatique
Faisons un jeu de shiritori avec Python
Essayez de créer un jeu simple avec Python 3 et iPhone
Sentons-nous comme un chercheur en matériaux avec l'apprentissage automatique
Faisons un jeu de squash
〇✕ J'ai fait un jeu
Créez un jeu à la Tetris!
Nostalgique, reproduisons un jeu de personnages comme CBM-3032 avec ncursesw.