├── .gitignore
├── LICENSE
├── README.md
├── README_EN.md
├── app.go
├── build
├── README.md
├── appicon.png
├── darwin
│ ├── Info.dev.plist
│ └── Info.plist
├── sh
│ ├── dev.bat
│ ├── macos
│ │ └── prod.sh
│ ├── prod_x64.bat
│ ├── prod_x86.bat
│ └── test.bat
└── windows
│ ├── icon.ico
│ ├── info.json
│ ├── installer
│ ├── project.nsi
│ └── wails_tools.nsh
│ └── wails.exe.manifest
├── components
├── backup.go
├── bat.go
├── cache.go
├── font.go
├── hfsdb.go
├── linkFile.go
├── lua.go
├── output.go
├── platform.go
├── rom.go
├── rungame.go
├── thumb.go
├── upgrade.go
└── zip.go
├── config
├── config.go
├── romBase.go
├── romSetting.go
├── share.go
└── subgame.go
├── constant
├── args.go
├── const.go
└── path.go
├── controller
├── audio.go
├── cache.go
├── config.go
├── controller.go
├── dialog.go
├── image.go
├── input.go
├── menu.go
├── others.go
├── platform.go
├── rom.go
├── romManage.go
├── rombase.go
├── shortcut.go
├── simulator.go
└── upgrade.go
├── data.dll
├── db
├── config.go
├── configVO.go
├── db.go
├── filter.go
├── menu.go
├── menuVO.go
├── platform.go
├── platformUi.go
├── platformVO.go
├── rom.go
├── romVO.go
├── rombaseAlias.go
├── rombaseEnum.go
├── shortcut.go
├── simulator.go
├── thumbs.go
└── upgrade.go
├── frontend
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── postcss.config.cjs
├── public
│ └── images
│ │ ├── context
│ │ ├── alias.png
│ │ ├── app.png
│ │ ├── audio.png
│ │ ├── baseinfo.png
│ │ ├── copy.png
│ │ ├── delete.png
│ │ ├── doc.png
│ │ ├── down.png
│ │ ├── fav_0.png
│ │ ├── fav_1.png
│ │ ├── files.png
│ │ ├── folder.png
│ │ ├── hide_0.png
│ │ ├── hide_1.png
│ │ ├── move.png
│ │ ├── output.png
│ │ ├── rename.png
│ │ ├── strategy.png
│ │ ├── sub.png
│ │ ├── thumbs.png
│ │ ├── unlink.png
│ │ └── up.png
│ │ ├── ctl
│ │ ├── +.png
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ ├── 6.png
│ │ ├── 7.png
│ │ ├── 8.png
│ │ ├── 9.png
│ │ ├── A.png
│ │ ├── B.png
│ │ ├── C.png
│ │ ├── D.png
│ │ ├── E.png
│ │ ├── F.png
│ │ ├── K.png
│ │ ├── N.png
│ │ ├── P.png
│ │ └── S.png
│ │ ├── ico.png
│ │ ├── list-style
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ └── 4.png
│ │ ├── rom_animate.png
│ │ └── svg
│ │ ├── complete0.svg
│ │ ├── complete1.svg
│ │ ├── complete2.svg
│ │ ├── number.svg
│ │ └── time.svg
├── quasar.config.js
├── src
│ ├── App.vue
│ ├── boot
│ │ ├── .gitkeep
│ │ ├── axios.ts
│ │ ├── constant.ts
│ │ ├── i18n.ts
│ │ └── imgObserver.ts
│ ├── components
│ │ ├── AboutComponent.vue
│ │ ├── CopyrightComponent.vue
│ │ ├── SDialogComponent.vue
│ │ ├── UpgradeComponent.vue
│ │ ├── dialog.ts
│ │ ├── models.ts
│ │ └── utils.ts
│ ├── css
│ │ ├── app.css
│ │ ├── classic
│ │ │ ├── common.css
│ │ │ ├── contentBar.css
│ │ │ ├── contentContext.css
│ │ │ ├── headerBarFilter.css
│ │ │ ├── layout.css
│ │ │ ├── menuBar.css
│ │ │ ├── platformBar.css
│ │ │ └── rightBar.css
│ │ ├── grid.css
│ │ ├── manage.css
│ │ ├── modules.css
│ │ ├── page
│ │ │ └── platform.css
│ │ ├── playnite
│ │ │ ├── contentBar.css
│ │ │ ├── layout.css
│ │ │ ├── platform.css
│ │ │ └── romlistBar.css
│ │ ├── romManage.css
│ │ ├── tiny
│ │ │ ├── layout.css
│ │ │ ├── platform.css
│ │ │ └── romlistBar.css
│ │ └── transitions.css
│ ├── env.d.ts
│ ├── i18n
│ │ ├── en-US
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── pages
│ │ ├── ErrorNotFound.vue
│ │ ├── classic
│ │ │ ├── ContentBar.vue
│ │ │ ├── HeaderBarFilter.vue
│ │ │ ├── HeaderBarLogo.vue
│ │ │ ├── HeaderBarTool.vue
│ │ │ ├── Layout.vue
│ │ │ ├── LeftBarMenu.vue
│ │ │ ├── LeftBarPlatform.vue
│ │ │ ├── RightBar.vue
│ │ │ ├── configUI
│ │ │ │ ├── Layout.vue
│ │ │ │ └── UiConst.vue
│ │ │ ├── context
│ │ │ │ ├── BaseInfo.vue
│ │ │ │ ├── Context.vue
│ │ │ │ ├── CopyModule.vue
│ │ │ │ ├── MoveModule.vue
│ │ │ │ ├── ShareRom.vue
│ │ │ │ ├── SubGame.vue
│ │ │ │ └── Thumbs.vue
│ │ │ └── modules
│ │ │ │ ├── EventGamepad.vue
│ │ │ │ ├── EventKeyboard.vue
│ │ │ │ ├── FabInputRom.vue
│ │ │ │ ├── FabMenu.vue
│ │ │ │ ├── FabOutputRom.vue
│ │ │ │ └── RomSim.vue
│ │ ├── config
│ │ │ ├── Layout.vue
│ │ │ ├── RombaseEnum.vue
│ │ │ └── Shortcut.vue
│ │ ├── configPlatform
│ │ │ ├── Layout.vue
│ │ │ ├── RombaseAlias.vue
│ │ │ └── Simulators.vue
│ │ ├── home
│ │ │ └── Layout.vue
│ │ ├── playnite
│ │ │ ├── ContentBar.vue
│ │ │ ├── HeaderBarTool.vue
│ │ │ ├── Layout.vue
│ │ │ ├── Platform.vue
│ │ │ ├── RomListBar.vue
│ │ │ ├── configUI
│ │ │ │ ├── Layout.vue
│ │ │ │ └── UiConst.vue
│ │ │ └── modules
│ │ │ │ ├── EventGamepad.vue
│ │ │ │ └── EventKeyboard.vue
│ │ ├── romManage
│ │ │ ├── Layout.vue
│ │ │ ├── Repeat.vue
│ │ │ ├── Rombase.vue
│ │ │ ├── Romfile.vue
│ │ │ ├── Simulator.vue
│ │ │ └── Unowned.vue
│ │ ├── test
│ │ │ └── Layout.vue
│ │ └── tiny
│ │ │ ├── HeaderBarTool.vue
│ │ │ ├── Layout.vue
│ │ │ ├── Platform.vue
│ │ │ ├── RomListBar.vue
│ │ │ ├── configUI
│ │ │ ├── Layout.vue
│ │ │ └── UiConst.vue
│ │ │ └── modules
│ │ │ ├── EventGamepad.vue
│ │ │ └── EventKeyboard.vue
│ ├── quasar.d.ts
│ ├── router
│ │ ├── index.ts
│ │ └── routes.ts
│ ├── shims-vue.d.ts
│ └── stores
│ │ ├── example-store.ts
│ │ ├── globalData.ts
│ │ ├── index.ts
│ │ └── store-flag.d.ts
├── tsconfig.json
├── vite.config.js
└── vue.config.js
├── go.mod
├── go.sum
├── main.go
├── main_test.go
├── modules
├── audio.go
├── backup.go
├── cache.go
├── config.go
├── dialog.go
├── image.go
├── inputPlatform.go
├── menu.go
├── others.go
├── platform.go
├── rom.go
├── romConfig.go
├── romManage.go
├── romRename.go
├── romSimSetting.go
├── rombaseAlias.go
├── rombaseEnum.go
├── runGame.go
├── shortcut.go
├── simulator.go
├── strategyFiles.go
├── thumb.go
└── upgrade.go
├── readme
├── 1.jpg
├── 2.jpg
├── 3.jpg
└── logo.png
├── request
├── input.go
├── platform.go
└── rom.go
├── server
└── http.go
├── utils
├── alert.go
├── args.go
├── calljs.go
├── conv.go
├── convert.go
├── csv.go
├── encode.go
├── file.go
├── filepath.go
├── hfsdb.go
├── html.go
├── http.go
├── image.go
├── log.go
├── pinyin
│ ├── .travis.yml
│ ├── LICENSE
│ ├── Makefile
│ ├── README.md
│ ├── codecov.yml
│ ├── error.go
│ ├── pinyin.go
│ ├── pinyin.txt
│ ├── pinyin_test.go
│ └── resource.go
├── rom.go
├── slice.go
├── string.go
├── struct.go
├── system.go
├── translate.go
├── uuid.go
├── version.go
└── wails.go
└── wails.json
/.gitignore:
--------------------------------------------------------------------------------
1 | build/bin
2 | build/sh/prod_build.bat
3 | node_modules
4 | frontend/dist
5 | frontend/.quasar
6 | frontend/wailsjs
7 | tmp
8 | frontend/package.json.md5
9 | frontend/public/images/custom
10 | .idea
11 | frontend/public/temp
12 | .env
13 | cache
14 | .github
15 | .gitee
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 frontlon
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 |
6 |
7 | [[EN]](README_EN.md)
8 |
9 | **SimUI Pulsar 是由热爱街机文化的玩家开发的免费专业游戏ROM管理软件。是SimUI的重构版本。**
10 |
11 |
12 |
13 | ## 软件介绍
14 |
15 | 我们希望打造极致的产品,融入工匠精神,不断探索每一种更好的可能。SIMUI以资料收集和分享为圆心,为玩家打造更具品质的模拟游戏图书馆软件。
16 |
17 | 软件支持自由添加游戏平台、支持多游戏模拟器、多游戏目录;
18 |
19 | 支持ROM别名,ROM子游戏、支持游戏资料定义;
20 |
21 | 支持多种展示图显示、支持gif动画、支持游戏音乐、支持视频;
22 |
23 | 支持游戏简介、游戏攻略,支持富文本显示;
24 |
25 | 支持自定义皮肤、支持多语言;
26 |
27 | 支持手柄、支持摇杆;
28 |
29 | 软件懒加载机制、软件改
30 |
31 | 名、下载缩略图、自定义实用工具等诸多功能。
32 |
33 | 
34 |
35 | 
36 |
37 | 
38 |
39 |
40 | ## 软件地址
41 |
42 | [www.simui.net/](http://www.simui.net/)
43 |
44 |
45 |
46 | ## 技术栈
47 |
48 | GOLANG + wails + vue3 + quasar
49 |
50 | ## 目录说明
51 |
52 | ```
53 | build 编译目录
54 | components 业务组件
55 | config 配置相关
56 | constant 常量定义
57 | controller (前端调用入口)
58 | db 数据库dao
59 | frontend 前端代码
60 | modules 业务模块
61 | request 结构体定义
62 | utils 公共库
63 | ```
64 |
65 |
66 |
67 | ## 相关技术文档
68 |
69 | wails:https://wails.io/docs/introduction/
70 |
71 | vue3:https://vuejs.org/guide/introduction.html
72 |
73 | quasar:https://quasar.dev/docs
74 |
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [[简体中文]](README.md)
4 |
5 | **SimUI Pulsar is a free, professional game ROM management software developed by players who love arcade culture. It is a restructured version of SimUI.**
6 |
7 | ## Software Introduction
8 |
9 | We aim to create the ultimate product, incorporating a craftsman spirit, and continually exploring every better possibility. SIMUI, centered around data collection and sharing, is designed to provide players with a high-quality emulated game library software.
10 |
11 | The software supports the addition of game platforms freely, supports multiple game emulators, and multiple game directories;
12 |
13 | Supports ROM aliases, ROM sub-games, and game metadata definitions;
14 |
15 | Supports various display images, GIF animations, game music, and videos;
16 |
17 | Supports game descriptions and guides, with rich text display;
18 |
19 | Supports custom skins and multiple languages;
20 |
21 | Supports gamepads and joysticks;
22 |
23 | The software features lazy loading, renaming, thumbnail downloads, custom utilities, and many other functions.
24 |
25 | 
26 |
27 | 
28 |
29 | 
30 |
31 |
32 |
33 | ## Software Website
34 |
35 | [www.simui.net/](http://www.simui.net/)
36 |
37 | ## Technology Stack
38 |
39 | GOLANG + wails + vue3 + quasar
40 |
41 | ## Directory Explanation
42 |
43 | ```
44 | build Compilation directory
45 | components Business components
46 | config Configuration
47 | constant Constant definitions
48 | controller (Frontend call entry)
49 | db Database DAO
50 | frontend Frontend code
51 | modules Business modules
52 | request Structure definitions
53 | utils Common libraries
54 | ```
55 |
56 | ## Related Technical Documentation
57 |
58 | wails: https://wails.io/docs/introduction/
59 |
60 | vue3: https://vuejs.org/guide/introduction.html
61 |
62 | quasar: https://quasar.dev/docs
63 |
--------------------------------------------------------------------------------
/app.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "github.com/wailsapp/wails/v2/pkg/runtime"
6 | "simUI/config"
7 | )
8 |
9 | // App struct
10 | type App struct {
11 | ctx context.Context
12 | }
13 |
14 | // NewApp creates a new App application struct
15 | func NewApp() *App {
16 | return &App{}
17 | }
18 |
19 | // startup is called when the app starts. The context is saved
20 | // so we can call the runtime methods
21 | func (a *App) startup(ctx context.Context) {
22 | a.ctx = ctx
23 | config.Ctx = ctx
24 |
25 | //做一些配置
26 | runtime.WindowSetDarkTheme(ctx) //设为黑色主题
27 | }
28 |
--------------------------------------------------------------------------------
/build/README.md:
--------------------------------------------------------------------------------
1 | # Build Directory
2 |
3 | The build directory is used to house all the build files and assets for your application.
4 |
5 | The structure is:
6 |
7 | * bin - Output directory
8 | * darwin - macOS specific files
9 | * windows - Windows specific files
10 |
11 | ## Mac
12 |
13 | The `darwin` directory holds files specific to Mac builds.
14 | These may be customised and used as part of the build. To return these files to the default state, simply delete them
15 | and
16 | build with `wails build`.
17 |
18 | The directory contains the following files:
19 |
20 | - `Info.plist` - the main plist file used for Mac builds. It is used when building using `wails build`.
21 | - `Info.dev.plist` - same as the main plist file but used when building using `wails dev`.
22 |
23 | ## Windows
24 |
25 | The `windows` directory contains the manifest and rc files used when building with `wails build`.
26 | These may be customised for your application. To return these files to the default state, simply delete them and
27 | build with `wails build`.
28 |
29 | - `icon.ico` - The icon used for the application. This is used when building using `wails build`. If you wish to
30 | use a different icon, simply replace this file with your own. If it is missing, a new `icon.ico` file
31 | will be created using the `appicon.png` file in the build directory.
32 | - `installer/*` - The files used to create the Windows installer. These are used when building using `wails build`.
33 | - `info.json` - Application details used for Windows builds. The data here will be used by the Windows installer,
34 | as well as the application itself (right click the exe -> properties -> details)
35 | - `wails.exe.manifest` - The main application manifest file.
--------------------------------------------------------------------------------
/build/appicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/build/appicon.png
--------------------------------------------------------------------------------
/build/darwin/Info.dev.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CFBundlePackageType
5 | APPL
6 | CFBundleName
7 | {{.Info.ProductName}}
8 | CFBundleExecutable
9 | {{.Name}}
10 | CFBundleIdentifier
11 | com.wails.{{.Name}}
12 | CFBundleVersion
13 | {{.Info.ProductVersion}}
14 | CFBundleGetInfoString
15 | {{.Info.Comments}}
16 | CFBundleShortVersionString
17 | {{.Info.ProductVersion}}
18 | CFBundleIconFile
19 | iconfile
20 | LSMinimumSystemVersion
21 | 10.13.0
22 | NSHighResolutionCapable
23 | true
24 | NSHumanReadableCopyright
25 | {{.Info.Copyright}}
26 | NSAppTransportSecurity
27 |
28 | NSAllowsLocalNetworking
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/build/darwin/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CFBundlePackageType
5 | APPL
6 | CFBundleName
7 | {{.Info.ProductName}}
8 | CFBundleExecutable
9 | {{.Name}}
10 | CFBundleIdentifier
11 | com.wails.{{.Name}}
12 | CFBundleVersion
13 | {{.Info.ProductVersion}}
14 | CFBundleGetInfoString
15 | {{.Info.Comments}}
16 | CFBundleShortVersionString
17 | {{.Info.ProductVersion}}
18 | CFBundleIconFile
19 | iconfile
20 | LSMinimumSystemVersion
21 | 10.13.0
22 | NSHighResolutionCapable
23 | true
24 | NSHumanReadableCopyright
25 | {{.Info.Copyright}}
26 |
27 |
--------------------------------------------------------------------------------
/build/sh/dev.bat:
--------------------------------------------------------------------------------
1 | cd %~dp0
2 | cd ../../
3 | wails dev -loglevel "Info" -frontenddevserverurl "http://localhost:9000"
--------------------------------------------------------------------------------
/build/sh/macos/prod.sh:
--------------------------------------------------------------------------------
1 | cd %~dp0
2 | cd ../../../
3 | SET CGO_ENABLED=0
4 | SET GOOS=darwin
5 | SET GOARCH=amd64
6 | go build main.go
7 | pause
--------------------------------------------------------------------------------
/build/sh/prod_x64.bat:
--------------------------------------------------------------------------------
1 | cd %~dp0
2 | cd ../../
3 | wails build -ldflags="-H windowsgui -w -s -X main.buildTime=" -o ../bin/simui-pulsar.exe
--------------------------------------------------------------------------------
/build/sh/prod_x86.bat:
--------------------------------------------------------------------------------
1 | cd %~dp0
2 | cd ../../
3 | set CGO_ENABLED=1
4 | set GOOS=windows
5 | set GOARCH=386
6 | wails build -ldflags="-H windowsgui -w -s -X main.buildTime=" -o ../bin/simui-pulsar-x86.exe
--------------------------------------------------------------------------------
/build/sh/test.bat:
--------------------------------------------------------------------------------
1 | cd %~dp0
2 | cd ../../
3 | wails build -debug -devtools -upx -o ../bin/simui-pulsar-test.exe
--------------------------------------------------------------------------------
/build/windows/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/build/windows/icon.ico
--------------------------------------------------------------------------------
/build/windows/info.json:
--------------------------------------------------------------------------------
1 | {
2 | "fixed": {
3 | "file_version": "{{.Info.ProductVersion}}"
4 | },
5 | "info": {
6 | "0000": {
7 | "ProductVersion": "{{.Info.ProductVersion}}",
8 | "CompanyName": "{{.Info.CompanyName}}",
9 | "FileDescription": "{{.Info.ProductName}}",
10 | "LegalCopyright": "{{.Info.Copyright}}",
11 | "ProductName": "{{.Info.ProductName}}",
12 | "Comments": "{{.Info.Comments}}"
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/build/windows/wails.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | true/pm
12 | permonitorv2,permonitor
13 |
14 |
15 |
--------------------------------------------------------------------------------
/components/backup.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import (
4 | "simUI/constant"
5 | "simUI/utils"
6 | "strings"
7 | )
8 |
9 | // 复制文件夹时,生成 src 和 dst 两个路径
10 | func GetSrcAndDstPath(root, p, resType string) (string, string) {
11 | if root == "" || p == "" {
12 | return "", ""
13 | }
14 |
15 | if resType == "simulator" {
16 | p = utils.GetFilePath(p)
17 | }
18 |
19 | //绝对路径不拷贝
20 | src := strings.Replace(p, root, "", 1)
21 | if utils.IsAbsPath(src) {
22 | return p, ""
23 | }
24 | src = root + src
25 | dst := constant.ROOT_PATH + p
26 |
27 | //目录不存在,则新建
28 | fileType, _ := utils.CheckFileOrDir(dst)
29 | if fileType == 2 {
30 | utils.CreateDir(dst)
31 |
32 | //已存在,不复制
33 | if !utils.IsDirEmpty(dst) {
34 | return "", ""
35 | }
36 | } else {
37 | //已存在,不复制
38 | if utils.FileExists(dst) {
39 | return "", ""
40 | }
41 | }
42 | return src, dst
43 | }
44 |
--------------------------------------------------------------------------------
/components/bat.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import (
4 | "simUI/utils"
5 | )
6 |
7 | func CreateGameBat(batPath, gamePath string) error {
8 | relPath := utils.GetRelPathByTowPath(batPath, gamePath)
9 | f := utils.GetFileNameAndExt(gamePath)
10 |
11 | bat := ""
12 | bat += "cd %~dp0\r\n"
13 | bat += `cd "` + relPath + `"` + "\r\n"
14 | bat += `start "" "` + f + `"` + "\r\n"
15 | bat = utils.Utf8ToGbk(bat)
16 | return utils.CreateFile(batPath, bat)
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/components/font.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import (
4 | "github.com/adrg/sysfont"
5 | "simUI/constant"
6 | "simUI/db"
7 | "simUI/utils"
8 | "strings"
9 | )
10 |
11 | // 读取用户字体
12 | func GetUserFontList() []db.PlatformUIFont {
13 |
14 | files, err := utils.ScanCurrentDir(constant.FONT_PATH)
15 |
16 | list := []db.PlatformUIFont{}
17 |
18 | if err != nil || len(files) == 0 {
19 | return list
20 | }
21 |
22 | for _, f := range files {
23 |
24 | if f.IsDir() {
25 | continue
26 | }
27 |
28 | ext := strings.ToLower(utils.GetFileExt(f.Name()))
29 | if _, ok := constant.FONT_EXTS[ext]; !ok {
30 | continue
31 | }
32 |
33 | fonts := db.PlatformUIFont{
34 | Type: 2,
35 | Family: utils.GetFileName(f.Name()),
36 | Format: constant.FONT_EXTS[ext],
37 | Src: utils.ToRelPath(constant.FONT_PATH+f.Name(), ""),
38 | }
39 | list = append(list, fonts)
40 | }
41 |
42 | return list
43 | }
44 |
45 | /*
46 | 读取系统字体列表
47 | */
48 | func GetSystemFontList() []db.PlatformUIFont {
49 |
50 | // 创建一个字体查找器
51 | finder := sysfont.NewFinder(nil)
52 |
53 | // 获取系统所有字体
54 | fonts := finder.List()
55 |
56 | if fonts == nil || len(fonts) == 0 {
57 | return []db.PlatformUIFont{}
58 | }
59 |
60 | // 打印字体名称
61 | list := []string{}
62 | for _, font := range fonts {
63 | if font.Family == "" {
64 | continue
65 | }
66 | list = append(list, font.Family)
67 | }
68 |
69 | list = utils.SliceRemoveDuplicate(list)
70 |
71 | resp := []db.PlatformUIFont{}
72 | for _, family := range list {
73 | f := db.PlatformUIFont{
74 | Type: 1,
75 | Family: family,
76 | }
77 | resp = append(resp, f)
78 |
79 | }
80 | return resp
81 | }
82 |
--------------------------------------------------------------------------------
/components/linkFile.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import (
4 | "errors"
5 | "os"
6 | "path/filepath"
7 | "simUI/utils"
8 | "strings"
9 | )
10 |
11 | // rom链接文件类型
12 | var RomLinkExt = ".slnk"
13 | var RomLinkSplit = "||"
14 |
15 | type Slnk struct {
16 | RelRomPath string //rom相对路径
17 | AbsRomPath string //rom绝对路径
18 | Params []string //启动参数
19 | RomName string //rom名称
20 | }
21 |
22 | // 读取所有rom链接
23 | func ReadAllRomLinks(LinkPath string) (map[string]string, error) {
24 | result := map[string]string{}
25 | if err := filepath.Walk(LinkPath,
26 | func(p string, f os.FileInfo, err error) error {
27 |
28 | if f == nil {
29 | return nil
30 | }
31 |
32 | if f.IsDir() {
33 | return nil
34 | }
35 | if utils.GetFileExt(f.Name()) != RomLinkExt {
36 | return nil
37 | }
38 | romPath, _ := utils.ReadFile(p, false)
39 | result[utils.GetFileName(romPath)] = p
40 | return nil
41 | }); err != nil {
42 | return nil, err
43 |
44 | }
45 | return result, nil
46 | }
47 |
48 | // 读取一个rom的所有链接文件
49 | func ReadRomLinksByRom(LinkPath, romName string) ([]string, error) {
50 | result := []string{}
51 | if err := filepath.Walk(LinkPath,
52 | func(p string, f os.FileInfo, err error) error {
53 |
54 | if f == nil {
55 | return nil
56 | }
57 |
58 | if f.IsDir() {
59 | return nil
60 | }
61 | if utils.GetFileExt(f.Name()) != RomLinkExt {
62 | return nil
63 | }
64 | romPath, _ := utils.ReadFile(p, false)
65 |
66 | fileRomName := utils.GetFileName(romPath)
67 |
68 | if fileRomName == romName {
69 | result = append(result, p)
70 | }
71 |
72 | return nil
73 | }); err != nil {
74 | return nil, err
75 |
76 | }
77 | return result, nil
78 | }
79 |
80 | // 读取rom链接文件
81 | func GetLinkFileData(p string) *Slnk {
82 |
83 | if p == "" {
84 | return &Slnk{}
85 | }
86 |
87 | content, err := utils.ReadFile(p, false)
88 |
89 | arr := strings.Split(content, RomLinkSplit)
90 | romPath := arr[0]
91 | param := []string{}
92 | if len(arr) > 1 {
93 | param = strings.Split(arr[1], " ")
94 | }
95 | if err != nil {
96 | return &Slnk{}
97 | }
98 |
99 | data := &Slnk{
100 | RelRomPath: romPath,
101 | RomName: utils.GetFileName(romPath),
102 | AbsRomPath: utils.ToAbsPath(romPath, ""),
103 | Params: param,
104 | }
105 | return data
106 | }
107 |
108 | // 创建rom链接文件
109 | func CreateLinkFile(p string, f, params string) error {
110 | if utils.FileExists(p) {
111 | return nil
112 | }
113 | content := f
114 | if params != "" {
115 | content = content + RomLinkSplit + params
116 | }
117 |
118 | err := utils.CreateFile(p, content)
119 | if err != nil {
120 | return nil
121 | }
122 | return nil
123 | }
124 |
125 | // 设置rom链接文件
126 | func UpdateLinkFileData(p, romPath, param string) error {
127 |
128 | if p == "" {
129 | return nil
130 | }
131 |
132 | if !utils.FileExists(p) {
133 | return errors.New("链接文件不存在")
134 | }
135 |
136 | content := romPath + RomLinkSplit + param
137 | return utils.OverlayWriteFile(p, content)
138 | }
139 |
--------------------------------------------------------------------------------
/components/lua.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import (
4 | "github.com/Shopify/go-lua"
5 | "simUI/constant"
6 | "simUI/utils"
7 | )
8 |
9 | // 调用Lua代码
10 | func CallLua(luaFile string, simPath string, romPath string) {
11 | go func() {
12 | var luaState *lua.State
13 | luaState = lua.NewState()
14 | lua.OpenLibraries(luaState)
15 |
16 | if !utils.IsAbsPath(luaFile) {
17 | luaFile = constant.ROOT_PATH + luaFile
18 | }
19 |
20 | if err := lua.DoFile(luaState, luaFile); err != nil {
21 | utils.WriteLog("Lua Run Error:" + err.Error())
22 | return
23 | }
24 |
25 | // 调用lua函数
26 | luaState.Global("main")
27 |
28 | // 传递参数给lua函数
29 | luaState.PushString(constant.ROOT_PATH) //simui根目录
30 | luaState.PushString(simPath) //模拟器文件
31 | luaState.PushString(romPath) //rom文件
32 | if err := luaState.ProtectedCall(3, 0, 0); err != nil {
33 | utils.WriteLog("Lua Error:" + err.Error())
34 | return
35 | }
36 | }()
37 | }
38 |
--------------------------------------------------------------------------------
/components/output.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import (
4 | "archive/zip"
5 | "io"
6 | "os"
7 | "simUI/constant"
8 | "simUI/utils"
9 | )
10 |
11 | var zipMethod = 1 //0仅存储;1压缩
12 | var TmpOutputIniPath = "./cache/config.ini" //ini文件路径
13 |
14 | /**
15 | * 将资源文件添加到压缩文件中
16 | **/
17 | func CompressZip(file *os.File, prefix string, zw *zip.Writer) error {
18 | info, err := file.Stat()
19 | if err != nil {
20 | return err
21 | }
22 | if info.IsDir() {
23 | if prefix != "" {
24 | prefix = prefix + "/" + info.Name()
25 | } else {
26 | prefix = info.Name()
27 | }
28 | fileInfos, err := file.Readdir(-1)
29 | if err != nil {
30 | return err
31 | }
32 | for _, fi := range fileInfos {
33 | f, err := os.Open(file.Name() + "/" + fi.Name())
34 | defer f.Close()
35 | if err != nil {
36 | return err
37 | }
38 | err = CompressZip(f, prefix, zw)
39 | if err != nil {
40 | return err
41 | }
42 | }
43 | } else {
44 |
45 | header, err := zip.FileInfoHeader(info)
46 | if err != nil {
47 | return err
48 | }
49 | if prefix != "" {
50 | header.Name = prefix + "/" + header.Name
51 | }
52 | if zipMethod == 1 {
53 | header.Method = zip.Deflate //压缩
54 | } else {
55 | header.Method = zip.Store //仅存储
56 | }
57 |
58 | writer, err := zw.CreateHeader(header)
59 | if err != nil {
60 | return err
61 | }
62 |
63 | _, err = io.Copy(writer, file)
64 | if err != nil {
65 | return err
66 | }
67 | }
68 | return nil
69 | }
70 |
71 | // 检查分享文件
72 | func CheckZip(zipFile string) (bool, error) {
73 | exists := false
74 | reader, err := zip.OpenReader(zipFile)
75 | if err != nil {
76 | return false, err
77 | }
78 | defer reader.Close()
79 | for _, file := range reader.File {
80 | rc, err := file.Open()
81 | if err != nil {
82 | continue
83 | }
84 | defer rc.Close()
85 |
86 | f := utils.GetFileNameAndExt(file.Name)
87 | if f == constant.SHARE_FILE_NAME {
88 | exists = true
89 | break
90 | }
91 | }
92 | return exists, nil
93 | }
94 |
95 | // 分享文件解压
96 | func DecompressZip(zipFile, rootPath, romPath string) error {
97 | reader, err := zip.OpenReader(zipFile)
98 | if err != nil {
99 | return err
100 | }
101 | defer reader.Close()
102 | for _, file := range reader.File {
103 | rc, err := file.Open()
104 | if err != nil {
105 | return err
106 | }
107 | defer rc.Close()
108 |
109 | m := utils.GetFilePath(file.Name)
110 | f := rootPath + "/" + file.Name
111 | r := rootPath
112 | if m == constant.RES_DIR["rom"] {
113 | f = romPath + "/" + file.Name
114 | r = romPath
115 | }
116 | err = os.MkdirAll(r, 0755)
117 | if err != nil {
118 | return err
119 | }
120 | w, err := os.Create(f)
121 | if err != nil {
122 | return err
123 | }
124 | defer w.Close()
125 | _, err = io.Copy(w, rc)
126 | if err != nil {
127 | return err
128 | }
129 | w.Close()
130 | rc.Close()
131 | }
132 | return nil
133 | }
134 |
--------------------------------------------------------------------------------
/components/platform.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import (
4 | "simUI/constant"
5 | "simUI/db"
6 | )
7 |
8 | // 读取全部资源目录
9 | // typ:0全部 1图片 2图片+视频
10 | func GetResPath(platformId uint32, typ int) map[string]string {
11 |
12 | thumb, snap, poster, packing, title, cassette := "", "", "", "", "", ""
13 | icon, gif, background, video := "", "", "", ""
14 | doc, strategy, files, upload := "", "", "", ""
15 |
16 | info := (&db.Platform{}).GetVOById(platformId, false)
17 |
18 | if info != nil {
19 | thumb = info.ThumbPath
20 | snap = info.SnapPath
21 | poster = info.PosterPath
22 | packing = info.PackingPath
23 | title = info.TitlePath
24 | cassette = info.CassettePath
25 | icon = info.IconPath
26 | gif = info.GifPath
27 | background = info.BackgroundPath
28 | video = info.VideoPath
29 | doc = info.DocPath
30 | strategy = info.StrategyPath
31 | //audio = info.AudioPath
32 | files = info.FilesPath
33 | upload = info.UploadPath
34 | }
35 |
36 | res := map[string]string{
37 | "thumb": thumb,
38 | "snap": snap,
39 | "poster": poster,
40 | "packing": packing,
41 | "title": title,
42 | "cassette": cassette,
43 | "icon": icon,
44 | "gif": gif,
45 | "background": background,
46 | }
47 | if typ == 0 || typ == 2 {
48 | res["video"] = video
49 | }
50 | if typ == 0 {
51 | res["doc"] = doc
52 | res["strategy"] = strategy
53 | //res["audio"] = audio
54 | res["file"] = files
55 | res["upload"] = upload
56 | }
57 | return res
58 | }
59 |
60 | // 读取资源类型名
61 | func GetResExts() map[string][]string {
62 | res := map[string][]string{}
63 |
64 | picExt := constant.MEDIA_EXTS
65 | docExt := constant.DOC_EXTS
66 | fileExt := constant.FILE_EXTS
67 | //audioExt := constant.AUDIO_EXTS
68 |
69 | res["thumb"] = picExt
70 | res["snap"] = picExt
71 | res["poster"] = picExt
72 | res["packing"] = picExt
73 | res["title"] = picExt
74 | res["cassette"] = picExt
75 | res["icon"] = picExt
76 | res["gif"] = picExt
77 | res["background"] = picExt
78 | res["video"] = picExt
79 | res["doc"] = docExt
80 | res["strategy"] = docExt
81 | res["files"] = fileExt
82 | //res["audio"] = audioExt
83 | return res
84 | }
85 |
86 | // 根据图片类型 读取 图片路径
87 | func GetResPathByType(typ string, platformId uint32) string {
88 | resPath := GetResPath(platformId, 0)
89 | return resPath[typ]
90 | }
91 |
--------------------------------------------------------------------------------
/components/rom.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "simUI/constant"
7 | "simUI/utils"
8 | "strings"
9 | )
10 |
11 | // 读取游戏主资源
12 | func GetMasterRes(typ string, platform uint32, romName string) string {
13 |
14 | fileName := ""
15 | resName := ""
16 | pth := GetResPathByType(typ, platform)
17 | types := GetResExts()
18 | if pth != "" {
19 | for _, v := range types[typ] {
20 | fileName = pth + "/" + romName + v
21 | if utils.FileExists(fileName) {
22 | resName = fileName
23 | break
24 | }
25 | }
26 | }
27 |
28 | return resName
29 | }
30 |
31 | /**
32 | * 读取游戏子资源
33 | **/
34 | func GetSlaveRes(dir string, masterRomName string) ([]string, error) {
35 |
36 | dir = utils.ToAbsPath(masterRomName, dir)
37 |
38 | files := []string{}
39 | extMap := utils.SliceToMap(constant.MEDIA_EXTS)
40 |
41 | resList, err := utils.ScanCurrentDir(dir)
42 | if err != nil {
43 | return nil, nil
44 | }
45 |
46 | for _, f := range resList {
47 | if f.IsDir() {
48 | // 忽略目录
49 | continue
50 | }
51 |
52 | ext := utils.GetFileExt(f.Name())
53 | if _, ok := extMap[ext]; !ok {
54 | //不是图片,忽略
55 | continue
56 | }
57 |
58 | abs := utils.ToAbsPath(f.Name(), dir)
59 | files = append(files, abs)
60 |
61 | }
62 |
63 | return files, err
64 | }
65 |
66 | // rom封装主展示图
67 | func GetMasterRomThumbs(thumbType string, platform uint32, romNameMap map[string]string) map[string]string {
68 | dir := GetResPathByType(thumbType, platform)
69 | if dir == "" {
70 | return map[string]string{}
71 | }
72 |
73 | thumbsMap := map[string]string{}
74 | picMap := utils.SliceToMap(constant.MEDIA_EXTS)
75 |
76 | filepath.Walk(dir, func(filename string, f os.FileInfo, err error) error { //遍历目录
77 | if err != nil { //忽略错误
78 | return err
79 | }
80 |
81 | if f.IsDir() { // 忽略目录
82 | return nil
83 | }
84 |
85 | fExt := strings.ToLower(utils.GetFileExt(f.Name()))
86 | if _, ok := picMap[fExt]; !ok {
87 | return nil
88 | }
89 |
90 | fName := utils.GetFileName(f.Name())
91 |
92 | if _, ok := romNameMap[fName]; !ok {
93 | return nil
94 | }
95 |
96 | if _, ok := thumbsMap[fName]; ok {
97 | return nil
98 | }
99 |
100 | thumbsMap[fName] = filename
101 |
102 | return nil
103 | })
104 |
105 | return thumbsMap
106 | }
107 |
108 | /**
109 | * 读取游戏文件资源
110 | **/
111 | func GetFileRes(dir string, romName string) ([]string, error) {
112 |
113 | dir = utils.ToAbsPath(romName, dir)
114 |
115 | files := []string{}
116 |
117 | resList, err := utils.ScanCurrentDir(dir)
118 | if err != nil {
119 | return nil, nil
120 | }
121 |
122 | for _, f := range resList {
123 | if f.IsDir() {
124 | // 忽略目录
125 | continue
126 | }
127 |
128 | abs := utils.ToAbsPath(f.Name(), dir)
129 | files = append(files, abs)
130 | }
131 |
132 | return files, err
133 | }
134 |
--------------------------------------------------------------------------------
/components/upgrade.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import (
4 | "simUI/constant"
5 | "simUI/utils"
6 | )
7 |
8 | // 创建升级配置文件
9 | func UpgradeCreateBat() (string, error) {
10 | content := `@echo off
11 | cd %~dp0
12 | IF "%~1"=="" (
13 | echo param error.
14 | exit
15 | )
16 | set "currDir=%~dp0"
17 | set "zipDir=%~1"
18 | IF NOT EXIST "%zipDir%" (
19 | echo unzip dir is not exists.
20 | exit
21 | )
22 |
23 | echo start upgrade...
24 | ping 127.0.0.1 -n 5 > nul
25 | XCOPY /S /y "%zipDir%*" "%currDir%"
26 | start "" "%currDir%simui-pulsar.exe"
27 | RMDIR /s /q "%zipDir%"
28 | DEL "%~f0"
29 | `
30 | p := constant.ROOT_PATH + "upgrade.bat"
31 | if err := utils.CreateFile(p, content); err != nil {
32 | return "", err
33 | }
34 | return p, nil
35 | }
36 |
--------------------------------------------------------------------------------
/components/zip.go:
--------------------------------------------------------------------------------
1 | package components
2 |
3 | import (
4 | "archive/zip"
5 | "github.com/axgle/mahonia"
6 | "io"
7 | "os"
8 | "path/filepath"
9 | "simUI/constant"
10 | "simUI/utils"
11 | "strings"
12 | )
13 |
14 | /*
15 | zip解压
16 | */
17 |
18 | func UnzipRom(zipFile string) (string, error) {
19 |
20 | if strings.ToLower(filepath.Ext(zipFile)) != ".zip" {
21 | return "", nil
22 | }
23 |
24 | zipReader, err := zip.OpenReader(zipFile)
25 | if err != nil {
26 | return "", err
27 | }
28 | defer zipReader.Close()
29 |
30 | //拼接解压路径
31 | zipfileName := utils.GetFileName(zipFile)
32 | fpath := constant.CACHE_UNZIP_PATH + zipfileName + "/"
33 |
34 | if !utils.IsDirEmpty(fpath) {
35 | return fpath, nil
36 | }
37 |
38 | if !utils.DirExists(fpath) {
39 | if err = utils.CreateDir(fpath); err != nil {
40 | }
41 | }
42 |
43 | for _, f := range zipReader.File {
44 | //解决中文文件名乱码问题
45 | enc := mahonia.NewDecoder("gbk")
46 | f.Name = enc.ConvertString(f.Name)
47 |
48 | //开始解压
49 | if f.FileInfo().IsDir() {
50 | if err = utils.CreateDir(fpath + f.Name); err != nil {
51 | return "", err
52 | }
53 | } else {
54 | srcFile, err := f.Open()
55 | if err != nil {
56 | return "", err
57 | }
58 | defer srcFile.Close()
59 |
60 | dstPath := fpath + f.Name
61 | dstFile, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
62 | if err != nil {
63 | return "", err
64 | }
65 | defer dstFile.Close()
66 |
67 | _, err = io.Copy(dstFile, srcFile)
68 | if err != nil {
69 | return "", err
70 | }
71 |
72 | }
73 | }
74 | return fpath, nil
75 | }
76 |
77 | /*
78 | 清理解压缓存
79 | */
80 | func ClearZipRom() error {
81 | err := os.RemoveAll(constant.CACHE_UNZIP_PATH)
82 | if err != nil {
83 | return err
84 | }
85 | return nil
86 | }
87 |
--------------------------------------------------------------------------------
/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "github.com/go-ini/ini"
7 | "os"
8 | "simUI/constant"
9 | "simUI/db"
10 | "simUI/utils"
11 | )
12 |
13 | // 配置文件
14 | var (
15 | Cfg *ConfStruct //公共配置
16 | Ctx context.Context
17 | )
18 |
19 | // 配置文件
20 | type ConfStruct struct {
21 | LangList []string //语言列表
22 | Lang map[string]string //语言项
23 | }
24 |
25 | /*
26 | 初始化读取配置
27 | @author frontLon
28 | */
29 | func InitConf() error {
30 |
31 | err := errors.New("")
32 |
33 | //config配置
34 | configData := (&db.Config{}).GetConfig(true)
35 |
36 | //语言列表
37 | Cfg.LangList, err = getLangList()
38 | if err != nil {
39 | return err
40 | }
41 | //语言配置定义
42 | Cfg.Lang, err = getLang(configData.Lang)
43 | if err != nil {
44 | return err
45 | }
46 | return nil
47 | }
48 |
49 | // 读取语言参数配置
50 | func getLang(lang string) (map[string]string, error) {
51 | langpath := constant.LANG_PATH
52 | fpath := langpath + lang + ".ini"
53 | section := make(map[string]string)
54 |
55 | //如果默认语言不存在,则读取列表中的其他语言
56 | if !utils.FileExists(fpath) {
57 | if len(Cfg.LangList) > 0 {
58 | for langName, langFile := range Cfg.LangList {
59 | fpath = langpath + langFile
60 | //如果找到其他语言,则将第一项更新到数据库配置中
61 | if err := (&db.Config{}).UpdateOne("lang", langName); err != nil {
62 | return section, err
63 | }
64 | break
65 | }
66 | }
67 | }
68 |
69 | file, err := ini.LoadSources(ini.LoadOptions{IgnoreInlineComment: true}, fpath)
70 |
71 | if err != nil {
72 | return section, err
73 | }
74 |
75 | section = file.Section("").KeysHash()
76 | return section, nil
77 | }
78 |
79 | // 读取语言文件列表
80 | func getLangList() ([]string, error) {
81 | list := []string{}
82 | lists, _ := os.ReadDir(constant.LANG_PATH)
83 | for _, fi := range lists {
84 | if !fi.IsDir() { // 忽略目录
85 | list = append(list, utils.GetFileName(fi.Name()))
86 | }
87 | }
88 | return list, nil
89 | }
90 |
--------------------------------------------------------------------------------
/config/share.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "encoding/json"
5 | "simUI/utils"
6 | )
7 |
8 | type Share struct {
9 | RomFile string //master.zip
10 | SubGame []string //sub.zip
11 | Rombase *RomBase
12 | }
13 |
14 | // 读取分享配置
15 | func GetShareData(filePath string) (map[string]*Share, error) {
16 |
17 | share := map[string]*Share{}
18 |
19 | if !utils.FileExists(filePath) {
20 | return share, nil
21 | }
22 | content, _ := utils.ReadFile(filePath, false)
23 | if content != "" {
24 | json.Unmarshal([]byte(content), &share)
25 | }
26 | return share, nil
27 | }
28 |
29 | // 覆写分享配置
30 | func WriteDataToShareFile(filePath string, data map[string]*Share) error {
31 | content, _ := json.Marshal(data)
32 | utils.OverlayWriteFile(filePath, string(content))
33 | return nil
34 | }
35 |
--------------------------------------------------------------------------------
/config/subgame.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "encoding/json"
5 | "simUI/db"
6 | "simUI/utils"
7 | )
8 |
9 | // 子游戏文件名 => 主游戏文件名
10 | var SubGameList = map[uint32]map[string]string{}
11 |
12 | // 读取子游戏配置
13 | func GetSubGame(platform uint32, build bool) (map[string]string, error) {
14 |
15 | if len(SubGameList) == 0 {
16 | SubGameList = map[uint32]map[string]string{}
17 | }
18 |
19 | //如果已经读取,则直接返回
20 | if len(SubGameList[platform]) != 0 && build == false {
21 | return SubGameList[platform], nil
22 | }
23 |
24 | platformInfo := (&db.Platform{}).GetVOById(platform, false)
25 | section := map[string]string{}
26 |
27 | if !utils.FileExists(platformInfo.SubGameFile) {
28 | return section, nil
29 | }
30 | content, _ := utils.ReadFile(platformInfo.SubGameFile, false)
31 | if content != "" {
32 | json.Unmarshal([]byte(content), §ion)
33 | SubGameList[platform] = section
34 | }
35 | return section, nil
36 | }
37 |
38 | // 根据主游戏,读取所属的子游戏
39 | func GetSubGameByParent(platform uint32, romName string) ([]string, error) {
40 |
41 | //如果已经读取,则直接返回
42 | datas, _ := GetSubGame(platform, false)
43 |
44 | result := []string{}
45 | for k, v := range datas {
46 | if v == romName {
47 | result = append(result, k)
48 | }
49 | }
50 | return result, nil
51 | }
52 |
53 | // 设置子游戏
54 | func SetSubGame(platform uint32, slave, master string) error {
55 | if len(SubGameList) == 0 {
56 | SubGameList = map[uint32]map[string]string{}
57 | }
58 | if len(SubGameList[platform]) == 0 {
59 | SubGameList[platform] = map[string]string{}
60 | }
61 | SubGameList[platform][slave] = master
62 |
63 | platformInfo := (&db.Platform{}).GetVOById(platform, false)
64 |
65 | return WriteDataToSubGameFile(platformInfo.SubGameFile, SubGameList[platform])
66 | }
67 |
68 | // 删除子游戏
69 | func DelSubGame(platform uint32, slave string) error {
70 | if len(SubGameList) == 0 {
71 | SubGameList = map[uint32]map[string]string{}
72 | }
73 | if len(SubGameList[platform]) == 0 {
74 | SubGameList[platform] = map[string]string{}
75 | }
76 |
77 | if _, ok := SubGameList[platform][slave]; !ok {
78 | return nil
79 | }
80 |
81 | delete(SubGameList[platform], slave)
82 |
83 | platformInfo := (&db.Platform{}).GetVOById(platform, false)
84 |
85 | return WriteDataToSubGameFile(platformInfo.SubGameFile, SubGameList[platform])
86 | }
87 |
88 | // 覆写子游戏配置
89 | func WriteDataToSubGameFile(filePath string, data map[string]string) error {
90 | content, _ := json.Marshal(data)
91 | utils.OverlayWriteFile(filePath, string(content))
92 | return nil
93 | }
94 |
--------------------------------------------------------------------------------
/constant/args.go:
--------------------------------------------------------------------------------
1 | package constant
2 |
3 | /*type CmdArgs struct {
4 | Db string
5 | Dev bool
6 | }*/
7 |
8 | //var ARGS CmdArgs
9 |
--------------------------------------------------------------------------------
/constant/const.go:
--------------------------------------------------------------------------------
1 | package constant
2 |
3 | // 是否为开发环境
4 | var DEV = false
5 |
6 | var DB_ADD_MAX_NUM = 999 //数据库每次查询/写入的最大数量
7 |
8 | var VERSION_NO = "" //当前软件版本号
9 | var BUILD_TIME = "" //软件编译时间
10 |
11 | // doc文档支持的扩展名
12 | var DOC_EXTS = []string{".txt", ".html", ".htm", ".md"}
13 |
14 | // 支持的图片类型
15 | var MEDIA_EXTS = []string{".png", ".jpg", ".gif", ".webp", ".jpeg", ".ico", ".mp4", ".webm", "avif"}
16 |
17 | // 支持的音频类型
18 | var AUDIO_EXTS = []string{".mp3", ".dmi", ".wav", ".wma"}
19 |
20 | // 可直接运行的doc文档支持的扩展名
21 | var FILE_EXTS = []string{
22 | ".html", ".htm", ".mht", ".mhtml", ".url",
23 | ".chm", ".pdf", ".doc", ".docx", ".ppt", ".pptx", ".xls", ".xlsx", ".rtf",
24 | ".exe", ".com", ".cmd", ".bat", ".lnk",
25 | }
26 |
27 | // 可直接运行的扩展名
28 | var RUN_EXTS = []string{".exe", ".cmd", ".bat", ".lnk"}
29 |
30 | // 可直接通过explorer运行的扩展名
31 | var EXPLORER_EXTS = []string{".lnk"}
32 |
33 | // 支持的字体类型
34 | var FONT_EXTS = map[string]string{
35 | ".woff": "woff",
36 | ".woff2": "woff2",
37 | ".ttf": "truetype",
38 | ".otf": "opentype",
39 | ".eot": "embedded-opentype",
40 | ".svg": "svg",
41 | ".svgz": "svg",
42 | }
43 |
44 | /*EXPLORER_EXTS = []string{
45 | ".lnk", ".html", ".htm", ".mht", ".mhtml", ".url",
46 | ".chm", ".doc", ".docx", ".ppt", ".pptx", ".xls", ".xlsx", ".rtf",
47 | } //通过explorer运行的扩展名*/
48 |
49 | // 主题标识配置
50 | var UI_DEFAULT = "Default"
51 | var UI_PLAYNITE = "Playnite"
52 | var UI_TINY = "Tiny"
53 |
54 | // 平台资源文件夹定义
55 | var RES_DIR = map[string]string{
56 | "rom": "roms",
57 | "thumb": "thumbs",
58 | "snap": "snaps",
59 | "poster": "poster",
60 | "packing": "packing",
61 | "title": "title",
62 | "cassette": "cassette",
63 | "icon": "icon",
64 | "gif": "gif",
65 | "background": "background",
66 | "video": "video",
67 | "doc": "docs",
68 | "strategy": "strategies",
69 | "audio": "audio",
70 | "file": "files",
71 | "upload": "uploads",
72 | "link": "links",
73 | }
74 |
75 | // 默认展示图排序
76 | var DefaultThumbOrders = []string{
77 | "thumb", "snap", "poster", "packing", "title", "cassette", "icon", "gif", "background", "video",
78 | }
79 |
80 | // 默认过滤器
81 | var DefaultListColumns = []string{
82 | "BaseNameEn", "BaseNameJp", "BaseType", "BaseYear", "BasePublisher", "BaseProducer",
83 | "BaseCountry", "BaseTranslate", "BaseVersion", "Score", "Complete", "Menu",
84 | }
85 |
--------------------------------------------------------------------------------
/constant/path.go:
--------------------------------------------------------------------------------
1 | package constant
2 |
3 | // 项目目录
4 | var ROOT_PATH = "" //软件根目录
5 | var CACHE_PATH = "" //缓存目录
6 | var CACHE_UNZIP_PATH = "" //资源解压目录
7 | var CACHE_THUMB_PATH = "" //展示图备份目录
8 | var CACHE_UNOWNED_PATH = "" //无效资源备份目录
9 | var CACHE_REPEAT_PATH = "" //重复ROM备份目录
10 | var LANG_PATH = "" //语言目录
11 | var RESOURCE_PATH = "" //软件资源目录
12 | var FONT_PATH = "" //字体目录
13 |
14 | var ROMBASE_FILE_NAME = "rombase" //平台资料文件名
15 | var SETTING_FILE_NAME = "romsetting.csv" //平台rom配置文件名
16 | var SUBGAME_FILE_NAME = "subgame.json" //子游戏配置文件
17 | var SHARE_FILE_NAME = "share.json" //分享配置
18 | var SHARE_ZIP_EXT = ".shr" //分享文件扩展名
19 |
--------------------------------------------------------------------------------
/controller/audio.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | // 读取游戏音频
4 | //func (a *Controller) GetGameAudios(id uint64) string {
5 | // return Resp(modules.GetGameAudios(id))
6 | //}
7 |
8 | // 编辑游戏音频
9 | //func (a *Controller) UpdateGameAudio(lists []string) string {
10 | // return Resp(modules.UpdateGameAudio(lists))
11 | //}
12 |
13 | /*// 添加游戏音频
14 | func (a *Controller) AddGameAudio(id uint64, lists []string) string {
15 | return Resp(modules.AddGameAudio(id, lists))
16 | }*/
17 |
18 | /*// 改名游戏音频
19 | func (a *Controller) RenameGameAudio(pth string, newName string) string {
20 | return Resp(modules.RenameGameAudio(pth, newName))
21 | }
22 |
23 | // 删除游戏音频
24 | func (a *Controller) DelGameAudio(pth string) string {
25 | return Resp("", modules.DelGameAudio(pth))
26 | }*/
27 |
28 | /*
29 | func AudioController() {
30 |
31 | //读取音频文件列表
32 | utils.Window.DefineFunction("GetAudio", func(args ...*sciter.Value) *sciter.Value {
33 | id := uint64(utils.ToInt(args[0].String()))
34 | volist, err := modules.GetAudioList(id)
35 | if err != nil {
36 | utils.WriteLog(err.Error())
37 | return utils.ErrorMsg(err.Error())
38 | }
39 | jsonInfo, _ := json.Marshal(volist)
40 | return sciter.NewValue(string(jsonInfo))
41 | })
42 |
43 | //上传文件
44 | utils.Window.DefineFunction("UploadAudioFile", func(args ...*sciter.Value) *sciter.Value {
45 | id := uint64(utils.ToInt(args[0].String()))
46 | name := args[1].String()
47 | p := args[2].String()
48 |
49 | relPath, err := modules.UploadAudioFile(id, name, p)
50 | if err != nil {
51 | utils.WriteLog(err.Error())
52 | return utils.ErrorMsg(err.Error())
53 | }
54 |
55 | return sciter.NewValue(relPath)
56 | })
57 |
58 | //更新配置
59 | utils.Window.DefineFunction("UpdateAudio", func(args ...*sciter.Value) *sciter.Value {
60 | id := uint64(utils.ToInt(args[0].String()))
61 | data := args[1].String()
62 | if err := modules.UpdateAudio(id, data); err != nil {
63 | utils.WriteLog(err.Error())
64 | return utils.ErrorMsg(err.Error())
65 | }
66 | return sciter.NullValue()
67 | })
68 |
69 | //播放音频文件
70 | utils.Window.DefineFunction("PlayAudio", func(args ...*sciter.Value) *sciter.Value {
71 | urls := []string{}
72 | json.Unmarshal([]byte(args[0].String()), &urls)
73 |
74 | for k, v := range urls {
75 | urls[k] = utils.ToAbsPath(v)
76 | }
77 |
78 | if err := components.PlayAudio(urls); err != nil {
79 | utils.WriteLog(err.Error())
80 | return utils.ErrorMsg(err.Error())
81 | }
82 |
83 | return sciter.NullValue()
84 | })
85 |
86 | }*/
87 |
--------------------------------------------------------------------------------
/controller/cache.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import "simUI/modules"
4 |
5 | // 更新缓存
6 | func (a *Controller) CreateRomCache(platform uint32) string {
7 | return Resp(modules.CreateRomCache(platform))
8 | }
9 |
10 | // 清理游戏统计信息
11 | func (a *Controller) ClearGameStat() string {
12 | return Resp("", modules.ClearGameStat())
13 | }
14 |
15 | // 清理游戏资料
16 | func (a *Controller) ClearNotExistGameConfig() string {
17 | return Resp("", modules.ClearNotExistGameConfig())
18 | }
19 |
--------------------------------------------------------------------------------
/controller/config.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "encoding/json"
5 | "simUI/db"
6 | "simUI/modules"
7 | )
8 |
9 | // 读取配置
10 | func (a *Controller) GetConfig() string {
11 | return Resp(modules.GetConfig())
12 | }
13 |
14 | // 读取全部平台 - 平台原信息
15 | func (a *Controller) GetBaseConfig() string {
16 | return Resp(modules.GetBaseConfig())
17 | }
18 |
19 | // 更新一条配置
20 | func (a *Controller) UpdateOneConfig(key, data string) string {
21 | return Resp("", modules.UpdateOneConfig(key, data))
22 | }
23 |
24 | // 更新基本配置
25 | func (a *Controller) UpdateBaseConfig(data string) string {
26 | d := db.ConfigVO{}
27 | json.Unmarshal([]byte(data), &d)
28 | return Resp("", modules.UpdateBaseConfig(d))
29 | }
30 |
31 | // 更新颜色配置
32 | func (a *Controller) UpdateColorsConfig(data string) string {
33 | return Resp("", modules.UpdateOneConfig("Colors", data))
34 | }
35 |
36 | // 读取当前主题
37 | func (a *Controller) GetTheme() string {
38 | return Resp(modules.GetTheme())
39 | }
40 |
41 | // 设置当前主题
42 | func (a *Controller) SetTheme(theme string) string {
43 | return Resp("", modules.SetTheme(theme))
44 | }
45 |
46 | // 更新展示图排序
47 | func (a *Controller) UpdateThumbsOrders(orders []string) string {
48 | return Resp("", modules.UpdateThumbsOrders(orders))
49 | }
50 |
51 | // 读取字体列表
52 | func (a *Controller) GetFontList() string {
53 | return Resp(modules.GetFontList())
54 | }
55 |
--------------------------------------------------------------------------------
/controller/controller.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "encoding/json"
5 | )
6 |
7 | type Controller struct {
8 | }
9 |
10 | // NewApp creates a new App application struct
11 | func NewController() *Controller {
12 | return &Controller{}
13 | }
14 |
15 | type resp struct {
16 | Data any `json:"data"`
17 | Err string `json:"err"`
18 | }
19 |
20 | func Resp(data any, err error) string {
21 | errMsg := ""
22 | if err != nil {
23 | errMsg = err.Error()
24 | }
25 | result := resp{
26 | Data: data,
27 | Err: errMsg,
28 | }
29 | //fmt.Println("api resp:", data, err)
30 | js, _ := json.Marshal(result)
31 | return string(js)
32 | }
33 |
--------------------------------------------------------------------------------
/controller/dialog.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "simUI/modules"
5 | )
6 |
7 | // 弹出文件选择窗口
8 | func (a *Controller) OpenFileDialog(typ string) string {
9 | return Resp(modules.OpenFileDialog(typ))
10 | }
11 |
12 | // 弹出多文件选择窗口
13 | func (a *Controller) OpenMultiFileDialog(typ string) string {
14 | return Resp(modules.OpenMultiFileDialog(typ))
15 | }
16 |
17 | // 弹出目录选择窗口
18 | func (a *Controller) OpenDirectoryDialog() string {
19 | return Resp(modules.OpenDirectoryDialog())
20 | }
21 |
22 | // 保存文件对话框
23 | func (a *Controller) SaveFileDialog(filename string) string {
24 | return Resp(modules.SaveFileDialog(filename))
25 | }
26 |
27 | // 编辑器图片选择框
28 | func (a *Controller) OpenFileDialogForEditor() string {
29 | return Resp(modules.OpenFileDialogForEditor())
30 | }
31 |
--------------------------------------------------------------------------------
/controller/image.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "simUI/modules"
5 | )
6 |
7 | // 读取游戏图集
8 | func (a *Controller) GetGameThumbs(id uint64, typ string) string {
9 | return Resp(modules.GetGameThumbs(id, typ))
10 | }
11 |
12 | // 添加展示图
13 | func (a *Controller) AddGameThumb(romId uint64, typ string, imageList []string) string {
14 | return Resp(modules.AddThumb(romId, typ, imageList))
15 | }
16 |
17 | // 删除展示图
18 | func (a *Controller) DelGameThumb(romId uint64, typ string, master uint8, imgPath string) string {
19 | return Resp("", modules.DeleteThumb(romId, typ, master, imgPath))
20 | }
21 |
22 | // 图集排序
23 | func (a *Controller) SortGameThumb(romId uint64, typ string, imgList []string) string {
24 | return Resp(modules.SortGameThumb(romId, typ, imgList))
25 | }
26 |
27 | // 读取网络图片
28 | func (a *Controller) LoadWebThumbs(engine string, keyword string, page int) string {
29 | return Resp(modules.LoadWebThumbs(engine, keyword, page))
30 | }
31 |
32 | // 下载网络图片
33 | func (a *Controller) DownloadThumb(romId uint64, typ string, master uint8, httpUrl string, ext string) string {
34 | return Resp(modules.DownloadThumb(romId, typ, master, httpUrl, ext))
35 | }
36 |
37 | // base64转图片
38 | func (a *Controller) CreateRomResByBase64(id uint64, resType string, slaveRes uint8, fileType, base64Str string) string {
39 | return Resp(modules.CreateRomResByBase64(id, resType, slaveRes, fileType, base64Str))
40 | }
41 |
--------------------------------------------------------------------------------
/controller/input.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "errors"
5 | "simUI/modules"
6 | )
7 |
8 | // 导出一个rom
9 | func (a *Controller) OutputOneRom(id uint64, dir string, outputSubRom, outputRes, outputConf bool) string {
10 | return Resp("", modules.OutputOneRom(id, dir, outputSubRom, outputRes, outputConf))
11 | }
12 |
13 | // 导出一个平台rom
14 | func (a *Controller) OutputRomByPlatform(id uint32, dir string, outputSim bool) string {
15 | return Resp("", modules.OutputRomByPlatform(id, dir, outputSim))
16 | }
17 |
18 | // 添加游戏
19 | func (a *Controller) AddGame(opt string, platform uint32, menu string, romPath string, files []string, bootParam string, isBatFile uint8) string {
20 |
21 | if len(files) == 0 {
22 | return Resp("", errors.New("files not be empty"))
23 | }
24 |
25 | var err error
26 |
27 | switch opt {
28 | case "rom":
29 | err = modules.AddFileGame(platform, menu, romPath, files)
30 | case "pc":
31 | err = modules.AddPcOrFolderGame(platform, menu, romPath, files[0], bootParam, isBatFile)
32 | case "ps3":
33 | err = modules.AddPcOrFolderGame(platform, menu, romPath, files[0], "", 0)
34 | case "share":
35 | err = modules.InputOneShare(platform, menu, romPath, files[0])
36 | }
37 |
38 | return Resp("", err)
39 | }
40 |
41 | // 导出一张图片
42 | func (a *Controller) OutputOneImage(src, dst string) string {
43 | return Resp("", modules.OutputOneImage(src, dst))
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/controller/menu.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "simUI/modules"
5 | )
6 |
7 | // 读取平台下的所有目录
8 | func (a *Controller) GetMenuList(platform uint32) string {
9 | return Resp(modules.GetMenuList(platform))
10 | }
11 |
12 | // 添加目录
13 | func (a *Controller) AddMenu(platform uint32, path, name string) string {
14 | return Resp("", modules.AddMenu(platform, path, name))
15 | }
16 |
17 | // 编辑目录
18 | func (a *Controller) RenameMenu(platform uint32, path, newName string) string {
19 | return Resp("", modules.RenameMenu(platform, path, newName))
20 | }
21 |
22 | // 删除目录
23 | func (a *Controller) DeleteMenu(platform uint32, path string) string {
24 | return Resp("", modules.DeleteMenu(platform, path))
25 | }
26 |
27 | // 排序目录
28 | func (a *Controller) SortMenu(platform uint32, paths []string) string {
29 | return Resp("", modules.SortMenu(platform, paths))
30 | }
31 |
--------------------------------------------------------------------------------
/controller/others.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import "simUI/modules"
4 |
5 | // 启动上传服务
6 | func (a *Controller) StartUploadServer() string {
7 | return Resp(modules.StartUploadServer(), nil)
8 | }
9 |
--------------------------------------------------------------------------------
/controller/platform.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "encoding/json"
5 | "simUI/modules"
6 | "simUI/request"
7 | )
8 |
9 | // 读取全部平台 - VO数据
10 | func (a *Controller) GetPlatform() string {
11 | return Resp(modules.GetAllPlatform(), nil)
12 | }
13 |
14 | // 读取全部平台 - 平台原信息
15 | func (a *Controller) GetPlatformOriginal() string {
16 | return Resp(modules.GetPlatformOriginal())
17 | }
18 |
19 | // 读取一个平台信息
20 | func (a *Controller) GetPlatformById(id uint32) string {
21 | return Resp(modules.GetPlatformById(id))
22 | }
23 |
24 | // 读取平台标签
25 | func (a *Controller) GetAllPlatformTag() string {
26 | return Resp(modules.GetAllPlatformTag())
27 | }
28 |
29 | // 读取平台UI配置
30 | func (a *Controller) GetPlatformUi(id uint32, theme string) string {
31 | return Resp(modules.GetPlatformUi(id, theme))
32 | }
33 |
34 | // 添加一个平台
35 | func (a *Controller) AddPlatform(name string) string {
36 | return Resp(modules.AddPlatform(name))
37 | }
38 |
39 | // 编辑平台信息
40 | func (a *Controller) UpdatePlatform(data string) string {
41 | d := request.UpdatePlatform{}
42 | json.Unmarshal([]byte(data), &d)
43 | return Resp(modules.UpdatePlatform(d))
44 | }
45 |
46 | // 编辑平台简介
47 | func (a *Controller) UpdatePlatformDesc(id uint32, desc string) string {
48 | return Resp("", modules.UpdatePlatformDesc(id, desc))
49 | }
50 |
51 | // 更新平台排序
52 | func (a *Controller) UpdatePlatformSort(data string) string {
53 | d := []uint32{}
54 | json.Unmarshal([]byte(data), &d)
55 | return Resp("", modules.UpdatePlatformSort(d))
56 | }
57 |
58 | // 删除一个平台
59 | func (a *Controller) DelPlatform(id uint32) string {
60 | return Resp("", modules.DelPlatform(id))
61 | }
62 |
63 | // 更新平台UI设置
64 | func (a *Controller) UpdatePlatformUi(id uint32, theme string, data string) string {
65 | return Resp("", modules.UpdatePlatformUi(id, theme, data))
66 | }
67 |
68 | // 在资源管理器中打开目录
69 | func (a *Controller) OpenFolder(opt string, id uint64) string {
70 | return Resp("", modules.OpenFolder(opt, id))
71 | }
72 |
73 | // 在资源管理器中打开目录 - 直接访问文件
74 | func (a *Controller) OpenFolderByPath(pth string) string {
75 | return Resp("", modules.OpenFolderByPath(pth))
76 | }
77 |
--------------------------------------------------------------------------------
/controller/romManage.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "simUI/modules"
5 | )
6 |
7 | // rom文件重命名
8 | func (a *Controller) RenameRomFile(id uint64, name string) string {
9 | return Resp("", modules.RenameRomFile(id, name))
10 | }
11 |
12 | // 删除rom和资源
13 | func (a *Controller) DeleteRom(id uint64, delSubRom uint8, delRes uint8) string {
14 | return Resp("", modules.DeleteRomAndRes(id, delSubRom, delRes))
15 | }
16 |
17 | // 修改游戏别名
18 | func (a *Controller) RenameRomLink(id uint64, name string) string {
19 | return Resp("", modules.RenameRomLink(id, name))
20 | }
21 |
22 | // 复制链接文件
23 | func (a *Controller) CopyRomLink(id uint64, menu string) string {
24 | return Resp(modules.CopyRomLink(id, menu))
25 | }
26 |
27 | // 移动链接文件
28 | func (a *Controller) MoveRomLink(id uint64, menu string) string {
29 | return Resp("", modules.MoveRomLink(id, menu))
30 | }
31 |
32 | // 删除rom链接
33 | func (a *Controller) DeleteRomLink(id uint64) string {
34 | return Resp("", modules.DeleteRomLink(id))
35 | }
36 |
37 | // 绑定子游戏
38 | func (a *Controller) BindSubGame(pid, sid uint64) string {
39 | return Resp("", modules.BindSubGame(pid, sid))
40 | }
41 |
42 | // 解绑子游戏
43 | func (a *Controller) UnBindSubGame(id uint64) string {
44 | return Resp("", modules.UnBindSubGame(id))
45 | }
46 |
47 | // 批量复制链接文件
48 | func (a *Controller) BatchCopyRomLink(ids []uint64, menu string) string {
49 | return Resp("", modules.BatchCopyRomLink(ids, menu))
50 | }
51 |
52 | // 批量移动链接文件
53 | func (a *Controller) BatchMoveRomLink(ids []uint64, menu string) string {
54 | return Resp("", modules.BatchMoveRomLink(ids, menu))
55 | }
56 |
57 | // 批量删除rom链接
58 | func (a *Controller) BatchDeleteRomLink(ids []uint64) string {
59 | return Resp("", modules.BatchDeleteRomLink(ids))
60 | }
61 |
62 | // 批量删除rom和资源
63 | func (a *Controller) BatchDeleteRom(ids []uint64, delSubRom, delRes uint8) string {
64 | return Resp("", modules.BatchDeleteRomAndRes(ids, delSubRom, delRes))
65 | }
66 |
67 | // 查找无效资源
68 | func (a *Controller) CheckUnownedRes(platform uint32) string {
69 | return Resp(modules.CheckUnownedRes(platform))
70 | }
71 |
72 | // 删除无效资源文件
73 | func (a *Controller) DeleteUnownedFile(platform uint32, ids []string) string {
74 | return Resp("", modules.DeleteUnownedFile(platform, ids))
75 | }
76 |
77 | // 打开备份文件夹
78 | func (a *Controller) OpenCacheFolder(typ string, create int) string {
79 | return Resp("", modules.OpenCacheFolder(typ, create))
80 | }
81 |
82 | // 检查重复rom
83 | func (a *Controller) CheckRomRepeat(platform uint32) string {
84 | return Resp(modules.CheckRomRepeat(platform))
85 | }
86 |
87 | // 删除重复ROM文件
88 | func (a *Controller) DeleteRepeatFile(ids []string) string {
89 | return Resp("", modules.DeleteRepeatFile(ids))
90 | }
91 |
--------------------------------------------------------------------------------
/controller/rombase.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "encoding/json"
5 | "simUI/db"
6 | "simUI/modules"
7 | )
8 |
9 | // 读取全部枚举
10 | func (a *Controller) GetRomBaseEnum() string {
11 | return Resp(modules.GetRomBaseEnum())
12 | }
13 |
14 | // 根据类型读取资料枚举
15 | func (a *Controller) GetRomBaseEnumByType(typ string) string {
16 | return Resp(modules.GetRomBaseEnumByType(typ))
17 | }
18 |
19 | // 更新资料枚举
20 | func (a *Controller) UpdateRomBaseEnumByType(typ string, data string) string {
21 | d := []string{}
22 | _ = json.Unmarshal([]byte(data), &d)
23 | return Resp("", modules.UpdateRomBaseEnum(typ, d))
24 | }
25 |
26 | // 读取资料项别名
27 | func (a *Controller) GetRomBaseAlias(platform uint32) string {
28 | return Resp(modules.GetRomBaseAliasByPlatform(platform))
29 | }
30 |
31 | // 更新资料项别名
32 | func (a *Controller) UpdateRomBaseAlias(platform uint32, data string) string {
33 | d := map[string]string{}
34 | _ = json.Unmarshal([]byte(data), &d)
35 | return Resp("", modules.UpdateRomBaseAlias(platform, d))
36 | }
37 |
38 | // 读取一条rombase信息
39 | func (a *Controller) GetRomBase(id uint64) string {
40 | return Resp(modules.GetRomBase(id))
41 | }
42 |
43 | // 编辑rombase信息
44 | func (a *Controller) SetRomBase(id uint64, data, name string) string {
45 | d := map[string]string{}
46 | _ = json.Unmarshal([]byte(data), &d)
47 | return Resp(modules.SetRomBase(id, d, name))
48 | }
49 |
50 | // 批量rombase信息
51 | func (a *Controller) BatchSetRomBase(platform uint32, data string) string {
52 | d := map[string]*db.RomSimpleVO{}
53 | _ = json.Unmarshal([]byte(data), &d)
54 | return Resp("", modules.BatchSetRomBase(platform, d))
55 | }
56 |
--------------------------------------------------------------------------------
/controller/shortcut.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "encoding/json"
5 | "simUI/db"
6 | "simUI/modules"
7 | )
8 |
9 | // 读取快捷工具
10 | func (a *Controller) GetShortcuts(isAbs bool) string {
11 | return Resp(modules.GetShortcuts(isAbs))
12 | }
13 |
14 | // 更新快捷工具
15 | func (a *Controller) UpdateShortcut(data string) string {
16 | d := []*db.Shortcut{}
17 | json.Unmarshal([]byte(data), &d)
18 | return Resp(modules.UpdateShortcut(d))
19 | }
20 |
21 | // 更新快捷方式排序
22 | func (a *Controller) UpdateShortcutSort(data string) string {
23 | d := []uint32{}
24 | json.Unmarshal([]byte(data), &d)
25 | return Resp("", modules.UpdateShortcutSort(d))
26 | }
27 |
--------------------------------------------------------------------------------
/controller/simulator.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "encoding/json"
5 | "simUI/db"
6 | "simUI/modules"
7 | )
8 |
9 | // 读取所有模拟器
10 | func (a *Controller) GetAllSimulator() string {
11 | return Resp(modules.GetAllSimulator())
12 | }
13 |
14 | // 读取一个平台下的所有模拟器
15 | func (a *Controller) GetSimulatorByPlatform(id uint32) string {
16 | return Resp(modules.GetSimulatorByPlatform(id))
17 | }
18 |
19 | // 读取一个模拟器
20 | func (a *Controller) GetSimulatorById(id uint32) string {
21 | return Resp(modules.GetSimulatorById(id))
22 | }
23 |
24 | // 添加一个模拟器
25 | func (a *Controller) AddSimulator(platformId uint32, name string) string {
26 | return Resp(modules.AddSimulator(platformId, name))
27 | }
28 |
29 | // 更新模拟器
30 | func (a *Controller) UpdateSimulator(data string) string {
31 | d := db.Simulator{}
32 | json.Unmarshal([]byte(data), &d)
33 | return Resp(modules.UpdateSimulator(d))
34 | }
35 |
36 | // 更新模拟器排序
37 | func (a *Controller) UpdateSimulatorSort(data string) string {
38 | d := []uint32{}
39 | json.Unmarshal([]byte(data), &d)
40 | return Resp("", modules.UpdateSimulatorSort(d))
41 | }
42 |
43 | // 删除模拟器
44 | func (a *Controller) DelSimulator(id uint32) string {
45 | return Resp("", modules.DelSimulator(id))
46 | }
47 |
48 | // 设置rom模拟器id
49 | func (a *Controller) SetRomSimId(romId uint64, simId uint32) string {
50 | return Resp("", modules.SetRomSimId(romId, simId))
51 | }
52 |
53 | // 批量设置rom模拟器id
54 | func (a *Controller) BatchSetRomSimId(romIds []uint64, simId uint32) string {
55 | return Resp("", modules.BatchSetRomSimId(romIds, simId))
56 | }
57 |
--------------------------------------------------------------------------------
/controller/upgrade.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "simUI/modules"
5 | )
6 |
7 | // 检测升级
8 | func (a *Controller) CheckUpgrade(newVersion string) string {
9 | return Resp(modules.CheckUpgrade(newVersion))
10 | }
11 |
12 | // 跳过更新
13 | func (a *Controller) JumpUpgrade(version string) string {
14 | return Resp("", modules.JumpUpgrade(version))
15 | }
16 |
17 | // 下载新版本
18 | func (a *Controller) DownloadNewVersion(url string) string {
19 | return Resp(modules.DownloadNewVersion(url))
20 | }
21 |
22 | // 安装新版本
23 | func (a *Controller) InstallUpgrade(version string, unzipPath string) string {
24 | return Resp("", modules.InstallUpgrade(version, unzipPath))
25 | }
26 |
--------------------------------------------------------------------------------
/data.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/data.dll
--------------------------------------------------------------------------------
/db/config.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "fmt"
5 | _ "github.com/mattn/go-sqlite3"
6 | "simUI/utils"
7 | )
8 |
9 | type Config struct {
10 | Name string
11 | Data string
12 | Desc string
13 | }
14 |
15 | func (*Config) TableName() string {
16 | return "config"
17 | }
18 |
19 | // 根据id查询一条数据
20 | func (m *Config) Get() (map[string]string, error) {
21 |
22 | volist := []*Config{}
23 | result := getDb().Table(m.TableName()).Select("name,data").Find(&volist)
24 |
25 | if result.Error != nil {
26 | fmt.Println(result.Error.Error())
27 | }
28 |
29 | data := map[string]string{}
30 | for _, v := range volist {
31 | data[v.Name] = v.Data
32 | }
33 |
34 | return data, result.Error
35 | }
36 |
37 | // 根据id查询一条数据
38 | func (m *Config) GetByName(name string) (string, error) {
39 |
40 | vo := &Config{}
41 | result := getDb().Table(m.TableName()).Select("data").Where("name = ?", name).First(&vo)
42 |
43 | if result.Error != nil {
44 | fmt.Println(result.Error.Error())
45 | return "", result.Error
46 | }
47 | return vo.Data, nil
48 | }
49 |
50 | // 更新一个字段
51 | func (m *Config) UpdateOne(name string, data interface{}) error {
52 | result := getDb().Table(m.TableName()).Where("name = ?", name).Update("data", data)
53 |
54 | if result.Error != nil {
55 | fmt.Println(result.Error.Error())
56 | }
57 | return result.Error
58 | }
59 |
60 | // 更新一个字段,如果不存在则新增
61 | func (m *Config) UpsertOne(name string, data interface{}) error {
62 | count := 0
63 | result := getDb().Table(m.TableName()).Where("name = ?", name).Count(&count)
64 | if result.Error != nil {
65 | return result.Error
66 | }
67 |
68 | if count > 0 {
69 | result = getDb().Table(m.TableName()).Where("name = ?", name).Update("data", data)
70 | } else {
71 | c := &Config{
72 | Name: name,
73 | Data: utils.ToString(data),
74 | }
75 | result = getDb().Create(&c)
76 | }
77 |
78 | return result.Error
79 | }
80 |
81 | func (m *Config) Add() {
82 | getDb().Create(&m)
83 | }
84 |
--------------------------------------------------------------------------------
/db/filter.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "fmt"
5 | _ "github.com/mattn/go-sqlite3"
6 | "math"
7 | )
8 |
9 | type Filter struct {
10 | Platform uint32
11 | Type string
12 | Name string
13 | }
14 |
15 | type FilterModel Filter
16 |
17 | func (*Filter) TableName() string {
18 | return "filter"
19 | }
20 |
21 | // 写入数据
22 | func (m *Filter) BatchAdd(data []*Filter) {
23 |
24 | if len(data) == 0 {
25 | return
26 | }
27 |
28 | tx := getDb().Begin()
29 | for _, v := range data {
30 | tx.Create(&v)
31 | }
32 | tx.Commit()
33 | }
34 |
35 | func (*Filter) GetAll() ([]*Filter, error) {
36 | volist := []*Filter{}
37 |
38 | result := getDb().Select("name,type").Order("name ASC").Group("type,name").Find(&volist)
39 | if result.Error != nil {
40 | fmt.Println(result.Error)
41 | }
42 |
43 | return volist, nil
44 | }
45 |
46 | func (*Filter) GetByPlatform(platform uint32) ([]*Filter, error) {
47 | volist := []*Filter{}
48 |
49 | result := getDb().Select("name,type").Where("platform = ?", platform).Order("name ASC").Find(&volist)
50 | if result.Error != nil {
51 | fmt.Println(result.Error)
52 | }
53 |
54 | return volist, nil
55 | }
56 |
57 | // 删除记录
58 | func (m *Filter) DeleteByFileNames(platform uint32, t string, nameList []string) error {
59 |
60 | if len(nameList) == 0 {
61 | return nil
62 | }
63 |
64 | listLen := len(nameList)
65 |
66 | ceil := int(math.Ceil(float64(listLen) / float64(maxVar)))
67 |
68 | for i := 0; i < ceil; i++ {
69 | start := i * maxVar
70 | end := (i + 1) * maxVar
71 | if end > listLen {
72 | end = listLen
73 | }
74 | list := nameList[start:end]
75 | getDb().Where("platform = ? AND type = ? AND name in (?)", platform, t, list).Delete(&m)
76 | }
77 |
78 | return nil
79 | }
80 |
81 | // 删除不存在的平台下的所有menu
82 | func (m *Filter) DeleteByNotPlatform(platforms []string) error {
83 |
84 | if len(platforms) == 0 {
85 | m.Truncate()
86 | return nil
87 | }
88 |
89 | model := &Filter{}
90 | result := getDb().Not("platform", platforms).Delete(&model)
91 |
92 | if result.Error != nil {
93 | fmt.Println(result.Error)
94 | }
95 |
96 | return result.Error
97 | }
98 |
99 | // 清空表数据
100 | func (m *Filter) Truncate() error {
101 | result := getDb().Delete(&m)
102 | if result.Error != nil {
103 | fmt.Println(result.Error)
104 | }
105 | return result.Error
106 | }
107 |
--------------------------------------------------------------------------------
/db/menuVO.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "fmt"
5 | _ "github.com/mattn/go-sqlite3"
6 | "strings"
7 | )
8 |
9 | type MenuVO struct {
10 | Name string
11 | Path string
12 | Platform uint32
13 | SubMenu []*MenuVO //子目录
14 | }
15 |
16 | // 根据条件,查询多条数据
17 | func (m *Menu) GetVoByPlatform(platform uint32) ([]*MenuVO, error) {
18 | where := map[string]interface{}{}
19 |
20 | if platform > 0 {
21 | where["platform"] = platform
22 | }
23 |
24 | volist := []*MenuVO{}
25 | sublist := map[string][]*MenuVO{}
26 |
27 | menus, err := m.GetByPlatform(platform)
28 | if err != nil {
29 | fmt.Println(err)
30 | return volist, err
31 | }
32 |
33 | for _, v := range menus {
34 | item := &MenuVO{
35 | Name: v.Name,
36 | Path: v.Path,
37 | Platform: v.Platform,
38 | SubMenu: []*MenuVO{},
39 | }
40 | patharr := strings.Split(v.Path, "/")
41 | if len(patharr) <= 3 {
42 | volist = append(volist, item)
43 | } else {
44 | if _, ok := sublist[patharr[1]]; ok {
45 | sublist[patharr[1]] = append(sublist[patharr[1]], item)
46 | } else {
47 | sublist[patharr[1]] = []*MenuVO{item}
48 | }
49 | }
50 | }
51 |
52 | //填充子目录
53 | for k, v := range volist {
54 | patharr := strings.Split(v.Path, "/")
55 | if _, ok := sublist[patharr[1]]; ok {
56 | volist[k].SubMenu = sublist[patharr[1]]
57 | }
58 | }
59 |
60 | return volist, nil
61 | }
62 |
--------------------------------------------------------------------------------
/db/rombaseAlias.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "fmt"
5 | _ "github.com/mattn/go-sqlite3"
6 | )
7 |
8 | type RombaseAlias struct {
9 | Platform uint32 // 平台id
10 | Type string // 类型
11 | Alias string // 别名
12 | }
13 |
14 | func (*RombaseAlias) TableName() string {
15 | return "rombase_alias"
16 | }
17 |
18 | // 写入cate数据
19 | func (m *RombaseAlias) Add() error {
20 | result := getDb().Create(&m)
21 |
22 | if result.Error != nil {
23 | fmt.Println(result.Error)
24 | }
25 |
26 | return nil
27 | }
28 |
29 | // 更新喜爱状态
30 | func (m *RombaseAlias) UpdateByType() error {
31 | //更新数据
32 | result := getDb().Table(m.TableName()).Where("platform=? AND type = ?", m.Platform, m.Type).Update("alias", m.Alias)
33 | if result.Error != nil {
34 | fmt.Println(result.Error)
35 | return result.Error
36 | }
37 |
38 | return nil
39 | }
40 |
41 | // 根据类型读取数据
42 | func (*RombaseAlias) GetByPlatform(platform uint32) (map[string]string, error) {
43 | volist := []*RombaseAlias{}
44 | result := getDb().Where("platform = ?", platform).Find(&volist)
45 | if result.Error != nil {
46 | fmt.Println(result.Error.Error())
47 | }
48 |
49 | data := map[string]string{}
50 | if len(volist) > 0 {
51 | for _, v := range volist {
52 | data[v.Type] = v.Alias
53 | }
54 | }
55 | return data, result.Error
56 | }
57 |
58 | // 删除记录
59 | func (m *RombaseAlias) DeleteByType() error {
60 | result := getDb().Where("platform = ? AND type=? ", m.Platform, m.Type).Delete(&m)
61 | return result.Error
62 | }
63 |
--------------------------------------------------------------------------------
/db/rombaseEnum.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "fmt"
5 | _ "github.com/mattn/go-sqlite3"
6 | )
7 |
8 | type RombaseEnum struct {
9 | Type string // 类型
10 | Name string // 名称
11 | Sort uint32 // 排序
12 | }
13 |
14 | func (*RombaseEnum) TableName() string {
15 | return "rombase_enum"
16 | }
17 |
18 | // 批量写入
19 | func (m *RombaseEnum) BatchAdd(romlist []*RombaseEnum) error {
20 |
21 | if len(romlist) == 0 {
22 | return nil
23 | }
24 |
25 | tx := getDb().Begin()
26 | for _, v := range romlist {
27 | tx.Create(&v)
28 | }
29 | tx.Commit()
30 | return nil
31 | }
32 |
33 | // 读取全部数据
34 | func (*RombaseEnum) GetAll() ([]*RombaseEnum, error) {
35 | volist := []*RombaseEnum{}
36 | result := getDb().Order("sort Asc").Find(&volist)
37 | if result.Error != nil {
38 | fmt.Println(result.Error.Error())
39 | }
40 | return volist, result.Error
41 | }
42 |
43 | // 根据类型读取数据
44 | func (*RombaseEnum) GetByType(t string) ([]*RombaseEnum, error) {
45 | volist := []*RombaseEnum{}
46 | result := getDb().Where("type=?", t).Order("sort Asc").Find(&volist)
47 | if result.Error != nil {
48 | fmt.Println(result.Error.Error())
49 | }
50 | return volist, result.Error
51 | }
52 |
53 | // 删除一个类型
54 | func (m *RombaseEnum) DeleteByType() error {
55 | result := getDb().Where("type=? ", m.Type).Delete(&m)
56 | return result.Error
57 | }
58 |
--------------------------------------------------------------------------------
/db/shortcut.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "fmt"
5 | _ "github.com/mattn/go-sqlite3"
6 | )
7 |
8 | type Shortcut struct {
9 | Id uint32
10 | Name string
11 | Path string
12 | Sort uint32
13 | }
14 |
15 | func (*Shortcut) TableName() string {
16 | return "shortcut"
17 | }
18 |
19 | // 写入数据
20 | func (m *Shortcut) Add() (int64, error) {
21 |
22 | result := getDb().Create(&m)
23 |
24 | if result.Error != nil {
25 | fmt.Println(result.Error)
26 | }
27 |
28 | return int64(m.Id), result.Error
29 | }
30 |
31 | // 读取所有数据
32 | func (sim *Shortcut) GetAll() ([]*Shortcut, error) {
33 | volist := []*Shortcut{}
34 | result := getDb().Order("sort,id ASC").Find(&volist)
35 | if result.Error != nil {
36 | fmt.Println(result.Error)
37 | }
38 | return volist, nil
39 | }
40 |
41 | // 更新一条记录
42 | func (m *Shortcut) UpdateById() error {
43 | create := map[string]interface{}{
44 | "name": m.Name,
45 | "path": m.Path,
46 | "sort": m.Sort,
47 | }
48 | result := getDb().Table(m.TableName()).Where("id=?", m.Id).Updates(create)
49 |
50 | if result.Error != nil {
51 | fmt.Println(result.Error)
52 | }
53 | return result.Error
54 | }
55 |
56 | // 更新排序
57 | func (m *Shortcut) UpdateSortById() error {
58 | result := getDb().Table(m.TableName()).Where("id=?", m.Id).Update("sort", m.Sort)
59 | if result.Error != nil {
60 | fmt.Println(result.Error)
61 | }
62 | return result.Error
63 | }
64 |
65 | // 删除一条记录
66 | func (m *Shortcut) DeleteById() error {
67 | result := getDb().Where("id=?", m.Id).Delete(&m)
68 | if result.Error != nil {
69 | fmt.Println(result.Error)
70 | }
71 | return result.Error
72 | }
73 |
--------------------------------------------------------------------------------
/db/thumbs.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "fmt"
5 | _ "github.com/mattn/go-sqlite3"
6 | )
7 |
8 | type Thumbs struct {
9 | Platform uint32
10 | Type string
11 | ImageId string
12 | }
13 |
14 | var ThumbMap map[uint32]map[string]uint8
15 |
16 | func (*Thumbs) TableName() string {
17 | return "thumbs"
18 | }
19 |
20 | // 写入数据
21 | func (m *Thumbs) Add() error {
22 |
23 | result := getDb().Create(&m)
24 | if result.Error != nil {
25 | fmt.Println(result.Error)
26 | }
27 |
28 | return result.Error
29 | }
30 |
31 | func (m *Thumbs) GetByPlatform(platform uint32) (map[string]uint8, error) {
32 |
33 | if _, ok := ThumbMap[platform]; ok {
34 | return ThumbMap[platform], nil
35 | }
36 |
37 | volist := []*Thumbs{}
38 |
39 | result := getDb().Select("image_id").Where("platform = ?", platform).Find(&volist)
40 | if result.Error != nil {
41 | fmt.Println(result.Error)
42 | }
43 |
44 | platformMap := make(map[string]uint8)
45 | if len(volist) > 0 {
46 | for _, v := range volist {
47 | platformMap[v.ImageId] = 1
48 | }
49 | } else {
50 | //随便塞一个数据,防止穿透
51 | platformMap["a"] = 1
52 | }
53 |
54 | ThumbMap[platform] = platformMap
55 | return platformMap, nil
56 | }
57 |
58 | func (m *Thumbs) checkImage(platform uint32, imageId string) bool {
59 |
60 | if _, ok := ThumbMap[platform]; !ok {
61 | m.GetByPlatform(platform)
62 | }
63 |
64 | if _, ok := ThumbMap[platform][imageId]; ok {
65 | return true
66 | }
67 |
68 | return false
69 | }
70 |
--------------------------------------------------------------------------------
/db/upgrade.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | _ "github.com/mattn/go-sqlite3"
5 | )
6 |
7 | // 数据库更新列表,列表中数据不能删除,如果有重复update,则将老记录留空,最后添加新记录
8 | // 如果有添加,就往最后添加,不要往前面或中间添加
9 | func DbUpdateSqlList(version string) []string {
10 | return []string{
11 | //`update config SET version = ` + version + ` where id = 1`,
12 | `ALTER TABLE platform ADD COLUMN "hide_name" INTEGER NOT NULL DEFAULT 0`,
13 | `ALTER TABLE rom ADD COLUMN "sim_setting" TEXT NOT NULL DEFAULT ''`,
14 | ``,
15 | `CREATE UNIQUE INDEX "idx_name" ON "config" ("name");`,
16 | `INSERT INTO config ("name", "data", "desc") VALUES ('GameMultiOpen', '0', '是否允许模拟器游戏多开')`,
17 | `ALTER TABLE rom ADD COLUMN "favorite" INTEGER NOT NULL DEFAULT 0`,
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | //wails dev -appargs "-dev"
2 | //wails dev -s -skipbindings -appargs "-dev"
3 | //wails build -upx -ldflags="-H windowsgui -w -s"
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= productName %>
5 |
6 |
7 |
8 |
9 |
10 |
11 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "quasar-project",
3 | "version": "0.0.1",
4 | "description": "A Quasar Project",
5 | "productName": "Quasar App",
6 | "author": "frontlon ",
7 | "private": true,
8 | "scripts": {
9 | "test": "echo \"No test specified\" && exit 0",
10 | "dev": "quasar dev",
11 | "build": "quasar build"
12 | },
13 | "dependencies": {
14 | "@imengyu/vue3-context-menu": "^1.3.3",
15 | "@quasar/extras": "^1.16.6",
16 | "animate.css": "^4.1.1",
17 | "axios": "^1.2.1",
18 | "pinia": "^2.0.11",
19 | "quasar": "^2.12.7",
20 | "sortablejs": "^1.15.0",
21 | "swiper": "^11.1.3",
22 | "v-viewer": "^3.0.11",
23 | "viewerjs": "^1.11.5",
24 | "vue": "^3.0.0",
25 | "vue-i18n": "^9.2.2",
26 | "vue-router": "^4.0.0",
27 | "vue-star-rating": "^2.1.0",
28 | "vuedraggable": "^4.1.0"
29 | },
30 | "devDependencies": {
31 | "@intlify/vite-plugin-vue-i18n": "^3.3.1",
32 | "@quasar/app-vite": "^1.6.2",
33 | "@types/node": "^12.20.21",
34 | "autoprefixer": "^10.4.2",
35 | "typescript": "^4.5.4"
36 | },
37 | "engines": {
38 | "node": "^18 || ^16 || ^14.19",
39 | "npm": ">= 6.13.4",
40 | "yarn": ">= 1.21.1"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/frontend/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // https://github.com/michael-ciniawsky/postcss-load-config
3 |
4 | module.exports = {
5 | plugins: [
6 | // https://github.com/postcss/autoprefixer
7 | require('autoprefixer')({
8 | overrideBrowserslist: [
9 | 'last 4 Chrome versions',
10 | 'last 4 Firefox versions',
11 | 'last 4 Edge versions',
12 | 'last 4 Safari versions',
13 | 'last 4 Android versions',
14 | 'last 4 ChromeAndroid versions',
15 | 'last 4 FirefoxAndroid versions',
16 | 'last 4 iOS versions'
17 | ]
18 | })
19 |
20 | // https://github.com/elchininet/postcss-rtlcss
21 | // If you want to support RTL css, then
22 | // 1. yarn/npm install postcss-rtlcss
23 | // 2. optionally set quasar.config.js > framework > lang to an RTL language
24 | // 3. uncomment the following line:
25 | // require('postcss-rtlcss')
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/frontend/public/images/context/alias.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/alias.png
--------------------------------------------------------------------------------
/frontend/public/images/context/app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/app.png
--------------------------------------------------------------------------------
/frontend/public/images/context/audio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/audio.png
--------------------------------------------------------------------------------
/frontend/public/images/context/baseinfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/baseinfo.png
--------------------------------------------------------------------------------
/frontend/public/images/context/copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/copy.png
--------------------------------------------------------------------------------
/frontend/public/images/context/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/delete.png
--------------------------------------------------------------------------------
/frontend/public/images/context/doc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/doc.png
--------------------------------------------------------------------------------
/frontend/public/images/context/down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/down.png
--------------------------------------------------------------------------------
/frontend/public/images/context/fav_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/fav_0.png
--------------------------------------------------------------------------------
/frontend/public/images/context/fav_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/fav_1.png
--------------------------------------------------------------------------------
/frontend/public/images/context/files.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/files.png
--------------------------------------------------------------------------------
/frontend/public/images/context/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/folder.png
--------------------------------------------------------------------------------
/frontend/public/images/context/hide_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/hide_0.png
--------------------------------------------------------------------------------
/frontend/public/images/context/hide_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/hide_1.png
--------------------------------------------------------------------------------
/frontend/public/images/context/move.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/move.png
--------------------------------------------------------------------------------
/frontend/public/images/context/output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/output.png
--------------------------------------------------------------------------------
/frontend/public/images/context/rename.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/rename.png
--------------------------------------------------------------------------------
/frontend/public/images/context/strategy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/strategy.png
--------------------------------------------------------------------------------
/frontend/public/images/context/sub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/sub.png
--------------------------------------------------------------------------------
/frontend/public/images/context/thumbs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/thumbs.png
--------------------------------------------------------------------------------
/frontend/public/images/context/unlink.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/unlink.png
--------------------------------------------------------------------------------
/frontend/public/images/context/up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/context/up.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/+.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/+.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/1.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/2.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/3.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/4.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/5.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/6.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/7.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/8.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/9.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/A.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/A.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/B.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/B.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/C.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/D.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/D.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/E.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/F.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/F.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/K.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/K.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/N.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/N.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/P.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/P.png
--------------------------------------------------------------------------------
/frontend/public/images/ctl/S.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ctl/S.png
--------------------------------------------------------------------------------
/frontend/public/images/ico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/ico.png
--------------------------------------------------------------------------------
/frontend/public/images/list-style/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/list-style/1.png
--------------------------------------------------------------------------------
/frontend/public/images/list-style/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/list-style/2.png
--------------------------------------------------------------------------------
/frontend/public/images/list-style/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/list-style/3.png
--------------------------------------------------------------------------------
/frontend/public/images/list-style/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/list-style/4.png
--------------------------------------------------------------------------------
/frontend/public/images/rom_animate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/public/images/rom_animate.png
--------------------------------------------------------------------------------
/frontend/public/images/svg/complete2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/public/images/svg/number.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/public/images/svg/time.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
--------------------------------------------------------------------------------
/frontend/src/boot/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/src/boot/.gitkeep
--------------------------------------------------------------------------------
/frontend/src/boot/axios.ts:
--------------------------------------------------------------------------------
1 | import { boot } from 'quasar/wrappers';
2 | import axios, { AxiosInstance } from 'axios';
3 |
4 | declare module '@vue/runtime-core' {
5 | interface ComponentCustomProperties {
6 | $axios: AxiosInstance;
7 | $api: AxiosInstance;
8 | }
9 | }
10 |
11 | // Be careful when using SSR for cross-request state pollution
12 | // due to creating a Singleton instance here;
13 | // If any client changes this (global) instance, it might be a
14 | // good idea to move this instance creation inside of the
15 | // "export index () => {}" function below (which runs individually
16 | // for each client)
17 | const api = axios.create({ baseURL: 'https://api.example.com' });
18 |
19 | export default boot(({ app }) => {
20 | // for use inside Vue files (Options API) through this.$axios and this.$api
21 |
22 | app.config.globalProperties.$axios = axios;
23 | // ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
24 | // so you won't necessarily have to import axios in each vue file
25 |
26 | app.config.globalProperties.$api = api;
27 | // ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
28 | // so you can easily perform requests against your app's API
29 | });
30 |
31 | export { api };
32 |
--------------------------------------------------------------------------------
/frontend/src/boot/constant.ts:
--------------------------------------------------------------------------------
1 | //是否为开发模式
2 | export const IS_DEV = false;
3 |
4 | //模拟器常用启动参数
5 | export const SIMULATOR_BOOT_PARAM_OPTIONS = [
6 | {label: 'Default', value: '{RomFullPath}',},
7 | {label: 'MAME', value: '{RomName}',},
8 | {label: 'Winkawaks', value: '{RomName}',},
9 | {label: 'FBA Shuffle', value: '-g {RomName} -w',},
10 | {label: 'Demul', value: '-run=dc -image={RomFullPath}',},
11 | {label: 'ePsXe', value: '-loadbin {RomFullPath} -nogui',},
12 | {label: 'Dolphin', value: '-e {RomFullPath}',},
13 | {label: 'OpenBOR', value: 'load {RomFullPath}',},
14 | {label: 'RetroArch', value: '-L cores\\xxx.dll {RomFullPath}',},
15 | {label: 'DosBox', value: '-conf {RomFullPath} -exit',},
16 | {label: 'DuckStation', value: '{RomFullPath} -batch',},
17 | {label: 'Nebula2', value: '{RomName}',},
18 | {
19 | label: 'NullDC',
20 | value: '-config nullDC:Emulator.Autostart=1 -config ImageReader:LoadDefaultImage=1 -config ImageReader:DefaultImage={RomFullPath}',
21 | },
22 | {label: 'Yabuse', value: '–iso={RomFullPath}',},
23 | {label: 'PCem', value: '–config {RomFullPath} -f',},
24 | {label: 'Cemu', value: '-g {RomFullPath} -f',},
25 | ];
26 |
27 | //编辑器工具栏
28 | export const EDITOR_TOOLBAR = [
29 | [
30 | 'viewsource',
31 | {
32 | label: "",
33 | icon: "format_align_left",
34 | list: 'only-icons',
35 | options: ['left', 'center', 'right', 'justify']
36 | },
37 | {
38 | label: "",
39 | icon: "title",
40 | list: 'no-icons',
41 | options: ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
42 | },
43 | 'bold', 'italic', 'strike', 'underline', 'subscript', 'superscript',
44 | 'code', 'quote', 'unordered', 'ordered', 'outdent', 'indent',
45 | 'image', 'link', 'hr',
46 | 'removeFormat', 'fullscreen'
47 | ],
48 | ]
49 |
50 | //菜单图标大小
51 | export const CONTEXT_ICON_SIZE = {
52 | width: '20px',
53 | height: '20px',
54 | }
--------------------------------------------------------------------------------
/frontend/src/boot/i18n.ts:
--------------------------------------------------------------------------------
1 | import { boot } from 'quasar/wrappers';
2 | import { createI18n } from 'vue-i18n';
3 |
4 | import messages from 'src/i18n';
5 |
6 | export type MessageLanguages = keyof typeof messages;
7 | // Type-define 'en-US' as the master schema for the resource
8 | export type MessageSchema = typeof messages['en-US'];
9 |
10 | // See https://vue-i18n.intlify.dev/guide/advanced/typescript.html#global-resource-schema-type-definition
11 | /* eslint-disable @typescript-eslint/no-empty-interface */
12 | declare module 'vue-i18n' {
13 | // define the locale messages schema
14 | export interface DefineLocaleMessage extends MessageSchema {}
15 |
16 | // define the datetime format schema
17 | export interface DefineDateTimeFormat {}
18 |
19 | // define the number format schema
20 | export interface DefineNumberFormat {}
21 | }
22 | /* eslint-enable @typescript-eslint/no-empty-interface */
23 |
24 | export default boot(({ app }) => {
25 | const i18n = createI18n({
26 | locale: 'en-US',
27 | legacy: false,
28 | messages,
29 | });
30 |
31 | // Set i18n instance on app
32 | app.use(i18n);
33 | });
34 |
--------------------------------------------------------------------------------
/frontend/src/boot/imgObserver.ts:
--------------------------------------------------------------------------------
1 | // 定义一个全局的 Intersection Observer 实例
2 | const observer = new IntersectionObserver(entries => {
3 | entries.forEach(entry => {
4 |
5 | const lazyImage = entry.target.querySelector('img') as HTMLImageElement;
6 | console.log(lazyImage);
7 |
8 | if (lazyImage) {
9 | const src = lazyImage.src;
10 |
11 | lazyImage.dataset.src = lazyImage.dataset.src || src;
12 |
13 | if (entry.isIntersecting) {
14 | lazyImage.src = lazyImage.dataset.src || '';
15 | // observer.unobserve(lazyImage);
16 | } else {
17 | lazyImage.src = '';
18 | //lazyImage.removeAttribute("src")
19 |
20 | }
21 | }
22 |
23 | });
24 | });
25 |
26 | // vue3 自定义指令
27 | export default {
28 | mounted(el: HTMLElement) {
29 | observer.observe(el);
30 | },
31 | unmounted(el: HTMLElement) {
32 | observer.unobserve(el);
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/frontend/src/components/AboutComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | SIMUI Pulsar v{{versionNo}}
9 | 编译时间:{{buildTime}}
10 | 网站:www.simui.net
11 | QQ群:851347939(一群)
13 | |
14 | 648286315(二群)
16 |
17 | 邮箱:front_diablo@163.com
18 | 特别鸣谢
19 | 春华秋实|孟虎|睡神林克|wood.exe|alexxyz2|
冰澤|Vanisper|以及广大网友 ...
20 |
21 |
22 |
23 |
24 |
25 |
34 |
35 |
--------------------------------------------------------------------------------
/frontend/src/components/CopyrightComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | copyright
9 | info
10 |
11 |
12 |
13 |
14 |
15 |
24 |
25 |
--------------------------------------------------------------------------------
/frontend/src/components/SDialogComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ title }}
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{ subTitle }}
14 |
15 | {{ message }}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
85 |
86 |
100 |
--------------------------------------------------------------------------------
/frontend/src/components/dialog.ts:
--------------------------------------------------------------------------------
1 | import {QDialogOptions} from 'quasar'
2 | import SDialogComponent from "components/SDialogComponent.vue";
3 | import {ref} from "vue";
4 |
5 | //弹出dialog
6 | export function getPromptOpts(title: string, msg: string, okLabel: string, persistent: boolean, val: any = null, subTitle = ""): QDialogOptions {
7 | let data = {
8 | component: SDialogComponent,
9 | componentProps: {
10 | title: title,
11 | subTitle: subTitle,
12 | message: msg,
13 | persistent: persistent,
14 | okLabel: okLabel,
15 | }
16 | }
17 | if (val !== null) {
18 | data.componentProps.input = ref(val)
19 | }
20 | return data
21 | }
22 |
--------------------------------------------------------------------------------
/frontend/src/components/models.ts:
--------------------------------------------------------------------------------
1 | export interface Todo {
2 | id: number;
3 | content: string;
4 | }
5 |
6 | export interface Meta {
7 | totalCount: number;
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/src/css/app.css:
--------------------------------------------------------------------------------
1 | /* app global css */
2 | :root {
3 | --color-0: #000;
4 | --color-1: #111;
5 | --color-2: #222;
6 | --color-3: #333;
7 | --color-4: #444;
8 | --color-5: #555;
9 | --color-6: #666;
10 | --color-7: #777;
11 | --color-8: #888;
12 | --color-9: #999;
13 | --color-10: #AAA;
14 | --color-11: #BBB;
15 | --color-12: #CCC;
16 | --color-13: #DDD;
17 | --color-14: #EEE;
18 | --color-15: #FFF;
19 | --color-text: #FFF;
20 | --color-text-brand: #FFF;
21 | }
22 |
23 | * {
24 | color: var(--color-text);
25 | }
26 |
27 | body.body--dark{
28 | background: var(--color-1);
29 | user-select:none;
30 | }
31 |
32 | .border-in{
33 | border:1px solid var(--color-1);
34 | }
35 |
36 | .border-out{
37 | border:1px solid var(--color-3);
38 | }
39 |
40 | .wrap {
41 | white-space:normal;
42 | word-wrap:break-word;
43 | word-break:break-all;
44 | }
45 |
46 | a {
47 | color: inherit;
48 | text-decoration: none;
49 | }
50 |
51 | .scrollable {
52 | overflow-y: hidden;
53 | scrollbar-gutter: stable;
54 | }
55 |
56 | .scrollable:hover {
57 | overflow-y: scroll;
58 | }
59 |
60 | .select-no-option {
61 | font-size: 12px;
62 | color: var(--color-9);
63 | }
64 |
65 | .btn-active {
66 | background: var(--q-primary);
67 | }
68 |
69 | .btn-active * {
70 | color: var(--color-text-brand);
71 | }
72 |
73 | .btn-primary {
74 | background: var(--q-primary);
75 | height: auto!important;
76 | }
77 |
78 | .btn-primary * {
79 | color: var(--color-text-brand);
80 | }
81 |
82 | .q-loading .q-loading__box{
83 | background: var(--color-1)!important;
84 | color: var(--color-text)!important;
85 | }
86 |
87 | .selectable{
88 | user-select: text;
89 | }
90 |
91 | .update-btn{
92 | padding: 0 30px!important;
93 | }
94 |
--------------------------------------------------------------------------------
/frontend/src/css/classic/common.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --content-backgorund-image: "";
3 | --content-backgorund-size: "";
4 | --content-backgorund-repeat: "";
5 | --content-backgorund-mask: "";
6 | --base-font-size: "";
7 | }
--------------------------------------------------------------------------------
/frontend/src/css/classic/contentContext.css:
--------------------------------------------------------------------------------
1 |
2 | .q-item {
3 | font-size: 12px !important;
4 | border: 0;
5 | padding: 0 10px;
6 | height: 32px;
7 | min-height: 32px;
8 | line-height: 32px;
9 | }
10 |
11 | .context-list .q-item {
12 | padding: 0 0 0 10px;
13 | }
14 |
15 | .context-list .q-icon {
16 | font-size: 12px;
17 | }
18 |
19 | .q-item:nth-child(odd) {
20 | background: var(--color-3);
21 | }
22 |
23 | .q-item:nth-child(even) {
24 | background: var(--color-4);
25 | }
26 |
27 | .context-list .q-separator {
28 | border: 0;
29 | height: 1px;
30 | line-height: 1px;
31 | border: none;
32 | border-top: 1px solid var(--color-0);
33 | border-bottom: 1px solid var(--color-4);
34 | }
35 |
36 | .avatar {
37 | max-width: 24px;
38 | min-width: 24px;
39 | }
40 |
41 | .avatar img {
42 | width: 16px;
43 | }
44 |
45 | /*运行游戏*/
46 | .rungame {
47 | padding-left: 0 !important;
48 | }
49 |
50 | .rungame-left {
51 | padding-right: 10px !important;
52 | }
53 |
54 | .rungame-right {
55 | padding-left: 0px;
56 | }
57 |
58 | .rungame-right-ico {
59 | height: 32px;
60 | padding-left: 10px;
61 | }
62 |
63 | .move-list .q-item:nth-child(odd) {
64 | background: none;
65 | }
66 |
67 | .move-list .q-item:nth-child(even) {
68 | background: none;
69 | }
70 |
71 | .sub-tree{
72 | padding:10px 10px 10px 30px
73 | }
74 |
75 | .move-list .submenu-active{
76 | color: var(--color-15);
77 | background: var(--q-primary) !important;
78 | }
--------------------------------------------------------------------------------
/frontend/src/css/classic/headerBarFilter.css:
--------------------------------------------------------------------------------
1 | .filter-wrapper {
2 | padding-bottom: 5px;
3 | margin: 0;
4 | background: var(--color-3);
5 | }
6 |
7 | .filter-wrapper .search-btn {
8 | height: 25px;
9 | width: 50px;
10 | min-height: 25px;
11 | margin: 4px 0 0 4px;
12 | padding: 0;
13 | font-size: 10px;
14 | }
15 |
16 | .filter-input {
17 | font-size: 12px;
18 | background: var(--color-2);
19 | width: 80px;
20 | }
21 |
22 | :deep(.q-select) {
23 | min-height: 25px !important;
24 | max-width: 100px;
25 | height: 25px !important;
26 | font-size: 12px;
27 | line-height: 12px;
28 | }
29 |
30 |
31 | :deep(.q-field__input) {
32 | min-height: 25px !important;
33 | height: 25px !important;
34 |
35 | }
36 |
37 | :deep(.q-field__native) {
38 | min-height: 25px !important;
39 | height: 25px !important;
40 | padding: 0;
41 | white-space: nowrap;
42 | overflow: hidden;
43 | }
44 |
45 | :deep(.q-field__control) {
46 | min-height: 25px !important;
47 | height: 25px !important;
48 | padding: 0 1px 0 5px;
49 | }
50 |
51 | :deep(.q-field__marginal) {
52 | min-height: 25px !important;
53 | height: 25px !important;
54 | }
55 |
56 | .filter-badge{
57 | padding: 5px;
58 | background: var(--color-3);
59 | }
--------------------------------------------------------------------------------
/frontend/src/css/classic/layout.css:
--------------------------------------------------------------------------------
1 | body{
2 | font-size: 36px;
3 | width:100px!important;
4 |
5 | }
6 |
7 | .border-in{
8 | font-size: var(--base-font-size);
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/src/css/classic/menuBar.css:
--------------------------------------------------------------------------------
1 |
2 | /* 目录栏 */
3 | .menu-wrapper {
4 | height: calc(100vh - 64px) !important;
5 | background: var(--color-3);
6 | }
7 |
8 | .menu-title {
9 | padding: 0 10px;
10 | background: var(--color-2);
11 | color: var(--color-7);
12 | font-size: calc(var(--base-font-size) - 2px);
13 | }
14 |
15 | .menu-title .q-badge {
16 | padding: 5px;
17 | background: var(--color-3);
18 | }
19 |
20 | .menu-item {
21 | text-align: left;
22 | padding: 0 16px;
23 | font-size: calc(var(--base-font-size) - 2px);
24 |
25 | }
26 |
27 | .menu-sub-item-wrapper {
28 | background: var(--color-2);
29 | box-shadow: inset 0 0 5px var(--color-1);
30 | font-size: calc(var(--base-font-size) - 2px);
31 | }
32 |
33 | .item-font, .q-item {
34 | font-size: calc(var(--base-font-size) - 2px);
35 | }
36 |
37 | .menu-sub-item .q-item-label {
38 | text-indent: 20px;
39 | padding: 20px;
40 | }
41 |
42 | .menu-sub-item .q-item-section {
43 | text-indent: 20px;
44 | padding: 20px;
45 | }
46 |
47 | .menu-item .q-item-label {
48 | text-indent: 20px;
49 | padding: 20px;
50 | }
51 |
52 | .menu-item .q-item-section {
53 | text-indent: 20px;
54 | padding: 20px;
55 | }
56 |
--------------------------------------------------------------------------------
/frontend/src/css/classic/platformBar.css:
--------------------------------------------------------------------------------
1 |
2 | /* 平台栏 */
3 | .platform-wrapper {
4 | box-shadow: inset -5px 0 10px var(--color-1);
5 | height: calc(100vh - 64px) !important;
6 | background: var(--color-2);
7 | }
8 |
9 | .platform-item {
10 | border-bottom: 1px solid var(--color-0);
11 | border-top: 1px solid var(--color-4);
12 | text-align: center;
13 | min-height: 60px;
14 | }
15 |
16 | .platform-item:first-child {
17 | border-top: 0;
18 | }
19 |
20 | .platform-item:last-child {
21 | border-bottom: 0;
22 | }
23 |
24 | .platform-item .q-img {
25 | width: 32px;
26 | height: 32px;
27 | margin: 0 auto 5px auto
28 | }
29 |
30 | .platform-active {
31 | box-shadow: inset 0 0 10px var(--color-1);
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/frontend/src/css/grid.css:
--------------------------------------------------------------------------------
1 | .col-1{
2 | width: 4.166666667%!important;
3 | }
4 | .col-2{
5 | width: 8.333333333%!important;
6 | }
7 | .col-3{
8 | width: 12.5%!important;
9 | }
10 | .col-4{
11 | width: 16.66666667%!important;
12 | }
13 | .col-5{
14 | width: 20.833333333%!important;
15 | }
16 | .col-6{
17 | width: 25%!important;
18 | }
19 | .col-7{
20 | width: 29.166666667%!important;
21 | }
22 | .col-8{
23 | width: 33.33333333%!important;
24 | }
25 | .col-9{
26 | width: 37.5%!important;
27 | }
28 | .col-10{
29 | width: 41.66666667%!important;
30 | }
31 | .col-11{
32 | width: 45.833333333%!important;
33 | }
34 | .col-12{
35 | width: 50%!important;
36 | }
37 | .col-13{
38 | width: 54.166666667%!important;
39 | }
40 | .col-14{
41 | width: 58.33333333%!important;
42 | }
43 | .col-15{
44 | width: 62.5%!important;
45 | }
46 | .col-16{
47 | width: 66.66666667%!important;
48 | }
49 | .col-17{
50 | width: 70.833333333%!important;
51 | }
52 | .col-18{
53 | width: 75%!important;
54 | }
55 | .col-19{
56 | width: 79.166666667%!important;
57 | }
58 | .col-20{
59 | width: 83.33333333%!important;
60 | }
61 | .col-21{
62 | width: 87.5%!important;
63 | }
64 | .col-22{
65 | width: 91.66666667%!important;
66 | }
67 | .col-23{
68 | width: 95.833333333%!important;
69 | }
70 | .col-24{
71 | width: 100%!important;
72 | }
--------------------------------------------------------------------------------
/frontend/src/css/manage.css:
--------------------------------------------------------------------------------
1 | html,body{
2 | height: auto;
3 | overflow: visible;
4 | border: 1px solid #f00;
5 | }
6 | .manage-wrapper {
7 | max-width: 60%;
8 | margin: 0 auto;
9 | }
10 |
11 | .manage-wrapper .q-tab-panels {
12 | background: none;
13 | padding: 0;
14 | }
15 |
16 | .manage-wrapper .q-tab-panel, .q-item {
17 | padding: 0;
18 | }
19 |
20 | .manage-wrapper .mt {
21 | margin-top: 8px;
22 | }
23 |
24 | .manage-wrapper .separator {
25 | height: 5px;
26 | }
27 |
28 | .manage-wrapper .q-list {
29 | margin-bottom: 20px;
30 | }
31 |
32 | .manage-wrapper .q-item {
33 | margin: 3px 0
34 | }
35 |
36 | .manage-wrapper h6 {
37 | margin: 20px 0 10px 0
38 | }
39 |
40 | .open-dialog {
41 | cursor: pointer;
42 | }
43 |
44 | .bottom-bar {
45 | text-align: right;
46 | margin-top: 20px;
47 | }
48 |
49 |
50 | .editor {
51 | width: 100%;
52 | min-height: 400px;
53 | margin-top: 10px;
54 | }
55 |
56 | .editor-desc{
57 | display: flex;
58 | }
59 | .editor-desc .badge{
60 | margin: 0 5px;
61 | }
62 |
--------------------------------------------------------------------------------
/frontend/src/css/modules.css:
--------------------------------------------------------------------------------
1 | /*scrollbar*/
2 | ::-webkit-scrollbar {
3 | width: 2px;
4 | height: 2px;
5 | }
6 |
7 | ::-webkit-scrollbar-thumb {
8 | background: rgba(255, 255, 255, 0.5);
9 | border-radius: 3px;
10 | transition: opacity .3s;
11 | will-change: opacity;
12 | cursor: grab;
13 | }
14 |
15 | ::-webkit-scrollbar-thumb:hover {
16 | background: rgba(255, 255, 255, 0.3);
17 | background-clip: content-box;
18 | }
19 |
20 | ::-webkit-scrollbar-thumb:active {
21 | background: rgba(255, 255, 255, 0.5);
22 | }
23 |
24 | /*context-menu*/
25 | .dark {
26 | --mx-menu-backgroud: var(--color-1) !important;
27 | --mx-menu-hover-backgroud: var(--color-2) !important;;
28 | --mx-menu-active-backgroud: var(--color-3) !important;;
29 | --mx-menu-divider: var(--color-0) !important;
30 | }
31 |
32 | /* modules */
33 | .q-menu {
34 | border: 1px solid var(--color-3);
35 | box-shadow: 0 0 10px var(--color-1);
36 | max-height: max-content !important;
37 | background-color: var(--color-1);
38 | }
39 |
40 | .q-card {
41 | background: var(--color-1);
42 | }
43 |
44 | .q-tab-panels {
45 | background: var(--color-1);
46 | }
47 |
48 | .q-chip {
49 | background: var(--color-1);
50 | }
51 |
52 | .q-editor {
53 | background: var(--color-1);
54 | }
55 |
56 | .q-banner {
57 | background: var(--color-1);
58 | }
59 |
60 | .q-badge {
61 | color: var(--color-text);
62 | }
63 |
64 | .q-field__label {
65 | color: var(--color-text) !important;
66 | }
67 |
68 | .q-field--highlighted .q-field__control{
69 | background: var(--color-2)!important;
70 | }
71 |
72 | .q-checkbox--dark .q-checkbox__inner--truthy{
73 | color: var(--q-primary)!important;
74 | }
75 |
76 | .q-checkbox--dark .q-checkbox__svg{
77 | color: var(--color-text)!important;
78 | }
79 |
80 | .q-checkbox--dark .text-primary{
81 | background: none;
82 | }
83 |
84 | .q-checkbox--dark .q-checkbox__bg{
85 | color: inherit;
86 | }
87 |
88 | .q-toggle--dark .q-toggle__inner--truthy .q-toggle__thumb {
89 | color: var(--q-primary)!important;
90 | }
91 |
92 | .q-toggle--dark .q-toggle__inner--truthy .q-toggle__track {
93 | background: var(--q-primary)!important;
94 | }
95 |
96 | .q-table--dark{
97 | background: var(--color-2);
98 | }
99 |
100 | .q-table--dark .q-table__top{
101 | background: var(--color-1);
102 | }
103 | .q-table--dark .q-table__title{
104 | color: var(--color-8);
105 | }
106 |
107 | .vue-star-rating-star{
108 | margin: 0 2px;
109 | }
110 |
111 |
112 | .q-select-content{
113 | max-height: calc(100vh - 200px)!important;
114 | }
115 |
116 | .q-select-content-min{
117 | max-height: 50vh!important;
118 | }
119 |
--------------------------------------------------------------------------------
/frontend/src/css/page/platform.css:
--------------------------------------------------------------------------------
1 | .left-wrapper {
2 | min-height: 500px;
3 | background: var(--color-2);
4 | margin-right: 10px;
5 | }
6 |
7 | .platform-scroll {
8 | height: 500px;
9 | }
10 |
11 | .right-wrapper {
12 | background: var(--color-2);
13 | }
14 |
15 | .q-tab-panel {
16 | padding: 0 15px !important;
17 | }
18 |
19 | .platform-list .q-item {
20 | padding: 10px;
21 | margin: 0
22 | }
23 |
24 | .editor {
25 | width: 100%;
26 | min-height: 460px;
27 | margin-top: 10px;
28 | }
29 |
30 | .editor-desc{
31 | display: flex;
32 | }
33 | .editor-desc .badge{
34 | margin: 0 5px;
35 | }
36 |
37 | .platform-active {
38 | color: var(--color-text-brand) !important;
39 | background: var(--q-primary);
40 | }
41 |
42 | :deep(.q-editor__content img) {
43 | max-width: 200px;
44 | }
45 |
46 | .q-banner {
47 | margin-top: 8px;
48 | font-size: 12px;
49 | }
50 |
51 | .tag-label-wrapper {
52 | max-height: 200px;
53 | overflow: auto;
54 | border: 1px solid var(--color-3);
55 | width: 100%;
56 | }
57 |
--------------------------------------------------------------------------------
/frontend/src/css/playnite/layout.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --content-backgorund-image: "";
3 | --content-backgorund-mask: "";
4 | --content-backgorund-size: "";
5 | --content-backgorund-repeat: "";
6 | --base-font-size: "";
7 | }
8 |
9 | .bg {
10 | background-color: var(--color-2);
11 | background-image: var(--content-backgorund-image);
12 | background-repeat: var(--content-backgorund-repeat);
13 | background-size: var(--content-backgorund-size);
14 | transition: background-image 0.2s linear;
15 | font-size: var(--base-font-size);
16 | }
17 |
18 | .bg:before {
19 | content: "";
20 | display: block;
21 | position: absolute;
22 | width: 100%;
23 | height: calc(100vh);
24 | transition: background-image 0.5s linear;
25 | }
26 |
27 | .bg-mask {
28 | background-image: var(--content-backgorund-mask);
29 | background-repeat: repeat;
30 | position: absolute;
31 | top: 0;
32 | left: 0;
33 | width: 100%;
34 | height: 100%;
35 | z-index: 0;
36 | }
37 |
38 | .fuzzy1:before {
39 | backdrop-filter: blur(1px);
40 | }
41 |
42 | .fuzzy3:before {
43 | backdrop-filter: blur(3px);
44 | }
45 |
46 | .fuzzy5:before {
47 | backdrop-filter: blur(5px);
48 | }
49 |
50 | .fuzzy7:before {
51 | backdrop-filter: blur(7px);
52 | }
53 |
54 | .fuzzy9:before {
55 | backdrop-filter: blur(9px);
56 | }
57 |
58 | .fuzzy15:before {
59 | backdrop-filter: blur(15px);
60 | }
61 |
62 | .fuzzy30:before {
63 | backdrop-filter: blur(30px);
64 | }
65 |
66 | .fuzzy45:before {
67 | backdrop-filter: blur(45px);
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/frontend/src/css/playnite/platform.css:
--------------------------------------------------------------------------------
1 | .close-btn {
2 | position: absolute;
3 | top: 10px;
4 | right: 10px;
5 | width: 80px;
6 | height: 80px;
7 | font-size: 24px;
8 | border-radius: 50%;
9 | background: var(--color-1);
10 | color: #FFF;
11 | z-index: 3;
12 | }
13 |
14 |
15 | .wrapper {
16 | width: 100%;
17 | height: 100vh;
18 | background-color: rgba(0, 0, 0, 0.9);
19 | }
20 |
21 | /* 平台栏 */
22 | .platform-scroll-area{
23 | width: 100%;
24 | height: 100%;
25 | }
26 |
27 | .platform-list {
28 | width: 100%;
29 | height: 100%;
30 | display: flex;
31 | justify-content: center;
32 | }
33 |
34 | .platform-item {
35 | width: 200px;
36 | height: 80vh;
37 | display: block;
38 | text-align: center;
39 | margin-top: 10vh;
40 | padding: 0;
41 | position: relative;
42 | }
43 |
44 | .platform-ico-active {
45 | animation: zoomInOut 2s infinite; /* 动画名称,每次动画持续2秒,并且无限循环 */
46 | }
47 |
48 | .platform-item {
49 | display: block;
50 | }
51 |
52 | .platform-title {
53 | font-size: 36px;
54 | text-shadow: 2px 0 2px #000;
55 | font-weight: bolder;
56 | }
57 |
58 | .platform-logo {
59 | width: 72px;
60 | height: 72px;
61 | }
62 |
63 | .platform-active {
64 | background-color: var(--color-2);
65 | border-radius: 15px;
66 | background-repeat: repeat;
67 | background-size: cover;
68 | background-position: center center;
69 | position: relative;
70 | background-image: var(--content-backgorund-image);
71 | overflow: hidden;
72 | }
73 |
74 | .platform-content{
75 | position: absolute;
76 | z-index: 2;
77 | width: 100%;
78 | height: 100%;
79 | margin-top: 10vh;
80 |
81 | }
82 |
83 | .platform-bg{
84 | position: absolute;
85 | z-index: 1;
86 | width: 100%;
87 | height: 100%;
88 | backdrop-filter: blur(5px);
89 | }
90 |
91 |
92 | .menu-scroll-area {
93 | margin-top: 6vh;
94 | height: 53vh;
95 | }
96 |
97 | .menu-list {
98 | display: block;
99 | width: 100%;
100 | }
101 |
102 | .menu-list .q-item {
103 | text-align: center !important;
104 | display: block;
105 | padding: 0;
106 | height: 60px;
107 | line-height: 60px;
108 | font-size: 16px;
109 | }
110 |
111 | .menu-active {
112 | background: var(--q-primary);
113 | color: #FFF;
114 | }
115 |
--------------------------------------------------------------------------------
/frontend/src/css/playnite/romlistBar.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | .rom-wrapper {
4 | }
5 |
6 | .rom-scroll {
7 | height: calc(100vh - 30px) !important;
8 | width: 100%;
9 | max-width: 100%;
10 | }
11 |
12 | .active {
13 | background: rgba(255,255,255,0.2);
14 | }
15 |
16 |
17 | .module-title {
18 | text-align: center;
19 | word-break: break-all;
20 | white-space: pre-wrap;
21 | background: var(--color-1);
22 | }
23 |
24 | .image-error {
25 | background: var(--color-0);
26 | color: var(--color-6);
27 | }
28 |
29 | .rom-list {
30 |
31 | }
32 |
33 | .active {
34 | background: var(--q-primary);
35 | }
36 |
37 | .rom-list .rom-img{
38 | width: 20px;
39 | }
40 |
41 | .rom-block {
42 | display: flex;
43 | flex-wrap: wrap;
44 | }
45 |
46 |
47 | .img-direction-0 {
48 | }
49 |
50 | .img-direction-1 {
51 | aspect-ratio: 4/3;
52 | }
53 |
54 | .img-direction-2 {
55 | aspect-ratio: 3/4;
56 | }
57 |
58 | .img-direction-3 {
59 | aspect-ratio: 16/9;
60 | }
61 |
62 | .img-direction-4 {
63 | aspect-ratio: 9/16;
64 | }
65 |
66 | .img-direction-5 {
67 | aspect-ratio: 3/2;
68 | }
69 |
70 | .img-direction-6 {
71 | aspect-ratio: 2/3;
72 | }
73 |
74 | .img-direction-7 {
75 | aspect-ratio: 1/1;
76 | }
77 |
--------------------------------------------------------------------------------
/frontend/src/css/romManage.css:
--------------------------------------------------------------------------------
1 |
2 | .wrapper{
3 | margin: 0 auto;
4 | }
5 |
6 | :deep(::-webkit-scrollbar) {
7 | width: 20px !important;
8 | height: 10px !important;;
9 | }
10 |
11 | .rom-list {
12 | background: var(--color-1);
13 | }
14 |
15 | :deep(.q-table th) {
16 | text-align: left;
17 | font-weight: bold;
18 | color: var(--color-9);
19 | }
20 |
21 | :deep(.q-table td) {
22 | text-align: left;
23 | }
24 |
25 | :deep(.q-table th:first-child) {
26 | position: sticky;
27 | left: 0;
28 | z-index: 1;
29 | background: var(--color-3);
30 | color: var(--color-15);
31 | }
32 |
33 | :deep(.q-table td:first-child) {
34 | position: sticky;
35 | left: 0;
36 | z-index: 1;
37 | background: var(--color-3);
38 | color: var(--color-15);
39 | }
40 |
--------------------------------------------------------------------------------
/frontend/src/css/tiny/layout.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --content-backgorund-image: "";
3 | --content-backgorund-size: "";
4 | --content-backgorund-repeat: "";
5 | --content-backgorund-mask: "";
6 | --base-font-size: "";
7 | }
8 |
9 | .bg {
10 | background-color: var(--color-2);
11 | background-image: var(--content-backgorund-image);
12 | background-repeat: var(--content-backgorund-repeat);
13 | background-size: var(--content-backgorund-size);
14 | transition: background-image 0.2s linear;
15 | }
16 |
17 | .bg:before {
18 | content: "";
19 | display: block;
20 | position: absolute;
21 | width: 100%;
22 | height: calc(100vh);
23 | transition: background-image 0.5s linear;
24 | }
25 |
26 | .bg-mask{
27 | background-image: var(--content-backgorund-mask);
28 | background-repeat: repeat;
29 | position: absolute;
30 | top: 0;
31 | left: 0;
32 | width: 100%;
33 | height: 100%;
34 | z-index: 0;
35 | }
36 |
37 | .fuzzy1:before {
38 | backdrop-filter: blur(1px);
39 | }
40 |
41 | .fuzzy3:before {
42 | backdrop-filter: blur(3px);
43 | }
44 |
45 | .fuzzy5:before {
46 | backdrop-filter: blur(5px);
47 | }
48 |
49 | .fuzzy7:before {
50 | backdrop-filter: blur(7px);
51 | }
52 |
53 | .fuzzy9:before {
54 | backdrop-filter: blur(9px);
55 | }
56 |
57 | .fuzzy15:before {
58 | backdrop-filter: blur(15px);
59 | }
60 |
61 | .fuzzy30:before {
62 | backdrop-filter: blur(30px);
63 | }
64 |
65 | .fuzzy45:before {
66 | backdrop-filter: blur(45px);
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/frontend/src/css/tiny/platform.css:
--------------------------------------------------------------------------------
1 | .close-btn {
2 | position: absolute;
3 | top: 10px;
4 | right: 10px;
5 | width: 80px;
6 | height: 80px;
7 | font-size: 24px;
8 | border-radius: 50%;
9 | background: var(--color-1);
10 | color: #FFF;
11 | z-index: 3;
12 | }
13 |
14 |
15 | .wrapper {
16 | width: 100%;
17 | height: 100vh;
18 | background-color: rgba(0, 0, 0, 0.9);
19 | }
20 |
21 | /* 平台栏 */
22 | .platform-scroll-area{
23 | width: 100%;
24 | height: 100%;
25 | }
26 |
27 | .platform-list {
28 | width: 100%;
29 | height: 100%;
30 | display: flex;
31 | justify-content: center;
32 | }
33 |
34 | .platform-item {
35 | width: 200px;
36 | height: 80vh;
37 | display: block;
38 | text-align: center;
39 | margin-top: 10vh;
40 | padding: 0;
41 | position: relative;
42 | }
43 |
44 | .platform-ico-active {
45 | animation: zoomInOut 2s infinite; /* 动画名称,每次动画持续2秒,并且无限循环 */
46 | }
47 |
48 | .platform-item {
49 | display: block;
50 | }
51 |
52 | .platform-title {
53 | font-size: 36px;
54 | text-shadow: 2px 0 2px #000;
55 | font-weight: bolder;
56 | }
57 |
58 | .platform-logo {
59 | width: 72px;
60 | height: 72px;
61 | }
62 |
63 | .platform-active {
64 | background-color: var(--color-2);
65 | border-radius: 15px;
66 | background-repeat: repeat;
67 | background-size: cover;
68 | background-position: center center;
69 | position: relative;
70 | background-image: var(--content-backgorund-image);
71 | overflow: hidden;
72 | }
73 |
74 | .platform-content{
75 | position: absolute;
76 | z-index: 2;
77 | width: 100%;
78 | height: 100%;
79 | margin-top: 10vh;
80 | }
81 |
82 | .platform-bg{
83 | position: absolute;
84 | z-index: 1;
85 | width: 100%;
86 | height: 100%;
87 | backdrop-filter: blur(5px);
88 | }
89 |
90 |
91 | .menu-scroll-area {
92 | margin-top: 6vh;
93 | height: 53vh;
94 | }
95 |
96 | .menu-list {
97 | display: block;
98 | width: 100%;
99 | }
100 |
101 | .menu-list .q-item {
102 | text-align: center !important;
103 | display: block;
104 | padding: 0;
105 | height: 60px;
106 | line-height: 60px;
107 | font-size: 16px;
108 | }
109 |
110 | .menu-active {
111 | background: var(--q-primary);
112 | color: #FFF;
113 | }
114 |
--------------------------------------------------------------------------------
/frontend/src/css/tiny/romlistBar.css:
--------------------------------------------------------------------------------
1 | .platform-title {
2 | position: absolute;
3 | right: 30px;
4 | top: 40px;
5 | line-height: 1em;
6 | font-size: 48px;
7 | opacity: 0.3;
8 | font-weight: bolder;
9 | }
10 |
11 | .rom-wrapper {
12 | width: 100%;
13 | height: calc(100vh - 30px);
14 | display: flex;
15 | align-items: center;
16 | justify-content: center;
17 | margin: 0 auto;
18 | position: relative;
19 | }
20 |
21 | .thumb-box {
22 | width: 30%;
23 | height: 80%;
24 | display: flex;
25 | align-content: center;
26 | justify-items: right;
27 | justify-content: right;
28 | justify-self: right;
29 | }
30 |
31 |
32 | .carousel {
33 | background: none !important;
34 | background-position: top center;
35 | width: 100%;
36 | height: 100%;
37 | }
38 |
39 | .carousel .q-carousel__slide {
40 | background-size: contain !important;
41 | background: no-repeat center center transparent;
42 | height: 200px;
43 | width: 100%;
44 | padding: 0;
45 | display: flex;
46 | align-items: center;
47 | justify-content: center;
48 | }
49 |
50 | .carousel-img {
51 | width: 100%;
52 | height: 100%;
53 | max-width: 800px;
54 | }
55 |
56 | .no-carousel {
57 | text-align: center;
58 | color: var(--color-6);
59 | right: 50px;
60 | opacity: 0.8;
61 | width: 100%;
62 | height: 100%;
63 | display: flex;
64 | align-items: center;
65 | justify-content: center;
66 | background: rgba(0, 0, 0, 0.4);
67 | }
68 |
69 | .page-box {
70 | margin: 0 20px;
71 | display: flex;
72 | flex-wrap: wrap;
73 | justify-content: center;
74 | align-items: center;
75 | text-align: center;
76 | }
77 |
78 | .page-box .page-num {
79 | width: 100%;
80 | line-height: 2em;
81 | font-size: 18px;
82 | }
83 |
84 | .page-box .page-num.top {
85 | border-bottom: 1px solid #FFF;
86 | }
87 |
88 | .rom-box {
89 | width: 30%;
90 | height: 80%;
91 | }
92 |
93 | .rom-scroll {
94 | height: 100%;
95 | width: 100%;
96 | max-width: 100%;
97 | }
98 |
99 | .image-error {
100 | background: var(--color-0);
101 | color: var(--color-6);
102 | }
103 |
104 | .rom-title{}
105 |
106 | .rom-title .master-title {
107 | font-size: var(--base-font-size);
108 | white-space: nowrap;
109 | overflow: hidden;
110 | text-overflow: ellipsis;
111 | }
112 |
113 | .rom-title .sub-title {
114 | font-size: calc(var(--base-font-size) - 5px);
115 | color: var(--color-6);
116 | white-space: nowrap;
117 | overflow: hidden;
118 | text-overflow: ellipsis;
119 | display: block;
120 | }
121 |
122 | .rom-list .active {
123 | background: rgba(255, 255, 255, 0.4);
124 | }
125 |
126 | .rom-list .active .sub-title {
127 | color: var(--color-2);
128 | }
129 |
130 | .rom-list .rom-img {
131 | width: 48px;
132 | height: 48px;
133 | }
134 |
135 | .rom-list .q-item{
136 | padding: 10px;
137 | height: 65px;
138 | }
139 |
140 | .empty-romlist{
141 | font-size: 28px;
142 | opacity: 0.5;
143 | }
--------------------------------------------------------------------------------
/frontend/src/css/transitions.css:
--------------------------------------------------------------------------------
1 | /* scale */
2 | .scale-enter-active, .scale-leave-active {
3 | transition: all 0.5s ease;
4 | }
5 | .scale-enter-from, .scale-leave-to {
6 | opacity: 0;
7 | transform: scale(0.9);
8 | }
9 |
10 | /* scale-slide */
11 | .scale-slide-enter-active, .scale-slide-leave-active {
12 | position: absolute;
13 | transition: all 0.85s ease;
14 | }
15 | .scale-slide-enter-from {
16 | left: -100%;
17 | }
18 | .scale-slide-enter-to {
19 | left: 0;
20 | }
21 | .scale-slide-leave-from {
22 | transform: scale(1);
23 | }
24 | .scale-slide-leave-to {
25 | transform: scale(0.8);
26 | }
27 |
28 | /*fade*/
29 | .fade-enter-active, .fade-leave-active {
30 | transition: opacity 0.5s ease;
31 | }
32 | .fade-enter-from, .fade-leave-to {
33 | opacity: 0;
34 | }
35 |
36 | /* slide */
37 | /*.slide-enter-active, .slide-leave-active {
38 | transition: all 0.75s ease-out;
39 | }
40 | .slide-enter-to {
41 | position: absolute;
42 | right: 0;
43 | }
44 | .slide-enter-from {
45 | position: absolute;
46 | right: -100%;
47 | }
48 | .slide-leave-to {
49 | position: absolute;
50 | left: -100%;
51 | }
52 | .slide-leave-from {
53 | position: absolute;
54 | left: 0;
55 | }*/
56 |
57 |
58 |
59 |
60 | .fade-enter-active,
61 | .fade-leave-active {
62 | transition: opacity 0.75s ease;
63 | }
64 | .fade-enter,
65 | .fade-leave-active {
66 | opacity: 0;
67 | }
68 | .child-view {
69 | position: absolute;
70 | transition: all 0.75s cubic-bezier(0.55, 0, 0.1, 1);
71 | }
72 | .slide-left-enter,
73 | .slide-right-leave-active {
74 | opacity: 0;
75 | -webkit-transform: translate(30px, 0);
76 | transform: translate(30px, 0);
77 | }
78 | .slide-left-leave-active,
79 | .slide-right-enter {
80 | opacity: 0;
81 | -webkit-transform: translate(-30px, 0);
82 | transform: translate(-30px, 0);
83 | }
84 |
85 | @keyframes zoomInOut {
86 | 0%, 100% {
87 | transform: scale(1);
88 | }
89 | 50% {
90 | transform: scale(1.2);
91 | }
92 | }
--------------------------------------------------------------------------------
/frontend/src/env.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | declare namespace NodeJS {
4 | interface ProcessEnv {
5 | NODE_ENV: string;
6 | VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract' | undefined;
7 | VUE_ROUTER_BASE: string | undefined;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/src/i18n/en-US/index.ts:
--------------------------------------------------------------------------------
1 | // This is just an example,
2 | // so you can safely delete all index props below
3 |
4 | export default {
5 | failed: 'Action failed',
6 | success: 'Action was successful'
7 | };
8 |
--------------------------------------------------------------------------------
/frontend/src/i18n/index.ts:
--------------------------------------------------------------------------------
1 | import enUS from './en-US';
2 |
3 | export default {
4 | 'en-US': enUS
5 | };
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/ErrorNotFound.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 404
6 |
7 |
8 |
9 | Oops. Nothing here...
10 |
11 |
12 |
21 |
22 |
23 |
24 |
25 |
28 |
--------------------------------------------------------------------------------
/frontend/src/pages/classic/HeaderBarLogo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ titleInfo.title }}
5 | {{ titleInfo.subTitle }}
6 |
7 |
8 |
9 |
45 |
46 |
90 |
--------------------------------------------------------------------------------
/frontend/src/pages/configPlatform/RombaseAlias.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{lang.tipRombaseAlias}}
5 |
6 |
7 |
8 |
9 |
11 |
12 |
13 |
15 |
16 |
17 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
83 |
--------------------------------------------------------------------------------
/frontend/src/pages/home/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
70 |
71 |
80 |
--------------------------------------------------------------------------------
/frontend/src/pages/playnite/modules/EventKeyboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
48 |
49 |
51 |
--------------------------------------------------------------------------------
/frontend/src/pages/romManage/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{lang.romManage}}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
67 |
--------------------------------------------------------------------------------
/frontend/src/pages/test/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 | 将文件拖拽到此处上传
7 |
8 |
9 |
10 |
11 |
12 |
89 |
90 |
96 |
--------------------------------------------------------------------------------
/frontend/src/pages/tiny/configUI/UiConst.vue:
--------------------------------------------------------------------------------
1 |
65 |
--------------------------------------------------------------------------------
/frontend/src/pages/tiny/modules/EventKeyboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
35 |
36 |
38 |
--------------------------------------------------------------------------------
/frontend/src/quasar.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | // Forces TS to apply `@quasar/app-vite` augmentations of `quasar` package
4 | // Removing this would break `quasar/wrappers` imports as those typings are declared
5 | // into `@quasar/app-vite`
6 | // As a side effect, since `@quasar/app-vite` reference `quasar` to augment it,
7 | // this declaration also apply `quasar` own
8 | // augmentations (eg. adds `$q` into Vue component context)
9 | ///
10 |
--------------------------------------------------------------------------------
/frontend/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import { route } from 'quasar/wrappers';
2 | import {
3 | createMemoryHistory,
4 | createRouter,
5 | createWebHashHistory,
6 | createWebHistory,
7 | } from 'vue-router';
8 |
9 | import routes from './routes';
10 |
11 | /*
12 | * If not building with SSR mode, you can
13 | * directly export the Router instantiation;
14 | *
15 | * The function below can be async too; either use
16 | * async/await or return a Promise which resolves
17 | * with the Router instance.
18 | */
19 |
20 | export default route(function (/* { store, ssrContext } */) {
21 | const createHistory = process.env.SERVER
22 | ? createMemoryHistory
23 | : (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory);
24 |
25 | const Router = createRouter({
26 | scrollBehavior: () => ({ left: 0, top: 0 }),
27 | routes,
28 |
29 | // Leave this as is and make changes in quasar.conf.js instead!
30 | // quasar.conf.js -> build -> vueRouterMode
31 | // quasar.conf.js -> build -> publicPath
32 | history: createHistory(process.env.VUE_ROUTER_BASE),
33 | });
34 |
35 | return Router;
36 | });
37 |
--------------------------------------------------------------------------------
/frontend/src/router/routes.ts:
--------------------------------------------------------------------------------
1 | import {RouteRecordRaw} from 'vue-router';
2 |
3 | const routes: RouteRecordRaw[] = [
4 | {
5 | path: '/',
6 | component: () => import('pages/home/Layout.vue'),
7 | meta: {
8 | refreshPage: true
9 | }
10 | },
11 | {
12 | path: '/default',
13 | component: () => import('pages/classic/Layout.vue'),
14 | meta: {
15 | refreshPage: true
16 | }
17 | },
18 | {
19 | path: '/playnite',
20 | component: () => import('pages/playnite/Layout.vue'),
21 | meta: {
22 | refreshPage: true
23 | }
24 | },
25 | {
26 | path: '/tiny',
27 | component: () => import('pages/tiny/Layout.vue'),
28 | meta: {
29 | refreshPage: true
30 | }
31 | },
32 | {
33 | path: '/config',
34 | component: () => import('pages/config/Layout.vue'),
35 | meta: {
36 | refreshPage: true
37 | },
38 | },
39 | {
40 | path: '/platform',
41 | component: () => import('pages/configPlatform/Layout.vue'),
42 | meta: {
43 | refreshPage: true
44 | },
45 | },
46 | {
47 | path: '/classic/ui',
48 | component: () => import('pages/classic/configUI/Layout.vue'),
49 | meta: {
50 | refreshPage: true
51 | },
52 | },
53 | {
54 | path: '/playnite/ui',
55 | component: () => import('pages/playnite/configUI/Layout.vue'),
56 | meta: {
57 | refreshPage: true
58 | },
59 | },
60 | {
61 | path: '/tiny/ui',
62 | component: () => import('pages/tiny/configUI/Layout.vue'),
63 | meta: {
64 | refreshPage: true
65 | },
66 | },
67 | {
68 | path: '/romManage',
69 | component: () => import('pages/romManage/Layout.vue'),
70 | meta: {
71 | refreshPage: true
72 | },
73 | },
74 | {
75 | path: '/test',
76 | component: () => import('pages/test/Layout.vue'),
77 | meta: {
78 | refreshPage: true
79 | },
80 | },
81 | // Always leave this as last one,
82 | // but you can also remove it
83 | {
84 | path: '/:catchAll(.*)*',
85 | component: () => import('pages/ErrorNotFound.vue'),
86 | },
87 | ];
88 |
89 | export default routes;
90 |
--------------------------------------------------------------------------------
/frontend/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | ///
4 |
5 | // Mocks all files ending in `.vue` showing them as plain Vue instances
6 | declare module '*.vue' {
7 | import type { DefineComponent } from 'vue';
8 | const component: DefineComponent<{}, {}, any>;
9 | export default component;
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/src/stores/example-store.ts:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia';
2 |
3 | export const useCounterStore = defineStore('counter', {
4 | state: () => ({
5 | counter: 0,
6 | }),
7 | getters: {
8 | doubleCount: (state) => state.counter * 2,
9 | },
10 | actions: {
11 | increment() {
12 | this.counter++;
13 | },
14 | },
15 | });
16 |
--------------------------------------------------------------------------------
/frontend/src/stores/index.ts:
--------------------------------------------------------------------------------
1 | import { store } from 'quasar/wrappers'
2 | import { createPinia } from 'pinia'
3 | import { Router } from 'vue-router';
4 |
5 | /*
6 | * When adding new properties to stores, you should also
7 | * extend the `PiniaCustomProperties` interface.
8 | * @see https://pinia.vuejs.org/core-concepts/plugins.html#typing-new-store-properties
9 | */
10 | declare module 'pinia' {
11 | export interface PiniaCustomProperties {
12 | readonly router: Router;
13 | }
14 | }
15 |
16 | /*
17 | * If not building with SSR mode, you can
18 | * directly export the Store instantiation;
19 | *
20 | * The function below can be async too; either use
21 | * async/await or return a Promise which resolves
22 | * with the Store instance.
23 | */
24 |
25 | export default store((/* { ssrContext } */) => {
26 | const pinia = createPinia()
27 |
28 | // You can add Pinia plugins here
29 | // pinia.use(SomePiniaPlugin)
30 |
31 | return pinia
32 | })
33 |
--------------------------------------------------------------------------------
/frontend/src/stores/store-flag.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // THIS FEATURE-FLAG FILE IS AUTOGENERATED,
3 | // REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
4 | import "quasar/dist/types/feature-flag";
5 |
6 | declare module "quasar/dist/types/feature-flag" {
7 | interface QuasarFeatureFlags {
8 | store: true;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@quasar/app-vite/tsconfig-preset",
3 | "compilerOptions": {
4 | "baseUrl": "."
5 | }
6 | }
--------------------------------------------------------------------------------
/frontend/vite.config.js:
--------------------------------------------------------------------------------
1 | import {defineConfig} from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [vue()]
7 | })
8 |
9 | /*
10 | export index ({
11 | build:{
12 | rollupOptions:{
13 | external(source, importer, isResolved) {
14 | console.log(source, importer. isResolved);
15 | // 判断是否是/assets-’开头的路径,如果是则认为是外部依赖
16 | console.log("inininininiini")
17 | console.log(source)
18 | return source.startswith("assets-");
19 |
20 | }
21 | }
22 | }
23 | })*/
24 |
--------------------------------------------------------------------------------
/frontend/vue.config.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/frontend/vue.config.js
--------------------------------------------------------------------------------
/main_test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 | "simUI/config"
8 | "simUI/constant"
9 | "simUI/db"
10 | "simUI/utils"
11 | "strings"
12 | "testing"
13 | )
14 |
15 | func Program() {
16 |
17 | aa := "/ASS2ET/ASDFASDF"
18 | fmt.Println(strings.HasPrefix(aa, "/ASSET"))
19 | }
20 |
21 | func TestMain(m *testing.M) {
22 |
23 | rootpath, _ := filepath.Abs(filepath.Dir(os.Args[0]))
24 |
25 | //测试环境配置
26 | if utils.FileExists(".env") {
27 | rootpath, _ = utils.ReadFile(".env", true)
28 | constant.DEV = true
29 | }
30 |
31 | rootpath = strings.ReplaceAll(rootpath, "\\", "/")
32 | constant.ROOT_PATH = rootpath + "/" //当前软件的绝对路径
33 | constant.CACHE_PATH = rootpath + "/cache/" //缓存路径
34 | constant.CACHE_UNZIP_PATH = rootpath + "/cache/unzip/" //解压缓存路径
35 | constant.LANG_PATH = rootpath + "/language/" //语言目录
36 | constant.CACHE_UNOWNED_PATH = constant.CACHE_PATH + "unowned/" //无效资源备份目录
37 |
38 | //创建数据库文件
39 | if err := db.CreateDbFile(); err != nil {
40 | //系统alert提示
41 | utils.WriteLog(err.Error())
42 | utils.DialogError("Error", err.Error())
43 | return
44 | }
45 |
46 | //连接数据库
47 | if err := db.Conn(); err != nil {
48 | //系统alert提示
49 | utils.WriteLog("database connect faild!")
50 | utils.DialogError("Error", err.Error())
51 | return
52 | }
53 |
54 | //初始化配置
55 | config.Cfg = &config.ConfStruct{}
56 | errConf := config.InitConf()
57 | if errConf != nil {
58 | utils.DialogError("Error", errConf.Error())
59 | fmt.Println(errConf)
60 | os.Exit(1)
61 | return
62 | }
63 |
64 | //数据库升级
65 | //modules.UpgradeDB()
66 |
67 | if len(config.Cfg.Lang) == 0 {
68 | utils.WriteLog("没有找到语言文件或语言文件为空\n language files is not exists")
69 | utils.DialogError("Error", "没有找到语言文件或语言文件为空\n language files is not exists")
70 | return
71 | }
72 |
73 | //游戏手柄
74 | //modules.CheckJoystick()
75 |
76 | //软件启动时检测升级
77 | //modules.BootCheckUpgrade()
78 |
79 | //读软件配置
80 |
81 | Program()
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/modules/audio.go:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | // 读取rom展示图列表
4 | /*func GetGameAudios(id uint64) ([]GameAudio, error) {
5 |
6 | //游戏游戏详细数据
7 | info, err := (&db.Rom{}).GetById(id)
8 | lists := []GameAudio{}
9 | if err != nil {
10 | return lists, err
11 | }
12 |
13 | //资源路径
14 | resPath := components.GetResPathByType("audio", info.Platform)
15 |
16 | //音频列表
17 | files, _ := components.GetAudioRes(resPath, info.RomName)
18 |
19 | //子资源文件名排序
20 | sort.Slice(files, func(i, j int) bool {
21 | return strings.ToLower(files[i]) < strings.ToLower(files[j])
22 | })
23 |
24 | if len(files) > 0 {
25 | for _, v := range files {
26 | s := GameAudio{
27 | Name: utils.GetFileName(v),
28 | Path: utils.WailsPathEncode(v),
29 | }
30 | lists = append(lists, s)
31 | }
32 | }
33 |
34 | return lists, nil
35 | }*/
36 |
37 | /**
38 | * 编辑音频
39 | **/
40 | /*func UpdateGameAudio(paths []string) ([]string, error) {
41 | return nil, nil
42 | }
43 | */
44 | /**
45 | * 添加音频
46 | **/
47 | /*func AddGameAudio(id uint64, paths []string) ([]string, error) {
48 | vo, _ := (&db.Rom{}).GetById(id)
49 | platformInfo := (&db.Platform{}).GetVOById(vo.Platform, false)
50 |
51 | if platformInfo.AudioPath == "" {
52 | return nil, errors.New(config.Cfg.Lang["AudioMenuCanNotBeEmpty"])
53 | }
54 |
55 | //检查创建目录
56 | _ = utils.CreateDir(platformInfo.AudioPath)
57 |
58 | result := []string{}
59 | for _, p := range paths {
60 | p = utils.ToAbsPath(p, "")
61 |
62 | if utils.FileExists(p) {
63 | continue
64 | }
65 |
66 | name := utils.GetFileNameAndExt(p)
67 | dst := platformInfo.AudioPath + "/" + vo.RomName + "/" + name
68 | if err := utils.FileCopy(p, dst); err != nil {
69 | return nil, err
70 | }
71 | result = append(result, utils.WailsPathEncode(dst))
72 | }
73 |
74 | return result, nil
75 | }*/
76 |
77 | /**
78 | * 音频改名
79 | **/
80 | /*func RenameGameAudio(pth string, newName string) (string, error) {
81 | src := utils.WailsPathDecode(pth)
82 | dst := utils.ReplaceFileNameByPath(src, newName)
83 | if utils.FileExists(dst) {
84 | return "", errors.New("文件已存在")
85 | }
86 | err := utils.FileMove(src, dst)
87 | if err != nil {
88 | return "", err
89 | }
90 | return dst, nil
91 | }
92 | */
93 | /**
94 | * 删除音频
95 | **/
96 | /*func DelGameAudio(pth string) error {
97 | f := utils.WailsPathDecode(pth)
98 | return utils.FileDelete(f)
99 |
100 | }*/
101 |
--------------------------------------------------------------------------------
/modules/image.go:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "errors"
5 | "simUI/components"
6 | "simUI/db"
7 | "simUI/utils"
8 | "strings"
9 | "time"
10 | )
11 |
12 | var imageWidth = 320
13 | var quality = 80
14 |
15 | func CreateRomResByBase64(id uint64, resType string, slaveRes uint8, fileType string, base64Str string) (string, error) {
16 |
17 | fileTypes := strings.Split(fileType, "/")
18 | if fileTypes[0] != "image" && fileTypes[0] != "video" {
19 | return "", errors.New("文件类型错误")
20 | }
21 |
22 | rom, err := (&db.Rom{}).GetById(id)
23 | if err != nil {
24 | return "", err
25 | }
26 |
27 | resPath := components.GetResPathByType(resType, rom.Platform)
28 | pth := ""
29 | tm := utils.ToString(time.Now().Unix())
30 | if slaveRes == 0 {
31 | pth = resPath + "/" + rom.RomName + "." + fileTypes[1]
32 | } else {
33 | pth = resPath + "/" + rom.RomName + "/" + tm + "." + fileTypes[1]
34 | }
35 |
36 | if err = utils.Base64ToFile(base64Str, pth); err != nil {
37 | return "", err
38 | }
39 |
40 | //return utils.WailsPathEncode(pth), nil
41 | return pth, nil
42 |
43 | }
44 |
45 | func CreateImg() {
46 | /** 判断是不是图片 */
47 | /*in := "/Users/frontlon/go/src/wails/sim-ui-pulsar/in.jpg"
48 | format := utils.GetFileExt(in)
49 | out := "/Users/frontlon/go/src/wails/sim-ui-pulsar/out" + format
50 |
51 | err := utils.CreateThumbnail(in, out)
52 |
53 | fmt.Println("-=-=-")
54 | fmt.Println(err)
55 |
56 | if err != nil {
57 | fmt.Println(err)
58 | }
59 | */
60 | }
61 |
62 | //func CreateOptimizedImage(platform uint32, opt string) error {
63 | //
64 | // paths := components.GetResPath(platform, 0)
65 | // path := paths[opt]
66 | //
67 | // platformInfo := (&db.Platform{}).GetVOById(platform, false)
68 | // outputPath := platformInfo.OptimizedPath
69 | //
70 | // //先删除原文件
71 | // utils.DeleteDir(outputPath)
72 | // utils.CreateDir(outputPath)
73 | //
74 | // //读取文件总数
75 | // //files, _ := ioutil.ReadDir(path)
76 | // //fileCount := len(files)
77 | // i := 0
78 | // filepath.Walk(path, func(p string, f os.FileInfo, err error) error {
79 | // if f == nil {
80 | // return err
81 | // }
82 | // if f.IsDir() { /** 是否是目录 */
83 | // return nil
84 | // }
85 | //
86 | // /** 判断是不是图片 */
87 | // format := utils.GetFileExt(p)
88 | //
89 | // outputPath := outputPath + "/" + utils.GetFileNameAndExt(p)
90 | // if p != "" {
91 | // err := utils.ImageCompress(
92 | // func() (io.Reader, error) {
93 | // return os.Open(p)
94 | // },
95 | // func() (*os.File, error) {
96 | // return os.Open(p)
97 | // },
98 | // outputPath,
99 | // quality,
100 | // imageWidth,
101 | // format)
102 | //
103 | // if err != nil {
104 | // return err
105 | // }
106 | // }
107 | //
108 | // /*if i%10 == 0 {
109 | // utils.Loading("[2/3]已生成("+utils.ToString(i)+" / "+utils.ToString(fileCount)+")", config.Cfg.Platform[platform].Name)
110 | // }*/
111 | //
112 | // i++
113 | // return nil
114 | // })
115 | //
116 | // //数据更新完成后,页面回调,更新页面DOM
117 | // /*if _, err := utils.Window.Call("CB_createOptimizedCache"); err != nil {
118 | // fmt.Print(err)
119 | // }*/
120 | //
121 | // return nil
122 | //}
123 |
--------------------------------------------------------------------------------
/modules/others.go:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "simUI/server"
5 | )
6 |
7 | // 启动上传服务
8 | func StartUploadServer() string {
9 | if server.Addr == "" {
10 | server.StartHttpServer()
11 | }
12 | return server.Addr
13 | }
14 |
--------------------------------------------------------------------------------
/modules/romConfig.go:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "simUI/config"
5 | "simUI/db"
6 | )
7 |
8 | // 设为隐藏
9 | func SetHide(id uint64, hide uint8) error {
10 |
11 | //数据库中读取rom详情
12 | rom, err := (&db.Rom{}).GetById(id)
13 | if err != nil {
14 | return err
15 | }
16 |
17 | //更新数据
18 | if err = config.SetRomSettingOneField(rom.Platform, rom.RomName, "Hide", hide, true); err != nil {
19 | return err
20 | }
21 |
22 | err = (&db.Rom{RomName: rom.RomName, Hide: hide}).UpdateHide()
23 | if err != nil {
24 | return err
25 | }
26 |
27 | return nil
28 | }
29 |
30 | // 设为喜爱
31 | func SetFavorite(id uint64, fav uint8) error {
32 |
33 | //数据库中读取rom详情
34 | rom, err := (&db.Rom{}).GetById(id)
35 | if err != nil {
36 | return err
37 | }
38 |
39 | //更新数据
40 | if err = config.SetRomSettingOneField(rom.Platform, rom.RomName, "Favorite", fav, true); err != nil {
41 | return err
42 | }
43 |
44 | err = (&db.Rom{RomName: rom.RomName, Favorite: fav}).UpdateFavorite()
45 | if err != nil {
46 | return err
47 | }
48 |
49 | return nil
50 | }
51 |
--------------------------------------------------------------------------------
/modules/romSimSetting.go:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "fmt"
7 | "simUI/config"
8 | "simUI/db"
9 | "simUI/utils"
10 | )
11 |
12 | // 读取一个ROM模拟器配置
13 | func GetRomSimSettingById(romId uint64, simId uint32) (db.RomSimSetting, error) {
14 |
15 | resp := db.RomSimSetting{}
16 |
17 | romInfo, _ := (&db.Rom{}).GetById(romId)
18 | if romInfo == nil {
19 | return resp, errors.New(config.Cfg.Lang["romIsNotExists"])
20 | }
21 |
22 | simInfo, _ := (&db.Simulator{}).GetById(simId)
23 |
24 | if simInfo == nil {
25 | return resp, errors.New(config.Cfg.Lang["simIsNotExists"])
26 | }
27 |
28 | simName := utils.GetFileName(simInfo.Path)
29 |
30 | data := (&db.Rom{}).ConvertRomSimple(romInfo, []*db.Rom{})
31 |
32 | if _, ok := data.SimSetting[simName]; ok {
33 | resp = db.RomSimSetting{
34 | Cmd: data.SimSetting[simName].Cmd,
35 | RunBefore: data.SimSetting[simName].RunBefore,
36 | RunAfter: data.SimSetting[simName].RunAfter,
37 | Unzip: data.SimSetting[simName].Unzip,
38 | }
39 | }
40 |
41 | return resp, nil
42 | }
43 |
44 | // 更新ROM模拟器配置
45 | func UpdateRomSimSetting(romId uint64, simId uint32, data *db.RomSimSetting) error {
46 |
47 | data.RunBefore = utils.ToRelPath(data.RunBefore, "")
48 | data.RunAfter = utils.ToRelPath(data.RunAfter, "")
49 |
50 | //读rom信息
51 | romInfo, _ := (&db.Rom{}).GetById(romId)
52 | if romInfo == nil {
53 | return errors.New(config.Cfg.Lang["romIsNotExists"])
54 | }
55 | romVo := (&db.Rom{}).ConvertRomSimple(romInfo, []*db.Rom{})
56 | romSetting := map[uint32]db.RomSimSetting{}
57 | if romInfo.SimSetting != "" {
58 | json.Unmarshal([]byte(romInfo.SimSetting), &romSetting)
59 | }
60 |
61 | //读模拟器信息
62 | simInfo, _ := (&db.Simulator{}).GetById(simId)
63 | if simInfo == nil {
64 | return errors.New(config.Cfg.Lang["simIsNotExists"])
65 | }
66 |
67 | simName := utils.GetFileName(simInfo.Path)
68 | romVo.SimSetting[simName] = data
69 |
70 | //清除空数据
71 | for k, v := range romVo.SimSetting {
72 | if v.RunBefore == "" && v.RunAfter == "" && v.Cmd == "" && v.Unzip == "" {
73 | delete(romVo.SimSetting, k)
74 | }
75 | }
76 |
77 | create, _ := json.Marshal(romVo.SimSetting)
78 | if err := (&db.Rom{}).UpdateSimSettingById(romId, string(create)); err != nil {
79 | fmt.Println("UpdateRomSimSetting", err)
80 | return err
81 | }
82 |
83 | //更新到配置文件
84 | config.SetRomSettingOneField(romInfo.Platform, romInfo.RomName, "SimSetting", string(create), true)
85 |
86 | return nil
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/modules/rombaseAlias.go:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "simUI/config"
5 | "simUI/db"
6 | )
7 |
8 | // 这些是必须返回出一些别名配置
9 | var rombaseAliasList = []string{"OtherA", "OtherB", "OtherC", "OtherD"}
10 |
11 | // 根据类型读取数据
12 | func GetRomBaseAliasByPlatform(platform uint32) (map[string]string, error) {
13 | voMap, err := (&db.RombaseAlias{}).GetByPlatform(platform)
14 | if err != nil {
15 | voMap = map[string]string{}
16 | }
17 |
18 | for _, typ := range rombaseAliasList {
19 | if _, ok := voMap[typ]; !ok {
20 | voMap[typ] = config.Cfg.Lang["base"+typ]
21 | }
22 | }
23 |
24 | return voMap, nil
25 |
26 | }
27 |
28 | // 更新数据
29 | func UpdateRomBaseAlias(platform uint32, data map[string]string) error {
30 |
31 | existMap, _ := (&db.RombaseAlias{}).GetByPlatform(platform)
32 |
33 | for typ, alias := range data {
34 |
35 | //删除记录
36 | if alias == "" {
37 | (&db.RombaseAlias{
38 | Platform: platform,
39 | Type: typ,
40 | }).DeleteByType()
41 | continue
42 | }
43 |
44 | if _, ok := existMap[typ]; ok {
45 | //修改记录
46 | (&db.RombaseAlias{
47 | Platform: platform,
48 | Type: typ,
49 | Alias: alias,
50 | }).UpdateByType()
51 | } else {
52 | //新增记录
53 | (&db.RombaseAlias{
54 | Platform: platform,
55 | Type: typ,
56 | Alias: alias,
57 | }).Add()
58 | }
59 | }
60 | return nil
61 | }
62 |
--------------------------------------------------------------------------------
/modules/rombaseEnum.go:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "simUI/db"
5 | "strings"
6 | )
7 |
8 | // 读取rombase枚举
9 | func UpdateRomBaseEnum(t string, data []string) error {
10 |
11 | //先删除记录
12 | (&db.RombaseEnum{Type: t}).DeleteByType()
13 |
14 | if len(data) == 0 {
15 | return nil
16 | }
17 |
18 | create := []*db.RombaseEnum{}
19 | for k, v := range data {
20 | if strings.Trim(v, " ") == "" {
21 | continue
22 | }
23 | c := &db.RombaseEnum{}
24 | c.Type = t
25 | c.Name = v
26 | c.Sort = uint32(k) + 1
27 | create = append(create, c)
28 | }
29 | if err := (&db.RombaseEnum{}).BatchAdd(create); err != nil {
30 | return err
31 | }
32 | return nil
33 | }
34 |
35 | // 读取全部枚举数据
36 | func GetRomBaseEnum() (map[string][]string, error) {
37 | lists, err := (&db.RombaseEnum{}).GetAll()
38 | if err != nil {
39 | return nil, err
40 | }
41 | result := map[string][]string{}
42 |
43 | result["type"] = []string{}
44 | result["year"] = []string{}
45 | result["producer"] = []string{}
46 | result["publisher"] = []string{}
47 | result["country"] = []string{}
48 | result["translate"] = []string{}
49 | result["version"] = []string{}
50 |
51 | for _, v := range lists {
52 | result[v.Type] = append(result[v.Type], v.Name)
53 | }
54 | return result, nil
55 | }
56 |
57 | // 根据类型读取枚举数据
58 | func GetRomBaseEnumByType(t string) ([]string, error) {
59 | lists, err := (&db.RombaseEnum{}).GetByType(t)
60 | if err != nil {
61 | return nil, err
62 | }
63 | result := []string{}
64 | for _, v := range lists {
65 | result = append(result, v.Name)
66 | }
67 | return result, nil
68 | }
69 |
--------------------------------------------------------------------------------
/modules/shortcut.go:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "simUI/db"
5 | "simUI/utils"
6 | "strings"
7 | )
8 |
9 | // 读取快捷工具
10 | func GetShortcuts(isAbs bool) ([]*db.Shortcut, error) {
11 | volist, err := (&db.Shortcut{}).GetAll()
12 | if err != nil {
13 | utils.WriteLog(err.Error())
14 | return nil, err
15 | }
16 |
17 | if isAbs {
18 | for k, v := range volist {
19 | volist[k].Path = utils.ToAbsPath(v.Path, "")
20 | }
21 | }
22 | return volist, nil
23 | }
24 |
25 | // 更新快捷工具
26 | func UpdateShortcut(data []*db.Shortcut) ([]*db.Shortcut, error) {
27 | m := &db.Shortcut{}
28 | volist, err := m.GetAll()
29 | if err != nil {
30 | utils.WriteLog(err.Error())
31 | return nil, err
32 | }
33 | voMap := map[uint32]*db.Shortcut{}
34 | for _, v := range volist {
35 | voMap[v.Id] = v
36 | }
37 |
38 | for _, v := range data {
39 |
40 | if v.Name == "" && v.Path == "" {
41 | continue
42 | }
43 |
44 | v.Path = utils.ToRelPath(v.Path, "")
45 | v.Name = strings.TrimSpace(v.Name)
46 | //没有找到则添加
47 | if _, ok := voMap[v.Id]; !ok {
48 | v.Add()
49 | continue
50 | }
51 |
52 | exist := voMap[v.Id]
53 | //有差异则更新数据
54 | if v.Name != exist.Name || v.Path != exist.Path || v.Sort != exist.Sort {
55 | v.UpdateById()
56 | }
57 | }
58 |
59 | //检查删除
60 | dataMap := map[uint32]*db.Shortcut{}
61 | for _, v := range data {
62 | dataMap[v.Id] = v
63 | }
64 | for _, v := range volist {
65 | if _, ok := dataMap[v.Id]; !ok {
66 | v.DeleteById()
67 | }
68 | }
69 |
70 | return volist, nil
71 | }
72 |
73 | // 更新快捷软件排序
74 | func UpdateShortcutSort(lists []uint32) error {
75 | if len(lists) == 0 {
76 | return nil
77 | }
78 | for k, pfId := range lists {
79 | shortcut := &db.Shortcut{
80 | Id: pfId,
81 | Sort: uint32(k + 1),
82 | }
83 | err := shortcut.UpdateSortById()
84 | if err != nil {
85 | utils.WriteLog(err.Error())
86 | return err
87 | }
88 | }
89 | return nil
90 | }
91 |
--------------------------------------------------------------------------------
/modules/simulator.go:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "simUI/config"
5 | "simUI/db"
6 | "simUI/utils"
7 | )
8 |
9 | // 读取所有模拟器
10 | func GetAllSimulator() (map[uint32][]*db.Simulator, error) {
11 | dolist, _ := (&db.Simulator{}).GetAll()
12 | data := map[uint32][]*db.Simulator{}
13 |
14 | for _, v := range dolist {
15 | if _, ok := data[v.Platform]; ok {
16 | data[v.Platform] = append(data[v.Platform], v)
17 | } else {
18 | data[v.Platform] = []*db.Simulator{v}
19 | }
20 | }
21 | return data, nil
22 | }
23 |
24 | // 读取一个平台下的所有模拟器
25 | func GetSimulatorByPlatform(id uint32) ([]*db.Simulator, error) {
26 | return (&db.Simulator{}).GetByPlatform(id)
27 | }
28 |
29 | // 读取一个模拟器
30 | func GetSimulatorById(id uint32) (*db.Simulator, error) {
31 | return (&db.Simulator{}).GetById(id)
32 | }
33 |
34 | // 添加模拟器
35 | func AddSimulator(platformId uint32, name string) (*db.Simulator, error) {
36 |
37 | sim := &db.Simulator{
38 | Name: name,
39 | Platform: platformId,
40 | }
41 | id, err := sim.Add()
42 | if err != nil {
43 | return nil, err
44 | }
45 | sim.Id = id
46 | return sim, nil
47 | }
48 |
49 | // 更新模拟器
50 | func UpdateSimulator(data db.Simulator) (*db.Simulator, error) {
51 | sim := &db.Simulator{
52 | Id: data.Id,
53 | Name: data.Name,
54 | Platform: data.Platform,
55 | Path: data.Path,
56 | Cmd: data.Cmd,
57 | RunBefore: data.RunBefore,
58 | RunAfter: data.RunAfter,
59 | }
60 |
61 | //更新模拟器
62 | if err := sim.UpdateById(); err != nil {
63 | return sim, err
64 | }
65 |
66 | return sim, nil
67 | }
68 |
69 | // 更新模拟器排序
70 | func UpdateSimulatorSort(lists []uint32) error {
71 | if len(lists) == 0 {
72 | return nil
73 | }
74 | for k, pfId := range lists {
75 | simulator := &db.Simulator{
76 | Id: pfId,
77 | Sort: uint32(k + 1),
78 | }
79 | err := simulator.UpdateSortById()
80 | if err != nil {
81 | utils.WriteLog(err.Error())
82 | return err
83 | }
84 | }
85 | return nil
86 | }
87 |
88 | // 删除模拟器
89 | func DelSimulator(id uint32) error {
90 | if err := (&db.Simulator{Id: id}).DeleteById(); err != nil {
91 | utils.WriteLog(err.Error())
92 | return err
93 | }
94 | return nil
95 | }
96 |
97 | // 设置一个rom的模拟器id
98 | func SetRomSimId(romId uint64, simId uint32) error {
99 |
100 | if err := (&db.Rom{}).UpdateOneField(romId, "sim_id", simId); err != nil {
101 | return err
102 | }
103 |
104 | rom, _ := (&db.Rom{}).GetById(romId)
105 |
106 | //更新csv
107 | go func() {
108 | config.SetRomSettingOneField(rom.Platform, rom.RomName, "SimId", simId, true)
109 | }()
110 |
111 | return nil
112 | }
113 |
114 | // 批量设置rom的模拟器id
115 | func BatchSetRomSimId(romIds []uint64, simId uint32) error {
116 |
117 | err := (&db.Rom{}).UpdateSimIdByIds(romIds, simId)
118 | if err != nil {
119 | return err
120 | }
121 |
122 | roms, _ := (&db.Rom{}).GetByIds(romIds)
123 | if len(roms) == 0 {
124 | return nil
125 | }
126 | platform := roms[0].Platform
127 |
128 | //更新csv
129 | go func() {
130 | for _, v := range roms {
131 | config.SetRomSettingOneField(platform, v.RomName, "SimId", simId, true)
132 | }
133 | config.FlushRomSetting(platform)
134 | }()
135 |
136 | return nil
137 | }
138 |
--------------------------------------------------------------------------------
/modules/strategyFiles.go:
--------------------------------------------------------------------------------
1 | package modules
2 |
3 | import (
4 | "errors"
5 | "simUI/components"
6 | "simUI/config"
7 | "simUI/db"
8 | "simUI/utils"
9 | "sort"
10 | "strings"
11 | )
12 |
13 | type GameStrategyFile struct {
14 | Name string //文件名称
15 | Path string //文件路径
16 | }
17 |
18 | // 读取攻略文件
19 | func GetStrategyFiles(id uint64) ([]GameStrategyFile, error) {
20 |
21 | //游戏游戏详细数据
22 | info, err := (&db.Rom{}).GetById(id)
23 | lists := []GameStrategyFile{}
24 | if err != nil {
25 | return lists, err
26 | }
27 |
28 | //资源路径
29 | resPath := components.GetResPathByType("file", info.Platform)
30 |
31 | //音频列表
32 | files, _ := components.GetFileRes(resPath, info.RomName)
33 |
34 | //文件名排序
35 | sort.Slice(files, func(i, j int) bool {
36 | return strings.ToLower(files[i]) < strings.ToLower(files[j])
37 | })
38 |
39 | if len(files) > 0 {
40 | for _, v := range files {
41 | s := GameStrategyFile{
42 | Name: utils.GetFileName(v),
43 | Path: utils.ToRelPath(v, ""),
44 | }
45 | lists = append(lists, s)
46 | }
47 | }
48 | return lists, nil
49 | }
50 |
51 | // 更新攻略文件
52 | func UpdateStrategyFiles(id uint64, data []GameStrategyFile) ([]GameStrategyFile, error) {
53 |
54 | //游戏游戏详细数据
55 | info, err := (&db.Rom{}).GetById(id)
56 | if err != nil {
57 | return nil, err
58 | }
59 |
60 | //游戏游戏详细数据
61 | platformInfo := (&db.Platform{}).GetVOById(info.Platform, false)
62 |
63 | if platformInfo.FilesPath == "" {
64 | return nil, errors.New(config.Cfg.Lang["platformPathNotFound"])
65 | }
66 |
67 | //资源路径
68 | resPath := components.GetResPathByType("file", info.Platform)
69 |
70 | //攻略列表
71 | files, _ := components.GetFileRes(resPath, info.RomName)
72 | existMap := map[string]string{}
73 | for _, v := range files {
74 | existMap[utils.GetFileName(v)] = v
75 | }
76 |
77 | newData := map[string]string{}
78 | for _, v := range data {
79 |
80 | if v.Path == "" {
81 | continue
82 | }
83 |
84 | if v.Name == "" {
85 | v.Name = utils.GetFileName(v.Path)
86 | }
87 |
88 | absPath := utils.ToAbsPath(v.Path, "")
89 | v.Name = strings.TrimSpace(v.Name)
90 | pathName := utils.GetFileName(v.Path)
91 | pathExt := utils.GetFileExt(v.Path)
92 | inPath := platformInfo.FilesPath + "/" + info.RomName + "/" + v.Name + pathExt
93 |
94 | if !utils.FileExists(inPath) {
95 | //文件不存在,复制进来
96 | err = utils.FileCopy(absPath, inPath)
97 | if err != nil {
98 | return nil, err
99 | }
100 | } else if utils.FileExists(absPath) && pathName != v.Name {
101 | //文件存在,但是需要改名
102 | _, err = utils.FileRename(absPath, v.Name)
103 | if err != nil {
104 | return nil, err
105 | }
106 | }
107 | newData[v.Name] = absPath
108 | }
109 |
110 | //重新读一下音频列表,检查不存在则删除
111 | files, _ = components.GetFileRes(resPath, info.RomName)
112 |
113 | for _, p := range files {
114 | nfile := utils.GetFileName(p)
115 | if _, ok := newData[nfile]; !ok {
116 | utils.FileDelete(p)
117 | continue
118 | }
119 | }
120 | return GetStrategyFiles(id)
121 | }
122 |
--------------------------------------------------------------------------------
/readme/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/readme/1.jpg
--------------------------------------------------------------------------------
/readme/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/readme/2.jpg
--------------------------------------------------------------------------------
/readme/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/readme/3.jpg
--------------------------------------------------------------------------------
/readme/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontlon/simUI-pulsar/2822bcf2b0f630925a29b4d929d6b846af63db41/readme/logo.png
--------------------------------------------------------------------------------
/request/input.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | /**
4 | * 导出(分享)rom OutputRom
5 | **/
6 | type OutputRom struct {
7 | Save string `json:"save"` //zip文件保存路径
8 | Platform uint32 `json:"platform"` //平台id
9 | Opt string `json:"opt"` //导出类型
10 | Options []string `json:"options"` //导出选项
11 | Menus []string `json:"menus"` //目录列表
12 | Roms []uint64 `json:"roms"` //rom列表
13 | Simulators []uint32 `json:"simulators"` //模拟器列表
14 | }
15 |
--------------------------------------------------------------------------------
/request/platform.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | type UpdatePlatform struct {
4 | Id uint32 `json:"id"`
5 | Name string `json:"name"`
6 | Icon string `json:"icon"`
7 | Tag string `json:"tag"`
8 | RomExts []string `json:"romExts"`
9 | RootPath string `json:"rootPath"`
10 | RomPath []string `json:"romPath"`
11 | HideName uint8 `json:"hideName"`
12 | }
13 |
--------------------------------------------------------------------------------
/request/rom.go:
--------------------------------------------------------------------------------
1 | package request
2 |
3 | /**
4 | * 读取游戏列表 GetGameList
5 | **/
6 | type GetGameList struct {
7 | Theme string `json:"theme"` //主题
8 | ShowHide uint8 `json:"showHide"` //是否隐藏
9 | ShowSubGame uint8 `json:"showSubGame"` //是否显示子游戏
10 | Platform uint32 `json:"platform"` //平台
11 | Catname string `json:"catname"` //分类
12 | CatnameLike int `json:"catnameLike"` //分类模糊搜索 0精确查找 1模糊查找
13 | Keyword string `json:"keyword"` //关键字
14 | Letter string `json:"letter"` //字母索引
15 | Page int `json:"page"` //分页数
16 | BaseType string `json:"baseType"` //资料 - 游戏类型
17 | BasePublisher string `json:"basePublisher"` //资料 - 发布者
18 | BaseYear string `json:"baseYear"` //资料 - 发型年份
19 | BaseCountry string `json:"baseCountry"` //资料 - 国家
20 | BaseTranslate string `json:"baseTranslate"` //资料 - 语言
21 | BaseVersion string `json:"baseVersion"` //资料 - 版本
22 | BaseProducer string `json:"baseProducer"` //资料 - 制作商
23 | Score string `json:"score"` //评分
24 | Complete string `json:"complete"` //是否通关
25 | SimpleMode string `json:"simpleModel"` //返回的数据结构类型
26 | }
27 |
--------------------------------------------------------------------------------
/utils/alert.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "github.com/ncruces/zenity"
4 |
5 | // 调用系统dialog框 - 错误
6 | func DialogError(title, msg string) {
7 | zenity.Error(msg, zenity.Title(title), zenity.ErrorIcon)
8 | }
9 |
10 | // 调用系统dialog框 - 信息
11 | func DialogInfo(title, msg string) {
12 | zenity.Info(msg, zenity.Title(title), zenity.InfoIcon)
13 |
14 | }
15 |
16 | // 调用系统dialog框 - 警告
17 | func DialogWarn(title, msg string) {
18 | zenity.Warning(msg, zenity.Title(title), zenity.WarningIcon)
19 | }
20 |
--------------------------------------------------------------------------------
/utils/args.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | var args map[string]string
4 |
5 | // 解析并读取命令行参数
6 | func GetCmdArgs(name string) string {
7 | /*if len(args) == 0 {
8 | var db string
9 | var dev string
10 | flag.StringVar(&db, "db", "", "")
11 | flag.StringVar(&dev, "dev", "", "")
12 | flag.Parse()
13 |
14 | fmt.Println("-------------")
15 | fmt.Println(dev)
16 | args = map[string]string{
17 | "db": db,
18 | "dev": "",
19 | }
20 | }
21 | if _, ok := args[name]; ok {
22 | return args[name]
23 | } else {
24 | return ""
25 | }*/
26 | return ""
27 | }
28 |
--------------------------------------------------------------------------------
/utils/calljs.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | // 调用alert框
4 | func ErrorMsg(err string) error {
5 |
6 | /*if _, err := Window.Call("errorBox", sciter.NewValue(err)); err != nil {
7 | }*/
8 | return nil
9 | }
10 |
11 | // 调用loading框
12 | func Loading(str string, platform string) error {
13 | /*if _, err := Window.Call("startLoading", sciter.NewValue(str), sciter.NewValue(platform)); err != nil {
14 | }
15 | return sciter.NullValue()*/
16 | return nil
17 | }
18 |
19 | // 检查当前窗口激活状态
20 | func CheckWinActive() bool {
21 | /*active, err := Window.Call("checkWinActive")
22 | if err != nil {
23 | fmt.Println(err)
24 | }
25 | return active.Bool()*/
26 | return false
27 | }
28 |
29 | // 调用视图中的方向控制【手柄】
30 | func ViewDirection(dir int) bool {
31 | /*active, err := Window.Call("joystickDirection", sciter.NewValue(dir))
32 | if err != nil {
33 | fmt.Println(err)
34 | }
35 | return active.Bool()*/
36 | return false
37 | }
38 |
39 | // 调用视图中的按钮控制【手柄】
40 | func ViewButton(btn string) bool {
41 | /*active, err := Window.Call("joystickButton", sciter.NewValue(btn))
42 | if err != nil {
43 | fmt.Println(err)
44 | }
45 | return active.Bool()*/
46 | return false
47 | }
48 |
--------------------------------------------------------------------------------
/utils/conv.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "strconv"
5 | )
6 |
7 | /*
8 | 传入任意类型,将数据转换为字符串
9 | */
10 | func ToString(str interface{}) string {
11 | var val string
12 | switch t := str.(type) {
13 | case string:
14 | val = t
15 | case int:
16 | val = strconv.Itoa(t)
17 | case int8:
18 | val = strconv.Itoa(int(t))
19 | case int16:
20 | val = strconv.Itoa(int(t))
21 | case int32:
22 | val = strconv.Itoa(int(t))
23 | case int64:
24 | val = strconv.Itoa(int(t))
25 | case uint:
26 | val = strconv.Itoa(int(t))
27 | case uint8:
28 | val = strconv.Itoa(int(t))
29 | case uint16:
30 | val = strconv.Itoa(int(t))
31 | case uint32:
32 | val = strconv.Itoa(int(t))
33 | case uint64:
34 | val = strconv.Itoa(int(t))
35 | case float32:
36 | val = strconv.FormatFloat(float64(t), 'f', -1, 64)
37 | case float64:
38 | val = strconv.FormatFloat(t, 'f', -1, 64)
39 | case []uint8: //[]byte类型
40 | val = string(t)
41 | case bool:
42 | if t == true {
43 | val = "true"
44 | } else {
45 | val = "false"
46 | }
47 | default:
48 | val = t.(string)
49 | }
50 | return val
51 | }
52 |
53 | /*
54 | *
55 |
56 | 传入任意类型,将数据转换为整型
57 |
58 | *
59 | */
60 | func ToInt(str interface{}) int {
61 | var val int
62 | switch t := str.(type) {
63 | case int:
64 | val = int(t)
65 | case int8:
66 | val = int(t)
67 | case int16:
68 | val = int(t)
69 | case int32:
70 | val = int(t)
71 | case int64:
72 | val = int(t)
73 | case uint:
74 | val = int(t)
75 | case uint8:
76 | val = int(t)
77 | case uint16:
78 | val = int(t)
79 | case uint32:
80 | val = int(t)
81 | case uint64:
82 | val = int(t)
83 | case float32:
84 | val = int(t)
85 | case float64:
86 | val = int(t)
87 | case string:
88 | val, _ = strconv.Atoi(str.(string))
89 | case bool:
90 | if str.(bool) == true {
91 | val = 1
92 | } else {
93 | val = 0
94 | }
95 | default:
96 | val = 0
97 | }
98 | return val
99 | }
100 |
101 | func ToFloat64(str interface{}) float64 {
102 | var val float64
103 | switch t := str.(type) {
104 | case int:
105 | val = float64(t)
106 | case int8:
107 | val = float64(t)
108 | case int16:
109 | val = float64(t)
110 | case int32:
111 | val = float64(t)
112 | case int64:
113 | val = float64(t)
114 | case uint:
115 | val = float64(t)
116 | case uint8:
117 | val = float64(t)
118 | case uint16:
119 | val = float64(t)
120 | case uint32:
121 | val = float64(t)
122 | case uint64:
123 | val = float64(t)
124 | case float32:
125 | val = float64(t)
126 | case float64:
127 | val = float64(t)
128 | case string:
129 | val, _ = strconv.ParseFloat(t, 64)
130 | case bool:
131 | if str.(bool) == true {
132 | val = 1
133 | } else {
134 | val = 0
135 | }
136 | default:
137 | val = 0
138 | }
139 | return val
140 | }
141 |
--------------------------------------------------------------------------------
/utils/convert.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/axgle/mahonia"
5 | "golang.org/x/text/encoding/simplifiedchinese"
6 | )
7 |
8 | //转换为utf8
9 | func ToUTF8(str string) string {
10 | if str == "" {
11 | return str
12 | }
13 | srcCoder := mahonia.NewDecoder("gbk")
14 | srcResult := srcCoder.ConvertString(str)
15 | tagCoder := mahonia.NewDecoder("utf-8")
16 | _, cdata, _ := tagCoder.Translate([]byte(srcResult), true)
17 | result := string(cdata)
18 | return result
19 | }
20 |
21 | //检查内容是不是utf8
22 | func IsUTF8(str string) bool {
23 | buf := []byte(str)
24 | nBytes := 0
25 | for i := 0; i < len(buf); i++ {
26 | if nBytes == 0 {
27 | if (buf[i] & 0x80) != 0 { //与操作之后不为0,说明首位为1
28 | for (buf[i] & 0x80) != 0 {
29 | buf[i] <<= 1 //左移一位
30 | nBytes++ //记录字符共占几个字节
31 | }
32 | if nBytes < 2 || nBytes > 6 { //因为UTF8编码单字符最多不超过6个字节
33 | return false
34 | }
35 | nBytes-- //减掉首字节的一个计数
36 | }
37 | } else { //处理多字节字符
38 | if buf[i]&0xc0 != 0x80 { //判断多字节后面的字节是否是10开头
39 | return false
40 | }
41 | nBytes--
42 | }
43 | }
44 | return nBytes == 0
45 | }
46 |
47 | //utf8编码转gbk编码
48 | func Utf8ToGbk(str string) string {
49 | h, _ := simplifiedchinese.GBK.NewEncoder().Bytes([]byte(str))
50 | return string(h)
51 | }
52 |
--------------------------------------------------------------------------------
/utils/csv.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/csv"
5 | "fmt"
6 | "io"
7 | "os"
8 | )
9 |
10 | //读取csv
11 | func ReadCsv(filename string) ([][]string, error) {
12 |
13 | if filename == "" {
14 | return nil, nil
15 | }
16 |
17 | file, err := os.Open(filename)
18 | if err != nil {
19 | fmt.Println("Error:", err)
20 | return nil, err
21 | }
22 | defer file.Close()
23 | reader := csv.NewReader(file)
24 | data := [][]string{}
25 | for {
26 | record, err := reader.Read()
27 | if err == io.EOF {
28 | break
29 | }
30 | data = append(data, record)
31 | }
32 | return data, nil
33 | }
--------------------------------------------------------------------------------
/utils/encode.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/base64"
5 | "fmt"
6 | "os"
7 | "strings"
8 | )
9 |
10 | // base64编码
11 | func Base64Encode(s string) string {
12 | if s == "" {
13 | return ""
14 | }
15 | encodeString := base64.StdEncoding.EncodeToString([]byte(s))
16 | return strings.Replace(encodeString, "=", "_", -1)
17 | }
18 |
19 | // base64解码
20 | func Base64Decode(s string) string {
21 | if s == "" {
22 | return ""
23 | }
24 |
25 | s = strings.Replace(s, "_", "=", -1)
26 | decodeBytes, err := base64.StdEncoding.DecodeString(s)
27 | if err != nil {
28 | return ""
29 | }
30 | return string(decodeBytes)
31 | }
32 |
33 | // base64转换为文件
34 | func Base64ToFile(base64Str, outputPath string) error {
35 | // 提取base64数据部分
36 | data := base64Str
37 | if idx := strings.Index(base64Str, "base64,"); idx != -1 {
38 | data = base64Str[idx+7:]
39 | fmt.Println("=========", idx, idx+7)
40 |
41 | }
42 |
43 | // 解码base64内容
44 | decodedData, err := base64.StdEncoding.DecodeString(data)
45 | if err != nil {
46 | return fmt.Errorf("decode base64 string: %v", err)
47 | }
48 |
49 | // 写入文件
50 | err = os.WriteFile(outputPath, decodedData, 0666)
51 | if err != nil {
52 | return fmt.Errorf("write file: %v", err)
53 | }
54 |
55 | return nil
56 | }
57 |
--------------------------------------------------------------------------------
/utils/html.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "regexp"
5 | )
6 |
7 | //从html中抽取img地址
8 | func GetImgPathByHtml(htmls string) []string {
9 | var imgRE = regexp.MustCompile(`
]+\bsrc=["']([^"']+)["']`)
10 | imgs := imgRE.FindAllStringSubmatch(htmls, -1)
11 | out := make([]string, len(imgs))
12 | for i := range out {
13 | out[i] = imgs[i][1]
14 | }
15 | return out
16 | }
17 |
--------------------------------------------------------------------------------
/utils/http.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io"
7 | "io/ioutil"
8 | "net/http"
9 | "net/url"
10 | "os"
11 | "time"
12 | )
13 |
14 | const HttpTimeout = 3 * time.Second
15 |
16 | // get请求
17 | func HttpGet(uri string, headers map[string]string) ([]byte, error) {
18 | // 表单数据
19 |
20 | // 创建一个新的请求对象
21 | req, err := http.NewRequest("GET", uri, nil)
22 | if err != nil {
23 | fmt.Println(err)
24 | return nil, err
25 | }
26 |
27 | // 设置请求头部
28 | if headers != nil && len(headers) > 0 {
29 | for k, v := range headers {
30 | req.Header.Set(k, v)
31 | }
32 | }
33 | // 发送请求
34 | client := &http.Client{Timeout: HttpTimeout}
35 | resp, err := client.Do(req)
36 | if err != nil {
37 | fmt.Println(err)
38 | return nil, err
39 | }
40 | defer resp.Body.Close()
41 |
42 | bodyBytes, err := ioutil.ReadAll(resp.Body)
43 | if err != nil {
44 | fmt.Println(err)
45 | return nil, err
46 | }
47 | return bodyBytes, nil
48 | }
49 |
50 | // post form 请求
51 | func HttpPostForm(uri string, formData, headers map[string]string) ([]byte, error) {
52 | // 表单数据
53 | data := url.Values{}
54 | if formData != nil && len(formData) > 0 {
55 | for k, v := range formData {
56 | data.Set(k, v)
57 | }
58 | }
59 |
60 | // 创建一个缓冲区来存储表单编码后的数据
61 | buf := bytes.NewBufferString(data.Encode())
62 |
63 | // 创建一个新的请求对象
64 | req, err := http.NewRequest("POST", uri, buf)
65 | if err != nil {
66 | fmt.Println(err)
67 | return nil, err
68 | }
69 |
70 | // 设置请求头部
71 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
72 | if headers != nil && len(headers) > 0 {
73 | for k, v := range headers {
74 | req.Header.Set(k, v)
75 | }
76 | }
77 | // 发送请求
78 | client := &http.Client{Timeout: HttpTimeout}
79 | resp, err := client.Do(req)
80 | if err != nil {
81 | fmt.Println(err)
82 | return nil, err
83 | }
84 | defer resp.Body.Close()
85 |
86 | bodyBytes, err := io.ReadAll(resp.Body)
87 | if err != nil {
88 | fmt.Println(err)
89 | return nil, err
90 | }
91 | return bodyBytes, nil
92 | }
93 |
94 | // 下载文件到本地
95 | func DownloadFile(httpUrl string, localPath string) error {
96 |
97 | //下载文件
98 | response, err := http.Get(httpUrl)
99 | if err != nil {
100 | return err
101 | }
102 |
103 | dir := GetFilePath(localPath)
104 |
105 | if err = CreateDir(dir); err != nil {
106 | return err
107 | }
108 |
109 | f, err := os.Create(localPath)
110 | defer f.Close()
111 |
112 | if err != nil {
113 | return err
114 | }
115 |
116 | if _, err := io.Copy(f, response.Body); err != nil {
117 | return err
118 | }
119 |
120 | return nil
121 | }
122 |
--------------------------------------------------------------------------------
/utils/image.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "errors"
5 | "github.com/nfnt/resize"
6 | "image"
7 | "image/jpeg"
8 | "os"
9 | "strings"
10 | )
11 |
12 | func CreateThumbnail(src string, dst string, imgWidth int, quality int) error {
13 |
14 | format := strings.ToLower(GetFileExt(src))
15 |
16 | //检查图片格式是否支持
17 | allowFormats := map[string]uint8{".gif": 1, ".jpg": 1, ".jpeg": 1, ".png": 1}
18 | if _, ok := allowFormats[format]; !ok {
19 | return nil
20 | }
21 |
22 | if !FileExists(src) {
23 | return errors.New("file not exists")
24 | }
25 |
26 | //读取文件
27 | fa, err := os.Open(src)
28 | if err != nil {
29 | return err
30 | }
31 | defer fa.Close()
32 |
33 | config, _, err := image.DecodeConfig(fa)
34 | if err != nil {
35 | return err
36 | }
37 |
38 | if config.Width < imgWidth {
39 | return errors.New("no need dst scale")
40 | }
41 |
42 | fb, err := os.Open(src)
43 | if err != nil {
44 | return err
45 | }
46 | defer fb.Close()
47 | origin, _, err := image.Decode(fb)
48 | if err != nil {
49 | return err
50 | }
51 |
52 | // 做等比缩放
53 | width := uint(imgWidth)
54 | height := uint(imgWidth * config.Height / config.Width)
55 |
56 | canvas := resize.Thumbnail(width, height, origin, resize.Lanczos3)
57 |
58 | if FileExists(dst) {
59 | FileDelete(dst)
60 | }
61 |
62 | file_out, err := os.Create(dst)
63 | if err != nil {
64 | return err
65 | }
66 | defer file_out.Close()
67 |
68 | //生成图片
69 | err = jpeg.Encode(file_out, canvas, &jpeg.Options{quality})
70 | if err != nil {
71 | return err
72 | }
73 |
74 | return nil
75 | }
76 |
--------------------------------------------------------------------------------
/utils/log.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "io"
5 | "os"
6 | "runtime"
7 | "strconv"
8 | "time"
9 | )
10 |
11 | /*
12 | 写日志
13 | */
14 | func WriteLog(str string) {
15 | fileName := "log.txt"
16 |
17 | cachePath := "./cache/"
18 | if err := CreateDir(cachePath); err != nil {
19 | return
20 | }
21 |
22 | f, _ := os.OpenFile(cachePath+fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
23 |
24 | c_date := time.Now().Format("2006-01-02 15:04:05")
25 | _, c_file, c_line, _ := runtime.Caller(1)
26 |
27 | content := c_date + "\t" //日期
28 | content += c_file + "\t" //文件
29 | content += strconv.Itoa(c_line) + "\t" //行号
30 | content += str + "\r\n" //内容
31 |
32 | if _, err := io.WriteString(f, content); err != nil {
33 | return
34 | }
35 |
36 | defer f.Close()
37 | return
38 | }
39 |
--------------------------------------------------------------------------------
/utils/pinyin/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 |
3 | go:
4 | - 1.11.x
5 |
6 | os:
7 | - linux
8 | - osx
9 | - windows
10 |
11 | before_install:
12 | - go get -v ./...
13 |
14 | script:
15 | - go test ./... -race -coverprofile=coverage.txt -covermode=atomic
16 |
17 | after_success:
18 | - bash <(curl -s https://codecov.io/bash)
--------------------------------------------------------------------------------
/utils/pinyin/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Chain Zhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/utils/pinyin/Makefile:
--------------------------------------------------------------------------------
1 | PKG_LIST := $(shell go list ./... | grep -v /vendor/)
2 |
3 | test:
4 | @go test -short -cover ${PKG_LIST}
--------------------------------------------------------------------------------
/utils/pinyin/README.md:
--------------------------------------------------------------------------------
1 | # pinyin
2 |
3 | [](https://travis-ci.com/Chain-Zhang/pinyin)
4 | [](https://codecov.io/gh/Chain-Zhang/pinyin)
5 |
6 | golang实现中文汉字转拼音
7 |
8 | demo
9 |
10 | ```go
11 | package main
12 |
13 | import(
14 | "fmt"
15 | "github.com/chain-zhang/pinyin"
16 | )
17 |
18 | func main() {
19 | str, err := pinyin.New("我是中国人").Split("").Mode(InitialsInCapitals).Convert()
20 | if err != nil {
21 | // 错误处理
22 | }else{
23 | fmt.Println(str)
24 | }
25 |
26 | str, err = pinyin.New("我是中国人").Split(" ").Mode(pinyin.WithoutTone).Convert()
27 | if err != nil {
28 | // 错误处理
29 | }else{
30 | fmt.Println(str)
31 | }
32 |
33 | str, err = pinyin.New("我是中国人").Split("-").Mode(pinyin.Tone).Convert()
34 | if err != nil {
35 | // 错误处理
36 | }else{
37 | fmt.Println(str)
38 | }
39 |
40 | str, err = pinyin.New("我是中国人").Convert()
41 | if err != nil {
42 | // 错误处理
43 | }else{
44 | fmt.Println(str)
45 | }
46 | }
47 | ```
48 |
49 | 输出
50 |
51 | ```bash
52 | WoShiZhongGuoRen
53 | wo shi zhong guo ren
54 | wǒ-shì-zhōng-guó-rén
55 | wo shi zhong guo ren
56 | ```
57 |
58 | Mode 介绍
59 |
60 | * `InitialsInCapitals`: 首字母大写, 不带音调
61 | * `WithoutTone`: 全小写,不带音调
62 | * `Tone`: 全小写带音调
63 |
64 | Split 介绍
65 |
66 | split 方法是两个汉字之间的分隔符.
--------------------------------------------------------------------------------
/utils/pinyin/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | token: f3bed817-d998-44c1-b474-168d03f438bd
--------------------------------------------------------------------------------
/utils/pinyin/error.go:
--------------------------------------------------------------------------------
1 | package pinyin
2 |
3 | import "errors"
4 |
5 | var (
6 | ErrInitialize = errors.New("not yet initialized")
7 | )
8 |
--------------------------------------------------------------------------------
/utils/pinyin/pinyin_test.go:
--------------------------------------------------------------------------------
1 | package pinyin
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestConvert(t *testing.T) {
8 | str, err := New("hi,我是中国人").Split("").Mode(InitialsInCapitals).Convert()
9 | if err != nil {
10 | t.Error(err)
11 | } else {
12 | t.Log(str)
13 | }
14 |
15 | str, err = New("hi, 我是中国人").Split(" ").Mode(WithoutTone).Convert()
16 | if err != nil {
17 | t.Error(err)
18 | } else {
19 | t.Log(str)
20 | }
21 |
22 | str, err = New("我是hahah中国人").Split("-").Mode(Tone).Convert()
23 | if err != nil {
24 | t.Error(err)
25 | } else {
26 | t.Log(str)
27 | }
28 |
29 | str, err = New("我是h中国人a").Convert()
30 | if err != nil {
31 | t.Error(err)
32 | } else {
33 | t.Log(str)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/utils/rom.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | var prefix = "t0Ic" //前缀
8 |
9 | // 创建rom唯一id
10 | func CreateRomUniqId(t string, s int64) string {
11 | return prefix + "_" + t + "_" + ToString(s)
12 | }
13 |
14 | // 分割rom唯一id
15 | func SplitRomUniqId(uniqId string) (string, string) {
16 | if uniqId == "" {
17 | return "", ""
18 | }
19 | sli := strings.Split(uniqId, "_")
20 | return sli[1], sli[2] //返回 时间,大小
21 | }
22 |
23 | // 检查是不是唯一ID
24 | func HasRomUniqId(uniqId string) bool {
25 | if uniqId == "" {
26 | return false
27 | }
28 | sli := strings.Split(uniqId, "_")
29 |
30 | if sli[0] == prefix {
31 | return true
32 | }
33 | return false
34 | }
35 |
36 | // 读取rom的目录
37 | func getSplitRomMenu(rel string) string {
38 | p := GetFilePath(rel)
39 | parr := strings.Split(p, "/")
40 | parr = SliceRemoveEmpty(parr)
41 | menu := "_7b9"
42 | if len(parr) > 0 {
43 | menu = menu + "/" + parr[0]
44 | }
45 | return menu
46 | }
47 |
48 | // 处理rom路径为2级
49 | func HandleRomMenu(p string) string {
50 | rel := GetFilePath(p)
51 | arr := strings.Split(rel, "/")
52 | if len(arr) > 3 {
53 | arr = arr[0:3]
54 | rel = strings.Join(arr, "/") + "/"
55 | }
56 | return rel
57 | }
58 |
59 | // 读取map中第一个key和value
60 | func MapGetFirst[K, V comparable](mp map[K]V) (K, V) {
61 | var key K
62 | var val V
63 |
64 | if len(mp) == 0 {
65 | return key, val
66 | }
67 |
68 | for k, v := range mp {
69 | return k, v
70 | }
71 | return key, val
72 | }
73 |
--------------------------------------------------------------------------------
/utils/string.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "bytes"
5 | "crypto/md5"
6 | "fmt"
7 | "math/rand"
8 | "regexp"
9 | "simUI/utils/pinyin"
10 | "strings"
11 | "time"
12 | )
13 |
14 | /**
15 | * 字符串MD5
16 | **/
17 | func Md5(str string) string {
18 | data := []byte(str)
19 | has := md5.Sum(data)
20 | return fmt.Sprintf("%x", has)
21 | }
22 |
23 | /**
24 | * 字符转拼音
25 | **/
26 | func TextToPinyin(str string) string {
27 | str, err := pinyin.New(str).Split("").Mode(pinyin.WithoutTone).Convert()
28 | if err != nil {
29 | // 错误处理
30 | }
31 | str = strings.ToLower(str)
32 | str = strings.ReplaceAll(str, " ", "")
33 | return str
34 | }
35 |
36 | func Addslashes(str string) string {
37 | var buf bytes.Buffer
38 | for _, char := range str {
39 | switch char {
40 | case '\'', '"', '\\':
41 | buf.WriteRune('\\')
42 | }
43 | buf.WriteRune(char)
44 | }
45 | return buf.String()
46 | }
47 |
48 | // 输入一个范围,生成范围随机数
49 | func RandInt(min, max int) int {
50 | rand.Seed(time.Now().UnixNano())
51 | return rand.Intn(max-min+1) + min
52 | }
53 |
54 | // 输入一个长度,返回一个随机字符串
55 | func RandStr(min, max int) string {
56 | rand.Seed(time.Now().UnixNano())
57 |
58 | length := rand.Intn(max-min+1) + min
59 | result := make([]byte, length)
60 |
61 | for i := 0; i < length; i++ {
62 | result[i] = byte(rand.Intn(26) + 97)
63 | }
64 |
65 | return string(result)
66 | }
67 |
68 | // 检查一个字符串是不是纯英文
69 | func IsEnglish(str string) bool {
70 | reg := regexp.MustCompile(`[^a-zA-Z0-9!"# $%&'()*+,-./:;<=>?@[\\\]^_` + "`" + `{|}~]+`)
71 | enStr := reg.ReplaceAllString(str, "")
72 | return len(enStr) == len(str)
73 | }
74 |
75 | // 首字母大写
76 | func ToTitleCase(str string) string {
77 | if str == "" {
78 | return ""
79 | }
80 | return strings.ToUpper(string(str[0])) + str[1:]
81 | }
82 |
--------------------------------------------------------------------------------
/utils/struct.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "errors"
5 | "reflect"
6 | )
7 |
8 | // 根据Key,读取struct value
9 | // T是值或指针都可以
10 | func GetStructValue[T, V comparable](stu T, k string) V {
11 | var value V
12 | switch reflect.TypeOf(stu).Kind() {
13 | case reflect.Struct:
14 | value = reflect.ValueOf(stu).FieldByName(k).Interface().(V)
15 | case reflect.Ptr:
16 | isset := reflect.ValueOf(stu).Elem().FieldByName(k)
17 | if isset.IsValid() {
18 | value = reflect.ValueOf(stu).Elem().FieldByName(k).Interface().(V)
19 | }
20 | }
21 | return value
22 | }
23 |
24 | // 根据Key,设置struct value
25 | // T 必须是指针
26 | func SetStructValue[T comparable](stu T, k string, val any) error {
27 |
28 | structValue := reflect.ValueOf(stu).Elem()
29 | field := structValue.FieldByName(k)
30 |
31 | if !field.IsValid() {
32 | return errors.New("no such field")
33 | }
34 |
35 | if !field.CanSet() {
36 | return errors.New("cannot set this field")
37 | }
38 |
39 | value := ToString(val)
40 | field.Set(reflect.ValueOf(value))
41 | return nil
42 | }
43 |
--------------------------------------------------------------------------------
/utils/system.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | )
7 |
8 | // 检查端口是否占用
9 | func IsPortInUse(port int) bool {
10 | ln, err := net.Listen("tcp", ":"+fmt.Sprint(port))
11 | if err != nil {
12 | return true
13 | }
14 | defer ln.Close()
15 | return false
16 | }
17 |
--------------------------------------------------------------------------------
/utils/translate.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "encoding/json"
5 | )
6 |
7 | type BaiduTrans struct {
8 | From string `json:"from"`
9 | To string `json:"to"`
10 | TransResult []struct {
11 | Src string `json:"src"`
12 | Dst string `json:"dst"`
13 | } `json:"trans_result"`
14 | }
15 |
16 | // 百度翻译 to: zh/en/cht
17 | func BaiduTranslate(keyword string, to string) string {
18 | postUrl := "https://fanyi-api.baidu.com/api/trans/vip/translate?"
19 | appId := "20230421001650804"
20 | scriet := "FzLUusAtVas4v1FJJphE"
21 | salt := ToString(RandInt(8, 8))
22 | sign := appId + keyword + salt + scriet
23 |
24 | data := map[string]string{
25 | "q": keyword,
26 | "from": "auto",
27 | "to": to,
28 | "appid": appId,
29 | "salt": salt,
30 | "sign": Md5(sign),
31 | }
32 | resp, err := HttpPostForm(postUrl, data, nil)
33 | if err != nil {
34 | WriteLog("BaiduTranslate Error:" + err.Error())
35 | return ""
36 | }
37 |
38 | var body BaiduTrans
39 | json.Unmarshal(resp, &body)
40 | if len(body.TransResult) == 0 {
41 | return ""
42 | }
43 |
44 | return body.TransResult[0].Dst
45 | }
46 |
--------------------------------------------------------------------------------
/utils/uuid.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import "github.com/google/uuid"
4 |
5 | // 生成uuid
6 | func CreateUUid() string {
7 | return uuid.New().String()
8 | }
9 |
--------------------------------------------------------------------------------
/utils/version.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | // 比较版本号
9 | func CompareVersion(v1, v2 string) int {
10 | ver1 := strings.Split(v1, ".") // 将版本号按照"."分割为切片
11 | ver2 := strings.Split(v2, ".")
12 |
13 | for i := 0; i < len(ver1) || i < len(ver2); i++ {
14 | num1 := 0
15 | num2 := 0
16 |
17 | if i < len(ver1) {
18 | fmt.Sscanf(ver1[i], "%d", &num1)
19 | }
20 |
21 | if i < len(ver2) {
22 | fmt.Sscanf(ver2[i], "%d", &num2)
23 | }
24 |
25 | if num1 > num2 {
26 | return 1
27 | } else if num1 < num2 {
28 | return -1
29 | }
30 | }
31 |
32 | return 0
33 | }
34 |
--------------------------------------------------------------------------------
/utils/wails.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | const wailsPathPrefixHead = "/ASSET/"
8 | const wailsPathPrefixEnd = ".asset"
9 | const WailsPathPattern = `(/ASSET/.+?\.asset)` //正则
10 |
11 | // wails 路径编码
12 | func WailsPathEncode(p string, toAbs bool) string {
13 | if p == "" {
14 | return p
15 | }
16 |
17 | if strings.HasPrefix(p, wailsPathPrefixHead) {
18 | return p
19 | }
20 |
21 | if p == "" {
22 | return ""
23 | }
24 | if toAbs && !IsAbsPath(p) {
25 | p = ToAbsPath(p, "")
26 | }
27 | return wailsPathPrefixHead + Base64Encode(p) + wailsPathPrefixEnd
28 | }
29 |
30 | // wails 路径解码
31 | func WailsPathDecode(p string) string {
32 | if p == "" {
33 | return p
34 | }
35 | if !strings.HasPrefix(p, wailsPathPrefixHead) {
36 | return p
37 | }
38 |
39 | p = strings.Replace(p, wailsPathPrefixHead, "", 1)
40 | p = strings.Replace(p, wailsPathPrefixEnd, "", 1)
41 | return Base64Decode(p)
42 | }
43 |
44 | // 检查是否为 wails路径 true是,false不是
45 | func WailsPathCheck(p string) bool {
46 | if p == "" {
47 | return false
48 | }
49 | return strings.Contains(p, wailsPathPrefixHead)
50 | }
51 |
--------------------------------------------------------------------------------
/wails.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simui",
3 | "outputfilename": "simui",
4 | "frontend:install": "npm install",
5 | "frontend:build": "npm run build",
6 | "frontend:dev:watcher": "npm run dev",
7 | "author": {
8 | "name": "frontlon",
9 | "email": "front_diablo@163.com"
10 | }
11 | }
--------------------------------------------------------------------------------