diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa2f15e --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +build/ +.flatpak-builder/ +build-dir/ +.vscode +gdb.sh +_build/ +.flatpak/ +com.vysp3r.ProtonPlus.flatpak \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d08e3f9 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +RetroPlus@vysp3r.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..760c1d0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,95 @@ +# Contributing to RetroPlus + +First off, thanks for taking the time to contribute! โค๏ธ + +All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. ๐ŸŽ‰ + +> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: +> - Star the project +> - Tweet about it +> - Refer this project in your project's readme +> - Mention the project at local meetups and tell your friends/colleagues + +## Table of Contents + +- [I Have a Question](#i-have-a-question) +- [I Want To Contribute](#i-want-to-contribute) + - [Reporting Bugs](#reporting-bugs) + - [Suggesting Enhancements](#suggesting-enhancements) +- [Styleguides](#styleguides) + - [Commit Messages](#commit-messages) + + +## Code of Conduct + +This project and everyone participating in it is governed by the +[RetroPlus Code of Conduct](https://github.com/Vysp3r/RetroPlus/blob/master/CODE_OF_CONDUCT.md). +By participating, you are expected to uphold this code. Please report unacceptable behavior +to [@Vysp3r](https://github.com/Vysp3r). + +## I Have a Question + +> If you want to ask a question, we assume that you have read the available [Documentation](https://github.com/Vysp3r/RetroPlus/#readme). + +Before you ask a question, it is best to search for existing [Issues](https://github.com/Vysp3r/RetroPlus/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first. + +If you then still feel the need to ask a question and need clarification, we recommend the following: + +- Open an [Issue](https://github.com/Vysp3r/RetroPlus/issues/new). +- Provide as much context as you can about what you're running into. + +We will then take care of the issue as soon as possible. + +## I Want To Contribute + +> ### Legal Notice +> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. + +### Reporting Bugs + +#### Before Submitting a Bug Report + +A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. + +- Make sure that you are using the latest version. +- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](https://github.com/Vysp3r/RetroPlus/#readme). If you are looking for support, you might want to check [this section](#i-have-a-question)). +- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/Vysp3r/RetroPlus/issues?q=is%3Aopen+is%3Aissue+label%3A%22%F0%9F%90%9B+Bug%22). +- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue. +- Collect information about the bug: +- Stack trace (Traceback) +- OS, Platform and Version (Windows, Linux, macOS, x86, ARM) +- Possibly your input and the output +- Can you reliably reproduce the issue? And can you also reproduce it with older versions? + +#### How Do I Submit a Good Bug Report? + +> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to vyspr@tuta.io. + +We use GitHub issues to track bugs and errors. If you run into an issue with the project: + +- Open an [Issue](https://github.com/Vysp3r/RetroPlus/issues/new). +- Explain the behavior you would expect and the actual behavior. +- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. +- Provide the information you collected in the previous section. + + +### Suggesting Enhancements + +This section guides you through submitting an enhancement suggestion for RetroPlus, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. + +#### Before Submitting an Enhancement + +- Make sure that you are using the latest version. +- Read the [documentation](https://github.com/Vysp3r/RetroPlus/#readme) carefully and find out if the functionality is already covered, maybe by an individual configuration. +- Perform a [search](https://github.com/Vysp3r/RetroPlus/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. +- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. + +#### How Do I Submit a Good Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://github.com/Vysp3r/RetroPlus/issues). + +- Use a **clear and descriptive title** for the issue to identify the suggestion. +- Provide a **step-by-step description of the suggested enhancement** in as many details as possible. +- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you. +- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://github.com/ShareX/ShareX) to record GIFs on Windows, and [this tool](https://github.com/phw/peek) on linux. +- **Explain why this enhancement would be useful** to most RetroPlus users. You may also want to point out the other projects that solved it better and which could serve as inspiration. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e32c4e3 --- /dev/null +++ b/README.md @@ -0,0 +1,167 @@ +

+ +

+ RetroPlus +

+ +

+ A simple ROM downloader +

+ +

+ + Stars + + + Latest Release + + + License + + + Telegram + + + Discord + +

+ +

+ + Please do not theme this app + +

+ +

+ Join the Telegram/Discord! โ€” Don't forget to star the repo if you are enjoying the project! +

+ +

+ Preview +

