Skip to content

Commit

Permalink
Merge pull request #3 from caol64/1-3d-icons-can-display-different-ac…
Browse files Browse the repository at this point in the history
…tions

1 3d icons can display different actions
  • Loading branch information
caol64 authored Nov 20, 2023
2 parents 6f4f0b2 + 0f76106 commit 512b53e
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 22 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,26 @@ English | [中文](README_zh.md)

A PCSX2 memory card save file browser supports displaying 3D icons.

# Dependencies
## What's New
3D icons can exhibit different actions based on mouse interactions.
![](data/2.gif)

## Dependencies
The dependencies are listed below:
- Python3
- WxPython
- Numpy
- ModernGL
- PyGlm

# Quick Start
## Quick Start
```shell
python3 wxwindow.py
```

Run the above command to open a GUI window. From the menu bar, select `Open File` and then select the `.ps2` file from your computer.

# Documentation
## Documentation
- [Analyze the file system of the PS2 memory card](https://babyno.top/posts/2023/09/parsing-ps2-memcard-file-system/)
- [Export save files from the PS2 memory card](https://babyno.top/posts/2023/09/exporting-file-from-ps2-memcard/)
- [Analyze the 3D icons of PS2 game save files](https://babyno.top/posts/2023/10/parsing-ps2-3d-icon/)
Expand All @@ -29,7 +33,7 @@ Run the above command to open a GUI window. From the menu bar, select `Open File
- [Texture image encoding algorithm A1B5G5R5 in PS2](https://babyno.top/posts/2023/10/ps2-texture-encoding-algorithm-a1b5g5r5/)


# Reference
## Reference
- [gothi - icon.sys format](https://www.ps2savetools.com/documents/iconsys-format/)
- [Martin Akesson - PS2 Icon Format v0.5](http://www.csclub.uwaterloo.ca:11068/mymc/ps2icon-0.5.pdf)
- [Florian Märkl - mymcplus](https://git.sr.ht/~thestr4ng3r/mymcplus)
Expand Down
12 changes: 8 additions & 4 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,34 @@

`PCSX2`存档查看器,支持显示存档里的3D动态图标。

# 依赖
## 新特性
3d图标可以根据鼠标的交互变化不同的动作。
![](data/2.gif)

## 依赖
ps2mc-browser依赖如下:
- Python3
- WxPython
- Numpy
- ModernGL
- PyGlm

# 快速开始
## 快速开始
```shell
python3 wxwindow.py
```

在命令行输入上述命令,会打开GUI窗口。在顶部菜单栏选择`Open File`并且选择你电脑硬盘上的`PCSX2`存档。

# 文档
## 文档
- [解析PS2记忆卡文件系统](https://babyno.top/posts/2023/09/parsing-ps2-memcard-file-system/)
- [从PS2记忆卡中导出存档文件](https://babyno.top/posts/2023/09/exporting-file-from-ps2-memcard/)
- [解析PS2游戏存档3D图标](https://babyno.top/posts/2023/10/parsing-ps2-3d-icon/)
- [使用Python和OpenGL渲染PS2存档3D图标](https://babyno.top/posts/2023/10/rendering-ps2-3d-icon/)
- [RLE算法在PS2中的应用](https://babyno.top/posts/2023/10/rle-algorithm-in-ps2/)
- [PS2纹理图片编码算法A1B5G5R5](https://babyno.top/posts/2023/10/ps2-texture-encoding-algorithm-a1b5g5r5/)

# 参考
## 参考
- [gothi - icon.sys format](https://www.ps2savetools.com/documents/iconsys-format/)
- [Martin Akesson - PS2 Icon Format v0.5](http://www.csclub.uwaterloo.ca:11068/mymc/ps2icon-0.5.pdf)
- [Florian Märkl - mymcplus](https://git.sr.ht/~thestr4ng3r/mymcplus)
Expand Down
Binary file added data/2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 12 additions & 6 deletions src/browser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from typing import List, Tuple

from error import Error
from icon import Icon, IconSys
Expand Down Expand Up @@ -41,15 +42,20 @@ def list_all(self):
for entry in entries:
print(f" {entry.name}")

def get_icon(self, name):
def get_icon(self, name) -> Tuple[IconSys, List[Icon]]:
entries = self.lookup_entry_by_name(name)

icon_sys_entry = [e for e in entries if e.is_file() and e.name == "icon.sys"][0]
icon_sys = IconSys(self.ps2mc.read_data_cluster(icon_sys_entry))
icon_entry = [
e for e in entries if e.is_file() and e.name == icon_sys.icon_file_normal
][0]
icon = Icon(self.ps2mc.read_data_cluster(icon_entry))
return icon_sys, icon

icon_names_ = [icon_sys.icon_file_normal, icon_sys.icon_file_copy, icon_sys.icon_file_delete]
icon_names = []
for icon_name in icon_names_:
if icon_name not in icon_names:
icon_names.append(icon_name)
icon_entries = [[e for e in entries if e.is_file() and e.name == icon_name][0] for icon_name in icon_names]
icons = [Icon(self.ps2mc.read_data_cluster(icon_entry)) for icon_entry in icon_entries]
return icon_sys, icons

def destroy(self):
self.ps2mc.destroy()
6 changes: 6 additions & 0 deletions src/icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ def load_texture(self, offset):
return self.byte_val[offset: offset + Icon.__texture_size]

def load_texture_compressed(self, offset):
"""
See: https://babyno.top/posts/2023/10/rle-algorithm-in-ps2/
"""
compressed_size = struct.Struct("<I").unpack_from(self.byte_val, offset)[0]
offset += 4
rle_code_struct = struct.Struct("<H")
Expand All @@ -216,6 +219,9 @@ def load_texture_compressed(self, offset):
return bytes(texture_buf)

def decode_texture(self):
"""
See: https://babyno.top/posts/2023/10/ps2-texture-encoding-algorithm-a1b5g5r5/
"""
tex_offset = 0
rgb_tex_offset = 0
out = np.zeros(Icon.__rgb_texture_size, dtype=np.uint8)
Expand Down
36 changes: 36 additions & 0 deletions src/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import glm
import numpy as np
import utils


class Camera:
Expand All @@ -22,6 +23,9 @@ def __init__(self, win_size):


class IconModel:
"""
Vertex data for the 3D icon.
"""
__FIXED_POINT_FACTOR = 4096.0

def __init__(self, ctx, program, icon):
Expand Down Expand Up @@ -75,6 +79,9 @@ def release(self):


class BgModel:
"""
Vertex data for the background.
"""
__FIXED_COLOR_FACTOR = 255.0
__FIXED_ALPHA_FACTOR = 128.0

Expand Down Expand Up @@ -116,3 +123,32 @@ def vao(self):
def release(self):
self.vbo.release()
self._vao.release()


class CircleModel:
"""
Vertex data for the action button.
"""

def __init__(self, ctx, program, n):
self.ctx = ctx
self.program = program
self.vbos = []
self._vaos = []
self.circle_centers = utils.circle_centers(n)
if self.circle_centers:
for circle_center in self.circle_centers:
vertex_data = utils.circle_data(circle_center)
vbo = self.ctx.buffer(vertex_data)
self.vbos.append(vbo)
self._vaos.append(self.ctx.simple_vertex_array(self.program["circle"], vbo, "vertexPos"))

def circle_centers(self):
return self.circle_centers

def vaos(self):
return self._vaos

def release(self):
[vbo.release() for vbo in self.vbos]
[vao.release() for vao in self._vaos]
7 changes: 7 additions & 0 deletions src/shaders/circle.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#version 330 core

out vec4 fragColor;

void main() {
fragColor = vec4(1.0, 1.0, 1.0, 0.6);
}
7 changes: 7 additions & 0 deletions src/shaders/circle.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#version 330 core

in vec2 vertexPos;

void main() {
gl_Position = vec4(vertexPos, 0, 1.0);
}
60 changes: 60 additions & 0 deletions src/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import codecs
import numpy as np


CANVAS_WIDTH = 640
CANVAS_HEIGHT = 480
CIRCLE_SEGMENTS_NUM = 10
CIRCLE_RADIUS = 0.02


def zero_terminate(s):
Expand All @@ -20,3 +27,56 @@ def decode_sjis(s):
except Exception as ex:
print(ex)
return "\uFFFD" * 3


def distance(x1, y1, x2, y2):
"""
Calculating the distance between two points using the Pythagorean Theorem.
"""
return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5


def circle_centers(n):
"""
Create a specified number of circle centers and return their coordinates.
Ignore if the quantity is 1.
"""
if n == 1:
return []
return ((0.7, 0.85), (0.8, 0.85)) if n == 2 else ((0.6, 0.85), (0.7, 0.85), (0.8, 0.85))


def circle_data(circle_center):
"""
Construct vertex data that forms a circle based on the center coordinates and radius of the circle.
"""
vertices = []
for i in range(CIRCLE_SEGMENTS_NUM + 1):
angle = i * 2.0 * np.pi / CIRCLE_SEGMENTS_NUM
x = CIRCLE_RADIUS * np.cos(angle) + circle_center[0]
y = CIRCLE_RADIUS * np.sin(angle) + circle_center[1]
vertices.extend([x, y])
return np.array(vertices, dtype='f4')


def coord_convert(screen_x, screen_y):
"""
Convert screen coordinates to normalized device coordinates.
"""
ndc_x = (2.0 * screen_x / CANVAS_WIDTH) - 1.0
ndc_y = 1.0 - (2.0 * screen_y / CANVAS_HEIGHT)
return (ndc_x, ndc_y)


def determine_circle_index(screen_x, screen_y, circle_centers):
"""
Determine whether the given coordinates are within the radius coverage of a circle.
If so, return the index of the circle.
"""
if not circle_centers:
return None
ndc_x, ndc_y = coord_convert(screen_x, screen_y)
for index, circle_center in enumerate(circle_centers):
if distance(ndc_x, ndc_y, circle_center[0], circle_center[1]) <= CIRCLE_RADIUS:
return index
return None
Loading

0 comments on commit 512b53e

Please sign in to comment.