+ +- - - - + +## ๐Ÿ—๏ธ Building from source + +
+ Requirements + +- [git](https://github.com/git/git) +- [ninja](https://github.com/ninja-build/ninja) +- [meson >= 0.62.0](https://github.com/mesonbuild/meson) +- [gtk4](https://gitlab.gnome.org/GNOME/gtk/) +- [libadwaita >= 1.4](https://gitlab.gnome.org/GNOME/libadwaita) +- [json-glib](https://gitlab.gnome.org/GNOME/json-glib) +- [libsoup](https://gitlab.gnome.org/GNOME/libsoup) +- [desktop-file-utils](https://gitlab.freedesktop.org/xdg/desktop-file-utils) +- [libgee](https://gitlab.gnome.org/GNOME/libgee) +
+ + +
+ Fedora + +1. Install all dependencies: + ```bash + sudo dnf install \ + git \ + ninja-build \ + meson \ + gtk4-devel \ + libadwaita-devel \ + json-glib-devel \ + libsoup3-devel \ + desktop-file-utils \ + libgee-devel + ``` + +2. Clone the GitHub repo and change to repo directory: + ```bash + git clone https://github.com/Vysp3r/RetroPlus.git && \ + cd RetroPlus + ``` + +3. Build the source: + ```bash + meson build --prefix=/usr && \ + cd build && \ + ninja + ``` + +4. (Optional) Install application: + ```bash + ninja install + ``` + +5. Start application: + ```bash + cd src && \ + ./com.vysp3r.RetroPlus + ``` +
+ +
+ Flatpak Builder + +1. Install the distro dependencies using your package manager (apt, dnf, pacman, etc): + ```bash + sudo \ + git \ + flatpak \ + flatpak-builder + ``` + +2. Add the flathub repo to your user if not added before: + ```bash + flatpak --user --if-not-exists remote-add \ + flathub https://flathub.org/repo/flathub.flatpakrepo + ``` + +3. Install the needed runtimes for flatpak: + ```bash + flatpak --user install \ + runtime/org.gnome.Sdk/x86_64/43 \ + runtime/org.gnome.Platform/x86_64/43 + ``` + +4. Clone the GitHub repo and change to repo directory: + ```bash + git clone https://github.com/Vysp3r/RetroPlus.git && \ + cd RetroPlus + ``` + +5. Build the source inside the "build-dir" in the repo directory and install for the current user: + ```bash + flatpak-builder --user --install --force-clean \ + build-dir \ + com.vysp3r.RetroPlus.json + ``` + +6. Start application: + ```bash + flatpak --user run \ + com.vysp3r.RetroPlus + ``` +
+ +- - - - + +## ๐Ÿ™Œ Contribute to RetroPlus +**Please read our [Contribution Guidelines](/CONTRIBUTING.md)** + +All contributions are highly appreciated. + +- - - - + +## โœจ๏ธ Contributors + +[![Contributors](https://contrib.rocks/image?repo=Vysp3r/RetroPlus)](https://github.com/Vysp3r/RetroPlus/graphs/contributors) + +- - - - + +## ๐Ÿ’ Acknowledgment + +This README is based on the README from [Gradience](https://github.com/GradienceTeam/Gradience) by [Gradience Team](https://github.com/GradienceTeam) + +**[โคด๏ธ Back to Top](#RetroPlus)** diff --git a/com.vysp3r.RetroPlus.json b/com.vysp3r.RetroPlus.json new file mode 100644 index 0000000..9307db3 --- /dev/null +++ b/com.vysp3r.RetroPlus.json @@ -0,0 +1,50 @@ +{ + "id": "com.vysp3r.RetroPlus", + "runtime": "org.gnome.Platform", + "runtime-version": "45", + "sdk": "org.gnome.Sdk", + "sdk-extensions": [ + "org.freedesktop.Sdk.Extension.vala" + ], + "command": "com.vysp3r.RetroPlus", + "finish-args": [ + "--share=network", + "--share=ipc", + "--socket=fallback-x11", + "--device=dri", + "--filesystem=host", + "--talk-name=org.gtk.vfs.*", + "--filesystem=xdg-run/gvfsd" + ], + "build-options": { + "append-path": "/usr/lib/sdk/vala/bin", + "prepend-ld-library-path": "/usr/lib/sdk/vala/lib" + }, + "cleanup": [ + "/include", + "/lib/pkgconfig", + "/man", + "/share/doc", + "/share/gtk-doc", + "/share/man", + "/share/pkgconfig", + "/share/vala", + "*.la", + "*.a" + ], + "modules": [ + { + "name": "RetroPlus", + "builddir": true, + "buildsystem": "meson", + "sources": [ + { + "type": "git", + "url": "https://github.com/Vysp3r/RetroPlus.git", + "tag": "", + "commit": "" + } + ] + } + ] +} \ No newline at end of file diff --git a/data/com.vysp3r.RetroPlus.appdata.xml.in b/data/com.vysp3r.RetroPlus.appdata.xml.in new file mode 100644 index 0000000..4db4ffc --- /dev/null +++ b/data/com.vysp3r.RetroPlus.appdata.xml.in @@ -0,0 +1,9 @@ + + + com.vysp3r.RetroPlus.desktop + CC0-1.0 + GPL-3.0-or-later + +

No description

+
+
\ No newline at end of file diff --git a/data/com.vysp3r.RetroPlus.desktop.in b/data/com.vysp3r.RetroPlus.desktop.in new file mode 100644 index 0000000..705978c --- /dev/null +++ b/data/com.vysp3r.RetroPlus.desktop.in @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=RetroPlus +Exec=com.vysp3r.RetroPlus +Icon=com.vysp3r.RetroPlus +Terminal=false +Type=Application +Categories=GTK; +StartupNotify=true diff --git a/data/com.vysp3r.RetroPlus.gschema.xml b/data/com.vysp3r.RetroPlus.gschema.xml new file mode 100644 index 0000000..4ce17bb --- /dev/null +++ b/data/com.vysp3r.RetroPlus.gschema.xml @@ -0,0 +1,9 @@ + + + + + "" + The directory where downloads will be stored + + + \ No newline at end of file diff --git a/data/css/css.gresource.xml b/data/css/css.gresource.xml new file mode 100644 index 0000000..e8b6ae0 --- /dev/null +++ b/data/css/css.gresource.xml @@ -0,0 +1,6 @@ + + + + style.css + + \ No newline at end of file diff --git a/data/css/style.css b/data/css/style.css new file mode 100644 index 0000000..6396788 --- /dev/null +++ b/data/css/style.css @@ -0,0 +1,7 @@ +.p-10 { + padding: 10px; +} + +.search-row-extra { + text-shadow: -1px 0 red, 0 1px red, 1px 0 red, 0 -1px red, -1px -1px red, 1px 1px red, -1px 1px red, 1px -1px red; +} \ No newline at end of file diff --git a/data/icons/book-half.svg b/data/icons/book-half.svg new file mode 100644 index 0000000..6f2ca93 --- /dev/null +++ b/data/icons/book-half.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/data/icons/download.svg b/data/icons/download.svg new file mode 100644 index 0000000..3c30f1e --- /dev/null +++ b/data/icons/download.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/data/icons/funnel-fill.svg b/data/icons/funnel-fill.svg new file mode 100644 index 0000000..04d31a6 --- /dev/null +++ b/data/icons/funnel-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/data/icons/icons.gresource.xml b/data/icons/icons.gresource.xml new file mode 100644 index 0000000..06f366d --- /dev/null +++ b/data/icons/icons.gresource.xml @@ -0,0 +1,10 @@ + + + + book-half.svg + funnel-fill.svg + search.svg + download.svg + x-lg.svg + + \ No newline at end of file diff --git a/data/icons/search.svg b/data/icons/search.svg new file mode 100644 index 0000000..d9deb3c --- /dev/null +++ b/data/icons/search.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/data/icons/x-lg.svg b/data/icons/x-lg.svg new file mode 100644 index 0000000..b689cbb --- /dev/null +++ b/data/icons/x-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/data/logo/logo.png b/data/logo/logo.png new file mode 100644 index 0000000..442b6bb Binary files /dev/null and b/data/logo/logo.png differ diff --git a/data/logo/logo.svg b/data/logo/logo.svg new file mode 100644 index 0000000..271fbf2 --- /dev/null +++ b/data/logo/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/data/logo/logo.xcf b/data/logo/logo.xcf new file mode 100644 index 0000000..4f50ff4 Binary files /dev/null and b/data/logo/logo.xcf differ diff --git a/data/meson.build b/data/meson.build new file mode 100644 index 0000000..b9402d7 --- /dev/null +++ b/data/meson.build @@ -0,0 +1,64 @@ +icons_gresource = gnome.compile_resources( + 'gresource_icons', + 'icons/icons.gresource.xml', + source_dir: 'icons' +) + +css_gresource = gnome.compile_resources( + 'gresource_css', + 'css/css.gresource.xml', + source_dir: 'css' +) + +install_data('com.vysp3r.RetroPlus.gschema.xml', + install_dir: get_option('datadir') + '/glib-2.0/schemas' +) + +icon_sizes = ['32', '48', '64', '128'] +foreach i : icon_sizes + install_data( + join_paths('logo', 'logo.svg'), + install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'icons', 'hicolor', i + 'x' + i, 'apps'), + rename: meson.project_name() + '.svg' + ) + + install_data( + join_paths('logo', 'logo.svg'), + install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'icons', 'hicolor', i + 'x' + i + '@2', 'apps'), + rename: meson.project_name() + '.svg' + ) +endforeach + +desktop_file = i18n.merge_file( + input: 'com.vysp3r.RetroPlus.desktop.in', + output: 'com.vysp3r.RetroPlus.desktop', + type: 'desktop', + po_dir: '../po', + install: true, + install_dir: join_paths(get_option('datadir'), 'applications') +) + +desktop_utils = find_program('desktop-file-validate', required: false) +if desktop_utils.found() + test('Validate desktop file', desktop_utils, args: [desktop_file]) +endif + +appstream_file = i18n.merge_file( + input: 'com.vysp3r.RetroPlus.appdata.xml.in', + output: 'com.vysp3r.RetroPlus.appdata.xml', + po_dir: '../po', + install: true, + install_dir: join_paths(get_option('datadir'), 'appdata') +) + +appstream_util = find_program('appstream-util', required: true) +if appstream_util.found() + test('Validate appstream file', appstream_util, args: ['validate', appstream_file]) +endif + +compile_schemas = find_program('glib-compile-schemas', required: true) +if compile_schemas.found() + test('Validate schema file', + compile_schemas, + args: ['--strict', '--dry-run', meson.current_source_dir()]) +endif diff --git a/data/previews/Preview-1.png b/data/previews/Preview-1.png new file mode 100644 index 0000000..8b829d9 Binary files /dev/null and b/data/previews/Preview-1.png differ diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..5def0cd --- /dev/null +++ b/meson.build @@ -0,0 +1,20 @@ +project('com.vysp3r.RetroPlus', ['c', 'vala'], + version: '0.1.0', + meson_version: '>= 0.62.0', + default_options: [ 'warning_level=2', 'werror=false', ], +) + +i18n = import('i18n') +gnome = import('gnome') + +add_global_arguments('-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()), language: 'c') + +subdir('data') +subdir('src') +subdir('po') + +gnome.post_install( + glib_compile_schemas: true, + gtk_update_icon_cache: true, + update_desktop_database: true, +) diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..5fcd837 --- /dev/null +++ b/po/LINGUAS @@ -0,0 +1,10 @@ +cs +de +es +fr +it +ru +id +pt +zh +be diff --git a/po/POTFILES b/po/POTFILES new file mode 100644 index 0000000..a01d92d --- /dev/null +++ b/po/POTFILES @@ -0,0 +1,12 @@ +data/com.vysp3r.RetroPlus.desktop.in +data/com.vysp3r.RetroPlus.appdata.xml.in +src/window.vala +src/application.vala +src/preferences.vala +src/widgets/download-popover.vala +src/widgets/download-row.vala +src/widgets/game-detail-modal.vala +src/widgets/search-box.vala +src/widgets/search-filter-box.vala +src/widgets/search-row.vala +src/widgets/status-box.vala diff --git a/po/be.po b/po/be.po new file mode 100644 index 0000000..730d201 --- /dev/null +++ b/po/be.po @@ -0,0 +1,253 @@ +# Belarusian translations for com.vysp3r.RetroPlus package. +# Copyright (C) 2024 THE com.vysp3r.RetroPlus'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# Automatically generated, 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: 2024-01-21 16:48-0500\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: be\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/com.vysp3r.RetroPlus.pot b/po/com.vysp3r.RetroPlus.pot new file mode 100644 index 0000000..3a3eb8a --- /dev/null +++ b/po/com.vysp3r.RetroPlus.pot @@ -0,0 +1,252 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/com.vysp3r.VimmPlus.pot b/po/com.vysp3r.VimmPlus.pot new file mode 100644 index 0000000..4ad1fca --- /dev/null +++ b/po/com.vysp3r.VimmPlus.pot @@ -0,0 +1,239 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-01-29 21:29-0500\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:17 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:71 +#: src/widgets/search-filter-box.vala:15 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:81 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:86 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:41 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:42 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:43 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:60 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:63 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:76 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:224 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:234 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:240 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:246 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:252 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:258 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/cs.po b/po/cs.po new file mode 100644 index 0000000..c990ef5 --- /dev/null +++ b/po/cs.po @@ -0,0 +1,252 @@ +# Czech translations for com.vysp3r.RetroPlus package. +# Copyright (C) 2024 THE com.vysp3r.RetroPlus'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# Automatically generated, 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: 2024-01-21 16:48-0500\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/de.po b/po/de.po new file mode 100644 index 0000000..05754f8 --- /dev/null +++ b/po/de.po @@ -0,0 +1,252 @@ +# German translations for com.vysp3r.RetroPlus package. +# Copyright (C) 2024 THE com.vysp3r.RetroPlus'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# Automatically generated, 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: 2024-01-21 16:48-0500\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/es.po b/po/es.po new file mode 100644 index 0000000..c2d24aa --- /dev/null +++ b/po/es.po @@ -0,0 +1,252 @@ +# Spanish translations for com.vysp3r.RetroPlus package. +# Copyright (C) 2024 THE com.vysp3r.RetroPlus'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# Automatically generated, 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: 2024-01-21 16:48-0500\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..6a8cfc9 --- /dev/null +++ b/po/fr.po @@ -0,0 +1,252 @@ +# French translations for com.vysp3r.RetroPlus package. +# Copyright (C) 2024 THE com.vysp3r.RetroPlus'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# Automatically generated, 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: 2024-01-21 16:48-0500\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/id.po b/po/id.po new file mode 100644 index 0000000..e5110dc --- /dev/null +++ b/po/id.po @@ -0,0 +1,251 @@ +# Indonesian translations for com.vysp3r.RetroPlus package. +# Copyright (C) 2024 THE com.vysp3r.RetroPlus'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# Automatically generated, 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: 2024-01-21 16:48-0500\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: id\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/it.po b/po/it.po new file mode 100644 index 0000000..90e8e11 --- /dev/null +++ b/po/it.po @@ -0,0 +1,252 @@ +# Italian translations for com.vysp3r.RetroPlus package. +# Copyright (C) 2024 THE com.vysp3r.RetroPlus'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# Automatically generated, 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: 2024-01-21 16:48-0500\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/meson.build b/po/meson.build new file mode 100644 index 0000000..1fb320e --- /dev/null +++ b/po/meson.build @@ -0,0 +1,4 @@ +i18n.gettext( + meson.project_name(), + preset: 'glib' +) diff --git a/po/pt.po b/po/pt.po new file mode 100644 index 0000000..e7dc356 --- /dev/null +++ b/po/pt.po @@ -0,0 +1,252 @@ +# Portuguese translations for com.vysp3r.RetroPlus package. +# Copyright (C) 2024 THE com.vysp3r.RetroPlus'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# Automatically generated, 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: 2024-01-21 16:48-0500\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/ru.po b/po/ru.po new file mode 100644 index 0000000..d9d7ee1 --- /dev/null +++ b/po/ru.po @@ -0,0 +1,253 @@ +# Russian translations for com.vysp3r.RetroPlus package. +# Copyright (C) 2024 THE com.vysp3r.RetroPlus'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# Automatically generated, 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: 2024-01-21 16:48-0500\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/po/zh.po b/po/zh.po new file mode 100644 index 0000000..1f6a5bd --- /dev/null +++ b/po/zh.po @@ -0,0 +1,251 @@ +# Chinese translations for com.vysp3r.RetroPlus package. +# Copyright (C) 2024 THE com.vysp3r.RetroPlus'S COPYRIGHT HOLDER +# This file is distributed under the same license as the com.vysp3r.RetroPlus package. +# Automatically generated, 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: com.vysp3r.RetroPlus\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-02-02 18:08-0500\n" +"PO-Revision-Date: 2024-01-21 16:48-0500\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: zh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" + +#: data/com.vysp3r.RetroPlus.desktop.in:3 +msgid "RetroPlus" +msgstr "" + +#: data/com.vysp3r.RetroPlus.appdata.xml.in:7 +msgid "No description" +msgstr "" + +#. +#: src/window.vala:23 +msgid "Loading" +msgstr "" + +#. +#. +#: src/window.vala:105 src/window.vala:110 +msgid "An error occured during the initialization." +msgstr "" + +#. +#: src/window.vala:115 +msgid "An error occured while loading the systems." +msgstr "" + +#. +#. +#: src/window.vala:121 +msgid "To fix this problem, you should report this issue on GitHub." +msgstr "" + +#: src/preferences.vala:20 +msgid "Choose the download directory" +msgstr "" + +#. +#: src/preferences.vala:45 +msgid "Download directory" +msgstr "" + +#. +#: src/widgets/download-popover.vala:15 +msgid "No download in progress" +msgstr "" + +#: src/widgets/download-row.vala:32 +msgid "Queued" +msgstr "" + +#: src/widgets/download-row.vala:45 +msgid "Starting" +msgstr "" + +#: src/widgets/download-row.vala:49 +msgid "Cancelling" +msgstr "" + +#. +#. +#: src/widgets/game-detail-modal.vala:23 +msgid "Close" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:27 +msgid "Download" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:72 src/widgets/search-box.vala:64 +#: src/widgets/search-filter-box.vala:17 +msgid "System" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:75 src/widgets/search-box.vala:74 +msgid "Region" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:103 +msgid "Players" +msgstr "" + +#: src/widgets/game-detail-modal.vala:103 +msgid "Simultaneous" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:106 +msgid "Year" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:109 +msgid "Publisher" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:112 +msgid "Serial" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:115 +#: src/widgets/game-detail-modal.vala:220 +msgid "Size" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:118 src/widgets/search-box.vala:79 +msgid "Version" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:147 +msgid "Play online" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:151 +msgid "See manual" +msgstr "" + +#. +#. +#. +#: src/widgets/game-detail-modal.vala:215 +#: src/widgets/game-detail-modal.vala:251 +msgid "CRC" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:216 +#: src/widgets/game-detail-modal.vala:255 +msgid "MD5" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:217 +#: src/widgets/game-detail-modal.vala:260 +msgid "SHA1" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:225 +msgid "Graphics" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:228 +msgid "Sound" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:231 +msgid "Gameplay" +msgstr "" + +#. +#: src/widgets/game-detail-modal.vala:234 +msgid "Overall" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "votes" +msgstr "" + +#: src/widgets/game-detail-modal.vala:234 +msgid "vote" +msgstr "" + +#. +#: src/widgets/search-box.vala:35 +msgid "Preferences" +msgstr "" + +#: src/widgets/search-box.vala:36 +msgid "Keyboard Shortcuts" +msgstr "" + +#: src/widgets/search-box.vala:37 +msgid "About" +msgstr "" + +#: src/widgets/search-box.vala:53 +msgid "Search a game by name" +msgstr "" + +#: src/widgets/search-box.vala:56 +msgid "Advanced filtering options" +msgstr "" + +#. +#: src/widgets/search-box.vala:69 +msgid "Title" +msgstr "" + +#. +#: src/widgets/search-box.vala:240 +msgid "An error occured while opening" +msgstr "" + +#: src/widgets/search-box.vala:250 +msgid "download queued" +msgstr "" + +#: src/widgets/search-box.vala:256 +msgid "finished downloading" +msgstr "" + +#: src/widgets/search-box.vala:262 +msgid "download cancelled" +msgstr "" + +#: src/widgets/search-box.vala:268 +msgid "could not download due to an error" +msgstr "" + +#: src/widgets/search-box.vala:274 +msgid " is already downloaded" +msgstr "" + +#. +#: src/widgets/search-filter-box.vala:33 +msgid "Source" +msgstr "" + +#. +#: src/widgets/search-row.vala:41 +msgid "Manual available" +msgstr "" diff --git a/src/application.vala b/src/application.vala new file mode 100644 index 0000000..e0c0a9e --- /dev/null +++ b/src/application.vala @@ -0,0 +1,97 @@ +namespace RetroPlus { + public class Application : Adw.Application { + public static Window main_window; + public static Settings settings; + + construct { + application_id = Constants.APP_ID; + flags |= ApplicationFlags.FLAGS_NONE; + + Intl.bindtextdomain (Constants.APP_ID, Constants.LOCALE_DIR); + } + + public override void activate () { + // + settings = new Settings ("com.vysp3r.RetroPlus"); + + // + var display = Gdk.Display.get_default (); + + // + Gtk.IconTheme.get_for_display (display).add_resource_path ("/com/vysp3r/RetroPlus/icons"); + + // + var css_provider = new Gtk.CssProvider (); + css_provider.load_from_resource ("/com/vysp3r/RetroPlus/css/style.css"); + + // + Gtk.StyleContext.add_provider_for_display (display, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + + // Register the action to display the about dialog + var about_action = new SimpleAction ("show-about", null); + about_action.activate.connect (this.show_about_dialog); + this.add_action (about_action); + + // Register the action to display the about dialog + var preferences_action = new SimpleAction ("show-preferences", null); + preferences_action.activate.connect (this.show_preferences_dialog); + this.add_action (preferences_action); + + // Register the action to close the app on Ctrl + Q + var quit_action = new SimpleAction ("quit", null); + quit_action.activate.connect (this.quit); + this.set_accels_for_action ("app.quit", { "Q" }); + this.add_action (quit_action); + + // + main_window = new Window (); + main_window.initialize (); + main_window.show (); + } + + void show_preferences_dialog () { + var preferences_dialog = new Preferences (main_window); + preferences_dialog.show (); + } + + void show_about_dialog () { + const string[] devs = { + "Charles Malouin (Vysp3r) https://github.com/Vysp3r", + null + }; + + const string[] thanks = { + "GNOME Project https://www.gnome.org/", + "Bootstrap Icons https://github.com/twbs/icons", + "Vimm's Lair https://vimm.net/", + null + }; + + var about_window = new Adw.AboutWindow (); + about_window.set_application_name (Constants.APP_NAME); + about_window.set_application_icon (Constants.APP_ID); + about_window.set_version (Constants.APP_VERSION); + about_window.set_comments ("A simple ROM downloader"); + about_window.add_link ("Github", "https://github.com/Vysp3r/RetroPlus"); + about_window.set_issue_url ("https://github.com/Vysp3r/RetroPlus/issues/new/choose"); + about_window.set_copyright ("ยฉ 2024 Vysp3r"); + about_window.set_license_type (Gtk.License.GPL_3_0); + about_window.set_developers (devs); + about_window.add_credit_section ("Special thanks to", thanks); + about_window.set_transient_for (main_window); + about_window.set_modal (true); + about_window.show (); + } + + public static int main (string[] args) { + if (Thread.supported () == false) { + message ("Threads are not supported!"); + return -1; + } + + var application = new Application (); + + return application.run (args); + } + } +} \ No newline at end of file diff --git a/src/constants.vala.in b/src/constants.vala.in new file mode 100644 index 0000000..3a5b449 --- /dev/null +++ b/src/constants.vala.in @@ -0,0 +1,7 @@ +namespace RetroPlus.Constants { + const string APP_NAME = "RetroPlus"; + const string APP_ID = @APP_ID@; + const string APP_VERSION = @APP_VERSION@; + const string LOCALE_DIR = @LOCALE_DIR@; + const string RESOURCE_BASE = "/com/vysp3r/RetroPlus"; +} \ No newline at end of file diff --git a/src/gresource.xml b/src/gresource.xml new file mode 100644 index 0000000..232472d --- /dev/null +++ b/src/gresource.xml @@ -0,0 +1,6 @@ + + + + gtk/help-overlay.ui + + \ No newline at end of file diff --git a/src/gtk/help-overlay.ui b/src/gtk/help-overlay.ui new file mode 100644 index 0000000..6852f4b --- /dev/null +++ b/src/gtk/help-overlay.ui @@ -0,0 +1,29 @@ + + + + True + + + shortcuts + 10 + + + General + + + Show Shortcuts + win.show-help-overlay + + + + + Quit + app.quit + + + + + + + + \ No newline at end of file diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..ec31561 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,60 @@ +configuration = configuration_data() +configuration.set_quoted('APP_ID', meson.project_name()) +configuration.set_quoted('APP_VERSION', meson.project_version()) +configuration.set_quoted('LOCALE_DIR', join_paths (get_option('prefix'), get_option('localedir'))) + +constants = configure_file( + input : 'constants.vala.in', + output : 'constants.vala', + configuration : configuration +) + +sources = [ + 'application.vala', + + 'window.vala', + 'preferences.vala', + + 'widgets/search-box.vala', + 'widgets/status-box.vala', + 'widgets/game-detail-modal.vala', + 'widgets/download-popover.vala', + 'widgets/search-row.vala', + 'widgets/download-row.vala', + 'widgets/search-filter-box.vala', + + 'utils/web.vala', + 'utils/filesystem.vala', + 'utils/parser.vala', + + 'models/game.vala', + 'models/system.vala', + 'models/media.vala', + 'models/region.vala', + 'models/extra.vala', + 'models/source.vala', +] + +sources += gnome.compile_resources('sources', + 'gresource.xml', + c_name: 'ui' +) + +deps = [ + dependency('gtk4'), + dependency('libadwaita-1', version: '>= 1.3'), + dependency('libarchive'), + dependency('libsoup-3.0'), + dependency('json-glib-1.0'), + dependency('gee-0.8'), +] + +executable( + meson.project_name(), + sources, + icons_gresource, + css_gresource, + constants, + dependencies: deps, + install: true, +) diff --git a/src/models/extra.vala b/src/models/extra.vala new file mode 100644 index 0000000..e6abf39 --- /dev/null +++ b/src/models/extra.vala @@ -0,0 +1,11 @@ +namespace RetroPlus.Models { + public class Extra : Object { + public string title; + public string short_title; + + public Extra (string title, string short_title) { + this.title = title; + this.short_title = short_title; + } + } +} \ No newline at end of file diff --git a/src/models/game.vala b/src/models/game.vala new file mode 100644 index 0000000..cbbd2ca --- /dev/null +++ b/src/models/game.vala @@ -0,0 +1,231 @@ +namespace RetroPlus.Models { + public class Game : Object { + public int id { get; private set; } + public string title { get; private set; } + public string system { get; set; } + public List regions; + public int max_players { get; set; } + public bool has_max_players { + get { + return max_players > 0; + } + } + public bool simultaneous { get; set; } + public int year { get; set; } + public bool has_year { + get { + return year > 0; + } + } + public string? publisher { get; set; } + public bool has_publisher { + get { + return publisher != null; + } + } + public string? serial { get; set; } + public bool has_serial { + get { + return serial != null; + } + } + public double graphics_rating { get; set; } + public double sound_rating { get; set; } + public double gameplay_rating { get; set; } + public double overall_rating { get; set; } + public int total_votes { get; set; } + public bool rated { get; set; } + public List medias; + public string last_verification_date { get; set; } + public bool support_play_online { get; set; } + public bool missing { get; set; } + public int manual_id { get; set; } + public bool has_manual { + get { + return manual_id > 0; + } + } + public List extras; + public bool loaded { get; set; } + public string download_server { get; set; } + + public Game (int id, string title) { + this.id = id; + this.title = title; + } + + public delegate bool download_cancel_callback (); + + public delegate void download_progress_callback (double progress); + + public delegate void download_speed_callback (double bytes); + + public Game.from_search (int id, string system, string title, int manual_id, List extras, List regions, Media media) { + this.id = id; + this.system = system; + this.title = title; + this.manual_id = manual_id; + + this.medias.append (media); + + foreach (var extra in extras) { + this.extras.append (extra); + } + + foreach (var region in regions) { + this.regions.append (region); + } + } + + public string get_url () { + return @"https://vimm.net/vault/$id"; + } + + public string get_download_url (int media_id) { + return @"$download_server?mediaId=$media_id"; + } + + public string get_rating_url () { + return @"https://vimm.net/vault/?p=rating&id=$id"; + } + + public string get_play_online_url () { + return @"https://vimm.net/vault/?p=play&id=$id"; + } + + public string get_screen_image_url () { + return @"https://vimm.net/image.php?type=screen&id=$id"; + } + + public string get_box_image_url () { + return @"https://vimm.net/image.php?type=box&id=$id"; + } + + public string get_manual_url () { + return @"https://vimm.net/manual/?p=details&id=$manual_id"; + } + + public async bool load () { + if (loaded)return true; + + SourceFunc callback = load.callback; + bool output = true; + + ThreadFunc run = () => { + string res = ""; + if (!Utils.Web.get_request (get_url (), ref res)) { + return output = false; + } + + var game = this; + + if (!Utils.Parser.parse_game_request (res, ref game)) { + return output = false; + } + + loaded = true; + + Idle.add ((owned) callback); + return true; + }; + new Thread ("game-load", (owned) run); + + yield; + return output; + } + + public enum DownloadResults { + SUCCESS, + FILE_EXISTS, + ERROR + } + + public async DownloadResults download (Models.Media media, download_cancel_callback download_cancel_callback, download_progress_callback download_progress_callback, download_speed_callback download_speed_callback) { + try { + var session = new Soup.Session (); + session.set_user_agent (Utils.Web.get_user_agent ()); + + var message = new Soup.Message ("GET", get_download_url (media.id)); + message.request_headers.append ("Accept-Encoding", "gzip, deflate, br"); + message.request_headers.append ("Referer", "https://vimm.net/"); + message.request_headers.append ("Sec-Fetch-Dest", "document"); + message.request_headers.append ("Sec-Fetch-Mode", "navigate"); + message.request_headers.append ("Sec-Fetch-Site", "same-site"); + message.request_headers.append ("Sec-Fetch-User", "?1"); + message.request_headers.append ("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"); + + var input_stream = yield session.send_async (message, GLib.Priority.DEFAULT, null); + + if (message.status_code != 200) { + GLib.message (message.reason_phrase); + return DownloadResults.ERROR; + } + + string disposition; + GLib.HashTable params; + message.response_headers.get_content_disposition (out disposition, out params); + + if (!params.contains ("filename"))return DownloadResults.ERROR; + + var path = Application.settings.get_string ("download-directory") + "/" + params.get ("filename"); + + var file = GLib.File.new_for_path (path); + + if (file.query_exists ())return DownloadResults.FILE_EXISTS; + + FileOutputStream output_stream = yield file.create_async (FileCreateFlags.NONE, GLib.Priority.DEFAULT, null); + + const size_t chunk_size = 4096; + ulong bytes_downloaded = 0; + int64 total_bytes = message.response_headers.get_content_length (); + uint64 last_update = GLib.get_real_time (); + + while (true) { + if (download_cancel_callback ()) { + if (file.query_exists ()) { + file.delete (); + } + break; + } + + var chunk = yield input_stream.read_bytes_async (chunk_size); + + if (chunk.get_size () == 0) { + break; + } + + bytes_downloaded += output_stream.write (chunk.get_data ()); + + if (download_progress_callback != null) { + double progress = (bytes_downloaded * 1.0f) / (total_bytes * 1.0f); + download_progress_callback (progress); + } + + if (download_speed_callback != null) { + var download_speed = (int) (((double) bytes_downloaded) / (double) (get_real_time () - last_update) * ((double) 1000000)); + download_speed_callback (download_speed); + } + } + + yield output_stream.close_async (); + + session.abort (); + + yield nap (2000); + + return DownloadResults.SUCCESS; + } catch (GLib.Error e) { + GLib.message (e.message); + return DownloadResults.ERROR; + } + } + + async void nap (uint interval, int priority = GLib.Priority.DEFAULT) { + GLib.Timeout.add (interval, () => { + nap.callback (); + return false; + }, priority); + yield; + } + } +} \ No newline at end of file diff --git a/src/models/media.vala b/src/models/media.vala new file mode 100644 index 0000000..1f7768f --- /dev/null +++ b/src/models/media.vala @@ -0,0 +1,40 @@ +namespace RetroPlus.Models { + public class Media : Object { + public int id; + public double version; + public string? crc; + public string? md5; + public string? sha1; + public bool has_hash { + get { + return crc != null && md5 != null && sha1 != null; + } + } + public double download_size; + + public string get_formatted_download_size () { + if (download_size > 1048576) { + return "%.2f GB".printf (download_size / 1048576); + } + + if (download_size > 1024) { + return "%.2f MB".printf (download_size / 1024); + } + + return "%.2f KB".printf (download_size); + } + + public Media (double version) { + this.version = version; + } + + public Media.extra (int id, double version, string? crc, string? md5, string? sha1, double download_size) { + this.id = id; + this.version = version; + this.crc = crc; + this.md5 = md5; + this.sha1 = sha1; + this.download_size = download_size; + } + } +} \ No newline at end of file diff --git a/src/models/region.vala b/src/models/region.vala new file mode 100644 index 0000000..7800e00 --- /dev/null +++ b/src/models/region.vala @@ -0,0 +1,86 @@ +namespace RetroPlus.Models { + public class Region : Object { + public string title; + string flag_filename; + + public delegate void flag_callback (Models.Region region); + + public Region (string title, string flag_filename) { + this.title = title; + this.flag_filename = flag_filename; + } + + public string get_flag_url () { + return @"https://vimm.net/images/flags/$flag_filename"; + } + + public string get_flag_path () { + return get_flags_location () + flag_filename; + } + + public async bool download_flag () { + try { + if (FileUtils.test (get_flag_path (), GLib.FileTest.EXISTS))return true; + + var session = new Soup.Session (); + session.set_user_agent (Utils.Web.get_user_agent ()); + + var message = new Soup.Message ("GET", get_flag_url ()); + + var input_stream = session.send (message, null); + + if (message.status_code != 200) { + GLib.message (message.reason_phrase); + return false; + } + + var file = GLib.File.new_for_path (get_flag_path ()); + + FileOutputStream output_stream = file.create (FileCreateFlags.REPLACE_DESTINATION, null); + + const size_t chunk_size = 4096; + ulong bytes_downloaded = 0; + + while (true) { + var chunk = input_stream.read_bytes (chunk_size); + + if (chunk.get_size () == 0) { + break; + } + + bytes_downloaded += output_stream.write (chunk.get_data ()); + } + + output_stream.close (); + + session.abort (); + + return true; + } catch (GLib.Error e) { + GLib.message (e.message); + return false; + } + } + + public static async void load_flags (List regions, flag_callback flag_callback) { + foreach (var region in regions) { + flag_callback (region); + } + } + + public static string get_flags_location () { + return Environment.get_user_data_dir () + @"/flags/"; + } + + public static bool initialize () { + // + if (!FileUtils.test (get_flags_location (), FileTest.IS_DIR)) { + bool flags_valid = Utils.Filesystem.CreateDirectory (get_flags_location ()); + if (!flags_valid)return false; + } + + // + return true; + } + } +} \ No newline at end of file diff --git a/src/models/source.vala b/src/models/source.vala new file mode 100644 index 0000000..d809740 --- /dev/null +++ b/src/models/source.vala @@ -0,0 +1,9 @@ +namespace RetroPlus.Models { + public class Source : Object { + public string title { get; set; } + + public Source (string title) { + this.title = title; + } + } +} \ No newline at end of file diff --git a/src/models/system.vala b/src/models/system.vala new file mode 100644 index 0000000..aec5468 --- /dev/null +++ b/src/models/system.vala @@ -0,0 +1,109 @@ +namespace RetroPlus.Models { + public class System : Object { + public string id { get; private set; } + public string title { get; private set; } + public bool handheld { get; private set; } + public uint year { get; private set; } + public bool extra_info_loaded { get; set; } + public uint media_count { get; set; } + public uint media_total { get; set; } + public string media_last_synchronization_date { get; set; } + public unowned List monthly_top_ten_downloads_list { get; set; } + public unowned List overall_rating_list { get; set; } + public unowned List graphics_list { get; set; } + public unowned List sound_list { get; set; } + public unowned List gameplay_list { get; set; } + + public string get_url() { + return "https://vimm.net/vault/" + id; + } + + public float get_media_pourcentage() { + return (media_count / (float) media_total) * 100; + } + + public System(string id, string title, bool handheld, uint year) { + this.id = id; + this.title = title; + this.handheld = handheld; + this.year = year; + } + + public bool load_extra_info(bool force_load = false) { + // + if (extra_info_loaded && !force_load)return true; + + // + var res = ""; + var res_valid = Utils.Web.get_request(get_url(), ref res); + + // + if (!res_valid)return false; + + // + var temp_system = this; + var parsed = Utils.Parser.parse_system_request(res, ref temp_system); + + // + return extra_info_loaded = !parsed; + } + + public async List get_games_by_title(string game_title) { + SourceFunc callback = get_games_by_title.callback; + + var games = new List (); + + ThreadFunc run = () => { + // + string res = ""; + + // + var res_valid = Utils.Web.get_request(@"https://vimm.net/vault/?mode=adv&p=list&system=$id&q=$game_title&players=%3E%3D&playersValue=1&simultaneous=&publisher=&year=%3D&yearValue=&rating=%3E%3D&ratingValue=®ion=All&sort=Title&sortOrder=ASC", ref res); + if (!res_valid)return; + + // + var parsing_valid = Utils.Parser.parse_search_request(res, ref games); + if (!parsing_valid)return; + + // + Idle.add((owned) callback); + + // + return; + }; + new Thread ("search", (owned) run); + + yield; + return (owned) games; + } + + public static bool get_systems(ref Gee.HashMap systems) { + // + var system = new System("", "All", false, 0); + systems.set(system.id, system); + + // + uint[] console_years = { 1977, 1982, 1983, 1985, 1986, 1988, 1990, 1994, 1994, 1994, 1996, 1998, 2000, 2001, 2001, 2005, 2006, 2006, 2008 }; + string[] console_ids = { "Atari2600", "Atari5200", "NES", "SMS", "Atari7800", "Genesis", "SNES", "32X", "Saturn", "PS1", "N64", "Dreamcast", "PS2", "GameCube", "Xbox", "Xbox360", "PS3", "Wii", "WiiWare" }; + string[] console_names = { "Atari 2600", "Atari 5200", "Nintendo", "Master System", "Atari 7800", "Genesis", "Super Nintendo", "Sega 32X", "Saturn", "PlayStation", "Nintendo 64", "Dreamcast", "PlayStation 2", "GameCube", "Xbox", "Xbox 360", "PlayStation 3", "Wii", "WiiWare" }; + + for (var i = 0; i < console_names.length; i++) { + system = new System(console_ids[i], console_names[i], false, console_years[i]); + systems.set(console_ids[i], system); + } + + // + uint[] handheld_years = { 1989, 1989, 1990, 1995, 1998, 2001, 2004, 2004 }; + string[] handheld_ids = { "GB", "Lynx", "GG", "VB", "GBC", "GBA", "DS", "PSP" }; + string[] handheld_names = { "Game Boy", "Lynx", "Game Gear", "Virtual Boy", "Game Boy Color", "Game Boy Advance", "Nintendo DS", "PlayStation Portable" }; + + for (var i = 0; i < handheld_names.length; i++) { + system = new System(handheld_ids[i], handheld_names[i], true, handheld_years[i]); + systems.set(handheld_ids[i], system); + } + + // + return true; + } + } +} \ No newline at end of file diff --git a/src/preferences.vala b/src/preferences.vala new file mode 100644 index 0000000..de94585 --- /dev/null +++ b/src/preferences.vala @@ -0,0 +1,64 @@ +namespace RetroPlus { + public class Preferences : Adw.PreferencesWindow { + public Preferences(Window parent) { + this.set_transient_for(parent); + } + + construct { + // + this.set_size_request(250, 175); + this.set_default_size(250, 175); + + // + this.add(get_test_page()); + } + + Adw.PreferencesPage get_test_page() { + // + var file_dialog = new Gtk.FileDialog(); + file_dialog.set_modal(true); + file_dialog.set_title(_("Choose the download directory")); + + // + var download_directory_button = new Gtk.Button.from_icon_name("folder-open-symbolic"); + download_directory_button.add_css_class("flat"); + download_directory_button.set_size_request(25, 25); + download_directory_button.clicked.connect(() => { + file_dialog.select_folder.begin(this, null, (obj, res) => { + try { + var file = file_dialog.select_folder.end(res); + if (file == null)return; + Application.settings.set_string("download-directory", file.get_path()); + } catch (Error e) { + message(e.message); + } + }); + }); + + // + var download_directory_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 10); + download_directory_box.set_valign(Gtk.Align.CENTER); + download_directory_box.append(download_directory_button); + + // + var download_directory_row = new Adw.EntryRow(); + download_directory_row.set_title(_("Download directory")); + download_directory_row.set_editable(false); + download_directory_row.add_suffix(download_directory_box); + + // + Application.settings.bind("download-directory", download_directory_row, "text", GLib.SettingsBindFlags.DEFAULT); + + // + var main_group = new Adw.PreferencesGroup(); + main_group.add(download_directory_row); + + // + var page = new Adw.PreferencesPage(); + page.add(main_group); + + // + return page; + } + } +} \ No newline at end of file diff --git a/src/utils/filesystem.vala b/src/utils/filesystem.vala new file mode 100644 index 0000000..dd184b8 --- /dev/null +++ b/src/utils/filesystem.vala @@ -0,0 +1,62 @@ +namespace RetroPlus.Utils { + public class Filesystem { + public static bool GetFileContent (string path, ref string output) { + try { + File file = File.new_for_path (path); + + uint8[] contents; + string etag_out; + file.load_contents (null, out contents, out etag_out); + + output = (string) contents; + + return true; + } catch (GLib.Error e) { + message (e.message); + return false; + } + } + + public static bool ModifyFile (string path, string content) { + try { + FileUtils.set_contents (path, content, content.length); + return true; + } catch (GLib.Error e) { + message (e.message); + return false; + } + } + + public static bool CreateFile (string path, string? content = null) { + try { + var file = GLib.File.new_for_path (path); + FileOutputStream os = file.create (FileCreateFlags.PRIVATE); + if (content != null)os.write (content.data); + return true; + } catch (GLib.Error e) { + message (e.message); + return false; + } + } + + public static bool CreateDirectory (string path) { + try { + var directory = GLib.File.new_for_path (path); + directory.make_directory (); + return true; + } catch (GLib.Error e) { + message (e.message); + return false; + } + } + + public static Gdk.Pixbuf? GetPixbufFromFile (string path) { + try { + return new Gdk.Pixbuf.from_file (path); + } catch (Error e) { + // message (@"Failed loading image for $title ($id)"); + return null; + } + } + } +} \ No newline at end of file diff --git a/src/utils/parser.vala b/src/utils/parser.vala new file mode 100644 index 0000000..49cd591 --- /dev/null +++ b/src/utils/parser.vala @@ -0,0 +1,816 @@ +namespace RetroPlus.Utils { + public class Parser { + public static bool parse_markup (string text, out string parsed_text) { + try { + Pango.AttrList attr_list; + unichar accel_char; + Pango.parse_markup (text, text.length, 0, out attr_list, out parsed_text, out accel_char); + return true; + } catch { + return false; + } + } + + public static bool parse_search_request (string res, ref List games) { + if (res.length == 0)return false; + + var start_text = ""; + var start = res.index_of (start_text, res.index_of (start_text) + start_text.length); + + var end_text = ""; + var end = res.index_of (end_text, start); + + var temp_list = res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + + if (temp_list.contains ("No matches found."))return true; + + var offset = res.contains (">System") ? 1 : 0; + + while (temp_list.length != 0) { + temp_list = temp_list.strip (); + + var raw_id = ""; + var raw_title = ""; + var raw_manual_id = ""; + var extras = new List (); + var regions = new List (); + Models.Media media = null; + var system = ""; + + start_text = ""; + start = temp_list.index_of (start_text); + + end_text = ""; + end = temp_list.index_of (end_text); + + var temp_game = temp_list.substring (start + start_text.length, end - (start + start_text.length)); + + temp_list = temp_list.substring (temp_game.length + start_text.length + end_text.length); + + if (temp_game.contains (""))break; + + for (int i = 0; i < 5; i++) { + start_text = ""; + } + + end = temp_line.index_of (end_text, start); + + raw_id = temp_line.substring (start + start_text.length, end - (start + start_text.length)); + raw_id = raw_id.replace ("/vault/", ""); + + // + start_text = ">"; + start = temp_line.index_of (start_text, end); + + end_text = ""; + end = temp_line.index_of (end_text, start); + + raw_title = temp_line.substring (start + start_text.length, end - (start + start_text.length)); + raw_title = raw_title.replace ("
", ""); + + // + var extra_start = temp_line.index_of ("redBorder", end); + + if (extra_start != -1) { + // + start_text = "title=\""; + start = temp_line.index_of (start_text, extra_start); + + + end_text = "\">"; + end = temp_line.index_of (end_text, start); + + var extra_title = temp_line.substring (start + start_text.length, end - (start + start_text.length)); + + // + start_text = ">"; + start = temp_line.index_of (start_text, end); + + end_text = ""; + end = temp_line.index_of (end_text, start); + + var extra_short_title = temp_line.substring (start + start_text.length, end - (start + start_text.length)); + + // + var extra = new Models.Extra (extra_title, extra_short_title); + extras.append (extra); + + // + extra_start = temp_line.index_of ("redBorder", end); + + if (extra_start != -1) { + // + start_text = "title=\""; + start = temp_line.index_of (start_text, extra_start); + + + end_text = "\">"; + end = temp_line.index_of (end_text, start); + + extra_title = temp_line.substring (start + start_text.length, end - (start + start_text.length)); + + // + start_text = ">"; + start = temp_line.index_of (start_text, extra_start); + + + end_text = ""; + end = temp_line.index_of (end_text, start); + + extra_short_title = temp_line.substring (start + start_text.length, end - (start + start_text.length)); + + // + extra = new Models.Extra (extra_title, extra_short_title); + extras.append (extra); + } + } + + // + if (temp_line.contains ("/images/manual_1.gif")) { + start_text = "manual/"; + start = temp_line.index_of (start_text, end); + + end_text = "\">"; + end = temp_line.index_of (end_text, start); + + raw_manual_id = temp_line.substring (start + start_text.length, end - (start + start_text.length)); + } + } + + // + if (i == 1 + offset) { + // + end = 0; + + // + while (true) { + // + start_text = "flags/"; + start = temp_line.index_of (start_text, end); + + end_text = "\" class"; + end = temp_line.index_of (end_text, start); + + var region_flag_filename = temp_line.substring (start + start_text.length, end - (start + start_text.length)); + + // + start_text = "title=\""; + start = temp_line.index_of (start_text, end); + + end_text = "\" style"; + end = temp_line.index_of (end_text, start); + + var region_title = temp_line.substring (start + start_text.length, end - (start + start_text.length)); + + // + var region = new Models.Region (region_title, region_flag_filename); + + // + regions.append (region); + + // + if (temp_line.index_of ("title", end) == -1) break; + } + } + + // + if (i == 2 + offset) { + start_text = "\">"; + start = temp_line.index_of (start_text); + + end_text = "<"; + end = temp_line.index_of (end_text, start); + + var raw_version = temp_line.substring (start + start_text.length, end - (start + start_text.length)); + + double version; + if (!double.try_parse (raw_version, out version)) { + warning (@"Unable to parse the version ($raw_version)"); + return false; + } + + media = new Models.Media (version); + } + } + + int id; + var id_parsed = int.try_parse (raw_id, out id); + if (!id_parsed) { + warning (@"Unable to parse the id ($raw_id)"); + continue; + } + + string title; + var title_parsed = parse_markup(raw_title, out title); + if (!title_parsed) { + warning (@"Unable to parse the title ($raw_title)"); + continue; + } + + int manual_id; + var manual_parsed = int.try_parse (raw_manual_id, out manual_id); + if (!manual_parsed) { + warning (@"Unable to parse the manual id ($raw_manual_id)"); + continue; + } + + if (media == null) { + warning (@"Unable to parse the media"); + continue; + } + + var game = new Models.Game.from_search (id, system, title, manual_id, extras, regions, media); + games.append (game); + } + + return true; + } + + public static bool parse_game_request (string res, ref Models.Game game) { + // + var missing = res.contains ("This game is not currently in The Vault."); + + game.missing = missing; + + if (missing) { + warning (@"Unable to parse the game " + game.title + " since it's not currently in Vimm vault"); + return false; + } + + // + var start_text = "sectionTitle"; + var start = res.last_index_of (start_text) + 2; + + var end_text = ""; + var end = res.index_of (end_text, start); + + var system = res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + + game.system = system; + + // + start_text = "Players"; + start = res.index_of (start_text, start) + 59; + + end_text = ""; + end = res.index_of (end_text, start); + + var raw_max_players = res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + raw_max_players = raw_max_players.strip (); + + if (raw_max_players.contains ("Simultaneous")) { + raw_max_players = raw_max_players.split (" ")[0]; + game.simultaneous = true; + } + + if (raw_max_players.contains ("?")) { + game.max_players = -1; + } else { + int max_players; + if (!int.try_parse (raw_max_players, out max_players)) { + warning (@"Unable to parse the max players ($raw_max_players)"); + return false; + } + + game.max_players = max_players; + } + + // + start_text = "Year"; + start = res.index_of (start_text, start) + 38; + + end_text = ""; + end = res.index_of (end_text, start); + + var raw_year = res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + + if (raw_year.contains ("?")) { + game.year = -1; + } else { + int year; + if (!int.try_parse (raw_year, out year)) { + warning (@"Unable to parse the year ($raw_year)"); + return false; + } + + game.year = year; + } + + // + start_text = "Publisher"; + start = res.index_of (start_text, start) + 18; + + end_text = ""; + end = res.index_of (end_text, start) - 5; + + var publisher = start - 18 == -1 ? "" : res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + + if (publisher.length > 0) { + game.publisher = publisher; + } + + // + start_text = "Serial #"; + start = res.index_of (start_text, start) + 18; + if (res.contains ("id=\"serials\">"))start += 13; + + end_text = ""; + end = res.index_of (end_text, start) - 5; + + var serial = start - 18 == -1 ? "" : res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + serial = serial.replace ("
", " "); + + if (serial.length > 0) { + game.serial = serial; + } + + // + if (game.rated = !res.contains ("Rating")) { + // + start_text = "Graphics"; + start = res.index_of (start_text, start) + 18; + + end_text = ""; + end = res.index_of (end_text, start) - 5; + + var raw_graphics_rating = res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + + double graphics_rating; + if (!double.try_parse (raw_graphics_rating, out graphics_rating)) { + warning (@"Unable to parse the graphics rating ($raw_graphics_rating)"); + return false; + } + + game.graphics_rating = graphics_rating; + + // + start_text = "Sound"; + start = res.index_of (start_text, start) + 18; + + end_text = ""; + end = res.index_of (end_text, start) - 5; + + var raw_sound_rating = res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + + double sound_rating; + if (!double.try_parse (raw_sound_rating, out sound_rating)) { + warning (@"Unable to parse the sound rating ($raw_sound_rating)"); + return false; + } + + game.sound_rating = sound_rating; + + // + start_text = "Gameplay"; + start = res.index_of (start_text, start) + 18; + + end_text = ""; + end = res.index_of (end_text, start) - 5; + + var raw_gameplay_rating = res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + + double gameplay_rating; + if (!double.try_parse (raw_gameplay_rating, out gameplay_rating)) { + warning (@"Unable to parse the gameplay rating ($raw_gameplay_rating)"); + return false; + } + + game.gameplay_rating = gameplay_rating; + + // + start_text = "Overall"; + start = res.index_of (start_text, start) + 18; + + end_text = " 0) { + game.medias.remove (game.medias.nth_data (0)); + } + + for (var i = 0; i < medias_text_split.length; i++) { + // + if (i == medias_text_split.length - 1) break; + + // + var line = medias_text_split[i]; + + // + start_text = "ID\":"; + start = line.index_of (start_text, 0); + + end_text = ","; + end = line.index_of (end_text, start); + + var raw_id = line.substring (start + start_text.length, line.length - start - start_text.length - (line.length - end)); + + int id; + if (!int.try_parse (raw_id, out id)) { + warning (@"Unable to parse the id ($raw_id)"); + return false; + } + + // + start_text = "Version\":\""; + start = line.index_of (start_text, 0); + + end_text = "\","; + end = line.index_of (end_text, start); + + var raw_version = line.substring (start + start_text.length, line.length - start - start_text.length - (line.length - end)); + + double version; + if (!double.try_parse (raw_version, out version)) { + warning (@"Unable to parse the version ($raw_version)"); + return false; + } + + // + start_text = "Zipped\":\""; + start = line.index_of (start_text, end); + + end_text = "\","; + end = line.index_of (end_text, start); + + var raw_download_size = line.substring (start + start_text.length, line.length - start - start_text.length - (line.length - end)); + + double download_size; + if (!double.try_parse (raw_download_size, out download_size)) { + warning (@"Unable to parse the download size ($raw_download_size)"); + return false; + } + + // + string crc = null; + string md5 = null; + string sha1 = null; + + // + start_text = "GoodHash\":\""; + start = line.index_of (start_text, end); + + if (start != -1) { + end_text = "\","; + end = line.index_of (end_text, start); + + crc = line.substring (start + start_text.length, line.length - start - start_text.length - (line.length - end)); + } + + // + start_text = "GoodMd5\":\""; + start = line.index_of (start_text, end); + + if (start != -1) { + end_text = "\","; + end = line.index_of (end_text, start); + + md5 = line.substring (start + start_text.length, line.length - start - start_text.length - (line.length - end)); + } + + // + start_text = "GoodSha1\":\""; + start = line.index_of (start_text, end); + + if (start != -1) { + end_text = "\"}"; + end = line.index_of (end_text, start); + + sha1 = line.substring (start + start_text.length, line.length - start - start_text.length - (line.length - end)); + } + + // + var media = new Models.Media.extra (id, version, crc, md5, sha1, download_size); + game.medias.append (media); + } + + // + start_text = "//download"; + start = res.index_of (start_text, 0); + + end_text = "\" method="; + end = res.index_of (end_text, start); + + game.download_server = "https:" + res.substring (start, end - start); + + // + return true; + } + + public static bool parse_system_request (string res, ref Models.System console) { + // + var start_text = "Have "; + var start = res.index_of (start_text); + + var end_text = " of "; + var end = res.index_of (end_text); + + var media_count = res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + + // + start_text = " of "; + start = end; + + end_text = "media"; + end = res.index_of (end_text, start); + + var media_total = res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + + // + start_text = "dat: "; + start = res.index_of (start_text, end); + + end_text = ""; + end = res.index_of (end_text, start); + + var media_last_synchronization_date = res.substring (start + start_text.length, res.length - start - start_text.length - (res.length - end)); + + // + console.media_count = int.parse (media_count); + console.media_total = int.parse (media_total); + console.media_last_synchronization_date = media_last_synchronization_date; + + // + start_text = ""); + + for (int i = 1; i < overall_rating_rows.length; i++) { + // + start_text = "href=\""; + start = overall_rating_rows[i].index_of (start_text); + + end_text = "\">"; + end = overall_rating_rows[i].index_of (end_text); + + var id = overall_rating_rows[i].substring (start + start_text.length, overall_rating_rows[i].length - start - start_text.length - (overall_rating_rows[i].length - end)); + id = id.replace ("/vault/", ""); + + // + start_text = end_text; + start = end; + + end_text = "<"; + end = overall_rating_rows[i].index_of (end_text, start); + + var title = overall_rating_rows[i].substring (start + start_text.length, overall_rating_rows[i].length - start - start_text.length - (overall_rating_rows[i].length - end)); + parse_markup(title, out title); + + // + var game = new Models.Game (int.parse (id), title); + console.overall_rating_list.append (game); + } + + // + start_text = "Graphics"; + start = res.index_of (start_text, end); + + end_text = ""; + end = res.index_of (end_text, start); + + var graphics = res.substring (start, res.length - start - (res.length - end)); + var graphics_rows = graphics.split (""); + + for (int i = 1; i < graphics_rows.length; i++) { + // + start_text = "href=\""; + start = graphics_rows[i].index_of (start_text); + + end_text = "\">"; + end = graphics_rows[i].index_of (end_text); + + var id = graphics_rows[i].substring (start + start_text.length, graphics_rows[i].length - start - start_text.length - (graphics_rows[i].length - end)); + id = id.replace ("/vault/", ""); + + // + start_text = end_text; + start = end; + + end_text = "<"; + end = graphics_rows[i].index_of (end_text, start); + + var title = graphics_rows[i].substring (start + start_text.length, graphics_rows[i].length - start - start_text.length - (graphics_rows[i].length - end)); + parse_markup(title, out title); + + // + var game = new Models.Game (int.parse (id), title); + console.graphics_list.append (game); + } + + // + start_text = "Sound"; + start = res.index_of (start_text, end); + + end_text = ""; + end = res.index_of (end_text, start); + + var sound = res.substring (start, res.length - start - (res.length - end)); + var sound_rows = sound.split (""); + + for (int i = 1; i < sound_rows.length; i++) { + // + start_text = "href=\""; + start = sound_rows[i].index_of (start_text); + + end_text = "\">"; + end = sound_rows[i].index_of (end_text); + + var id = sound_rows[i].substring (start + start_text.length, sound_rows[i].length - start - start_text.length - (sound_rows[i].length - end)); + id = id.replace ("/vault/", ""); + + // + start_text = end_text; + start = end; + + end_text = "<"; + end = sound_rows[i].index_of (end_text, start); + + var title = sound_rows[i].substring (start + start_text.length, sound_rows[i].length - start - start_text.length - (sound_rows[i].length - end)); + parse_markup(title, out title); + + // + var game = new Models.Game (int.parse (id), title); + console.sound_list.append (game); + } + + // + start_text = "Gameplay"; + start = res.index_of (start_text, end); + + end_text = ""; + end = res.index_of (end_text, start); + + var gameplay = res.substring (start, res.length - start - (res.length - end)); + var gameplay_rows = gameplay.split (""); + + for (int i = 1; i < gameplay_rows.length; i++) { + // + start_text = "href=\""; + start = gameplay_rows[i].index_of (start_text); + + end_text = "\">"; + end = gameplay_rows[i].index_of (end_text); + + var id = gameplay_rows[i].substring (start + start_text.length, gameplay_rows[i].length - start - start_text.length - (gameplay_rows[i].length - end)); + id = id.replace ("/vault/", ""); + + // + start_text = end_text; + start = end; + + end_text = "<"; + end = gameplay_rows[i].index_of (end_text, start); + + var title = gameplay_rows[i].substring (start + start_text.length, gameplay_rows[i].length - start - start_text.length - (gameplay_rows[i].length - end)); + parse_markup(title, out title); + + // + var game = new Models.Game (int.parse (id), title); + console.gameplay_list.append (game); + } + + // + return true; + } + } +} \ No newline at end of file diff --git a/src/utils/web.vala b/src/utils/web.vala new file mode 100644 index 0000000..596bd88 --- /dev/null +++ b/src/utils/web.vala @@ -0,0 +1,21 @@ +namespace RetroPlus.Utils { + public class Web { + public static string get_user_agent () { + return "Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0"; + } + + public static bool get_request (string url, ref string res) { + try { + var session = new Soup.Session (); + var message = new Soup.Message ("GET", url); + session.set_user_agent (get_user_agent ()); + Bytes bytes = session.send_and_read (message); + res = (string) bytes.get_data (); + return true; + } catch (GLib.Error e) { + message (e.message); + return false; + } + } + } +} \ No newline at end of file diff --git a/src/widgets/download-popover.vala b/src/widgets/download-popover.vala new file mode 100644 index 0000000..aa32082 --- /dev/null +++ b/src/widgets/download-popover.vala @@ -0,0 +1,111 @@ +namespace RetroPlus.Widgets { + public class DownloadPopover : Gtk.Popover { + Gtk.ListBox download_list { get; set; } + + public signal void download_cancelled (Models.Game game); + + public signal void download_finished (Models.Game game); + + public signal void download_error (Models.Game game); + + public signal void download_file_exists (Models.Game game); + + construct { + // + var placeholder_label = new Gtk.Label (_("No download in progress")); + placeholder_label.add_css_class ("p-10"); + + // + download_list = new Gtk.ListBox (); + download_list.set_placeholder (placeholder_label); + download_list.set_vexpand (true); + download_list.add_css_class ("boxed-list"); + download_list.set_selection_mode (Gtk.SelectionMode.NONE); + + // + var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); + box.append (download_list); + + // + this.set_child (box); + } + + public void add_download (Models.Game game, Models.Media media) { + // + var cancelled = false; + + // + var row = new DownloadRow (game); + + // + row.cancel_button.clicked.connect (() => { + // + row.set_cancelling (); + + // + cancelled = true; + + // + var previous_row = download_list.get_row_at_index (0) as DownloadRow; + + // + download_list.remove (row); + + // + download_cancelled (game); + + // + var first_row = download_list.get_row_at_index (0) as DownloadRow; + if (first_row != null && previous_row == row) { + first_row.start_download (); + } + }); + + // + row.start_download.connect (() => { + // + row.set_starting (); + + // + game.download.begin (media, () => cancelled, row.set_progress, row.set_download_speed, (obj, res) => { + // + var download_result = game.download.end (res); + + // + if (cancelled)return; + + // + download_list.remove (row); + + // + switch (download_result) { + case Models.Game.DownloadResults.SUCCESS: + download_finished (game); + break; + case Models.Game.DownloadResults.FILE_EXISTS: + download_file_exists (game); + break; + case Models.Game.DownloadResults.ERROR: + download_error (game); + break; + } + + // + var first_row = download_list.get_row_at_index (0) as DownloadRow; + if (first_row != null) { + first_row.start_download (); + } + }); + }); + + // + download_list.append (row); + + // + var first_row = download_list.get_row_at_index (0) as DownloadRow; + if (first_row == row) { + first_row.start_download (); + } + } + } +} \ No newline at end of file diff --git a/src/widgets/download-row.vala b/src/widgets/download-row.vala new file mode 100644 index 0000000..9cd49d4 --- /dev/null +++ b/src/widgets/download-row.vala @@ -0,0 +1,71 @@ +namespace RetroPlus.Widgets { + public class DownloadRow : Gtk.ListBoxRow { + public Models.Game game { get; construct; } + public Gtk.Button cancel_button { get; set; } + Gtk.ProgressBar progress_bar { get; set; } + + public signal void start_download (); + + public DownloadRow (Models.Game game) { + Object (game: game); + } + + construct { + // + var title_label = new Gtk.Label (game.title); + title_label.set_ellipsize (Pango.EllipsizeMode.END); + title_label.set_tooltip_text (game.title); + title_label.set_hexpand (true); + + // + cancel_button = new Gtk.Button.from_icon_name ("x-lg-symbolic"); + cancel_button.add_css_class ("flat"); + + // + var top_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10); + top_box.append (title_label); + top_box.append (cancel_button); + + // + progress_bar = new Gtk.ProgressBar (); + progress_bar.set_show_text (true); + progress_bar.set_text (_("Queued")); + + // + var main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); + main_box.add_css_class ("p-10"); + main_box.append (top_box); + main_box.append (progress_bar); + + // + this.set_child (main_box); + } + + public void set_starting () { + progress_bar.set_text (_("Starting")); + } + + public void set_cancelling () { + progress_bar.set_text (_("Cancelling")); + } + + public void set_progress (double progress) { + progress_bar.set_fraction (progress); + } + + public void set_download_speed (double bytes) { + var pourcentage = progress_bar.get_fraction () * 100; + if (bytes < 1024.0f) { + progress_bar.set_text ("%.0f% | ".printf (pourcentage) + "%.0f Bps".printf (bytes)); + } else { + var kilobytes = bytes / 1024.0f; + if (kilobytes < 1024.0f) { + progress_bar.set_text ("%.0f% | ".printf (pourcentage) + "%.0f Kbps".printf (kilobytes)); + } else { + var megabytes = kilobytes / 1024.0f; + progress_bar.set_text ("%.0f% | ".printf (pourcentage) + "%.1f Mbps".printf (megabytes)); + } + } + } + } +} \ No newline at end of file diff --git a/src/widgets/game-detail-modal.vala b/src/widgets/game-detail-modal.vala new file mode 100644 index 0000000..4fdc8ec --- /dev/null +++ b/src/widgets/game-detail-modal.vala @@ -0,0 +1,276 @@ +namespace RetroPlus.Widgets { + public class GameDetailModal : Adw.MessageDialog { + public Models.Game game { get; construct; } + GLib.ListStore media_list_store { get; set; } + Gtk.DropDown media_dropdown { get; set; } + Gtk.Label crc_label { get; set; } + Gtk.Label md5_label { get; set; } + Gtk.Label sha1_label { get; set; } + Gtk.Label download_size_label { get; set; } + + public signal void download_clicked (Models.Game game, Models.Media media); + public signal void close_clicked (); + + public GameDetailModal (Models.Game game) { + Object (game: game); + } + + construct { + // + this.set_transient_for (Application.main_window); + + // + add_response ("close", _("Close")); + set_response_appearance ("close", Adw.ResponseAppearance.DEFAULT); + + // + add_response ("download", _("Download")); + set_response_appearance ("download", Adw.ResponseAppearance.SUGGESTED); + + // + this.set_close_response ("close"); + + // + this.response.connect (on_response); + + // + this.set_heading (game.title); + + // + var carousel = new Adw.Carousel (); + carousel.set_interactive (true); + carousel.append (get_info_box ()); + if (game.rated)carousel.append (get_rating_box ()); + if (game.medias.nth_data (0).has_hash)carousel.append (get_hash_box ()); + + // + var carousel_indicator_dots = new Adw.CarouselIndicatorDots (); + carousel_indicator_dots.set_carousel (carousel); + + // + var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 5); + box.append (carousel); + box.append (carousel_indicator_dots); + + // + this.set_extra_child (box); + } + + void on_response (string response) { + switch (response) { + case "download": + download_clicked (game, (Models.Media) media_dropdown.get_selected_item ()); + break; + case "close": + close_clicked (); + break; + } + } + + Gtk.Box get_info_box () { + // + var system_label = new Gtk.Label (_("System") + ": " + game.system); + + // + var region_label = new Gtk.Label (_("Region") + ": "); + + var flags_box = new Gtk.FlowBox (); + flags_box.set_max_children_per_line (2); + flags_box.set_halign (Gtk.Align.CENTER); + flags_box.set_size_request (50, 0); + flags_box.set_visible (game.regions.length () > 0); + + Models.Region.load_flags.begin (game.regions, (region) => { + region.download_flag.begin ((obj, res) => { + var downloaded = region.download_flag.end (res); + + if (downloaded) { + var image = new Gtk.Image.from_file (region.get_flag_path ()); + image.set_tooltip_text (region.title); + + flags_box.append (image); + } + }); + }); + + var region_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 5); + region_box.set_halign (Gtk.Align.CENTER); + region_box.set_hexpand (true); + region_box.append (region_label); + region_box.append (flags_box); + + // + var players_label = new Gtk.Label (_("Players") + ": " + game.max_players.to_string () + (game.simultaneous ? " " + _("Simultaneous") : "")); + + // + var year_label = new Gtk.Label (_("Year") + ": " + (game.year.to_string ())); + + // + var publisher_label = new Gtk.Label (_("Publisher") + ": " + game.publisher); + + // + var serial_label = new Gtk.Label (_("Serial") + "#: " + game.serial); + + // + download_size_label = new Gtk.Label (_("Size") + ": " + game.medias.nth_data (0).get_formatted_download_size ()); + + // + var media_label = new Gtk.Label (_("Version") + ": " + (game.medias.length () == 1 ? "%.2f".printf (game.medias.nth_data (0).version) : "")); + + // + media_list_store = new GLib.ListStore (typeof (Models.Media)); + + foreach (var media in game.medias) { + media_list_store.append (media); + } + + // + var media_selection_model = new Gtk.SingleSelection (media_list_store); + + // + var media_factory = new Gtk.SignalListItemFactory (); + media_factory.bind.connect (media_factory_bind); + media_factory.setup.connect (media_factory_setup); + + // + media_dropdown = new Gtk.DropDown (media_selection_model, null); + media_dropdown.set_factory (media_factory); + media_dropdown.notify["selected-item"].connect (on_media_dropdown_selected_item_changed); + + // + var media_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10); + media_box.set_halign (Gtk.Align.CENTER); + media_box.append (media_label); + if (game.medias.length () > 1)media_box.append (media_dropdown); + + // + var play_online_button = new Gtk.Button.with_label (_("Play online")); + play_online_button.clicked.connect (on_play_online_button_clicked); + + // + var manual_button = new Gtk.Button.with_label (_("See manual")); + manual_button.clicked.connect (on_manual_button_clicked); + + // + var extra_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10); + extra_box.set_halign (Gtk.Align.CENTER); + if (game.support_play_online)extra_box.append (play_online_button); + if (game.has_manual)extra_box.append (manual_button); + + // + var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 5); + box.set_valign (Gtk.Align.CENTER); + box.set_hexpand (true); + box.append (system_label); + box.append (region_box); + if (game.has_max_players)box.append (players_label); + if (game.has_year)box.append (year_label); + if (game.has_publisher)box.append (publisher_label); + if (game.has_serial)box.append (serial_label); + box.append (download_size_label); + box.append (media_box); + if (game.support_play_online || game.has_manual)box.append (extra_box); + + // + return box; + } + + void on_play_online_button_clicked () { + var uri_launcher = new Gtk.UriLauncher (game.get_play_online_url ()); + uri_launcher.launch.begin (Application.main_window, null); + } + + void on_manual_button_clicked () { + var uri_launcher = new Gtk.UriLauncher (game.get_manual_url ()); + uri_launcher.launch.begin (Application.main_window, null); + } + + void media_factory_bind (Gtk.SignalListItemFactory factory, Object item) { + Gtk.ListItem list_item = item as Gtk.ListItem; + + var string_holder = list_item.get_item () as Models.Media; + + var title = list_item.get_data ("title"); + title.label = "%.2f".printf (string_holder.version); + } + + void media_factory_setup (Gtk.SignalListItemFactory factory, Object item) { + Gtk.ListItem list_item = item as Gtk.ListItem; + + var title = new Gtk.Label (""); + title.xalign = 0.0f; + + list_item.set_data ("title", title); + list_item.set_child (title); + } + + void on_media_dropdown_selected_item_changed () { + // + var media = media_dropdown.get_selected_item () as Models.Media; + + // + if (media == null)return; + + // + crc_label.set_text (_("CRC") + ": " + media.crc); + md5_label.set_text (_("MD5") + ": " + media.md5); + sha1_label.set_text (_("SHA1") + ": " + media.sha1); + + // + download_size_label.set_text (_("Size") + ": " + media.get_formatted_download_size ()); + } + + Gtk.Box get_rating_box () { + // + var graphics_rating_label = new Gtk.Label (_("Graphics") + ": " + "%.2f".printf (game.graphics_rating)); + + // + var sound_rating_label = new Gtk.Label (_("Sound") + ": " + "%.2f".printf (game.sound_rating)); + + // + var gameplay_rating_label = new Gtk.Label (_("Gameplay") + ": " + "%.2f".printf (game.gameplay_rating)); + + // + var overall_rating_label = new Gtk.Label (_("Overall") + ": " + "%.2f".printf (game.overall_rating) + " (" + game.total_votes.to_string () + " " + (game.total_votes > 1 ? _("votes") : _("vote")) + ")"); + + // + var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 5); + box.set_valign (Gtk.Align.CENTER); + box.set_hexpand (true); + box.append (graphics_rating_label); + box.append (sound_rating_label); + box.append (gameplay_rating_label); + box.append (overall_rating_label); + + // + return box; + } + + Gtk.Box get_hash_box () { + // + crc_label = new Gtk.Label (_("CRC") + ": " + game.medias.nth_data (0).crc); + crc_label.set_tooltip_text (game.medias.nth_data (0).crc); + + // + md5_label = new Gtk.Label (_("MD5") + ": " + game.medias.nth_data (0).md5); + md5_label.set_ellipsize (Pango.EllipsizeMode.END); + md5_label.set_tooltip_text (game.medias.nth_data (0).md5); + + // + sha1_label = new Gtk.Label (_("SHA1") + ": " + game.medias.nth_data (0).sha1); + sha1_label.set_ellipsize (Pango.EllipsizeMode.END); + sha1_label.set_tooltip_text (game.medias.nth_data (0).sha1); + + // + var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 5); + box.set_valign (Gtk.Align.CENTER); + box.set_hexpand (true); + box.append (crc_label); + box.append (md5_label); + box.append (sha1_label); + + // + return box; + } + } +} \ No newline at end of file diff --git a/src/widgets/search-box.vala b/src/widgets/search-box.vala new file mode 100644 index 0000000..c7a46bc --- /dev/null +++ b/src/widgets/search-box.vala @@ -0,0 +1,279 @@ +namespace RetroPlus.Widgets { + public class SearchBox : Gtk.Box { + Gtk.Entry search_entry { get; set; } + public SearchFilterBox search_filter_box { get; construct; } + Gtk.ListBox games_list { get; set; } + Gtk.Spinner spinner { get; set; } + DownloadPopover download_popover { get; set; } + Adw.ToastOverlay toast_overlay { get; set; } + List search_results; + Gtk.Label system_label { get; set; } + + public SearchBox (SearchFilterBox search_filter_box) { + Object (search_filter_box: search_filter_box); + } + + construct { + // + this.set_orientation (Gtk.Orientation.VERTICAL); + + // + var download_button = new Gtk.Button.from_icon_name ("download-symbolic"); + download_button.clicked.connect (on_download_button_clicked); + + // + download_popover = new DownloadPopover (); + download_popover.set_parent (download_button); + download_popover.set_autohide (true); + download_popover.download_finished.connect (on_download_finished); + download_popover.download_cancelled.connect (on_download_cancelled); + download_popover.download_error.connect (on_download_error); + download_popover.download_file_exists.connect (on_download_file_exists); + + // + var menu_model = new GLib.Menu (); + menu_model.append (_("Preferences"), "app.show-preferences"); + menu_model.append (_("Keyboard Shortcuts"), "win.show-help-overlay"); + menu_model.append (_("About"), "app.show-about"); + + // + var menu_button = new Gtk.MenuButton (); + menu_button.set_icon_name ("open-menu-symbolic"); + menu_button.set_menu_model (menu_model); + + // + var header = new Adw.HeaderBar (); + header.add_css_class ("flat"); + header.pack_start (download_button); + header.pack_end (menu_button); + + // + search_entry = new Gtk.Entry (); + search_entry.set_hexpand (true); + search_entry.set_placeholder_text (_("Search a game by name")); + search_entry.set_icon_from_icon_name (Gtk.EntryIconPosition.PRIMARY, "search-symbolic"); + search_entry.set_icon_from_icon_name (Gtk.EntryIconPosition.SECONDARY, "funnel-fill-symbolic"); + search_entry.set_icon_tooltip_text (Gtk.EntryIconPosition.SECONDARY, _("Advanced filtering options")); + search_entry.icon_press.connect (on_search_entry_icon_pressed); + search_entry.activate.connect (on_search_entry_activated); + + // + search_filter_box.set_visible (false); + + // + system_label = new Gtk.Label (_("System")); + system_label.set_halign (Gtk.Align.CENTER); + system_label.set_size_request (80, 0); + + // + var title_label = new Gtk.Label (_("Title")); + title_label.set_halign (Gtk.Align.CENTER); + title_label.set_hexpand (true); + + // + var region_label = new Gtk.Label (_("Region")); + region_label.set_halign (Gtk.Align.CENTER); + region_label.set_size_request (50, 0); + + // + var version_label = new Gtk.Label (_("Version")); + version_label.set_halign (Gtk.Align.CENTER); + version_label.set_size_request (55, 0); + + // + var legend_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10); + legend_box.set_margin_start (10); + legend_box.set_margin_end (12); + legend_box.append (system_label); + legend_box.append (title_label); + legend_box.append (region_label); + legend_box.append (version_label); + + // + games_list = new Gtk.ListBox (); + games_list.set_activate_on_single_click (false); + games_list.set_selection_mode (Gtk.SelectionMode.SINGLE); + games_list.add_css_class ("boxed-list"); + games_list.set_hexpand (true); + games_list.row_selected.connect (on_game_list_row_selected); + + // + var scrolled_window = new Gtk.ScrolledWindow (); + scrolled_window.set_propagate_natural_width (false); + scrolled_window.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); + scrolled_window.set_vexpand (true); + scrolled_window.set_child (games_list); + + // + spinner = new Gtk.Spinner (); + spinner.set_size_request (50, 50); + spinner.set_halign (Gtk.Align.CENTER); + spinner.set_valign (Gtk.Align.CENTER); + + // + var overlay = new Gtk.Overlay (); + overlay.set_child (scrolled_window); + overlay.add_overlay (spinner); + + // + var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 10); + box.append (search_entry); + box.append (search_filter_box); + box.append (legend_box); + box.append (overlay); + + // + var clamp = new Adw.Clamp (); + clamp.set_margin_start (10); + clamp.set_margin_end (10); + clamp.set_margin_bottom (10); + clamp.set_maximum_size (820); + clamp.set_child (box); + + // + toast_overlay = new Adw.ToastOverlay (); + toast_overlay.set_child (clamp); + + // + this.append (header); + this.append (toast_overlay); + } + + void on_download_button_clicked () { + download_popover.popup (); + } + + void on_search_entry_activated () { + // + spinner.start (); + + // + games_list.set_sensitive (false); + + // + search_entry.set_sensitive (false); + + // + games_list.remove_all (); + + // + var system = search_filter_box.system_dropdown.get_selected_item () as Models.System; + + // + system_label.set_visible (system.id == ""); + + // + system.get_games_by_title.begin (search_entry.get_text (), (obj, res) => { + // + search_results = system.get_games_by_title.end (res); + + // + foreach (var game in search_results) { + // + var row = new SearchRow (game, system.id == ""); + + // + games_list.append (row); + } + + // + spinner.stop (); + + // + games_list.set_sensitive (true); + + // + search_entry.set_sensitive (true); + }); + } + + void on_search_entry_icon_pressed (Gtk.EntryIconPosition entry_icon_position) { + if (entry_icon_position == Gtk.EntryIconPosition.SECONDARY)search_filter_box.set_visible (!search_filter_box.get_visible ()); + } + + void on_game_list_row_selected (Gtk.ListBoxRow? row) { + // + if (row == null)return; + + // + var game = search_results.nth_data (row.get_index ()); + + // + if (game == null)return; + + // + spinner.start (); + + // + games_list.set_sensitive (false); + + // + search_entry.set_sensitive (false); + + // + game.load.begin ((obj, res) => { + // + var error = !game.load.end (res); + + // + spinner.stop (); + + // + games_list.set_sensitive (true); + + // + search_entry.set_sensitive (true); + + // + if (!error) { + if (game.medias.length () > 0) { + var game_detail_modal = new GameDetailModal (game); + game_detail_modal.download_clicked.connect (on_download_started); + game_detail_modal.show (); + } else { + error = true; + } + } + + // + if (error) { + var toast = new Adw.Toast (_("An error occured while opening") + " " + game.title); + + toast_overlay.add_toast (toast); + } + }); + } + + void on_download_started (Models.Game game, Models.Media media) { + download_popover.add_download (game, media); + + var toast = new Adw.Toast (game.title + " " + _("download queued")); + + toast_overlay.add_toast (toast); + } + + void on_download_finished (Models.Game game) { + var toast = new Adw.Toast (game.title + " " + _("finished downloading")); + + toast_overlay.add_toast (toast); + } + + void on_download_cancelled (Models.Game game) { + var toast = new Adw.Toast (game.title + " " + _("download cancelled")); + + toast_overlay.add_toast (toast); + } + + void on_download_error (Models.Game game) { + var toast = new Adw.Toast (game.title + " " + _("could not download due to an error")); + + toast_overlay.add_toast (toast); + } + + void on_download_file_exists (Models.Game game) { + var toast = new Adw.Toast (game.title + " " + _(" is already downloaded")); + + toast_overlay.add_toast (toast); + } + } +} \ No newline at end of file diff --git a/src/widgets/search-filter-box.vala b/src/widgets/search-filter-box.vala new file mode 100644 index 0000000..af64b37 --- /dev/null +++ b/src/widgets/search-filter-box.vala @@ -0,0 +1,108 @@ +namespace RetroPlus.Widgets { + public class SearchFilterBox : Gtk.Box { + ListStore system_list_store { get; set; } + ListStore source_list_store { get; set; } + public Gtk.DropDown system_dropdown { get; set; } + public Gtk.DropDown source_dropdown { get; set; } + bool initialized { get; set; } + + construct { + // + this.set_orientation (Gtk.Orientation.VERTICAL); + this.set_spacing (10); + this.add_css_class ("card"); + this.add_css_class ("p-10"); + + // + var system_label = new Gtk.Label (_("System") + ":"); + + system_list_store = new ListStore (typeof (Models.System)); + + var system_factory = new Gtk.SignalListItemFactory (); + system_factory.bind.connect (system_factory_bind); + system_factory.setup.connect (system_factory_setup); + + system_dropdown = new Gtk.DropDown (system_list_store, null); + system_dropdown.set_factory (system_factory); + + var system_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10); + system_box.append (system_label); + system_box.append (system_dropdown); + + // + var source_label = new Gtk.Label (_("Source") + ":"); + + source_list_store = new ListStore (typeof (Models.Source)); + + var source_factory = new Gtk.SignalListItemFactory (); + source_factory.bind.connect (source_factory_bind); + source_factory.setup.connect (source_factory_setup); + + source_dropdown = new Gtk.DropDown (source_list_store, null); + source_dropdown.set_factory (source_factory); + + var source_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10); + source_box.append (source_label); + source_box.append (source_dropdown); + + // + var flow_box = new Gtk.FlowBox (); + flow_box.append (system_box); + flow_box.append (source_box); + + // + this.append (flow_box); + } + + void system_factory_bind (Gtk.SignalListItemFactory factory, Object object) { + var list_item = object as Gtk.ListItem; + + var system = list_item.get_item () as Models.System; + + var title = list_item.get_data ("title"); + title.label = system.title; + } + + void system_factory_setup (Gtk.SignalListItemFactory factory, Object object) { + var list_item = object as Gtk.ListItem; + + var title = new Gtk.Label (""); + + list_item.set_data ("title", title); + list_item.set_child (title); + } + + void source_factory_bind (Gtk.SignalListItemFactory factory, Object object) { + var list_item = object as Gtk.ListItem; + + var source = list_item.get_item () as Models.Source; + + var title = list_item.get_data ("title"); + title.label = source.title; + } + + void source_factory_setup (Gtk.SignalListItemFactory factory, Object object) { + var list_item = object as Gtk.ListItem; + + var title = new Gtk.Label (""); + + list_item.set_data ("title", title); + list_item.set_child (title); + } + + public void initialize (Gee.Iterator system) { + // + system_list_store.remove_all (); + + // + system.foreach ((system) => { + system_list_store.append (system); + + return true; + }); + + // + source_list_store.append (new Models.Source("Vimm's Lair")); + } + } +} \ No newline at end of file diff --git a/src/widgets/search-row.vala b/src/widgets/search-row.vala new file mode 100644 index 0000000..abaebe9 --- /dev/null +++ b/src/widgets/search-row.vala @@ -0,0 +1,82 @@ +namespace RetroPlus.Widgets { + public class SearchRow : Gtk.Box { + public Models.Game game { get; construct; } + public bool show_system_label { get; construct; } + + public SearchRow (Models.Game game, bool show_system_label) { + Object (game: game, show_system_label: show_system_label); + } + + construct { + // + this.set_spacing (10); + this.add_css_class ("p-10"); + + // + var system_label = new Gtk.Label (game.system); + system_label.set_halign (Gtk.Align.CENTER); + system_label.set_size_request (80, 0); + + // + var title = new Gtk.Label (game.title); + title.set_ellipsize (Pango.EllipsizeMode.END); + title.set_tooltip_text (game.title); + title.set_halign (Gtk.Align.START); + + // + var extra_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10); + extra_box.set_visible (game.extras.length () > 0); + extra_box.add_css_class ("search-row-extra"); + + foreach (var extra in game.extras) { + var label = new Gtk.Label (extra.short_title); + label.set_tooltip_text (extra.title); + label.set_halign (Gtk.Align.START); + + extra_box.append (label); + } + + // + var manual = new Gtk.Image.from_icon_name ("book-half-symbolic"); + manual.set_tooltip_text (_("Manual available")); + + // + var spacer = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); + spacer.set_hexpand (true); + + // + var region_box = new Gtk.FlowBox (); + region_box.set_max_children_per_line (2); + region_box.set_halign (Gtk.Align.CENTER); + region_box.set_size_request (50, 0); + region_box.set_visible (game.regions.length () > 0); + + Models.Region.load_flags.begin (game.regions, (region) => { + region.download_flag.begin ((obj, res) => { + var downloaded = region.download_flag.end (res); + + if (downloaded) { + var image = new Gtk.Image.from_file (region.get_flag_path ()); + image.set_tooltip_text (region.title); + + region_box.append (image); + } + }); + }); + + // + var version = new Gtk.Label ("v" + "%.2f".printf (game.medias.nth_data (0).version)); + version.set_halign (Gtk.Align.CENTER); + version.set_size_request (55, 0); + + // + if (show_system_label)this.append (system_label); + this.append (title); + this.append (extra_box); + if (game.has_manual)this.append (manual); + this.append (spacer); + this.append (region_box); + this.append (version); + } + } +} \ No newline at end of file diff --git a/src/widgets/status-box.vala b/src/widgets/status-box.vala new file mode 100644 index 0000000..23afe7d --- /dev/null +++ b/src/widgets/status-box.vala @@ -0,0 +1,30 @@ +namespace RetroPlus.Widgets { + public class StatusBox : Gtk.Box { + Adw.StatusPage status_page { get; set; } + + construct { + // + var header = new Adw.HeaderBar (); + header.add_css_class ("flat"); + + // + status_page = new Adw.StatusPage (); + status_page.set_vexpand (true); + status_page.set_hexpand (true); + + // + var toolbar_view = new Adw.ToolbarView (); + toolbar_view.add_top_bar (header); + toolbar_view.set_content (status_page); + + // + this.append (toolbar_view); + } + + public void set_values (string? icon_name, string title, string description) { + status_page.set_icon_name (icon_name); + status_page.set_title (title); + status_page.set_description (description); + } + } +} \ No newline at end of file diff --git a/src/window.vala b/src/window.vala new file mode 100644 index 0000000..237dd1b --- /dev/null +++ b/src/window.vala @@ -0,0 +1,128 @@ +namespace RetroPlus { + public class Window : Adw.ApplicationWindow { + Gee.HashMap systems; + + Widgets.StatusBox status_box { get; set; } + Widgets.SearchBox search_box { get; set; } + Adw.NavigationView navigation_view { get; set; } + + public bool settings_error { get; set; } + public bool initialization_error { get; set; } + public bool systems_error { get; set; } + public bool init_done { get; set; } + + construct { + // + this.set_application ((Adw.Application) GLib.Application.get_default ()); + this.set_title (Constants.APP_NAME); + this.set_size_request (400, 500); + this.set_default_size (400, 500); + + // + status_box = new Widgets.StatusBox (); + status_box.set_values ("com.vysp3r.RetroPlus", _("Loading") + "...", ""); + + // + this.notify["init-done"].connect (on_init_done_changed); + + // + this.notify["settings-error"].connect (on_error_changed); + + // + this.notify["games-error"].connect (on_error_changed); + + // + this.notify["systems-error"].connect (on_error_changed); + + // + this.set_content (status_box); + } + + public void initialize () { + new Thread ("initialize", () => { + // + settings_error = false; + initialization_error = false; + systems_error = false; + init_done = false; + + // + var download_directory = Application.settings.get_string ("download-directory"); + var user_download_directory = GLib.Environment.get_user_special_dir (GLib.UserDirectory.DOWNLOAD); + if (download_directory == "")Application.settings.set_string ("download-directory", user_download_directory); + else if (settings_error = !FileUtils.test (download_directory, GLib.FileTest.IS_DIR)) { + message (@"The download-directory setting was invalid therefore it was reset to '$user_download_directory'"); + Application.settings.set_string ("download-directory", user_download_directory); + return; + } + + // + if (initialization_error = !Models.Region.initialize ())return; + + // + systems = new Gee.HashMap (); + if (systems_error = !Models.System.get_systems (ref systems))return; + + // + init_done = true; + }); + } + + void on_init_done_changed () { + // + if (!init_done)return; + + // + var ordered_systems = systems.values.order_by ((a, b) => { + return strcmp (a.title, b.title); + }); + + // + var search_filter_box = new Widgets.SearchFilterBox (); + search_filter_box.initialize (ordered_systems); + + // + search_box = new Widgets.SearchBox (search_filter_box); + + // + var search_page = new Adw.NavigationPage.with_tag (search_box, Constants.APP_NAME, "search"); + + // + navigation_view = new Adw.NavigationView (); + navigation_view.set_animate_transitions (true); + navigation_view.add (search_page); + + // + this.set_content (navigation_view); + } + + void on_error_changed () { + // + var title = ""; + + // + if (settings_error) { + title = _("An error occured during the initialization."); + } + + // + if (initialization_error) { + title = _("An error occured during the initialization."); + } + + // + if (systems_error) { + title = _("An error occured while loading the systems."); + } + + // + if (title != "") { + // + status_box.set_values (null, title, _("To fix this problem, you should report this issue on GitHub.")); + + // + this.set_content (status_box); + } + } + } +} \ No newline at end of file